返回 AiToEarn
config.ts
1 import { aitoearnAuthConfigSchema } from '@yikart/aitoearn-auth'
2 import { assetsConfigSchema } from '@yikart/assets'
3 import { baseConfig, createZodDto, i18nObjectSchema, selectConfig } from '@yikart/common'
4 import { AiLogChannel, mongodbConfigSchema } from '@yikart/mongodb'
5 import { redisConfigSchema } from '@yikart/redis'
6 import { redlockConfigSchema } from '@yikart/redlock'
7 import z from 'zod'
8 import { dashscopeConfigSchema } from './core/ai/libs/dashscope'
9 import { geminiConfigSchema } from './core/ai/libs/gemini'
10 import { grokConfigSchema } from './core/ai/libs/grok'
11 import { openaiConfigSchema } from './core/ai/libs/openai'
12 import { relayConfigSchema } from './core/ai/libs/relay/relay.config'
13 import { volcengineConfigSchema } from './core/ai/libs/volcengine'
14
15 const videoModelInputConstraintSchema = z.object({
16 maxCount: z.number().int().min(0).optional(),
17 formats: z.array(z.string()).optional(),
18 minDuration: z.number().positive().optional(),
19 maxDuration: z.number().positive().optional(),
20 maxTotalDuration: z.number().positive().optional(),
21 maxSizeMb: z.number().positive().optional(),
22 minAspectRatio: z.number().positive().optional(),
23 maxAspectRatio: z.number().positive().optional(),
24 minWidth: z.number().int().positive().optional(),
25 maxWidth: z.number().int().positive().optional(),
26 minPixels: z.number().int().positive().optional(),
27 maxPixels: z.number().int().positive().optional(),
28 minFps: z.number().positive().optional(),
29 maxFps: z.number().positive().optional(),
30 })
31
32 const videoModelInputConstraintsSchema = z.object({
33 images: videoModelInputConstraintSchema.optional(),
34 videos: videoModelInputConstraintSchema.optional(),
35 audios: videoModelInputConstraintSchema.optional(),
36 }).optional()
37
38 const videoModelRuntimeFallbackSchema = z.object({
39 channel: z.enum(AiLogChannel),
40 model: z.string(),
41 }).strict()
42
43 const videoModelRuntimeModelSchema = z.object({
44 model: z.string(),
45 mode: z.string().optional(),
46 resolution: z.string().optional(),
47 fallback: videoModelRuntimeFallbackSchema.optional(),
48 }).strict()
49
50 export const aiModelsConfigSchema = z.object({
51 chat: z.array(z.object({
52 name: z.string(),
53 description: z.string(),
54 summary: z.string().optional(),
55 logo: z.string().optional(),
56 tags: z.array(i18nObjectSchema).default([]),
57 mainTag: z.string().optional(),
58 channel: z.enum(AiLogChannel),
59 scenes: z.string().array().optional(),
60 inputModalities: z.array(z.enum(['text', 'image', 'video', 'audio'])),
61 outputModalities: z.array(z.enum(['text', 'image', 'video', 'audio'])),
62 })),
63 image: z.object({
64 generation: z.array(z.object({
65 name: z.string(),
66 description: z.string(),
67 summary: z.string().optional(),
68 logo: z.string().optional(),
69 tags: z.array(i18nObjectSchema).default([]),
70 mainTag: z.string().optional(),
71 runtimeModel: z.string().optional(),
72 retry: z.number().int().min(0).optional(),
73 sizes: z.array(z.string()),
74 qualities: z.array(z.string()),
75 styles: z.array(z.string()),
76 })),
77 edit: z.array(z.object({
78 name: z.string(),
79 description: z.string(),
80 summary: z.string().optional(),
81 logo: z.string().optional(),
82 tags: z.array(i18nObjectSchema).default([]),
83 mainTag: z.string().optional(),
84 runtimeModel: z.string().optional(),
85 retry: z.number().int().min(0).optional(),
86 sizes: z.array(z.string()),
87 maxInputImages: z.number(),
88 })),
89 }),
90 video: z.object({
91 generation: z.array(z.object({
92 name: z.string(),
93 description: z.string(),
94 summary: z.string().optional(),
95 logo: z.string().optional(),
96 tags: z.array(i18nObjectSchema).default([]),
97 mainTag: z.string().optional(),
98 channel: z.enum(AiLogChannel),
99 modes: z.array(z.enum(['text2video', 'image2video', 'flf2video', 'lf2video', 'multi-image2video', 'multi-ref', 'video2video'])),
100 resolutions: z.array(z.string()),
101 durations: z.array(z.number()),
102 maxInputImages: z.number().int().min(0),
103 inputConstraints: videoModelInputConstraintsSchema,
104 aspectRatios: z.array(z.string()),
105 runtimeModels: z.array(videoModelRuntimeModelSchema).optional(),
106 defaults: z.object({
107 resolution: z.string().optional(),
108 aspectRatio: z.string().optional(),
109 duration: z.number().optional(),
110 }),
111 queuePriority: z.number().int().min(1).max(2097152).optional(),
112 retry: z.number().int().min(0).optional(),
113 })),
114 }),
115 })
116
117 export const aiConfigSchema = z.object({
118 models: aiModelsConfigSchema,
119 openai: openaiConfigSchema,
120 volcengine: z.object({
121 ...volcengineConfigSchema.shape,
122 callbackUrl: z.string().optional(),
123 }),
124 grok: grokConfigSchema,
125 dashscope: dashscopeConfigSchema,
126 gemini: geminiConfigSchema,
127 relay: relayConfigSchema.optional(),
128 anthropic: z.object({
129 baseUrl: z.string(),
130 apiKey: z.string(),
131 }),
132 draftGeneration: z.object({
133 planner: z.object({
134 defaultModel: z.string(),
135 }),
136 queue: z.object({
137 lowPriorityMinPriority: z.number().int().min(1).max(2097152),
138 lowPriorityConcurrency: z.number().int().min(1),
139 }).default({
140 lowPriorityMinPriority: 1000,
141 lowPriorityConcurrency: 2,
142 }),
143 imageModels: z.array(z.object({
144 model: z.string().describe('对外模型名'),
145 displayName: z.string().describe('展示名称'),
146 runtimeModel: z.string().optional().describe('实际调用的上游模型名'),
147 queuePriority: z.number().int().min(1).max(2097152).optional().describe('BullMQ priority,数值越小优先级越高'),
148 tags: z.array(i18nObjectSchema).default([]),
149 supportedAspectRatios: z.array(z.string()).describe('支持的图片宽高比列表'),
150 maxInputImages: z.number().int().min(1).describe('最多可输入的参考图片数量'),
151 pricing: z.array(z.object({
152 resolution: z.string().describe('分辨率,如 1K, 2K, 4K'),
153 })),
154 })),
155 }),
156 })
157
158 const defaultAgentModels = [
159 'claude-opus-4-6',
160 'claude-haiku-4-5-20251001-thinking',
161 'claude-opus-4-5-20251101-thinking',
162 'claude-opus-4-5-20251101',
163 'claude-sonnet-4-5-20250929-thinking',
164 'claude-haiku-4-5-20251001',
165 'claude-opus-4-1-20250805',
166 'claude-opus-4-1-20250805-thinking',
167 'claude-sonnet-4-5-20250929',
168 'claude-opus-4-6',
169 'claude-opus-4-6-thinking',
170 ]
171
172 const agentModelNameSchema = z.string().min(1).regex(/^[^,]+$/)
173
174 // Agent 配置
175 export const agentConfigSchema = z.object({
176 baseUrl: z.string(),
177 apiKey: z.string(),
178 models: z.array(agentModelNameSchema).min(1).default(defaultAgentModels).describe('Agent 可用模型列表'),
179 defaultModel: agentModelNameSchema.default('claude-opus-4-6').describe('Agent 默认模型'),
180 backgroundModel: agentModelNameSchema.default('claude-haiku-4-5-20251001').describe('Agent 后台子任务模型'),
181 thinkModel: agentModelNameSchema.default('claude-opus-4-6').describe('Agent 思考任务模型'),
182 taskTimeoutMs: z.number().default(60 * 60 * 1000).describe('Agent 任务超时时间(毫秒),默认 60 分钟'),
183 }).superRefine((agent, ctx) => {
184 const configuredModels = new Set(agent.models)
185
186 for (const [field, model] of Object.entries({
187 defaultModel: agent.defaultModel,
188 backgroundModel: agent.backgroundModel,
189 thinkModel: agent.thinkModel,
190 })) {
191 if (!configuredModels.has(model)) {
192 ctx.addIssue({
193 code: 'custom',
194 path: [field],
195 message: `${field} must be included in agent.models`,
196 })
197 }
198 }
199 })
200
201 const serverClientConfigSchema = z.object({
202 baseUrl: z.string(),
203 })
204
205 export const appConfigSchema = z.object({
206 ...baseConfig.shape,
207 auth: aitoearnAuthConfigSchema,
208 redis: redisConfigSchema,
209 mongodb: mongodbConfigSchema,
210 redlock: redlockConfigSchema,
211 serverClient: serverClientConfigSchema,
212 assets: assetsConfigSchema,
213 ai: aiConfigSchema,
214 agent: agentConfigSchema,
215 })
216
217 export class AppConfig extends createZodDto(appConfigSchema) { }
218
219 export const config = selectConfig(AppConfig)
220
220 lines TYPESCRIPT