feat: 初始化 Nest 服务骨架与多平台 Chat SSE 网关
- 新增 NestJS + Fastify 入口、配置模块与 Swagger 集成 - 划分 client-app / admin-app 与 shared-domain ai-gateway - 实现统一 SSE Chat 接口,支持千问、DeepSeek、火山引擎非流式上游与网关分片输出 - 补充项目方案与 JWT 最小实现文档 Made-with: Cursor
This commit is contained in:
82
src/apps/shared-domain/ai-gateway/providers/volc.provider.ts
Normal file
82
src/apps/shared-domain/ai-gateway/providers/volc.provider.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { Injectable, InternalServerErrorException } from '@nestjs/common';
|
||||
import { request } from 'undici';
|
||||
import { ProviderStreamResult, StreamChatRequest } from '../types/chat.types';
|
||||
import { AiProvider } from './provider.interface';
|
||||
|
||||
@Injectable()
|
||||
export class VolcProvider implements AiProvider {
|
||||
readonly code = 'volc';
|
||||
|
||||
supports(model?: string): boolean {
|
||||
if (!model) return true;
|
||||
return (
|
||||
model.toLowerCase().includes('volc') ||
|
||||
model.toLowerCase().includes('ark')
|
||||
);
|
||||
}
|
||||
|
||||
async streamChat(req: StreamChatRequest): Promise<ProviderStreamResult> {
|
||||
const apiKey = process.env.VOLC_API_KEY;
|
||||
const baseUrl =
|
||||
process.env.VOLC_BASE_URL || 'https://ark.cn-beijing.volces.com/api/v3';
|
||||
|
||||
if (!apiKey) {
|
||||
throw new InternalServerErrorException('VOLC_API_KEY 未配置');
|
||||
}
|
||||
|
||||
const model = req.model || 'ep-default';
|
||||
const upstreamBody = {
|
||||
model,
|
||||
stream: false,
|
||||
messages: (req.messages || []).map((m) => ({
|
||||
role: m.role,
|
||||
content: m.content,
|
||||
})),
|
||||
};
|
||||
|
||||
const { statusCode, body } = await request(`${baseUrl}/chat/completions`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(upstreamBody),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (statusCode < 200 || statusCode >= 300) {
|
||||
const text = await body.text();
|
||||
throw new InternalServerErrorException(
|
||||
`火山引擎调用失败: ${statusCode} - ${text}`,
|
||||
);
|
||||
}
|
||||
|
||||
const json = (await body.json()) as any;
|
||||
const content: string =
|
||||
json.choices?.[0]?.message?.content ??
|
||||
'[Volc] 未返回内容,请检查请求参数或模型配置。';
|
||||
|
||||
const promptTokens: number = json.usage?.prompt_tokens ?? 0;
|
||||
const completionTokens: number = json.usage?.completion_tokens ?? 0;
|
||||
const totalTokens: number =
|
||||
json.usage?.total_tokens ?? Math.max(1, promptTokens + completionTokens);
|
||||
|
||||
const chunks = this.splitText(content, 24).map((c) => ({ content: c }));
|
||||
|
||||
return {
|
||||
requestId: json.id || `volc_${Date.now()}`,
|
||||
providerCode: this.code,
|
||||
model,
|
||||
chunks,
|
||||
usage: { promptTokens, completionTokens, totalTokens },
|
||||
};
|
||||
}
|
||||
|
||||
private splitText(text: string, size: number) {
|
||||
const result: string[] = [];
|
||||
for (let i = 0; i < text.length; i += size) {
|
||||
result.push(text.slice(i, i + size));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user