update
This commit is contained in:
parent
f6be32825f
commit
ac3e34e1d8
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@ node_modules
|
||||
frontend/dist
|
||||
.idea
|
||||
.vs
|
||||
package.json.md5
|
||||
package.json.md5
|
||||
cache.json
|
@ -5,13 +5,13 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func (a *App) SaveConfig(config interface{}) string {
|
||||
jsonData, err := json.MarshalIndent(config, "", " ")
|
||||
func (a *App) SaveJson(fileName string, jsonData interface{}) string {
|
||||
text, err := json.MarshalIndent(jsonData, "", " ")
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
if err := os.WriteFile("config.json", jsonData, 0644); err != nil {
|
||||
if err := os.WriteFile(fileName, text, 0644); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return ""
|
||||
|
100525
backend-python/20B_tokenizer.json
Normal file
100525
backend-python/20B_tokenizer.json
Normal file
File diff suppressed because it is too large
Load Diff
135
backend-python/main.py
Normal file
135
backend-python/main.py
Normal file
@ -0,0 +1,135 @@
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
from typing import List
|
||||
import os
|
||||
import sysconfig
|
||||
|
||||
from fastapi import FastAPI, Request, status, HTTPException
|
||||
from langchain.llms import RWKV
|
||||
from pydantic import BaseModel
|
||||
from sse_starlette.sse import EventSourceResponse
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
|
||||
from rwkv_helper import rwkv_generate
|
||||
|
||||
|
||||
def set_torch():
|
||||
torch_path = os.path.join(sysconfig.get_paths()["purelib"], "torch\\lib")
|
||||
paths = os.environ.get("PATH", "")
|
||||
if os.path.exists(torch_path):
|
||||
print(f"torch found: {torch_path}")
|
||||
if torch_path in paths:
|
||||
print("torch already set")
|
||||
else:
|
||||
print("run:")
|
||||
os.environ['PATH'] = paths + os.pathsep + torch_path + os.pathsep
|
||||
print(f'set Path={paths + os.pathsep + torch_path + os.pathsep}')
|
||||
else:
|
||||
print("torch not found")
|
||||
|
||||
|
||||
def torch_gc():
|
||||
import torch
|
||||
|
||||
if torch.cuda.is_available():
|
||||
with torch.cuda.device(0):
|
||||
torch.cuda.empty_cache()
|
||||
torch.cuda.ipc_collect()
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
@app.on_event('startup')
|
||||
def init():
|
||||
global model
|
||||
|
||||
set_torch()
|
||||
|
||||
model = RWKV(
|
||||
model=sys.argv[2],
|
||||
strategy=sys.argv[1],
|
||||
tokens_path=f"{pathlib.Path(__file__).parent.resolve()}/20B_tokenizer.json"
|
||||
)
|
||||
|
||||
if os.environ.get("ngrok_token") is not None:
|
||||
ngrok_connect()
|
||||
|
||||
|
||||
def ngrok_connect():
|
||||
from pyngrok import ngrok, conf
|
||||
conf.set_default(conf.PyngrokConfig(ngrok_path="./ngrok"))
|
||||
ngrok.set_auth_token(os.environ["ngrok_token"])
|
||||
http_tunnel = ngrok.connect(8000)
|
||||
print(http_tunnel.public_url)
|
||||
|
||||
|
||||
class Message(BaseModel):
|
||||
role: str
|
||||
content: str
|
||||
|
||||
|
||||
class Body(BaseModel):
|
||||
messages: List[Message]
|
||||
model: str
|
||||
stream: bool
|
||||
max_tokens: int
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World!"}
|
||||
|
||||
@app.post("update-config")
|
||||
def updateConfig(body: Body):
|
||||
pass
|
||||
|
||||
|
||||
@app.post("/v1/chat/completions")
|
||||
@app.post("/chat/completions")
|
||||
async def completions(body: Body, request: Request):
|
||||
global model
|
||||
|
||||
question = body.messages[-1]
|
||||
if question.role == 'user':
|
||||
question = question.content
|
||||
else:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, "No Question Found")
|
||||
|
||||
completion_text = ""
|
||||
for message in body.messages:
|
||||
if message.role == 'user':
|
||||
completion_text += "Bob: " + message.content + "\n\n"
|
||||
elif message.role == 'assistant':
|
||||
completion_text += "Alice: " + message.content + "\n\n"
|
||||
completion_text += "Alice:"
|
||||
|
||||
async def eval_rwkv():
|
||||
if body.stream:
|
||||
for response, delta in rwkv_generate(model, completion_text):
|
||||
if await request.is_disconnected():
|
||||
break
|
||||
yield json.dumps({"response": response, "choices": [{"delta": {"content": delta}}], "model": "rwkv"})
|
||||
yield "[DONE]"
|
||||
else:
|
||||
response = None
|
||||
for response, delta in rwkv_generate(model, completion_text):
|
||||
pass
|
||||
yield json.dumps({"response": response, "model": "rwkv"})
|
||||
# torch_gc()
|
||||
|
||||
return EventSourceResponse(eval_rwkv())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run("main:app", reload=False, app_dir="backend-python")
|
40
backend-python/rwkv_helper.py
Normal file
40
backend-python/rwkv_helper.py
Normal file
@ -0,0 +1,40 @@
|
||||
from typing import Dict
|
||||
from langchain.llms import RWKV
|
||||
|
||||
|
||||
def rwkv_generate(model: RWKV, prompt: str):
|
||||
model.model_state = None
|
||||
model.model_tokens = []
|
||||
logits = model.run_rnn(model.tokenizer.encode(prompt).ids)
|
||||
begin = len(model.model_tokens)
|
||||
out_last = begin
|
||||
|
||||
occurrence: Dict = {}
|
||||
|
||||
response = ""
|
||||
for i in range(model.max_tokens_per_generation):
|
||||
for n in occurrence:
|
||||
logits[n] -= (
|
||||
model.penalty_alpha_presence
|
||||
+ occurrence[n] * model.penalty_alpha_frequency
|
||||
)
|
||||
token = model.pipeline.sample_logits(
|
||||
logits, temperature=model.temperature, top_p=model.top_p
|
||||
)
|
||||
|
||||
END_OF_TEXT = 0
|
||||
if token == END_OF_TEXT:
|
||||
break
|
||||
if token not in occurrence:
|
||||
occurrence[token] = 1
|
||||
else:
|
||||
occurrence[token] += 1
|
||||
|
||||
logits = model.run_rnn([token])
|
||||
delta: str = model.tokenizer.decode(model.model_tokens[out_last:])
|
||||
if "\ufffd" not in delta: # avoid utf-8 display issues
|
||||
response += delta
|
||||
yield response, delta
|
||||
out_last = begin + i + 1
|
||||
if i >= model.max_tokens_per_generation - 100:
|
||||
break
|
19
frontend/package-lock.json
generated
19
frontend/package-lock.json
generated
@ -13,7 +13,8 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router": "^6.11.0",
|
||||
"react-router-dom": "^6.11.0"
|
||||
"react-router-dom": "^6.11.0",
|
||||
"usehooks-ts": "^2.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.17",
|
||||
@ -3315,6 +3316,22 @@
|
||||
"react-dom": ">=16.8.0 <19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/usehooks-ts": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmmirror.com/usehooks-ts/-/usehooks-ts-2.9.1.tgz",
|
||||
"integrity": "sha512-2FAuSIGHlY+apM9FVlj8/oNhd+1y+Uwv5QNkMQz1oSfdHk4PXo1qoCw9I5M7j0vpH8CSWFJwXbVPeYDjLCx9PA==",
|
||||
"workspaces": [
|
||||
"packages/eslint-config-custom"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.15.0",
|
||||
"npm": ">=8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
@ -14,7 +14,8 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router": "^6.11.0",
|
||||
"react-router-dom": "^6.11.0"
|
||||
"react-router-dom": "^6.11.0",
|
||||
"usehooks-ts": "^2.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.17",
|
||||
|
@ -27,10 +27,12 @@ import {FluentProvider, Tab, TabList, webDarkTheme} from '@fluentui/react-compon
|
||||
import {FC, useEffect, useState} from 'react';
|
||||
import {Route, Routes, useLocation, useNavigate} from 'react-router';
|
||||
import {pages} from './Pages';
|
||||
import {useMediaQuery} from 'usehooks-ts';
|
||||
|
||||
const App: FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const mq = useMediaQuery('(min-width: 640px)');
|
||||
|
||||
const [path, setPath] = useState<string>(pages[0].path);
|
||||
|
||||
@ -42,7 +44,7 @@ const App: FC = () => {
|
||||
return (
|
||||
<FluentProvider theme={webDarkTheme} className="h-screen">
|
||||
<div className="flex h-full">
|
||||
<div className="flex flex-col w-40 p-2 justify-between">
|
||||
<div className="flex flex-col w-16 sm:w-48 p-2 justify-between">
|
||||
<TabList
|
||||
size="large"
|
||||
appearance="subtle"
|
||||
@ -50,9 +52,9 @@ const App: FC = () => {
|
||||
onTabSelect={(_, {value}) => selectTab(value)}
|
||||
vertical
|
||||
>
|
||||
{pages.filter(page=>page.top).map(({label, path, icon}, index) => (
|
||||
{pages.filter(page => page.top).map(({label, path, icon}, index) => (
|
||||
<Tab icon={icon} key={`${path}-${index}`} value={path}>
|
||||
{label}
|
||||
{mq && label}
|
||||
</Tab>
|
||||
))}
|
||||
</TabList>
|
||||
@ -63,9 +65,9 @@ const App: FC = () => {
|
||||
onTabSelect={(_, {value}) => selectTab(value)}
|
||||
vertical
|
||||
>
|
||||
{pages.filter(page=>!page.top).map(({label, path, icon}, index) => (
|
||||
{pages.filter(page => !page.top).map(({label, path, icon}, index) => (
|
||||
<Tab icon={icon} key={`${path}-${index}`} value={path}>
|
||||
{label}
|
||||
{mq && label}
|
||||
</Tab>
|
||||
))}
|
||||
</TabList>
|
||||
|
@ -1,269 +1,86 @@
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
createTableColumn,
|
||||
Dropdown,
|
||||
Input,
|
||||
PresenceBadgeStatus,
|
||||
Select,
|
||||
Slider,
|
||||
Switch,
|
||||
TableCellLayout,
|
||||
TableColumnDefinition,
|
||||
Text,
|
||||
Tooltip
|
||||
} from '@fluentui/react-components';
|
||||
import {
|
||||
AddCircle20Regular,
|
||||
Delete20Regular,
|
||||
DocumentPdfRegular,
|
||||
DocumentRegular,
|
||||
EditRegular,
|
||||
FolderRegular,
|
||||
OpenRegular,
|
||||
PeopleRegular,
|
||||
Save20Regular,
|
||||
VideoRegular
|
||||
} from '@fluentui/react-icons';
|
||||
import {Button, Divider, Dropdown, Input, Option, Slider, Switch, Text} from '@fluentui/react-components';
|
||||
import {AddCircle20Regular, DataUsageSettings20Regular, Delete20Regular, Save20Regular} from '@fluentui/react-icons';
|
||||
import React, {FC} from 'react';
|
||||
import {Section} from './components/Section';
|
||||
import {Labeled} from './components/Labeled';
|
||||
import {ToolTipButton} from './components/ToolTipButton';
|
||||
|
||||
type FileCell = {
|
||||
label: string;
|
||||
icon: JSX.Element;
|
||||
};
|
||||
|
||||
type LastUpdatedCell = {
|
||||
label: string;
|
||||
timestamp: number;
|
||||
};
|
||||
|
||||
type LastUpdateCell = {
|
||||
label: string;
|
||||
icon: JSX.Element;
|
||||
};
|
||||
|
||||
type AuthorCell = {
|
||||
label: string;
|
||||
status: PresenceBadgeStatus;
|
||||
};
|
||||
|
||||
type Item = {
|
||||
file: FileCell;
|
||||
author: AuthorCell;
|
||||
lastUpdated: LastUpdatedCell;
|
||||
lastUpdate: LastUpdateCell;
|
||||
};
|
||||
|
||||
const items: Item[] = [
|
||||
{
|
||||
file: {label: 'Meeting notes', icon: <DocumentRegular/>},
|
||||
author: {label: 'Max Mustermann', status: 'available'},
|
||||
lastUpdated: {label: '7h ago', timestamp: 1},
|
||||
lastUpdate: {
|
||||
label: 'You edited this',
|
||||
icon: <EditRegular/>
|
||||
}
|
||||
},
|
||||
{
|
||||
file: {label: 'Thursday presentation', icon: <FolderRegular/>},
|
||||
author: {label: 'Erika Mustermann', status: 'busy'},
|
||||
lastUpdated: {label: 'Yesterday at 1:45 PM', timestamp: 2},
|
||||
lastUpdate: {
|
||||
label: 'You recently opened this',
|
||||
icon: <OpenRegular/>
|
||||
}
|
||||
},
|
||||
{
|
||||
file: {label: 'Training recording', icon: <VideoRegular/>},
|
||||
author: {label: 'John Doe', status: 'away'},
|
||||
lastUpdated: {label: 'Yesterday at 1:45 PM', timestamp: 2},
|
||||
lastUpdate: {
|
||||
label: 'You recently opened this',
|
||||
icon: <OpenRegular/>
|
||||
}
|
||||
},
|
||||
{
|
||||
file: {label: 'Purchase order', icon: <DocumentPdfRegular/>},
|
||||
author: {label: 'Jane Doe', status: 'offline'},
|
||||
lastUpdated: {label: 'Tue at 9:30 AM', timestamp: 3},
|
||||
lastUpdate: {
|
||||
label: 'You shared this in a Teams chat',
|
||||
icon: <PeopleRegular/>
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const columns: TableColumnDefinition<Item>[] = [
|
||||
createTableColumn<Item>({
|
||||
columnId: 'file',
|
||||
compare: (a, b) => {
|
||||
return a.file.label.localeCompare(b.file.label);
|
||||
},
|
||||
renderHeaderCell: () => {
|
||||
return 'File';
|
||||
},
|
||||
renderCell: (item) => {
|
||||
return (
|
||||
<TableCellLayout media={item.file.icon}>
|
||||
{item.file.label}
|
||||
</TableCellLayout>
|
||||
);
|
||||
}
|
||||
}),
|
||||
createTableColumn<Item>({
|
||||
columnId: 'author',
|
||||
compare: (a, b) => {
|
||||
return a.author.label.localeCompare(b.author.label);
|
||||
},
|
||||
renderHeaderCell: () => {
|
||||
return 'Author';
|
||||
},
|
||||
renderCell: (item) => {
|
||||
return (
|
||||
<TableCellLayout
|
||||
media={
|
||||
<Avatar
|
||||
aria-label={item.author.label}
|
||||
name={item.author.label}
|
||||
badge={{status: item.author.status}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{item.author.label}
|
||||
</TableCellLayout>
|
||||
);
|
||||
}
|
||||
}),
|
||||
createTableColumn<Item>({
|
||||
columnId: 'lastUpdated',
|
||||
compare: (a, b) => {
|
||||
return a.lastUpdated.timestamp - b.lastUpdated.timestamp;
|
||||
},
|
||||
renderHeaderCell: () => {
|
||||
return 'Last updated';
|
||||
},
|
||||
|
||||
renderCell: (item) => {
|
||||
return item.lastUpdated.label;
|
||||
}
|
||||
}),
|
||||
createTableColumn<Item>({
|
||||
columnId: 'lastUpdate',
|
||||
compare: (a, b) => {
|
||||
return a.lastUpdate.label.localeCompare(b.lastUpdate.label);
|
||||
},
|
||||
renderHeaderCell: () => {
|
||||
return 'Last update';
|
||||
},
|
||||
renderCell: (item) => {
|
||||
return (
|
||||
<TableCellLayout media={item.lastUpdate.icon}>
|
||||
{item.lastUpdate.label}
|
||||
</TableCellLayout>
|
||||
);
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
// <DataGrid
|
||||
// items={items}
|
||||
// columns={columns}
|
||||
// >
|
||||
// <DataGridBody<Item>>
|
||||
// {({ item, rowId }) => (
|
||||
// <DataGridRow<Item> key={rowId}>
|
||||
// {({ renderCell }) => (
|
||||
// <DataGridCell>{renderCell(item)}</DataGridCell>
|
||||
// )}
|
||||
// </DataGridRow>
|
||||
// )}
|
||||
// </DataGridBody>
|
||||
// </DataGrid>
|
||||
|
||||
export const Configs: FC = () => {
|
||||
return (
|
||||
<div className="flex flex-col box-border gap-5 p-2">
|
||||
<div className="flex flex-col gap-2 p-2 h-full">
|
||||
<Text size={600}>Configs</Text>
|
||||
<Section
|
||||
title="Config List"
|
||||
content={
|
||||
<div className="flex gap-5 items-center w-full">
|
||||
<Dropdown className="w-full"/>
|
||||
<ToolTipButton desc="New Config" icon={<AddCircle20Regular/>}/>
|
||||
<ToolTipButton desc="Delete Config" icon={<Delete20Regular/>}/>
|
||||
<ToolTipButton desc="Save Config" icon={<Save20Regular/>}/>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<Section
|
||||
title="Default API Parameters"
|
||||
desc="Hover your mouse over the text to view a detailed description. Settings marked with * will take effect immediately after being saved."
|
||||
content={
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="grid grid-cols-2">
|
||||
<Divider/>
|
||||
<div className="flex gap-2 items-center w-full">
|
||||
<Dropdown style={{minWidth: 0}} className="grow"/>
|
||||
<ToolTipButton desc="New Config" icon={<AddCircle20Regular/>}/>
|
||||
<ToolTipButton desc="Delete Config" icon={<Delete20Regular/>}/>
|
||||
<ToolTipButton desc="Save Config" icon={<Save20Regular/>}/>
|
||||
</div>
|
||||
<div className="flex flex-col h-full gap-2 overflow-y-hidden">
|
||||
<Section
|
||||
title="Default API Parameters"
|
||||
desc="Hover your mouse over the text to view a detailed description. Settings marked with * will take effect immediately after being saved."
|
||||
content={
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||
<Labeled label="API Port" desc="127.0.0.1:8000" content={
|
||||
<Input type="number" min={1} max={65535} step={1}/>
|
||||
}/>
|
||||
<Labeled label="Max Response Token *" content={
|
||||
<div className="flex items-center">
|
||||
<Slider className="w-48" step={400} min={100} max={8100}/>
|
||||
<div className="flex items-center grow">
|
||||
<Slider style={{minWidth: 0}} className="grow" step={400} min={100} max={8100}/>
|
||||
<Text>1000</Text>
|
||||
</div>
|
||||
}/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2">
|
||||
<Labeled label="Temperature *" content={
|
||||
<Slider/>
|
||||
<Slider style={{minWidth: 0}} className="grow"/>
|
||||
}/>
|
||||
<Labeled label="Top_P *" content={
|
||||
<Slider/>
|
||||
<Slider style={{minWidth: 0}} className="grow"/>
|
||||
}/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2">
|
||||
<Labeled label="Presence Penalty *" content={
|
||||
<Slider/>
|
||||
<Slider style={{minWidth: 0}} className="grow"/>
|
||||
}/>
|
||||
<Labeled label="Count Penalty *" content={
|
||||
<Slider/>
|
||||
<Slider style={{minWidth: 0}} className="grow"/>
|
||||
}/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<Section
|
||||
title="Model Parameters"
|
||||
content={
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="grid grid-cols-2">
|
||||
}
|
||||
/>
|
||||
<Section
|
||||
title="Model Parameters"
|
||||
content={
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||
<Labeled label="Model" content={
|
||||
<div className="flex gap-2 grow">
|
||||
<Dropdown style={{minWidth: 0}} className="grow"/>
|
||||
<ToolTipButton desc="Manage Models" icon={<DataUsageSettings20Regular/>}/>
|
||||
</div>
|
||||
}/>
|
||||
<div/>
|
||||
<Labeled label="Device" content={
|
||||
<Select className="w-28">
|
||||
<option>CPU</option>
|
||||
<option>CUDA: 0</option>
|
||||
</Select>
|
||||
<Dropdown style={{minWidth: 0}} className="grow">
|
||||
<Option>CPU</Option>
|
||||
<Option>CUDA: 0</Option>
|
||||
</Dropdown>
|
||||
}/>
|
||||
<Labeled label="Precision" content={
|
||||
<Select className="w-28">
|
||||
<option>fp16</option>
|
||||
<option>int8</option>
|
||||
<option>fp32</option>
|
||||
</Select>
|
||||
<Dropdown style={{minWidth: 0}} className="grow">
|
||||
<Option>fp16</Option>
|
||||
<Option>int8</Option>
|
||||
<Option>fp32</Option>
|
||||
</Dropdown>
|
||||
}/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2">
|
||||
<Labeled label="Streamed Layers" content={
|
||||
<Slider/>
|
||||
<Slider style={{minWidth: 0}} className="grow"/>
|
||||
}/>
|
||||
<Labeled label="Enable High Precision For Last Layer" content={
|
||||
<Switch/>
|
||||
}/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<div className="fixed bottom-2 right-2">
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row-reverse sm:fixed bottom-2 right-2">
|
||||
<Button appearance="primary" size="large">Run</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
Storage20Regular
|
||||
} from '@fluentui/react-icons';
|
||||
import {useNavigate} from 'react-router';
|
||||
import {SaveConfig} from '../../wailsjs/go/backend_golang/App';
|
||||
import {SaveJson} from '../../wailsjs/go/backend_golang/App';
|
||||
|
||||
type NavCard = {
|
||||
label: string;
|
||||
@ -55,10 +55,10 @@ export const Home: FC = () => {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col justify-between h-full">
|
||||
<img className="rounded-xl select-none" src={Banner}/>
|
||||
<img className="rounded-xl select-none hidden sm:block" src={Banner}/>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Text size={600} weight="medium">Introduction</Text>
|
||||
<Text size={300}>
|
||||
<div className="h-40 overflow-y-auto">
|
||||
RWKV is an RNN with Transformer-level LLM performance, which can also be directly trained like a GPT
|
||||
transformer (parallelizable). And it's 100% attention-free. You only need the hidden state at position t to
|
||||
compute the state at position t+1. You can use the "GPT" mode to quickly compute the hidden state for the
|
||||
@ -66,43 +66,47 @@ export const Home: FC = () => {
|
||||
<br/>
|
||||
So it's combining the best of RNN and transformer - great performance, fast inference, saves VRAM, fast
|
||||
training, "infinite" ctx_len, and free sentence embedding (using the final hidden state).
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-5">
|
||||
{navCards.map(({label, path, icon, desc}, index) => (
|
||||
<CompoundButton className="w-1/5" icon={icon} secondaryContent={desc} key={`${path}-${index}`} value={path}
|
||||
<CompoundButton icon={icon} secondaryContent={desc} key={`${path}-${index}`} value={path}
|
||||
size="large" onClick={() => onClickNavCard(path)}>
|
||||
{label}
|
||||
</CompoundButton>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-row-reverse sm:fixed bottom-2 right-2">
|
||||
<div className="flex gap-3">
|
||||
<Dropdown style={{minWidth: 0}}
|
||||
placeholder="Config"
|
||||
value={selectedConfig}
|
||||
onOptionSelect={(_, data) => {
|
||||
if (data.optionValue)
|
||||
setSelectedConfig(data.optionValue);
|
||||
}}>
|
||||
<Option id="item-1" key="item-1">
|
||||
RWKV-3B-4G MEM
|
||||
</Option>
|
||||
<Option id="item-2" key="item-2">
|
||||
Item 2
|
||||
</Option>
|
||||
<Option id="item-3" key="item-3">
|
||||
Item 3
|
||||
</Option>
|
||||
<Option id="item-4" key="item-4">
|
||||
Item 4
|
||||
</Option>
|
||||
</Dropdown>
|
||||
<Button appearance="primary" size="large"
|
||||
onClick={() => SaveJson('config.json', {a: 1234, b: 'test'})}>Run</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-4 items-end">
|
||||
Version: 1.0.0
|
||||
<Link>Help</Link>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<Dropdown placeholder="Config"
|
||||
value={selectedConfig}
|
||||
onOptionSelect={(_, data) => {
|
||||
if (data.optionValue)
|
||||
setSelectedConfig(data.optionValue);
|
||||
}}>
|
||||
<Option id="item-1" key="item-1">
|
||||
RWKV-3B-4G MEM
|
||||
</Option>
|
||||
<Option id="item-2" key="item-2">
|
||||
Item 2
|
||||
</Option>
|
||||
<Option id="item-3" key="item-3">
|
||||
Item 3
|
||||
</Option>
|
||||
<Option id="item-4" key="item-4">
|
||||
Item 4
|
||||
</Option>
|
||||
</Dropdown>
|
||||
<Button appearance="primary" size="large" onClick={() => SaveConfig({a: 1234, b: 'test'})}>Run</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,10 +1,167 @@
|
||||
import React, {FC} from 'react';
|
||||
import {Text} from '@fluentui/react-components';
|
||||
import React, {FC, useEffect} from 'react';
|
||||
import {
|
||||
createTableColumn,
|
||||
DataGrid,
|
||||
DataGridBody,
|
||||
DataGridCell,
|
||||
DataGridHeader,
|
||||
DataGridHeaderCell,
|
||||
DataGridRow,
|
||||
TableCellLayout,
|
||||
TableColumnDefinition,
|
||||
Text,
|
||||
Textarea
|
||||
} from '@fluentui/react-components';
|
||||
import {EditRegular} from '@fluentui/react-icons/lib/fonts';
|
||||
import {ToolTipButton} from './components/ToolTipButton';
|
||||
import {ArrowClockwise20Regular} from '@fluentui/react-icons';
|
||||
|
||||
type Operation = {
|
||||
icon: JSX.Element;
|
||||
desc: string
|
||||
}
|
||||
|
||||
type Item = {
|
||||
filename: string;
|
||||
desc: string;
|
||||
size: number;
|
||||
lastUpdated: number;
|
||||
actions: Operation[];
|
||||
isLocal: boolean;
|
||||
};
|
||||
|
||||
const items: Item[] = [
|
||||
{
|
||||
filename: 'RWKV-4-Raven-14B-v11x-Eng99%-Other1%-20230501-ctx8192.pth',
|
||||
desc: 'Mainly English language corpus',
|
||||
size: 28297309490,
|
||||
lastUpdated: 1,
|
||||
actions: [{icon: <EditRegular/>, desc: 'Edit'}],
|
||||
isLocal: false
|
||||
}
|
||||
];
|
||||
|
||||
const columns: TableColumnDefinition<Item>[] = [
|
||||
createTableColumn<Item>({
|
||||
columnId: 'file',
|
||||
compare: (a, b) => {
|
||||
return a.filename.localeCompare(b.filename);
|
||||
},
|
||||
renderHeaderCell: () => {
|
||||
return 'File';
|
||||
},
|
||||
renderCell: (item) => {
|
||||
return (
|
||||
<TableCellLayout className="break-all">
|
||||
{item.filename}
|
||||
</TableCellLayout>
|
||||
);
|
||||
}
|
||||
}),
|
||||
createTableColumn<Item>({
|
||||
columnId: 'desc',
|
||||
compare: (a, b) => {
|
||||
return a.desc.localeCompare(b.desc);
|
||||
},
|
||||
renderHeaderCell: () => {
|
||||
return 'Desc';
|
||||
},
|
||||
renderCell: (item) => {
|
||||
return (
|
||||
<TableCellLayout>
|
||||
{item.desc}
|
||||
</TableCellLayout>
|
||||
);
|
||||
}
|
||||
}),
|
||||
createTableColumn<Item>({
|
||||
columnId: 'size',
|
||||
compare: (a, b) => {
|
||||
return a.size - b.size;
|
||||
},
|
||||
renderHeaderCell: () => {
|
||||
return 'Size';
|
||||
},
|
||||
renderCell: (item) => {
|
||||
return (
|
||||
<TableCellLayout>
|
||||
{(item.size / (1024 * 1024 * 1024)).toFixed(2) + 'GB'}
|
||||
</TableCellLayout>
|
||||
);
|
||||
}
|
||||
}),
|
||||
createTableColumn<Item>({
|
||||
columnId: 'lastUpdated',
|
||||
compare: (a, b) => {
|
||||
return a.lastUpdated - b.lastUpdated;
|
||||
},
|
||||
renderHeaderCell: () => {
|
||||
return 'Last updated';
|
||||
},
|
||||
|
||||
renderCell: (item) => {
|
||||
return new Date(item.lastUpdated).toLocaleString();
|
||||
}
|
||||
}),
|
||||
createTableColumn<Item>({
|
||||
columnId: 'actions',
|
||||
compare: (a, b) => {
|
||||
return a.isLocal === b.isLocal ? 0 : a.isLocal ? -1 : 1;
|
||||
},
|
||||
renderHeaderCell: () => {
|
||||
return 'Actions';
|
||||
},
|
||||
renderCell: (item) => {
|
||||
return (
|
||||
<TableCellLayout>
|
||||
</TableCellLayout>
|
||||
);
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
export const Models: FC = () => {
|
||||
useEffect(() => {
|
||||
fetch('https://cdn.jsdelivr.net/gh/josstorer/RWKV-Runner/manifest.json')
|
||||
.then(
|
||||
res => res.json().then(console.log)
|
||||
);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col box-border gap-5 p-2">
|
||||
<Text size={600}>In Development</Text>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex justify-between">
|
||||
<Text weight="medium">Model Source Url List</Text>
|
||||
<ToolTipButton desc="Refresh" icon={<ArrowClockwise20Regular/>}/>
|
||||
</div>
|
||||
<Text size={100}>description</Text>
|
||||
<Textarea size="large" resize="vertical"
|
||||
defaultValue="https://cdn.jsdelivr.net/gh/josstorer/RWKV-Runner/manifest.json;"/>
|
||||
</div>
|
||||
<DataGrid
|
||||
items={items}
|
||||
columns={columns}
|
||||
sortable={true}
|
||||
>
|
||||
<DataGridHeader>
|
||||
<DataGridRow>
|
||||
{({renderHeaderCell}) => (
|
||||
<DataGridHeaderCell>{renderHeaderCell()}</DataGridHeaderCell>
|
||||
)}
|
||||
</DataGridRow>
|
||||
</DataGridHeader>
|
||||
<DataGridBody<Item>>
|
||||
{({item, rowId}) => (
|
||||
<DataGridRow<Item> key={rowId}>
|
||||
{({renderCell}) => (
|
||||
<DataGridCell>{renderCell(item)}</DataGridCell>
|
||||
)}
|
||||
</DataGridRow>
|
||||
)}
|
||||
</DataGridBody>
|
||||
</DataGrid>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -3,12 +3,12 @@ import {Label, Tooltip} from '@fluentui/react-components';
|
||||
|
||||
export const Labeled: FC<{ label: string; desc?: string, content: ReactElement }> = ({label, desc, content}) => {
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<div className="grid grid-cols-2 items-center">
|
||||
{desc ?
|
||||
<Tooltip content={desc} showDelay={0} hideDelay={0} relationship="description">
|
||||
<Label className="w-44">{label}</Label>
|
||||
<Label>{label}</Label>
|
||||
</Tooltip> :
|
||||
<Label className="w-44">{label}</Label>
|
||||
<Label>{label}</Label>
|
||||
}
|
||||
{content}
|
||||
</div>
|
||||
|
@ -1,14 +1,21 @@
|
||||
import {FC, ReactElement} from 'react';
|
||||
import {Text} from '@fluentui/react-components';
|
||||
import {Card, Text} from '@fluentui/react-components';
|
||||
|
||||
export const Section: FC<{ title: string; desc?: string, content: ReactElement }> = ({title, desc, content}) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-5">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text weight="medium">{title}</Text>
|
||||
{desc && <Text size={100}>{desc}</Text>}
|
||||
</div>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export const Section: FC<{
|
||||
title: string; desc?: string, content: ReactElement, outline?: boolean
|
||||
}> =
|
||||
({title, desc, content, outline = true}) => {
|
||||
return (
|
||||
<Card size="small" appearance={outline ? 'outline' : 'subtle'}>
|
||||
<div className="flex flex-col gap-5">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text weight="medium">{title}</Text>
|
||||
{desc && <Text size={100}>{desc}</Text>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="overflow-y-auto overflow-x-hidden p-1">
|
||||
{content}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
@ -3,6 +3,25 @@
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
/* Works on Chrome, Edge, and Safari */
|
||||
*::-webkit-scrollbar {
|
||||
width: 9px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(155, 155, 155, 0.5);
|
||||
border-radius: 20px;
|
||||
border: transparent;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
2
frontend/wailsjs/go/backend_golang/App.d.ts
vendored
2
frontend/wailsjs/go/backend_golang/App.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export function SaveConfig(arg1:any):Promise<string>;
|
||||
export function SaveJson(arg1:string,arg2:any):Promise<string>;
|
||||
|
@ -2,6 +2,6 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export function SaveConfig(arg1) {
|
||||
return window['go']['backend_golang']['App']['SaveConfig'](arg1);
|
||||
export function SaveJson(arg1, arg2) {
|
||||
return window['go']['backend_golang']['App']['SaveJson'](arg1, arg2);
|
||||
}
|
||||
|
2
main.go
2
main.go
@ -22,7 +22,7 @@ func main() {
|
||||
Title: "RWKV-Runner",
|
||||
Width: 1024,
|
||||
Height: 640,
|
||||
MinWidth: 1024,
|
||||
MinWidth: 375,
|
||||
MinHeight: 640,
|
||||
AssetServer: &assetserver.Options{
|
||||
Assets: assets,
|
||||
|
17
manifest.json
Normal file
17
manifest.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"models": [
|
||||
{
|
||||
"name": "RWKV-4-Raven-14B-v11x-Eng99%-Other1%-20230501-ctx8192.pth",
|
||||
"desc": {
|
||||
"en": "Mainly English language corpus",
|
||||
"zh": "英语语料为主"
|
||||
},
|
||||
"size": 28297309490,
|
||||
"lastUpdated": "2023-05-02T09:43:33",
|
||||
"url": "https://huggingface.co/BlinkDL/rwkv-4-raven/blob/main/RWKV-4-Raven-14B-v11x-Eng99%25-Other1%25-20230501-ctx8192.pth",
|
||||
"downloadUrl": "https://huggingface.co/BlinkDL/rwkv-4-raven/resolve/main/RWKV-4-Raven-14B-v11x-Eng99%25-Other1%25-20230501-ctx8192.pth",
|
||||
"SHA256": "c4bc72406c3c62613e8e2592e8d07ac045f8a88381c728f8eb60af890e299f4d"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user