支持自定义大模型接入 80%
This commit is contained in:
parent
39919e7273
commit
36744f2a71
@ -4,6 +4,7 @@
|
||||
- 修复选择模型后点击确认跳转回 deepseek 的 bug
|
||||
- 修复 mcp 项目初始化点击工具全部都是空的 bug
|
||||
- 修复无法重新连接的 bug
|
||||
- 支持自定义第三方 openai 兼容的模型服务
|
||||
|
||||
## [main] 0.0.3
|
||||
|
||||
|
@ -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` |
|
||||
|
@ -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";
|
||||
}
|
||||
|
Binary file not shown.
@ -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;
|
||||
}
|
@ -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 };
|
||||
|
@ -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)');
|
||||
|
||||
|
@ -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": "إضافة خدمة"
|
||||
}
|
@ -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"
|
||||
}
|
@ -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"
|
||||
}
|
@ -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"
|
||||
}
|
@ -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": "サービスを追加"
|
||||
}
|
@ -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": "서비스 추가"
|
||||
}
|
@ -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": "Добавить услугу"
|
||||
}
|
@ -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": "添加服务"
|
||||
}
|
@ -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": "新增服務"
|
||||
}
|
53
renderer/src/views/setting/api.ts
Normal file
53
renderer/src/views/setting/api.ts
Normal file
@ -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);
|
||||
}
|
@ -1,21 +1,35 @@
|
||||
<template>
|
||||
<div class="setting-section">
|
||||
<h2>{{ "API" }}</h2>
|
||||
<h2 class="api-title">
|
||||
{{ "API" }}
|
||||
</h2>
|
||||
<div class="setting-option">
|
||||
<span>
|
||||
<span class="iconfont icon-company"></span>
|
||||
<span class="option-title">{{ t('server-provider') }}</span>
|
||||
</span>
|
||||
<div style="width: 160px;">
|
||||
<el-select
|
||||
name="language-setting"
|
||||
class="language-setting"
|
||||
v-model="llmManager.currentModelIndex"
|
||||
<el-select name="language-setting" class="language-setting" v-model="llmManager.currentModelIndex"
|
||||
@change="onmodelchange">
|
||||
|
||||
<el-option v-for="(option, index) in llms" :value="index" :label="option.name" :key="index">
|
||||
<div class="llm-option">
|
||||
<span>{{ option.name }}</span>
|
||||
<el-dropdown trigger="hover" @command="handleCommand">
|
||||
<span>
|
||||
<span class="iconfont icon-more"></span>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="{type: 'edit', index}">
|
||||
<span class="iconfont icon-edit"> {{ t('edit') }}</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type: 'delete', index}" divided>
|
||||
<span class="iconfont icon-delete"> {{ t('delete') }}</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
@ -27,73 +41,76 @@
|
||||
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="setting-option">
|
||||
<span>
|
||||
<span class="iconfont icon-llm"></span>
|
||||
<span class="option-title">{{ t('model') }}</span>
|
||||
</span>
|
||||
<div style="width: 160px;">
|
||||
<el-select
|
||||
v-if="llms[llmManager.currentModelIndex]"
|
||||
name="language-setting" class="language-setting"
|
||||
v-model="llms[llmManager.currentModelIndex].userModel"
|
||||
@change="onmodelchange"
|
||||
>
|
||||
<el-option v-for="option in llms[llmManager.currentModelIndex].models"
|
||||
:value="option"
|
||||
:label="option"
|
||||
:key="option"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-option">
|
||||
<span>
|
||||
<span class="iconfont icon-url-line"></span>
|
||||
<span class="option-title">{{ t('api-root-url') }}</span>
|
||||
</span>
|
||||
<div style="width: 240px;">
|
||||
<el-input
|
||||
v-if="llms[llmManager.currentModelIndex]"
|
||||
v-model="llms[llmManager.currentModelIndex].baseUrl"
|
||||
placeholder="https://"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-option">
|
||||
<span>
|
||||
<span class="iconfont icon-token"></span>
|
||||
<span class="option-title">{{ t('api-token') }}</span>
|
||||
</span>
|
||||
<div style="width: 240px;">
|
||||
<el-input
|
||||
v-if="llms[llmManager.currentModelIndex]"
|
||||
v-model="llms[llmManager.currentModelIndex].userToken"
|
||||
show-password
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ConnectInterfaceOpenai />
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="setting-save-container">
|
||||
<el-button type="success" @click="addNewServer">
|
||||
{{ t("add-new-server") }}
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="makeSimpleTalk"
|
||||
:loading="simpleTestResult.start"
|
||||
>
|
||||
<span v-show="!simpleTestResult.start" class="iconfont icon-test"></span>
|
||||
{{ t('test') }}
|
||||
</el-button>
|
||||
|
||||
<el-button type="primary" @click="saveLlmSetting">
|
||||
<span class="iconfont icon-save"></span>
|
||||
{{ t('save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 测试连通性的地方 -->
|
||||
<ConnectTest />
|
||||
|
||||
<!-- 当前页面的聊天框 -->
|
||||
<el-dialog v-model="dialogVisible" width="50%" style="min-width: 500px; max-width: 800px;padding: 20px;">
|
||||
|
||||
<br>
|
||||
|
||||
<el-form :model="newProviderForm" label-width="auto">
|
||||
<el-form-item :label="t('server-provider')">
|
||||
<el-input v-model="newProviderForm.provider" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('model')">
|
||||
<el-input-tag
|
||||
v-model="newProviderForm.models"
|
||||
placeholder="gpt-3.5-turbo"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('api-root-url')">
|
||||
<el-input v-model="newProviderForm.baseUrl" placeholder="https://" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('api-token')">
|
||||
<el-input v-model="newProviderForm.userToken" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">{{ t("cancel") }}</el-button>
|
||||
<el-button type="primary" @click="dialogConfirm">{{ t("confirm") }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { llmManager, llms, onmodelchange } from './llm';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { llmManager, llms } from './llm';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { saveSetting } from '@/hook/setting';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { pinkLog } from './util';
|
||||
|
||||
import ConnectInterfaceOpenai from './connect-interface-openai.vue';
|
||||
import ConnectTest from './connect-test.vue';
|
||||
import { makeSimpleTalk, simpleTestResult } from './api';
|
||||
|
||||
defineComponent({ name: 'api' });
|
||||
const { t } = useI18n();
|
||||
@ -101,16 +118,207 @@ const { t } = useI18n();
|
||||
function saveLlmSetting() {
|
||||
saveSetting(() => {
|
||||
ElMessage({
|
||||
message: '成功保存',
|
||||
message: t('success-save'),
|
||||
type: 'success'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const currentDialogMode = ref('');
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
function addNewServer() {
|
||||
newProviderForm.value = {
|
||||
provider: '',
|
||||
models: [],
|
||||
baseUrl: '',
|
||||
userToken: ''
|
||||
};
|
||||
|
||||
currentDialogMode.value = 'add';
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
function editModel(index: number) {
|
||||
currentDialogMode.value = 'edit';
|
||||
editingIndex.value = index;
|
||||
|
||||
const formData = JSON.parse(JSON.stringify(llms[index]));
|
||||
newProviderForm.value = {
|
||||
provider: formData.name,
|
||||
models: formData.models,
|
||||
baseUrl: formData.baseUrl,
|
||||
userToken: formData.userToken
|
||||
};
|
||||
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const newProviderForm = ref<{
|
||||
provider: string;
|
||||
models: string[];
|
||||
baseUrl: string;
|
||||
userToken: string;
|
||||
}>({
|
||||
provider: '',
|
||||
models: [],
|
||||
baseUrl: '',
|
||||
userToken: ''
|
||||
});
|
||||
|
||||
const editingIndex = ref(-1);
|
||||
|
||||
|
||||
function dialogConfirm() {
|
||||
if (currentDialogMode.value === 'add') {
|
||||
addNewProvider();
|
||||
} else if (currentDialogMode.value === 'edit') {
|
||||
updateProvider();
|
||||
}
|
||||
}
|
||||
|
||||
function addNewProvider() {
|
||||
const formData = JSON.parse(JSON.stringify(newProviderForm.value));
|
||||
|
||||
llms.push({
|
||||
id: formData.provider,
|
||||
name: formData.provider,
|
||||
models: formData.models,
|
||||
userModel: formData.models[0],
|
||||
baseUrl: formData.baseUrl,
|
||||
userToken: formData.userToken,
|
||||
isOpenAICompatible: true,
|
||||
description: "User Defined Server",
|
||||
website: "",
|
||||
});
|
||||
|
||||
dialogVisible.value = false;
|
||||
newProviderForm.value = {
|
||||
provider: '',
|
||||
models: [],
|
||||
baseUrl: '',
|
||||
userToken: ''
|
||||
};
|
||||
}
|
||||
|
||||
function updateProvider() {
|
||||
if (editingIndex.value < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = JSON.parse(JSON.stringify(newProviderForm.value));
|
||||
|
||||
llms[editingIndex.value] = {
|
||||
...llms[editingIndex.value],
|
||||
id: formData.provider,
|
||||
name: formData.provider,
|
||||
models: formData.models,
|
||||
userModel: formData.models[0] || '',
|
||||
baseUrl: formData.baseUrl,
|
||||
userToken: formData.userToken
|
||||
};
|
||||
|
||||
console.log(llms[editingIndex.value]);
|
||||
|
||||
newProviderForm.value = {
|
||||
provider: '',
|
||||
models: [],
|
||||
baseUrl: '',
|
||||
userToken: ''
|
||||
};
|
||||
|
||||
dialogVisible.value = false;
|
||||
saveLlmSetting();
|
||||
}
|
||||
|
||||
function onmodelchange() {
|
||||
pinkLog('切换模型到:' + llms[llmManager.currentModelIndex].id);
|
||||
saveLlmSetting();
|
||||
}
|
||||
|
||||
function deleteModel(index: number) {
|
||||
if (llms.length <= 1) {
|
||||
ElMessage.warning(t('reserve-one-last-model'));
|
||||
return;
|
||||
}
|
||||
|
||||
ElMessageBox.confirm(
|
||||
t('confirm-delete-model'),
|
||||
'warning',
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning',
|
||||
}
|
||||
).then(() => {
|
||||
llms.splice(index, 1);
|
||||
|
||||
if (llmManager.currentModelIndex === index) {
|
||||
llmManager.currentModelIndex = 0;
|
||||
} else if (llmManager.currentModelIndex > index) {
|
||||
llmManager.currentModelIndex --;
|
||||
}
|
||||
|
||||
saveLlmSetting();
|
||||
}).catch(() => {
|
||||
// 用户取消删除
|
||||
});
|
||||
}
|
||||
|
||||
function handleCommand(command: {type: string, index: number}) {
|
||||
if (command.type === 'edit') {
|
||||
editModel(command.index);
|
||||
} else if (command.type === 'delete') {
|
||||
deleteModel(command.index);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.setting-save-container {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.setting-save-container .iconfont {
|
||||
margin-right: 5px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.api-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
.api-title .iconfont {
|
||||
font-weight: 300;
|
||||
font-size: 18px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
transition: var(--animation-3s);
|
||||
}
|
||||
|
||||
.api-title .iconfont:hover {
|
||||
color: var(--main-color);
|
||||
}
|
||||
|
||||
.llm-option {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
--el-color-primary: white;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
color: var(--el-color-danger);
|
||||
cursor: pointer;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.delete-icon:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
77
renderer/src/views/setting/connect-interface-openai.vue
Normal file
77
renderer/src/views/setting/connect-interface-openai.vue
Normal file
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div class="setting-option">
|
||||
<span>
|
||||
<span class="iconfont icon-llm"></span>
|
||||
<span class="option-title">{{ t('model') }}</span>
|
||||
</span>
|
||||
<div style="width: 160px;">
|
||||
<el-select v-if="llms[llmManager.currentModelIndex]"
|
||||
name="language-setting"
|
||||
v-model="llms[llmManager.currentModelIndex].userModel"
|
||||
@change="onmodelchange"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in llms[llmManager.currentModelIndex].models"
|
||||
:value="option"
|
||||
:label="option"
|
||||
:key="option.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-option">
|
||||
<span>
|
||||
<span class="iconfont icon-url-line"></span>
|
||||
<span class="option-title">{{ t('api-root-url') }}</span>
|
||||
</span>
|
||||
<div style="width: 240px;">
|
||||
<el-input v-if="llms[llmManager.currentModelIndex]"
|
||||
v-model="llms[llmManager.currentModelIndex].baseUrl" placeholder="https://" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-option">
|
||||
<span>
|
||||
<span class="iconfont icon-token"></span>
|
||||
<span class="option-title">{{ t('api-token') }}</span>
|
||||
</span>
|
||||
<div style="width: 240px;">
|
||||
<el-input v-if="llms[llmManager.currentModelIndex]"
|
||||
v-model="llms[llmManager.currentModelIndex].userToken" show-password />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/* eslint-disable */
|
||||
|
||||
import { defineComponent } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { llmManager, llms } from './llm';
|
||||
import { pinkLog } from './util';
|
||||
import { saveSetting } from '@/hook/setting';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
defineComponent({ name: 'connect-interface-openai' });
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
function saveLlmSetting() {
|
||||
saveSetting(() => {
|
||||
ElMessage({
|
||||
message: t('success-save'),
|
||||
type: 'success'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function onmodelchange() {
|
||||
pinkLog('切换模型到:' + llms[llmManager.currentModelIndex].id);
|
||||
saveLlmSetting();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
53
renderer/src/views/setting/connect-test.vue
Normal file
53
renderer/src/views/setting/connect-test.vue
Normal file
@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div class="connect-test" v-if="simpleTestResult.done || simpleTestResult.error">
|
||||
<div class="test-result">
|
||||
<div class="result-item" v-if="simpleTestResult.done">
|
||||
<span class="iconfont icon-success"></span>
|
||||
<span>{{ "✅ okey dockey :D" }}</span>
|
||||
</div>
|
||||
<div class="result-item error" v-if="simpleTestResult.error">
|
||||
<span class="iconfont icon-error"></span>
|
||||
<span>{{ '❌ ' + simpleTestResult.error }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { simpleTestResult } from './api';
|
||||
|
||||
defineComponent({ name: 'connect-test' });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.connect-test {
|
||||
margin-top: 20px;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--el-bg-color);
|
||||
}
|
||||
|
||||
.test-result {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--el-color-success);
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.result-item.error {
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
|
||||
.result-item .iconfont {
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
@ -1,27 +1,29 @@
|
||||
<template>
|
||||
<div class="setting-container">
|
||||
<div>
|
||||
<el-segmented
|
||||
v-model="settingSections.current"
|
||||
:options="settingSections.data"
|
||||
size="large"
|
||||
style="margin: 10px; font-size: 16px; background-color: var(--background);"
|
||||
>
|
||||
<template #default="scope">
|
||||
<div class="setting-section-option">
|
||||
{{ scope.item.label }}
|
||||
</div>
|
||||
</template>
|
||||
</el-segmented>
|
||||
<el-scrollbar height="100%">
|
||||
<div class="setting-container">
|
||||
<div>
|
||||
<el-segmented
|
||||
v-model="settingSections.current"
|
||||
:options="settingSections.data"
|
||||
size="large"
|
||||
style="margin: 10px; font-size: 16px; background-color: var(--background);"
|
||||
>
|
||||
<template #default="scope">
|
||||
<div class="setting-section-option">
|
||||
{{ scope.item.label }}
|
||||
</div>
|
||||
</template>
|
||||
</el-segmented>
|
||||
<div>
|
||||
|
||||
<General v-show="settingSections.current === 'general'"></General>
|
||||
<Api v-show="settingSections.current === 'api'"></Api>
|
||||
<Appearance v-show="settingSections.current === 'appearance'"></Appearance>
|
||||
<General v-show="settingSections.current === 'general'"></General>
|
||||
<Api v-show="settingSections.current === 'api'"></Api>
|
||||
<Appearance v-show="settingSections.current === 'appearance'"></Appearance>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -7,8 +7,3 @@ export const llms = reactive<any[]>([]);
|
||||
export const llmManager = reactive({
|
||||
currentModelIndex: 0,
|
||||
});
|
||||
|
||||
export function onmodelchange() {
|
||||
pinkLog('切换模型到:' + llms[llmManager.currentModelIndex].id);
|
||||
saveSetting();
|
||||
}
|
1
service/.gitignore
vendored
1
service/.gitignore
vendored
@ -22,3 +22,4 @@ pnpm-debug.log*
|
||||
*.sln
|
||||
*.sw?
|
||||
config.json
|
||||
setting.json
|
@ -19,9 +19,9 @@ function getConfigurationPath() {
|
||||
if (!fs.existsSync(configDir)) {
|
||||
fs.mkdirSync(configDir, { recursive: true });
|
||||
}
|
||||
return path.join(configDir, 'config.json');
|
||||
return path.join(configDir, 'setting.json');
|
||||
}
|
||||
return 'config.json';
|
||||
return 'setting.json';
|
||||
}
|
||||
|
||||
function getTabSavePath(serverInfo: IServerVersion) {
|
||||
|
@ -1,103 +1,4 @@
|
||||
{
|
||||
"currentIndex": 0,
|
||||
"tabs": [
|
||||
{
|
||||
"name": "交互测试",
|
||||
"icon": "icon-robot",
|
||||
"type": "blank",
|
||||
"componentIndex": 3,
|
||||
"storage": {
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "请帮我找几张明日方舟 m3 的图片",
|
||||
"extraInfo": {
|
||||
"created": 1745169263803,
|
||||
"serverName": "deepseek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_0_bdf947d9-41d9-4244-b145-55e5dc4f1bdd",
|
||||
"index": 0,
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "image_crawler",
|
||||
"arguments": "{\"key_word\":\"明日方舟 m3\",\"image_num\":5}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"extraInfo": {
|
||||
"created": 1745169269404,
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 224,
|
||||
"completion_tokens": 33,
|
||||
"total_tokens": 257,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"prompt_cache_hit_tokens": 0,
|
||||
"prompt_cache_miss_tokens": 224
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"tool_call_id": "call_0_bdf947d9-41d9-4244-b145-55e5dc4f1bdd",
|
||||
"content": "[{\"type\":\"text\",\"text\":\"[ImageResult(url='https://embed.pixiv.net/decorate.php?illust_id=128971880', title='', source='bing'), ImageResult(url='https://embed.pixiv.net/artwork.php?illust_id=128973165', title='', source='bing'), ImageResult(url='https://embed.pixiv.net/artwork.php?illust_id=128981472', title='', source='bing'), ImageResult(url='https://embed.pixiv.net/decorate.php?illust_id=128933348', title='', source='bing'), ImageResult(url='https://embed.pixiv.net/artwork.php?illust_id=128952894', title='', source='bing')]\"}]",
|
||||
"extraInfo": {
|
||||
"created": 1745169270649,
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 224,
|
||||
"completion_tokens": 33,
|
||||
"total_tokens": 257,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"prompt_cache_hit_tokens": 0,
|
||||
"prompt_cache_miss_tokens": 224
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "以下是一些关于“明日方舟 m3”的图片:\n\n1. \n2. \n3. \n4. \n5. \n\n如果需要更多图片或其他帮助,请告诉我!",
|
||||
"extraInfo": {
|
||||
"created": 1745169280218,
|
||||
"serverName": "deepseek",
|
||||
"usage": {
|
||||
"prompt_tokens": 420,
|
||||
"completion_tokens": 142,
|
||||
"total_tokens": 562,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 192
|
||||
},
|
||||
"prompt_cache_hit_tokens": 192,
|
||||
"prompt_cache_miss_tokens": 228
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"modelIndex": 0,
|
||||
"enableTools": [
|
||||
{
|
||||
"name": "image_crawler",
|
||||
"description": "根据关键词从指定搜索引擎爬取图片",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"enableWebSearch": false,
|
||||
"temperature": 0.7,
|
||||
"contextLength": 10,
|
||||
"systemPrompt": "你是一个聪明的机器人,我现在会询问你一些问题,这些问题可能会调用我给你的工具,如果用户的输入的问题内包含的参数不足以发起一次工具调用,那么你应该提醒用户缺失的参数。对于图片,请按照 ![]() 进行渲染,而不是将这个返回到 ``` 的 code 中。"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"tabs": []
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
{
|
||||
"currentIndex": 2,
|
||||
"tabs": [
|
||||
{
|
||||
"name": "工具",
|
||||
"icon": "icon-tool",
|
||||
"type": "blank",
|
||||
"componentIndex": 2,
|
||||
"storage": {
|
||||
"currentToolName": "add"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "资源",
|
||||
"icon": "icon-file",
|
||||
"type": "blank",
|
||||
"componentIndex": 0,
|
||||
"storage": {
|
||||
"currentResourceName": "greeting"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "交互测试",
|
||||
"icon": "icon-robot",
|
||||
"type": "blank",
|
||||
"componentIndex": 3,
|
||||
"storage": {
|
||||
"messages": [],
|
||||
"settings": {
|
||||
"modelIndex": 0,
|
||||
"enableTools": [],
|
||||
"enableWebSearch": false,
|
||||
"temperature": 0.7,
|
||||
"contextLength": 10,
|
||||
"systemPrompt": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user