@zqagent/core 0.3.11 → 0.3.13

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.
Files changed (33) hide show
  1. package/dist/{TurnEngine-DtGEuVLT.d.ts → TurnEngine-BZjUGic_.d.ts} +34 -4
  2. package/dist/{TurnEngine-BPKhWuSI.d.cts → TurnEngine-CMGdcSdp.d.cts} +34 -4
  3. package/dist/index.cjs +69 -16
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +7 -2
  6. package/dist/index.d.ts +7 -2
  7. package/dist/index.js +69 -16
  8. package/dist/index.js.map +1 -1
  9. package/dist/internal/engine/FileLogHook.d.cts +2 -1
  10. package/dist/internal/engine/FileLogHook.d.ts +2 -1
  11. package/dist/internal/llmConfig.cjs +30 -5
  12. package/dist/internal/llmConfig.cjs.map +1 -1
  13. package/dist/internal/llmConfig.d.cts +32 -1
  14. package/dist/internal/llmConfig.d.ts +32 -1
  15. package/dist/internal/llmConfig.js +30 -5
  16. package/dist/internal/llmConfig.js.map +1 -1
  17. package/dist/internal/providers/diagnostics.cjs +22 -2
  18. package/dist/internal/providers/diagnostics.cjs.map +1 -1
  19. package/dist/internal/providers/diagnostics.d.cts +25 -3
  20. package/dist/internal/providers/diagnostics.d.ts +25 -3
  21. package/dist/internal/providers/diagnostics.js +19 -2
  22. package/dist/internal/providers/diagnostics.js.map +1 -1
  23. package/dist/internal/providers/models.cjs.map +1 -1
  24. package/dist/internal/providers/models.js.map +1 -1
  25. package/dist/internal/update/controller.cjs +1 -1
  26. package/dist/internal/update/controller.js +1 -1
  27. package/dist/internal/update/platform.cjs +1 -1
  28. package/dist/internal/update/platform.js +1 -1
  29. package/dist/tools/explore.cjs.map +1 -1
  30. package/dist/tools/explore.d.cts +2 -1
  31. package/dist/tools/explore.d.ts +2 -1
  32. package/dist/tools/explore.js.map +1 -1
  33. package/package.json +4 -3
@@ -1,5 +1,6 @@
1
1
  import { T as TokenUsage, M as Message, a as ToolSchema, R as ReasoningEffort, S as StreamChunk, b as MessageAttachment, c as StoredAttachment } from './types-BYyXWWPW.js';
2
2
  import { a as ToolMetadata, T as ToolRegistry } from './ToolRegistry-Cy0fQxrb.js';
3
+ import { ApiMode } from './internal/providers/diagnostics.js';
3
4
  import { ChatCompletionMessageParam } from 'openai/resources/chat/completions.js';
4
5
  import { RequestOptions } from 'openai/core.mjs';
5
6
  import { ModelCapability } from '@zqagent/shared/model-capabilities';
@@ -344,22 +345,51 @@ declare class OpenAIProvider implements ProviderClient {
344
345
  private _logDir?;
345
346
  /** 服务端点 baseURL(用于识别 DeepSeek 等特定供应商) */
346
347
  private _baseURL;
347
- /** 模型 → API 模式缓存:某模型不支持 Responses 后,后续直走 Chat */
348
- private _modeByModel;
348
+ /**
349
+ * 本实例的 API 模式。
350
+ *
351
+ * 显式配置 > 全局 `OPENAI_FORCE_API_MODE` > auto(先 Responses 再降级)。
352
+ * 早先只有全局环境变量一种表达方式,一个节点挂两家供应商时无法各自指定。
353
+ */
354
+ private _apiMode;
349
355
  constructor(opts: {
356
+ /**
357
+ * API Key。
358
+ *
359
+ * - 显式 `""`(含纯空白):**明确表示该端点不需要鉴权**(本地 Ollama /
360
+ * llama.cpp、内网网关),不会回落到环境变量,SDK 收到占位 Key。
361
+ * - `undefined`:回落到 `OPENAI_API_KEY`;两者都没有时抛错(多数情况是
362
+ * 真的没配,早点给出可读提示好过让用户对着 401 猜)。
363
+ *
364
+ * 调用方不要用「配置里 Key 为空」来推断无需鉴权:那也可能只是还没填,
365
+ * 见 `LlmProviderEntry.noApiKey`。
366
+ */
350
367
  apiKey?: string;
351
368
  baseURL?: string;
352
369
  /** LLM 请求日志根目录,按日期分目录,不传则不记录 */
353
370
  logDir?: string;
371
+ /**
372
+ * 单次请求超时(毫秒)。缺省/非法时回落到 `OPENAI_TIMEOUT_MS`,再回落 10 分钟。
373
+ *
374
+ * 必须能按供应商设置:推理模型的合理预算与普通模型差一个数量级,共用一个全局值
375
+ * 只能二选一(慢模型被掐断,或快模型失败时干等十分钟)。
376
+ */
377
+ timeoutMs?: number | null;
378
+ /**
379
+ * API 模式(auto / responses / chat)。缺省时读 `OPENAI_FORCE_API_MODE`,再缺省 auto。
380
+ */
381
+ apiMode?: ApiMode | null;
354
382
  });
355
383
  /**
356
384
  * 解析指定模型的 API 模式。
357
385
  *
358
- * 优先级:环境变量 OPENAI_FORCE_API_MODE(responses|chat)强制指定
359
- * > 运行时降级缓存(该模型曾因"不支持"失败而缓存为 chat)
386
+ * 优先级:本实例的显式配置(responses|chat)
387
+ * > 运行时降级缓存(该端点+模型曾因"不支持"失败而缓存为 chat)
360
388
  * > 默认 responses(Responses 优先)。
361
389
  */
362
390
  private _resolveMode;
391
+ /** 记录「该端点+模型不支持 Responses」,后续同进程内的请求直走 Chat */
392
+ private _markChatMode;
363
393
  /**
364
394
  * Responses 失败的分类(决定「能不能改走 Chat Completions」以及要不要缓存降级)。
365
395
  *
@@ -1,5 +1,6 @@
1
1
  import { T as TokenUsage, M as Message, a as ToolSchema, R as ReasoningEffort, S as StreamChunk, b as MessageAttachment, c as StoredAttachment } from './types-BYyXWWPW.cjs';
2
2
  import { a as ToolMetadata, T as ToolRegistry } from './ToolRegistry-DouDvn9E.cjs';
3
+ import { ApiMode } from './internal/providers/diagnostics.cjs';
3
4
  import { ChatCompletionMessageParam } from 'openai/resources/chat/completions.js';
4
5
  import { RequestOptions } from 'openai/core.mjs';
5
6
  import { ModelCapability } from '@zqagent/shared/model-capabilities';
@@ -344,22 +345,51 @@ declare class OpenAIProvider implements ProviderClient {
344
345
  private _logDir?;
345
346
  /** 服务端点 baseURL(用于识别 DeepSeek 等特定供应商) */
346
347
  private _baseURL;
347
- /** 模型 → API 模式缓存:某模型不支持 Responses 后,后续直走 Chat */
348
- private _modeByModel;
348
+ /**
349
+ * 本实例的 API 模式。
350
+ *
351
+ * 显式配置 > 全局 `OPENAI_FORCE_API_MODE` > auto(先 Responses 再降级)。
352
+ * 早先只有全局环境变量一种表达方式,一个节点挂两家供应商时无法各自指定。
353
+ */
354
+ private _apiMode;
349
355
  constructor(opts: {
356
+ /**
357
+ * API Key。
358
+ *
359
+ * - 显式 `""`(含纯空白):**明确表示该端点不需要鉴权**(本地 Ollama /
360
+ * llama.cpp、内网网关),不会回落到环境变量,SDK 收到占位 Key。
361
+ * - `undefined`:回落到 `OPENAI_API_KEY`;两者都没有时抛错(多数情况是
362
+ * 真的没配,早点给出可读提示好过让用户对着 401 猜)。
363
+ *
364
+ * 调用方不要用「配置里 Key 为空」来推断无需鉴权:那也可能只是还没填,
365
+ * 见 `LlmProviderEntry.noApiKey`。
366
+ */
350
367
  apiKey?: string;
351
368
  baseURL?: string;
352
369
  /** LLM 请求日志根目录,按日期分目录,不传则不记录 */
353
370
  logDir?: string;
371
+ /**
372
+ * 单次请求超时(毫秒)。缺省/非法时回落到 `OPENAI_TIMEOUT_MS`,再回落 10 分钟。
373
+ *
374
+ * 必须能按供应商设置:推理模型的合理预算与普通模型差一个数量级,共用一个全局值
375
+ * 只能二选一(慢模型被掐断,或快模型失败时干等十分钟)。
376
+ */
377
+ timeoutMs?: number | null;
378
+ /**
379
+ * API 模式(auto / responses / chat)。缺省时读 `OPENAI_FORCE_API_MODE`,再缺省 auto。
380
+ */
381
+ apiMode?: ApiMode | null;
354
382
  });
355
383
  /**
356
384
  * 解析指定模型的 API 模式。
357
385
  *
358
- * 优先级:环境变量 OPENAI_FORCE_API_MODE(responses|chat)强制指定
359
- * > 运行时降级缓存(该模型曾因"不支持"失败而缓存为 chat)
386
+ * 优先级:本实例的显式配置(responses|chat)
387
+ * > 运行时降级缓存(该端点+模型曾因"不支持"失败而缓存为 chat)
360
388
  * > 默认 responses(Responses 优先)。
361
389
  */
362
390
  private _resolveMode;
391
+ /** 记录「该端点+模型不支持 Responses」,后续同进程内的请求直走 Chat */
392
+ private _markChatMode;
363
393
  /**
364
394
  * Responses 失败的分类(决定「能不能改走 Chat Completions」以及要不要缓存降级)。
365
395
  *
package/dist/index.cjs CHANGED
@@ -386,9 +386,23 @@ function paramErrorHints(message, code = "") {
386
386
  }
387
387
  return hints;
388
388
  }
389
- function llmTimeoutMs() {
389
+ var DEFAULT_LLM_TIMEOUT_MS = 6e5;
390
+ function normLlmTimeoutMs(v) {
391
+ if (v === null || v === void 0 || v === "") return null;
392
+ const n = Number(v);
393
+ if (!Number.isFinite(n) || n <= 0) return null;
394
+ return Math.min(Math.round(n), 30 * 6e4);
395
+ }
396
+ function llmTimeoutMs(explicit) {
397
+ const fromArg = normLlmTimeoutMs(explicit);
398
+ if (fromArg) return fromArg;
390
399
  const n = Number(process.env.OPENAI_TIMEOUT_MS);
391
- return Number.isFinite(n) && n > 0 ? n : 6e5;
400
+ return Number.isFinite(n) && n > 0 ? n : DEFAULT_LLM_TIMEOUT_MS;
401
+ }
402
+ function normApiMode(v) {
403
+ const s = typeof v === "string" ? v.trim().toLowerCase() : "";
404
+ if (s === "auto" || s === "responses" || s === "chat") return s;
405
+ return null;
392
406
  }
393
407
  function llmStreamIdleMs() {
394
408
  const n = Number(process.env.OPENAI_STREAM_IDLE_MS);
@@ -4615,6 +4629,32 @@ var ResponsesApiError = class extends Error {
4615
4629
  type;
4616
4630
  requestId;
4617
4631
  };
4632
+ var NO_AUTH_KEY = "not-needed";
4633
+ var degradedModes = /* @__PURE__ */ new Map();
4634
+ var DEGRADED_TTL_MS = 30 * 6e4;
4635
+ var DEGRADED_MAX = 500;
4636
+ function modeKey(baseURL, model) {
4637
+ return `${baseURL}::${model}`;
4638
+ }
4639
+ function isDegraded(baseURL, model) {
4640
+ const k = modeKey(baseURL, model);
4641
+ const hit = degradedModes.get(k);
4642
+ if (!hit) return false;
4643
+ if (Date.now() - hit.at > DEGRADED_TTL_MS) {
4644
+ degradedModes.delete(k);
4645
+ return false;
4646
+ }
4647
+ return true;
4648
+ }
4649
+ function markDegraded(baseURL, model) {
4650
+ degradedModes.delete(modeKey(baseURL, model));
4651
+ degradedModes.set(modeKey(baseURL, model), { at: Date.now() });
4652
+ while (degradedModes.size > DEGRADED_MAX) {
4653
+ const oldest = degradedModes.keys().next();
4654
+ if (oldest.done) break;
4655
+ degradedModes.delete(oldest.value);
4656
+ }
4657
+ }
4618
4658
  var OpenAIProvider = class {
4619
4659
  /** OpenAI SDK 客户端 */
4620
4660
  client;
@@ -4622,23 +4662,31 @@ var OpenAIProvider = class {
4622
4662
  _logDir;
4623
4663
  /** 服务端点 baseURL(用于识别 DeepSeek 等特定供应商) */
4624
4664
  _baseURL;
4625
- /** 模型 → API 模式缓存:某模型不支持 Responses 后,后续直走 Chat */
4626
- _modeByModel = /* @__PURE__ */ new Map();
4665
+ /**
4666
+ * 本实例的 API 模式。
4667
+ *
4668
+ * 显式配置 > 全局 `OPENAI_FORCE_API_MODE` > auto(先 Responses 再降级)。
4669
+ * 早先只有全局环境变量一种表达方式,一个节点挂两家供应商时无法各自指定。
4670
+ */
4671
+ _apiMode;
4627
4672
  constructor(opts) {
4673
+ const explicitKey = opts.apiKey !== void 0;
4628
4674
  const apiKey = cleanLlmValue(opts.apiKey ?? process.env.OPENAI_API_KEY);
4629
4675
  const baseURL = normalizeBaseURL(opts.baseURL ?? process.env.OPENAI_BASE_URL);
4630
- if (!apiKey) {
4676
+ if (!apiKey && !explicitKey) {
4631
4677
  throw new Error(sanitizeEmoji("\u274C \u672A\u8BBE\u7F6E OPENAI_API_KEY \u73AF\u5883\u53D8\u91CF"));
4632
4678
  }
4679
+ const effectiveKey = apiKey ?? NO_AUTH_KEY;
4633
4680
  this._baseURL = baseURL ?? "";
4681
+ this._apiMode = normApiMode(opts.apiMode) ?? normApiMode(process.env.OPENAI_FORCE_API_MODE) ?? "auto";
4634
4682
  const agent = socksAgent();
4635
4683
  const proxyHint = unsupportedProxyHint();
4636
4684
  if (proxyHint) console.warn(`[llm] ${proxyHint}`);
4637
4685
  const baseFetch = agent ? makeSocksFetch(agent) : ((input, init) => globalThis.fetch(input, init));
4638
4686
  this.client = new import_openai.default({
4639
- apiKey,
4687
+ apiKey: effectiveKey,
4640
4688
  ...baseURL ? { baseURL } : {},
4641
- timeout: llmTimeoutMs(),
4689
+ timeout: llmTimeoutMs(opts.timeoutMs),
4642
4690
  maxRetries: llmMaxRetries(),
4643
4691
  // 类型:SDK 期望 (RequestInfo, RequestInit) => Promise<Response>,
4644
4692
  // node-fetch 的 Response 与 DOM Response 结构不同,此处显式放宽
@@ -4655,14 +4703,17 @@ var OpenAIProvider = class {
4655
4703
  /**
4656
4704
  * 解析指定模型的 API 模式。
4657
4705
  *
4658
- * 优先级:环境变量 OPENAI_FORCE_API_MODE(responses|chat)强制指定
4659
- * > 运行时降级缓存(该模型曾因"不支持"失败而缓存为 chat)
4706
+ * 优先级:本实例的显式配置(responses|chat)
4707
+ * > 运行时降级缓存(该端点+模型曾因"不支持"失败而缓存为 chat)
4660
4708
  * > 默认 responses(Responses 优先)。
4661
4709
  */
4662
4710
  _resolveMode(model) {
4663
- const forced = process.env.OPENAI_FORCE_API_MODE?.toLowerCase();
4664
- if (forced === "responses" || forced === "chat") return forced;
4665
- return this._modeByModel.get(model) ?? "responses";
4711
+ if (this._apiMode !== "auto") return this._apiMode;
4712
+ return isDegraded(this._baseURL, model) ? "chat" : "responses";
4713
+ }
4714
+ /** 记录「该端点+模型不支持 Responses」,后续同进程内的请求直走 Chat */
4715
+ _markChatMode(model) {
4716
+ markDegraded(this._baseURL, model);
4666
4717
  }
4667
4718
  /**
4668
4719
  * Responses 失败的分类(决定「能不能改走 Chat Completions」以及要不要缓存降级)。
@@ -4738,7 +4789,7 @@ var OpenAIProvider = class {
4738
4789
  } catch (err) {
4739
4790
  const kind = this._responsesFailureKind(err, true);
4740
4791
  if (kind) {
4741
- if (kind === "endpoint") this._modeByModel.set(model, "chat");
4792
+ if (kind === "endpoint") this._markChatMode(model);
4742
4793
  console.warn(
4743
4794
  `[llm] ${model} Responses API \u4E0D\u53EF\u7528(${kind})\uFF0C\u6539\u7528 Chat Completions (${String(
4744
4795
  err?.message ?? err
@@ -4812,7 +4863,7 @@ var OpenAIProvider = class {
4812
4863
  } catch (err) {
4813
4864
  const kind = this._responsesFailureKind(err, true);
4814
4865
  if (kind) {
4815
- if (kind === "endpoint") this._modeByModel.set(model, "chat");
4866
+ if (kind === "endpoint") this._markChatMode(model);
4816
4867
  console.warn(
4817
4868
  `[llm] ${model} Responses API \u4E0D\u53EF\u7528(${kind})\uFF0C\u6539\u7528 Chat Completions (${String(
4818
4869
  err?.message ?? err
@@ -4858,7 +4909,7 @@ var OpenAIProvider = class {
4858
4909
  } catch (err) {
4859
4910
  const kind = emitted ? null : this._responsesFailureKind(err, false);
4860
4911
  if (!kind) throw enrichLlmError(err, this._baseURL);
4861
- if (kind === "endpoint") this._modeByModel.set(ctx.model, "chat");
4912
+ if (kind === "endpoint") this._markChatMode(ctx.model);
4862
4913
  console.warn(
4863
4914
  `[llm] ${ctx.model} Responses \u6D41\u5185\u5931\u8D25(${kind})\uFF0C\u6539\u7528 Chat Completions (${String(
4864
4915
  err?.message ?? err
@@ -13387,6 +13438,8 @@ function createDefaultEngine(opts) {
13387
13438
  mode = "interactive" /* INTERACTIVE */,
13388
13439
  maxIterations = DEFAULT_MAX_ITERATIONS,
13389
13440
  reasoningEffort,
13441
+ timeoutMs,
13442
+ apiMode,
13390
13443
  allowedCommands = [],
13391
13444
  shellPolicy = "approve",
13392
13445
  sandbox,
@@ -13407,7 +13460,7 @@ function createDefaultEngine(opts) {
13407
13460
  } = opts;
13408
13461
  const envEffort = process.env.OPENAI_REASONING_EFFORT?.toLowerCase();
13409
13462
  const effectiveEffort = reasoningEffort ?? (envEffort === "low" || envEffort === "medium" || envEffort === "high" ? envEffort : void 0);
13410
- const effectiveProvider = provider ?? new OpenAIProvider({ logDir });
13463
+ const effectiveProvider = provider ?? new OpenAIProvider({ logDir, timeoutMs, apiMode });
13411
13464
  const sandboxPlan = resolveSandbox({ ...sandbox, workspace });
13412
13465
  const registry = new ToolRegistry();
13413
13466
  const conflictGuard = new ConflictGuard();