feat: M0-08 — SiliconFlow provider + Content Safety integration + dynamic config
Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 17s
Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 17s
This commit is contained in:
parent
577b2c7abe
commit
22132410a2
@ -13,6 +13,7 @@ import { LearningTrendWorkflow } from './workflows/learning-trend.workflow';
|
||||
import { AdminAiGatewayController } from './ai.controller';
|
||||
import { MockAiProvider } from './providers/mock-ai.provider';
|
||||
import { DeepSeekProvider } from './providers/deepseek.provider';
|
||||
import { SiliconFlowProvider } from './providers/siliconflow.provider';
|
||||
import { MiniMaxProvider } from './providers/minimax.provider';
|
||||
import type { AiProvider } from './providers/ai-provider.interface';
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import { ModelRouter } from '../model-router';
|
||||
import { PromptTemplateService } from '../prompts/prompt-template.service';
|
||||
import { AiCostCalculatorService } from '../usage/ai-cost-calculator.service';
|
||||
import { AiUsageLogService } from '../usage/ai-usage-log.service';
|
||||
import { ContentSafetyService } from '../../content-safety/content-safety.service';
|
||||
import type { AiProvider } from '../providers/ai-provider.interface';
|
||||
import type { GatewayRequest, GatewayResponse, ModelTier } from './ai-gateway.types';
|
||||
|
||||
@ -17,7 +18,9 @@ export class AiGatewayService {
|
||||
private readonly promptTemplate: PromptTemplateService,
|
||||
private readonly costCalculator: AiCostCalculatorService,
|
||||
private readonly usageLog: AiUsageLogService,
|
||||
private readonly contentSafety?: ContentSafetyService,
|
||||
private readonly providers: Map<string, AiProvider>,
|
||||
private readonly contentSafety?: ContentSafetyService,
|
||||
) {}
|
||||
|
||||
async generate(request: GatewayRequest, timeoutMs = this.DEFAULT_TIMEOUT_MS): Promise<GatewayResponse> {
|
||||
@ -47,6 +50,9 @@ export class AiGatewayService {
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
const safetyCheck = await this.contentSafety?.check(output.rawText, { contentType: 'ai_output' }).catch(() => ({ safe: true }));
|
||||
if (!safetyCheck.safe) throw new Error('AI output blocked by content safety');
|
||||
|
||||
const parsed = this.parseJson(output.rawText, request.outputSchema);
|
||||
const estimatedCost = this.costCalculator.calculate(
|
||||
target.provider,
|
||||
|
||||
@ -4,6 +4,8 @@ import type { AiProvider, AiGenerateInput, AiGenerateOutput } from './ai-provide
|
||||
|
||||
@Injectable()
|
||||
export class DeepSeekProvider implements AiProvider {
|
||||
private baseUrl = process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com/v1';
|
||||
private apiKey = process.env.DEEPSEEK_API_KEY || '';
|
||||
readonly name = 'deepseek';
|
||||
private readonly logger = new Logger(DeepSeekProvider.name);
|
||||
private readonly apiKey: string;
|
||||
|
||||
40
src/modules/ai/providers/siliconflow.provider.ts
Normal file
40
src/modules/ai/providers/siliconflow.provider.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import type { AiProvider, AiGenerateInput, AiGenerateOutput } from './ai-provider.interface';
|
||||
|
||||
@Injectable()
|
||||
export class SiliconFlowProvider implements AiProvider {
|
||||
readonly name = 'siliconflow';
|
||||
|
||||
private getConfig() {
|
||||
return {
|
||||
baseUrl: process.env.SILICONFLOW_BASE_URL || 'https://api.siliconflow.cn/v1',
|
||||
apiKey: process.env.SILICONFLOW_API_KEY || '',
|
||||
};
|
||||
}
|
||||
|
||||
async generate(input: AiGenerateInput): Promise<AiGenerateOutput> {
|
||||
const { baseUrl, apiKey } = this.getConfig();
|
||||
const start = Date.now();
|
||||
|
||||
const resp = await fetch(`${baseUrl}/chat/completions`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
|
||||
body: JSON.stringify({
|
||||
model: input.model,
|
||||
messages: input.messages,
|
||||
temperature: input.temperature ?? 0.3,
|
||||
max_tokens: input.maxTokens ?? 4096,
|
||||
}),
|
||||
signal: input.signal ?? AbortSignal.timeout(60_000),
|
||||
});
|
||||
|
||||
if (!resp.ok) throw new Error(`SiliconFlow ${resp.status}: ${await resp.text().slice(0, 200)}`);
|
||||
const data = await resp.json();
|
||||
const choice = data.choices?.[0];
|
||||
return {
|
||||
rawText: choice?.message?.content || '',
|
||||
usage: { inputTokens: data.usage?.prompt_tokens || 0, outputTokens: data.usage?.completion_tokens || 0 },
|
||||
latencyMs: Date.now() - start,
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user