koishi-plugin-aka-ai-generator 0.6.13 → 0.7.0

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/lib/index.d.ts CHANGED
@@ -13,6 +13,12 @@ export interface StyleConfig {
13
13
  export interface StyleGroupConfig {
14
14
  prompts: StyleConfig[];
15
15
  }
16
+ export interface VideoStyleConfig {
17
+ commandName: string;
18
+ prompt: string;
19
+ duration?: number;
20
+ aspectRatio?: string;
21
+ }
16
22
  export interface Config {
17
23
  provider: ImageProvider;
18
24
  yunwuApiKey: string;
@@ -35,6 +41,11 @@ export interface Config {
35
41
  logLevel: 'info' | 'debug';
36
42
  securityBlockWindow: number;
37
43
  securityBlockWarningThreshold: number;
44
+ enableVideoGeneration: boolean;
45
+ videoModelId: string;
46
+ videoMaxWaitTime: number;
47
+ videoCreditsMultiplier: number;
48
+ videoStyles: VideoStyleConfig[];
38
49
  }
39
50
  export declare const Config: Schema<Config>;
40
51
  export declare function apply(ctx: Context, config: Config): void;
package/lib/index.js CHANGED
@@ -875,6 +875,147 @@ var GeminiProvider = class {
875
875
  }
876
876
  };
877
877
 
878
+ // src/providers/yunwu-video.ts
879
+ var YunwuVideoProvider = class {
880
+ static {
881
+ __name(this, "YunwuVideoProvider");
882
+ }
883
+ config;
884
+ constructor(config) {
885
+ this.config = config;
886
+ }
887
+ /**
888
+ * 创建视频生成任务
889
+ * API: POST /v1/videos/generations
890
+ */
891
+ async createVideoTask(prompt, imageUrl, options) {
892
+ const { logger, ctx } = this.config;
893
+ try {
894
+ logger?.info("下载输入图片", { imageUrl });
895
+ const { data: imageBase64, mimeType } = await downloadImageAsBase64(
896
+ ctx,
897
+ imageUrl,
898
+ this.config.apiTimeout,
899
+ logger
900
+ );
901
+ const requestBody = {
902
+ model: this.config.modelId,
903
+ prompt,
904
+ image: `data:${mimeType};base64,${imageBase64}`,
905
+ duration: options?.duration || 5,
906
+ aspect_ratio: options?.aspectRatio || "16:9"
907
+ };
908
+ if (options?.fps) {
909
+ requestBody.fps = options.fps;
910
+ }
911
+ logger?.info("提交视频生成任务", {
912
+ model: this.config.modelId,
913
+ promptLength: prompt.length,
914
+ duration: requestBody.duration,
915
+ aspectRatio: requestBody.aspect_ratio
916
+ });
917
+ const response = await ctx.http.post(
918
+ `${this.config.apiBase}/v1/videos/generations`,
919
+ requestBody,
920
+ {
921
+ headers: {
922
+ "Authorization": `Bearer ${this.config.apiKey}`,
923
+ "Content-Type": "application/json"
924
+ },
925
+ timeout: this.config.apiTimeout * 1e3
926
+ }
927
+ );
928
+ if (response.error) {
929
+ throw new Error(sanitizeString(response.error.message || "创建任务失败"));
930
+ }
931
+ const taskId = response.id || response.task_id || response.data?.id;
932
+ if (!taskId) {
933
+ logger?.error("未能获取任务ID", { response });
934
+ throw new Error("未能获取任务ID");
935
+ }
936
+ logger?.info("视频任务已创建", { taskId });
937
+ return taskId;
938
+ } catch (error) {
939
+ logger?.error("创建视频任务失败", { error: sanitizeError(error) });
940
+ throw new Error(`创建视频任务失败: ${sanitizeString(error.message)}`);
941
+ }
942
+ }
943
+ /**
944
+ * 查询任务状态
945
+ * API: GET /v1/videos/generations/{taskId}
946
+ */
947
+ async queryTaskStatus(taskId) {
948
+ const { logger, ctx } = this.config;
949
+ try {
950
+ const response = await ctx.http.get(
951
+ `${this.config.apiBase}/v1/videos/generations/${taskId}`,
952
+ {
953
+ headers: {
954
+ "Authorization": `Bearer ${this.config.apiKey}`
955
+ },
956
+ timeout: this.config.apiTimeout * 1e3
957
+ }
958
+ );
959
+ const status = response.status || response.data?.status || "pending";
960
+ const videoUrl = response.video_url || response.url || response.data?.video_url || response.data?.url;
961
+ return {
962
+ status,
963
+ taskId,
964
+ videoUrl,
965
+ error: response.error || response.data?.error,
966
+ progress: response.progress || response.data?.progress
967
+ };
968
+ } catch (error) {
969
+ logger?.error("查询任务状态失败", { taskId, error: sanitizeError(error) });
970
+ throw new Error(`查询任务失败: ${sanitizeString(error.message)}`);
971
+ }
972
+ }
973
+ /**
974
+ * 轮询等待任务完成
975
+ */
976
+ async pollTaskCompletion(taskId, maxWaitTime = 300, pollInterval = 3, onProgress) {
977
+ const { logger } = this.config;
978
+ const startTime = Date.now();
979
+ while (true) {
980
+ const elapsed = (Date.now() - startTime) / 1e3;
981
+ if (elapsed > maxWaitTime) {
982
+ logger?.warn("视频生成超时", { taskId, elapsed, maxWaitTime });
983
+ throw new Error(`视频生成超时(已等待${Math.floor(elapsed)}秒),任务ID: ${taskId}
984
+ 请使用"查询视频 ${taskId}"命令稍后查询结果`);
985
+ }
986
+ const status = await this.queryTaskStatus(taskId);
987
+ logger?.debug("任务状态", { taskId, status: status.status, elapsed: Math.floor(elapsed) });
988
+ if (onProgress) {
989
+ await onProgress(status);
990
+ }
991
+ if (status.status === "completed" && status.videoUrl) {
992
+ logger?.info("视频生成完成", { taskId, elapsed: Math.floor(elapsed) });
993
+ return status.videoUrl;
994
+ }
995
+ if (status.status === "failed") {
996
+ throw new Error(status.error || "视频生成失败");
997
+ }
998
+ await new Promise((resolve) => setTimeout(resolve, pollInterval * 1e3));
999
+ }
1000
+ }
1001
+ /**
1002
+ * 生成视频(主入口)
1003
+ */
1004
+ async generateVideo(prompt, imageUrl, options, maxWaitTime = 300) {
1005
+ const { logger } = this.config;
1006
+ try {
1007
+ logger?.info("开始生成视频", { prompt, imageUrl, options });
1008
+ const taskId = await this.createVideoTask(prompt, imageUrl, options);
1009
+ const videoUrl = await this.pollTaskCompletion(taskId, maxWaitTime);
1010
+ logger?.info("视频生成完成", { taskId, videoUrl });
1011
+ return videoUrl;
1012
+ } catch (error) {
1013
+ logger?.error("视频生成失败", { error: sanitizeError(error) });
1014
+ throw error;
1015
+ }
1016
+ }
1017
+ };
1018
+
878
1019
  // src/providers/index.ts
879
1020
  function createImageProvider(config) {
880
1021
  switch (config.provider) {
@@ -1056,8 +1197,12 @@ var UserManager = class {
1056
1197
  }
1057
1198
  // 批量更新用户数据 (用于充值)
1058
1199
  async updateUsersBatch(updates) {
1200
+ await this.loadUsersData();
1059
1201
  await this.dataLock.acquire(async () => {
1060
- await this.loadUsersData();
1202
+ if (!this.usersCache) {
1203
+ this.logger.error("updateUsersBatch: usersCache 为空,这不应该发生");
1204
+ this.usersCache = {};
1205
+ }
1061
1206
  updates(this.usersCache);
1062
1207
  await this.saveUsersDataInternal();
1063
1208
  });
@@ -1158,20 +1303,26 @@ var UserManager = class {
1158
1303
  return { ...rateLimitCheck };
1159
1304
  }
1160
1305
  this.updateRateLimit(userId);
1306
+ await this.loadUsersData();
1307
+ const userData = await this.getUserData(userId, userName);
1161
1308
  return await this.dataLock.acquire(async () => {
1162
- const userData = await this.getUserData(userId, userName);
1309
+ if (!this.usersCache) {
1310
+ this.logger.error("checkAndReserveQuota: usersCache 为空,这不应该发生");
1311
+ this.usersCache = {};
1312
+ }
1313
+ const cachedUserData = this.usersCache[userId] || userData;
1163
1314
  const today = (/* @__PURE__ */ new Date()).toDateString();
1164
- const lastReset = new Date(userData.lastDailyReset || userData.createdAt).toDateString();
1165
- let dailyCount = userData.dailyUsageCount;
1315
+ const lastReset = new Date(cachedUserData.lastDailyReset || cachedUserData.createdAt).toDateString();
1316
+ let dailyCount = cachedUserData.dailyUsageCount;
1166
1317
  if (today !== lastReset) {
1167
1318
  dailyCount = 0;
1168
1319
  }
1169
1320
  const remainingToday = Math.max(0, config.dailyFreeLimit - dailyCount);
1170
- const totalAvailable = remainingToday + userData.remainingPurchasedCount;
1321
+ const totalAvailable = remainingToday + cachedUserData.remainingPurchasedCount;
1171
1322
  if (totalAvailable < numImages) {
1172
1323
  return {
1173
1324
  allowed: false,
1174
- message: `生成 ${numImages} 张图片需要 ${numImages} 次可用次数,但您的可用次数不足(今日免费剩余:${remainingToday}次,充值剩余:${userData.remainingPurchasedCount}次,共${totalAvailable}次)`
1325
+ message: `生成 ${numImages} 张图片需要 ${numImages} 次可用次数,但您的可用次数不足(今日免费剩余:${remainingToday}次,充值剩余:${cachedUserData.remainingPurchasedCount}次,共${totalAvailable}次)`
1175
1326
  };
1176
1327
  }
1177
1328
  const reservationId = `${userId}_${Date.now()}_${Math.random()}`;
@@ -1180,8 +1331,12 @@ var UserManager = class {
1180
1331
  }
1181
1332
  // 扣减额度并记录使用
1182
1333
  async consumeQuota(userId, userName, commandName, numImages, config) {
1334
+ await this.loadUsersData();
1183
1335
  return await this.dataLock.acquire(async () => {
1184
- await this.loadUsersData();
1336
+ if (!this.usersCache) {
1337
+ this.logger.error("consumeQuota: usersCache 为空,这不应该发生");
1338
+ this.usersCache = {};
1339
+ }
1185
1340
  let userData = this.usersCache[userId];
1186
1341
  const now = (/* @__PURE__ */ new Date()).toISOString();
1187
1342
  const today = (/* @__PURE__ */ new Date()).toDateString();
@@ -1430,6 +1585,32 @@ var Config = import_koishi2.Schema.intersect([
1430
1585
  styleGroups: import_koishi2.Schema.dict(import_koishi2.Schema.object({
1431
1586
  prompts: import_koishi2.Schema.array(StyleItemSchema).role("table").default([]).description("属于该类型的 prompt 列表")
1432
1587
  })).role("table").default({}).description("按类型管理的 prompt 组,键名即为分组名称")
1588
+ }),
1589
+ // 视频生成配置
1590
+ import_koishi2.Schema.object({
1591
+ enableVideoGeneration: import_koishi2.Schema.boolean().default(false).description("启用图生成视频功能(消耗较大,需谨慎开启)"),
1592
+ videoModelId: import_koishi2.Schema.string().default("sora-2").description("视频生成模型ID (sora-2 或 sora-2-pro)"),
1593
+ videoMaxWaitTime: import_koishi2.Schema.number().default(300).min(60).max(600).description("视频生成最大等待时间(秒),超时后可异步查询"),
1594
+ videoCreditsMultiplier: import_koishi2.Schema.number().default(5).min(1).max(20).description("视频生成积分倍数(相对于图片生成,默认5倍)"),
1595
+ videoStyles: import_koishi2.Schema.array(import_koishi2.Schema.object({
1596
+ commandName: import_koishi2.Schema.string().required().description("命令名称").role("table-cell", { width: 100 }),
1597
+ prompt: import_koishi2.Schema.string().role("textarea", { rows: 2 }).required().description("视频描述 prompt"),
1598
+ duration: import_koishi2.Schema.number().min(3).max(10).description("视频时长(秒)"),
1599
+ aspectRatio: import_koishi2.Schema.string().description("宽高比(如 16:9)")
1600
+ })).role("table").default([
1601
+ {
1602
+ commandName: "慢动作视频",
1603
+ prompt: "缓慢流畅的运镜,展现细节和质感",
1604
+ duration: 8,
1605
+ aspectRatio: "16:9"
1606
+ },
1607
+ {
1608
+ commandName: "快节奏视频",
1609
+ prompt: "快速动态的场景变化,充满活力",
1610
+ duration: 5,
1611
+ aspectRatio: "16:9"
1612
+ }
1613
+ ]).description("视频风格预设")
1433
1614
  })
1434
1615
  ]);
1435
1616
  function apply(ctx, config) {
@@ -1453,6 +1634,19 @@ function apply(ctx, config) {
1453
1634
  }
1454
1635
  __name(getProviderInstance, "getProviderInstance");
1455
1636
  const modelMappingIndex = buildModelMappingIndex(config.modelMappings);
1637
+ let videoProvider = null;
1638
+ if (config.enableVideoGeneration) {
1639
+ videoProvider = new YunwuVideoProvider({
1640
+ apiKey: config.yunwuApiKey,
1641
+ modelId: config.videoModelId,
1642
+ apiBase: "https://yunwu.ai",
1643
+ apiTimeout: config.apiTimeout,
1644
+ logLevel: config.logLevel,
1645
+ logger,
1646
+ ctx
1647
+ });
1648
+ logger.info(`视频生成功能已启用 (模型: ${config.videoModelId})`);
1649
+ }
1456
1650
  const styleDefinitions = collectStyleDefinitions();
1457
1651
  function collectStyleDefinitions() {
1458
1652
  const unique = /* @__PURE__ */ new Map();
@@ -1942,6 +2136,181 @@ ${infoParts.join("\n")}`;
1942
2136
  }
1943
2137
  }
1944
2138
  }
2139
+ if (config.enableVideoGeneration && videoProvider) {
2140
+ ctx.command("图生视频 [img:text]", "根据图片和描述生成视频").option("duration", "-d <duration:number> 视频时长(3-10秒)").option("ratio", "-r <ratio:string> 宽高比(16:9, 9:16, 1:1)").action(async ({ session, options }, img) => {
2141
+ if (!session?.userId) return "会话无效";
2142
+ const userId = session.userId;
2143
+ const userName = session.username || userId || "未知用户";
2144
+ const videoCredits = config.videoCreditsMultiplier;
2145
+ const limitCheck = await userManager.checkAndReserveQuota(
2146
+ userId,
2147
+ userName,
2148
+ videoCredits,
2149
+ config
2150
+ );
2151
+ if (!limitCheck.allowed) {
2152
+ return limitCheck.message;
2153
+ }
2154
+ if (!userManager.startTask(userId)) {
2155
+ return "您有一个任务正在进行中,请等待完成";
2156
+ }
2157
+ try {
2158
+ const inputResult = await getInputData(session, img, "single");
2159
+ if ("error" in inputResult) {
2160
+ return inputResult.error;
2161
+ }
2162
+ const { images: imageUrls, text: extraText } = inputResult;
2163
+ if (imageUrls.length === 0) {
2164
+ return "未检测到输入图片,请发送一张图片";
2165
+ }
2166
+ let prompt = extraText || "";
2167
+ if (!prompt) {
2168
+ await session.send("请输入视频描述(描述视频中的动作和场景变化)\n提示:描述越详细,生成效果越好");
2169
+ const promptMsg = await session.prompt(3e4);
2170
+ if (!promptMsg) {
2171
+ return "等待超时";
2172
+ }
2173
+ const elements = import_koishi2.h.parse(promptMsg);
2174
+ const text = import_koishi2.h.select(elements, "text").map((e) => e.attrs.content).join(" ").trim();
2175
+ if (!text) {
2176
+ return "未检测到描述";
2177
+ }
2178
+ prompt = text;
2179
+ }
2180
+ const duration = options?.duration || 5;
2181
+ if (duration < 3 || duration > 10) {
2182
+ return "视频时长必须在 3-10 秒之间";
2183
+ }
2184
+ const ratio = options?.ratio || "16:9";
2185
+ const validRatios = ["16:9", "9:16", "1:1"];
2186
+ if (!validRatios.includes(ratio)) {
2187
+ return `宽高比必须是以下之一: ${validRatios.join(", ")}`;
2188
+ }
2189
+ await session.send(
2190
+ `🎬 开始生成视频...
2191
+ 📝 描述:${prompt}
2192
+ ⏱️ 时长:${duration}秒
2193
+ 📐 宽高比:${ratio}
2194
+ ⚠️ 预计需要 1-3 分钟,请耐心等待
2195
+ 💡 提示:生成过程中可继续使用其他功能`
2196
+ );
2197
+ const startTime = Date.now();
2198
+ const videoUrl = await videoProvider.generateVideo(
2199
+ prompt,
2200
+ imageUrls[0],
2201
+ {
2202
+ duration,
2203
+ aspectRatio: ratio
2204
+ },
2205
+ config.videoMaxWaitTime
2206
+ );
2207
+ await recordUserUsage(session, "图生视频", videoCredits, false);
2208
+ await session.send(import_koishi2.h.video(videoUrl));
2209
+ const totalTime = Math.floor((Date.now() - startTime) / 1e3);
2210
+ await session.send(`✅ 视频生成完成!(耗时 ${totalTime} 秒)`);
2211
+ } catch (error) {
2212
+ logger.error("视频生成失败", { userId, error: sanitizeError(error) });
2213
+ const errorMsg = error.message || "";
2214
+ if (errorMsg.includes("任务ID:")) {
2215
+ return errorMsg;
2216
+ }
2217
+ return `视频生成失败:${sanitizeString(errorMsg)}`;
2218
+ } finally {
2219
+ userManager.endTask(userId);
2220
+ }
2221
+ });
2222
+ }
2223
+ if (config.enableVideoGeneration && videoProvider) {
2224
+ ctx.command("查询视频 <taskId:string>", "根据任务ID查询视频生成状态").action(async ({ session }, taskId) => {
2225
+ if (!session?.userId) return "会话无效";
2226
+ if (!taskId || !taskId.trim()) {
2227
+ return "请提供任务ID,格式:查询视频 <任务ID>";
2228
+ }
2229
+ try {
2230
+ await session.send("🔍 正在查询视频生成状态...");
2231
+ const status = await videoProvider.queryTaskStatus(taskId.trim());
2232
+ if (status.status === "completed" && status.videoUrl) {
2233
+ await session.send(import_koishi2.h.video(status.videoUrl));
2234
+ return "✅ 视频已生成完成!";
2235
+ } else if (status.status === "processing" || status.status === "pending") {
2236
+ const progressText = status.progress ? `(进度:${status.progress}%)` : "";
2237
+ return `⏳ 视频正在生成中${progressText},请稍后再次查询
2238
+ 任务ID:${taskId}`;
2239
+ } else if (status.status === "failed") {
2240
+ return `❌ 视频生成失败:${status.error || "未知错误"}`;
2241
+ } else {
2242
+ return `❓ 未知状态:${status.status}`;
2243
+ }
2244
+ } catch (error) {
2245
+ logger.error("查询视频任务失败", { taskId, error: sanitizeError(error) });
2246
+ return `查询失败:${sanitizeString(error.message)}`;
2247
+ }
2248
+ });
2249
+ }
2250
+ if (config.enableVideoGeneration && videoProvider && config.videoStyles?.length > 0) {
2251
+ for (const style of config.videoStyles) {
2252
+ if (!style.commandName || !style.prompt) continue;
2253
+ ctx.command(`${style.commandName} [img:text]`, "视频风格转换").action(async ({ session }, img) => {
2254
+ if (!session?.userId) return "会话无效";
2255
+ const userId = session.userId;
2256
+ const userName = session.username || userId || "未知用户";
2257
+ const videoCredits = config.videoCreditsMultiplier;
2258
+ const limitCheck = await userManager.checkAndReserveQuota(
2259
+ userId,
2260
+ userName,
2261
+ videoCredits,
2262
+ config
2263
+ );
2264
+ if (!limitCheck.allowed) {
2265
+ return limitCheck.message;
2266
+ }
2267
+ if (!userManager.startTask(userId)) {
2268
+ return "您有一个任务正在进行中,请等待完成";
2269
+ }
2270
+ try {
2271
+ const inputResult = await getInputData(session, img, "single");
2272
+ if ("error" in inputResult) {
2273
+ return inputResult.error;
2274
+ }
2275
+ const { images: imageUrls, text: extraText } = inputResult;
2276
+ if (imageUrls.length === 0) {
2277
+ return "未检测到输入图片,请发送一张图片";
2278
+ }
2279
+ let finalPrompt = style.prompt;
2280
+ if (extraText) {
2281
+ finalPrompt += " - " + extraText;
2282
+ }
2283
+ await session.send(
2284
+ `🎬 开始生成视频(${style.commandName})...
2285
+ 📝 描述:${finalPrompt}
2286
+ ⏱️ 预计需要 1-3 分钟`
2287
+ );
2288
+ const videoUrl = await videoProvider.generateVideo(
2289
+ finalPrompt,
2290
+ imageUrls[0],
2291
+ {
2292
+ duration: style.duration || 5,
2293
+ aspectRatio: style.aspectRatio || "16:9"
2294
+ },
2295
+ config.videoMaxWaitTime
2296
+ );
2297
+ await recordUserUsage(session, style.commandName, videoCredits, false);
2298
+ await session.send(import_koishi2.h.video(videoUrl));
2299
+ await session.send(`✅ 视频生成完成!`);
2300
+ } catch (error) {
2301
+ logger.error("视频风格转换失败", { userId, style: style.commandName, error: sanitizeError(error) });
2302
+ const errorMsg = error.message || "";
2303
+ if (errorMsg.includes("任务ID:")) {
2304
+ return errorMsg;
2305
+ }
2306
+ return `视频生成失败:${sanitizeString(errorMsg)}`;
2307
+ } finally {
2308
+ userManager.endTask(userId);
2309
+ }
2310
+ });
2311
+ logger.info(`已注册视频风格命令: ${style.commandName}`);
2312
+ }
2313
+ }
1945
2314
  ctx.command(`${COMMANDS.TXT_TO_IMG} [prompt:text]`, "根据文字描述生成图像").option("num", "-n <num:number> 生成图片数量 (1-4)").action(async ({ session, options }, prompt) => {
1946
2315
  if (!session?.userId) return "会话无效";
1947
2316
  const numImages = options?.num || config.defaultNumImages;
@@ -2384,6 +2753,17 @@ Prompt: ${prompt}`);
2384
2753
  result += `• ${cmd.name} - ${cmd.description}
2385
2754
  `;
2386
2755
  });
2756
+ if (config.enableVideoGeneration) {
2757
+ result += "\n🎥 视频生成功能:\n";
2758
+ result += "• 图生视频 - 根据图片和描述生成视频\n";
2759
+ result += "• 查询视频 - 根据任务ID查询视频状态\n";
2760
+ if (config.videoStyles?.length > 0) {
2761
+ config.videoStyles.forEach((style) => {
2762
+ result += `• ${style.commandName} - 视频风格预设
2763
+ `;
2764
+ });
2765
+ }
2766
+ }
2387
2767
  if (userIsAdmin) {
2388
2768
  result += "\n🔧 管理员指令:\n";
2389
2769
  commandRegistry.adminCommands.forEach((cmd) => {
@@ -21,3 +21,5 @@ export declare function createImageProvider(config: ProviderFactoryConfig): Imag
21
21
  export { ImageProvider } from './types';
22
22
  export { GptGodProvider } from './gptgod';
23
23
  export { GeminiProvider } from './gemini';
24
+ export { VideoProvider, VideoTaskStatus, VideoGenerationOptions } from './types';
25
+ export { YunwuVideoProvider } from './yunwu-video';
@@ -15,4 +15,32 @@ export interface ProviderConfig {
15
15
  logger: any;
16
16
  ctx: any;
17
17
  }
18
+ export interface VideoGenerationOptions {
19
+ duration?: number;
20
+ aspectRatio?: string;
21
+ fps?: number;
22
+ style?: string;
23
+ }
24
+ export interface VideoTaskStatus {
25
+ status: 'pending' | 'processing' | 'completed' | 'failed';
26
+ taskId: string;
27
+ videoUrl?: string;
28
+ error?: string;
29
+ progress?: number;
30
+ }
31
+ export interface VideoProvider {
32
+ /**
33
+ * 创建视频生成任务
34
+ * @returns 任务ID
35
+ */
36
+ createVideoTask(prompt: string, imageUrl: string, options?: VideoGenerationOptions): Promise<string>;
37
+ /**
38
+ * 查询任务状态
39
+ */
40
+ queryTaskStatus(taskId: string): Promise<VideoTaskStatus>;
41
+ /**
42
+ * 生成视频(包含轮询等待)
43
+ */
44
+ generateVideo(prompt: string, imageUrl: string, options?: VideoGenerationOptions, maxWaitTime?: number): Promise<string>;
45
+ }
18
46
  export { sanitizeError, sanitizeString } from './utils';
@@ -0,0 +1,28 @@
1
+ import { VideoProvider, VideoTaskStatus, VideoGenerationOptions, ProviderConfig } from './types';
2
+ export interface YunwuVideoConfig extends ProviderConfig {
3
+ apiKey: string;
4
+ modelId: string;
5
+ apiBase: string;
6
+ }
7
+ export declare class YunwuVideoProvider implements VideoProvider {
8
+ private config;
9
+ constructor(config: YunwuVideoConfig);
10
+ /**
11
+ * 创建视频生成任务
12
+ * API: POST /v1/videos/generations
13
+ */
14
+ createVideoTask(prompt: string, imageUrl: string, options?: VideoGenerationOptions): Promise<string>;
15
+ /**
16
+ * 查询任务状态
17
+ * API: GET /v1/videos/generations/{taskId}
18
+ */
19
+ queryTaskStatus(taskId: string): Promise<VideoTaskStatus>;
20
+ /**
21
+ * 轮询等待任务完成
22
+ */
23
+ private pollTaskCompletion;
24
+ /**
25
+ * 生成视频(主入口)
26
+ */
27
+ generateVideo(prompt: string, imageUrl: string, options?: VideoGenerationOptions, maxWaitTime?: number): Promise<string>;
28
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-aka-ai-generator",
3
3
  "description": "自用AI生成插件(GPTGod & Yunwu)",
4
- "version": "0.6.13",
4
+ "version": "0.7.0",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [