增加 task-loop 的打包
This commit is contained in:
parent
68f45fedf7
commit
1fef3a1150
154
package-lock.json
generated
154
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "openmcp",
|
"name": "openmcp",
|
||||||
"version": "0.0.6",
|
"version": "0.0.8",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "openmcp",
|
"name": "openmcp",
|
||||||
"version": "0.0.6",
|
"version": "0.0.8",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.10.2",
|
"@modelcontextprotocol/sdk": "^1.10.2",
|
||||||
"@seald-io/nedb": "^4.1.1",
|
"@seald-io/nedb": "^4.1.1",
|
||||||
@ -24,6 +24,7 @@
|
|||||||
"@types/showdown": "^2.0.0",
|
"@types/showdown": "^2.0.0",
|
||||||
"@types/vscode": "^1.72.0",
|
"@types/vscode": "^1.72.0",
|
||||||
"copy-webpack-plugin": "^13.0.0",
|
"copy-webpack-plugin": "^13.0.0",
|
||||||
|
"null-loader": "^4.0.1",
|
||||||
"ts-loader": "^9.5.1",
|
"ts-loader": "^9.5.1",
|
||||||
"typescript": "^5.4.2",
|
"typescript": "^5.4.2",
|
||||||
"webpack": "^5.99.5",
|
"webpack": "^5.99.5",
|
||||||
@ -570,6 +571,16 @@
|
|||||||
"proxy-from-env": "^1.1.0"
|
"proxy-from-env": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/big.js": {
|
||||||
|
"version": "5.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||||
|
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bmp-js": {
|
"node_modules/bmp-js": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
|
||||||
@ -980,6 +991,16 @@
|
|||||||
"integrity": "sha512-kL4+wUTD7RSA5FHx5YwWtjDnEEkIIikFgWHR4P6fqjw1PPLlqYkxeOb++wAauAssat0YClCy8Y3C5SxgSkjibQ==",
|
"integrity": "sha512-kL4+wUTD7RSA5FHx5YwWtjDnEEkIIikFgWHR4P6fqjw1PPLlqYkxeOb++wAauAssat0YClCy8Y3C5SxgSkjibQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/emojis-list": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/encodeurl": {
|
"node_modules/encodeurl": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||||
@ -1266,6 +1287,13 @@
|
|||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-json-stable-stringify": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/fast-uri": {
|
"node_modules/fast-uri": {
|
||||||
"version": "3.0.6",
|
"version": "3.0.6",
|
||||||
"resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.0.6.tgz",
|
"resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.0.6.tgz",
|
||||||
@ -1870,6 +1898,19 @@
|
|||||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/json5": {
|
||||||
|
"version": "2.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||||
|
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"json5": "lib/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/kind-of": {
|
"node_modules/kind-of": {
|
||||||
"version": "6.0.3",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz",
|
||||||
@ -1897,6 +1938,21 @@
|
|||||||
"node": ">=6.11.5"
|
"node": ">=6.11.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/loader-utils": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"big.js": "^5.2.2",
|
||||||
|
"emojis-list": "^3.0.0",
|
||||||
|
"json5": "^2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/localforage": {
|
"node_modules/localforage": {
|
||||||
"version": "1.10.0",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
|
||||||
@ -2064,6 +2120,80 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/null-loader": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"loader-utils": "^2.0.0",
|
||||||
|
"schema-utils": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/webpack"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"webpack": "^4.0.0 || ^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/null-loader/node_modules/ajv": {
|
||||||
|
"version": "6.12.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
|
"json-schema-traverse": "^0.4.1",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/null-loader/node_modules/ajv-keywords": {
|
||||||
|
"version": "3.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||||
|
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"ajv": "^6.9.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/null-loader/node_modules/json-schema-traverse": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/null-loader/node_modules/schema-utils": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/json-schema": "^7.0.8",
|
||||||
|
"ajv": "^6.12.5",
|
||||||
|
"ajv-keywords": "^3.5.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/webpack"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
@ -2305,6 +2435,16 @@
|
|||||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/punycode": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.14.0",
|
"version": "6.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||||
@ -3037,6 +3177,16 @@
|
|||||||
"browserslist": ">= 4.21.0"
|
"browserslist": ">= 4.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/uri-js": {
|
||||||
|
"version": "4.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||||
|
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/util": {
|
"node_modules/util": {
|
||||||
"version": "0.12.5",
|
"version": "0.12.5",
|
||||||
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
|
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
|
||||||
|
@ -219,7 +219,8 @@
|
|||||||
"pretest": "npm run compile && npm run lint",
|
"pretest": "npm run compile && npm run lint",
|
||||||
"lint": "eslint src --ext ts",
|
"lint": "eslint src --ext ts",
|
||||||
"test": "node ./out/test/runTest.js",
|
"test": "node ./out/test/runTest.js",
|
||||||
"prepare:ocr": "webpack --config webpack/webpack.tesseract.js"
|
"prepare:ocr": "webpack --config webpack/webpack.tesseract.js",
|
||||||
|
"build:task-loop": "webpack --config webpack/webpack.task-loop.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.10.2",
|
"@modelcontextprotocol/sdk": "^1.10.2",
|
||||||
@ -238,6 +239,7 @@
|
|||||||
"@types/showdown": "^2.0.0",
|
"@types/showdown": "^2.0.0",
|
||||||
"@types/vscode": "^1.72.0",
|
"@types/vscode": "^1.72.0",
|
||||||
"copy-webpack-plugin": "^13.0.0",
|
"copy-webpack-plugin": "^13.0.0",
|
||||||
|
"null-loader": "^4.0.1",
|
||||||
"ts-loader": "^9.5.1",
|
"ts-loader": "^9.5.1",
|
||||||
"typescript": "^5.4.2",
|
"typescript": "^5.4.2",
|
||||||
"webpack": "^5.99.5",
|
"webpack": "^5.99.5",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ToolCallContent, ToolItem } from "@/hook/type";
|
import type { 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';
|
||||||
|
357
renderer/src/components/main-panel/chat/core/task-loop-sdk.ts
Normal file
357
renderer/src/components/main-panel/chat/core/task-loop-sdk.ts
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
import type { Ref } from "vue";
|
||||||
|
import { ToolCall, ChatStorage, getToolSchema, MessageState } from "../chat-box/chat";
|
||||||
|
import { useMessageBridge } from "@/api/message-bridge";
|
||||||
|
import type { OpenAI } from 'openai';
|
||||||
|
import { llmManager, llms } from "@/views/setting/llm";
|
||||||
|
import { pinkLog, redLog } from "@/views/setting/util";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { handleToolCalls } from "./handle-tool-calls";
|
||||||
|
|
||||||
|
export type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk;
|
||||||
|
export type ChatCompletionCreateParamsBase = OpenAI.Chat.Completions.ChatCompletionCreateParams & { id?: string };
|
||||||
|
interface TaskLoopOptions {
|
||||||
|
maxEpochs: number;
|
||||||
|
maxJsonParseRetry: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IErrorMssage {
|
||||||
|
state: MessageState,
|
||||||
|
msg: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IDoConversationResult {
|
||||||
|
stop: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 对任务循环进行的抽象封装
|
||||||
|
*/
|
||||||
|
export class TaskLoop {
|
||||||
|
private bridge = useMessageBridge();
|
||||||
|
private currentChatId = '';
|
||||||
|
private completionUsage: ChatCompletionChunk['usage'] | undefined;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly streamingContent: Ref<string>,
|
||||||
|
private readonly streamingToolCalls: Ref<ToolCall[]>,
|
||||||
|
private onError: (error: IErrorMssage) => void = (msg) => {},
|
||||||
|
private onChunk: (chunk: ChatCompletionChunk) => void = (chunk) => {},
|
||||||
|
private onDone: () => void = () => {},
|
||||||
|
private onEpoch: () => void = () => {},
|
||||||
|
private readonly taskOptions: TaskLoopOptions = { maxEpochs: 20, maxJsonParseRetry: 3 },
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleChunkDeltaContent(chunk: ChatCompletionChunk) {
|
||||||
|
const content = chunk.choices[0]?.delta?.content || '';
|
||||||
|
if (content) {
|
||||||
|
this.streamingContent.value += content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleChunkDeltaToolCalls(chunk: ChatCompletionChunk) {
|
||||||
|
const toolCall = chunk.choices[0]?.delta?.tool_calls?.[0];
|
||||||
|
|
||||||
|
if (toolCall) {
|
||||||
|
const currentCall = this.streamingToolCalls.value[toolCall.index];
|
||||||
|
|
||||||
|
if (currentCall === undefined) {
|
||||||
|
// 新的工具调用开始
|
||||||
|
this.streamingToolCalls.value[toolCall.index] = {
|
||||||
|
id: toolCall.id,
|
||||||
|
index: toolCall.index,
|
||||||
|
type: 'function',
|
||||||
|
function: {
|
||||||
|
name: toolCall.function?.name || '',
|
||||||
|
arguments: toolCall.function?.arguments || ''
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// 累积现有工具调用的信息
|
||||||
|
if (currentCall) {
|
||||||
|
if (toolCall.id) {
|
||||||
|
currentCall.id = toolCall.id;
|
||||||
|
}
|
||||||
|
if (toolCall.function?.name) {
|
||||||
|
currentCall.function.name = toolCall.function.name;
|
||||||
|
}
|
||||||
|
if (toolCall.function?.arguments) {
|
||||||
|
currentCall.function.arguments += toolCall.function.arguments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleChunkUsage(chunk: ChatCompletionChunk) {
|
||||||
|
const usage = chunk.usage;
|
||||||
|
if (usage) {
|
||||||
|
this.completionUsage = usage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private doConversation(chatData: ChatCompletionCreateParamsBase) {
|
||||||
|
|
||||||
|
return new Promise<IDoConversationResult>((resolve, reject) => {
|
||||||
|
const chunkHandler = this.bridge.addCommandListener('llm/chat/completions/chunk', data => {
|
||||||
|
// data.code 一定为 200,否则不会走这个 route
|
||||||
|
const { chunk } = data.msg as { chunk: ChatCompletionChunk };
|
||||||
|
|
||||||
|
// 处理增量的 content 和 tool_calls
|
||||||
|
this.handleChunkDeltaContent(chunk);
|
||||||
|
this.handleChunkDeltaToolCalls(chunk);
|
||||||
|
this.handleChunkUsage(chunk);
|
||||||
|
|
||||||
|
this.onChunk(chunk);
|
||||||
|
}, { once: false });
|
||||||
|
|
||||||
|
const doneHandler = this.bridge.addCommandListener('llm/chat/completions/done', data => {
|
||||||
|
this.onDone();
|
||||||
|
chunkHandler();
|
||||||
|
errorHandler();
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
stop: false
|
||||||
|
});
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
|
const errorHandler = this.bridge.addCommandListener('llm/chat/completions/error', data => {
|
||||||
|
this.onError({
|
||||||
|
state: MessageState.ReceiveChunkError,
|
||||||
|
msg: data.msg || '请求模型服务时发生错误'
|
||||||
|
});
|
||||||
|
|
||||||
|
chunkHandler();
|
||||||
|
doneHandler();
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
stop: true
|
||||||
|
});
|
||||||
|
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
|
console.log(chatData);
|
||||||
|
|
||||||
|
this.bridge.postMessage({
|
||||||
|
command: 'llm/chat/completions',
|
||||||
|
data: JSON.parse(JSON.stringify(chatData)),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public makeChatData(tabStorage: ChatStorage): ChatCompletionCreateParamsBase | undefined {
|
||||||
|
const baseURL = llms[llmManager.currentModelIndex].baseUrl;
|
||||||
|
const apiKey = llms[llmManager.currentModelIndex].userToken || '';
|
||||||
|
|
||||||
|
if (apiKey.trim() === '') {
|
||||||
|
|
||||||
|
if (tabStorage.messages.length > 0 && tabStorage.messages[tabStorage.messages.length - 1].role === 'user') {
|
||||||
|
tabStorage.messages.pop();
|
||||||
|
ElMessage.error('请先设置 API Key');
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const model = llms[llmManager.currentModelIndex].userModel;
|
||||||
|
const temperature = tabStorage.settings.temperature;
|
||||||
|
const tools = getToolSchema(tabStorage.settings.enableTools);
|
||||||
|
|
||||||
|
const userMessages = [];
|
||||||
|
if (tabStorage.settings.systemPrompt) {
|
||||||
|
userMessages.push({
|
||||||
|
role: 'system',
|
||||||
|
content: tabStorage.settings.systemPrompt
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果超出了 tabStorage.settings.contextLength, 则删除最早的消息
|
||||||
|
const loadMessages = tabStorage.messages.slice(- tabStorage.settings.contextLength);
|
||||||
|
userMessages.push(...loadMessages);
|
||||||
|
|
||||||
|
// 增加一个id用于锁定状态
|
||||||
|
const id = crypto.randomUUID();
|
||||||
|
|
||||||
|
const chatData = {
|
||||||
|
id,
|
||||||
|
baseURL,
|
||||||
|
apiKey,
|
||||||
|
model,
|
||||||
|
temperature,
|
||||||
|
tools,
|
||||||
|
messages: userMessages,
|
||||||
|
} as ChatCompletionCreateParamsBase;
|
||||||
|
|
||||||
|
return chatData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abort() {
|
||||||
|
this.bridge.postMessage({
|
||||||
|
command: 'llm/chat/completions/abort',
|
||||||
|
data: {
|
||||||
|
id: this.currentChatId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.streamingContent.value = '';
|
||||||
|
this.streamingToolCalls.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerOnError(handler: (msg: IErrorMssage) => void) {
|
||||||
|
this.onError = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerOnChunk(handler: (chunk: ChatCompletionChunk) => void) {
|
||||||
|
this.onChunk = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerOnDone(handler: () => void) {
|
||||||
|
this.onDone = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerOnEpoch(handler: () => void) {
|
||||||
|
this.onEpoch = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setMaxEpochs(maxEpochs: number) {
|
||||||
|
this.taskOptions.maxEpochs = maxEpochs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 开启循环,异步更新 DOM
|
||||||
|
*/
|
||||||
|
public async start(tabStorage: ChatStorage, userMessage: string) {
|
||||||
|
// 添加目前的消息
|
||||||
|
tabStorage.messages.push({
|
||||||
|
role: 'user',
|
||||||
|
content: userMessage,
|
||||||
|
extraInfo: {
|
||||||
|
created: Date.now(),
|
||||||
|
state: MessageState.Success,
|
||||||
|
serverName: llms[llmManager.currentModelIndex].id || 'unknown'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let jsonParseErrorRetryCount = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.taskOptions.maxEpochs; ++ i) {
|
||||||
|
|
||||||
|
this.onEpoch();
|
||||||
|
|
||||||
|
// 初始累计清空
|
||||||
|
this.streamingContent.value = '';
|
||||||
|
this.streamingToolCalls.value = [];
|
||||||
|
this.completionUsage = undefined;
|
||||||
|
|
||||||
|
// 构造 chatData
|
||||||
|
const chatData = this.makeChatData(tabStorage);
|
||||||
|
|
||||||
|
if (!chatData) {
|
||||||
|
this.onDone();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentChatId = chatData.id!;
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
const doConverationResult = await this.doConversation(chatData);
|
||||||
|
|
||||||
|
console.log(doConverationResult);
|
||||||
|
|
||||||
|
|
||||||
|
// 如果存在需要调度的工具
|
||||||
|
if (this.streamingToolCalls.value.length > 0) {
|
||||||
|
|
||||||
|
tabStorage.messages.push({
|
||||||
|
role: 'assistant',
|
||||||
|
content: this.streamingContent.value || '',
|
||||||
|
tool_calls: this.streamingToolCalls.value,
|
||||||
|
extraInfo: {
|
||||||
|
created: Date.now(),
|
||||||
|
state: MessageState.Success,
|
||||||
|
serverName: llms[llmManager.currentModelIndex].id || 'unknown'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pinkLog('调用工具数量:' + this.streamingToolCalls.value.length);
|
||||||
|
|
||||||
|
for (const toolCall of this.streamingToolCalls.value || []) {
|
||||||
|
const toolCallResult = await handleToolCalls(toolCall);
|
||||||
|
|
||||||
|
if (toolCallResult.state === MessageState.ParseJsonError) {
|
||||||
|
// 如果是因为解析 JSON 错误,则重新开始
|
||||||
|
tabStorage.messages.pop();
|
||||||
|
jsonParseErrorRetryCount ++;
|
||||||
|
|
||||||
|
redLog('解析 JSON 错误 ' + toolCall?.function?.arguments);
|
||||||
|
|
||||||
|
// 如果因为 JSON 错误而失败太多,就只能中断了
|
||||||
|
if (jsonParseErrorRetryCount >= this.taskOptions.maxJsonParseRetry) {
|
||||||
|
tabStorage.messages.push({
|
||||||
|
role: 'assistant',
|
||||||
|
content: `解析 JSON 错误,无法继续调用工具 (累计错误次数 ${this.taskOptions.maxJsonParseRetry})`,
|
||||||
|
extraInfo: {
|
||||||
|
created: Date.now(),
|
||||||
|
state: toolCallResult.state,
|
||||||
|
serverName: llms[llmManager.currentModelIndex].id || 'unknown',
|
||||||
|
usage: undefined
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (toolCallResult.state === MessageState.Success) {
|
||||||
|
tabStorage.messages.push({
|
||||||
|
role: 'tool',
|
||||||
|
index: toolCall.index || 0,
|
||||||
|
tool_call_id: toolCall.id || toolCall.function.name,
|
||||||
|
content: toolCallResult.content,
|
||||||
|
extraInfo: {
|
||||||
|
created: Date.now(),
|
||||||
|
state: toolCallResult.state,
|
||||||
|
serverName: llms[llmManager.currentModelIndex].id || 'unknown',
|
||||||
|
usage: this.completionUsage
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (toolCallResult.state === MessageState.ToolCall) {
|
||||||
|
|
||||||
|
tabStorage.messages.push({
|
||||||
|
role: 'tool',
|
||||||
|
index: toolCall.index || 0,
|
||||||
|
tool_call_id: toolCall.id || toolCall.function.name,
|
||||||
|
content: toolCallResult.content,
|
||||||
|
extraInfo: {
|
||||||
|
created: Date.now(),
|
||||||
|
state: toolCallResult.state,
|
||||||
|
serverName: llms[llmManager.currentModelIndex].id || 'unknown',
|
||||||
|
usage: this.completionUsage
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (this.streamingContent.value) {
|
||||||
|
tabStorage.messages.push({
|
||||||
|
role: 'assistant',
|
||||||
|
content: this.streamingContent.value,
|
||||||
|
extraInfo: {
|
||||||
|
created: Date.now(),
|
||||||
|
state: MessageState.Success,
|
||||||
|
serverName: llms[llmManager.currentModelIndex].id || 'unknown',
|
||||||
|
usage: this.completionUsage
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 一些提示
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回答聚合完成后根据 stop 来决定是否提前中断
|
||||||
|
if (doConverationResult.stop) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,8 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import { ToolCall, ChatStorage, getToolSchema, MessageState } from "../chat-box/chat";
|
import { ToolCall, ChatStorage, getToolSchema, MessageState } from "../chat-box/chat";
|
||||||
import { useMessageBridge } from "@/api/message-bridge";
|
import { useMessageBridge } from "@/api/message-bridge";
|
||||||
import type { OpenAI } from 'openai';
|
import type { OpenAI } from 'openai';
|
||||||
import { callTool } from "../../tool/tools";
|
|
||||||
import { llmManager, llms } from "@/views/setting/llm";
|
import { llmManager, llms } from "@/views/setting/llm";
|
||||||
import { pinkLog, redLog } from "@/views/setting/util";
|
import { pinkLog, redLog } from "@/views/setting/util";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
|
44
webpack/webpack.task-loop.js
Normal file
44
webpack/webpack.task-loop.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mode: 'production',
|
||||||
|
entry: './renderer/src/components/main-panel/chat/core/task-loop-sdk.ts',
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, '../openmcp-sdk'),
|
||||||
|
filename: 'task-loop-sdk.js',
|
||||||
|
libraryTarget: 'commonjs2'
|
||||||
|
},
|
||||||
|
target: 'node',
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.js'],
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, '../renderer/src'), // 修正路径别名
|
||||||
|
},
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.vue$/,
|
||||||
|
use: {
|
||||||
|
loader: 'null-loader'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimizer: [
|
||||||
|
new TerserPlugin({
|
||||||
|
extractComments: false, // 禁用提取许可证文件
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
vue: 'vue', // 不打包 vue 库
|
||||||
|
},
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user