UNPKG

autosnippet

Version:

Extract code patterns into a knowledge base for AI coding assistants

294 lines (293 loc) 11 kB
/** * AiProvider - AI 提供商抽象基类 * 所有具体 Provider 必须实现这3个方法 */ /** Loose JSON record for external API responses (inherently untyped) */ export type ApiResponse = Record<string, any>; /** AI provider 构造配置 */ export interface AiProviderConfig { model?: string; apiKey?: string; baseUrl?: string; timeout?: number; maxRetries?: number; circuitThreshold?: number; maxConcurrency?: number | string; name?: string; embedModel?: string; responses?: Record<string, unknown>; [key: string]: unknown; } /** 对话历史条目 */ export interface ChatHistoryEntry { role: 'user' | 'assistant'; content: string; } /** 对话上下文选项 */ export interface ChatContext { history?: ChatHistoryEntry[]; temperature?: number; maxTokens?: number; systemPrompt?: string; } /** 统一消息格式 */ export interface UnifiedMessage { role: 'user' | 'assistant' | 'tool'; content?: string | null; toolCalls?: Array<{ id: string; name: string; args: Record<string, unknown>; thoughtSignature?: string; }>; toolCallId?: string; name?: string; } /** 工具 schema */ export interface ToolSchema { name: string; description?: string; parameters?: Record<string, unknown>; } /** chatWithTools 选项 */ export interface ChatWithToolsOptions { messages?: unknown[]; toolSchemas?: unknown[]; toolChoice?: string; systemPrompt?: string; temperature?: number; maxTokens?: number; /** 外部中止信号 — hard timeout 时取消进行中的 LLM 请求 */ abortSignal?: AbortSignal; } /** 函数调用结果 */ export interface FunctionCallResult { id: string; name: string; args: Record<string, unknown>; thoughtSignature?: string; } /** chatWithTools 返回值 */ export interface ChatWithToolsResult { text: string | null; functionCalls: FunctionCallResult[] | null; usage?: TokenUsage | null; } /** Token 用量 */ export interface TokenUsage { inputTokens: number; outputTokens: number; totalTokens: number; } /** chatWithStructuredOutput 选项 */ export interface StructuredOutputOptions { schema?: Record<string, unknown>; openChar?: string; closeChar?: string; temperature?: number; maxTokens?: number; systemPrompt?: string; } /** enrichCandidates 选项 */ export interface EnrichOptions { lang?: string; } /** enrichCandidates 候选条目 */ export interface EnrichCandidate { code?: string; language?: string; title?: string; description?: string; rationale?: string; knowledgeType?: string; complexity?: string; scope?: string; steps?: unknown[]; constraints?: { preconditions?: unknown[]; boundaries?: unknown[]; sideEffects?: unknown[]; }; summary?: string; category?: string; } /** 文件内容条目(用于语言检测) */ export interface FileContentEntry { name?: string; [key: string]: unknown; } /** 语言 profile */ export interface LanguageProfile { primaryLanguage: string; role: string; patternExamples: string; extractionExamples: string; categories: string; } /** Logger 接口 — 兼容 winston.Logger 实例 */ export interface AiLogger { debug(message: string, ...args: unknown[]): void; info(message: string, ...args: unknown[]): void; warn(message: string, ...args: unknown[]): void; error(message: string, ...args: unknown[]): void; [key: string]: unknown; } export declare class AiProvider { _activeRequests: number; _circuitCooldownMs: number; _circuitFailures: number; _circuitOpenedAt: number; _circuitState: 'CLOSED' | 'OPEN' | 'HALF_OPEN'; _circuitThreshold: number; _maxConcurrency: number; _rateLimitedUntil: number; _requestQueue: Array<(value?: unknown) => void>; apiKey: string; baseUrl: string; logger: AiLogger | null; maxRetries: number; model: string; name: string; timeout: number; _fallbackFrom?: string; /** * Token 用量回调 — 每次 API 调用后触发(包括 chat / chatWithStructuredOutput / chatWithTools) * 由外部(如 DI 容器)注入以实现全局 token 计量。 */ _onTokenUsage: ((usage: TokenUsage & { source?: string; }) => void) | null; constructor(config?: AiProviderConfig); _acquireRequestSlot(): Promise<void>; _releaseRequestSlot(): void; _waitForRateLimitWindow(): Promise<void>; _setRateLimitWindow(waitMs: number): void; /** * 对话 - 发送 prompt + context,返回文本响应 * @param context {history: [], temperature, maxTokens} */ chat(prompt: string, context?: ChatContext): Promise<string>; /** * 从 API 原始响应中提取 token 用量并触发回调。 * 子类在 chat() / chatWithStructuredOutput() 中调用。 */ _emitTokenUsage(usage: TokenUsage | null | undefined, source?: string): void; /** 摘要 - 对代码/文档生成结构化摘要 */ summarize(code: string): Promise<unknown>; /** 向量嵌入 - 返回浮点数组 */ embed(text: string | string[]): Promise<number[] | number[][]>; /** * 探测 provider 是否可用(轻量级 API 调用验证连接性) * 子类可覆盖实现更具体的探测逻辑 */ probe(): Promise<boolean>; /** 检查是否支持 embedding */ supportsEmbedding(): boolean; /** * 是否支持原生结构化函数调用(非文本解析) * 子类(如 GoogleGeminiProvider)覆盖返回 true */ get supportsNativeToolCalling(): boolean; /** * 带工具声明的结构化对话 — 原生函数调用 API * * 支持原生函数调用的 Provider(Gemini / OpenAI / Claude)覆盖此方法, * 返回结构化 functionCall 而非文本,AgentRuntime 据此跳过正则解析。 * * 默认实现降级为 chat(),由 AgentRuntime 进行文本解析。 * * 统一消息格式 (Provider-Agnostic): * - { role: 'user', content: 'text' } * - { role: 'assistant', content: 'text or null', toolCalls: [{id, name, args}] } * - { role: 'tool', toolCallId: 'id', name: 'tool_name', content: 'result string' } * * @param prompt 用户消息(仅在 messages 为空时使用) * @param opts.messages 统一格式消息历史 * @param opts.toolSchemas [{name, description, parameters}] * @param opts.toolChoice 'auto' | 'required' | 'none' * @param [opts.systemPrompt] 系统指令 * @returns >|null}>} */ chatWithTools(prompt: string, opts?: ChatWithToolsOptions): Promise<ChatWithToolsResult>; /** * Structured Output — 请求 AI 返回严格 JSON 格式响应 * * 子类覆盖以利用原生 JSON mode: * - Gemini: responseMimeType: 'application/json' + responseSchema * - OpenAI: response_format: { type: 'json_object' } * - Claude: 无原生支持,使用默认实现 (chat + extractJSON) * * @param prompt 完整提示词(应包含返回 JSON 的指令) * @param [opts.schema] JSON Schema(Gemini/OpenAI 的 structured output 用) * @param [opts.openChar='{'] extractJSON 边界起始符(fallback 用) * @param [opts.closeChar='}'] extractJSON 边界终止符 * @param [opts.systemPrompt] 可选系统指令 * @returns 解析后的 JSON 对象/数组,解析失败返回 null */ chatWithStructuredOutput(prompt: string, opts?: StructuredOutputOptions): Promise<unknown>; /** 内部日志辅助(子类可通过 this.logger 覆盖) */ _log(level: string, message: string): void; /** * 根据用户语言偏好生成输出语言指令 * @param [lang] 语言代码,如 'zh', 'en' * @returns 语言指令段落(为空则返回空字符串) */ _buildLangInstruction(lang: string | undefined): string; /** 根据文件扩展名检测语言特征,返回提示词适配参数 */ _detectLanguageProfile(filesContent: FileContentEntry[]): LanguageProfile; /** * AI 语义字段补全 — 分析候选代码,填补缺失的语义字段 * @param candidates 候选对象数组,每项至少含 {code, language, title?} * @returns enriched 候选数组(仅含补全的字段) */ enrichCandidates(candidates: EnrichCandidate[], options?: EnrichOptions): Promise<any[]>; /** 构建 enrichCandidates 提示词 */ _buildEnrichPrompt(candidates: EnrichCandidate[], options?: EnrichOptions): string; /** * 解析当前 Provider 应使用的代理 URL。 * 优先级(从高到低): * 1. Provider 专属: ASD_{PROVIDER}_PROXY_HTTPS / ASD_{PROVIDER}_PROXY_HTTP * 2. 全局 ASD 专属: ASD_AI_PROXY * 3. 系统通用: HTTPS_PROXY / HTTP_PROXY / ALL_PROXY * * Provider 名称映射: google-gemini → GOOGLE, openai → OPENAI, claude → CLAUDE, deepseek → DEEPSEEK */ _resolveProxyUrl(): string; /** * 代理感知的 fetch — 自动检测代理并使用 undici ProxyAgent。 * 子类的 _post() 应调用此方法替代全局 fetch()。 */ _fetch(url: string, options?: Record<string, unknown>): Promise<import("undici").Response>; /** * 从 LLM 响应提取 JSON (extractJSON kept below) * 支持截断修复:当 AI 输出被 token 限制截断时,尝试关闭未完成的 JSON 结构 */ extractJSON(text: string, openChar?: string, closeChar?: string): any; /** * 修复被截断的 JSON 数组 — 回收已完成的对象 * 策略 1(主路径): 字符级解析找到最后一个完整的顶层 {...} 对象 * 策略 2(回退路径): 正则 + 渐进 JSON.parse 尝试(应对代码段中未转义引号导致 inString 追踪失效) */ _repairTruncatedArray(text: string): any[] | null; /** 字符级深度追踪修复(原逻辑,处理标准 JSON) */ _repairByCharTracking(text: string): any[] | null; /** * 正则回退修复 — 不依赖 inString 追踪 * 寻找所有 "},\s*{" 或 "}\s*]" 边界,从后往前尝试 JSON.parse */ _repairByRegexFallback(text: string): any[] | null; /** 在指定位置截断并尝试闭合 JSON 数组 */ _tryRepairAt(text: string, endPos: number): any[] | null; /** * 指数退避重试 + 熔断器(受 Cline 三级错误恢复启发) * * 熔断器三态: * CLOSED — 正常工作,计数连续失败 * OPEN — 连续 N 次失败,直接拒绝请求(快速失败),持续 cooldownMs * HALF_OPEN — 冷却期后尝试一次,成功则恢复,失败则重新 OPEN * * 这避免了 AI 服务宕机时无意义的重试风暴。 */ _withRetry<T>(fn: () => Promise<T>, retries?: number, baseDelay?: number): Promise<T>; } export default AiProvider;