Compare commits

..

12 Commits

Author SHA1 Message Date
josc146
1c5eae1983 update readme 2023-05-22 11:48:56 +08:00
josc146
375af3bc1a improve compatible API 2023-05-22 11:24:57 +08:00
josc146
85493da730 add compatible /v1/completions API 2023-05-22 11:18:37 +08:00
josc146
bbad153ecb code format 2023-05-22 10:52:06 +08:00
josc146
035c6ab8de typo 2023-05-22 10:08:38 +08:00
josc146
c98c32f2f6 add giteeUpdatesSource 2023-05-22 00:04:18 +08:00
josc146
b2960052d9 trim chat content 2023-05-21 23:44:56 +08:00
josc146
74ceffb32c fix completion_text 2023-05-21 23:25:58 +08:00
josc146
c40e57aafd change get-pip source 2023-05-21 23:24:20 +08:00
josc146
394a7aef42 update readme 2023-05-21 17:43:23 +08:00
josc146
927d1a78b5 fix language setting 2023-05-21 17:34:24 +08:00
josc146
39222f037d update readme 2023-05-21 17:28:51 +08:00
36 changed files with 783 additions and 481 deletions

View File

@@ -1,3 +1,83 @@
# RWKV-Runner
<p align="center">
<img src="https://github.com/josStorer/RWKV-Runner/assets/13366013/d24834b0-265d-45f5-93c0-fac1e19562af">
</p>
In development
<h1 align="center">RWKV Runner</h1>
<div align="center">
This project aims to eliminate the barriers of using large language models by automating everything for you. All you
need is a lightweight executable program of just a few megabytes. Additionally, this project provides an interface
compatible with the OpenAI API, which means that every ChatGPT client is an RWKV client.
[![license][license-image]][license-url]
[![release][release-image]][release-url]
English | [简体中文](README_ZH.md)
[Preview](#Preview) | [Download][download-url]
[license-image]: http://img.shields.io/badge/license-MIT-blue.svg
[license-url]: https://github.com/josStorer/RWKV-Runner/blob/master/LICENSE
[release-image]: https://img.shields.io/github/release/josStorer/RWKV-Runner.svg
[release-url]: https://github.com/josStorer/RWKV-Runner/releases/latest
[download-url]: https://github.com/josStorer/RWKV-Runner/releases/download/v1.0.0/RWKV-Runner_windows_x64.exe
</div>
## Features
- RWKV model management and one-click startup
- Fully compatible with the OpenAI API, making every ChatGPT client an RWKV client. After starting the model,
open http://127.0.0.1:8000/docs to view more details.
- Automatic dependency installation, requiring only a lightweight executable program
- User-friendly chat interaction interface included
- Easy-to-understand and operate parameter configuration
- Built-in model conversion tool
- Built-in download management and remote model inspection
- Multilingual localization
- Theme switching
- Automatic updates
## Todo
- Model training functionality
- CUDA operator int8 acceleration
- macOS support
- Linux support
## Related Repositories:
- RWKV-4-Raven: https://huggingface.co/BlinkDL/rwkv-4-raven/tree/main
- ChatRWKV: https://github.com/BlinkDL/ChatRWKV
- RWKV-LM: https://github.com/BlinkDL/RWKV-LM
## Preview
### Homepage
![image](https://github.com/josStorer/RWKV-Runner/assets/13366013/60efbb65-29e3-4346-a597-5bdcd099251c)
### Chat
![image](https://github.com/josStorer/RWKV-Runner/assets/13366013/6cde9c45-51bb-4dee-b1fe-746862448520)
### Configuration
![image](https://github.com/josStorer/RWKV-Runner/assets/13366013/93270a68-9d6d-4247-b6a3-e543c65a876b)
### Model Management
![image](https://github.com/josStorer/RWKV-Runner/assets/13366013/6f96fdd3-fdf5-4b78-af80-2afbd1ad173b)
### Download Management
![image](https://github.com/josStorer/RWKV-Runner/assets/13366013/6982e7ee-bace-4a88-bb47-92379185bf9d)
### Settings
![image](https://github.com/josStorer/RWKV-Runner/assets/13366013/b3b2ab46-344c-4f04-b066-1503f776eeb9)

81
README_ZH.md Normal file
View File

@@ -0,0 +1,81 @@
<p align="center">
<img src="https://github.com/josStorer/RWKV-Runner/assets/13366013/d24834b0-265d-45f5-93c0-fac1e19562af">
</p>
<h1 align="center">RWKV Runner</h1>
<div align="center">
本项目旨在消除大语言模型的使用门槛全自动为你处理一切你只需要一个仅仅几MB的可执行程序。此外本项目提供了与OpenAI
API兼容的接口这意味着一切ChatGPT客户端都是RWKV客户端。
[![license][license-image]][license-url]
[![release][release-image]][release-url]
[English](README.md) | 简体中文
[预览](#Preview) | [下载][download-url]
[license-image]: http://img.shields.io/badge/license-MIT-blue.svg
[license-url]: https://github.com/josStorer/RWKV-Runner/blob/master/LICENSE
[release-image]: https://img.shields.io/github/release/josStorer/RWKV-Runner.svg
[release-url]: https://github.com/josStorer/RWKV-Runner/releases/latest
[download-url]: https://github.com/josStorer/RWKV-Runner/releases/download/v1.0.0/RWKV-Runner_windows_x64.exe
</div>
## 功能
- RWKV模型管理一键启动
- 与OpenAI API完全兼容一切ChatGPT客户端都是RWKV客户端。启动模型后打开 http://127.0.0.1:8000/docs 查看详细内容
- 全自动依赖安装,你只需要一个轻巧的可执行程序
- 自带用户友好的聊天交互页面
- 易于理解和操作的参数配置
- 内置模型转换工具
- 内置下载管理和远程模型检视
- 多语言本地化
- 主题切换
- 自动更新
## Todo
- 模型训练功能
- CUDA算子int8提速
- macOS支持
- linux支持
## 相关仓库:
- RWKV-4-Raven: https://huggingface.co/BlinkDL/rwkv-4-raven/tree/main
- ChatRWKV: https://github.com/BlinkDL/ChatRWKV
- RWKV-LM: https://github.com/BlinkDL/RWKV-LM
## Preview
### 主页
![image](https://github.com/josStorer/RWKV-Runner/assets/13366013/9d25380a-a17b-443f-b823-86c754ebebf0)
### 聊天
![image](https://github.com/josStorer/RWKV-Runner/assets/13366013/0e66d5fa-f34a-409f-9cd4-d880815733f3)
### 配置
![image](https://github.com/josStorer/RWKV-Runner/assets/13366013/ad9921fc-7248-40a3-9e18-03445b86e4bf)
### 模型管理
![image](https://github.com/josStorer/RWKV-Runner/assets/13366013/7c36f15f-3e77-49cd-a16d-99a29f870bdf)
### 下载管理
![image](https://github.com/josStorer/RWKV-Runner/assets/13366013/32fde30b-11dd-43b9-9667-ad6975be2106)
### 设置
![image](https://github.com/josStorer/RWKV-Runner/assets/13366013/e8a0f746-9da7-48e3-b3fc-e1453ac50de2)

View File

@@ -53,5 +53,16 @@ def exit():
parent.kill()
def debug():
model = RWKV(
model="../models/RWKV-4-Raven-7B-v11-Eng49%-Chn49%-Jpn1%-Other1%-20230430-ctx8192.pth",
strategy="cuda fp16",
tokens_path="20B_tokenizer.json",
)
d = model.tokenizer.decode([])
print(d)
if __name__ == "__main__":
uvicorn.run("main:app", port=8000 if len(sys.argv) == 1 else int(sys.argv[1]))
# debug()

View File

@@ -17,10 +17,11 @@ class Message(BaseModel):
content: str
class CompletionBody(ModelConfigBody):
class ChatCompletionBody(ModelConfigBody):
messages: List[Message]
model: str = "rwkv"
stream: bool = False
stop: str = None
completion_lock = Lock()
@@ -28,7 +29,7 @@ completion_lock = Lock()
@router.post("/v1/chat/completions")
@router.post("/chat/completions")
async def completions(body: CompletionBody, request: Request):
async def chat_completions(body: ChatCompletionBody, request: Request):
model: RWKV = global_var.get(global_var.Model)
if model is None:
raise HTTPException(status.HTTP_400_BAD_REQUEST, "model not loaded")
@@ -42,9 +43,23 @@ async def completions(body: CompletionBody, request: Request):
completion_text = ""
for message in body.messages:
if message.role == "user":
completion_text += "Bob: " + message.content + "\n\n"
completion_text += (
"Bob: "
+ message.content.replace("\\n", "\n")
.replace("\r\n", "\n")
.replace("\n\n", "\n")
.strip()
+ "\n\n"
)
elif message.role == "assistant":
completion_text += "Alice: " + message.content + "\n\n"
completion_text += (
"Alice: "
+ message.content.replace("\\n", "\n")
.replace("\r\n", "\n")
.replace("\n\n", "\n")
.strip()
+ "\n\n"
)
completion_text += "Alice:"
async def eval_rwkv():
@@ -56,7 +71,9 @@ async def completions(body: CompletionBody, request: Request):
set_rwkv_config(model, body)
if body.stream:
for response, delta in rwkv_generate(
model, completion_text, stop="\n\nBob"
model,
completion_text,
stop="\n\nBob" if body.stop is None else body.stop,
):
if await request.is_disconnected():
break
@@ -93,7 +110,9 @@ async def completions(body: CompletionBody, request: Request):
else:
response = None
for response, delta in rwkv_generate(
model, completion_text, stop="\n\nBob"
model,
completion_text,
stop="\n\nBob" if body.stop is None else body.stop,
):
if await request.is_disconnected():
break
@@ -121,3 +140,90 @@ async def completions(body: CompletionBody, request: Request):
return EventSourceResponse(eval_rwkv())
else:
return await eval_rwkv().__anext__()
class CompletionBody(ModelConfigBody):
prompt: str
model: str = "rwkv"
stream: bool = False
stop: str = None
@router.post("/v1/completions")
@router.post("/completions")
async def completions(body: CompletionBody, request: Request):
model: RWKV = global_var.get(global_var.Model)
if model is None:
raise HTTPException(status.HTTP_400_BAD_REQUEST, "model not loaded")
async def eval_rwkv():
while completion_lock.locked():
await asyncio.sleep(0.1)
else:
completion_lock.acquire()
set_rwkv_config(model, global_var.get(global_var.Model_Config))
set_rwkv_config(model, body)
if body.stream:
for response, delta in rwkv_generate(
model, body.prompt, stop=body.stop
):
if await request.is_disconnected():
break
yield json.dumps(
{
"response": response,
"model": "rwkv",
"choices": [
{
"text": delta,
"index": 0,
"finish_reason": None,
}
],
}
)
if await request.is_disconnected():
completion_lock.release()
return
yield json.dumps(
{
"response": response,
"model": "rwkv",
"choices": [
{
"text": "",
"index": 0,
"finish_reason": "stop",
}
],
}
)
yield "[DONE]"
else:
response = None
for response, delta in rwkv_generate(
model, body.prompt, stop=body.stop
):
if await request.is_disconnected():
break
if await request.is_disconnected():
completion_lock.release()
return
yield {
"response": response,
"model": "rwkv",
"choices": [
{
"text": response,
"index": 0,
"finish_reason": "stop",
}
],
}
# torch_gc()
completion_lock.release()
if body.stream:
return EventSourceResponse(eval_rwkv())
else:
return await eval_rwkv().__anext__()

View File

@@ -1,4 +1,4 @@
import zhHans from './zh-hans/main.json'
import zhHans from './zh-hans/main.json';
export const resources = {
zh: {
@@ -34,4 +34,4 @@ export const resources = {
// zhHant: {
// translation: zhHant,
// },
}
};

View File

@@ -71,7 +71,7 @@
"Copy": "复制",
"Read Aloud": "朗读",
"Hello! I'm RWKV, an open-source and commercially available large language model.": "你好! 我是RWKV, 一个开源可商用的大语言模型.",
"This tools API is compatible with OpenAI API. It can be used with any ChatGPT tool you like. Go to the settings of some ChatGPT tool, replace the 'https://api.openai.com' part in the API address with '": "本工具的API与OpenAI API兼容. 因此可以配合任意你喜欢的ChatGPT工具使用. 打开某个ChatGPT工具的设置, 将API地址中的'https://api.openai.com'部分替换为'",
"This tool's API is compatible with OpenAI API. It can be used with any ChatGPT tool you like. Go to the settings of some ChatGPT tool, replace the 'https://api.openai.com' part in the API address with '": "本工具的API与OpenAI API兼容. 因此可以配合任意你喜欢的ChatGPT工具使用. 打开某个ChatGPT工具的设置, 将API地址中的'https://api.openai.com'部分替换为'",
"New Version Available": "新版本可用",
"Update": "更新",
"Please click the button in the top right corner to start the model": "请点击右上角的按钮启动模型",
@@ -96,5 +96,6 @@
"Install": "安装",
"This is the latest version": "已是最新版",
"Use Tsinghua Pip Mirrors": "使用清华大学Pip镜像源",
"Model Config Exception": "模型配置异常"
"Model Config Exception": "模型配置异常",
"Use Gitee Updates Source": "使用Gitee更新源"
}

View File

@@ -46,7 +46,8 @@ export const ReadButton: FC<{ content: string }> = observer(({content}) => {
};
return (
<ToolTipButton desc={t('Read Aloud')} size="small" appearance="subtle" icon={speaking ? <MuteIcon/> : <UnmuteIcon/>}
<ToolTipButton desc={t('Read Aloud')} size="small" appearance="subtle"
icon={speaking ? <MuteIcon /> : <UnmuteIcon />}
onClick={speaking ? stopSpeak : startSpeak} />
);
});

View File

@@ -1,6 +1,12 @@
import React, { FC, MouseEventHandler, ReactElement } from 'react';
import commonStore, { ModelStatus } from '../stores/commonStore';
import { AddToDownloadList, DepCheck, FileExists, InstallPyDep, StartServer } from '../../wailsjs/go/backend_golang/App';
import {
AddToDownloadList,
DepCheck,
FileExists,
InstallPyDep,
StartServer
} from '../../wailsjs/go/backend_golang/App';
import { Button } from '@fluentui/react-components';
import { observer } from 'mobx-react-lite';
import { exit, readRoot, switchModel, updateConfig } from '../apis';
@@ -40,8 +46,8 @@ export const RunButton: FC<{ onClickRun?: MouseEventHandler, iconMode?: boolean
commonStore.setModelStatus(ModelStatus.Starting);
const modelConfig = commonStore.getCurrentModelConfig();
let modelName = ''
let modelPath = ''
let modelName = '';
let modelPath = '';
if (modelConfig && modelConfig.modelParameters) {
modelName = modelConfig.modelParameters.modelName;
modelPath = `./${manifest.localModelDir}/${modelName}`;
@@ -66,7 +72,7 @@ export const RunButton: FC<{ onClickRun?: MouseEventHandler, iconMode?: boolean
} else if (depErrorMsg.includes('DepCheck Error')) {
toastWithButton(t('Python dependencies are incomplete, would you like to install them?'), t('Install'), () => {
InstallPyDep(commonStore.settings.cnMirror);
setTimeout(WindowShow, 1000)
setTimeout(WindowShow, 1000);
});
} else {
toast(depErrorMsg, { type: 'error' });
@@ -103,7 +109,7 @@ export const RunButton: FC<{ onClickRun?: MouseEventHandler, iconMode?: boolean
await exit(1000).catch(() => {
});
StartServer(port);
setTimeout(WindowShow, 1000)
setTimeout(WindowShow, 1000);
let timeoutCount = 6;
let loading = false;

View File

@@ -170,6 +170,7 @@ const ChatPanel: FC = observer(() => {
scrollToBottom();
if (e.data === '[DONE]') {
commonStore.conversations[answerId].done = true;
commonStore.conversations[answerId].content = commonStore.conversations[answerId].content.trim();
commonStore.setConversations(commonStore.conversations);
commonStore.setConversationsOrder([...commonStore.conversationsOrder]);
return;
@@ -322,7 +323,7 @@ export const Chat: FC = observer(() => {
</div>
</div>
<Text size={100}>
{t('This tools API is compatible with OpenAI API. It can be used with any ChatGPT tool you like. Go to the settings of some ChatGPT tool, replace the \'https://api.openai.com\' part in the API address with \'') + `http://127.0.0.1:${port}` + '\'.'}
{t('This tool\'s API is compatible with OpenAI API. It can be used with any ChatGPT tool you like. Go to the settings of some ChatGPT tool, replace the \'https://api.openai.com\' part in the API address with \'') + `http://127.0.0.1:${port}` + '\'.'}
</Text>
<Divider style={{ flexGrow: 0 }} />
<ChatPanel />

View File

@@ -633,7 +633,7 @@ export const Configs: FC = observer(() => {
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
<Labeled label={t('API Port')}
desc={t('Open the following URL with your browser to view the API documentation') + `: http://127.0.0.1:${port}/docs. ` +
t('This tools API is compatible with OpenAI API. It can be used with any ChatGPT tool you like. Go to the settings of some ChatGPT tool, replace the \'https://api.openai.com\' part in the API address with \'') + `http://127.0.0.1:${port}` + '\'.'}
t('This tool\'s API is compatible with OpenAI API. It can be used with any ChatGPT tool you like. Go to the settings of some ChatGPT tool, replace the \'https://api.openai.com\' part in the API address with \'') + `http://127.0.0.1:${port}` + '\'.'}
content={
<NumberInput value={port} min={1} max={65535} step={1}
onChange={(e, data) => {
@@ -734,7 +734,7 @@ export const Configs: FC = observer(() => {
}).catch(e => {
toast(`${t('Convert Failed')} - ${e.message || e}`, { type: 'error' });
});
setTimeout(WindowShow, 1000)
setTimeout(WindowShow, 1000);
} else {
toast(`${t('Model Not Found')} - ${modelPath}`, { type: 'error' });
}

View File

@@ -18,6 +18,7 @@ export type SettingsType = {
language: Language,
darkMode: boolean
autoUpdatesCheck: boolean
giteeUpdatesSource: boolean
cnMirror: boolean
}
@@ -64,6 +65,17 @@ export const Settings: FC = observer(() => {
checkUpdate(true);
}} />
} />
{
commonStore.settings.language === 'zh' &&
<Labeled label={t('Use Gitee Updates Source')} flex spaceBetween content={
<Switch checked={commonStore.settings.giteeUpdatesSource}
onChange={(e, data) => {
commonStore.setSettings({
giteeUpdatesSource: data.checked
});
}} />
} />
}
{
commonStore.settings.language === 'zh' &&
<Labeled label={t('Use Tsinghua Pip Mirrors')} flex spaceBetween content={

View File

@@ -9,6 +9,7 @@ import {DownloadStatus} from '../pages/Downloads';
import { SettingsType } from '../pages/Settings';
import { IntroductionContent } from '../pages/Home';
import { AboutContent } from '../pages/About';
import i18n from 'i18next';
export enum ModelStatus {
Offline,
@@ -18,43 +19,37 @@ export enum ModelStatus {
}
class CommonStore {
constructor() {
makeAutoObservable(this);
}
// global
modelStatus: ModelStatus = ModelStatus.Offline;
depComplete: boolean = false;
// home
introduction: IntroductionContent = manifest.introduction;
// chat
conversations: Conversations = {};
conversationsOrder: string[] = [];
// configs
currentModelConfigIndex: number = 0;
modelConfigs: ModelConfig[] = [];
// models
modelSourceManifestList: string = 'https://cdn.jsdelivr.net/gh/josstorer/RWKV-Runner/manifest.json;';
modelSourceList: ModelSourceItem[] = [];
// downloads
downloadList: DownloadStatus[] = [];
// settings
settings: SettingsType = {
language: getUserLanguage(),
darkMode: !isSystemLightMode(),
autoUpdatesCheck: true,
giteeUpdatesSource: getUserLanguage() === 'zh',
cnMirror: getUserLanguage() === 'zh'
};
// about
about: AboutContent = manifest.about;
constructor() {
makeAutoObservable(this);
}
getCurrentModelConfig = () => {
return this.modelConfigs[this.currentModelConfigIndex];
};
@@ -120,6 +115,9 @@ class CommonStore {
else
WindowSetLightTheme();
if (this.settings.language)
i18n.changeLanguage(this.settings.language);
if (saveConfig)
saveConfigs();
};

View File

@@ -210,13 +210,18 @@ export function bytesToKb(size: number) {
export async function checkUpdate(notifyEvenLatest: boolean = false) {
let updateUrl = '';
await fetch('https://api.github.com/repos/josstorer/RWKV-Runner/releases/latest').then((r) => {
await fetch(!commonStore.settings.giteeUpdatesSource ?
'https://api.github.com/repos/josstorer/RWKV-Runner/releases/latest' :
'https://gitee.com/api/v5/repos/josc146/RWKV-Runner/releases/latest'
).then((r) => {
if (r.ok) {
r.json().then((data) => {
if (data.tag_name) {
const versionTag = data.tag_name;
if (versionTag.replace('v', '') > manifest.version) {
updateUrl = `https://github.com/josStorer/RWKV-Runner/releases/download/${versionTag}/RWKV-Runner_windows_x64.exe`;
updateUrl = !commonStore.settings.giteeUpdatesSource ?
`https://github.com/josStorer/RWKV-Runner/releases/download/${versionTag}/RWKV-Runner_windows_x64.exe` :
`https://gitee.com/josc146/RWKV-Runner/releases/download/${versionTag}/RWKV-Runner_windows_x64.exe`;
toastWithButton(t('New Version Available') + ': ' + versionTag, t('Update'), () => {
deletePythonProgramFiles();
setTimeout(() => {

View File

@@ -59,7 +59,7 @@
"path": "backend-python/20B_tokenizer.json"
},
{
"url": "https://bootstrap.pypa.io/get-pip.py",
"url": "https://cdn.jsdelivr.net/gh/pypa/get-pip/public/get-pip.py",
"path": "backend-python/get-pip.py"
}
],