Files
2026Technology-Competition/docs/superpowers/specs/implementation-steps/09-phase4.1-ai-provider.md
T
范智鹏 805737fcfc feat: 设置面板 + Provider 扩展 + 构建脚本
- 设置面板:三步引导 / AI 配置 / API Key / 规则管理 / 连接测试
- Provider 重构:统一 OpenAICompatibleProvider 基类,新增 Gemini/Claude/混元/智谱等
- 审查面板 Webview:三 Tab、统计卡片、问题列表、postMessage 通信
- esbuild 构建脚本 + 生产打包 + PMD 下载
- 保存文件自动静态分析(500ms debounce)
2026-07-16 22:20:30 +08:00

5.0 KiB

Step 09 — Phase 4.1: AI Provider 基础设施

依赖: Step 01(配置模块)
参考设计: §5.2

目标

实现 AI Provider 策略模式基础设施:抽象基类、2 个 Provider 实现、工厂函数。

新建文件

# 文件 说明
1 src/ai/providers/base.ts AIProvider 抽象基类 + ChatOptions
2 src/ai/providers/deepseek.ts DeepSeekProvider
3 src/ai/providers/openai.ts OpenAIProvider
4 src/ai/factory.ts createProvider() 工厂

1. src/ai/providers/base.ts

export interface ChatOptions {
  model: string;
  temperature: number;
  timeoutMs: number;
}

export abstract class AIProvider {
  abstract id: string;
  abstract name: string;

  constructor(
    protected apiKey: string,
    protected baseUrl: string
  ) {}

  abstract chat(
    systemPrompt: string,
    userPrompt: string,
    options: ChatOptions
  ): Promise<string>;
}

2. src/ai/providers/deepseek.ts

import { AIProvider, ChatOptions } from './base';

export class DeepSeekProvider extends AIProvider {
  id = 'deepseek';
  name = 'DeepSeek';

  async chat(systemPrompt: string, userPrompt: string, options: ChatOptions): Promise<string> {
    const url = `${this.baseUrl}/chat/completions`;

    const body = JSON.stringify({
      model: options.model,
      temperature: options.temperature,
      messages: [
        { role: 'system', content: systemPrompt },
        { role: 'user', content: userPrompt },
      ],
    });

    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), options.timeoutMs);

    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Bearer ${this.apiKey}`,
        },
        body,
        signal: controller.signal,
      });

      if (!response.ok) {
        const errorText = await response.text();
        if (response.status === 401) {
          throw new Error('API Key 无效,请重新设置');
        }
        throw new Error(`API 请求失败 (${response.status}): ${errorText}`);
      }

      const data = await response.json() as {
        choices: Array<{ message: { content: string } }>;
      };
      return data.choices[0]?.message?.content ?? '';
    } finally {
      clearTimeout(timeout);
    }
  }
}

3. src/ai/providers/openai.ts

import { AIProvider, ChatOptions } from './base';

export class OpenAIProvider extends AIProvider {
  id = 'openai';
  name = 'OpenAI';

  async chat(systemPrompt: string, userPrompt: string, options: ChatOptions): Promise<string> {
    const url = `${this.baseUrl}/chat/completions`;

    const body = JSON.stringify({
      model: options.model,
      temperature: options.temperature,
      messages: [
        { role: 'system', content: systemPrompt },
        { role: 'user', content: userPrompt },
      ],
    });

    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), options.timeoutMs);

    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Bearer ${this.apiKey}`,
        },
        body,
        signal: controller.signal,
      });

      if (!response.ok) {
        const errorText = await response.text();
        if (response.status === 401) {
          throw new Error('API Key 无效,请重新设置');
        }
        throw new Error(`API 请求失败 (${response.status}): ${errorText}`);
      }

      const data = await response.json() as {
        choices: Array<{ message: { content: string } }>;
      };
      return data.choices[0]?.message?.content ?? '';
    } finally {
      clearTimeout(timeout);
    }
  }
}

4. src/ai/factory.ts

import { AIProvider } from './providers/base';
import { DeepSeekProvider } from './providers/deepseek';
import { OpenAIProvider } from './providers/openai';

type ProviderConstructor = new (apiKey: string, baseUrl: string) => AIProvider;

const registry: Record<string, ProviderConstructor> = {
  deepseek: DeepSeekProvider,
  openai: OpenAIProvider,
};

export function createProvider(providerId: string, apiKey: string, baseUrl: string): AIProvider {
  const Cls = registry[providerId];
  if (!Cls) {
    throw new Error(`未知的 Provider: ${providerId}`);
  }
  return new Cls(apiKey, baseUrl);
}

export function getProviderIds(): string[] {
  return Object.keys(registry);
}

关键逻辑

  • Provider 统一实现 chat(systemPrompt, userPrompt, options): Promise<string>
  • 支持 AbortController 超时控制
  • HTTP 401 → 抛出 "API Key 无效" 错误
  • 工厂函数通过注册表字符串查找,便于添加新的 Provider
  • 两个 Provider 实现几乎相同(都是 openai 兼容 API),可考虑后续合并

验收

  • 4 个文件创建完成
  • npm run compile 通过
  • npm run lint 通过