feat(web): 重构前端UI并支持OpenAI协议

- 添加账号管理详情页(基本信息、提示词、CapCut、参考图标签页)
- 重构资产页面,按项目组分开展示图片/视频
- 聊天界面支持深度思考内容折叠展示、复制、删除消息
- 设置页面支持Agent配置(Anthropic/OpenAI协议)和工具配置
- 后端支持OpenAI兼容协议流式输出和DeepSeek思考模式
- 添加对话置顶/删除功能、数据库迁移、资产清单API
- 添加账号参考图上传/删除、技能配置持久化、连接测试API
This commit is contained in:
2026-05-07 23:48:26 +08:00
parent 01963aac96
commit 088bdb9a8e
40 changed files with 2594 additions and 678 deletions

View File

@@ -1,4 +1,5 @@
import Anthropic from '@anthropic-ai/sdk';
import OpenAI from 'openai';
import { tools, ToolDefinition } from './tools';
import { getDb } from '../db';
import fs from 'fs';
@@ -9,34 +10,44 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const PROJECT_ROOT = path.resolve(__dirname, '..', '..', '..', '..');
function getAnthropicClient(): Anthropic {
export type Protocol = 'anthropic' | 'openai';
interface ApiConfig {
protocol: Protocol;
apiKey: string;
baseURL: string | undefined;
model: string;
}
function getApiConfig(): ApiConfig {
const configRow = getDb().prepare('SELECT value FROM configs WHERE key = ?').get('api_keys') as { value: string } | undefined;
let apiKey = process.env.ANTHROPIC_API_KEY || '';
let baseURL: string | undefined;
let model = process.env.ANTHROPIC_MODEL || 'claude-sonnet-4-6';
let protocol: Protocol = 'anthropic';
if (configRow) {
try {
const cfg = JSON.parse(configRow.value);
if (cfg.ANTHROPIC_AUTH_TOKEN) apiKey = cfg.ANTHROPIC_AUTH_TOKEN;
if (cfg.ANTHROPIC_BASE_URL) baseURL = cfg.ANTHROPIC_BASE_URL;
if (cfg.ANTHROPIC_MODEL) model = cfg.ANTHROPIC_MODEL;
if (cfg.PROTOCOL === 'openai') protocol = 'openai';
} catch {}
}
return new Anthropic({
apiKey,
baseURL,
});
return { protocol, apiKey, baseURL, model };
}
function getModel(): string {
const configRow = getDb().prepare('SELECT value FROM configs WHERE key = ?').get('api_keys') as { value: string } | undefined;
if (configRow) {
try {
const cfg = JSON.parse(configRow.value);
if (cfg.ANTHROPIC_MODEL) return cfg.ANTHROPIC_MODEL;
} catch {}
}
return process.env.ANTHROPIC_MODEL || 'claude-sonnet-4-6';
function getAnthropicClient(): Anthropic {
const { apiKey, baseURL } = getApiConfig();
return new Anthropic({ apiKey, baseURL });
}
function getOpenAIClient(): OpenAI {
const { apiKey, baseURL } = getApiConfig();
return new OpenAI({ apiKey, baseURL: baseURL || 'https://api.openai.com/v1' });
}
export class VideoAgent {
@@ -46,6 +57,22 @@ export class VideoAgent {
this.tools = tools;
}
getProtocol(): Protocol {
return getApiConfig().protocol;
}
getModel(): string {
return getApiConfig().model;
}
getAnthropicClient(): Anthropic {
return getAnthropicClient();
}
getOpenAIClient(): OpenAI {
return getOpenAIClient();
}
getAnthropicTools(): Anthropic.Tool[] {
return this.tools.map((t) => ({
name: t.name,
@@ -54,6 +81,17 @@ export class VideoAgent {
}));
}
getOpenAITools(): OpenAI.ChatCompletionTool[] {
return this.tools.map((t) => ({
type: 'function' as const,
function: {
name: t.name,
description: t.description,
parameters: t.input_schema,
},
}));
}
async executeTool(name: string, params: Record<string, unknown>): Promise<string> {
const tool = this.tools.find((t) => t.name === name);
if (!tool) throw new Error(`Unknown tool: ${name}`);
@@ -61,7 +99,6 @@ export class VideoAgent {
}
getSystemPrompt(): string {
// Dynamically list accounts
const accountsDir = path.join(PROJECT_ROOT, 'accounts');
let accountList = '暂无账号';
if (fs.existsSync(accountsDir)) {
@@ -107,14 +144,6 @@ ${accountList}
- 如果用户只是闲聊,就闲聊。如果用户想做视频,引导完成流程
- 不要编造账号或文件路径,使用工具获取真实信息`;
}
getClient(): Anthropic {
return getAnthropicClient();
}
getModel(): string {
return getModel();
}
}
export const videoAgent = new VideoAgent();