fin issue49

This commit is contained in:
锦恢 2025-07-14 23:48:10 +08:00
parent 017aa37b73
commit f27eb4cf97
14 changed files with 140 additions and 45 deletions

View File

@ -3,8 +3,11 @@
## [main] 0.1.10 ## [main] 0.1.10
- 修复 issue #48: 修复错误的引导路径。 - 修复 issue #48: 修复错误的引导路径。
- 完成引导界面的多语言。 - 完成引导界面的多语言。
- 修复部分模型的 usage 无法正常显示的问题。 - 支持 kimi 的 usage 计数 + 支持 kimi 的 system prompt。
- 实现 openmcp 工具测试的并行实现和暂停功能。 - 实现 openmcp 工具测试的并行执行和暂停功能。
- 修正 API 测速中算法,剥离为 tps + 排队时间两部分。
- 大模型 api 测速目前可以自定义 prompt 了。
- 实现 issue#49工具模块调试希望支持markdown渲染回显。
## [main] 0.1.9 ## [main] 0.1.9
- 增加 mook 功能可以利用随机种子或者AI生成来自动化填充测试 tool 的表单数据 - 增加 mook 功能可以利用随机种子或者AI生成来自动化填充测试 tool 的表单数据

View File

@ -2,10 +2,13 @@
<div class="tool-logger"> <div class="tool-logger">
<span> <span>
<span>{{ t('response') }}</span> <span>{{ t('response') }}</span>
<span style="width: 200px;"> <span style="width: 200px; display: flex;">
<el-switch v-model="showRawJson" inline-prompt active-text="JSON" inactive-text="Text" <el-segmented v-model="renderMode.current" :options="renderMode.data" size="default"
style="margin-left: 10px; width: 200px;" style="margin: 10px; background-color: var(--background); font-size: 12px;">
:inactive-action-style="'backgroundColor: var(--sidebar)'" /> <template #default="scope">
{{ scope.item.label }}
</template>
</el-segmented>
</span> </span>
</span> </span>
<el-scrollbar height="500px"> <el-scrollbar height="500px">
@ -20,7 +23,7 @@
<div v-else> <div v-else>
<!-- 展示原本的信息 --> <!-- 展示原本的信息 -->
<template v-if="!showRawJson && tabStorage.lastToolCallResponse"> <template v-if="renderMode.current === 'plaintext'">
<div <div
v-for="(c, idx) in tabStorage.lastToolCallResponse!.content" v-for="(c, idx) in tabStorage.lastToolCallResponse!.content"
:key="idx" :key="idx"
@ -30,8 +33,12 @@
</div> </div>
</template> </template>
<template v-else-if="renderMode.current === 'markdown'">
<div class="markdown" v-html="resultMarkdown"></div>
</template>
<!-- 展示 json --> <!-- 展示 json -->
<template v-else> <template v-else-if="renderMode.current === 'json'">
<json-render :json="tabStorage.lastToolCallResponse"/> <json-render :json="tabStorage.lastToolCallResponse"/>
</template> </template>
</div> </div>
@ -42,11 +49,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineComponent, defineProps, computed, ref } from 'vue'; import { defineComponent, defineProps, computed, ref, reactive } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { tabs } from '../panel'; import { tabs } from '../panel';
import type { ToolStorage } from './tools'; import type { ToolStorage } from './tools';
import JsonRender from '@/components/json-render/index.vue'; import JsonRender from '@/components/json-render/index.vue';
import { markdownToHtml } from '../chat/markdown/markdown';
defineComponent({ name: 'tool-logger' }); defineComponent({ name: 'tool-logger' });
const { t } = useI18n(); const { t } = useI18n();
@ -61,7 +69,37 @@ const props = defineProps({
const tab = tabs.content[props.tabId]; const tab = tabs.content[props.tabId];
const tabStorage = tab.storage as ToolStorage; const tabStorage = tab.storage as ToolStorage;
const showRawJson = ref(false); const resultMarkdown = computed(() => {
const lastToolCallResponse = tabStorage.lastToolCallResponse;
if (!lastToolCallResponse) {
return '';
}
if (typeof lastToolCallResponse === 'string') {
return markdownToHtml(lastToolCallResponse.toString());
}
const rawText = lastToolCallResponse.content.map(c => c.text).join('\n');
const html = markdownToHtml(rawText);
return html;
})
const renderMode = reactive({
current: 'plaintext',
data: [
{
value: 'plaintext',
label: 'plaintext'
},
{
value: 'markdown',
label: 'markdown',
},
{
value: 'json',
label: 'json'
}
]
});
</script> </script>
@ -97,12 +135,6 @@ const showRawJson = ref(false);
min-height: 450px; min-height: 450px;
height: fit-content; height: fit-content;
font-family: var(--code-font-family); font-family: var(--code-font-family);
white-space: pre-wrap;
word-break: break-all;
user-select: text;
cursor: text;
font-size: 15px;
line-height: 1.5;
background-color: var(--sidebar); background-color: var(--sidebar);
} }

View File

@ -230,5 +230,6 @@
"guide.16.3": "إذا كنت مستخدمًا للإضافة، فهناك بعض المواد التي أعددناها في الجزء السفلي من اللوحة اليسرى تحت عنوان \"البدء والمساعدة\"، نأمل أن تساعدك في تطوير خادم MCP الخاص بك بشكل أنيق.", "guide.16.3": "إذا كنت مستخدمًا للإضافة، فهناك بعض المواد التي أعددناها في الجزء السفلي من اللوحة اليسرى تحت عنوان \"البدء والمساعدة\"، نأمل أن تساعدك في تطوير خادم MCP الخاص بك بشكل أنيق.",
"guide.16.4": "دعونا نعمل معًا على دمج المزيد والمزيد من APIs و SDKs في النموذج الكبير!", "guide.16.4": "دعونا نعمل معًا على دمج المزيد والمزيد من APIs و SDKs في النموذج الكبير!",
"maybe-end": "الفصل الأخير؟", "maybe-end": "الفصل الأخير؟",
"finish": "النهاية" "finish": "النهاية",
"queue-time": "وقت الانتظار"
} }

View File

@ -230,5 +230,6 @@
"guide.16.3": "Falls Sie ein Plugin-Benutzer sind, finden Sie unten im linken Panel unter \"Erste Schritte & Hilfe\" einige von uns vorbereitete Materialien, die Ihnen hoffentlich dabei helfen, Ihren MCP-Server elegant zu entwickeln.", "guide.16.3": "Falls Sie ein Plugin-Benutzer sind, finden Sie unten im linken Panel unter \"Erste Schritte & Hilfe\" einige von uns vorbereitete Materialien, die Ihnen hoffentlich dabei helfen, Ihren MCP-Server elegant zu entwickeln.",
"guide.16.4": "Lasst uns gemeinsam immer mehr APIs und SDKs in das große Modell integrieren!", "guide.16.4": "Lasst uns gemeinsam immer mehr APIs und SDKs in das große Modell integrieren!",
"maybe-end": "Ende?", "maybe-end": "Ende?",
"finish": "Ende" "finish": "Ende",
"queue-time": "Wartezeit"
} }

View File

@ -230,5 +230,6 @@
"guide.16.3": "If you are a plugin user, there are some materials we have prepared at the bottom of the left panel under \"Getting Started & Help\", hoping to assist you in elegantly developing your MCP server.", "guide.16.3": "If you are a plugin user, there are some materials we have prepared at the bottom of the left panel under \"Getting Started & Help\", hoping to assist you in elegantly developing your MCP server.",
"guide.16.4": "Let's work together to integrate more and more APIs and SDKs into the large model!", "guide.16.4": "Let's work together to integrate more and more APIs and SDKs into the large model!",
"maybe-end": "Final Chapter?", "maybe-end": "Final Chapter?",
"finish": "End" "finish": "End",
"queue-time": "Waiting time"
} }

View File

@ -230,5 +230,6 @@
"guide.16.3": "Si vous êtes un utilisateur de plugin, vous trouverez quelques documents que nous avons préparés en bas du panneau de gauche sous \"Démarrage & Aide\", dans l'espoir de vous aider à développer élégamment votre serveur MCP.", "guide.16.3": "Si vous êtes un utilisateur de plugin, vous trouverez quelques documents que nous avons préparés en bas du panneau de gauche sous \"Démarrage & Aide\", dans l'espoir de vous aider à développer élégamment votre serveur MCP.",
"guide.16.4": "Travaillons ensemble pour intégrer de plus en plus d'APIs et de SDKs dans le grand modèle !", "guide.16.4": "Travaillons ensemble pour intégrer de plus en plus d'APIs et de SDKs dans le grand modèle !",
"maybe-end": "Chapitre final ?", "maybe-end": "Chapitre final ?",
"finish": "Fin" "finish": "Fin",
"queue-time": "Temps d'attente"
} }

View File

@ -230,5 +230,6 @@
"guide.16.3": "プラグインユーザーの場合、左パネルの下部にある「はじめにヘルプ」には、MCPサーバーをエレガントに開発するための資料が用意されています。ぜひご活用ください。", "guide.16.3": "プラグインユーザーの場合、左パネルの下部にある「はじめにヘルプ」には、MCPサーバーをエレガントに開発するための資料が用意されています。ぜひご活用ください。",
"guide.16.4": "大規模モデルにどんどんAPIやSDKを統合していきましょう", "guide.16.4": "大規模モデルにどんどんAPIやSDKを統合していきましょう",
"maybe-end": "終章?", "maybe-end": "終章?",
"finish": "終了" "finish": "終了",
"queue-time": "待ち時間"
} }

View File

@ -230,5 +230,6 @@
"guide.16.3": "플러그인 사용자라면, 왼쪽 패널 하단의 '시작하기 및 도움말'에 MCP 서버를 우아하게 개발할 수 있도록 준비한 자료가 있습니다. 도움이 되길 바랍니다.", "guide.16.3": "플러그인 사용자라면, 왼쪽 패널 하단의 '시작하기 및 도움말'에 MCP 서버를 우아하게 개발할 수 있도록 준비한 자료가 있습니다. 도움이 되길 바랍니다.",
"guide.16.4": "대형 모델에 점점 더 많은 API와 SDK를 통합해 봅시다!", "guide.16.4": "대형 모델에 점점 더 많은 API와 SDK를 통합해 봅시다!",
"maybe-end": "종장?", "maybe-end": "종장?",
"finish": "종료" "finish": "종료",
"queue-time": "대기 시간"
} }

View File

@ -230,5 +230,6 @@
"guide.16.3": "Если вы пользователь плагина, в нижней части левой панели в разделе \"Начало работы и помощь\" есть некоторые материалы, которые мы подготовили, надеясь помочь вам в элегантной разработке вашего сервера MCP.", "guide.16.3": "Если вы пользователь плагина, в нижней части левой панели в разделе \"Начало работы и помощь\" есть некоторые материалы, которые мы подготовили, надеясь помочь вам в элегантной разработке вашего сервера MCP.",
"guide.16.4": "Давайте вместе интегрировать все больше и больше API и SDK в большую модель!", "guide.16.4": "Давайте вместе интегрировать все больше и больше API и SDK в большую модель!",
"maybe-end": "Заключительная глава?", "maybe-end": "Заключительная глава?",
"finish": "Конец" "finish": "Конец",
"queue-time": "Время ожидания"
} }

View File

@ -230,5 +230,6 @@
"guide.16.3": "如果是插件用户,左侧面板的最下面「入门与帮助」有一些我们准备好的资料,希望能帮到阁下优雅地开发你的 mcp 服务器。", "guide.16.3": "如果是插件用户,左侧面板的最下面「入门与帮助」有一些我们准备好的资料,希望能帮到阁下优雅地开发你的 mcp 服务器。",
"guide.16.4": "让我们一起把越来越多的 api 和 sdk 接入 大模型吧!", "guide.16.4": "让我们一起把越来越多的 api 和 sdk 接入 大模型吧!",
"maybe-end": "终章?", "maybe-end": "终章?",
"finish": "结束" "finish": "结束",
"queue-time": "排队时间"
} }

View File

@ -230,5 +230,6 @@
"guide.16.3": "如果是插件用戶,左側面板的最下面「入門與幫助」有一些我們準備好的資料,希望能幫到閣下優雅地開發你的 MCP 伺服器。", "guide.16.3": "如果是插件用戶,左側面板的最下面「入門與幫助」有一些我們準備好的資料,希望能幫到閣下優雅地開發你的 MCP 伺服器。",
"guide.16.4": "讓我們一起把越來越多的API和SDK接入大模型吧", "guide.16.4": "讓我們一起把越來越多的API和SDK接入大模型吧",
"maybe-end": "終章?", "maybe-end": "終章?",
"finish": "結束" "finish": "結束",
"queue-time": "排隊時間"
} }

View File

@ -5,26 +5,31 @@ import { reactive, ref } from 'vue';
import { makeUsageStatistic } from '@/components/main-panel/chat/core/usage'; import { makeUsageStatistic } from '@/components/main-panel/chat/core/usage';
export const llmSettingRef = ref<any>(null); export const llmSettingRef = ref<any>(null);
type NumberLike = string | number | undefined;
export const testPrompt = ref('you\'re a smart assistant, please write an article of at least 100 words to introduce mcp');
export const simpleTestResult = reactive<{ export const simpleTestResult = reactive<{
done: boolean, done: boolean,
start: boolean, start: boolean,
error: any, error: any,
tps: string | number | undefined tps: NumberLike,
outputTps: NumberLike
queueTime: NumberLike
}>({ }>({
done: false, done: false,
start: false, start: false,
error: '', error: '',
tps: undefined tps: undefined,
outputTps: undefined,
queueTime: undefined,
}); });
export async function makeSimpleTalk() { export async function makeSimpleTalk() {
simpleTestResult.done = false; simpleTestResult.done = false;
simpleTestResult.start = true; simpleTestResult.start = true;
simpleTestResult.tps = undefined; simpleTestResult.tps = undefined;
simpleTestResult.queueTime = undefined;
// 使用最简单的 hello 来测试
const testMessage = 'hello';
const loop = new TaskLoop(); const loop = new TaskLoop();
@ -44,6 +49,14 @@ export async function makeSimpleTalk() {
loop.setMaxEpochs(1); loop.setMaxEpochs(1);
let receiveTime = -1;
loop.registerOnChunk(() => {
if (receiveTime <= 0) {
receiveTime = Date.now();
}
});
loop.registerOnDone(() => { loop.registerOnDone(() => {
simpleTestResult.error = ''; simpleTestResult.error = '';
simpleTestResult.done = true; simpleTestResult.done = true;
@ -59,17 +72,19 @@ export async function makeSimpleTalk() {
}); });
const startTime = Date.now(); const startTime = Date.now();
await loop.start(chatStorage, testMessage); await loop.start(chatStorage, testPrompt.value);
const endTime = Date.now();
const costTime = (endTime - receiveTime) / 1000;
simpleTestResult.queueTime = (receiveTime - startTime) / 1000;
const costTime = (Date.now() - startTime!) / 1000;
const message = chatStorage.messages[chatStorage.messages.length - 1]; const message = chatStorage.messages[chatStorage.messages.length - 1];
console.log(chatStorage.messages);
console.log(message.extraInfo);
if (message?.extraInfo) { if (message?.extraInfo) {
const usage = message.extraInfo.usage; const usage = message.extraInfo.usage;
if (usage?.prompt_tokens && usage.completion_tokens) { if (usage?.prompt_tokens && usage.completion_tokens) {
const total = usage?.prompt_tokens + usage?.completion_tokens; // const total = usage?.prompt_tokens + usage?.completion_tokens;
const total = usage?.completion_tokens;
simpleTestResult.tps = (total / costTime).toFixed(2); simpleTestResult.tps = (total / costTime).toFixed(2);
} }
} }

View File

@ -66,15 +66,39 @@
{{ t('update-model-list') }} {{ t('update-model-list') }}
</el-button> </el-button>
<el-button <!-- 新增测试 prompt 下拉输入 -->
type="primary" <el-popover
id="test-llm-button" placement="top"
@click="makeSimpleTalk" width="400"
:loading="simpleTestResult.start" trigger="click"
v-model:visible="testPromptPopoverVisible"
> >
<span v-show="!simpleTestResult.start" class="iconfont icon-test"></span> <template #reference>
{{ t('test') }} <el-button
</el-button> type="primary"
id="test-llm-button"
:loading="simpleTestResult.start"
>
<span v-show="!simpleTestResult.start" class="iconfont icon-test"></span>
{{ t('test') }}
</el-button>
</template>
<div style="margin-bottom: 8px; font-weight: bold;">
{{ t('prompts') }}
</div>
<el-input
type="textarea"
v-model="testPrompt"
:rows="3"
:placeholder="t('test-prompt-placeholder') || '输入测试内容...'"
style="margin-bottom: 8px;"
clearable
/>
<div style="text-align: right;">
<el-button size="small" @click="testPromptPopoverVisible = false">{{ t('cancel') }}</el-button>
<el-button size="small" type="primary" @click="testPromptPopoverVisible = false; makeSimpleTalk()">{{ t('confirm') }}</el-button>
</div>
</el-popover>
<el-button <el-button
type="primary" type="primary"
@ -130,7 +154,7 @@ import { pinkLog } from './util';
import ConnectInterfaceOpenai from './connect-interface-openai.vue'; import ConnectInterfaceOpenai from './connect-interface-openai.vue';
import ConnectTest from './connect-test.vue'; import ConnectTest from './connect-test.vue';
import { llmSettingRef, makeSimpleTalk, simpleTestResult } from './api'; import { llmSettingRef, makeSimpleTalk, simpleTestResult, testPrompt } from './api';
import { useMessageBridge } from '@/api/message-bridge'; import { useMessageBridge } from '@/api/message-bridge';
import { mcpSetting } from '@/hook/mcp'; import { mcpSetting } from '@/hook/mcp';
@ -148,6 +172,7 @@ function saveLlmSetting() {
const currentDialogMode = ref(''); const currentDialogMode = ref('');
const dialogVisible = ref(false); const dialogVisible = ref(false);
const testPromptPopoverVisible = ref(false);
function addNewServer() { function addNewServer() {
newProviderForm.value = { newProviderForm.value = {

View File

@ -7,6 +7,9 @@
<div class="result-item" v-if="simpleTestResult.done"> <div class="result-item" v-if="simpleTestResult.done">
<span class="iconfont icon-dui"></span> <span class="iconfont icon-dui"></span>
<span>{{ " okey dockey :D" }}</span> <span>{{ " okey dockey :D" }}</span>
<span v-if="simpleTestResult.queueTime" class="queue-time">
{{ t('queue-time') }}: {{ simpleTestResult.queueTime }} s
</span>
<span v-if="simpleTestResult.tps" class="tps">{{ simpleTestResult.tps }} token/s</span> <span v-if="simpleTestResult.tps" class="tps">{{ simpleTestResult.tps }} token/s</span>
<span v-else class="tps">{{ t("server-not-support-statistic") }}</span> <span v-else class="tps">{{ t("server-not-support-statistic") }}</span>
</div> </div>
@ -82,4 +85,12 @@ console.log(llms[llmManager.currentModelIndex]);
margin-top: 15px; margin-top: 15px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.queue-time {
margin-left: 8px;
color: var(--foreground);
background-color: var(--el-fill-color-light);
padding: 2px 6px;
border-radius: 4px;
}
</style> </style>