Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c5eae1983 | ||
|
|
375af3bc1a | ||
|
|
85493da730 | ||
|
|
bbad153ecb | ||
|
|
035c6ab8de | ||
|
|
c98c32f2f6 | ||
|
|
b2960052d9 | ||
|
|
74ceffb32c | ||
|
|
c40e57aafd | ||
|
|
394a7aef42 | ||
|
|
927d1a78b5 | ||
|
|
39222f037d |
84
README.md
84
README.md
@@ -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
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Chat
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Model Management
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Download Management
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Settings
|
||||||
|
|
||||||
|

|
||||||
|
|||||||
81
README_ZH.md
Normal file
81
README_ZH.md
Normal 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
|
||||||
|
|
||||||
|
### 主页
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 聊天
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 配置
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 模型管理
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 下载管理
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 设置
|
||||||
|
|
||||||
|

|
||||||
@@ -53,5 +53,16 @@ def exit():
|
|||||||
parent.kill()
|
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__":
|
if __name__ == "__main__":
|
||||||
uvicorn.run("main:app", port=8000 if len(sys.argv) == 1 else int(sys.argv[1]))
|
uvicorn.run("main:app", port=8000 if len(sys.argv) == 1 else int(sys.argv[1]))
|
||||||
|
# debug()
|
||||||
|
|||||||
@@ -17,10 +17,11 @@ class Message(BaseModel):
|
|||||||
content: str
|
content: str
|
||||||
|
|
||||||
|
|
||||||
class CompletionBody(ModelConfigBody):
|
class ChatCompletionBody(ModelConfigBody):
|
||||||
messages: List[Message]
|
messages: List[Message]
|
||||||
model: str = "rwkv"
|
model: str = "rwkv"
|
||||||
stream: bool = False
|
stream: bool = False
|
||||||
|
stop: str = None
|
||||||
|
|
||||||
|
|
||||||
completion_lock = Lock()
|
completion_lock = Lock()
|
||||||
@@ -28,7 +29,7 @@ completion_lock = Lock()
|
|||||||
|
|
||||||
@router.post("/v1/chat/completions")
|
@router.post("/v1/chat/completions")
|
||||||
@router.post("/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)
|
model: RWKV = global_var.get(global_var.Model)
|
||||||
if model is None:
|
if model is None:
|
||||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, "model not loaded")
|
raise HTTPException(status.HTTP_400_BAD_REQUEST, "model not loaded")
|
||||||
@@ -42,9 +43,23 @@ async def completions(body: CompletionBody, request: Request):
|
|||||||
completion_text = ""
|
completion_text = ""
|
||||||
for message in body.messages:
|
for message in body.messages:
|
||||||
if message.role == "user":
|
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":
|
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:"
|
completion_text += "Alice:"
|
||||||
|
|
||||||
async def eval_rwkv():
|
async def eval_rwkv():
|
||||||
@@ -56,7 +71,9 @@ async def completions(body: CompletionBody, request: Request):
|
|||||||
set_rwkv_config(model, body)
|
set_rwkv_config(model, body)
|
||||||
if body.stream:
|
if body.stream:
|
||||||
for response, delta in rwkv_generate(
|
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():
|
if await request.is_disconnected():
|
||||||
break
|
break
|
||||||
@@ -93,7 +110,9 @@ async def completions(body: CompletionBody, request: Request):
|
|||||||
else:
|
else:
|
||||||
response = None
|
response = None
|
||||||
for response, delta in rwkv_generate(
|
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():
|
if await request.is_disconnected():
|
||||||
break
|
break
|
||||||
@@ -121,3 +140,90 @@ async def completions(body: CompletionBody, request: Request):
|
|||||||
return EventSourceResponse(eval_rwkv())
|
return EventSourceResponse(eval_rwkv())
|
||||||
else:
|
else:
|
||||||
return await eval_rwkv().__anext__()
|
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__()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import zhHans from './zh-hans/main.json'
|
import zhHans from './zh-hans/main.json';
|
||||||
|
|
||||||
export const resources = {
|
export const resources = {
|
||||||
zh: {
|
zh: {
|
||||||
@@ -34,4 +34,4 @@ export const resources = {
|
|||||||
// zhHant: {
|
// zhHant: {
|
||||||
// translation: zhHant,
|
// translation: zhHant,
|
||||||
// },
|
// },
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
"Copy": "复制",
|
"Copy": "复制",
|
||||||
"Read Aloud": "朗读",
|
"Read Aloud": "朗读",
|
||||||
"Hello! I'm RWKV, an open-source and commercially available large language model.": "你好! 我是RWKV, 一个开源可商用的大语言模型.",
|
"Hello! I'm RWKV, an open-source and commercially available large language model.": "你好! 我是RWKV, 一个开源可商用的大语言模型.",
|
||||||
"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'部分替换为'",
|
"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": "新版本可用",
|
"New Version Available": "新版本可用",
|
||||||
"Update": "更新",
|
"Update": "更新",
|
||||||
"Please click the button in the top right corner to start the model": "请点击右上角的按钮启动模型",
|
"Please click the button in the top right corner to start the model": "请点击右上角的按钮启动模型",
|
||||||
@@ -96,5 +96,6 @@
|
|||||||
"Install": "安装",
|
"Install": "安装",
|
||||||
"This is the latest version": "已是最新版",
|
"This is the latest version": "已是最新版",
|
||||||
"Use Tsinghua Pip Mirrors": "使用清华大学Pip镜像源",
|
"Use Tsinghua Pip Mirrors": "使用清华大学Pip镜像源",
|
||||||
"Model Config Exception": "模型配置异常"
|
"Model Config Exception": "模型配置异常",
|
||||||
|
"Use Gitee Updates Source": "使用Gitee更新源"
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,8 @@ export const ReadButton: FC<{ content: string }> = observer(({content}) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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} />
|
onClick={speaking ? stopSpeak : startSpeak} />
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import React, { FC, MouseEventHandler, ReactElement } from 'react';
|
import React, { FC, MouseEventHandler, ReactElement } from 'react';
|
||||||
import commonStore, { ModelStatus } from '../stores/commonStore';
|
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 { Button } from '@fluentui/react-components';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { observer } from 'mobx-react-lite';
|
||||||
import { exit, readRoot, switchModel, updateConfig } from '../apis';
|
import { exit, readRoot, switchModel, updateConfig } from '../apis';
|
||||||
@@ -40,8 +46,8 @@ export const RunButton: FC<{ onClickRun?: MouseEventHandler, iconMode?: boolean
|
|||||||
commonStore.setModelStatus(ModelStatus.Starting);
|
commonStore.setModelStatus(ModelStatus.Starting);
|
||||||
|
|
||||||
const modelConfig = commonStore.getCurrentModelConfig();
|
const modelConfig = commonStore.getCurrentModelConfig();
|
||||||
let modelName = ''
|
let modelName = '';
|
||||||
let modelPath = ''
|
let modelPath = '';
|
||||||
if (modelConfig && modelConfig.modelParameters) {
|
if (modelConfig && modelConfig.modelParameters) {
|
||||||
modelName = modelConfig.modelParameters.modelName;
|
modelName = modelConfig.modelParameters.modelName;
|
||||||
modelPath = `./${manifest.localModelDir}/${modelName}`;
|
modelPath = `./${manifest.localModelDir}/${modelName}`;
|
||||||
@@ -66,7 +72,7 @@ export const RunButton: FC<{ onClickRun?: MouseEventHandler, iconMode?: boolean
|
|||||||
} else if (depErrorMsg.includes('DepCheck Error')) {
|
} else if (depErrorMsg.includes('DepCheck Error')) {
|
||||||
toastWithButton(t('Python dependencies are incomplete, would you like to install them?'), t('Install'), () => {
|
toastWithButton(t('Python dependencies are incomplete, would you like to install them?'), t('Install'), () => {
|
||||||
InstallPyDep(commonStore.settings.cnMirror);
|
InstallPyDep(commonStore.settings.cnMirror);
|
||||||
setTimeout(WindowShow, 1000)
|
setTimeout(WindowShow, 1000);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast(depErrorMsg, { type: 'error' });
|
toast(depErrorMsg, { type: 'error' });
|
||||||
@@ -103,7 +109,7 @@ export const RunButton: FC<{ onClickRun?: MouseEventHandler, iconMode?: boolean
|
|||||||
await exit(1000).catch(() => {
|
await exit(1000).catch(() => {
|
||||||
});
|
});
|
||||||
StartServer(port);
|
StartServer(port);
|
||||||
setTimeout(WindowShow, 1000)
|
setTimeout(WindowShow, 1000);
|
||||||
|
|
||||||
let timeoutCount = 6;
|
let timeoutCount = 6;
|
||||||
let loading = false;
|
let loading = false;
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ const ChatPanel: FC = observer(() => {
|
|||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
if (e.data === '[DONE]') {
|
if (e.data === '[DONE]') {
|
||||||
commonStore.conversations[answerId].done = true;
|
commonStore.conversations[answerId].done = true;
|
||||||
|
commonStore.conversations[answerId].content = commonStore.conversations[answerId].content.trim();
|
||||||
commonStore.setConversations(commonStore.conversations);
|
commonStore.setConversations(commonStore.conversations);
|
||||||
commonStore.setConversationsOrder([...commonStore.conversationsOrder]);
|
commonStore.setConversationsOrder([...commonStore.conversationsOrder]);
|
||||||
return;
|
return;
|
||||||
@@ -322,7 +323,7 @@ export const Chat: FC = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Text size={100}>
|
<Text size={100}>
|
||||||
{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}` + '\'.'}
|
{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>
|
</Text>
|
||||||
<Divider style={{ flexGrow: 0 }} />
|
<Divider style={{ flexGrow: 0 }} />
|
||||||
<ChatPanel />
|
<ChatPanel />
|
||||||
|
|||||||
@@ -633,7 +633,7 @@ export const Configs: FC = observer(() => {
|
|||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||||
<Labeled label={t('API Port')}
|
<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. ` +
|
desc={t('Open the following URL with your browser to view the API documentation') + `: http://127.0.0.1:${port}/docs. ` +
|
||||||
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}` + '\'.'}
|
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={
|
content={
|
||||||
<NumberInput value={port} min={1} max={65535} step={1}
|
<NumberInput value={port} min={1} max={65535} step={1}
|
||||||
onChange={(e, data) => {
|
onChange={(e, data) => {
|
||||||
@@ -734,7 +734,7 @@ export const Configs: FC = observer(() => {
|
|||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
toast(`${t('Convert Failed')} - ${e.message || e}`, { type: 'error' });
|
toast(`${t('Convert Failed')} - ${e.message || e}`, { type: 'error' });
|
||||||
});
|
});
|
||||||
setTimeout(WindowShow, 1000)
|
setTimeout(WindowShow, 1000);
|
||||||
} else {
|
} else {
|
||||||
toast(`${t('Model Not Found')} - ${modelPath}`, { type: 'error' });
|
toast(`${t('Model Not Found')} - ${modelPath}`, { type: 'error' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export type SettingsType = {
|
|||||||
language: Language,
|
language: Language,
|
||||||
darkMode: boolean
|
darkMode: boolean
|
||||||
autoUpdatesCheck: boolean
|
autoUpdatesCheck: boolean
|
||||||
|
giteeUpdatesSource: boolean
|
||||||
cnMirror: boolean
|
cnMirror: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +65,17 @@ export const Settings: FC = observer(() => {
|
|||||||
checkUpdate(true);
|
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' &&
|
commonStore.settings.language === 'zh' &&
|
||||||
<Labeled label={t('Use Tsinghua Pip Mirrors')} flex spaceBetween content={
|
<Labeled label={t('Use Tsinghua Pip Mirrors')} flex spaceBetween content={
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {DownloadStatus} from '../pages/Downloads';
|
|||||||
import { SettingsType } from '../pages/Settings';
|
import { SettingsType } from '../pages/Settings';
|
||||||
import { IntroductionContent } from '../pages/Home';
|
import { IntroductionContent } from '../pages/Home';
|
||||||
import { AboutContent } from '../pages/About';
|
import { AboutContent } from '../pages/About';
|
||||||
|
import i18n from 'i18next';
|
||||||
|
|
||||||
export enum ModelStatus {
|
export enum ModelStatus {
|
||||||
Offline,
|
Offline,
|
||||||
@@ -18,43 +19,37 @@ export enum ModelStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CommonStore {
|
class CommonStore {
|
||||||
constructor() {
|
|
||||||
makeAutoObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// global
|
// global
|
||||||
modelStatus: ModelStatus = ModelStatus.Offline;
|
modelStatus: ModelStatus = ModelStatus.Offline;
|
||||||
depComplete: boolean = false;
|
depComplete: boolean = false;
|
||||||
|
|
||||||
// home
|
// home
|
||||||
introduction: IntroductionContent = manifest.introduction;
|
introduction: IntroductionContent = manifest.introduction;
|
||||||
|
|
||||||
// chat
|
// chat
|
||||||
conversations: Conversations = {};
|
conversations: Conversations = {};
|
||||||
conversationsOrder: string[] = [];
|
conversationsOrder: string[] = [];
|
||||||
|
|
||||||
// configs
|
// configs
|
||||||
currentModelConfigIndex: number = 0;
|
currentModelConfigIndex: number = 0;
|
||||||
modelConfigs: ModelConfig[] = [];
|
modelConfigs: ModelConfig[] = [];
|
||||||
|
|
||||||
// models
|
// models
|
||||||
modelSourceManifestList: string = 'https://cdn.jsdelivr.net/gh/josstorer/RWKV-Runner/manifest.json;';
|
modelSourceManifestList: string = 'https://cdn.jsdelivr.net/gh/josstorer/RWKV-Runner/manifest.json;';
|
||||||
modelSourceList: ModelSourceItem[] = [];
|
modelSourceList: ModelSourceItem[] = [];
|
||||||
|
|
||||||
// downloads
|
// downloads
|
||||||
downloadList: DownloadStatus[] = [];
|
downloadList: DownloadStatus[] = [];
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
settings: SettingsType = {
|
settings: SettingsType = {
|
||||||
language: getUserLanguage(),
|
language: getUserLanguage(),
|
||||||
darkMode: !isSystemLightMode(),
|
darkMode: !isSystemLightMode(),
|
||||||
autoUpdatesCheck: true,
|
autoUpdatesCheck: true,
|
||||||
|
giteeUpdatesSource: getUserLanguage() === 'zh',
|
||||||
cnMirror: getUserLanguage() === 'zh'
|
cnMirror: getUserLanguage() === 'zh'
|
||||||
};
|
};
|
||||||
|
|
||||||
// about
|
// about
|
||||||
about: AboutContent = manifest.about;
|
about: AboutContent = manifest.about;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
getCurrentModelConfig = () => {
|
getCurrentModelConfig = () => {
|
||||||
return this.modelConfigs[this.currentModelConfigIndex];
|
return this.modelConfigs[this.currentModelConfigIndex];
|
||||||
};
|
};
|
||||||
@@ -120,6 +115,9 @@ class CommonStore {
|
|||||||
else
|
else
|
||||||
WindowSetLightTheme();
|
WindowSetLightTheme();
|
||||||
|
|
||||||
|
if (this.settings.language)
|
||||||
|
i18n.changeLanguage(this.settings.language);
|
||||||
|
|
||||||
if (saveConfig)
|
if (saveConfig)
|
||||||
saveConfigs();
|
saveConfigs();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -210,13 +210,18 @@ export function bytesToKb(size: number) {
|
|||||||
|
|
||||||
export async function checkUpdate(notifyEvenLatest: boolean = false) {
|
export async function checkUpdate(notifyEvenLatest: boolean = false) {
|
||||||
let updateUrl = '';
|
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) {
|
if (r.ok) {
|
||||||
r.json().then((data) => {
|
r.json().then((data) => {
|
||||||
if (data.tag_name) {
|
if (data.tag_name) {
|
||||||
const versionTag = data.tag_name;
|
const versionTag = data.tag_name;
|
||||||
if (versionTag.replace('v', '') > manifest.version) {
|
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'), () => {
|
toastWithButton(t('New Version Available') + ': ' + versionTag, t('Update'), () => {
|
||||||
deletePythonProgramFiles();
|
deletePythonProgramFiles();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
"path": "backend-python/20B_tokenizer.json"
|
"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"
|
"path": "backend-python/get-pip.py"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user