feat: enhance MCP Prompt plugin and recursive call capabilities

- Updated `tsconfig.web.json` to support wildcard imports for `@cherrystudio/ai-core`.
- Enhanced `package.json` to include type definitions and imports for built-in plugins.
- Introduced recursive call functionality in `PluginManager` and `PluginEngine`, allowing for improved handling of tool interactions.
- Added `MCPPromptPlugin` to facilitate tool calls within prompts, enabling recursive processing of tool results.
- Refactored `transformStream` methods across plugins to accommodate new parameters and improve type safety.
This commit is contained in:
MyPrototypeWhat 2025-06-26 19:42:04 +08:00
parent 8b67a45804
commit 9293f26612
12 changed files with 729 additions and 39 deletions

View File

@ -118,6 +118,11 @@
"types": "./src/index.ts",
"import": "./src/index.ts",
"require": "./src/index.ts"
},
"./core/plugins/built-in": {
"types": "./src/core/plugins/built-in/index.ts",
"import": "./src/core/plugins/built-in/index.ts",
"require": "./src/core/plugins/built-in/index.ts"
}
}
}

View File

@ -5,3 +5,5 @@
export const BUILT_IN_PLUGIN_PREFIX = 'built-in:'
export { createLoggingPlugin } from './logging'
export type { MCPPromptConfig, ToolUseResult } from './mcpPrompt'
export { createMCPPromptPlugin } from './mcpPrompt'

View File

@ -0,0 +1,670 @@
/**
* MCP Prompt
* Function Call prompt
*
*/
import { ToolSet } from 'ai'
import { definePlugin } from '../index'
import type { AiRequestContext } from '../types'
/**
* 使 AI SDK Tool
*/
// export interface Tool {
// type: 'function'
// function: {
// name: string
// description?: string
// parameters?: {
// type: 'object'
// properties: Record<string, any>
// required?: string[]
// additionalProperties?: boolean
// }
// }
// }
/**
*
* AI响应中解析出的工具使用意图
*/
export interface ToolUseResult {
id: string
toolName: string
arguments: any
status: 'pending' | 'invoking' | 'done' | 'error'
}
/**
* MCP Prompt
*/
export interface MCPPromptConfig {
// 是否启用(用于运行时开关)
enabled?: boolean
// 自定义系统提示符构建函数(可选,有默认实现)
buildSystemPrompt?: (userSystemPrompt: string, tools: ToolSet) => Promise<string>
// 自定义工具解析函数(可选,有默认实现)
parseToolUse?: (content: string, tools: ToolSet) => ToolUseResult[]
}
// 全局存储,解决 transformStream 中无 context 的问题
const globalToolsStorage = new Map<string, ToolSet>()
/**
* ID
*/
function generateExecutionId(): string {
return `mcp_${Date.now()}_${Math.random().toString(36).slice(2)}`
}
/**
*
*/
/**
*
*/
function storeGlobalTools(executionId: string, tools: ToolSet) {
globalToolsStorage.set(executionId, tools)
}
/**
*
*/
function getGlobalTools(executionId: string): ToolSet | undefined {
return globalToolsStorage.get(executionId)
}
/**
*
*/
function clearGlobalTools(executionId: string) {
globalToolsStorage.delete(executionId)
}
/**
* Cherry Studio
*/
const DEFAULT_SYSTEM_PROMPT = `In this environment you have access to a set of tools you can use to answer the user's question. \\
You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
## Tool Use Formatting
Tool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:
<tool_use>
<name>{tool_name}</name>
<arguments>{json_arguments}</arguments>
</tool_use>
The tool name should be the exact name of the tool you are using, and the arguments should be a JSON object containing the parameters required by that tool. For example:
<tool_use>
<name>python_interpreter</name>
<arguments>{"code": "5 + 3 + 1294.678"}</arguments>
</tool_use>
The user will respond with the result of the tool use, which should be formatted as follows:
<tool_use_result>
<name>{tool_name}</name>
<result>{result}</result>
</tool_use_result>
The result should be a string, which can represent a file or any other output type. You can use this result as input for the next action.
For example, if the result of the tool use is an image file, you can use it in the next action like this:
<tool_use>
<name>image_transformer</name>
<arguments>{"image": "image_1.jpg"}</arguments>
</tool_use>
Always adhere to this format for the tool use to ensure proper parsing and execution.
## Tool Use Examples
{{ TOOL_USE_EXAMPLES }}
## Tool Use Available Tools
Above example were using notional tools that might not exist for you. You only have access to these tools:
{{ AVAILABLE_TOOLS }}
## Tool Use Rules
Here are the rules you should always follow to solve your task:
1. Always use the right arguments for the tools. Never use variable names as the action arguments, use the value instead.
2. Call a tool only when needed: do not call the search agent if you do not need information, try to solve the task yourself.
3. If no tool call is needed, just answer the question directly.
4. Never re-do a tool call that you previously did with the exact same parameters.
5. For tool use, MAKE SURE use XML tag format as shown in the examples above. Do not use any other format.
# User Instructions
{{ USER_SYSTEM_PROMPT }}
Now Begin! If you solve the task correctly, you will receive a reward of $1,000,000.`
/**
* 使 Cherry Studio
*/
const DEFAULT_TOOL_USE_EXAMPLES = `
Here are a few examples using notional tools:
---
User: Generate an image of the oldest person in this document.
A: I can use the document_qa tool to find out who the oldest person is in the document.
<tool_use>
<name>document_qa</name>
<arguments>{"document": "document.pdf", "question": "Who is the oldest person mentioned?"}</arguments>
</tool_use>
User: <tool_use_result>
<name>document_qa</name>
<result>John Doe, a 55 year old lumberjack living in Newfoundland.</result>
</tool_use_result>
A: I can use the image_generator tool to create a portrait of John Doe.
<tool_use>
<name>image_generator</name>
<arguments>{"prompt": "A portrait of John Doe, a 55-year-old man living in Canada."}</arguments>
</tool_use>
User: <tool_use_result>
<name>image_generator</name>
<result>image.png</result>
</tool_use_result>
A: the image is generated as image.png
---
User: "What is the result of the following operation: 5 + 3 + 1294.678?"
A: I can use the python_interpreter tool to calculate the result of the operation.
<tool_use>
<name>python_interpreter</name>
<arguments>{"code": "5 + 3 + 1294.678"}</arguments>
</tool_use>
User: <tool_use_result>
<name>python_interpreter</name>
<result>1302.678</result>
</tool_use_result>
A: The result of the operation is 1302.678.
---
User: "Which city has the highest population , Guangzhou or Shanghai?"
A: I can use the search tool to find the population of Guangzhou.
<tool_use>
<name>search</name>
<arguments>{"query": "Population Guangzhou"}</arguments>
</tool_use>
User: <tool_use_result>
<name>search</name>
<result>Guangzhou has a population of 15 million inhabitants as of 2021.</result>
</tool_use_result>
A: I can use the search tool to find the population of Shanghai.
<tool_use>
<name>search</name>
<arguments>{"query": "Population Shanghai"}</arguments>
</tool_use>
User: <tool_use_result>
<name>search</name>
<result>26 million (2019)</result>
</tool_use_result>
Assistant: The population of Shanghai is 26 million, while Guangzhou has a population of 15 million. Therefore, Shanghai has the highest population.`
/**
* Cherry Studio
*/
function buildAvailableTools(tools: ToolSet): string {
const availableTools = Object.keys(tools)
.map((toolName: string) => {
const tool = tools[toolName]
return `
<tool>
<name>${toolName}</name>
<description>${tool.description || ''}</description>
<arguments>
${tool.parameters ? JSON.stringify(tool.parameters) : ''}
</arguments>
</tool>
`
})
.join('\n')
return `<tools>
${availableTools}
</tools>`
}
/**
* Cherry Studio
*/
async function defaultBuildSystemPrompt(userSystemPrompt: string, tools: ToolSet): Promise<string> {
const availableTools = buildAvailableTools(tools)
const fullPrompt = DEFAULT_SYSTEM_PROMPT.replace('{{ TOOL_USE_EXAMPLES }}', DEFAULT_TOOL_USE_EXAMPLES)
.replace('{{ AVAILABLE_TOOLS }}', availableTools)
.replace('{{ USER_SYSTEM_PROMPT }}', userSystemPrompt || '')
return fullPrompt
}
/**
* Cherry Studio
* XML
*/
function defaultParseToolUse(content: string, tools: ToolSet): ToolUseResult[] {
if (!content || !tools || Object.keys(tools).length === 0) {
return []
}
// 支持两种格式:
// 1. 完整的 <tool_use></tool_use> 标签包围的内容
// 2. 只有内部内容(从 TagExtractor 提取出来的)
let contentToProcess = content
// 如果内容不包含 <tool_use> 标签,说明是从 TagExtractor 提取的内部内容,需要包装
if (!content.includes('<tool_use>')) {
contentToProcess = `<tool_use>\n${content}\n</tool_use>`
}
const toolUsePattern =
/<tool_use>([\s\S]*?)<name>([\s\S]*?)<\/name>([\s\S]*?)<arguments>([\s\S]*?)<\/arguments>([\s\S]*?)<\/tool_use>/g
const results: ToolUseResult[] = []
let match
let idx = 0
// Find all tool use blocks
while ((match = toolUsePattern.exec(contentToProcess)) !== null) {
const toolName = match[2].trim()
const toolArgs = match[4].trim()
// Try to parse the arguments as JSON
let parsedArgs
try {
parsedArgs = JSON.parse(toolArgs)
} catch (error) {
// If parsing fails, use the string as is
parsedArgs = toolArgs
}
// Find the corresponding tool
const tool = tools[toolName]
if (!tool) {
console.warn(`Tool "${toolName}" not found in available tools`)
continue
}
// Add to results array
results.push({
id: `${toolName}-${idx++}`, // Unique ID for each tool use
toolName: toolName,
arguments: parsedArgs,
status: 'pending'
})
}
return results
}
/**
* MCP Prompt
*/
export const createMCPPromptPlugin = definePlugin((config: MCPPromptConfig = {}) => {
const { enabled = true, buildSystemPrompt = defaultBuildSystemPrompt, parseToolUse = defaultParseToolUse } = config
// 为每个插件实例生成唯一ID
const executionId = generateExecutionId()
return {
name: 'built-in:mcp-prompt',
transformParams: async (params: any, context: AiRequestContext) => {
if (!enabled || !params.tools) return params
// 保存原始工具信息到 WeakMap 和全局存储中
const tools: ToolSet = params.tools
console.log('tools', tools)
// storeTools(context, tools)
storeGlobalTools(executionId, tools)
// 构建系统提示符
const userSystemPrompt = typeof params.system === 'string' ? params.system : ''
const systemPrompt = await buildSystemPrompt(userSystemPrompt, tools)
// 将工具信息保存到参数中(用于后续解析)
const transformedParams = {
...params,
system: systemPrompt,
// 移除 tools改为 prompt 模式
tools: undefined
}
console.log('transformedParams', transformedParams)
return transformedParams
},
// 流式处理:监听 step-finish 事件并处理工具调用
transformStream: (_, context: AiRequestContext) => () => {
let textBuffer = ''
let executedResults: { toolCallId: string; toolName: string; result: any; isError?: boolean }[] = []
return new TransformStream<any>({
async transform(chunk, controller) {
console.log('chunk', chunk)
// 收集文本内容
if (chunk.type === 'text-delta') {
textBuffer += chunk.textDelta || ''
console.log('textBuffer', textBuffer)
controller.enqueue(chunk)
return
}
// 监听 step-finish 事件
if (chunk.type === 'step-finish' || chunk.type === 'finish') {
console.log('[MCP Prompt Stream] Received step-finish, checking for tool use...')
// 获取工具信息
const tools = getGlobalTools(executionId)
console.log('tools', tools)
if (!tools) {
console.log('[MCP Prompt Stream] No tools available, passing through')
controller.enqueue(chunk)
return
}
// 解析工具调用
const parsedTools = parseToolUse(textBuffer, tools)
// console.log('textBuffer', textBuffer)
const validToolUses = parsedTools.filter((t) => t.status === 'pending')
console.log('parsedTools', parsedTools)
// 如果没有有效的工具调用,直接传递原始事件
if (validToolUses.length === 0) {
console.log('[MCP Prompt Stream] No valid tool uses found, passing through')
controller.enqueue(chunk)
return
}
console.log('[MCP Prompt Stream] Found valid tool uses:', validToolUses.length)
// 修改 step-finish 事件,标记为工具调用
if (chunk.type !== 'finish') {
controller.enqueue({
...chunk,
finishReason: 'tool-call'
})
}
// 发送 step-start 事件(工具调用步骤开始)
controller.enqueue({
type: 'step-start'
})
// 执行工具调用
executedResults = []
for (const toolUse of validToolUses) {
try {
const tool = tools[toolUse.toolName]
if (!tool || typeof tool.execute !== 'function') {
throw new Error(`Tool "${toolUse.toolName}" has no execute method`)
}
console.log(`[MCP Prompt Stream] Executing tool: ${toolUse.toolName}`, toolUse.arguments)
// 发送 tool-call 事件
controller.enqueue({
type: 'tool-call',
toolCallId: toolUse.id,
toolName: toolUse.toolName,
args: toolUse.arguments
})
const result = await tool.execute(toolUse.arguments, {
toolCallId: toolUse.id,
messages: [],
abortSignal: new AbortController().signal
})
// 发送 tool-result 事件
controller.enqueue({
type: 'tool-result',
toolCallId: toolUse.id,
toolName: toolUse.toolName,
args: toolUse.arguments,
result
})
executedResults.push({
toolCallId: toolUse.id,
toolName: toolUse.toolName,
result,
isError: false
})
} catch (error) {
console.error(`[MCP Prompt Stream] Tool execution failed: ${toolUse.toolName}`, error)
controller.enqueue({
type: 'tool-result',
toolCallId: toolUse.id,
toolName: toolUse.toolName,
args: toolUse.arguments,
isError: true,
result: error instanceof Error ? error.message : String(error)
})
executedResults.push({
toolCallId: toolUse.id,
toolName: toolUse.toolName,
result: error instanceof Error ? error.message : String(error),
isError: true
})
}
}
// 发送最终的 step-finish 事件
controller.enqueue({
type: 'step-finish',
finishReason: 'tool-call'
// usage: { completionTokens: 0, promptTokens: 0, totalTokens: 0 }
})
// 递归调用逻辑
if (context.recursiveCall && validToolUses.length > 0) {
console.log('[MCP Prompt] Starting recursive call after tool execution...')
// 构建工具结果的文本表示使用Cherry Studio标准格式
const toolResultsText = executedResults
.map((tr) => {
if (!tr.isError) {
return `<tool_use_result>\n <name>${tr.toolName}</name>\n <result>${JSON.stringify(tr.result)}</result>\n</tool_use_result>`
} else {
const error = tr.result || 'Unknown error'
return `<tool_use_result>\n <name>${tr.toolName}</name>\n <error>${error}</error>\n</tool_use_result>`
}
})
.join('\n\n')
// 构建新的对话消息
const newMessages = [
...(context.originalParams.messages || []),
{
role: 'assistant',
content: textBuffer
},
{
role: 'user',
content: toolResultsText
}
]
// 递归调用,继续对话
const recursiveParams = {
...context.originalParams,
messages: newMessages,
tools: tools // 重新传递 tools
}
try {
const recursiveResult = await context.recursiveCall(recursiveParams)
// 将递归调用的结果流接入当前流
if (recursiveResult && recursiveResult.fullStream) {
const reader = recursiveResult.fullStream.getReader()
try {
while (true) {
const { done, value } = await reader.read()
if (done) {
break
}
// 将递归流的数据传递到当前流
controller.enqueue(value)
}
} finally {
reader.releaseLock()
}
} else {
console.warn('[MCP Prompt] No fullstream found in recursive result:', recursiveResult)
}
} catch (error) {
console.error('[MCP Prompt] Recursive call failed:', error)
// 发送错误信息后也要确保流不会中断
controller.enqueue({
type: 'text-delta',
textDelta: `\n\n[Error: Recursive call failed: ${error instanceof Error ? error.message : String(error)}]`
})
// 发送一个错误后的结束信号
controller.enqueue({
type: 'finish',
finishReason: 'error'
})
}
}
// 清理状态
// clearGlobalTools(executionId)
textBuffer = ''
executedResults = []
return
}
// 对于其他类型的事件,直接传递
controller.enqueue(chunk)
},
flush() {
// 清理全局存储
clearGlobalTools(executionId)
}
})
}
// transformResult: async (result: any, context: AiRequestContext) => {
// // 这个方法现在主要用于非流式场景
// if (!enabled || !result || typeof result.text !== 'string') return result
// console.log('[MCP Prompt] transformResult called - likely non-streaming mode')
// // 从 WeakMap 中获取工具信息
// const tools: ToolSet | undefined = getStoredTools(context)
// if (!tools || typeof tools !== 'object') return result
// // 使用工具解析函数(默认或自定义)
// const parsedTools = parseToolUse(result.text, tools)
// if (!parsedTools || parsedTools.length === 0) return result
// // 过滤掉解析失败的工具调用
// const validToolUses = parsedTools.filter((t) => t.status === 'pending')
// if (validToolUses.length === 0) {
// console.warn('[MCP Prompt] No valid tool uses found:', parsedTools)
// return result
// }
// // 只在非流式模式下执行工具调用并递归
// if (context.recursiveCall) {
// console.log('[MCP Prompt] Non-streaming: Executing tools and continuing conversation...')
// // 执行工具调用
// const toolResults = await Promise.all(
// validToolUses.map(async (toolUse) => {
// try {
// const tool = tools[toolUse.toolName]
// if (!tool || typeof tool.execute !== 'function') {
// throw new Error(`Tool "${toolUse.toolName}" has no execute method`)
// }
// console.log(`[MCP Prompt] Non-streaming: Executing tool: ${toolUse.toolName}`, toolUse.arguments)
// const result = await tool.execute(toolUse.arguments, {
// toolCallId: toolUse.id,
// messages: [],
// abortSignal: new AbortController().signal
// })
// return {
// id: toolUse.id,
// name: toolUse.toolName,
// arguments: toolUse.arguments,
// result,
// success: true
// }
// } catch (error) {
// console.error(`[MCP Prompt] Non-streaming: Tool execution failed: ${toolUse.toolName}`, error)
// return {
// id: toolUse.id,
// name: toolUse.toolName,
// arguments: toolUse.arguments,
// error: error instanceof Error ? error.message : String(error),
// success: false
// }
// }
// })
// )
// // 构建工具结果的文本表示
// const toolResultsText = toolResults
// .map((tr) => {
// if (tr.success) {
// return `<tool_use_result>\n <name>${tr.name}</name>\n <result>${JSON.stringify(tr.result)}</result>\n</tool_use_result>`
// } else {
// return `<tool_use_result>\n <name>${tr.name}</name>\n <error>${tr.error}</error>\n</tool_use_result>`
// }
// })
// .join('\n\n')
// // 构建新的对话消息
// const newMessages = [
// ...(context.originalParams.messages || []),
// {
// role: 'assistant',
// content: result.text
// },
// {
// role: 'user',
// content: toolResultsText
// }
// ]
// // 递归调用,继续对话
// const recursiveParams = {
// ...context.originalParams,
// messages: newMessages,
// tools: tools // 重新传递 tools在新的 context 中会重新存储
// }
// try {
// console.log('[MCP Prompt] Non-streaming: Starting recursive call...')
// const recursiveResult = await context.recursiveCall(recursiveParams)
// return recursiveResult
// } catch (error) {
// console.error('[MCP Prompt] Non-streaming: Recursive call failed:', error)
// return result
// }
// }
// return result
// }
}
})

View File

@ -13,7 +13,8 @@ export function createContext(providerId: string, modelId: string, originalParam
originalParams,
metadata: {},
startTime: Date.now(),
requestId: `${providerId}-${modelId}-${Date.now()}-${Math.random().toString(36).slice(2)}`
requestId: `${providerId}-${modelId}-${Date.now()}-${Math.random().toString(36).slice(2)}`,
recursiveCall: undefined
}
}

View File

@ -1,5 +1,3 @@
import type { TextStreamPart, ToolSet } from 'ai'
import { AiPlugin, AiRequestContext } from './types'
/**
@ -121,18 +119,8 @@ export class PluginManager {
/**
* AI SDK
*/
collectStreamTransforms<TOOLS extends ToolSet>(): Array<
(options: {
tools?: TOOLS
stopStream: () => void
}) => TransformStream<TextStreamPart<TOOLS>, TextStreamPart<TOOLS>>
> {
return this.plugins.map((plugin) => plugin.transformStream).filter(Boolean) as Array<
(options: {
tools?: TOOLS
stopStream: () => void
}) => TransformStream<TextStreamPart<TOOLS>, TextStreamPart<TOOLS>>
>
collectStreamTransforms(params: any, context: AiRequestContext) {
return this.plugins.map((plugin) => plugin.transformStream?.(params, context))
}
/**

View File

@ -1,5 +1,11 @@
import type { TextStreamPart, ToolSet } from 'ai'
/**
*
* 使 any
*/
export type RecursiveCallFn = (newParams: any) => Promise<any>
/**
* AI
*/
@ -10,6 +16,8 @@ export interface AiRequestContext {
metadata: Record<string, any>
startTime: number
requestId: string
recursiveCall?: RecursiveCallFn
[key: string]: any
}
/**
@ -33,7 +41,10 @@ export interface AiPlugin {
onError?: (error: Error, context: AiRequestContext) => void | Promise<void>
// 【Stream】流处理 - 直接使用 AI SDK
transformStream?: <TOOLS extends ToolSet>(options: {
transformStream?: (
params: any,
context: AiRequestContext
) => <TOOLS extends ToolSet>(options: {
tools: TOOLS
stopStream: () => void
}) => TransformStream<TextStreamPart<TOOLS>, TextStreamPart<TOOLS>>

View File

@ -68,6 +68,12 @@ export class PluginEngine<T extends ProviderId = ProviderId> {
// 使用正确的createContext创建请求上下文
const context = createContext(this.providerId, modelId, params)
// 🔥 为上下文添加递归调用能力
context.recursiveCall = (newParams: any): Promise<TResult> => {
// 递归调用自身,重新走完整的插件流程
return this.executeWithPlugins(methodName, modelId, newParams, executor)
}
try {
// 1. 触发请求开始事件
await this.pluginManager.executeParallel('onRequestStart', context)
@ -109,6 +115,12 @@ export class PluginEngine<T extends ProviderId = ProviderId> {
// 创建请求上下文
const context = createContext(this.providerId, modelId, params)
// 🔥 为上下文添加递归调用能力
context.recursiveCall = (newParams: any): Promise<TResult> => {
// 递归调用自身,重新走完整的插件流程
return this.executeStreamWithPlugins(methodName, modelId, newParams, executor)
}
try {
// 1. 触发请求开始事件
await this.pluginManager.executeParallel('onRequestStart', context)
@ -121,7 +133,7 @@ export class PluginEngine<T extends ProviderId = ProviderId> {
const transformedParams = await this.pluginManager.executeSequential('transformParams', params, context)
// 4. 收集流转换器
const streamTransforms = this.pluginManager.collectStreamTransforms()
const streamTransforms = this.pluginManager.collectStreamTransforms(transformedParams, context)
// 5. 执行流式 API 调用
const result = await executor(finalModelId, transformedParams, streamTransforms)

View File

@ -15,6 +15,7 @@ import {
type ProviderSettingsMap,
StreamTextParams
} from '@cherrystudio/ai-core'
import { createMCPPromptPlugin } from '@cherrystudio/ai-core/core/plugins/built-in'
import { isDedicatedImageGenerationModel } from '@renderer/config/models'
import { createVertexProvider, isVertexAIConfigured, isVertexProvider } from '@renderer/hooks/useVertexAI'
import type { GenerateImageParams, Model, Provider } from '@renderer/types'
@ -113,7 +114,16 @@ export default class ModernAiProvider {
// TODO:如果后续在调用completions时需要切换provider的话,
// 初始化时不构建中间件,等到需要时再构建
const config = providerToAiSdkConfig(provider)
// 创建MCP Prompt插件
const mcpPromptPlugin = createMCPPromptPlugin({
enabled: true
})
console.log('[Modern AI Provider] Creating executor with MCP Prompt plugin enabled')
this.modernExecutor = createExecutor(config.providerId, config.options, [
mcpPromptPlugin,
reasonPlugin({
delayInMs: 80,
chunkingRegex: /([\u4E00-\u9FFF]{3})|\S+\s+/
@ -174,16 +184,6 @@ export default class ModernAiProvider {
if (middlewareConfig.onChunk) {
// 流式处理 - 使用适配器
const adapter = new AiSdkToChunkAdapter(middlewareConfig.onChunk)
// this.modernExecutor.pluginEngine.use(
// createMCPPromptPlugin({
// mcpTools: middlewareConfig.mcpTools || [],
// assistant: params.assistant,
// onChunk: middlewareConfig.onChunk,
// recursiveCall: this.modernExecutor.streamText,
// recursionDepth: 0,
// maxRecursionDepth: 20
// })
// )
const streamResult = await this.modernExecutor.streamText(
modelId,
params,

View File

@ -20,8 +20,8 @@ export interface MCPPromptPluginConfig {
* MCP Prompt
* prompt
*/
export const createMCPPromptPlugin = (config: MCPPromptPluginConfig) => {
return definePlugin({
export const createMCPPromptPlugin = definePlugin((config: MCPPromptPluginConfig) => {
return {
name: 'mcp-prompt-plugin',
// 1. 参数转换 - 注入工具描述到系统提示
@ -49,7 +49,7 @@ export const createMCPPromptPlugin = (config: MCPPromptPluginConfig) => {
},
// 2. 流处理 - 检测工具调用并执行
transformStream: () => {
transformStream: () => () => {
let fullResponseText = ''
let hasProcessedTools = false
@ -87,8 +87,8 @@ export const createMCPPromptPlugin = (config: MCPPromptPluginConfig) => {
}
})
}
})
}
}
})
/**
*

View File

@ -3,7 +3,7 @@ import { definePlugin } from '@cherrystudio/ai-core'
export default definePlugin(({ delayInMs, chunkingRegex }: { delayInMs: number; chunkingRegex: RegExp }) => ({
name: 'reasonPlugin',
transformStream: () => {
transformStream: () => () => {
let buffer = ''
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
const detectChunk = (buffer: string) => {

View File

@ -2,9 +2,10 @@ import { definePlugin, smoothStream } from '@cherrystudio/ai-core'
export default definePlugin({
name: 'textPlugin',
transformStream: smoothStream({
delayInMs: 80,
// 中文3个字符一个chunk,英文一个单词一个chunk
chunking: /([\u4E00-\u9FFF]{3})|\S+\s+/
})
transformStream: () =>
smoothStream({
delayInMs: 80,
// 中文3个字符一个chunk,英文一个单词一个chunk
chunking: /([\u4E00-\u9FFF]{3})|\S+\s+/
})
})

View File

@ -16,7 +16,7 @@
"@renderer/*": ["src/renderer/src/*"],
"@shared/*": ["packages/shared/*"],
"@types": ["src/renderer/src/types/index.ts"],
"@cherrystudio/ai-core": ["packages/aiCore/src/"]
"@cherrystudio/ai-core/*": ["packages/aiCore/src/*"]
}
}
}