修复工具调用的错误
This commit is contained in:
parent
ad857e6544
commit
f925da7d7d
@ -15,7 +15,7 @@ import MainPanel from '@/components/main-panel/index.vue';
|
|||||||
import { setDefaultCss } from './hook/css';
|
import { setDefaultCss } from './hook/css';
|
||||||
import { greenLog, pinkLog } from './views/setting/util';
|
import { greenLog, pinkLog } from './views/setting/util';
|
||||||
import { acquireVsCodeApi, useMessageBridge } from './api/message-bridge';
|
import { acquireVsCodeApi, useMessageBridge } from './api/message-bridge';
|
||||||
import { connectionArgs, connectionMethods, doConnect, launchConnect } from './views/connect/connection';
|
import { connectionArgs, connectionMethods, doConnect, launchConnect, loadEnvVar } from './views/connect/connection';
|
||||||
import { loadSetting } from './hook/setting';
|
import { loadSetting } from './hook/setting';
|
||||||
import { loadPanels } from './hook/panel';
|
import { loadPanels } from './hook/panel';
|
||||||
|
|
||||||
@ -29,13 +29,16 @@ bridge.addCommandListener('hello', data => {
|
|||||||
|
|
||||||
|
|
||||||
function initDebug() {
|
function initDebug() {
|
||||||
connectionArgs.commandString = 'node C:/Users/K/code/servers/src/puppeteer/dist/index.js';
|
connectionArgs.commandString = 'node /Users/bytedance/projects/mcp/servers/src/puppeteer/dist/index.js';
|
||||||
connectionMethods.current = 'STDIO';
|
connectionMethods.current = 'STDIO';
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
// 初始化 设置
|
// 初始化 设置
|
||||||
loadSetting();
|
loadSetting();
|
||||||
|
|
||||||
|
// 初始化环境变量
|
||||||
|
loadEnvVar();
|
||||||
|
|
||||||
// 尝试连接
|
// 尝试连接
|
||||||
await doConnect();
|
await doConnect();
|
||||||
|
|
||||||
@ -56,6 +59,9 @@ async function initProduce() {
|
|||||||
// 初始化 设置
|
// 初始化 设置
|
||||||
loadSetting();
|
loadSetting();
|
||||||
|
|
||||||
|
// 初始化环境变量
|
||||||
|
loadEnvVar();
|
||||||
|
|
||||||
// 尝试连接
|
// 尝试连接
|
||||||
await launchConnect();
|
await launchConnect();
|
||||||
|
|
||||||
|
@ -93,18 +93,6 @@ export default defineComponent({
|
|||||||
return JSON.stringify(obj1) === JSON.stringify(obj2)
|
return JSON.stringify(obj1) === JSON.stringify(obj2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动调整文本区域高度
|
|
||||||
const adjustTextareaHeight = () => {
|
|
||||||
nextTick(() => {
|
|
||||||
if (textareaRef.value) {
|
|
||||||
textareaRef.value.style.height = 'auto'
|
|
||||||
textareaRef.value.style.height = `${textareaRef.value.scrollHeight}px`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(inputValue, adjustTextareaHeight, { immediate: true })
|
|
||||||
|
|
||||||
const handleKeydown = (event: KeyboardEvent) => {
|
const handleKeydown = (event: KeyboardEvent) => {
|
||||||
if (event.key === '{') {
|
if (event.key === '{') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ToolItem } from "@/hook/type";
|
import { ToolCallContent, ToolItem } from "@/hook/type";
|
||||||
import { Ref, ref } from "vue";
|
import { Ref, ref } from "vue";
|
||||||
|
|
||||||
import type { OpenAI } from 'openai';
|
import type { OpenAI } from 'openai';
|
||||||
@ -24,8 +24,17 @@ export interface IExtraInfo {
|
|||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChatMessage {
|
export interface ToolMessage {
|
||||||
role: 'user' | 'assistant' | 'system' | 'tool';
|
role: 'tool';
|
||||||
|
content: ToolCallContent[];
|
||||||
|
tool_call_id?: string
|
||||||
|
name?: string // 工具名称,当 role 为 tool
|
||||||
|
tool_calls?: ToolCall[],
|
||||||
|
extraInfo: IExtraInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TextMessage {
|
||||||
|
role: 'user' | 'assistant' | 'system';
|
||||||
content: string;
|
content: string;
|
||||||
tool_call_id?: string
|
tool_call_id?: string
|
||||||
name?: string // 工具名称,当 role 为 tool
|
name?: string // 工具名称,当 role 为 tool
|
||||||
@ -33,6 +42,8 @@ export interface ChatMessage {
|
|||||||
extraInfo: IExtraInfo
|
extraInfo: IExtraInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ChatMessage = ToolMessage | TextMessage;
|
||||||
|
|
||||||
// 新增状态和工具数据
|
// 新增状态和工具数据
|
||||||
interface EnableToolItem {
|
interface EnableToolItem {
|
||||||
name: string;
|
name: string;
|
||||||
@ -69,7 +80,7 @@ export const allTools = ref<ToolItem[]>([]);
|
|||||||
export interface IRenderMessage {
|
export interface IRenderMessage {
|
||||||
role: 'user' | 'assistant/content' | 'assistant/tool_calls' | 'tool';
|
role: 'user' | 'assistant/content' | 'assistant/tool_calls' | 'tool';
|
||||||
content: string;
|
content: string;
|
||||||
toolResult?: string;
|
toolResult?: ToolCallContent[];
|
||||||
tool_calls?: ToolCall[];
|
tool_calls?: ToolCall[];
|
||||||
showJson?: Ref<boolean>;
|
showJson?: Ref<boolean>;
|
||||||
extraInfo: IExtraInfo;
|
extraInfo: IExtraInfo;
|
||||||
|
@ -5,9 +5,12 @@
|
|||||||
<div v-for="(message, index) in renderMessages" :key="index"
|
<div v-for="(message, index) in renderMessages" :key="index"
|
||||||
:class="['message-item', message.role.split('/')[0]]"
|
:class="['message-item', message.role.split('/')[0]]"
|
||||||
>
|
>
|
||||||
<div class="message-avatar" v-if="message.role.startsWith('assistant')">
|
<div class="message-avatar" v-if="message.role === 'assistant/content'">
|
||||||
<span class="iconfont icon-robot"></span>
|
<span class="iconfont icon-robot"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="message-avatar" v-else-if="message.role === 'assistant/tool_calls'">
|
||||||
|
<span class="iconfont icon-tool"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 用户输入的部分 -->
|
<!-- 用户输入的部分 -->
|
||||||
<div class="message-content" v-if="message.role === 'user'">
|
<div class="message-content" v-if="message.role === 'user'">
|
||||||
@ -27,22 +30,7 @@
|
|||||||
|
|
||||||
<!-- 正在加载的部分实时解析 markdown -->
|
<!-- 正在加载的部分实时解析 markdown -->
|
||||||
<div v-if="isLoading" class="message-item assistant">
|
<div v-if="isLoading" class="message-item assistant">
|
||||||
<div class="message-avatar">
|
<Message.StreamingBox :streaming-content="streamingContent" />
|
||||||
<span class="iconfont icon-chat"></span>
|
|
||||||
</div>
|
|
||||||
<div class="message-content">
|
|
||||||
<div class="message-role">
|
|
||||||
Agent
|
|
||||||
<span class="message-reminder">
|
|
||||||
正在生成答案
|
|
||||||
<span class="tool-loading iconfont icon-double-loading">
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="message-text">
|
|
||||||
<span v-html="waitingMarkdownToHtml(streamingContent)"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
@ -84,26 +72,16 @@ import { ElMessage, ScrollbarInstance } from 'element-plus';
|
|||||||
import { tabs } from '../panel';
|
import { tabs } from '../panel';
|
||||||
import { ChatMessage, ChatStorage, IRenderMessage, MessageState, ToolCall } from './chat';
|
import { ChatMessage, ChatStorage, IRenderMessage, MessageState, ToolCall } from './chat';
|
||||||
|
|
||||||
import Setting from './setting.vue';
|
|
||||||
|
|
||||||
// 引入 markdown.ts 中的函数
|
|
||||||
import { markdownToHtml } from './markdown';
|
|
||||||
import { TaskLoop } from './task-loop';
|
import { TaskLoop } from './task-loop';
|
||||||
import { llmManager, llms } from '@/views/setting/llm';
|
import { llmManager, llms } from '@/views/setting/llm';
|
||||||
|
|
||||||
import * as Message from './message';
|
import * as Message from './message';
|
||||||
|
import Setting from './setting.vue';
|
||||||
|
|
||||||
defineComponent({ name: 'chat' });
|
defineComponent({ name: 'chat' });
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
function waitingMarkdownToHtml(content: string) {
|
|
||||||
if (content) {
|
|
||||||
return markdownToHtml(content);
|
|
||||||
}
|
|
||||||
return '<span class="typing-cursor">|</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
tabId: {
|
tabId: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@ -358,6 +336,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
.message-avatar {
|
.message-avatar {
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
|
margin-top: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content {
|
.message-content {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import Assistant from "./assistant.vue";
|
import Assistant from "./assistant.vue";
|
||||||
import Toolcall from "./toolcall.vue";
|
import Toolcall from "./toolcall.vue";
|
||||||
import User from "./user.vue";
|
import User from "./user.vue";
|
||||||
export { Assistant, Toolcall, User };
|
import StreamingBox from "./streaming-box.vue";
|
||||||
|
export { Assistant, Toolcall, User, StreamingBox };
|
@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<div class="message-avatar">
|
||||||
|
<span class="iconfont icon-chat"></span>
|
||||||
|
</div>
|
||||||
|
<div class="message-content">
|
||||||
|
<div class="message-role">
|
||||||
|
Agent
|
||||||
|
<span class="message-reminder">
|
||||||
|
正在生成答案
|
||||||
|
<span class="tool-loading iconfont icon-double-loading">
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="message-text">
|
||||||
|
<span v-html="waitingMarkdownToHtml(streamingContent)"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineProps } from 'vue';
|
||||||
|
import { markdownToHtml } from '../markdown';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
streamingContent: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function waitingMarkdownToHtml(content: string) {
|
||||||
|
if (content) {
|
||||||
|
return markdownToHtml(content);
|
||||||
|
}
|
||||||
|
return '<span class="typing-cursor">|</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
@ -1,8 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="message-role">
|
<div class="message-role">
|
||||||
Agent
|
|
||||||
<span class="message-reminder" v-if="!props.message.toolResult">
|
<span class="message-reminder" v-if="!props.message.toolResult">
|
||||||
正在使用工具
|
Agent 正在使用工具
|
||||||
<span class="tool-loading iconfont icon-double-loading">
|
<span class="tool-loading iconfont icon-double-loading">
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@ -16,7 +15,6 @@
|
|||||||
<template #title>
|
<template #title>
|
||||||
<div class="tool-calls">
|
<div class="tool-calls">
|
||||||
<div class="tool-call-header">
|
<div class="tool-call-header">
|
||||||
<span class="tool-type">{{ 'tool use' }}</span>
|
|
||||||
<span class="tool-name">{{ props.message.tool_calls[0].function.name }}</span>
|
<span class="tool-name">{{ props.message.tool_calls[0].function.name }}</span>
|
||||||
<el-button size="small" @click="createTest(props.message.tool_calls[0])">
|
<el-button size="small" @click="createTest(props.message.tool_calls[0])">
|
||||||
<span class="iconfont icon-send"></span>
|
<span class="iconfont icon-send"></span>
|
||||||
@ -34,24 +32,30 @@
|
|||||||
|
|
||||||
<!-- 工具调用结果 -->
|
<!-- 工具调用结果 -->
|
||||||
<div v-if="props.message.toolResult">
|
<div v-if="props.message.toolResult">
|
||||||
<div class="tool-call-header">
|
<div class="tool-call-header result">
|
||||||
<span class="tool-name" :class="{ 'error': !isValidJson }">
|
<span class="tool-name" :class="{ 'error': !isValid }">
|
||||||
{{ isValidJson ? '响应': '错误' }}
|
{{ isValid ? '响应': '错误' }}
|
||||||
|
|
||||||
|
<el-button v-if="!isValid" size="small"
|
||||||
|
@click="gotoIssue()"
|
||||||
|
>
|
||||||
|
反馈
|
||||||
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
<span style="width: 200px;" class="tools-dialog-container" v-if="isValidJson">
|
<span style="width: 200px;" class="tools-dialog-container" v-if="isValid">
|
||||||
<el-switch v-model="props.message.showJson!.value" inline-prompt active-text="JSON"
|
<el-switch v-model="props.message.showJson!.value" inline-prompt active-text="JSON"
|
||||||
inactive-text="Text" style="margin-left: 10px; width: 200px;"
|
inactive-text="Text" style="margin-left: 10px; width: 200px;"
|
||||||
:inactive-action-style="'backgroundColor: var(--sidebar)'" />
|
:inactive-action-style="'backgroundColor: var(--sidebar)'" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-result" v-if="isValidJson">
|
<div class="tool-result" v-if="isValid">
|
||||||
<div v-if="props.message.showJson!.value" class="tool-result-content">
|
<div v-if="props.message.showJson!.value" class="tool-result-content">
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<div v-html="jsonResultToHtml(props.message.toolResult)"></div>
|
<div v-html="toHtml(props.message.toolResult)"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
<div v-for="(item, index) in JSON.parse(props.message.toolResult)" :key="index"
|
<div v-for="(item, index) in props.message.toolResult" :key="index"
|
||||||
class="response-item"
|
class="response-item"
|
||||||
>
|
>
|
||||||
<el-scrollbar width="100%">
|
<el-scrollbar width="100%">
|
||||||
@ -68,19 +72,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="tool-result" :class="{ 'error': !isValidJson }">
|
<div v-else class="tool-result" :class="{ 'error': !isValid }">
|
||||||
<div class="tool-result-content">
|
<div class="tool-result-content"
|
||||||
<div class="inner">
|
v-for="(error, index) of collectErrors"
|
||||||
{{ props.message.toolResult }}
|
:key="index"
|
||||||
</div>
|
>
|
||||||
|
{{ error }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<MessageMeta :message="message" />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
</div>
|
</div>
|
||||||
<MessageMeta :message="message" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -90,6 +98,7 @@ import MessageMeta from './message-meta.vue';
|
|||||||
import { markdownToHtml } from '../markdown';
|
import { markdownToHtml } from '../markdown';
|
||||||
import { createTest } from '@/views/setting/llm';
|
import { createTest } from '@/views/setting/llm';
|
||||||
import { IRenderMessage, MessageState } from '../chat';
|
import { IRenderMessage, MessageState } from '../chat';
|
||||||
|
import { ToolCallContent } from '@/hook/type';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
message: {
|
message: {
|
||||||
@ -112,22 +121,54 @@ watch(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const jsonResultToHtml = (jsonString: string) => {
|
const toHtml = (toolResult: ToolCallContent[]) => {
|
||||||
const formattedJson = JSON.stringify(JSON.parse(jsonString), null, 2);
|
const formattedJson = JSON.stringify(toolResult, null, 2);
|
||||||
const html = markdownToHtml('```json\n' + formattedJson + '\n```');
|
const html = markdownToHtml('```json\n' + formattedJson + '\n```');
|
||||||
return html;
|
return html;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const jsonResultToHtml = (jsonResult: string) => {
|
||||||
const isValidJson = computed(() => {
|
|
||||||
try {
|
try {
|
||||||
JSON.parse(props.message.toolResult || '');
|
const formattedJson = JSON.stringify(JSON.parse(jsonResult), null, 2);
|
||||||
|
const html = markdownToHtml('```json\n' + formattedJson + '\n```');
|
||||||
|
return html;
|
||||||
|
} catch (error) {
|
||||||
|
const html = markdownToHtml('```json\n' + jsonResult + '\n```');
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function gotoIssue() {
|
||||||
|
window.open('https://github.com/LSTM-Kirigaya/openmcp-client/issues', '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
|
const isValid = computed(() => {
|
||||||
|
try {
|
||||||
|
const item = props.message.toolResult![0];
|
||||||
|
if (item.type === 'error') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const collectErrors = computed(() => {
|
||||||
|
const errorMessages = [];
|
||||||
|
try {
|
||||||
|
const errorResults = props.message.toolResult!.filter(item => item.type === 'error');
|
||||||
|
console.log(errorResults);
|
||||||
|
|
||||||
|
for (const errorResult of errorResults) {
|
||||||
|
errorMessages.push(errorResult.text);
|
||||||
|
}
|
||||||
|
return errorMessages;
|
||||||
|
} catch {
|
||||||
|
return errorMessages;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -140,10 +181,17 @@ const isValidJson = computed(() => {
|
|||||||
border-left: 3px solid var(--el-color-error);
|
border-left: 3px solid var(--el-color-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-calls {
|
.message-text .el-collapse-item__header {
|
||||||
margin-top: 10px;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-text .el-collapse-item__content {
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.tool-call-item {
|
.tool-call-item {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
@ -151,6 +199,9 @@ const isValidJson = computed(() => {
|
|||||||
.tool-call-header {
|
.tool-call-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-call-header.result {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<el-tooltip :content="t('tool-use')" placement="top">
|
<el-tooltip :content="t('tool-use')" placement="top">
|
||||||
<div class="setting-button" :class="{ 'active': availableToolsNum > 0 }" size="small"
|
<div class="setting-button" :class="{ 'active': availableToolsNum > 0 }" size="small"
|
||||||
@click="toggleTools">
|
@click="toggleTools">
|
||||||
<span class="iconfont icon-tool badge-outer" v-if="availableToolsNum > 0">
|
<span class="iconfont icon-tool badge-outer">
|
||||||
<span class="badge-inner">
|
<span class="badge-inner">
|
||||||
{{ availableToolsNum }}
|
{{ availableToolsNum }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -46,20 +46,15 @@ export class TaskLoop {
|
|||||||
const toolResponse = await callTool(toolName, toolArgs);
|
const toolResponse = await callTool(toolName, toolArgs);
|
||||||
|
|
||||||
if (!toolResponse.isError) {
|
if (!toolResponse.isError) {
|
||||||
const content = JSON.stringify(toolResponse.content);
|
// const content = JSON.stringify(toolResponse.content);
|
||||||
return {
|
return {
|
||||||
content,
|
content: toolResponse.content,
|
||||||
state: MessageState.Success,
|
state: MessageState.Success
|
||||||
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
this.onError({
|
|
||||||
state: MessageState.ToolCall,
|
|
||||||
msg: `工具调用失败: ${toolResponse.content}`
|
|
||||||
});
|
|
||||||
console.error(toolResponse.content);
|
|
||||||
return {
|
return {
|
||||||
content: toolResponse.content.toString(),
|
content: toolResponse.content,
|
||||||
state: MessageState.ToolCall
|
state: MessageState.ToolCall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,13 +65,27 @@ export class TaskLoop {
|
|||||||
msg: `工具调用失败: ${(error as Error).message}`
|
msg: `工具调用失败: ${(error as Error).message}`
|
||||||
});
|
});
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: (error as Error).message,
|
content: [{
|
||||||
|
type: 'error',
|
||||||
|
text: this.parseErrorObject(error)
|
||||||
|
}],
|
||||||
state: MessageState.ToolCall
|
state: MessageState.ToolCall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private parseErrorObject(error: any): string {
|
||||||
|
if (typeof error === 'string') {
|
||||||
|
return error;
|
||||||
|
} else if (typeof error === 'object') {
|
||||||
|
return JSON.stringify(error, null, 2);
|
||||||
|
} else {
|
||||||
|
return error.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private handleChunkDeltaContent(chunk: ChatCompletionChunk) {
|
private handleChunkDeltaContent(chunk: ChatCompletionChunk) {
|
||||||
const content = chunk.choices[0]?.delta?.content || '';
|
const content = chunk.choices[0]?.delta?.content || '';
|
||||||
if (content) {
|
if (content) {
|
||||||
@ -271,7 +280,11 @@ export class TaskLoop {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const toolCallResult = await this.handleToolCalls(this.streamingToolCalls.value);
|
const toolCallResult = await this.handleToolCalls(this.streamingToolCalls.value);
|
||||||
if (toolCallResult) {
|
|
||||||
|
console.log(toolCallResult);
|
||||||
|
|
||||||
|
|
||||||
|
if (toolCallResult.content) {
|
||||||
const toolCall = this.streamingToolCalls.value[0];
|
const toolCall = this.streamingToolCalls.value[0];
|
||||||
|
|
||||||
tabStorage.messages.push({
|
tabStorage.messages.push({
|
||||||
|
@ -12,7 +12,7 @@ export const promptsManager = reactive<{
|
|||||||
export interface PromptStorage {
|
export interface PromptStorage {
|
||||||
currentPromptName: string;
|
currentPromptName: string;
|
||||||
lastPromptGetResponse?: PromptsGetResponse;
|
lastPromptGetResponse?: PromptsGetResponse;
|
||||||
formData: Record<string, number | boolean | string>;
|
formData: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parsePromptTemplate(template: string): {
|
export function parsePromptTemplate(template: string): {
|
||||||
|
@ -13,7 +13,7 @@ export const resourcesManager = reactive<{
|
|||||||
export interface ResourceStorage {
|
export interface ResourceStorage {
|
||||||
currentResourceName: string;
|
currentResourceName: string;
|
||||||
lastResourceReadResponse?: ResourcesReadResponse;
|
lastResourceReadResponse?: ResourcesReadResponse;
|
||||||
formData: Record<string, number | string | boolean>;
|
formData: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
v-model="tabStorage.formData[name]"
|
v-model="tabStorage.formData[name]"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
<k-input-object
|
<k-input-object
|
||||||
v-else-if="property.type === 'object'"
|
v-else-if="property.type === 'object'"
|
||||||
v-model="tabStorage.formData[name]"
|
v-model="tabStorage.formData[name]"
|
||||||
|
@ -3,34 +3,33 @@
|
|||||||
<span>
|
<span>
|
||||||
<span>{{ t('response') }}</span>
|
<span>{{ t('response') }}</span>
|
||||||
<span style="width: 200px;">
|
<span style="width: 200px;">
|
||||||
<el-switch
|
<el-switch v-model="showRawJson" inline-prompt active-text="JSON" inactive-text="Text"
|
||||||
v-model="showRawJson"
|
|
||||||
inline-prompt
|
|
||||||
active-text="JSON"
|
|
||||||
inactive-text="Text"
|
|
||||||
style="margin-left: 10px; width: 200px;"
|
style="margin-left: 10px; width: 200px;"
|
||||||
:inactive-action-style="'backgroundColor: var(--sidebar)'"
|
:inactive-action-style="'backgroundColor: var(--sidebar)'" />
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<el-scrollbar height="500px">
|
<el-scrollbar height="500px">
|
||||||
<div
|
<div class="output-content" contenteditable="false">
|
||||||
class="output-content"
|
|
||||||
contenteditable="false"
|
<!-- TODO: 更加稳定,现在通过下面这个来判断上一次执行结果是否成功 -->
|
||||||
>
|
<div v-if="typeof tabStorage.lastToolCallResponse === 'string'" class="error-tool-call">
|
||||||
<template v-if="!showRawJson">
|
<span>
|
||||||
<template v-if="tabStorage.lastToolCallResponse?.isError">
|
{{ tabStorage.lastToolCallResponse }}
|
||||||
<span style="color: var(--el-color-error)">
|
</span>
|
||||||
{{ tabStorage.lastToolCallResponse.content.map(c => c.text).join('\n') }}
|
</div>
|
||||||
</span>
|
|
||||||
|
<div v-else>
|
||||||
|
<!-- 展示原本的信息 -->
|
||||||
|
<template v-if="!showRawJson">
|
||||||
|
{{tabStorage.lastToolCallResponse?.content.map(c => c.text).join('\n')}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 展示 json -->
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ tabStorage.lastToolCallResponse?.content.map(c => c.text).join('\n') }}
|
{{ formattedJson }}
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</div>
|
||||||
<template v-else>
|
|
||||||
{{ formattedJson }}
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
@ -60,17 +59,15 @@ const showRawJson = ref(false);
|
|||||||
|
|
||||||
const formattedJson = computed(() => {
|
const formattedJson = computed(() => {
|
||||||
try {
|
try {
|
||||||
|
if (typeof tabStorage.lastToolCallResponse === 'string') {
|
||||||
|
return tabStorage.lastToolCallResponse;
|
||||||
|
}
|
||||||
return JSON.stringify(tabStorage.lastToolCallResponse, null, 2);
|
return JSON.stringify(tabStorage.lastToolCallResponse, null, 2);
|
||||||
} catch {
|
} catch {
|
||||||
return 'Invalid JSON';
|
return 'Invalid JSON';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const jsonResultToHtml = (jsonString: string) => {
|
|
||||||
const formattedJson = JSON.stringify(JSON.parse(jsonString), null, 2);
|
|
||||||
const html = markdownToHtml('```json\n' + formattedJson + '\n```');
|
|
||||||
return html;
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -93,7 +90,7 @@ const jsonResultToHtml = (jsonString: string) => {
|
|||||||
background-color: var(--sidebar);
|
background-color: var(--sidebar);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-logger > span:first-child {
|
.tool-logger>span:first-child {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -113,4 +110,10 @@ const jsonResultToHtml = (jsonString: string) => {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
background-color: var(--sidebar);
|
background-color: var(--sidebar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-tool-call {
|
||||||
|
background-color: rgba(245, 108, 108, 0.5);
|
||||||
|
padding: 5px 9px;
|
||||||
|
border-radius: .5em;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -11,7 +11,7 @@ export const toolsManager = reactive<{
|
|||||||
|
|
||||||
export interface ToolStorage {
|
export interface ToolStorage {
|
||||||
currentToolName: string;
|
currentToolName: string;
|
||||||
lastToolCallResponse?: ToolCallResponse;
|
lastToolCallResponse?: ToolCallResponse | string;
|
||||||
formData: Record<string, any>;
|
formData: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ export function callTool(toolName: string, toolArgs: Record<string, any>) {
|
|||||||
console.log(data.msg);
|
console.log(data.msg);
|
||||||
|
|
||||||
if (data.code !== 200) {
|
if (data.code !== 200) {
|
||||||
reject(new Error(data.msg + ''));
|
resolve(data.msg);
|
||||||
} else {
|
} else {
|
||||||
resolve(data.msg);
|
resolve(data.msg);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ interface TypeAble {
|
|||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDefaultValue(property: TypeAble) {
|
export function getDefaultValue(property: TypeAble): any {
|
||||||
if (property.type === 'number' || property.type === 'integer') {
|
if (property.type === 'number' || property.type === 'integer') {
|
||||||
return 0;
|
return 0;
|
||||||
} else if (property.type === 'boolean') {
|
} else if (property.type === 'boolean') {
|
||||||
|
@ -81,6 +81,7 @@ export interface PromptsGetResponse {
|
|||||||
export interface ToolCallContent {
|
export interface ToolCallContent {
|
||||||
type: string;
|
type: string;
|
||||||
text: string;
|
text: string;
|
||||||
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ToolCallResponse {
|
export interface ToolCallResponse {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
OpenMCP Client 0.0.4 由 OpenMCP@<a href="https://www.zhihu.com/people/can-meng-zhong-de-che-xian">锦恢</a> 开发
|
OpenMCP Client 0.0.5 由 OpenMCP@<a href="https://www.zhihu.com/people/can-meng-zhong-de-che-xian">锦恢</a> 开发
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -389,3 +389,74 @@ export function getServerVersion() {
|
|||||||
export const envVarStatus = {
|
export const envVarStatus = {
|
||||||
launched: false
|
launched: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function lookupEnvVar(varNames: string[]) {
|
||||||
|
const bridge = useMessageBridge();
|
||||||
|
|
||||||
|
return new Promise<string[] | undefined>((resolve, reject) => {
|
||||||
|
bridge.addCommandListener('lookup-env-var', data => {
|
||||||
|
const { code, msg } = data;
|
||||||
|
|
||||||
|
if (code === 200) {
|
||||||
|
connectionResult.logString.push({
|
||||||
|
type: 'info',
|
||||||
|
message: '预设环境变量同步完成'
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve(msg);
|
||||||
|
} else {
|
||||||
|
connectionResult.logString.push({
|
||||||
|
type: 'error',
|
||||||
|
message: '预设环境变量同步失败: ' + msg
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve(undefined);
|
||||||
|
}
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
|
console.log(varNames);
|
||||||
|
|
||||||
|
|
||||||
|
bridge.postMessage({
|
||||||
|
command: 'lookup-env-var',
|
||||||
|
data: {
|
||||||
|
keys: varNames
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function handleEnvSwitch(enabled: boolean) {
|
||||||
|
const presetVars = ['HOME', 'LOGNAME', 'PATH', 'SHELL', 'TERM', 'USER'];
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
const values = await lookupEnvVar(presetVars);
|
||||||
|
|
||||||
|
if (values) {
|
||||||
|
// 将 key values 合并进 connectionEnv.data 中
|
||||||
|
// 若已有相同的 key, 则替换 value
|
||||||
|
for (let i = 0; i < presetVars.length; i++) {
|
||||||
|
const key = presetVars[i];
|
||||||
|
const value = values[i];
|
||||||
|
const sameNameItems = connectionEnv.data.filter(item => item.key === key);
|
||||||
|
if (sameNameItems.length > 0) {
|
||||||
|
const conflictItem = sameNameItems[0];
|
||||||
|
conflictItem.value = value;
|
||||||
|
} else {
|
||||||
|
connectionEnv.data.push({
|
||||||
|
key: key, value: value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 清空 connectionEnv.data 中所有 key 为 presetVars 的项
|
||||||
|
const reserveItems = connectionEnv.data.filter(item => !presetVars.includes(item.key));
|
||||||
|
connectionEnv.data = reserveItems;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadEnvVar() {
|
||||||
|
return await handleEnvSwitch(true);
|
||||||
|
}
|
@ -42,51 +42,13 @@
|
|||||||
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, onMounted, ref } from 'vue';
|
import { defineComponent, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { connectionEnv, connectionResult, EnvItem, envVarStatus } from './connection';
|
import { connectionEnv, EnvItem, handleEnvSwitch } from './connection';
|
||||||
import { useMessageBridge } from '@/api/message-bridge';
|
|
||||||
|
|
||||||
defineComponent({ name: 'env-var' });
|
defineComponent({ name: 'env-var' });
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const bridge = useMessageBridge();
|
|
||||||
|
|
||||||
function lookupEnvVar(varNames: string[]) {
|
|
||||||
console.log('enter');
|
|
||||||
|
|
||||||
return new Promise<string[] | undefined>((resolve, reject) => {
|
|
||||||
bridge.addCommandListener('lookup-env-var', data => {
|
|
||||||
const { code, msg } = data;
|
|
||||||
|
|
||||||
if (code === 200) {
|
|
||||||
connectionResult.logString.push({
|
|
||||||
type: 'info',
|
|
||||||
message: '预设环境变量同步完成'
|
|
||||||
});
|
|
||||||
|
|
||||||
resolve(msg);
|
|
||||||
} else {
|
|
||||||
connectionResult.logString.push({
|
|
||||||
type: 'error',
|
|
||||||
message: '预设环境变量同步失败: ' + msg
|
|
||||||
});
|
|
||||||
|
|
||||||
resolve(undefined);
|
|
||||||
}
|
|
||||||
}, { once: true });
|
|
||||||
|
|
||||||
console.log(varNames);
|
|
||||||
|
|
||||||
|
|
||||||
bridge.postMessage({
|
|
||||||
command: 'lookup-env-var',
|
|
||||||
data: {
|
|
||||||
keys: varNames
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 添加环境变量
|
* @description 添加环境变量
|
||||||
@ -126,46 +88,6 @@ function deleteEnvVar(option: EnvItem) {
|
|||||||
|
|
||||||
const envEnabled = ref(true);
|
const envEnabled = ref(true);
|
||||||
|
|
||||||
async function handleEnvSwitch(enabled: boolean) {
|
|
||||||
const presetVars = ['HOME', 'LOGNAME', 'PATH', 'SHELL', 'TERM', 'USER'];
|
|
||||||
|
|
||||||
if (enabled) {
|
|
||||||
const values = await lookupEnvVar(presetVars);
|
|
||||||
|
|
||||||
if (values) {
|
|
||||||
// 将 key values 合并进 connectionEnv.data 中
|
|
||||||
// 若已有相同的 key, 则替换 value
|
|
||||||
for (let i = 0; i < presetVars.length; i++) {
|
|
||||||
const key = presetVars[i];
|
|
||||||
const value = values[i];
|
|
||||||
const sameNameItems = connectionEnv.data.filter(item => item.key === key);
|
|
||||||
if (sameNameItems.length > 0) {
|
|
||||||
const conflictItem = sameNameItems[0];
|
|
||||||
conflictItem.value = value;
|
|
||||||
} else {
|
|
||||||
connectionEnv.data.push({
|
|
||||||
key: key, value: value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 清空 connectionEnv.data 中所有 key 为 presetVars 的项
|
|
||||||
const reserveItems = connectionEnv.data.filter(item => !presetVars.includes(item.key));
|
|
||||||
connectionEnv.data = reserveItems;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
if (envVarStatus.launched) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleEnvSwitch(envEnabled.value);
|
|
||||||
envVarStatus.launched = true;
|
|
||||||
}, 200);
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
1
service/.gitignore
vendored
1
service/.gitignore
vendored
@ -25,3 +25,4 @@ config.json
|
|||||||
setting.json
|
setting.json
|
||||||
|
|
||||||
tabs.example-servers/puppeteer.json
|
tabs.example-servers/puppeteer.json
|
||||||
|
*.traineddata
|
73
service/package-lock.json
generated
73
service/package-lock.json
generated
@ -20,6 +20,7 @@
|
|||||||
"pako": "^2.1.0",
|
"pako": "^2.1.0",
|
||||||
"pino": "^9.6.0",
|
"pino": "^9.6.0",
|
||||||
"pino-pretty": "^13.0.0",
|
"pino-pretty": "^13.0.0",
|
||||||
|
"tesseract.js": "^6.0.1",
|
||||||
"ws": "^8.18.1"
|
"ws": "^8.18.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -768,6 +769,12 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bmp-js": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "1.20.3",
|
"version": "1.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||||
@ -1598,6 +1605,12 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/idb-keyval": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/inflight": {
|
"node_modules/inflight": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
@ -1692,6 +1705,12 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/is-promise/-/is-promise-4.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/is-promise/-/is-promise-4.0.0.tgz",
|
||||||
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="
|
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/is-url": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/isexe": {
|
"node_modules/isexe": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
|
||||||
@ -2028,6 +2047,15 @@
|
|||||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/opencollective-postinstall": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"opencollective-postinstall": "index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pako": {
|
"node_modules/pako": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
|
||||||
@ -2262,6 +2290,12 @@
|
|||||||
"node": ">= 12.13.0"
|
"node": ">= 12.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/regenerator-runtime": {
|
||||||
|
"version": "0.13.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||||
|
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.10",
|
"version": "1.22.10",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||||
@ -2604,6 +2638,30 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tesseract.js": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-/sPvMvrCtgxnNRCjbTYbr7BRu0yfWDsMZQ2a/T5aN/L1t8wUQN6tTWv6p6FwzpoEBA0jrN2UD2SX4QQFRdoDbA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"bmp-js": "^0.1.0",
|
||||||
|
"idb-keyval": "^6.2.0",
|
||||||
|
"is-url": "^1.2.4",
|
||||||
|
"node-fetch": "^2.6.9",
|
||||||
|
"opencollective-postinstall": "^2.0.3",
|
||||||
|
"regenerator-runtime": "^0.13.3",
|
||||||
|
"tesseract.js-core": "^6.0.0",
|
||||||
|
"wasm-feature-detect": "^1.2.11",
|
||||||
|
"zlibjs": "^0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tesseract.js-core": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-1Qncm/9oKM7xgrQXZXNB+NRh19qiXGhxlrR8EwFbK5SaUbPZnS5OMtP/ghtqfd23hsr1ZvZbZjeuAGcMxd/ooA==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/thread-stream": {
|
"node_modules/thread-stream": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
|
||||||
@ -2820,6 +2878,12 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wasm-feature-detect": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/web-streams-polyfill": {
|
"node_modules/web-streams-polyfill": {
|
||||||
"version": "4.0.0-beta.3",
|
"version": "4.0.0-beta.3",
|
||||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
|
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
|
||||||
@ -2905,6 +2969,15 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/zlibjs": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz",
|
||||||
|
"integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/zod": {
|
"node_modules/zod": {
|
||||||
"version": "3.24.2",
|
"version": "3.24.2",
|
||||||
"resolved": "https://registry.npmmirror.com/zod/-/zod-3.24.2.tgz",
|
"resolved": "https://registry.npmmirror.com/zod/-/zod-3.24.2.tgz",
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
"pako": "^2.1.0",
|
"pako": "^2.1.0",
|
||||||
"pino": "^9.6.0",
|
"pino": "^9.6.0",
|
||||||
"pino-pretty": "^13.0.0",
|
"pino-pretty": "^13.0.0",
|
||||||
|
"tesseract.js": "^6.0.1",
|
||||||
"ws": "^8.18.1"
|
"ws": "^8.18.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { PostMessageble } from "../adapter";
|
import { PostMessageble } from "../hook/adapter";
|
||||||
import { MCPClient } from "./connect";
|
import { MCPClient } from "../hook/client";
|
||||||
|
|
||||||
|
|
||||||
export async function lookupEnvVarHandler(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
export async function lookupEnvVarHandler(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
import { PostMessageble } from '../adapter';
|
import { PostMessageble } from '../hook/adapter';
|
||||||
import { connect, MCPClient, type MCPOptions } from './connect';
|
import { connect, MCPClient, type MCPOptions } from '../hook/client';
|
||||||
import { lookupEnvVarHandler } from './env-var';
|
import { lookupEnvVarHandler } from './env-var';
|
||||||
import { callTool, getPrompt, getServerVersion, listPrompts, listResources, listResourceTemplates, listTools, readResource } from './handler';
|
import { callTool, getPrompt, getServerVersion, listPrompts, listResources, listResourceTemplates, listTools, readResource } from './mcp-server';
|
||||||
import { chatCompletionHandler } from './llm';
|
import { chatCompletionHandler } from './llm';
|
||||||
import { panelLoadHandler, panelSaveHandler } from './panel';
|
import { panelLoadHandler, panelSaveHandler } from './panel';
|
||||||
import { settingLoadHandler, settingSaveHandler } from './setting';
|
import { settingLoadHandler, settingSaveHandler } from './setting';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { OpenAI } from 'openai';
|
import { OpenAI } from 'openai';
|
||||||
import { MCPClient } from './connect';
|
import { MCPClient } from '../hook/client';
|
||||||
import { PostMessageble } from '../adapter';
|
import { PostMessageble } from '../hook/adapter';
|
||||||
|
|
||||||
let currentStream: AsyncIterable<any> | null = null;
|
let currentStream: AsyncIterable<any> | null = null;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { PostMessageble } from "../adapter";
|
import { PostMessageble } from "../hook/adapter";
|
||||||
import { MCPClient } from "./connect";
|
import { MCPClient } from "../hook/client";
|
||||||
|
|
||||||
// ==================== 接口定义 ====================
|
// ==================== 接口定义 ====================
|
||||||
export interface GetPromptOption {
|
export interface GetPromptOption {
|
0
service/src/controller/ocr.ts
Normal file
0
service/src/controller/ocr.ts
Normal file
@ -1,6 +1,6 @@
|
|||||||
import { PostMessageble } from '../adapter';
|
import { PostMessageble } from '../hook/adapter';
|
||||||
import { loadConfig, loadTabSaveConfig, saveConfig, saveTabSaveConfig } from '../util';
|
import { loadConfig, loadTabSaveConfig, saveConfig, saveTabSaveConfig } from '../hook/setting';
|
||||||
import { MCPClient } from './connect';
|
import { MCPClient } from '../hook/client';
|
||||||
|
|
||||||
export async function panelSaveHandler(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
export async function panelSaveHandler(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
||||||
try {
|
try {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { PostMessageble } from '../adapter';
|
import { PostMessageble } from '../hook/adapter';
|
||||||
import { loadConfig, saveConfig } from '../util';
|
import { loadConfig, saveConfig } from '../hook/setting';
|
||||||
import { MCPClient } from './connect';
|
import { MCPClient } from '../hook/client';
|
||||||
|
|
||||||
export async function settingSaveHandler(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
export async function settingSaveHandler(client: MCPClient | undefined, data: any, webview: PostMessageble) {
|
||||||
try {
|
try {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { PostMessageble } from "../adapter";
|
import { PostMessageble } from "../hook/adapter";
|
||||||
import { MCPClient } from "./connect";
|
import { MCPClient } from "../hook/client";
|
||||||
|
|
||||||
export function ping(client: MCPClient | undefined, webview: PostMessageble) {
|
export function ping(client: MCPClient | undefined, webview: PostMessageble) {
|
||||||
if (!client) {
|
if (!client) {
|
||||||
|
22
service/src/hook/ocr.ts
Normal file
22
service/src/hook/ocr.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import Tesseract from 'tesseract.js';
|
||||||
|
|
||||||
|
async function tesseractOCR(
|
||||||
|
imagePath: string,
|
||||||
|
logger: (message: Tesseract.LoggerMessage) => void,
|
||||||
|
lang: string = 'eng+chi_sim'
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const { data: { text } } = await Tesseract.recognize(
|
||||||
|
imagePath,
|
||||||
|
lang,
|
||||||
|
{
|
||||||
|
logger
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return text;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('OCR error:', error);
|
||||||
|
}
|
||||||
|
return '无法识别图片';
|
||||||
|
}
|
@ -2,7 +2,7 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import { llms } from './llm';
|
import { llms } from './llm';
|
||||||
import { IServerVersion } from './controller/connect';
|
import { IServerVersion } from './client';
|
||||||
|
|
||||||
export let VSCODE_WORKSPACE = '';
|
export let VSCODE_WORKSPACE = '';
|
||||||
|
|
@ -1,3 +1,12 @@
|
|||||||
|
// server/src/types.ts
|
||||||
|
export interface IMessage {
|
||||||
|
type: string;
|
||||||
|
data: Record<string, unknown>;
|
||||||
|
timestamp?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MessageHandler = (message: IMessage) => void;
|
||||||
|
|
||||||
// ==================== 基础类型定义 ====================
|
// ==================== 基础类型定义 ====================
|
||||||
export interface SchemaProperty {
|
export interface SchemaProperty {
|
||||||
title: string;
|
title: string;
|
@ -1,5 +1,5 @@
|
|||||||
export { messageController } from './controller';
|
export { messageController } from './controller';
|
||||||
export { VSCodeWebViewLike } from './adapter';
|
export { VSCodeWebViewLike } from './hook/adapter';
|
||||||
export { setVscodeWorkspace } from './util';
|
export { setVscodeWorkspace } from './hook/setting';
|
||||||
// TODO: 更加规范
|
// TODO: 更加规范
|
||||||
export { client } from './controller';
|
export { client } from './controller';
|
@ -1,8 +0,0 @@
|
|||||||
// server/src/types.ts
|
|
||||||
export interface IMessage {
|
|
||||||
type: string;
|
|
||||||
data: Record<string, unknown>;
|
|
||||||
timestamp?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MessageHandler = (message: IMessage) => void;
|
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"currentIndex": 0,
|
"currentIndex": 1,
|
||||||
"tabs": [
|
"tabs": [
|
||||||
{
|
{
|
||||||
"name": "交互测试",
|
"name": "交互测试",
|
||||||
@ -171,7 +171,7 @@
|
|||||||
"storage": {
|
"storage": {
|
||||||
"currentToolName": "get_weather_by_city_code",
|
"currentToolName": "get_weather_by_city_code",
|
||||||
"formData": {
|
"formData": {
|
||||||
"city_code": 101210101
|
"city_code": 0
|
||||||
},
|
},
|
||||||
"lastToolCallResponse": {
|
"lastToolCallResponse": {
|
||||||
"content": [
|
"content": [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user