@h-ai/serv 0.1.0-alpha.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/features/serv-feature-ai.ts"],"names":[],"mappings":";;;;;;AAmCA,IAAM,aAAa,WAAA,CAAY,EAAA;AAQxB,SAAS,mBAAmB,IAAA,EAAuB;AACxD,EAAA,MAAM,CAAA,GAAI,SAAA,CAAU,UAAU,CAAA,CAAE,QAAA,EAAsB;AAEtD,EAAA,OAAO,EAAE,MAAA,CAAO;AAAA,IACd,KAAA,EAAO;AAAA,MACL,gBAAA,EAAkB,EAAE,KAAA,CAAM,gBAAA,CAAiB,QAAQ,WAAA,CAAyD,OAAO,EAAE,KAAA,EAAM,KAAM;AAC/H,QAAA,OAAO,uBAAA,CAAwB,MAAM,IAAA,CAAK,EAAA,CAAG,IAAI,IAAA,CAAK,uBAAA,CAAwB,KAAK,CAAC,CAAC,CAAA;AAAA,MACvF,CAAC,CAAC,CAAA;AAAA,MACF,WAAA,EAAa,EAAE,KAAA,CAAM,WAAA,CAAY,QAAQ,WAAA,CAAoE,OAAO,EAAE,KAAA,EAAM,KAAM;AAChI,QAAA,OAAO,WAAA,CAAY,MAAM,KAAK,CAAA;AAAA,MAChC,CAAC,CAAC,CAAA;AAAA,MACF,WAAA,EAAa,EAAE,KAAA,CAAM,WAAA,CAAY,QAAQ,WAAA,CAA2D,OAAO,EAAE,KAAA,EAAM,KAAM;AACvH,QAAA,MAAM,EAAE,QAAA,EAAU,SAAA,EAAW,KAAA,EAAO,OAAM,GAAI,KAAA;AAC9C,QAAA,OAAO,oBAAA,CAAqB,MAAM,IAAA,CAAK,EAAA,CAAG,IAAI,UAAA,CAAW,EAAE,QAAA,EAAU,SAAA,EAAU,EAAG,EAAE,KAAA,EAAO,KAAA,EAAO,CAAC,CAAA;AAAA,MACrG,CAAC,CAAC;AAAA,KACJ;AAAA,IACA,QAAA,EAAU;AAAA,MACR,MAAA,EAAQ,EAAE,QAAA,CAAS,MAAA,CAAO,QAAQ,WAAA,CAA2D,OAAO,EAAE,KAAA,EAAM,KAAM;AAChH,QAAA,MAAM,EAAE,KAAA,EAAO,GAAG,OAAA,EAAQ,GAAI,KAAA;AAC9B,QAAA,OAAO,eAAA,CAAgB,MAAM,IAAA,CAAK,EAAA,CAAG,OAAO,MAAA,CAAO,KAAA,EAAO,OAAO,CAAC,CAAA;AAAA,MACpE,CAAC,CAAC,CAAA;AAAA,MACF,IAAA,EAAM,EAAE,QAAA,CAAS,IAAA,CAAK,QAAQ,WAAA,CAAwE,OAAO,EAAE,KAAA,EAAM,KAAM;AACzH,QAAA,OAAO,IAAA,CAAK,EAAA,CAAG,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA;AAAA,MACtC,CAAC,CAAC;AAAA,KACJ;AAAA,IACA,QAAA,EAAU;AAAA,MACR,IAAA,EAAM,EAAE,QAAA,CAAS,IAAA,CAAK,QAAQ,WAAA,CAA0D,OAAO,EAAE,KAAA,EAAM,KAAM;AAC3G,QAAA,OAAO,eAAA,CAAgB,MAAM,IAAA,CAAK,EAAA,CAAG,IAAI,YAAA,CAAa,KAAA,CAAM,QAAQ,CAAC,CAAA;AAAA,MACvE,CAAC,CAAC;AAAA;AACJ,GACD,CAAA;AACH;AAEA,SAAS,wBAAwB,KAAA,EAAqD;AACpF,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,aAAa,CAAA;AAAA,IAC1C,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,aAAa,KAAA,CAAM,WAAA;AAAA,IACnB,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,YAAY,KAAA,CAAM;AAAA,GACpB;AACF;AAEA,SAAS,cAAc,OAAA,EAAiE;AACtF,EAAA,IAAI,OAAA,CAAQ,SAAS,QAAA,EAAU;AAC7B,IAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,SAAS,aAAA,CAAc,OAAA,CAAQ,OAAO,CAAA,EAAE;AAAA,EACnE;AAEA,EAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAQ;AAC3B,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,OAAA,CAAQ,WAAW,EAAA,EAAG;AAAA,EACxD;AAEA,EAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAQ;AAC3B,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,aAAA,CAAc,OAAA,CAAQ,OAAO,CAAA,EAAG,YAAA,EAAc,OAAA,CAAQ,YAAA,IAAgB,EAAA,EAAG;AAAA,EAC3G;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,WAAA;AAAA,IACN,OAAA,EAAS,kBAAA,CAAmB,OAAA,CAAQ,OAAO,CAAA;AAAA,IAC3C,YAAY,OAAA,CAAQ;AAAA,GACtB;AACF;AAEA,SAAS,cAAc,OAAA,EAAuE;AAC5F,EAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,OAAO,OAAA,EAAS,GAAA,CAAI,CAAA,IAAA,KAAQ,IAAA,CAAK,SAAS,MAAA,GAAS,IAAA,CAAK,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,GAAG,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA,IAAK,EAAA;AACnG;AAEA,SAAS,mBAAmB,OAAA,EAA8E;AACxG,EAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,OAAO,OAAA,GAAU,aAAA,CAAc,OAAO,CAAA,GAAI,IAAA;AAC5C;AAEA,SAAS,wBAAwB,MAAA,EAA4E;AAC3G,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,EAAA,CAAG;AAAA,IACR,EAAA,EAAI,OAAO,IAAA,CAAK,EAAA;AAAA,IAChB,MAAA,EAAQ,iBAAA;AAAA,IACR,OAAA,EAAS,OAAO,IAAA,CAAK,OAAA;AAAA,IACrB,KAAA,EAAO,OAAO,IAAA,CAAK,KAAA;AAAA,IACnB,OAAA,EAAS,MAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,IAAI,CAAA,MAAA,MAAW;AAAA,MAC1C,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,OAAA,EAAS;AAAA,QACP,IAAA,EAAM,WAAA;AAAA,QACN,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,OAAA,IAAW,IAAA;AAAA,QACnC,UAAA,EAAY,oBAAA,CAAqB,MAAA,CAAO,OAAA,CAAQ,UAAU;AAAA,OAC5D;AAAA,MACA,aAAA,EAAe,iBAAA,CAAkB,MAAA,CAAO,aAAa;AAAA,KACvD,CAAE,CAAA;AAAA,IACF,KAAA,EAAO;AAAA,MACL,aAAA,EAAe,MAAA,CAAO,IAAA,CAAK,KAAA,EAAO,aAAA,IAAiB,CAAA;AAAA,MACnD,iBAAA,EAAmB,MAAA,CAAO,IAAA,CAAK,KAAA,EAAO,iBAAA,IAAqB,CAAA;AAAA,MAC3D,YAAA,EAAc,MAAA,CAAO,IAAA,CAAK,KAAA,EAAO,YAAA,IAAgB;AAAA;AACnD,GACD,CAAA;AACH;AAEA,SAAS,kBAAkB,MAAA,EAAiF;AAC1G,EAAA,IAAI,MAAA,KAAW,QAAA,IAAY,MAAA,KAAW,YAAA,IAAgB,WAAW,gBAAA,EAAkB;AACjF,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,qBAAqB,MAAA,EAA+F;AAC3H,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,EAAA,CAAG,EAAE,KAAA,EAAO,MAAA,CAAO,KAAK,GAAA,CAAI,eAAe,GAAG,CAAA;AACvD;AAEA,SAAS,gBAAgB,MAAA,EAAqD;AAC5E,EAAA,OAAO;AAAA,IACL,IAAI,MAAA,CAAO,EAAA;AAAA,IACX,UAAU,MAAA,CAAO,QAAA;AAAA,IACjB,WAAW,MAAA,CAAO,SAAA;AAAA,IAClB,OAAA,EAAS;AAAA,MACP,KAAA,EAAO,OAAO,OAAA,CAAQ,KAAA;AAAA,MACtB,QAAA,EAAU,MAAA,CAAO,OAAA,CAAQ,QAAA,CAAS,IAAI,gBAAgB;AAAA,KACxD;AAAA,IACA,QAAA,EAAU;AAAA,MACR,OAAA,EAAS,OAAO,QAAA,CAAS,OAAA;AAAA,MACzB,SAAA,EAAW,oBAAA,CAAqB,MAAA,CAAO,QAAA,CAAS,SAAS,CAAA;AAAA,MACzD,YAAA,EAAc,OAAO,QAAA,CAAS,YAAA;AAAA,MAC9B,KAAA,EAAO;AAAA,QACL,aAAA,EAAe,MAAA,CAAO,QAAA,CAAS,KAAA,CAAM,aAAA;AAAA,QACrC,iBAAA,EAAmB,MAAA,CAAO,QAAA,CAAS,KAAA,CAAM,iBAAA;AAAA,QACzC,YAAA,EAAc,MAAA,CAAO,QAAA,CAAS,KAAA,CAAM;AAAA;AACtC,KACF;AAAA,IACA,WAAW,MAAA,CAAO,SAAA;AAAA,IAClB,UAAU,MAAA,CAAO;AAAA,GACnB;AACF;AAEA,SAAS,iBAAiB,OAAA,EAAiE;AACzF,EAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAQ;AAC3B,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,aAAA,CAAc,QAAQ,OAAO,CAAA,EAAG,YAAA,EAAc,OAAA,CAAQ,YAAA,EAAa;AAAA,EACrG;AAEA,EAAA,IAAI,OAAA,CAAQ,SAAS,WAAA,EAAa;AAChC,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,WAAA;AAAA,MACN,OAAA,EAAS,aAAA,CAAc,OAAA,CAAQ,OAAO,CAAA;AAAA,MACtC,UAAA,EAAY,oBAAA,CAAqB,OAAA,CAAQ,UAAU;AAAA,KACrD;AAAA,EACF;AAEA,EAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAQ;AAC3B,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,SAAS,aAAA,CAAc,OAAA,CAAQ,OAAO,CAAA,EAAE;AAAA,EACjE;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,SAAS,aAAA,CAAc,OAAA,CAAQ,OAAO,CAAA,EAAE;AACnE;AAEA,SAAS,cAAc,OAAA,EAA0B;AAC/C,EAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,EAAG;AAC3B,IAAA,OAAO,EAAA;AAAA,EACT;AAEA,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,IAAA,KAAS;AAC3B,IAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,IAAA,EAAM;AAC7C,MAAA,OAAO,EAAA;AAAA,IACT;AAEA,IAAA,MAAM,MAAA,GAAS,IAAA;AACf,IAAA,IAAI,OAAO,MAAA,CAAO,IAAA,KAAS,QAAA,EAAU;AACnC,MAAA,OAAO,MAAA,CAAO,IAAA;AAAA,IAChB;AAEA,IAAA,IAAI,OAAO,MAAA,CAAO,SAAA,KAAc,QAAA,IAAY,MAAA,CAAO,cAAc,IAAA,EAAM;AACrE,MAAA,MAAM,QAAQ,MAAA,CAAO,SAAA;AACrB,MAAA,OAAO,OAAO,KAAA,CAAM,GAAA,KAAQ,QAAA,GAAW,MAAM,GAAA,GAAM,EAAA;AAAA,IACrD;AAEA,IAAA,OAAO,EAAA;AAAA,EACT,CAAC,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,CAAE,KAAK,IAAI,CAAA;AAC9B;AAEA,SAAS,qBAAqB,SAAA,EAAmC;AAC/D,EAAA,OAAO,SAAA,EAAW,OAAA,CAAQ,CAAC,QAAA,KAAa;AACtC,IAAA,IAAI,QAAA,CAAS,IAAA,KAAS,UAAA,IAAc,EAAE,cAAc,QAAA,CAAA,EAAW;AAC7D,MAAA,OAAO,EAAC;AAAA,IACV;AAEA,IAAA,OAAO,CAAC;AAAA,MACN,IAAI,QAAA,CAAS,EAAA;AAAA,MACb,IAAA,EAAM,UAAA;AAAA,MACN,QAAA,EAAU;AAAA,QACR,IAAA,EAAM,SAAS,QAAA,CAAS,IAAA;AAAA,QACxB,SAAA,EAAW,SAAS,QAAA,CAAS;AAAA;AAC/B,KACD,CAAA;AAAA,EACH,CAAC,CAAA;AACH;AAEA,eAAe,WAAA,CAAY,MAAuB,KAAA,EAAmF;AACnI,EAAA,MAAM,SAAS,MAAM,IAAA,CAAK,GAAG,GAAA,CAAI,GAAA,CAAI,MAAM,OAAA,EAAS;AAAA,IAClD,cAAc,KAAA,CAAM,YAAA;AAAA,IACpB,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,aAAa,KAAA,CAAM;AAAA,GACpB,CAAA;AAED,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,EAAA,CAAG;AAAA,IACR,SAAS,MAAA,CAAO,IAAA;AAAA,IAChB,OAAO,KAAA,CAAM,KAAA,IAAS,KAAK,EAAA,CAAG,MAAA,EAAQ,IAAI,KAAA,IAAS;AAAA,GACpD,CAAA;AACH","file":"ai.js","sourcesContent":["/**\n * @h-ai/serv — AI 默认 procedures\n *\n * 基于 `@h-ai/ai` 提供开箱即用的 AI procedures 实现:对话补全、消息发送、聊天历史、记忆管理、会话列表。\n * 通过 `createAiProcedures(deps)` 组装后直接挂载到 oRPC router。\n * @module features/serv-feature-ai\n */\n\nimport type {\n AIFunctions,\n ChatCompletionRequest,\n ChatCompletionResponse,\n ChatMessage,\n MemoryEntry,\n SessionInfo,\n ToolCall,\n} from '@h-ai/ai'\nimport type {\n AiChatCompletionData,\n AiChatCompletionInput,\n AiChatHistoryInput,\n AiChatRecord,\n AiMemoryListInput,\n AiMemoryRecallInput,\n AiSendMessageInput,\n AiSessionListInput,\n} from '@h-ai/api-contract'\nimport type { HaiResult } from '@h-ai/core'\nimport type { ServContext } from '../serv-context.js'\nimport { apiContract } from '@h-ai/api-contract'\nimport { ok } from '@h-ai/core'\nimport { implement } from '@orpc/server'\nimport { requireAuth } from '../pipelines/serv-pipeline-require-auth.js'\nimport { wrapItemsResult } from './serv-feature-helpers.js'\n\nconst aiContract = apiContract.ai\n\n/** AI 默认 procedures 依赖。 */\nexport interface AiProcedureDeps {\n readonly ai: AIFunctions\n}\n\n/** 创建 AI 默认 procedures。 */\nexport function createAiProcedures(deps: AiProcedureDeps) {\n const p = implement(aiContract).$context<ServContext>()\n\n return p.router({\n chats: {\n createCompletion: p.chats.createCompletion.handler(requireAuth<AiChatCompletionInput, AiChatCompletionData>(async ({ input }) => {\n return mapChatCompletionResult(await deps.ai.llm.chat(toChatCompletionRequest(input)))\n })),\n sendMessage: p.chats.sendMessage.handler(requireAuth<AiSendMessageInput, { content: string, model: string }>(async ({ input }) => {\n return sendMessage(deps, input)\n })),\n listHistory: p.chats.listHistory.handler(requireAuth<AiChatHistoryInput, { items: AiChatRecord[] }>(async ({ input }) => {\n const { objectId, sessionId, limit, order } = input\n return mapChatRecordsResult(await deps.ai.llm.getHistory({ objectId, sessionId }, { limit, order }))\n })),\n },\n memories: {\n recall: p.memories.recall.handler(requireAuth<AiMemoryRecallInput, { items: MemoryEntry[] }>(async ({ input }) => {\n const { query, ...options } = input\n return wrapItemsResult(await deps.ai.memory.recall(query, options))\n })),\n list: p.memories.list.handler(requireAuth<AiMemoryListInput, { items: MemoryEntry[], total: number }>(async ({ input }) => {\n return deps.ai.memory.listPage(input)\n })),\n },\n sessions: {\n list: p.sessions.list.handler(requireAuth<AiSessionListInput, { items: SessionInfo[] }>(async ({ input }) => {\n return wrapItemsResult(await deps.ai.llm.listSessions(input.objectId))\n })),\n },\n })\n}\n\nfunction toChatCompletionRequest(input: AiChatCompletionInput): ChatCompletionRequest {\n return {\n messages: input.messages.map(toChatMessage),\n model: input.model,\n temperature: input.temperature,\n top_p: input.top_p,\n max_tokens: input.max_tokens,\n }\n}\n\nfunction toChatMessage(message: AiChatCompletionInput['messages'][number]): ChatMessage {\n if (message.role === 'system') {\n return { role: 'system', content: toMessageText(message.content) }\n }\n\n if (message.role === 'user') {\n return { role: 'user', content: message.content ?? '' }\n }\n\n if (message.role === 'tool') {\n return { role: 'tool', content: toMessageText(message.content), tool_call_id: message.tool_call_id ?? '' }\n }\n\n return {\n role: 'assistant',\n content: toAssistantContent(message.content),\n tool_calls: message.tool_calls,\n }\n}\n\nfunction toMessageText(content: AiChatCompletionInput['messages'][number]['content']): string {\n if (typeof content === 'string') {\n return content\n }\n\n return content?.map(part => part.type === 'text' ? part.text : part.image_url.url).join('\\n') ?? ''\n}\n\nfunction toAssistantContent(content: AiChatCompletionInput['messages'][number]['content']): string | null {\n if (typeof content === 'string') {\n return content\n }\n\n return content ? toMessageText(content) : null\n}\n\nfunction mapChatCompletionResult(result: HaiResult<ChatCompletionResponse>): HaiResult<AiChatCompletionData> {\n if (!result.success) {\n return result\n }\n\n return ok({\n id: result.data.id,\n object: 'chat.completion',\n created: result.data.created,\n model: result.data.model,\n choices: result.data.choices.map(choice => ({\n index: choice.index,\n message: {\n role: 'assistant',\n content: choice.message.content ?? null,\n tool_calls: mapFunctionToolCalls(choice.message.tool_calls),\n },\n finish_reason: toApiFinishReason(choice.finish_reason),\n })),\n usage: {\n prompt_tokens: result.data.usage?.prompt_tokens ?? 0,\n completion_tokens: result.data.usage?.completion_tokens ?? 0,\n total_tokens: result.data.usage?.total_tokens ?? 0,\n },\n })\n}\n\nfunction toApiFinishReason(reason: string | null): AiChatCompletionData['choices'][number]['finish_reason'] {\n if (reason === 'length' || reason === 'tool_calls' || reason === 'content_filter') {\n return reason\n }\n\n return 'stop'\n}\n\nfunction mapChatRecordsResult(result: HaiResult<Array<import('@h-ai/ai').ChatRecord>>): HaiResult<{ items: AiChatRecord[] }> {\n if (!result.success) {\n return result\n }\n\n return ok({ items: result.data.map(toApiChatRecord) })\n}\n\nfunction toApiChatRecord(record: import('@h-ai/ai').ChatRecord): AiChatRecord {\n return {\n id: record.id,\n objectId: record.objectId,\n sessionId: record.sessionId,\n request: {\n model: record.request.model,\n messages: record.request.messages.map(toApiChatMessage),\n },\n response: {\n content: record.response.content,\n toolCalls: mapFunctionToolCalls(record.response.toolCalls),\n finishReason: record.response.finishReason,\n usage: {\n prompt_tokens: record.response.usage.prompt_tokens,\n completion_tokens: record.response.usage.completion_tokens,\n total_tokens: record.response.usage.total_tokens,\n },\n },\n createdAt: record.createdAt,\n duration: record.duration,\n }\n}\n\nfunction toApiChatMessage(message: ChatMessage): AiChatCompletionInput['messages'][number] {\n if (message.role === 'tool') {\n return { role: 'tool', content: toContentText(message.content), tool_call_id: message.tool_call_id }\n }\n\n if (message.role === 'assistant') {\n return {\n role: 'assistant',\n content: toContentText(message.content),\n tool_calls: mapFunctionToolCalls(message.tool_calls),\n }\n }\n\n if (message.role === 'user') {\n return { role: 'user', content: toContentText(message.content) }\n }\n\n return { role: 'system', content: toContentText(message.content) }\n}\n\nfunction toContentText(content: unknown): string {\n if (typeof content === 'string') {\n return content\n }\n\n if (!Array.isArray(content)) {\n return ''\n }\n\n return content.map((part) => {\n if (typeof part !== 'object' || part === null) {\n return ''\n }\n\n const record = part as Record<string, unknown>\n if (typeof record.text === 'string') {\n return record.text\n }\n\n if (typeof record.image_url === 'object' && record.image_url !== null) {\n const image = record.image_url as Record<string, unknown>\n return typeof image.url === 'string' ? image.url : ''\n }\n\n return ''\n }).filter(Boolean).join('\\n')\n}\n\nfunction mapFunctionToolCalls(toolCalls: ToolCall[] | undefined) {\n return toolCalls?.flatMap((toolCall) => {\n if (toolCall.type !== 'function' || !('function' in toolCall)) {\n return []\n }\n\n return [{\n id: toolCall.id,\n type: 'function' as const,\n function: {\n name: toolCall.function.name,\n arguments: toolCall.function.arguments,\n },\n }]\n })\n}\n\nasync function sendMessage(deps: AiProcedureDeps, input: AiSendMessageInput): Promise<HaiResult<{ content: string, model: string }>> {\n const result = await deps.ai.llm.ask(input.message, {\n systemPrompt: input.systemPrompt,\n model: input.model,\n temperature: input.temperature,\n })\n\n if (!result.success) {\n return result\n }\n\n return ok({\n content: result.data,\n model: input.model ?? deps.ai.config?.llm.model ?? 'default',\n })\n}\n"]}