UNPKG

@cherrystudio/ai-core

Version:

Cherry Studio AI Core - Unified AI Provider Interface Based on Vercel AI SDK

269 lines (268 loc) 9.9 kB
import * as ai0 from "ai"; import { LanguageModel, TextStreamPart, ToolSet, generateObject, generateText, streamObject, streamText } from "ai"; import { AnthropicProviderOptions, AnthropicProviderSettings } from "@ai-sdk/anthropic"; import { AzureOpenAIProviderSettings } from "@ai-sdk/azure"; import { DeepSeekProviderSettings } from "@ai-sdk/deepseek"; import { GoogleGenerativeAIProviderOptions, GoogleGenerativeAIProviderSettings } from "@ai-sdk/google"; import { OpenAIProviderSettings, OpenAIResponsesProviderOptions } from "@ai-sdk/openai"; import { OpenAICompatibleProviderSettings } from "@ai-sdk/openai-compatible"; import { XaiProviderSettings } from "@ai-sdk/xai"; import { SharedV2ProviderMetadata } from "@ai-sdk/provider"; import * as z from "zod/v4"; //#region src/core/providers/types.d.ts /** * Provider 相关核心类型定义 * 只定义必要的接口,其他类型直接使用 AI SDK */ type ProviderId = keyof ProviderSettingsMap & string; interface ProviderConfig { id: string; name: string; creator?: (options: any) => any; import?: () => Promise<any>; creatorFunctionName?: string; supportsImageGeneration?: boolean; imageCreator?: (options: any) => any; validateOptions?: (options: any) => boolean; } declare class ProviderError extends Error { providerId: string; code?: string | undefined; cause?: Error | undefined; constructor(message: string, providerId: string, code?: string | undefined, cause?: Error | undefined); } type ProviderSettingsMap = { openai: OpenAIProviderSettings; 'openai-responses': OpenAIProviderSettings; 'openai-compatible': OpenAICompatibleProviderSettings; anthropic: AnthropicProviderSettings; google: GoogleGenerativeAIProviderSettings; xai: XaiProviderSettings; azure: AzureOpenAIProviderSettings; deepseek: DeepSeekProviderSettings; }; //#endregion //#region src/core/plugins/types.d.ts /** * 递归调用函数类型 * 使用 any 是因为递归调用时参数和返回类型可能完全不同 */ type RecursiveCallFn = (newParams: any) => Promise<any>; /** * AI 请求上下文 */ interface AiRequestContext { providerId: ProviderId; modelId: string; originalParams: any; metadata: Record<string, any>; startTime: number; requestId: string; recursiveCall: RecursiveCallFn; isRecursiveCall?: boolean; mcpTools?: ToolSet; [key: string]: any; } /** * 钩子分类 */ interface AiPlugin { name: string; enforce?: 'pre' | 'post'; resolveModel?: (modelId: string, context: AiRequestContext) => Promise<LanguageModel | null> | LanguageModel | null; loadTemplate?: (templateName: string, context: AiRequestContext) => any | null | Promise<any | null>; configureContext?: (context: AiRequestContext) => void | Promise<void>; transformParams?: (params: any, context: AiRequestContext) => any | Promise<any>; transformResult?: (result: any, context: AiRequestContext) => any | Promise<any>; onRequestStart?: (context: AiRequestContext) => void | Promise<void>; onRequestEnd?: (context: AiRequestContext, result: any) => void | Promise<void>; onError?: (error: Error, context: AiRequestContext) => void | Promise<void>; transformStream?: (params: any, context: AiRequestContext) => <TOOLS extends ToolSet>(options?: { tools: TOOLS; stopStream: () => void; }) => TransformStream<TextStreamPart<TOOLS>, TextStreamPart<TOOLS>>; } /** * 插件管理器配置 */ interface PluginManagerConfig { plugins: AiPlugin[]; context: Partial<AiRequestContext>; } /** * 钩子执行结果 */ interface HookResult<T = any> { value: T; stop?: boolean; } //#endregion //#region src/types.d.ts type ProviderSettings = ProviderSettingsMap[keyof ProviderSettingsMap]; type StreamTextParams = Omit<Parameters<typeof streamText>[0], 'model'>; type GenerateTextParams = Omit<Parameters<typeof generateText>[0], 'model'>; type StreamObjectParams = Omit<Parameters<typeof streamObject>[0], 'model'>; type GenerateObjectParams = Omit<Parameters<typeof generateObject>[0], 'model'>; //#endregion //#region src/core/plugins/manager.d.ts /** * 插件管理器 */ declare class PluginManager { private plugins; constructor(plugins?: AiPlugin[]); /** * 添加插件 */ use(plugin: AiPlugin): this; /** * 移除插件 */ remove(pluginName: string): this; /** * 插件排序:pre -> normal -> post */ private sortPlugins; /** * 执行 First 钩子 - 返回第一个有效结果 */ executeFirst<T>(hookName: 'resolveModel' | 'loadTemplate', arg: any, context: AiRequestContext): Promise<T | null>; /** * 执行 Sequential 钩子 - 链式数据转换 */ executeSequential<T>(hookName: 'transformParams' | 'transformResult', initialValue: T, context: AiRequestContext): Promise<T>; /** * 执行 ConfigureContext 钩子 - 串行配置上下文 */ executeConfigureContext(context: AiRequestContext): Promise<void>; /** * 执行 Parallel 钩子 - 并行副作用 */ executeParallel(hookName: 'onRequestStart' | 'onRequestEnd' | 'onError', context: AiRequestContext, result?: any, error?: Error): Promise<void>; /** * 收集所有流转换器(返回数组,AI SDK 原生支持) */ collectStreamTransforms(params: any, context: AiRequestContext): ((<TOOLS extends ai0.ToolSet>(options?: { tools: TOOLS; stopStream: () => void; }) => TransformStream<ai0.TextStreamPart<TOOLS>, ai0.TextStreamPart<TOOLS>>) | undefined)[]; /** * 获取所有插件信息 */ getPlugins(): AiPlugin[]; /** * 获取插件统计信息 */ getStats(): { total: number; pre: number; normal: number; post: number; hooks: { resolveModel: number; loadTemplate: number; transformParams: number; transformResult: number; onRequestStart: number; onRequestEnd: number; onError: number; transformStream: number; }; }; } //#endregion //#region src/core/plugins/index.d.ts declare function createContext<T extends ProviderId>(providerId: T, modelId: string, originalParams: any): AiRequestContext; declare function definePlugin(plugin: AiPlugin): AiPlugin; declare function definePlugin<T extends (...args: any[]) => AiPlugin>(pluginFactory: T): T; //#endregion //#region src/core/options/openrouter.d.ts type OpenRouterProviderOptions = { models?: string[]; /** * https://openrouter.ai/docs/use-cases/reasoning-tokens * One of `max_tokens` or `effort` is required. * If `exclude` is true, reasoning will be removed from the response. Default is false. */ reasoning?: { exclude?: boolean; } & ({ max_tokens: number; } | { effort: 'high' | 'medium' | 'low'; }); /** * A unique identifier representing your end-user, which can * help OpenRouter to monitor and detect abuse. */ user?: string; extraBody?: Record<string, unknown>; /** * Enable usage accounting to get detailed token usage information. * https://openrouter.ai/docs/use-cases/usage-accounting */ usage?: { /** * When true, includes token usage information in the response. */ include: boolean; }; }; //#endregion //#region src/core/options/xai.d.ts declare const xaiProviderOptions: z.ZodObject<{ reasoningEffort: z.ZodOptional<z.ZodEnum<{ low: "low"; high: "high"; }>>; searchParameters: z.ZodOptional<z.ZodObject<{ mode: z.ZodEnum<{ auto: "auto"; off: "off"; on: "on"; }>; returnCitations: z.ZodOptional<z.ZodBoolean>; fromDate: z.ZodOptional<z.ZodString>; toDate: z.ZodOptional<z.ZodString>; maxSearchResults: z.ZodOptional<z.ZodNumber>; sources: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{ type: z.ZodLiteral<"web">; country: z.ZodOptional<z.ZodString>; excludedWebsites: z.ZodOptional<z.ZodArray<z.ZodString>>; allowedWebsites: z.ZodOptional<z.ZodArray<z.ZodString>>; safeSearch: z.ZodOptional<z.ZodBoolean>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"x">; xHandles: z.ZodOptional<z.ZodArray<z.ZodString>>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"news">; country: z.ZodOptional<z.ZodString>; excludedWebsites: z.ZodOptional<z.ZodArray<z.ZodString>>; safeSearch: z.ZodOptional<z.ZodBoolean>; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"rss">; links: z.ZodArray<z.ZodURL>; }, z.core.$strip>]>>>; }, z.core.$strip>>; }, z.core.$strip>; type XaiProviderOptions = z.infer<typeof xaiProviderOptions>; //#endregion //#region src/core/options/types.d.ts /** * 供应商选项类型,如果map中没有,说明没有约束 */ type ProviderOptionsMap = { openai: OpenAIResponsesProviderOptions; anthropic: AnthropicProviderOptions; google: GoogleGenerativeAIProviderOptions; openrouter: OpenRouterProviderOptions; xai: XaiProviderOptions; }; type ExtractProviderOptions<T extends keyof ProviderOptionsMap> = ProviderOptionsMap[T]; /** * 类型安全的ProviderOptions * 对于已知供应商使用严格类型,对于未知供应商允许任意Record<string, JSONValue> */ type TypedProviderOptions = { [K in keyof ProviderOptionsMap]?: ProviderOptionsMap[K] } & { [K in string]?: Record<string, any> } & SharedV2ProviderMetadata; //#endregion export { type AiPlugin, type AiRequestContext, type AnthropicProviderSettings, type AzureOpenAIProviderSettings, type DeepSeekProviderSettings, ExtractProviderOptions, GenerateObjectParams, GenerateTextParams, type GoogleGenerativeAIProviderSettings, type HookResult, type OpenAICompatibleProviderSettings, type OpenAIProviderSettings, PluginManager, type PluginManagerConfig, ProviderConfig, ProviderError, type ProviderId, ProviderOptionsMap, ProviderSettings, type ProviderSettingsMap, StreamObjectParams, StreamTextParams, TypedProviderOptions, type XaiProviderSettings, createContext, definePlugin };