qwenproxy-cli 1.0.0 → 1.0.2
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/LICENSE +14 -14
- package/README.md +906 -906
- package/bin/qwenproxy.js +5 -1
- package/package.json +77 -78
- package/src/api/error-classifier.ts +159 -159
- package/src/api/error-helpers.ts +118 -118
- package/src/api/models.ts +261 -261
- package/src/api/server.ts +860 -859
- package/src/cache/memory-cache.ts +385 -385
- package/src/clean-cache.ts +204 -204
- package/src/core/account-concurrency.ts +671 -671
- package/src/core/account-manager.ts +301 -297
- package/src/core/account-priority.ts +163 -163
- package/src/core/accounts.ts +186 -186
- package/src/core/config.ts +383 -383
- package/src/core/crypto-utils.ts +79 -79
- package/src/core/database.ts +276 -276
- package/src/core/errors.ts +118 -118
- package/src/core/logger.ts +269 -269
- package/src/core/memory-usage.ts +84 -84
- package/src/core/metrics.ts +291 -291
- package/src/core/model-alias.ts +77 -77
- package/src/core/model-registry.ts +544 -544
- package/src/core/mutex.ts +119 -119
- package/src/core/paths.ts +199 -199
- package/src/core/prompt-limits.ts +214 -214
- package/src/core/reasoning-effort.ts +102 -102
- package/src/core/stream-registry.ts +96 -96
- package/src/core/waf-isolation.ts +117 -117
- package/src/core/watchdog.ts +195 -195
- package/src/delete-chats.ts +23 -23
- package/src/index.ts +65 -64
- package/src/login.ts +147 -147
- package/src/reset-cooldowns.ts +11 -11
- package/src/routes/anthropic/index.ts +355 -355
- package/src/routes/anthropic/translate.ts +522 -522
- package/src/routes/anthropic/types.ts +154 -154
- package/src/routes/anthropic/validation.ts +144 -144
- package/src/routes/chat/account.ts +1817 -1817
- package/src/routes/chat/context.ts +241 -241
- package/src/routes/chat/errors.ts +85 -85
- package/src/routes/chat/helpers.ts +268 -268
- package/src/routes/chat/index.ts +618 -618
- package/src/routes/chat/media.ts +285 -285
- package/src/routes/chat/retry-policy.ts +754 -754
- package/src/routes/chat/stop.ts +98 -98
- package/src/routes/chat/streaming.ts +2710 -2710
- package/src/routes/chat/validation.ts +526 -526
- package/src/routes/chat.ts +2 -2
- package/src/routes/completions.ts +290 -290
- package/src/routes/images.ts +139 -139
- package/src/routes/responses/adapter.ts +503 -503
- package/src/routes/responses/index.ts +405 -405
- package/src/routes/responses/state.ts +230 -230
- package/src/routes/responses/streaming.ts +528 -528
- package/src/routes/responses/types.ts +285 -285
- package/src/routes/responses/validation.ts +202 -202
- package/src/routes/upload.ts +731 -731
- package/src/routes/videos.ts +214 -214
- package/src/services/auth-playwright.ts +173 -173
- package/src/services/captcha-coordinator.ts +161 -161
- package/src/services/captcha-solver.ts +553 -553
- package/src/services/chat-cleanup.ts +80 -80
- package/src/services/context-meter.ts +317 -317
- package/src/services/fingerprint.ts +242 -242
- package/src/services/human-behavior.ts +173 -173
- package/src/services/media-generation.ts +1748 -1748
- package/src/services/playwright.ts +2878 -2800
- package/src/services/qwen-chat-pool.ts +345 -345
- package/src/services/qwen-errors.ts +133 -133
- package/src/services/qwen-headers.ts +79 -79
- package/src/services/qwen-thread-state.ts +393 -393
- package/src/services/qwen-url.ts +19 -19
- package/src/services/qwen.ts +3126 -3126
- package/src/services/session-keeper.ts +88 -88
- package/src/services/token-estimation-metrics.ts +118 -118
- package/src/sync/claude-code.ts +75 -75
- package/src/sync/codex.ts +123 -123
- package/src/sync/index.ts +362 -362
- package/src/sync/omp.ts +105 -105
- package/src/sync/opencode.ts +214 -214
- package/src/sync/types.ts +53 -53
- package/src/sync/utils.ts +27 -27
- package/src/sync-clients.ts +189 -189
- package/src/tools/instructions.ts +137 -137
- package/src/tools/manifest.ts +81 -81
- package/src/tools/parser.ts +2989 -2989
- package/src/tools/toolcall-tags.ts +142 -142
- package/src/tui/app.ts +259 -264
- package/src/tui/index.ts +61 -61
- package/src/tui/markdown.ts +258 -258
- package/src/tui/proxy-client.ts +331 -326
- package/src/tui/screen.ts +294 -278
- package/src/tui/server-manager.ts +270 -270
- package/src/tui/theme.ts +432 -432
- package/src/tui/types.ts +33 -33
- package/src/tui/views/accounts-view.ts +656 -656
- package/src/tui/views/chat-view.ts +1018 -823
- package/src/tui/views/logs-view.ts +479 -413
- package/src/tui/views/status-view.ts +204 -204
- package/src/tui/views/storage-view.ts +304 -291
- package/src/tui/views/sync-view.ts +409 -409
- package/src/types/ali-oss.d.ts +32 -32
- package/src/update-cli.ts +121 -0
- package/src/utils/context-truncation.ts +84 -84
- package/src/utils/json.ts +380 -380
- package/src/utils/session-id.ts +37 -37
- package/src/utils/tool-call-guard.ts +84 -84
- package/src/utils/types.ts +109 -109
package/src/routes/videos.ts
CHANGED
|
@@ -1,214 +1,214 @@
|
|
|
1
|
-
import type { Context } from "hono";
|
|
2
|
-
import {
|
|
3
|
-
generateVideo,
|
|
4
|
-
isSupportedMediaSize,
|
|
5
|
-
MEDIA_SIZE_OPTIONS,
|
|
6
|
-
pollVideoTask,
|
|
7
|
-
supportsPromptMediaGeneration,
|
|
8
|
-
} from "../services/media-generation.ts";
|
|
9
|
-
import { logger } from "../core/logger.ts";
|
|
10
|
-
import { sendOpenAIError } from "../api/error-helpers.ts";
|
|
11
|
-
import { NotFoundError, ValidationError } from "../core/errors.ts";
|
|
12
|
-
|
|
13
|
-
const DEFAULT_SIZE = "16:9";
|
|
14
|
-
|
|
15
|
-
const TASK_TTL_MS = 60 * 60_000;
|
|
16
|
-
|
|
17
|
-
type MediaTaskStatus = "pending" | "running" | "completed" | "failed";
|
|
18
|
-
|
|
19
|
-
interface VideoTaskEntry {
|
|
20
|
-
accountId: string;
|
|
21
|
-
chatId: string;
|
|
22
|
-
createdAt: number;
|
|
23
|
-
status: MediaTaskStatus;
|
|
24
|
-
videoUrl?: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface VideosGenerationsRequest {
|
|
28
|
-
model?: unknown;
|
|
29
|
-
prompt?: unknown;
|
|
30
|
-
size?: unknown;
|
|
31
|
-
wait?: unknown;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const videoTasks = new Map<string, VideoTaskEntry>();
|
|
35
|
-
|
|
36
|
-
function cleanupExpiredTasks(): void {
|
|
37
|
-
const cutoff = Date.now() - TASK_TTL_MS;
|
|
38
|
-
for (const [taskId, entry] of videoTasks) {
|
|
39
|
-
if (entry.createdAt < cutoff) {
|
|
40
|
-
videoTasks.delete(taskId);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function validationError(message: string, param: string): ValidationError {
|
|
46
|
-
const err = new ValidationError(message);
|
|
47
|
-
err.param = param;
|
|
48
|
-
return err;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export async function videosGenerations(c: Context): Promise<Response> {
|
|
52
|
-
cleanupExpiredTasks();
|
|
53
|
-
|
|
54
|
-
let body: VideosGenerationsRequest;
|
|
55
|
-
try {
|
|
56
|
-
body = await c.req.json();
|
|
57
|
-
} catch {
|
|
58
|
-
return sendOpenAIError(c, new ValidationError("Request body must be valid JSON"));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const prompt = typeof body.prompt === "string" ? body.prompt.trim() : "";
|
|
62
|
-
if (!prompt) {
|
|
63
|
-
return sendOpenAIError(
|
|
64
|
-
c,
|
|
65
|
-
validationError("`prompt` must be a non-empty string", "prompt"),
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const size = body.size === undefined ? DEFAULT_SIZE : body.size;
|
|
70
|
-
if (!isSupportedMediaSize(size)) {
|
|
71
|
-
return sendOpenAIError(
|
|
72
|
-
c,
|
|
73
|
-
validationError(
|
|
74
|
-
`\`size\` must be one of: ${MEDIA_SIZE_OPTIONS.join(", ")}`,
|
|
75
|
-
"size",
|
|
76
|
-
),
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const model =
|
|
81
|
-
typeof body.model === "string" && body.model.trim()
|
|
82
|
-
? body.model.trim()
|
|
83
|
-
: "";
|
|
84
|
-
if (!model) {
|
|
85
|
-
return sendOpenAIError(
|
|
86
|
-
c,
|
|
87
|
-
validationError("`model` must be the model selected by the client", "model"),
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
if (!supportsPromptMediaGeneration(model, "video")) {
|
|
91
|
-
return sendOpenAIError(
|
|
92
|
-
c,
|
|
93
|
-
validationError(
|
|
94
|
-
`Model \`${model}\` requires a reference image and is not available through prompt-only video generation yet`,
|
|
95
|
-
"model",
|
|
96
|
-
),
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
const wait = body.wait !== false;
|
|
100
|
-
|
|
101
|
-
logger.info("Video generation request", {
|
|
102
|
-
model,
|
|
103
|
-
size,
|
|
104
|
-
wait,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
try {
|
|
108
|
-
const result = await generateVideo({
|
|
109
|
-
model,
|
|
110
|
-
prompt,
|
|
111
|
-
size,
|
|
112
|
-
waitForCompletion: wait,
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
const created = Math.floor(Date.now() / 1000);
|
|
116
|
-
|
|
117
|
-
// The upstream can return the video URL inline, without a task id.
|
|
118
|
-
if (!result.task_id) {
|
|
119
|
-
if (result.status === "completed" && result.video_url) {
|
|
120
|
-
return c.json({
|
|
121
|
-
created,
|
|
122
|
-
task_id: "",
|
|
123
|
-
status: "completed",
|
|
124
|
-
data: [{ url: result.video_url }],
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
return sendOpenAIError(
|
|
128
|
-
c,
|
|
129
|
-
new Error("Video generation returned no task or video URL"),
|
|
130
|
-
500,
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
videoTasks.set(result.task_id, {
|
|
135
|
-
accountId: result.accountId,
|
|
136
|
-
chatId: result.chatId,
|
|
137
|
-
createdAt: Date.now(),
|
|
138
|
-
status: result.status,
|
|
139
|
-
videoUrl: result.video_url,
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
if (result.status === "failed") {
|
|
143
|
-
logger.error("Video generation task failed", { taskId: result.task_id });
|
|
144
|
-
return sendOpenAIError(c, new Error("Video generation failed"), 500);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (result.status === "completed") {
|
|
148
|
-
return c.json({
|
|
149
|
-
created,
|
|
150
|
-
task_id: result.task_id,
|
|
151
|
-
status: "completed",
|
|
152
|
-
data: [{ url: result.video_url }],
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// wait=false, or the upstream poll window elapsed before completion.
|
|
157
|
-
return c.json({
|
|
158
|
-
created,
|
|
159
|
-
task_id: result.task_id,
|
|
160
|
-
status: result.status,
|
|
161
|
-
});
|
|
162
|
-
} catch (error) {
|
|
163
|
-
logger.error("Video generation failed", {
|
|
164
|
-
model,
|
|
165
|
-
error: error instanceof Error ? error.message : String(error),
|
|
166
|
-
});
|
|
167
|
-
return sendOpenAIError(c, error, 500);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export async function videoTaskStatus(c: Context): Promise<Response> {
|
|
172
|
-
cleanupExpiredTasks();
|
|
173
|
-
|
|
174
|
-
const taskId = c.req.param("taskId");
|
|
175
|
-
if (!taskId) {
|
|
176
|
-
return sendOpenAIError(c, new NotFoundError("Video task not found"));
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const entry = videoTasks.get(taskId);
|
|
180
|
-
if (!entry) {
|
|
181
|
-
return sendOpenAIError(c, new NotFoundError(`Video task not found: ${taskId}`));
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const wait = c.req.query("wait") === "true";
|
|
185
|
-
|
|
186
|
-
try {
|
|
187
|
-
const isTerminal = entry.status === "completed" || entry.status === "failed";
|
|
188
|
-
if (wait && !isTerminal) {
|
|
189
|
-
// pollVideoTask blocks until a terminal status or its internal timeout.
|
|
190
|
-
const result = await pollVideoTask({ taskId, accountId: entry.accountId });
|
|
191
|
-
entry.status = result.status;
|
|
192
|
-
entry.videoUrl = result.video_url ?? entry.videoUrl;
|
|
193
|
-
if (result.status === "failed") {
|
|
194
|
-
logger.error("Video task failed", {
|
|
195
|
-
taskId,
|
|
196
|
-
error: result.error ?? "unknown",
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return c.json({
|
|
202
|
-
task_id: taskId,
|
|
203
|
-
status: entry.status,
|
|
204
|
-
video_url: entry.videoUrl ?? null,
|
|
205
|
-
created: Math.floor(entry.createdAt / 1000),
|
|
206
|
-
});
|
|
207
|
-
} catch (error) {
|
|
208
|
-
logger.error("Video task status check failed", {
|
|
209
|
-
taskId,
|
|
210
|
-
error: error instanceof Error ? error.message : String(error),
|
|
211
|
-
});
|
|
212
|
-
return sendOpenAIError(c, error, 500);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
import {
|
|
3
|
+
generateVideo,
|
|
4
|
+
isSupportedMediaSize,
|
|
5
|
+
MEDIA_SIZE_OPTIONS,
|
|
6
|
+
pollVideoTask,
|
|
7
|
+
supportsPromptMediaGeneration,
|
|
8
|
+
} from "../services/media-generation.ts";
|
|
9
|
+
import { logger } from "../core/logger.ts";
|
|
10
|
+
import { sendOpenAIError } from "../api/error-helpers.ts";
|
|
11
|
+
import { NotFoundError, ValidationError } from "../core/errors.ts";
|
|
12
|
+
|
|
13
|
+
const DEFAULT_SIZE = "16:9";
|
|
14
|
+
|
|
15
|
+
const TASK_TTL_MS = 60 * 60_000;
|
|
16
|
+
|
|
17
|
+
type MediaTaskStatus = "pending" | "running" | "completed" | "failed";
|
|
18
|
+
|
|
19
|
+
interface VideoTaskEntry {
|
|
20
|
+
accountId: string;
|
|
21
|
+
chatId: string;
|
|
22
|
+
createdAt: number;
|
|
23
|
+
status: MediaTaskStatus;
|
|
24
|
+
videoUrl?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface VideosGenerationsRequest {
|
|
28
|
+
model?: unknown;
|
|
29
|
+
prompt?: unknown;
|
|
30
|
+
size?: unknown;
|
|
31
|
+
wait?: unknown;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const videoTasks = new Map<string, VideoTaskEntry>();
|
|
35
|
+
|
|
36
|
+
function cleanupExpiredTasks(): void {
|
|
37
|
+
const cutoff = Date.now() - TASK_TTL_MS;
|
|
38
|
+
for (const [taskId, entry] of videoTasks) {
|
|
39
|
+
if (entry.createdAt < cutoff) {
|
|
40
|
+
videoTasks.delete(taskId);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function validationError(message: string, param: string): ValidationError {
|
|
46
|
+
const err = new ValidationError(message);
|
|
47
|
+
err.param = param;
|
|
48
|
+
return err;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function videosGenerations(c: Context): Promise<Response> {
|
|
52
|
+
cleanupExpiredTasks();
|
|
53
|
+
|
|
54
|
+
let body: VideosGenerationsRequest;
|
|
55
|
+
try {
|
|
56
|
+
body = await c.req.json();
|
|
57
|
+
} catch {
|
|
58
|
+
return sendOpenAIError(c, new ValidationError("Request body must be valid JSON"));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const prompt = typeof body.prompt === "string" ? body.prompt.trim() : "";
|
|
62
|
+
if (!prompt) {
|
|
63
|
+
return sendOpenAIError(
|
|
64
|
+
c,
|
|
65
|
+
validationError("`prompt` must be a non-empty string", "prompt"),
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const size = body.size === undefined ? DEFAULT_SIZE : body.size;
|
|
70
|
+
if (!isSupportedMediaSize(size)) {
|
|
71
|
+
return sendOpenAIError(
|
|
72
|
+
c,
|
|
73
|
+
validationError(
|
|
74
|
+
`\`size\` must be one of: ${MEDIA_SIZE_OPTIONS.join(", ")}`,
|
|
75
|
+
"size",
|
|
76
|
+
),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const model =
|
|
81
|
+
typeof body.model === "string" && body.model.trim()
|
|
82
|
+
? body.model.trim()
|
|
83
|
+
: "";
|
|
84
|
+
if (!model) {
|
|
85
|
+
return sendOpenAIError(
|
|
86
|
+
c,
|
|
87
|
+
validationError("`model` must be the model selected by the client", "model"),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (!supportsPromptMediaGeneration(model, "video")) {
|
|
91
|
+
return sendOpenAIError(
|
|
92
|
+
c,
|
|
93
|
+
validationError(
|
|
94
|
+
`Model \`${model}\` requires a reference image and is not available through prompt-only video generation yet`,
|
|
95
|
+
"model",
|
|
96
|
+
),
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
const wait = body.wait !== false;
|
|
100
|
+
|
|
101
|
+
logger.info("Video generation request", {
|
|
102
|
+
model,
|
|
103
|
+
size,
|
|
104
|
+
wait,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const result = await generateVideo({
|
|
109
|
+
model,
|
|
110
|
+
prompt,
|
|
111
|
+
size,
|
|
112
|
+
waitForCompletion: wait,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const created = Math.floor(Date.now() / 1000);
|
|
116
|
+
|
|
117
|
+
// The upstream can return the video URL inline, without a task id.
|
|
118
|
+
if (!result.task_id) {
|
|
119
|
+
if (result.status === "completed" && result.video_url) {
|
|
120
|
+
return c.json({
|
|
121
|
+
created,
|
|
122
|
+
task_id: "",
|
|
123
|
+
status: "completed",
|
|
124
|
+
data: [{ url: result.video_url }],
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return sendOpenAIError(
|
|
128
|
+
c,
|
|
129
|
+
new Error("Video generation returned no task or video URL"),
|
|
130
|
+
500,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
videoTasks.set(result.task_id, {
|
|
135
|
+
accountId: result.accountId,
|
|
136
|
+
chatId: result.chatId,
|
|
137
|
+
createdAt: Date.now(),
|
|
138
|
+
status: result.status,
|
|
139
|
+
videoUrl: result.video_url,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
if (result.status === "failed") {
|
|
143
|
+
logger.error("Video generation task failed", { taskId: result.task_id });
|
|
144
|
+
return sendOpenAIError(c, new Error("Video generation failed"), 500);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (result.status === "completed") {
|
|
148
|
+
return c.json({
|
|
149
|
+
created,
|
|
150
|
+
task_id: result.task_id,
|
|
151
|
+
status: "completed",
|
|
152
|
+
data: [{ url: result.video_url }],
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// wait=false, or the upstream poll window elapsed before completion.
|
|
157
|
+
return c.json({
|
|
158
|
+
created,
|
|
159
|
+
task_id: result.task_id,
|
|
160
|
+
status: result.status,
|
|
161
|
+
});
|
|
162
|
+
} catch (error) {
|
|
163
|
+
logger.error("Video generation failed", {
|
|
164
|
+
model,
|
|
165
|
+
error: error instanceof Error ? error.message : String(error),
|
|
166
|
+
});
|
|
167
|
+
return sendOpenAIError(c, error, 500);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export async function videoTaskStatus(c: Context): Promise<Response> {
|
|
172
|
+
cleanupExpiredTasks();
|
|
173
|
+
|
|
174
|
+
const taskId = c.req.param("taskId");
|
|
175
|
+
if (!taskId) {
|
|
176
|
+
return sendOpenAIError(c, new NotFoundError("Video task not found"));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const entry = videoTasks.get(taskId);
|
|
180
|
+
if (!entry) {
|
|
181
|
+
return sendOpenAIError(c, new NotFoundError(`Video task not found: ${taskId}`));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const wait = c.req.query("wait") === "true";
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const isTerminal = entry.status === "completed" || entry.status === "failed";
|
|
188
|
+
if (wait && !isTerminal) {
|
|
189
|
+
// pollVideoTask blocks until a terminal status or its internal timeout.
|
|
190
|
+
const result = await pollVideoTask({ taskId, accountId: entry.accountId });
|
|
191
|
+
entry.status = result.status;
|
|
192
|
+
entry.videoUrl = result.video_url ?? entry.videoUrl;
|
|
193
|
+
if (result.status === "failed") {
|
|
194
|
+
logger.error("Video task failed", {
|
|
195
|
+
taskId,
|
|
196
|
+
error: result.error ?? "unknown",
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return c.json({
|
|
202
|
+
task_id: taskId,
|
|
203
|
+
status: entry.status,
|
|
204
|
+
video_url: entry.videoUrl ?? null,
|
|
205
|
+
created: Math.floor(entry.createdAt / 1000),
|
|
206
|
+
});
|
|
207
|
+
} catch (error) {
|
|
208
|
+
logger.error("Video task status check failed", {
|
|
209
|
+
taskId,
|
|
210
|
+
error: error instanceof Error ? error.message : String(error),
|
|
211
|
+
});
|
|
212
|
+
return sendOpenAIError(c, error, 500);
|
|
213
|
+
}
|
|
214
|
+
}
|