This commit is contained in:
josc146 2023-05-06 20:17:39 +08:00
parent f6be32825f
commit ac3e34e1d8
18 changed files with 101037 additions and 295 deletions

3
.gitignore vendored
View File

@ -3,4 +3,5 @@ node_modules
frontend/dist frontend/dist
.idea .idea
.vs .vs
package.json.md5 package.json.md5
cache.json

View File

@ -5,13 +5,13 @@ import (
"os" "os"
) )
func (a *App) SaveConfig(config interface{}) string { func (a *App) SaveJson(fileName string, jsonData interface{}) string {
jsonData, err := json.MarshalIndent(config, "", " ") text, err := json.MarshalIndent(jsonData, "", " ")
if err != nil { if err != nil {
return err.Error() 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 err.Error()
} }
return "" return ""

File diff suppressed because it is too large Load Diff

135
backend-python/main.py Normal file
View 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")

View 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

View File

@ -13,7 +13,8 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router": "^6.11.0", "react-router": "^6.11.0",
"react-router-dom": "^6.11.0" "react-router-dom": "^6.11.0",
"usehooks-ts": "^2.9.1"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.0.17", "@types/react": "^18.0.17",
@ -3315,6 +3316,22 @@
"react-dom": ">=16.8.0 <19.0.0" "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": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",

View File

@ -14,7 +14,8 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router": "^6.11.0", "react-router": "^6.11.0",
"react-router-dom": "^6.11.0" "react-router-dom": "^6.11.0",
"usehooks-ts": "^2.9.1"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.0.17", "@types/react": "^18.0.17",

View File

@ -27,10 +27,12 @@ import {FluentProvider, Tab, TabList, webDarkTheme} from '@fluentui/react-compon
import {FC, useEffect, useState} from 'react'; import {FC, useEffect, useState} from 'react';
import {Route, Routes, useLocation, useNavigate} from 'react-router'; import {Route, Routes, useLocation, useNavigate} from 'react-router';
import {pages} from './Pages'; import {pages} from './Pages';
import {useMediaQuery} from 'usehooks-ts';
const App: FC = () => { const App: FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const mq = useMediaQuery('(min-width: 640px)');
const [path, setPath] = useState<string>(pages[0].path); const [path, setPath] = useState<string>(pages[0].path);
@ -42,7 +44,7 @@ const App: FC = () => {
return ( return (
<FluentProvider theme={webDarkTheme} className="h-screen"> <FluentProvider theme={webDarkTheme} className="h-screen">
<div className="flex h-full"> <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 <TabList
size="large" size="large"
appearance="subtle" appearance="subtle"
@ -50,9 +52,9 @@ const App: FC = () => {
onTabSelect={(_, {value}) => selectTab(value)} onTabSelect={(_, {value}) => selectTab(value)}
vertical 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}> <Tab icon={icon} key={`${path}-${index}`} value={path}>
{label} {mq && label}
</Tab> </Tab>
))} ))}
</TabList> </TabList>
@ -63,9 +65,9 @@ const App: FC = () => {
onTabSelect={(_, {value}) => selectTab(value)} onTabSelect={(_, {value}) => selectTab(value)}
vertical 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}> <Tab icon={icon} key={`${path}-${index}`} value={path}>
{label} {mq && label}
</Tab> </Tab>
))} ))}
</TabList> </TabList>

View File

@ -1,269 +1,86 @@
import { import {Button, Divider, Dropdown, Input, Option, Slider, Switch, Text} from '@fluentui/react-components';
Avatar, import {AddCircle20Regular, DataUsageSettings20Regular, Delete20Regular, Save20Regular} from '@fluentui/react-icons';
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 React, {FC} from 'react'; import React, {FC} from 'react';
import {Section} from './components/Section'; import {Section} from './components/Section';
import {Labeled} from './components/Labeled'; import {Labeled} from './components/Labeled';
import {ToolTipButton} from './components/ToolTipButton'; 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 = () => { export const Configs: FC = () => {
return ( 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> <Text size={600}>Configs</Text>
<Section <Divider/>
title="Config List" <div className="flex gap-2 items-center w-full">
content={ <Dropdown style={{minWidth: 0}} className="grow"/>
<div className="flex gap-5 items-center w-full"> <ToolTipButton desc="New Config" icon={<AddCircle20Regular/>}/>
<Dropdown className="w-full"/> <ToolTipButton desc="Delete Config" icon={<Delete20Regular/>}/>
<ToolTipButton desc="New Config" icon={<AddCircle20Regular/>}/> <ToolTipButton desc="Save Config" icon={<Save20Regular/>}/>
<ToolTipButton desc="Delete Config" icon={<Delete20Regular/>}/> </div>
<ToolTipButton desc="Save Config" icon={<Save20Regular/>}/> <div className="flex flex-col h-full gap-2 overflow-y-hidden">
</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."
<Section content={
title="Default API Parameters" <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
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">
<Labeled label="API Port" desc="127.0.0.1:8000" content={ <Labeled label="API Port" desc="127.0.0.1:8000" content={
<Input type="number" min={1} max={65535} step={1}/> <Input type="number" min={1} max={65535} step={1}/>
}/> }/>
<Labeled label="Max Response Token *" content={ <Labeled label="Max Response Token *" content={
<div className="flex items-center"> <div className="flex items-center grow">
<Slider className="w-48" step={400} min={100} max={8100}/> <Slider style={{minWidth: 0}} className="grow" step={400} min={100} max={8100}/>
<Text>1000</Text> <Text>1000</Text>
</div> </div>
}/> }/>
</div>
<div className="grid grid-cols-2">
<Labeled label="Temperature *" content={ <Labeled label="Temperature *" content={
<Slider/> <Slider style={{minWidth: 0}} className="grow"/>
}/> }/>
<Labeled label="Top_P *" content={ <Labeled label="Top_P *" content={
<Slider/> <Slider style={{minWidth: 0}} className="grow"/>
}/> }/>
</div>
<div className="grid grid-cols-2">
<Labeled label="Presence Penalty *" content={ <Labeled label="Presence Penalty *" content={
<Slider/> <Slider style={{minWidth: 0}} className="grow"/>
}/> }/>
<Labeled label="Count Penalty *" content={ <Labeled label="Count Penalty *" content={
<Slider/> <Slider style={{minWidth: 0}} className="grow"/>
}/> }/>
</div> </div>
</div> }
} />
/> <Section
<Section title="Model Parameters"
title="Model Parameters" content={
content={ <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
<div className="flex flex-col gap-1"> <Labeled label="Model" content={
<div className="grid grid-cols-2"> <div className="flex gap-2 grow">
<Dropdown style={{minWidth: 0}} className="grow"/>
<ToolTipButton desc="Manage Models" icon={<DataUsageSettings20Regular/>}/>
</div>
}/>
<div/>
<Labeled label="Device" content={ <Labeled label="Device" content={
<Select className="w-28"> <Dropdown style={{minWidth: 0}} className="grow">
<option>CPU</option> <Option>CPU</Option>
<option>CUDA: 0</option> <Option>CUDA: 0</Option>
</Select> </Dropdown>
}/> }/>
<Labeled label="Precision" content={ <Labeled label="Precision" content={
<Select className="w-28"> <Dropdown style={{minWidth: 0}} className="grow">
<option>fp16</option> <Option>fp16</Option>
<option>int8</option> <Option>int8</Option>
<option>fp32</option> <Option>fp32</Option>
</Select> </Dropdown>
}/> }/>
</div>
<div className="grid grid-cols-2">
<Labeled label="Streamed Layers" content={ <Labeled label="Streamed Layers" content={
<Slider/> <Slider style={{minWidth: 0}} className="grow"/>
}/> }/>
<Labeled label="Enable High Precision For Last Layer" content={ <Labeled label="Enable High Precision For Last Layer" content={
<Switch/> <Switch/>
}/> }/>
</div> </div>
</div> }
} />
/> </div>
<div className="fixed bottom-2 right-2"> <div className="flex flex-row-reverse sm:fixed bottom-2 right-2">
<Button appearance="primary" size="large">Run</Button> <Button appearance="primary" size="large">Run</Button>
</div> </div>
</div> </div>

View File

@ -8,7 +8,7 @@ import {
Storage20Regular Storage20Regular
} from '@fluentui/react-icons'; } from '@fluentui/react-icons';
import {useNavigate} from 'react-router'; import {useNavigate} from 'react-router';
import {SaveConfig} from '../../wailsjs/go/backend_golang/App'; import {SaveJson} from '../../wailsjs/go/backend_golang/App';
type NavCard = { type NavCard = {
label: string; label: string;
@ -55,10 +55,10 @@ export const Home: FC = () => {
return ( return (
<div className="flex flex-col justify-between h-full"> <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"> <div className="flex flex-col gap-2">
<Text size={600} weight="medium">Introduction</Text> <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 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 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 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/> <br/>
So it's combining the best of RNN and transformer - great performance, fast inference, saves VRAM, fast 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). training, "infinite" ctx_len, and free sentence embedding (using the final hidden state).
</Text> </div>
</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) => ( {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)}> size="large" onClick={() => onClickNavCard(path)}>
{label} {label}
</CompoundButton> </CompoundButton>
))} ))}
</div> </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"> <div className="flex gap-4 items-end">
Version: 1.0.0 Version: 1.0.0
<Link>Help</Link> <Link>Help</Link>
</div> </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>
</div> </div>
); );

View File

@ -1,10 +1,167 @@
import React, {FC} from 'react'; import React, {FC, useEffect} from 'react';
import {Text} from '@fluentui/react-components'; 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 = () => { export const Models: FC = () => {
useEffect(() => {
fetch('https://cdn.jsdelivr.net/gh/josstorer/RWKV-Runner/manifest.json')
.then(
res => res.json().then(console.log)
);
}, []);
return ( return (
<div className="flex flex-col box-border gap-5 p-2"> <div className="flex flex-col box-border gap-5 p-2">
<Text size={600}>In Development</Text> <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> </div>
); );
}; };

View File

@ -3,12 +3,12 @@ import {Label, Tooltip} from '@fluentui/react-components';
export const Labeled: FC<{ label: string; desc?: string, content: ReactElement }> = ({label, desc, content}) => { export const Labeled: FC<{ label: string; desc?: string, content: ReactElement }> = ({label, desc, content}) => {
return ( return (
<div className="flex items-center"> <div className="grid grid-cols-2 items-center">
{desc ? {desc ?
<Tooltip content={desc} showDelay={0} hideDelay={0} relationship="description"> <Tooltip content={desc} showDelay={0} hideDelay={0} relationship="description">
<Label className="w-44">{label}</Label> <Label>{label}</Label>
</Tooltip> : </Tooltip> :
<Label className="w-44">{label}</Label> <Label>{label}</Label>
} }
{content} {content}
</div> </div>

View File

@ -1,14 +1,21 @@
import {FC, ReactElement} from 'react'; 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}) => { export const Section: FC<{
return ( title: string; desc?: string, content: ReactElement, outline?: boolean
<div className="flex flex-col gap-5"> }> =
<div className="flex flex-col gap-1"> ({title, desc, content, outline = true}) => {
<Text weight="medium">{title}</Text> return (
{desc && <Text size={100}>{desc}</Text>} <Card size="small" appearance={outline ? 'outline' : 'subtle'}>
</div> <div className="flex flex-col gap-5">
{content} <div className="flex flex-col gap-1">
</div> <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>
);
};

View File

@ -3,6 +3,25 @@
@tailwind utilities; @tailwind utilities;
body { body {
margin: 0; margin: 0;
overflow: hidden; 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;
}

View File

@ -1,4 +1,4 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
export function SaveConfig(arg1:any):Promise<string>; export function SaveJson(arg1:string,arg2:any):Promise<string>;

View File

@ -2,6 +2,6 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
export function SaveConfig(arg1) { export function SaveJson(arg1, arg2) {
return window['go']['backend_golang']['App']['SaveConfig'](arg1); return window['go']['backend_golang']['App']['SaveJson'](arg1, arg2);
} }

View File

@ -22,7 +22,7 @@ func main() {
Title: "RWKV-Runner", Title: "RWKV-Runner",
Width: 1024, Width: 1024,
Height: 640, Height: 640,
MinWidth: 1024, MinWidth: 375,
MinHeight: 640, MinHeight: 640,
AssetServer: &assetserver.Options{ AssetServer: &assetserver.Options{
Assets: assets, Assets: assets,

17
manifest.json Normal file
View 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"
}
]
}