@lingjingai/scriptctl 0.63.2 → 0.63.3
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/README.md +1 -1
- package/changes/0.63.3.md +5 -0
- package/dist/bin.js +114 -13
- package/dist/bin.js.map +3 -3
- package/dist/llm/router.d.ts +3 -2
- package/dist/llm/router.js +90 -13
- package/dist/llm/router.js.map +1 -1
- package/package.json +1 -1
- package/skills/scriptctl/SKILL.md +4 -0
- package/skills/scriptctl/references/ingest-workflow.md +4 -3
- package/dist/cli/register-batch.d.ts +0 -3
- package/dist/cli/register-batch.js +0 -28
- package/dist/cli/register-batch.js.map +0 -1
- package/dist/cli/register-edits.d.ts +0 -4
- package/dist/cli/register-edits.js +0 -234
- package/dist/cli/register-edits.js.map +0 -1
- package/dist/cli/register-query.d.ts +0 -4
- package/dist/cli/register-query.js +0 -95
- package/dist/cli/register-query.js.map +0 -1
- package/dist/infra/llm/doubao-video-model.d.ts +0 -56
- package/dist/infra/llm/doubao-video-model.js +0 -231
- package/dist/infra/llm/doubao-video-model.js.map +0 -1
package/README.md
CHANGED
|
@@ -136,7 +136,7 @@ MIT
|
|
|
136
136
|
|
|
137
137
|
### 指定视频理解服务
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
Gemini 文本、结构化工具和视频请求在当前路由重试耗尽或被审核拦截后,由已配置的豆包兜底。视频的默认路由与显式指定的 Gemini 均保留所选主模型;视频上传失败时豆包读取同一份本地文件。用户取消不触发兜底。豆包使用 `ARK_API_KEY`,`ARK_MODEL` 可指定已开通的视频理解端点,默认 `ep-20260721150136-98dj9`。直接选择豆包时只调用豆包:
|
|
140
140
|
|
|
141
141
|
```bash
|
|
142
142
|
# 豆包:默认读取 ARK_API_KEY;模型也可通过 ARK_MODEL 设置
|
package/dist/bin.js
CHANGED
|
@@ -265,6 +265,23 @@ function contextOf(failure) {
|
|
|
265
265
|
...failure.details !== void 0 ? { details: failure.details } : {}
|
|
266
266
|
};
|
|
267
267
|
}
|
|
268
|
+
function aggregateFailures(failures, message2) {
|
|
269
|
+
if (failures.length === 0) {
|
|
270
|
+
return createOperationalFailure(
|
|
271
|
+
{ failure_type: "unknown", failure_code: "unknown" },
|
|
272
|
+
{ retryable: false, message: message2 }
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
const first = failures[0];
|
|
276
|
+
const sameClassification = failures.every(
|
|
277
|
+
(failure) => failure.failure_type === first.failure_type && failure.failure_code === first.failure_code
|
|
278
|
+
);
|
|
279
|
+
const classification = sameClassification ? classificationOf(first) : { failure_type: "provider", failure_code: "attempts_exhausted" };
|
|
280
|
+
return createOperationalFailure(classification, {
|
|
281
|
+
retryable: failures.some((failure) => failure.retryable),
|
|
282
|
+
message: message2
|
|
283
|
+
});
|
|
284
|
+
}
|
|
268
285
|
function formatFailure(failure) {
|
|
269
286
|
const route = failure.provider || failure.model ? `${failure.provider ?? "provider"}/${failure.model ?? "model"} ` : "";
|
|
270
287
|
const reason = failure.provider_reason ? ` provider_reason=${failure.provider_reason}` : "";
|
|
@@ -7816,6 +7833,32 @@ function credentialError(provider) {
|
|
|
7816
7833
|
data: { expected: ["credential environment variable referenced by the selected provider"], actual: [`provider: ${provider}`, "credential: missing"] }
|
|
7817
7834
|
});
|
|
7818
7835
|
}
|
|
7836
|
+
function routedFailure(error, route) {
|
|
7837
|
+
return createOperationalFailure(classificationOf(error.failure), {
|
|
7838
|
+
...contextOf(error.failure),
|
|
7839
|
+
provider: route.provider,
|
|
7840
|
+
model: route.model,
|
|
7841
|
+
retryable: error.failure.retryable,
|
|
7842
|
+
message: error.message
|
|
7843
|
+
});
|
|
7844
|
+
}
|
|
7845
|
+
function fallbackFailure(primary, error, route) {
|
|
7846
|
+
const failures = [primary, routedFailure(error, route)];
|
|
7847
|
+
const aggregate = aggregateFailures(failures, "Primary model and Doubao fallback both failed.");
|
|
7848
|
+
return new LlmTransportError(error.code, aggregate.message, {
|
|
7849
|
+
retryable: aggregate.retryable,
|
|
7850
|
+
classification: classificationOf(aggregate),
|
|
7851
|
+
context: contextOf(aggregate),
|
|
7852
|
+
modelFailures: failures,
|
|
7853
|
+
details: failures.map(formatFailure)
|
|
7854
|
+
});
|
|
7855
|
+
}
|
|
7856
|
+
function isGeminiRoute(route) {
|
|
7857
|
+
return route.protocol === "google-gemini" || /(^|\/)gemini-/i.test(route.model);
|
|
7858
|
+
}
|
|
7859
|
+
function canFallback(error, signal) {
|
|
7860
|
+
return !signal?.aborted && error instanceof LlmTransportError && error.code !== "cancelled";
|
|
7861
|
+
}
|
|
7819
7862
|
var ModelRouter = class {
|
|
7820
7863
|
constructor(config, options = {}) {
|
|
7821
7864
|
this.config = config;
|
|
@@ -8096,15 +8139,66 @@ var ModelRouter = class {
|
|
|
8096
8139
|
}
|
|
8097
8140
|
const adapter = this.videoAdapter(route);
|
|
8098
8141
|
return {
|
|
8099
|
-
prepare: (localPath, mimeType, mode, signal) =>
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8142
|
+
prepare: async (localPath, mimeType, mode, signal) => {
|
|
8143
|
+
const started = Date.now();
|
|
8144
|
+
try {
|
|
8145
|
+
return await adapter.prepare(localPath, mimeType, mode, signal);
|
|
8146
|
+
} catch (error) {
|
|
8147
|
+
if (!canFallback(error, signal) || !this.doubaoAdapter()) throw error;
|
|
8148
|
+
this.recordEvent(
|
|
8149
|
+
route.info,
|
|
8150
|
+
"failed",
|
|
8151
|
+
new Date(started).toISOString(),
|
|
8152
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
8153
|
+
Date.now() - started,
|
|
8154
|
+
error.usage,
|
|
8155
|
+
error.attempts,
|
|
8156
|
+
error.code
|
|
8157
|
+
);
|
|
8158
|
+
return { kind: "local-fallback", path: localPath, mimeType, primaryFailure: routedFailure(error, route.info) };
|
|
8159
|
+
}
|
|
8160
|
+
},
|
|
8161
|
+
release: (media, signal) => media.kind === "local-fallback" ? Promise.resolve() : adapter.release(media, signal),
|
|
8162
|
+
generate: async (request2) => {
|
|
8163
|
+
request2.signal?.throwIfAborted();
|
|
8164
|
+
if (request2.media.kind === "local-fallback") {
|
|
8165
|
+
return this.generateDoubaoVideo(task, request2, request2.media.primaryFailure);
|
|
8166
|
+
}
|
|
8167
|
+
try {
|
|
8168
|
+
return await this.tracked(route.info, () => adapter.generate({ ...request2, maxOutputTokens: spec2.maxOutputTokens }));
|
|
8169
|
+
} catch (error) {
|
|
8170
|
+
if (!canFallback(error, request2.signal) || !this.doubaoAdapter() || !request2.localPath) throw error;
|
|
8171
|
+
return this.generateDoubaoVideo(task, request2, routedFailure(error, route.info));
|
|
8172
|
+
}
|
|
8173
|
+
}
|
|
8105
8174
|
};
|
|
8106
8175
|
}
|
|
8107
|
-
|
|
8176
|
+
async generateDoubaoVideo(task, request2, primary) {
|
|
8177
|
+
const adapter = this.doubaoAdapter();
|
|
8178
|
+
if (!adapter) throw credentialError("doubao");
|
|
8179
|
+
const localPath = request2.media.kind === "local-fallback" ? request2.media.path : request2.localPath;
|
|
8180
|
+
if (!localPath) throw configRuntimeError("Doubao video fallback requires the local video.", ["localPath"], ["localPath: missing"]);
|
|
8181
|
+
const localMime = request2.localMime ?? (request2.media.kind === "file-api" ? request2.media.file.mimeType : request2.media.mimeType);
|
|
8182
|
+
const route = this.doubaoRoute(task);
|
|
8183
|
+
process.stderr.write(`\u21AA\uFE0F ${primary.model} \u8BF7\u6C42\u5931\u8D25\uFF0C\u56DE\u9000\u5230\u8C46\u5305 ${route.model} (${formatFailure(primary)})
|
|
8184
|
+
`);
|
|
8185
|
+
try {
|
|
8186
|
+
return await this.tracked(route, () => adapter.run({
|
|
8187
|
+
localPath,
|
|
8188
|
+
localMime,
|
|
8189
|
+
user: request2.user,
|
|
8190
|
+
fps: request2.fps,
|
|
8191
|
+
maxOutputTokens: LLM_TASK_REGISTRY[task].maxOutputTokens,
|
|
8192
|
+
...request2.system ? { system: request2.system } : {},
|
|
8193
|
+
...request2.responseJsonSchema ? { responseJsonSchema: request2.responseJsonSchema } : {},
|
|
8194
|
+
...request2.signal ? { signal: request2.signal } : {}
|
|
8195
|
+
}));
|
|
8196
|
+
} catch (error) {
|
|
8197
|
+
if (!canFallback(error, request2.signal)) throw error;
|
|
8198
|
+
throw fallbackFailure(primary, error, route);
|
|
8199
|
+
}
|
|
8200
|
+
}
|
|
8201
|
+
// Resolve the default fallback or explicitly selected Doubao video.
|
|
8108
8202
|
// Overridable in tests to inject a fake Ark client.
|
|
8109
8203
|
doubaoAdapter(route) {
|
|
8110
8204
|
return this.arkAdapter(route);
|
|
@@ -8128,29 +8222,36 @@ var ModelRouter = class {
|
|
|
8128
8222
|
doubaoModel() {
|
|
8129
8223
|
return (process.env[DOUBAO_FALLBACK.modelEnv] ?? "").trim() || DOUBAO_FALLBACK.model;
|
|
8130
8224
|
}
|
|
8131
|
-
async
|
|
8225
|
+
async withTextFallback(task, signal, generate) {
|
|
8132
8226
|
const route = this.resolve(task);
|
|
8133
8227
|
const adapter = this.adapter(route);
|
|
8134
8228
|
try {
|
|
8135
8229
|
return await this.tracked(route.info, () => generate(adapter));
|
|
8136
8230
|
} catch (error) {
|
|
8137
|
-
if (signal
|
|
8231
|
+
if (!canFallback(error, signal) || !isGeminiRoute(route.info) && error.code !== "content_filtered") throw error;
|
|
8138
8232
|
const doubao = this.doubaoTextAdapter();
|
|
8139
8233
|
if (!doubao) throw error;
|
|
8140
|
-
|
|
8234
|
+
const primary = routedFailure(error, route.info);
|
|
8235
|
+
const fallback = this.doubaoRoute(task);
|
|
8236
|
+
process.stderr.write(`\u21AA\uFE0F ${route.info.model} \u8BF7\u6C42\u5931\u8D25\uFF0C\u56DE\u9000\u5230\u8C46\u5305 ${fallback.model} (${formatFailure(primary)})
|
|
8141
8237
|
`);
|
|
8142
|
-
|
|
8238
|
+
try {
|
|
8239
|
+
return await this.tracked(fallback, () => generate(doubao));
|
|
8240
|
+
} catch (fallbackError) {
|
|
8241
|
+
if (!canFallback(fallbackError, signal)) throw fallbackError;
|
|
8242
|
+
throw fallbackFailure(primary, fallbackError, fallback);
|
|
8243
|
+
}
|
|
8143
8244
|
}
|
|
8144
8245
|
}
|
|
8145
8246
|
async generateText(task, request2) {
|
|
8146
8247
|
const spec2 = LLM_TASK_REGISTRY[task];
|
|
8147
8248
|
if (spec2.mode !== "text") throw configRuntimeError("The task does not support text generation.", ["text task"], [`task: ${task}`]);
|
|
8148
|
-
return this.
|
|
8249
|
+
return this.withTextFallback(task, request2.signal, (adapter) => adapter.generateText({ ...request2, maxOutputTokens: spec2.maxOutputTokens }));
|
|
8149
8250
|
}
|
|
8150
8251
|
async generateTool(task, request2) {
|
|
8151
8252
|
const spec2 = LLM_TASK_REGISTRY[task];
|
|
8152
8253
|
if (spec2.mode !== "tool") throw configRuntimeError("The task does not support tool generation.", ["tool task"], [`task: ${task}`]);
|
|
8153
|
-
return this.
|
|
8254
|
+
return this.withTextFallback(task, request2.signal, (adapter) => adapter.generateTool({ ...request2, maxOutputTokens: spec2.maxOutputTokens }));
|
|
8154
8255
|
}
|
|
8155
8256
|
};
|
|
8156
8257
|
function createLlmExecutor(explicitConfigPath, options) {
|