pi-distill 1.4.0 → 1.5.0

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 CHANGED
@@ -110,7 +110,7 @@ Agent consumes a result suited to the current decision, with auditable diagnosti
110
110
  2. The `tool_call` handler captures the parameter and removes it before forwarding the call, so the underlying tool never receives the extension-only field.
111
111
  3. The `tool_result` handler sees the actual output and decides what to do; it does not rely on the agent predicting the output size.
112
112
  4. Every tool call must include a non-empty `outputRequest`. A prompt containing only `RAW` explicitly requests the original. Any other non-empty prompt permits distillation once the configured threshold is reached.
113
- 5. A timed-out attempt is retried according to `timeoutRetryCount`, while other failures use `errorRetryCount` (both default to one retry). If all attempts fail, no model is available, or compression is ineffective, the original facts are retained and the status is exposed through details and the audit card. JSON responses wrapped in Markdown fences such as `````json … ````` are also accepted.
113
+ 5. OpenAI-compatible completion requests enable native JSON mode with `response_format: { "type": "json_object" }`; OpenAI Responses-compatible requests use the equivalent `text.format`. A timed-out attempt is retried according to `timeoutRetryCount`, while other failures use `errorRetryCount` (both default to one retry). If all attempts fail, no model is available, or compression is ineffective, the original facts are retained and the status is exposed through details and the audit card. JSON responses wrapped in Markdown fences such as `````json … ````` are also accepted.
114
114
 
115
115
  ## Output contract
116
116
 
package/README.zh-CN.md CHANGED
@@ -112,7 +112,7 @@ Agent 消费更适合当前决策的结果,并获得可审计的处理诊断
112
112
  2. `tool_call` 事件捕获这个参数,并在交给底层工具前移除它,因此原工具不会收到扩展专用字段。
113
113
  3. `tool_result` 事件拿到真实输出后再做判断,不依赖 Agent 对输出长度的预测。
114
114
  4. 每次工具调用都必须包含非空的 `outputRequest`;严格的 `RAW` 表示明确要求原文;其他非空 prompt 才允许进入提炼流程。
115
- 5. 单次提炼超时后按照 `timeoutRetryCount` 重试,其他异常按照 `errorRetryCount` 重试(两者默认都重试 1 次);全部尝试失败、没有可用模型或结果收益过低时,扩展保留原始事实,并通过 details 和审计卡片暴露状态;模型用 Markdown 的 JSON 代码围栏(如 `````json … `````)包裹响应时也会兼容解析。
115
+ 5. OpenAI-compatible Completions 提炼请求会通过 `response_format: { "type": "json_object" }` 启用原生 JSON 模式;OpenAI Responses-compatible 请求使用等价的 `text.format`。单次提炼超时后按照 `timeoutRetryCount` 重试,其他异常按照 `errorRetryCount` 重试(两者默认都重试 1 次);全部尝试失败、没有可用模型或结果收益过低时,扩展保留原始事实,并通过 details 和审计卡片暴露状态;模型用 Markdown 的 JSON 代码围栏(如 `````json … `````)包裹响应时也会兼容解析。
116
116
 
117
117
  ## 输出处理契约
118
118
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-distill",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Pi tool-output distillation with file-first configuration",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
package/src/index.ts CHANGED
@@ -152,8 +152,44 @@ type SummaryResult = {
152
152
  };
153
153
 
154
154
  type SummaryCompletion = (...args: Parameters<typeof complete>) => ReturnType<typeof complete>;
155
+ type SummaryCompletionModel = Parameters<SummaryCompletion>[0];
156
+ type SummaryCompletionOptions = Parameters<SummaryCompletion>[2];
155
157
  type DistillWarningReporter = (message: string) => void;
156
158
 
159
+ const OPENAI_RESPONSES_APIS = new Set([
160
+ "openai-responses",
161
+ "openai-codex-responses",
162
+ "azure-openai-responses",
163
+ ]);
164
+
165
+ function isObjectRecord(value: unknown): value is Record<string, unknown> {
166
+ return typeof value === "object" && value !== null && !Array.isArray(value);
167
+ }
168
+
169
+ /** Enforce JSON mode on OpenAI-compatible summary requests without breaking other APIs. */
170
+ function addSummaryJsonResponseFormat(payload: unknown, model: SummaryCompletionModel): unknown {
171
+ if (!isObjectRecord(payload)) return undefined;
172
+
173
+ if (model.api === "openai-completions") {
174
+ return {
175
+ ...payload,
176
+ response_format: { type: "json_object" },
177
+ };
178
+ }
179
+
180
+ if (OPENAI_RESPONSES_APIS.has(model.api)) {
181
+ return {
182
+ ...payload,
183
+ text: {
184
+ ...(isObjectRecord(payload.text) ? payload.text : {}),
185
+ format: { type: "json_object" },
186
+ },
187
+ };
188
+ }
189
+
190
+ return undefined;
191
+ }
192
+
157
193
  class SummaryAttemptError extends Error {
158
194
  constructor(message: string, readonly usage?: SummaryUsage) {
159
195
  super(message);
@@ -684,6 +720,14 @@ async function summarizeOutput(
684
720
  const auth = await context.ctx.modelRegistry.getApiKeyAndHeaders(model);
685
721
  if (auth.ok === false) throw new Error(`Summarizer authentication failed: ${auth.error}`);
686
722
 
723
+ const completionOptions = {
724
+ apiKey: auth.apiKey,
725
+ headers: auth.headers,
726
+ env: auth.env,
727
+ maxTokens: Math.max(256, Math.ceil(config.maxChars / 2)),
728
+ onPayload: addSummaryJsonResponseFormat,
729
+ signal,
730
+ } satisfies SummaryCompletionOptions;
687
731
  const response = await completion(
688
732
  model,
689
733
  {
@@ -702,13 +746,7 @@ async function summarizeOutput(
702
746
  },
703
747
  ],
704
748
  },
705
- {
706
- apiKey: auth.apiKey,
707
- headers: auth.headers,
708
- env: auth.env,
709
- maxTokens: Math.max(256, Math.ceil(config.maxChars / 2)),
710
- signal,
711
- },
749
+ completionOptions,
712
750
  );
713
751
 
714
752
  const usage = normalizeSummaryUsage(response.usage);