autosnippet
Version:
Extract code patterns into a knowledge base for AI coding assistants
294 lines (293 loc) • 11 kB
TypeScript
/**
* 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;