@bolloon/bolloon-agent 0.1.29 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +13 -1
- package/dist/llm/config-store.js +64 -0
- package/dist/llm/pi-ai.js +28 -0
- package/dist/llm/video-config-store.js +171 -0
- package/dist/web/api-config.html +296 -53
- package/dist/web/client.js +43 -15
- package/dist/web/server.js +81 -4
- package/dist/web/style.css +83 -0
- package/package.json +1 -1
- package/src/index.ts +13 -1
- package/src/llm/audio-config-store.ts +241 -0
- package/src/llm/config-store.ts +61 -1
- package/src/llm/pi-ai.ts +25 -1
- package/src/llm/video-config-store.ts +251 -0
- package/src/web/api-config.html +296 -53
- package/src/web/client.js +43 -15
- package/src/web/server.ts +151 -4
- package/src/web/style.css +83 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video Generation API Configuration Store
|
|
3
|
+
*
|
|
4
|
+
* 视频生成模型配置(与 LLM 完全独立)。当前内置 Seedance(火山引擎 ARK)。
|
|
5
|
+
* Seedance 任务流: POST /contents/generations/tasks → 轮询 GET /contents/generations/tasks/{id}
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
|
|
11
|
+
export type VideoProvider = 'seedance' | 'minimax-video';
|
|
12
|
+
|
|
13
|
+
export interface VideoProviderConfig {
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
apiKey: string;
|
|
16
|
+
baseUrl: string;
|
|
17
|
+
model: string;
|
|
18
|
+
/** 默认分辨率,如 720p / 1080p */
|
|
19
|
+
resolution?: string;
|
|
20
|
+
/** 默认时长(秒) */
|
|
21
|
+
duration?: number;
|
|
22
|
+
/** 默认宽高比,如 16:9 / 9:16 / 1:1 */
|
|
23
|
+
ratio?: string;
|
|
24
|
+
/** 是否需要 API Key(火山方舟需要) */
|
|
25
|
+
requiresApiKey?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface VideoConfig {
|
|
29
|
+
activeProvider: VideoProvider;
|
|
30
|
+
providers: Record<VideoProvider, VideoProviderConfig>;
|
|
31
|
+
updatedAt: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const CONFIG_DIR = path.join(process.env.HOME || '/tmp', '.bolloon');
|
|
35
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, 'video-config.json');
|
|
36
|
+
|
|
37
|
+
export const DEFAULT_VIDEO_PROVIDER_CONFIGS: Record<VideoProvider, VideoProviderConfig> = {
|
|
38
|
+
seedance: {
|
|
39
|
+
enabled: false,
|
|
40
|
+
apiKey: '',
|
|
41
|
+
baseUrl: 'https://ark.cn-beijing.volces.com/api/v3',
|
|
42
|
+
// 文生视频 lite 版(也支持图生视频,加 --resolution 等参数)
|
|
43
|
+
model: 'doubao-seedance-1-0-lite-t2v-250428',
|
|
44
|
+
resolution: '720p',
|
|
45
|
+
duration: 5,
|
|
46
|
+
ratio: '16:9',
|
|
47
|
+
requiresApiKey: true
|
|
48
|
+
},
|
|
49
|
+
'minimax-video': {
|
|
50
|
+
enabled: false,
|
|
51
|
+
apiKey: '',
|
|
52
|
+
baseUrl: 'https://api.minimaxi.com/v1',
|
|
53
|
+
model: 'MiniMax-video-01',
|
|
54
|
+
resolution: '720p',
|
|
55
|
+
duration: 6,
|
|
56
|
+
ratio: '16:9',
|
|
57
|
+
requiresApiKey: true
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const VIDEO_PROVIDER_INFO: Record<VideoProvider, { name: string; description: string; requiresApiKey: boolean; docs?: string }> = {
|
|
62
|
+
seedance: {
|
|
63
|
+
name: 'Seedance (火山方舟)',
|
|
64
|
+
description: '字节跳动文生视频 / 图生视频模型',
|
|
65
|
+
requiresApiKey: true,
|
|
66
|
+
docs: 'https://www.volcengine.com/docs/82379'
|
|
67
|
+
},
|
|
68
|
+
'minimax-video': {
|
|
69
|
+
name: 'MiniMax Video',
|
|
70
|
+
description: 'MiniMax 文生视频 (Video-01)',
|
|
71
|
+
requiresApiKey: true,
|
|
72
|
+
docs: 'https://platform.minimaxi.com/document/Video%20Generation'
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
function getDefaultConfig(): VideoConfig {
|
|
77
|
+
const envConfigs: Partial<Record<VideoProvider, VideoProviderConfig>> = {};
|
|
78
|
+
|
|
79
|
+
if (process.env.SEEDANCE_API_KEY || process.env.ARK_API_KEY) {
|
|
80
|
+
envConfigs.seedance = {
|
|
81
|
+
...DEFAULT_VIDEO_PROVIDER_CONFIGS.seedance,
|
|
82
|
+
enabled: true,
|
|
83
|
+
apiKey: process.env.SEEDANCE_API_KEY || process.env.ARK_API_KEY || ''
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const sharedKey = process.env.MINIMAX_API_KEY || '';
|
|
88
|
+
if (sharedKey) {
|
|
89
|
+
envConfigs['minimax-video'] = {
|
|
90
|
+
...DEFAULT_VIDEO_PROVIDER_CONFIGS['minimax-video'],
|
|
91
|
+
enabled: true,
|
|
92
|
+
apiKey: sharedKey
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const activeProvider: VideoProvider = 'seedance';
|
|
97
|
+
|
|
98
|
+
const providers = { ...DEFAULT_VIDEO_PROVIDER_CONFIGS };
|
|
99
|
+
for (const [provider, config] of Object.entries(envConfigs)) {
|
|
100
|
+
if (config) {
|
|
101
|
+
providers[provider as VideoProvider] = config;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
activeProvider,
|
|
107
|
+
providers,
|
|
108
|
+
updatedAt: new Date().toISOString()
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
class VideoConfigStore {
|
|
113
|
+
private config: VideoConfig | null = null;
|
|
114
|
+
private initialized: boolean = false;
|
|
115
|
+
|
|
116
|
+
async initialize(): Promise<void> {
|
|
117
|
+
if (this.initialized) return;
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
await fs.mkdir(CONFIG_DIR, { recursive: true });
|
|
121
|
+
const data = await fs.readFile(CONFIG_PATH, 'utf-8');
|
|
122
|
+
const loadedConfig = JSON.parse(data);
|
|
123
|
+
|
|
124
|
+
// 确保加载的配置包含所有默认供应商
|
|
125
|
+
const defaultProviders = Object.keys(DEFAULT_VIDEO_PROVIDER_CONFIGS) as VideoProvider[];
|
|
126
|
+
for (const provider of defaultProviders) {
|
|
127
|
+
if (!loadedConfig.providers[provider]) {
|
|
128
|
+
loadedConfig.providers[provider] = { ...DEFAULT_VIDEO_PROVIDER_CONFIGS[provider] };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const activeProvider = loadedConfig.activeProvider as VideoProvider;
|
|
133
|
+
if (!activeProvider || !DEFAULT_VIDEO_PROVIDER_CONFIGS[activeProvider]) {
|
|
134
|
+
loadedConfig.activeProvider = 'seedance';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.config = loadedConfig;
|
|
138
|
+
} catch {
|
|
139
|
+
this.config = getDefaultConfig();
|
|
140
|
+
await this.save();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.initialized = true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private async save(): Promise<void> {
|
|
147
|
+
if (!this.config) return;
|
|
148
|
+
this.config.updatedAt = new Date().toISOString();
|
|
149
|
+
await fs.writeFile(CONFIG_PATH, JSON.stringify(this.config, null, 2));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async getConfig(): Promise<VideoConfig> {
|
|
153
|
+
await this.initialize();
|
|
154
|
+
return { ...this.config! };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async getProvider(provider: VideoProvider): Promise<VideoProviderConfig | null> {
|
|
158
|
+
await this.initialize();
|
|
159
|
+
return this.config?.providers[provider] || null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async getActiveProvider(): Promise<VideoProvider> {
|
|
163
|
+
await this.initialize();
|
|
164
|
+
return this.config?.activeProvider || 'seedance';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async getActiveProviderConfig(): Promise<VideoProviderConfig | null> {
|
|
168
|
+
await this.initialize();
|
|
169
|
+
const provider = this.config?.activeProvider;
|
|
170
|
+
if (!provider) return null;
|
|
171
|
+
return this.config?.providers[provider] || null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async setActiveProvider(provider: VideoProvider): Promise<void> {
|
|
175
|
+
await this.initialize();
|
|
176
|
+
|
|
177
|
+
if (!this.config?.providers[provider]) {
|
|
178
|
+
throw new Error(`Unknown video provider: ${provider}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.config.activeProvider = provider;
|
|
182
|
+
await this.save();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async updateProvider(provider: VideoProvider, updates: Partial<VideoProviderConfig>): Promise<void> {
|
|
186
|
+
await this.initialize();
|
|
187
|
+
|
|
188
|
+
if (!this.config?.providers[provider]) {
|
|
189
|
+
throw new Error(`Unknown video provider: ${provider}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
this.config.providers[provider] = {
|
|
193
|
+
...this.config.providers[provider],
|
|
194
|
+
...updates
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
await this.save();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 测试连接:只校验 API key 是否能被 ARK 接受(创建任务失败不算连接失败,
|
|
202
|
+
* 返回 401/403 才算失败)。
|
|
203
|
+
*/
|
|
204
|
+
async testProvider(provider: VideoProvider): Promise<{ success: boolean; error?: string; latency?: number }> {
|
|
205
|
+
await this.initialize();
|
|
206
|
+
|
|
207
|
+
const config = this.config?.providers[provider];
|
|
208
|
+
if (!config) {
|
|
209
|
+
return { success: false, error: 'Provider not configured' };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (!config.enabled) {
|
|
213
|
+
return { success: false, error: 'Provider is not enabled' };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (config.requiresApiKey && !config.apiKey) {
|
|
217
|
+
return { success: false, error: 'API key is required' };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const startTime = Date.now();
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
// 列出可用模型(轻量级健康检查)。ARK 端点:GET /models
|
|
224
|
+
const response = await fetch(`${config.baseUrl.replace(/\/$/, '')}/models`, {
|
|
225
|
+
method: 'GET',
|
|
226
|
+
headers: { 'Authorization': `Bearer ${config.apiKey}` }
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const latency = Date.now() - startTime;
|
|
230
|
+
|
|
231
|
+
if (response.ok) {
|
|
232
|
+
return { success: true, latency };
|
|
233
|
+
} else {
|
|
234
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
235
|
+
return {
|
|
236
|
+
success: false,
|
|
237
|
+
error: `HTTP ${response.status}: ${errorText.substring(0, 200)}`,
|
|
238
|
+
latency
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
} catch (error: any) {
|
|
242
|
+
return { success: false, error: error.message || 'Connection failed', latency: Date.now() - startTime };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
getAllProviderInfo() {
|
|
247
|
+
return VIDEO_PROVIDER_INFO;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export const videoConfigStore = new VideoConfigStore();
|