Skip to content

Commit

Permalink
add: draft for model player UI derived from WebUI
Browse files Browse the repository at this point in the history
  • Loading branch information
lizable authored and agatha197 committed Jan 6, 2025
1 parent e2c145d commit 6dabc24
Show file tree
Hide file tree
Showing 73 changed files with 1,779 additions and 453 deletions.
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
NODE_ENV: 'production'
}
};
globalThis.packageVersion = "24.09.0-alpha.1";
globalThis.buildNumber = "6011";
globalThis.packageVersion = "25.1.0-alpha.1";
globalThis.buildNumber = "6441";

</script>
<!-- DO NOT CHANGE BELOW LINE -->
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"manifest_version": 9,
"name": "Backend.AI Web UI",
"short_name": "BackendAIWebUI",
"version": "24.09.0-alpha.1",
"version": "25.1.0-alpha.1",
"start_url": "/",
"display": "standalone",
"background_color": "#fff",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "backend.ai-webui",
"productName": "Backend.AI Desktop",
"version": "24.09.0-alpha.1",
"version": "25.1.0-alpha.1",
"repository": "https://github.com/lablup/backend.ai-webui.git",
"author": "Lablup Inc. <[email protected]>",
"license": "LGPL-3.0-or-later",
Expand Down
2 changes: 1 addition & 1 deletion react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "backend-ai-webui-react",
"version": "24.09.0-alpha.1",
"version": "25.1.0-alpha.1",
"private": true,
"dependencies": {
"@ai-sdk/openai": "^1.0.11",
Expand Down
27 changes: 23 additions & 4 deletions react/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,13 @@ const ComputeSessionList = React.lazy(
() => import('./components/ComputeSessionList'),
);
const AgentSummaryPage = React.lazy(() => import('./pages/AgentSummaryPage'));

/**
* Pages for Model Player
*/
const PlaygroundPage = React.lazy(
() => import('./components/lablupTalkativotUI/LLMPlaygroundPage'),
);
const ModelStorePage = React.lazy(() => import('./pages/ModelStorePage'));
interface CustomHandle {
title?: string;
labelKey?: string;
Expand Down Expand Up @@ -106,17 +112,17 @@ const router = createBrowserRouter([
children: [
{
path: '/',
element: <WebUINavigate to="/summary" replace />,
element: <WebUINavigate to="/playground" replace />,
},
{
//for electron dev mode
path: '/build/electron-app/app/index.html',
element: <WebUINavigate to="/summary" replace />,
element: <WebUINavigate to="/playground" replace />,
},
{
//for electron prod mode
path: '/app/index.html',
element: <WebUINavigate to="/summary" replace />,
element: <WebUINavigate to="/playground" replace />,
},
{
path: '/summary',
Expand Down Expand Up @@ -359,6 +365,19 @@ const router = createBrowserRouter([
path: '*',
element: <></>,
},
/**
* Pages for Model Player
*/
{
path: '/playground',
handle: { labelKey: 'webui.menu.Playground' },
Component: PlaygroundPage,
},
{
path: '/model-store',
handle: { labelKey: 'webui.menu.ModelStore' },
Component: ModelStorePage,
},
],
},
]);
Expand Down
23 changes: 23 additions & 0 deletions react/src/components/BAIIcons/VLLMIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ReactComponent as logo } from './vllm-color.svg';
import Icon from '@ant-design/icons';
import { CustomIconComponentProps } from '@ant-design/icons/lib/components/Icon';

interface CustomIconProps
extends Omit<CustomIconComponentProps, 'width' | 'height' | 'fill'> {
size?: number;
}

const VLLMIcon: React.FC<CustomIconProps> = (props) => {
return (
<Icon
component={logo}
{...props}
style={{
fontSize: props.size,
...props.style,
}}
/>
);
};

export default VLLMIcon;
1 change: 1 addition & 0 deletions react/src/components/BAIIcons/vllm-color.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
122 changes: 122 additions & 0 deletions react/src/components/ChatContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { useTanQuery } from '../hooks/reactQueryAlias';
import { ChatContentEndpointDetailQuery } from './__generated__/ChatContentEndpointDetailQuery.graphql';
import { Model } from './lablupTalkativotUI/ChatUIModal';
import LLMChatCard from './lablupTalkativotUI/LLMChatCard';
import { ReloadOutlined } from '@ant-design/icons';
import { Alert, Button } from 'antd';
import graphql from 'babel-plugin-relay/macro';
import _ from 'lodash';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useLazyLoadQuery } from 'react-relay/hooks';

interface ChatContentProps {
endpointId: string;
endpointUrl: string;
basePath: string;
}

const ChatContent: React.FC<ChatContentProps> = ({
endpointId,
endpointUrl,
basePath,
}) => {
const { t } = useTranslation();

const { endpoint_token_list } =
useLazyLoadQuery<ChatContentEndpointDetailQuery>(
graphql`
query ChatContentEndpointDetailQuery(
$endpointId: UUID!
$tokenListOffset: Int!
$tokenListLimit: Int!
) {
endpoint_token_list(
limit: $tokenListLimit
offset: $tokenListOffset
endpoint_id: $endpointId
) {
total_count
items {
id
token
endpoint_id
created_at
valid_until
}
}
}
`,
{
tokenListLimit: 100,
tokenListOffset: 0,
endpointId: endpointId as string,
},
{
fetchPolicy: 'network-only',
},
);

const newestValidToken =
_.orderBy(endpoint_token_list?.items, ['valid_until'], ['desc'])[0]
?.token ?? '';

const {
data: modelsResult,
// error,
refetch,
} = useTanQuery<{
data: Array<Model>;
}>({
queryKey: ['models', endpointUrl],
queryFn: () => {
return fetch(new URL(basePath + '/models', endpointUrl).toString(), {
headers: {
Authorization: `BackendAI ${newestValidToken}`,
},
})
.then((res) => res.json())
.catch((err) => {
console.log(err);
});
},
});

return (
<LLMChatCard
endpointId={endpointId || ''}
baseURL={new URL(basePath, endpointUrl).toString()}
models={_.map(modelsResult?.data, (m) => ({
id: m.id,
name: m.id,
}))}
apiKey={newestValidToken}
fetchOnClient
style={{ flex: 1 }}
allowCustomModel={false}
alert={
_.isEmpty(modelsResult?.data) && (
<Alert
type="warning"
showIcon
message={t('chatui.CannotFindModel')}
action={
<Button
icon={<ReloadOutlined />}
onClick={() => {
refetch();
}}
>
{t('button.Refresh')}
</Button>
}
/>
)
}
modelId={modelsResult?.data?.[0].id ?? 'custom'}
modelToken={newestValidToken}
/>
);
};

export default ChatContent;
61 changes: 61 additions & 0 deletions react/src/components/ImportFromHuggingFacePanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import Flex from '../components/Flex';
import BAICard from './BAICard';
import { App, Button, Input, theme } from 'antd';
import type { GetProps } from 'antd';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';

const ImportFromHuggingFacePanel: React.FC = () => {
const { t } = useTranslation();

Check warning on line 9 in react/src/components/ImportFromHuggingFacePanel.tsx

View workflow job for this annotation

GitHub Actions / coverage

't' is assigned a value but never used
const { token } = theme.useToken();
const { message } = App.useApp();
const [search, setSearch] = useState<string>('');

Check warning on line 12 in react/src/components/ImportFromHuggingFacePanel.tsx

View workflow job for this annotation

GitHub Actions / coverage

'search' is assigned a value but never used
type SearchProps = GetProps<typeof Input.Search>;

const { Search } = Input;
const onSearch: SearchProps['onSearch'] = (value, _e, info) => {
// TODO: download model from hugging face by URL
setSearch(value);
};

return (
<BAICard
style={{
backgroundImage:
'linear-gradient(rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.6)), url(/resources/images/model-player/hugging-face-background.jpg)',
}}
>
<Flex
direction="row"
align="center"
justify="center"
style={{ padding: '20px' }}
gap={'sm'}
>
<Search
placeholder="Import From Hugging Face"
allowClear
enterButton={
<Button
type="primary"
onClick={() => {
message.info({
key: 'import-from-hugging-face',
content: 'Only available for administrators.',
});
}}
// FIXME: Temporary use hardcoded color for the button background
style={{ color: token.colorBgBase, backgroundColor: '#FF7A00' }}
>
Download
</Button>
}
size="large"
onSearch={onSearch}
/>
</Flex>
</BAICard>
);
};

export default ImportFromHuggingFacePanel;
Loading

0 comments on commit 6dabc24

Please sign in to comment.