diff --git a/CHANGELOG.md b/CHANGELOG.md index 373c0c8..667b329 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - 修复选择模型后点击确认跳转回 deepseek 的 bug - 修复 mcp 项目初始化点击工具全部都是空的 bug - 修复无法重新连接的 bug +- 支持自定义第三方 openai 兼容的模型服务 ## [main] 0.0.3 diff --git a/README.md b/README.md index d4a8e0b..ae80068 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ | `all` | 完成最基本的各类基础设施 | `完整版本` | 100% | `Done` | | `render` | chat 模式下支持进行成本分析 | `迭代版本` | 100% | `Done` | | `ext` | 支持基本的 MCP 项目管理 | `MVP` | 0% | `P0` | -| `service` | 支持自定义大模型接入 | `MVP` | 20% | `P0` | +| `service` | 支持自定义大模型接入 | `MVP` | 80% | `P0` | | `all` | 支持同时调试多个 MCP Server | `MVP` | 0% | `P1` | | `all` | 支持通过大模型进行在线验证 | `迭代版本` | 100% | `Done` | | `all` | 支持 completion/complete 协议字段 | `MVP` | 0% | `P1` | diff --git a/renderer/public/iconfont.css b/renderer/public/iconfont.css index 2c10aa2..99e732e 100644 --- a/renderer/public/iconfont.css +++ b/renderer/public/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 4870215 */ - src: url('iconfont.woff2?t=1744476757936') format('woff2'), - url('iconfont.woff?t=1744476757936') format('woff'), - url('iconfont.ttf?t=1744476757936') format('truetype'); + src: url('iconfont.woff2?t=1745222758998') format('woff2'), + url('iconfont.woff?t=1745222758998') format('woff'), + url('iconfont.ttf?t=1745222758998') format('truetype'); } .iconfont { @@ -13,6 +13,22 @@ -moz-osx-font-smoothing: grayscale; } +.icon-test:before { + content: "\e8ad"; +} + +.icon-save:before { + content: "\e67c"; +} + +.icon-more:before { + content: "\e612"; +} + +.icon-edit:before { + content: "\e63b"; +} + .icon-connect:before { content: "\ecda"; } diff --git a/renderer/public/iconfont.woff2 b/renderer/public/iconfont.woff2 index 73dd05e..e574c55 100644 Binary files a/renderer/public/iconfont.woff2 and b/renderer/public/iconfont.woff2 differ diff --git a/renderer/public/mcp.css b/renderer/public/mcp.css index 89e2dc6..543573c 100644 --- a/renderer/public/mcp.css +++ b/renderer/public/mcp.css @@ -205,39 +205,10 @@ a { background-position: center; } -.server-icon { - background-size: contain; - background-repeat: no-repeat; - background-position: center; - width: 20px; - height: 20px; - margin-right: 8px; +.el-button:hover { + color: var(--foreground) !important; } -.deepseek-icon { - background-image: url('./images/deepseek.com.ico'); -} - -.openai-icon { - background-image: url('./images/openai.com.ico'); -} - -.mistral-icon { - background-image: url('./images/mistral.ai.ico'); -} - -.ollama-icon { - background-image: url('./images/ollama.png'); -} - -.grop-icon { - background-image: url('./images/grok.com.png'); -} - -.perplexity-icon { - background-image: url('./images/perplexity.ai.ico'); -} - -.kimi-icon { - background-image: url('./images/kimichat.cn.png'); +.el-dropdown-menu__item:hover { + background-color: var(--background) !important; } \ No newline at end of file diff --git a/renderer/src/components/main-panel/chat/task-loop.ts b/renderer/src/components/main-panel/chat/task-loop.ts index 2e2c6ab..219d338 100644 --- a/renderer/src/components/main-panel/chat/task-loop.ts +++ b/renderer/src/components/main-panel/chat/task-loop.ts @@ -105,7 +105,7 @@ export class TaskLoop { const chunkHandler = this.bridge.addCommandListener('llm/chat/completions/chunk', data => { if (data.code !== 200) { this.onError(data.msg || '请求模型服务时发生错误'); - reject(new Error(data.msg || '请求模型服务时发生错误')); + resolve(); return; } const { chunk } = data.msg as { chunk: ChatCompletionChunk }; diff --git a/renderer/src/hook/css.ts b/renderer/src/hook/css.ts index 29d0942..643b235 100644 --- a/renderer/src/hook/css.ts +++ b/renderer/src/hook/css.ts @@ -25,6 +25,7 @@ export function setDefaultCss() { document.body.style.setProperty('--el-color-primary-light-5', 'var(--button-disabled)'); document.body.style.setProperty('--el-bg-color', 'var(--background)'); document.body.style.setProperty('--el-text-color-primary', 'var(--foreground)'); + document.body.style.setProperty('--el-button-hover-text-color', 'var(--background)'); // document.body.style.setProperty('--el-color-white', 'var(--background)'); diff --git a/renderer/src/i18n/ar.json b/renderer/src/i18n/ar.json index ec28e86..aa5af34 100644 --- a/renderer/src/i18n/ar.json +++ b/renderer/src/i18n/ar.json @@ -123,5 +123,12 @@ "input-token": "إدخال", "output-token": "إخراج", "total": "الإجمالي", - "cache-hit-ratio": "معدل ضربات التخزين المؤقت" + "cache-hit-ratio": "معدل ضربات التخزين المؤقت", + "success-save": "تم الحفظ بنجاح", + "confirm-delete-model": "هل تريد حذف موفر النموذج؟", + "reserve-one-last-model": "احتفظ بنموذج واحد على الأقل", + "edit": "تعديل", + "delete": "حذف", + "test": "اختبار", + "add-new-server": "إضافة خدمة" } \ No newline at end of file diff --git a/renderer/src/i18n/de.json b/renderer/src/i18n/de.json index 601356a..9f9607f 100644 --- a/renderer/src/i18n/de.json +++ b/renderer/src/i18n/de.json @@ -123,5 +123,12 @@ "input-token": "Eingabe", "output-token": "Ausgabe", "total": "Gesamt", - "cache-hit-ratio": "Cache-Trefferquote" + "cache-hit-ratio": "Cache-Trefferquote", + "success-save": "Erfolgreich gespeichert", + "confirm-delete-model": "Modellanbieter wirklich löschen?", + "reserve-one-last-model": "Behalten Sie mindestens ein Modell", + "edit": "Bearbeiten", + "delete": "Löschen", + "test": "Test", + "add-new-server": "Dienst hinzufügen" } \ No newline at end of file diff --git a/renderer/src/i18n/en.json b/renderer/src/i18n/en.json index 4730438..d87c5ff 100644 --- a/renderer/src/i18n/en.json +++ b/renderer/src/i18n/en.json @@ -99,7 +99,7 @@ "choose-a-project-debug": "Select a project to debug", "model": "Model", "server-provider": "Service Provider", - "api-root-url": "API root path", + "api-root-url": "Base Url", "api-token": "API key", "connection-method": "Connection method", "command": "Command", @@ -123,5 +123,12 @@ "input-token": "Input", "output-token": "Output", "total": "Total", - "cache-hit-ratio": "Cache hit rate" + "cache-hit-ratio": "Cache hit rate", + "success-save": "Successfully saved", + "confirm-delete-model": "Are you sure you want to delete the model provider?", + "reserve-one-last-model": "Keep at least one model", + "edit": "Edit", + "delete": "Delete", + "test": "Test", + "add-new-server": "Add service" } \ No newline at end of file diff --git a/renderer/src/i18n/fr.json b/renderer/src/i18n/fr.json index b0be8a7..77f9766 100644 --- a/renderer/src/i18n/fr.json +++ b/renderer/src/i18n/fr.json @@ -123,5 +123,12 @@ "input-token": "Entrée", "output-token": "Sortie", "total": "Total", - "cache-hit-ratio": "Taux de réussite du cache" + "cache-hit-ratio": "Taux de réussite du cache", + "success-save": "Enregistré avec succès", + "confirm-delete-model": "Êtes-vous sûr de vouloir supprimer le fournisseur de modèles ?", + "reserve-one-last-model": "Conservez au moins un modèle", + "edit": "Modifier", + "delete": "Supprimer", + "test": "Test", + "add-new-server": "Ajouter un service" } \ No newline at end of file diff --git a/renderer/src/i18n/ja.json b/renderer/src/i18n/ja.json index 1afd60f..af92b68 100644 --- a/renderer/src/i18n/ja.json +++ b/renderer/src/i18n/ja.json @@ -123,5 +123,12 @@ "input-token": "入力", "output-token": "出力", "total": "合計", - "cache-hit-ratio": "キャッシュヒット率" + "cache-hit-ratio": "キャッシュヒット率", + "success-save": "正常に保存されました", + "confirm-delete-model": "このモデルプロバイダーを削除しますか?", + "reserve-one-last-model": "少なくとも1つのモデルを保持してください", + "edit": "編集", + "delete": "削除", + "test": "テスト", + "add-new-server": "サービスを追加" } \ No newline at end of file diff --git a/renderer/src/i18n/ko.json b/renderer/src/i18n/ko.json index bb61813..4fd2360 100644 --- a/renderer/src/i18n/ko.json +++ b/renderer/src/i18n/ko.json @@ -123,5 +123,12 @@ "input-token": "입력", "output-token": "출력", "total": "총계", - "cache-hit-ratio": "캐시 적중률" + "cache-hit-ratio": "캐시 적중률", + "success-save": "성공적으로 저장됨", + "confirm-delete-model": "이 모델 공급자를 삭제하시겠습니까?", + "reserve-one-last-model": "적어도 하나의 모델을 유지하세요", + "edit": "편집", + "delete": "삭제", + "test": "테스트", + "add-new-server": "서비스 추가" } \ No newline at end of file diff --git a/renderer/src/i18n/ru.json b/renderer/src/i18n/ru.json index c90b163..a73710e 100644 --- a/renderer/src/i18n/ru.json +++ b/renderer/src/i18n/ru.json @@ -123,5 +123,12 @@ "input-token": "Ввод", "output-token": "Вывод", "total": "Итого", - "cache-hit-ratio": "Коэффициент попаданий в кэш" + "cache-hit-ratio": "Коэффициент попаданий в кэш", + "success-save": "Успешно сохранено", + "confirm-delete-model": "Вы уверены, что хотите удалить поставщика моделей?", + "reserve-one-last-model": "Оставьте хотя бы одну модель", + "edit": "Редактировать", + "delete": "Удалить", + "test": "Тест", + "add-new-server": "Добавить услугу" } \ No newline at end of file diff --git a/renderer/src/i18n/zh-cn.json b/renderer/src/i18n/zh-cn.json index f4ca929..2c030bb 100644 --- a/renderer/src/i18n/zh-cn.json +++ b/renderer/src/i18n/zh-cn.json @@ -123,5 +123,12 @@ "input-token": "输入", "output-token": "输出", "total": "总计", - "cache-hit-ratio": "缓存命中率" + "cache-hit-ratio": "缓存命中率", + "success-save": "成功保存", + "confirm-delete-model": "确定删除该模型提供商?", + "reserve-one-last-model": "至少保留一个模型", + "edit": "编辑", + "delete": "删除", + "test": "测试", + "add-new-server": "添加服务" } \ No newline at end of file diff --git a/renderer/src/i18n/zh-tw.json b/renderer/src/i18n/zh-tw.json index bc5f21b..83427c5 100644 --- a/renderer/src/i18n/zh-tw.json +++ b/renderer/src/i18n/zh-tw.json @@ -123,5 +123,12 @@ "input-token": "輸入", "output-token": "輸出", "total": "總計", - "cache-hit-ratio": "緩存命中率" + "cache-hit-ratio": "緩存命中率", + "success-save": "成功儲存", + "confirm-delete-model": "確定刪除該模型提供商?", + "reserve-one-last-model": "至少保留一個模型", + "edit": "編輯", + "delete": "刪除", + "test": "測試", + "add-new-server": "新增服務" } \ No newline at end of file diff --git a/renderer/src/views/setting/api.ts b/renderer/src/views/setting/api.ts new file mode 100644 index 0000000..a0ecbc0 --- /dev/null +++ b/renderer/src/views/setting/api.ts @@ -0,0 +1,53 @@ +import { ChatStorage } from '@/components/main-panel/chat/chat'; +import { TaskLoop } from '@/components/main-panel/chat/task-loop'; +import { llmManager } from './llm'; +import { reactive, ref } from 'vue'; + +export const simpleTestResult = reactive<{ + done: boolean, + start: boolean, + error: any +}>({ + done: false, + start: false, + error: '', +}); + +export function makeSimpleTalk() { + simpleTestResult.done = false; + simpleTestResult.start = true; + + // 使用最简单的 hello 来测试 + const testMessage = 'hello'; + + const s1 = ref(''); + const s2 = ref([]); + const loop = new TaskLoop(s1, s2); + + const chatStorage: ChatStorage = { + messages: [], + settings: { + temperature: 0.7, + modelIndex: llmManager.currentModelIndex, + systemPrompt: '', + enableTools: [], + enableWebSearch: false, + contextLength: 5 + } + }; + + loop.registerOnDone(() => { + console.log('done'); + simpleTestResult.error = ''; + simpleTestResult.done = true; + simpleTestResult.start = false; + }); + + loop.registerOnError(error => { + console.log(error); + simpleTestResult.error = error; + simpleTestResult.start = false; + }); + + loop.start(chatStorage, testMessage); +} \ No newline at end of file diff --git a/renderer/src/views/setting/api.vue b/renderer/src/views/setting/api.vue index 246b73b..2f45463 100644 --- a/renderer/src/views/setting/api.vue +++ b/renderer/src/views/setting/api.vue @@ -1,21 +1,35 @@ \ No newline at end of file diff --git a/renderer/src/views/setting/connect-interface-openai.vue b/renderer/src/views/setting/connect-interface-openai.vue new file mode 100644 index 0000000..0219a3e --- /dev/null +++ b/renderer/src/views/setting/connect-interface-openai.vue @@ -0,0 +1,77 @@ + + + + + \ No newline at end of file diff --git a/renderer/src/views/setting/connect-test.vue b/renderer/src/views/setting/connect-test.vue new file mode 100644 index 0000000..0ad2294 --- /dev/null +++ b/renderer/src/views/setting/connect-test.vue @@ -0,0 +1,53 @@ + + + + + \ No newline at end of file diff --git a/renderer/src/views/setting/index.vue b/renderer/src/views/setting/index.vue index bc32990..9b83a6b 100644 --- a/renderer/src/views/setting/index.vue +++ b/renderer/src/views/setting/index.vue @@ -1,27 +1,29 @@