From 6473395f86617d341b3d6c1e23a3dc2a67e5d63c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=B9=BF?= Date: Tue, 10 Jun 2025 18:21:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(ai):=20=E6=B7=BB=E5=8A=A0=20AI=20=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E9=80=82=E9=85=8D=E5=99=A8=E7=AE=A1=E7=90=86=E5=99=A8?= =?UTF-8?q?=E5=92=8C=E6=96=B0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 AiAdapterManager 类,用于管理和初始化不同 AI 服务适配器 - 添加 AiServiceAdapter 接口和两个实现类:DeepSeekAdapter 和 TongyiAdapter - 在 AiConfig 实体中增加 provider 字段,用于标识 AI 服务提供商 - 新增 ai-config 模块,负责 AI 配置的管理和存储 - 在 ai 服务中集成适配器管理器,支持动态获取不同 AI 服务的适配器 - 更新 ai 控制器,添加获取适配器信息的接口 --- .lingma/rules/project_rule.md | 1 + backend/src/ai/ai-adapter.manager.ts | 38 ++++++++++++++++++++++++++++ backend/src/ai/ai-config.entity.ts | 4 +++ backend/src/ai/ai-config.module.ts | 22 ++++++++++++++++ backend/src/ai/ai.adapter.ts | 34 +++++++++++++++++++++++++ backend/src/ai/ai.controller.ts | 8 +++++- backend/src/ai/ai.module.ts | 23 ++++++----------- backend/src/ai/ai.service.ts | 19 ++++++++++++++ backend/src/ai/deepseek.adapter.ts | 16 ++++++++++++ backend/src/ai/tongyi.adapter.ts | 16 ++++++++++++ backend/tsconfig.json | 4 ++- docs/project/status.md | 2 +- 12 files changed, 169 insertions(+), 18 deletions(-) create mode 100644 backend/src/ai/ai-adapter.manager.ts create mode 100644 backend/src/ai/ai-config.module.ts create mode 100644 backend/src/ai/ai.adapter.ts create mode 100644 backend/src/ai/deepseek.adapter.ts create mode 100644 backend/src/ai/tongyi.adapter.ts diff --git a/.lingma/rules/project_rule.md b/.lingma/rules/project_rule.md index 35a81c3..e18624f 100644 --- a/.lingma/rules/project_rule.md +++ b/.lingma/rules/project_rule.md @@ -6,3 +6,4 @@ 1. 语言:TypeScript 2. nodejs 使用版本v22 3. 执行node, npm 相关命令时先执行`nvm use v22`切换到v22版本 +4. dev环境运行命令是`npm run dev` \ No newline at end of file diff --git a/backend/src/ai/ai-adapter.manager.ts b/backend/src/ai/ai-adapter.manager.ts new file mode 100644 index 0000000..1d418db --- /dev/null +++ b/backend/src/ai/ai-adapter.manager.ts @@ -0,0 +1,38 @@ +import { AiConfig } from './ai-config.entity'; +import { AiServiceAdapter } from './ai.adapter'; +import { DeepSeekAdapter } from './deepseek.adapter'; +import { TongyiAdapter } from './tongyi.adapter'; + +export class AiAdapterManager { + private adapters: Map = new Map(); + + constructor(private readonly aiConfigs: AiConfig[]) { + this.initializeAdapters(); + } + + private initializeAdapters() { + this.aiConfigs?.forEach(config => { + // 确保config.id是字符串类型 + const serviceId = config.id.toString(); + + switch (config.provider) { + case 'deepseek': + this.adapters.set(serviceId, new DeepSeekAdapter(config)); + break; + case 'tongyi': + this.adapters.set(serviceId, new TongyiAdapter(config)); + break; + default: + throw new Error(`Unsupported AI provider: ${config.provider}`); + } + }); + } + + getAdapter(serviceId: string): AiServiceAdapter { + const adapter = this.adapters.get(serviceId); + if (!adapter) { + throw new Error(`AI adapter not found for service ID: ${serviceId}`); + } + return adapter; + } +} \ No newline at end of file diff --git a/backend/src/ai/ai-config.entity.ts b/backend/src/ai/ai-config.entity.ts index d32da92..259767e 100644 --- a/backend/src/ai/ai-config.entity.ts +++ b/backend/src/ai/ai-config.entity.ts @@ -12,6 +12,10 @@ export class AiConfig { @Column() apiKey!: string; + // 新增provider字段,用于标识AI服务提供商 + @Column({ type: 'enum', enum: ['deepseek', 'tongyi'] }) + provider!: 'deepseek' | 'tongyi'; + @OneToOne(() => Workspace) @JoinColumn() workspace!: Workspace; diff --git a/backend/src/ai/ai-config.module.ts b/backend/src/ai/ai-config.module.ts new file mode 100644 index 0000000..3151734 --- /dev/null +++ b/backend/src/ai/ai-config.module.ts @@ -0,0 +1,22 @@ +import { Module } from '@nestjs/common'; +import { AiConfigService } from './ai-config.service'; +import { AiConfigController } from './ai-config.controller'; +import { WorkspaceModule } from '../workspace/workspace.module'; +import { AiConfigRepository } from './ai-config.repository'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { AiConfig } from './ai-config.entity'; +import { WorkspaceRepository } from '../workspace/workspace.repository'; +import { JwtStrategy } from '@src/auth'; + +@Module({ + imports: [WorkspaceModule, TypeOrmModule.forFeature([AiConfig])], + providers: [ + AiConfigService, + AiConfigRepository, + WorkspaceRepository, + JwtStrategy + ], + controllers: [AiConfigController], + exports: [AiConfigService] +}) +export class AiConfigModule {} \ No newline at end of file diff --git a/backend/src/ai/ai.adapter.ts b/backend/src/ai/ai.adapter.ts new file mode 100644 index 0000000..dbe58ec --- /dev/null +++ b/backend/src/ai/ai.adapter.ts @@ -0,0 +1,34 @@ +import { AiConfig } from "./ai-config.entity"; + +export interface AiServiceAdapter { + generateText(model: string, prompt: string, config?: Record): Promise; + chat(model: string, messages: Array<{role: string, content: string}>, config?: Record): Promise; +} + +export class DeepSeekAdapter implements AiServiceAdapter { + constructor(private readonly config: AiConfig) {} + + async generateText(model: string, prompt: string): Promise { + // 实现DeepSeek的文本生成逻辑 + return `Generated text by ${model}: ${prompt}`; + } + + async chat(model: string, messages: Array<{role: string, content: string}>): Promise { + // 实现DeepSeek的对话交互逻辑 + return `Chat response from ${model}: ${JSON.stringify(messages)}`; + } +} + +export class TongyiAdapter implements AiServiceAdapter { + constructor(private readonly config: AiConfig) {} + + async generateText(model: string, prompt: string): Promise { + // 实现通义千问的文本生成逻辑 + return `Tongyi generated text: ${prompt}`; + } + + async chat(model: string, messages: Array<{role: string, content: string}>): Promise { + // 实现通义千问的对话交互逻辑 + return `Tongyi chat response: ${JSON.stringify(messages)}`; + } +} \ No newline at end of file diff --git a/backend/src/ai/ai.controller.ts b/backend/src/ai/ai.controller.ts index 4e86547..d02a464 100644 --- a/backend/src/ai/ai.controller.ts +++ b/backend/src/ai/ai.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Post, Body, UseGuards } from '@nestjs/common'; +import { Controller, Post, Body, UseGuards, Get, Param } from '@nestjs/common'; import { AiService } from './ai.service'; import { JwtAuthGuard } from '../auth/jwt-auth.guard'; @@ -11,4 +11,10 @@ export class AiController { generateCode(@Body() body: { prompt: string; workspaceId: number }) { return this.aiService.generateCode(body.prompt, body.workspaceId); } + + @Get('adapter/:serviceId') + getAdapterInfo(@Param('serviceId') serviceId: string) { + const adapter = this.aiService.getAdapter(serviceId); + return { provider: adapter.constructor.name }; + } } \ No newline at end of file diff --git a/backend/src/ai/ai.module.ts b/backend/src/ai/ai.module.ts index c96ec76..8ec4c1a 100644 --- a/backend/src/ai/ai.module.ts +++ b/backend/src/ai/ai.module.ts @@ -1,22 +1,15 @@ + import { Module } from '@nestjs/common'; -import { AiConfigService } from './ai-config.service'; -import { AiConfigController } from './ai-config.controller'; -import { WorkspaceModule } from '../workspace/workspace.module'; -import { AiConfigRepository } from './ai-config.repository'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AiConfig } from './ai-config.entity'; -import { WorkspaceRepository } from '../workspace/workspace.repository'; // 新增导入 -import { JwtStrategy } from '@src/auth'; +import { AiService } from './ai.service'; +import { AiController } from './ai.controller'; +import { AiAdapterManager } from './ai-adapter.manager'; @Module({ - imports: [WorkspaceModule, TypeOrmModule.forFeature([AiConfig])], - providers: [ - AiConfigService, - AiConfigRepository, - WorkspaceRepository, // 添加缺失的依赖 - JwtStrategy - ], - controllers: [AiConfigController], - exports: [AiConfigService] + imports: [TypeOrmModule.forFeature([AiConfig])], + providers: [AiService, AiAdapterManager], + controllers: [AiController], + exports: [AiService] }) export class AiModule {} \ No newline at end of file diff --git a/backend/src/ai/ai.service.ts b/backend/src/ai/ai.service.ts index 77c68d7..67fb398 100644 --- a/backend/src/ai/ai.service.ts +++ b/backend/src/ai/ai.service.ts @@ -2,19 +2,38 @@ import { Injectable } from '@nestjs/common'; import { HttpService } from '@nestjs/axios'; import { ConfigService } from '@nestjs/config'; import { AiConfigService } from './ai-config.service'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { AiConfig } from './ai-config.entity'; +import { AiAdapterManager } from './ai-adapter.manager'; @Injectable() export class AiService { private readonly defaultModel: string; + private adapterManager!: AiAdapterManager; constructor( private readonly httpService: HttpService, private readonly configService: ConfigService, private readonly aiConfigService: AiConfigService, + @InjectRepository(AiConfig) + private aiConfigRepository: Repository, ) { this.defaultModel = this.configService.get('AI_DEFAULT_MODEL', 'gpt-3.5-turbo'); } + async initializeAdapterManager() { + const configs = await this.aiConfigRepository.find(); + this.adapterManager = new AiAdapterManager(configs); + } + + getAdapter(serviceId: string) { + if (!this.adapterManager) { + throw new Error('AI adapter manager not initialized'); + } + return this.adapterManager.getAdapter(serviceId); + } + async generateCode(prompt: string, workspaceId: number) { // 获取工作区的AI配置 const aiConfig = await this.aiConfigService.getAiConfigByWorkspace(workspaceId); diff --git a/backend/src/ai/deepseek.adapter.ts b/backend/src/ai/deepseek.adapter.ts new file mode 100644 index 0000000..530d99e --- /dev/null +++ b/backend/src/ai/deepseek.adapter.ts @@ -0,0 +1,16 @@ +import { AiServiceAdapter } from './ai.adapter'; + +export class DeepSeekAdapter implements AiServiceAdapter { + constructor(private readonly config: any) {} + + // 实现AiServiceAdapter接口定义的方法 + async generateText(model: string, prompt: string, config?: Record): Promise { + // 这里添加调用DeepSeek API的具体实现 + return `Response from DeepSeek: ${prompt}`; + } + + async chat(model: string, messages: Array<{role: string, content: string}>, config?: Record): Promise { + // 这里添加调用DeepSeek API的具体实现 + return `Chat response from DeepSeek: ${messages[messages.length - 1].content}`; + } +} \ No newline at end of file diff --git a/backend/src/ai/tongyi.adapter.ts b/backend/src/ai/tongyi.adapter.ts new file mode 100644 index 0000000..f4a482a --- /dev/null +++ b/backend/src/ai/tongyi.adapter.ts @@ -0,0 +1,16 @@ +import { AiServiceAdapter } from './ai.adapter'; + +export class TongyiAdapter implements AiServiceAdapter { + constructor(private readonly config: any) {} + + // 实现AiServiceAdapter接口定义的方法 + async generateText(model: string, prompt: string, config?: Record): Promise { + // 这里添加调用Tongyi API的具体实现 + return `Response from Tongyi: ${prompt}`; + } + + async chat(model: string, messages: Array<{role: string, content: string}>, config?: Record): Promise { + // 这里添加调用Tongyi API的具体实现 + return `Chat response from Tongyi: ${messages[messages.length - 1].content}`; + } +} \ No newline at end of file diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 2bbb58b..dd6dab3 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -13,7 +13,9 @@ "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { - "@src/*": ["./src/*"] + "@src/*": ["./src/*"], + "./deepseek.adapter": ["./src/ai/deepseek.adapter"], + "./tongyi.adapter": ["./src/ai/tongyi.adapter"] } }, "include": ["src/**/*"] diff --git a/docs/project/status.md b/docs/project/status.md index f40e37d..a8e4598 100644 --- a/docs/project/status.md +++ b/docs/project/status.md @@ -1,7 +1,7 @@ # 项目状态报告 ## 日期 -2023-10-05 +2025-06-09 ## 已完成功能 1. 基础架构搭建