@lingjingai/scriptctl 0.62.2 → 0.62.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.
@@ -0,0 +1,4 @@
1
+ # 0.62.3
2
+
3
+ - 梗概、文本整理与结构化文本任务遇到明确的内容审核拒绝时,使用已配置的豆包 Pro Ark Responses 端点兜底。
4
+ - 保持原始文本、结构化 Schema、取消信号及每条实际模型线路的 token 与调用成本记录。
package/dist/bin.js CHANGED
@@ -1043,7 +1043,7 @@ var AnthropicModel = class {
1043
1043
  }
1044
1044
  };
1045
1045
 
1046
- // src/infra/llm/doubao-video-model.ts
1046
+ // src/infra/llm/doubao-model.ts
1047
1047
  import * as fs7 from "node:fs";
1048
1048
  import * as path7 from "node:path";
1049
1049
  import { File } from "node:buffer";
@@ -1173,7 +1173,7 @@ function httpError(status, message2, detail, context = {}) {
1173
1173
  });
1174
1174
  }
1175
1175
 
1176
- // src/infra/llm/doubao-video-model.ts
1176
+ // src/infra/llm/doubao-model.ts
1177
1177
  var POLL_INTERVAL_MS = 2e3;
1178
1178
  function delay2(ms) {
1179
1179
  return new Promise((resolve19) => setTimeout(resolve19, ms));
@@ -1203,7 +1203,7 @@ function extractText(res) {
1203
1203
  }
1204
1204
  return parts.join("").trim();
1205
1205
  }
1206
- var DoubaoVideoModel = class {
1206
+ var DoubaoModel = class {
1207
1207
  constructor(model, timeoutMs, maxAttempts, apiKey, baseUrl, client) {
1208
1208
  this.model = model;
1209
1209
  this.timeoutMs = timeoutMs;
@@ -1278,12 +1278,45 @@ var DoubaoVideoModel = class {
1278
1278
  if (request2.responseJsonSchema) {
1279
1279
  params["text"] = { format: { type: "json_schema", name: "video_serial", schema: request2.responseJsonSchema } };
1280
1280
  }
1281
+ return this.generateResponse(params, request2.signal, Boolean(request2.responseJsonSchema));
1282
+ }
1283
+ async generateText(request2) {
1284
+ const params = this.textParams(request2);
1285
+ if (request2.responseFormat) params["text"] = { format: { type: request2.responseFormat } };
1286
+ return this.generateResponse(params, request2.signal, Boolean(request2.responseFormat));
1287
+ }
1288
+ async generateTool(request2) {
1289
+ const params = this.textParams(request2);
1290
+ params["text"] = { format: {
1291
+ type: "json_schema",
1292
+ name: request2.tool.name,
1293
+ description: request2.tool.description,
1294
+ schema: request2.tool.inputSchema
1295
+ } };
1296
+ const response = await this.generateResponse(params, request2.signal, true);
1297
+ return {
1298
+ input: JSON.parse(response.text),
1299
+ finishReason: response.finishReason,
1300
+ attempts: response.attempts,
1301
+ usage: response.usage,
1302
+ raw: response.raw
1303
+ };
1304
+ }
1305
+ textParams(request2) {
1306
+ return {
1307
+ model: this.model,
1308
+ max_output_tokens: request2.maxOutputTokens,
1309
+ instructions: request2.system,
1310
+ input: [{ role: "user", content: [{ type: "input_text", text: request2.user }] }]
1311
+ };
1312
+ }
1313
+ async generateResponse(params, signal, structured) {
1281
1314
  const { value, attempts, failedUsage } = await withTransientRetry(this.maxAttempts, async () => {
1282
1315
  let res;
1283
1316
  try {
1284
- res = await this.client.createResponse(params, request2.signal);
1317
+ res = await this.client.createResponse(params, signal);
1285
1318
  } catch (error) {
1286
- throw toArkError(error, request2.signal, "generate", this.model);
1319
+ throw toArkError(error, signal, "generate", this.model);
1287
1320
  }
1288
1321
  const status = (res.status ?? "").toLowerCase();
1289
1322
  const usage = arkUsage(res.usage);
@@ -1294,11 +1327,11 @@ var DoubaoVideoModel = class {
1294
1327
  }
1295
1328
  const text2 = extractText(res);
1296
1329
  if (!text2) throw new LlmTransportError("empty_response", "Doubao returned an empty response.", { retryable: true, usage });
1297
- if (request2.responseJsonSchema) {
1330
+ if (structured) {
1298
1331
  try {
1299
1332
  JSON.parse(text2);
1300
1333
  } catch {
1301
- throw new LlmTransportError("invalid_response", "Doubao returned malformed JSON for a structured video response.", {
1334
+ throw new LlmTransportError("invalid_response", "Doubao returned malformed JSON for a structured response.", {
1302
1335
  retryable: true,
1303
1336
  usage,
1304
1337
  context: { provider: "doubao", model: this.model, operation: "generate" }
@@ -2457,6 +2490,12 @@ var ModelRouter = class {
2457
2490
  // Resolve an explicit Doubao route or the default fallback; absent fallback credentials skip it.
2458
2491
  // Overridable in tests to inject a fake Ark client.
2459
2492
  doubaoAdapter(route) {
2493
+ return this.arkAdapter(route);
2494
+ }
2495
+ doubaoTextAdapter() {
2496
+ return this.arkAdapter();
2497
+ }
2498
+ arkAdapter(route) {
2460
2499
  const apiKey = route ? this.videoCredential(route) : (process.env[DOUBAO_FALLBACK.apiKeyEnv] ?? "").trim();
2461
2500
  if (!apiKey) return null;
2462
2501
  const model = route?.info.model ?? this.doubaoModel();
@@ -2464,7 +2503,7 @@ var ModelRouter = class {
2464
2503
  const key = `doubao ${baseUrl} ${model}`;
2465
2504
  const cached2 = this.doubaoAdapters.get(key);
2466
2505
  if (cached2) return cached2;
2467
- const adapter = new DoubaoVideoModel(model, DOUBAO_FALLBACK.timeoutMs, DOUBAO_FALLBACK.maxAttempts, apiKey, baseUrl);
2506
+ const adapter = new DoubaoModel(model, DOUBAO_FALLBACK.timeoutMs, DOUBAO_FALLBACK.maxAttempts, apiKey, baseUrl);
2468
2507
  this.doubaoAdapters.set(key, adapter);
2469
2508
  return adapter;
2470
2509
  }
@@ -2472,19 +2511,29 @@ var ModelRouter = class {
2472
2511
  doubaoModel() {
2473
2512
  return (process.env[DOUBAO_FALLBACK.modelEnv] ?? "").trim() || DOUBAO_FALLBACK.model;
2474
2513
  }
2514
+ async withModerationFallback(task, signal, generate) {
2515
+ const route = this.resolve(task);
2516
+ const adapter = this.adapter(route);
2517
+ try {
2518
+ return await this.tracked(route.info, () => generate(adapter));
2519
+ } catch (error) {
2520
+ if (signal?.aborted || !(error instanceof LlmTransportError) || error.code !== "content_filtered") throw error;
2521
+ const doubao = this.doubaoTextAdapter();
2522
+ if (!doubao) throw error;
2523
+ process.stderr.write(`\u21AA\uFE0F \u6587\u672C\u5185\u5BB9\u5BA1\u6838\u62D2\u7EDD\uFF0C\u56DE\u9000\u5230\u8C46\u5305 ${this.doubaoModel()}
2524
+ `);
2525
+ return this.tracked(this.doubaoRoute(task), () => generate(doubao));
2526
+ }
2527
+ }
2475
2528
  async generateText(task, request2) {
2476
2529
  const spec2 = LLM_TASK_REGISTRY[task];
2477
2530
  if (spec2.mode !== "text") throw configRuntimeError("The task does not support text generation.", ["text task"], [`task: ${task}`]);
2478
- const route = this.resolve(task);
2479
- const adapter = this.adapter(route);
2480
- return this.tracked(route.info, () => adapter.generateText({ ...request2, maxOutputTokens: spec2.maxOutputTokens }));
2531
+ return this.withModerationFallback(task, request2.signal, (adapter) => adapter.generateText({ ...request2, maxOutputTokens: spec2.maxOutputTokens }));
2481
2532
  }
2482
2533
  async generateTool(task, request2) {
2483
2534
  const spec2 = LLM_TASK_REGISTRY[task];
2484
2535
  if (spec2.mode !== "tool") throw configRuntimeError("The task does not support tool generation.", ["tool task"], [`task: ${task}`]);
2485
- const route = this.resolve(task);
2486
- const adapter = this.adapter(route);
2487
- return this.tracked(route.info, () => adapter.generateTool({ ...request2, maxOutputTokens: spec2.maxOutputTokens }));
2536
+ return this.withModerationFallback(task, request2.signal, (adapter) => adapter.generateTool({ ...request2, maxOutputTokens: spec2.maxOutputTokens }));
2488
2537
  }
2489
2538
  };
2490
2539
  function createLlmExecutor(explicitConfigPath, options) {