feat(dependencies): update ai-sdk packages and improve logging

- Upgraded `@ai-sdk/gateway` to version 1.0.8 and `@ai-sdk/provider-utils` to version 3.0.4 in yarn.lock for enhanced functionality.
- Updated `ai` dependency in package.json to version ^5.0.16 for better compatibility.
- Added logging functionality in `AiSdkToChunkAdapter` to track chunk types and improve debugging.
- Refactored plugin imports to streamline code and enhance readability.
- Removed unnecessary console logs in `searchOrchestrationPlugin` to clean up the codebase.
This commit is contained in:
MyPrototypeWhat
2025-08-19 16:03:51 +08:00
parent 179b7af9bd
commit d4da7d817d
8 changed files with 55 additions and 67 deletions
@@ -4,11 +4,14 @@
*/
import { TextStreamPart, ToolSet } from '@cherrystudio/ai-core'
import { loggerService } from '@logger'
import { BaseTool, WebSearchResults, WebSearchSource } from '@renderer/types'
import { Chunk, ChunkType } from '@renderer/types/chunk'
import { ToolCallChunkHandler } from './handleToolCallChunk'
const logger = loggerService.withContext('AiSdkToChunkAdapter')
export interface CherryStudioChunk {
type: 'text-delta' | 'text-complete' | 'tool-call' | 'tool-result' | 'finish' | 'error'
text?: string
@@ -83,6 +86,7 @@ export class AiSdkToChunkAdapter {
chunk: TextStreamPart<any>,
final: { text: string; reasoningContent: string; webSearchResults: any[]; reasoningId: string }
) {
logger.info(`AI SDK chunk type: ${chunk.type}`, chunk)
switch (chunk.type) {
// === 文本相关事件 ===
case 'text-start':
@@ -105,12 +109,12 @@ export class AiSdkToChunkAdapter {
final.text = ''
break
case 'reasoning-start':
if (final.reasoningId !== chunk.id) {
final.reasoningId = chunk.id
this.onChunk({
type: ChunkType.THINKING_START
})
}
// if (final.reasoningId !== chunk.id) {
final.reasoningId = chunk.id
this.onChunk({
type: ChunkType.THINKING_START
})
// }
break
case 'reasoning-delta':
final.reasoningContent += chunk.text || ''
@@ -171,7 +175,7 @@ export class AiSdkToChunkAdapter {
// break
case 'finish-step': {
const { providerMetadata } = chunk
const { providerMetadata, finishReason } = chunk
// googel web search
if (providerMetadata?.google?.groundingMetadata) {
this.onChunk({
@@ -182,6 +186,9 @@ export class AiSdkToChunkAdapter {
}
})
}
if (finishReason === 'tool-calls') {
this.onChunk({ type: ChunkType.LLM_RESPONSE_CREATED })
}
// else {
// this.onChunk({
// type: ChunkType.LLM_WEB_SEARCH_COMPLETE,
@@ -1,6 +1,6 @@
import { AiPlugin } from '@cherrystudio/ai-core'
import { createPromptToolUsePlugin, webSearchPlugin } from '@cherrystudio/ai-core/built-in/plugins'
import store from '@renderer/store'
import { getEnableDeveloperMode } from '@renderer/hooks/useSettings'
import { Assistant } from '@renderer/types'
import { AiSdkMiddlewareConfig } from '../middleware/AiSdkMiddlewareBuilder'
@@ -16,7 +16,7 @@ export function buildPlugins(
): AiPlugin[] {
const plugins: AiPlugin[] = []
if (middlewareConfig.topicId && store.getState().settings.enableDeveloperMode) {
if (middlewareConfig.topicId && getEnableDeveloperMode()) {
// 0. 添加 telemetry 插件
plugins.push(
createTelemetryPlugin({
@@ -247,8 +247,6 @@ export const searchOrchestrationPlugin = (assistant: Assistant, topicId: string)
const userMessages: { [requestId: string]: ModelMessage } = {}
let currentContext: AiRequestContext | null = null
console.log('searchOrchestrationPlugin', assistant)
return definePlugin({
name: 'search-orchestration',
enforce: 'pre', // 确保在其他插件之前执行
@@ -264,15 +262,14 @@ export const searchOrchestrationPlugin = (assistant: Assistant, topicId: string)
* 🔍 Step 1: 意图识别阶段
*/
onRequestStart: async (context: AiRequestContext) => {
console.log('onRequestStart', context)
if (context.isAnalyzing) return
// console.log('🧠 [SearchOrchestration] Starting intent analysis...', context.requestId)
// 没开启任何搜索则不进行意图分析
if (!(assistant.webSearchProviderId || assistant.knowledge_bases?.length || assistant.enableMemory)) return
try {
const messages = context.originalParams.messages
// console.log('🧠 [SearchOrchestration]', context.isAnalyzing)
if (!messages || messages.length === 0) {
console.log('🧠 [SearchOrchestration] No messages found, skipping analysis')
return
}
@@ -283,10 +280,8 @@ export const searchOrchestrationPlugin = (assistant: Assistant, topicId: string)
userMessages[context.requestId] = lastUserMessage
// 判断是否需要各种搜索
const knowledgeBaseIds = assistant.knowledge_bases?.map((base) => base.id)
// console.log('knowledgeBaseIds', knowledgeBaseIds)
const knowledgeBaseIds = assistant.knowledge_bases.map((base) => base.id)
const hasKnowledgeBase = !isEmpty(knowledgeBaseIds)
// console.log('hasKnowledgeBase', hasKnowledgeBase)
const knowledgeRecognition = assistant.knowledgeRecognition || 'on'
const globalMemoryEnabled = selectGlobalMemoryEnabled(store.getState())
@@ -294,11 +289,6 @@ export const searchOrchestrationPlugin = (assistant: Assistant, topicId: string)
const shouldKnowledgeSearch = hasKnowledgeBase && knowledgeRecognition === 'on'
const shouldMemorySearch = globalMemoryEnabled && assistant.enableMemory
// console.log('🧠 [SearchOrchestration] Search capabilities:', {
// shouldWebSearch,
// hasKnowledgeBase,
// shouldMemorySearch
// })
// 执行意图分析
if (shouldWebSearch || hasKnowledgeBase) {
const analysisResult = await analyzeSearchIntent(lastUserMessage, assistant, {
@@ -84,6 +84,7 @@ const ChooseTool = (toolResponse: MCPToolResponse): { label: React.ReactNode; bo
export default function MessageTool({ block }: Props) {
// FIXME: 语义错误,这里已经不是 MCP tool 了
const toolResponse = block.metadata?.rawMcpToolResponse
if (!toolResponse) return null
const toolRenderer = ChooseTool(toolResponse)
+4 -30
View File
@@ -9,6 +9,7 @@ import { AiSdkMiddlewareConfig } from '@renderer/aiCore/middleware/AiSdkMiddlewa
import { buildStreamTextParams } from '@renderer/aiCore/transformParameters'
import { isDedicatedImageGenerationModel, isEmbeddingModel } from '@renderer/config/models'
import { getStoreSetting } from '@renderer/hooks/useSettings'
import { getEnableDeveloperMode } from '@renderer/hooks/useSettings'
import i18n from '@renderer/i18n'
import store from '@renderer/store'
import { Assistant, MCPServer, MCPTool, Model, Provider } from '@renderer/types'
@@ -156,30 +157,9 @@ export async function fetchChatCompletion({
// }
// --- Call AI Completions ---
onChunkReceived({ type: ChunkType.LLM_RESPONSE_CREATED })
const enableDeveloperMode = getEnableDeveloperMode()
// 在 AI SDK 调用时设置正确的 OpenTelemetry 上下文
if (topicId) {
logger.info('Attempting to set OpenTelemetry context', { topicId })
const { currentSpan } = await import('@renderer/services/SpanManagerService')
const parentSpan = currentSpan(topicId, modelId)
logger.info('Parent span lookup result', {
topicId,
hasParentSpan: !!parentSpan,
parentSpanId: parentSpan?.spanContext().spanId,
parentTraceId: parentSpan?.spanContext().traceId
})
if (parentSpan) {
logger.info('Found parent span, using completionsForTrace for proper span hierarchy', {
topicId,
parentSpanId: parentSpan.spanContext().spanId,
parentTraceId: parentSpan.spanContext().traceId
})
} else {
logger.warn('No parent span found for topicId, using completionsForTrace anyway', { topicId })
}
if (topicId && enableDeveloperMode) {
// 使用带trace支持的completions方法,它会自动创建子span并关联到父span
await AI.completionsForTrace(modelId, aiSdkParams, {
...middlewareConfig,
@@ -188,14 +168,8 @@ export async function fetchChatCompletion({
callType: 'chat'
})
} else {
logger.warn('No topicId provided, using regular completions')
// 没有topicId时,禁用telemetry以避免警告
const configWithoutTelemetry = {
...middlewareConfig,
topicId: undefined // 确保telemetryPlugin不会尝试查找span
}
await AI.completions(modelId, aiSdkParams, {
...configWithoutTelemetry,
...middlewareConfig,
assistant,
callType: 'chat'
})
@@ -23,6 +23,8 @@ export const createToolCallbacks = (deps: ToolCallbacksDependencies) => {
return {
onToolCallPending: (toolResponse: MCPToolResponse) => {
console.log('onToolCallPending', toolResponse)
if (blockManager.hasInitialPlaceholder) {
const changes = {
type: MessageBlockType.TOOL,