UNPKG

@cherrystudio/ai-core

Version:

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

269 lines (268 loc) 9.95 kB
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 * as ai0 from "ai"; import { LanguageModel, TextStreamPart, ToolSet, generateObject, generateText, streamObject, streamText } from "ai"; 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 as PluginManager$1, type PluginManagerConfig, ProviderConfig, ProviderError, type ProviderId, ProviderOptionsMap, ProviderSettings, type ProviderSettingsMap, StreamObjectParams, StreamTextParams, TypedProviderOptions, type XaiProviderSettings, createContext as createContext$1, definePlugin as definePlugin$1 };