@lingjingai/scriptctl 0.63.0 → 0.63.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.
@@ -0,0 +1,15 @@
1
+ # 0.63.1
2
+
3
+ ## Added
4
+ - None.
5
+
6
+ ## Changed
7
+ - `scriptctl ingest` 默认视频路由使用官方 `gemini-3.8-flash → gemini-3.7-flash → 豆包`,暂时清空默认次卡候选列表;显式指定次卡仍可使用。
8
+
9
+ ## Fixed
10
+ - Gemini 次卡渠道(`-c1`、`-c2`、`-c3` 等)的视频结构化输出通过 prompt 携带完整字段定义,并支持从附带说明或代码块的响应中提取 JSON;官方渠道继续使用原生 JSON Schema。
11
+ - `@lingjingai/screenplay` publishes its `sc` executable and defaults to public npm access.
12
+ - `scriptctl ingest` now requires every video shot and voice to include a non-empty `MM:SS` timestamp in its structured model output.
13
+
14
+ ## Breaking
15
+ - None.
@@ -0,0 +1,4 @@
1
+ # 0.63.2
2
+
3
+ - Gemini 视频请求固定使用配置的模型与服务,在当前路由内有限重试,失败时直接返回原因。
4
+ - 修复内置 Mastra 审核的 Google API 地址拼接,服务根地址和 /v1beta 地址均可正确请求。
@@ -7,9 +7,7 @@
7
7
  - None.
8
8
 
9
9
  ## Fixed
10
- - Gemini 次卡渠道(`-c1`、`-c2`、`-c3` 等)的视频结构化输出通过 prompt 携带完整字段定义,并支持从附带说明或代码块的响应中提取 JSON;官方渠道继续使用原生 JSON Schema。
11
- - `@lingjingai/screenplay` publishes its `sc` executable and defaults to public npm access.
12
- - `scriptctl ingest` now requires every video shot and voice to include a non-empty `MM:SS` timestamp in its structured model output.
10
+ - None.
13
11
 
14
12
  ## Breaking
15
13
  - None.
package/dist/bin.js CHANGED
@@ -265,23 +265,6 @@ 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
- }
285
268
  function formatFailure(failure) {
286
269
  const route = failure.provider || failure.model ? `${failure.provider ?? "provider"}/${failure.model ?? "model"} ` : "";
287
270
  const reason = failure.provider_reason ? ` provider_reason=${failure.provider_reason}` : "";
@@ -6539,7 +6522,7 @@ var BUILTIN_LLM_CONFIG = {
6539
6522
  // reliably than grok did (skeleton leads split correctly, no paren-polluted
6540
6523
  // names), which markdown/text mode could not hold on this model.
6541
6524
  "ingest-normalize": { provider: "gw", model: "claude-sonnet-4-6-aihubmix", temperature: 0 },
6542
- "ingest-video": { provider: "google-gemini", model: "gemini-3.7-flash" },
6525
+ "ingest-video": { provider: "google-gemini", model: "gemini-3.8-flash" },
6543
6526
  // `scriptctl review` names this profile directly rather than through a
6544
6527
  // task id: it drives its own agent loop instead of the router, and one
6545
6528
  // Gemini credential serves both the loop and its video checks.
@@ -7794,15 +7777,6 @@ var OpenAiCompatibleModel = class {
7794
7777
  };
7795
7778
 
7796
7779
  // src/llm/router.ts
7797
- var VIDEO_FALLBACK_MODELS = ["gemini-3.7-flash"];
7798
- var PROXY_GEMINI_MODELS = [
7799
- "gemini-3.7-flash-c3",
7800
- "gemini-3.6-flash-c3",
7801
- "gemini-3.5-flash-c3",
7802
- "gemini-3.1-pro-preview-think-c3"
7803
- ];
7804
- var PROXY_GEMINI_API_KEY_ENVS = ["GEMINI_PROXY_API_KEY", "REDRAW_GEMINI_PROXY_API_KEY"];
7805
- var PROXY_GEMINI_BASE_URL_ENVS = ["GEMINI_PROXY_BASE_URL", "REDRAW_GEMINI_PROXY_BASE_URL"];
7806
7780
  var DOUBAO_FALLBACK = {
7807
7781
  model: "ep-20260721150136-98dj9",
7808
7782
  modelEnv: "ARK_MODEL",
@@ -7842,26 +7816,6 @@ function credentialError(provider) {
7842
7816
  data: { expected: ["credential environment variable referenced by the selected provider"], actual: [`provider: ${provider}`, "credential: missing"] }
7843
7817
  });
7844
7818
  }
7845
- function routedFailure(error, route) {
7846
- if (error instanceof LlmTransportError) {
7847
- return createOperationalFailure(classificationOf(error.failure), {
7848
- ...contextOf(error.failure),
7849
- provider: route.provider,
7850
- model: route.model,
7851
- retryable: error.failure.retryable,
7852
- message: error.failure.message
7853
- });
7854
- }
7855
- return createOperationalFailure(
7856
- { failure_type: "unknown", failure_code: "unknown" },
7857
- {
7858
- provider: route.provider,
7859
- model: route.model,
7860
- retryable: false,
7861
- message: error instanceof Error ? error.message : String(error)
7862
- }
7863
- );
7864
- }
7865
7819
  var ModelRouter = class {
7866
7820
  constructor(config, options = {}) {
7867
7821
  this.config = config;
@@ -8032,52 +7986,6 @@ var ModelRouter = class {
8032
7986
  throw error;
8033
7987
  }
8034
7988
  }
8035
- videoRoute(route, model) {
8036
- const modelConfig = model === route.info.model ? route.modelConfig : { ...route.modelConfig, model };
8037
- return {
8038
- ...route.info,
8039
- model,
8040
- fingerprint: routeFingerprint(route.info.task, route.providerConfig, modelConfig)
8041
- };
8042
- }
8043
- resolvedVideoRoute(route, model) {
8044
- const modelConfig = model === route.info.model ? route.modelConfig : { ...route.modelConfig, model };
8045
- return {
8046
- info: this.videoRoute(route, model),
8047
- providerConfig: route.providerConfig,
8048
- modelConfig
8049
- };
8050
- }
8051
- proxyVideoRoutes(task) {
8052
- if (this.options.videoProvider) return [];
8053
- const apiKeyEnv = PROXY_GEMINI_API_KEY_ENVS.find((name) => (process.env[name] ?? "").trim());
8054
- const baseUrlEnv = PROXY_GEMINI_BASE_URL_ENVS.find((name) => (process.env[name] ?? "").trim());
8055
- if (!apiKeyEnv || !baseUrlEnv) return [];
8056
- const baseUrl = normalizeProviderBaseUrl(process.env[baseUrlEnv], baseUrlEnv);
8057
- return PROXY_GEMINI_MODELS.map((model, index) => {
8058
- const providerConfig = {
8059
- protocol: "google-gemini",
8060
- baseUrl,
8061
- apiKeyEnv,
8062
- timeoutMs: 9e4,
8063
- maxAttempts: index < 2 ? 2 : 1
8064
- };
8065
- const modelConfig = { provider: "gemini-proxy", model };
8066
- return {
8067
- info: {
8068
- task,
8069
- profile: "gemini-proxy",
8070
- provider: "gemini-proxy",
8071
- protocol: "google-gemini",
8072
- baseUrl,
8073
- model,
8074
- fingerprint: routeFingerprint(task, providerConfig, modelConfig)
8075
- },
8076
- providerConfig,
8077
- modelConfig
8078
- };
8079
- });
8080
- }
8081
7989
  resolveDoubao(task, explicit) {
8082
7990
  const overrides = explicit ? this.options.videoProvider : void 0;
8083
7991
  const modelName = explicit ? (overrides?.model ?? process.env["ARK_MODEL"] ?? "").trim() : this.doubaoModel();
@@ -8119,9 +8027,7 @@ var ModelRouter = class {
8119
8027
  this.adapters.set(key, adapter);
8120
8028
  return adapter;
8121
8029
  }
8122
- // model defaults to the route's model; pass an override to build a peer adapter
8123
- // that shares the provider/credential but targets a fallback model.
8124
- videoAdapter(route, model) {
8030
+ videoAdapter(route) {
8125
8031
  if (route.providerConfig.protocol !== "google-gemini") {
8126
8032
  throw configRuntimeError(
8127
8033
  "Video tasks require a google-gemini provider.",
@@ -8129,14 +8035,13 @@ var ModelRouter = class {
8129
8035
  [`provider: ${route.info.provider}`, `protocol: ${route.providerConfig.protocol}`]
8130
8036
  );
8131
8037
  }
8132
- const modelName = model ?? route.info.model;
8038
+ const modelName = route.info.model;
8133
8039
  const key = `${route.info.provider} ${route.providerConfig.baseUrl ?? "official"} ${modelName}`;
8134
8040
  const cached2 = this.videoAdapters.get(key);
8135
8041
  if (cached2) return cached2;
8136
8042
  const apiKey = this.videoCredential(route);
8137
8043
  if (!apiKey) throw credentialError(route.info.provider);
8138
- const modelConfig = model ? { ...route.modelConfig, model } : route.modelConfig;
8139
- const adapter = new GeminiVideoModel(route.providerConfig, modelConfig, apiKey);
8044
+ const adapter = new GeminiVideoModel(route.providerConfig, route.modelConfig, apiKey);
8140
8045
  this.videoAdapters.set(key, adapter);
8141
8046
  return adapter;
8142
8047
  }
@@ -8151,8 +8056,8 @@ var ModelRouter = class {
8151
8056
  if (spec2.mode !== "video") throw configRuntimeError("The task does not support video generation.", ["video task"], [`task: ${task}`]);
8152
8057
  const route = this.resolve(task);
8153
8058
  if (this.options.videoProvider?.provider === "doubao") {
8154
- const adapter = this.doubaoAdapter(route);
8155
- if (!adapter) throw credentialError("doubao");
8059
+ const adapter2 = this.doubaoAdapter(route);
8060
+ if (!adapter2) throw credentialError("doubao");
8156
8061
  return {
8157
8062
  prepare: async (localPath, mimeType, mode, signal) => {
8158
8063
  if (mode !== "file-api") {
@@ -8167,7 +8072,7 @@ var ModelRouter = class {
8167
8072
  }
8168
8073
  request2.signal?.throwIfAborted();
8169
8074
  const { path: localPath, mimeType: localMime } = request2.media;
8170
- return this.tracked(route.info, () => adapter.run({
8075
+ return this.tracked(route.info, () => adapter2.run({
8171
8076
  localPath,
8172
8077
  localMime,
8173
8078
  user: request2.user,
@@ -8189,88 +8094,17 @@ var ModelRouter = class {
8189
8094
  [`provider: ${route.info.provider}`, `protocol: ${route.providerConfig.protocol}`]
8190
8095
  );
8191
8096
  }
8192
- const officialRoutes = (this.options.videoProvider ? [route.info.model] : [route.info.model, ...VIDEO_FALLBACK_MODELS.filter((m) => m !== route.info.model)]).map((model) => this.resolvedVideoRoute(route, model));
8193
- const proxyRoutes = this.proxyVideoRoutes(task);
8194
- if (this.options.videoProvider || proxyRoutes.length === 0) void this.videoAdapter(route);
8097
+ const adapter = this.videoAdapter(route);
8195
8098
  return {
8196
- prepare: async (localPath, mimeType, mode, signal) => {
8197
- const prepareRoute = mode === "base64" && proxyRoutes.length > 0 ? proxyRoutes[0] : route;
8198
- try {
8199
- return await this.videoAdapter(prepareRoute).prepare(localPath, mimeType, mode, signal);
8200
- } catch (error) {
8201
- if (signal?.aborted || mode !== "file-api" || this.options.videoProvider || !this.doubaoAdapter()) throw error;
8202
- const failure = routedFailure(error, route.info);
8203
- process.stderr.write(`\u21AA\uFE0F Gemini \u6587\u4EF6\u4E0A\u4F20\u5931\u8D25,\u6539\u7531\u8C46\u5305\u8BFB\u53D6\u672C\u5730\u89C6\u9891 (${formatFailure(failure)})
8204
- `);
8205
- return { kind: "local-fallback", path: localPath, mimeType, primaryFailure: failure };
8206
- }
8207
- },
8208
- release: (media, signal) => media.kind === "base64" || media.kind === "local-fallback" ? Promise.resolve() : this.videoAdapter(route).release(media, signal),
8209
- generate: async (request2) => {
8210
- const failures = request2.media.kind === "local-fallback" ? [request2.media.primaryFailure] : [];
8211
- const skipped = [];
8212
- const geminiChain = request2.media.kind === "local-fallback" ? [] : request2.media.kind === "base64" ? [...proxyRoutes, ...officialRoutes] : officialRoutes;
8213
- for (const [index, attemptRoute] of geminiChain.entries()) {
8214
- try {
8215
- const response = await this.tracked(
8216
- attemptRoute.info,
8217
- () => this.videoAdapter(attemptRoute).generate({ ...request2, maxOutputTokens: spec2.maxOutputTokens })
8218
- );
8219
- if (index > 0) process.stderr.write(`\u21AA\uFE0F \u5DF2\u5207\u6362\u5230\u5907\u7528\u6A21\u578B ${attemptRoute.info.model}
8220
- `);
8221
- return response;
8222
- } catch (error) {
8223
- if (request2.signal?.aborted) throw error;
8224
- const failure = routedFailure(error, attemptRoute.info);
8225
- failures.push(failure);
8226
- const detail = formatFailure(failure);
8227
- if (index < geminiChain.length - 1) {
8228
- process.stderr.write(`\u21AA\uFE0F ${attemptRoute.info.model} \u5F53\u524D\u4E0D\u53EF\u7528\uFF0C\u5207\u6362\u4E0B\u4E00\u7EBF\u8DEF (${detail})
8229
- `);
8230
- }
8231
- }
8232
- }
8233
- const doubao = this.options.videoProvider ? null : this.doubaoAdapter();
8234
- const localPath = request2.media.kind === "local-fallback" ? request2.media.path : request2.localPath;
8235
- if (doubao && localPath) {
8236
- const attemptRoute = this.doubaoRoute(task);
8237
- try {
8238
- process.stderr.write(`\u21AA\uFE0F Gemini \u5168\u90E8\u5931\u8D25,\u56DE\u9000\u5230\u8C46\u5305 ${this.doubaoModel()}
8239
- `);
8240
- const response = await this.tracked(attemptRoute, () => doubao.run({
8241
- localPath,
8242
- localMime: request2.media.kind === "local-fallback" ? request2.media.mimeType : request2.localMime ?? "video/mp4",
8243
- user: request2.user,
8244
- fps: request2.fps,
8245
- maxOutputTokens: spec2.maxOutputTokens,
8246
- ...request2.system ? { system: request2.system } : {},
8247
- ...request2.responseJsonSchema ? { responseJsonSchema: request2.responseJsonSchema } : {},
8248
- ...request2.signal ? { signal: request2.signal } : {}
8249
- }));
8250
- return response;
8251
- } catch (error) {
8252
- if (request2.signal?.aborted) throw error;
8253
- failures.push(routedFailure(error, attemptRoute));
8254
- }
8255
- } else if (!doubao) {
8256
- if (!this.options.videoProvider) {
8257
- skipped.push("doubao: skipped (ARK_API_KEY not set)");
8258
- }
8259
- } else {
8260
- skipped.push("doubao: skipped (no local file to re-upload)");
8261
- }
8262
- const aggregate = aggregateFailures(failures, "All configured video model attempts failed.");
8263
- throw new LlmTransportError("request_failed", aggregate.message, {
8264
- retryable: aggregate.retryable,
8265
- classification: classificationOf(aggregate),
8266
- context: contextOf(aggregate),
8267
- modelFailures: failures,
8268
- details: [...failures.map(formatFailure), ...skipped]
8269
- });
8270
- }
8099
+ prepare: (localPath, mimeType, mode, signal) => adapter.prepare(localPath, mimeType, mode, signal),
8100
+ release: (media, signal) => adapter.release(media, signal),
8101
+ generate: (request2) => this.tracked(route.info, () => adapter.generate({
8102
+ ...request2,
8103
+ maxOutputTokens: spec2.maxOutputTokens
8104
+ }))
8271
8105
  };
8272
8106
  }
8273
- // Resolve an explicit Doubao route or the default fallback; absent fallback credentials skip it.
8107
+ // Resolve explicitly selected Doubao video.
8274
8108
  // Overridable in tests to inject a fake Ark client.
8275
8109
  doubaoAdapter(route) {
8276
8110
  return this.arkAdapter(route);