add deferred tools support for kimi in openai-completions api

This commit is contained in:
David Brailovsky
2026-07-16 12:26:56 +00:00
parent 36db3fa385
commit f16b4e0cda
10 changed files with 223 additions and 6 deletions
+1
View File
@@ -449,6 +449,7 @@ For providers with partial OpenAI compatibility, use the `compat` field.
| `sendSessionAffinityHeaders` | For `openai-completions`, send session-affinity headers from the session id when caching is enabled. Default: `false`. |
| `sessionAffinityFormat` | For `openai-completions` and `openai-responses`, the session-affinity header format: `openai` sends `session_id`/`x-client-request-id` (completions also `x-session-affinity`), `openai-nosession` omits the underscore-containing `session_id` header, `openrouter` sends `x-session-id`. Does not affect the `prompt_cache_key` body param. Default: auto-detected. |
| `supportsStrictMode` | Include the `strict` field in tool definitions |
| `deferredToolsMode` | Use provider-specific deferred tool serialization. Currently only `"kimi"` is supported for Kimi's OpenAI-compatible Chat Completions format. |
| `supportsLongCacheRetention` | Whether the provider accepts long cache retention when cache retention is `long`: `prompt_cache_retention: "24h"` for OpenAI prompt caching, or `cache_control.ttl: "1h"` when `cacheControlFormat` is `anthropic`. Default: `true`. |
| `openRouterRouting` | OpenRouter provider routing preferences. This object is sent as-is in the `provider` field of the [OpenRouter API request](https://openrouter.ai/docs/guides/routing/provider-selection). |
| `vercelGatewayRouting` | Vercel AI Gateway routing config for provider selection (`only`, `order`) |
@@ -36,6 +36,7 @@ cp permission-gate.ts ~/.pi/agent/extensions/
| `questionnaire.ts` | Multi-question input with tab bar navigation between questions |
| `tool-override.ts` | Override built-in tools (e.g., add logging/access control to `read`) |
| `dynamic-tools.ts` | Register tools after startup (`session_start`) and at runtime via command, with prompt snippets and tool-specific prompt guidelines |
| `kimi-deferred-tools.ts` | Search for and progressively activate tools for Kimi's deferred-tool loading protocol |
| `structured-output.ts` | Final structured-output tool that returns `terminate: true` so the agent can end on the tool call |
| `built-in-tool-renderer.ts` | Custom compact rendering for built-in tools (read, bash, edit, write) while keeping original behavior |
| `minimal-mode.ts` | Override built-in tool rendering for minimal display (only tool calls, no output in collapsed mode) |
@@ -0,0 +1,61 @@
/**
* Minimal Kimi deferred-tool loading demo.
*
* pi -e ./kimi-deferred-tools.ts
* example prompt: Use the available tools to calculate 100 + 500. Do not calculate it yourself.
*/
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";
function calculate(_expr: string): string {
return "42";
}
export default function (pi: ExtensionAPI): void {
pi.registerTool({
name: "Calculator",
label: "Calculator",
description: "Evaluate a simple arithmetic expression.",
parameters: Type.Object({
expr: Type.String({ description: "An expression such as 100 + 500" }),
}),
async execute(_toolCallId, params) {
return {
content: [{ type: "text", text: calculate(params.expr) }],
details: {},
};
},
});
pi.registerTool({
name: "tool_search",
label: "Tool Search",
description: "Find and activate tools for a capability.",
promptSnippet: "Search for additional tools when the active tools cannot perform the task",
parameters: Type.Object({
query: Type.String({ description: "Capability to search for" }),
}),
async execute(_toolCallId, params) {
if (!params.query.toLowerCase().includes("calc")) {
return {
content: [{ type: "text", text: "The relevant tools do not exist." }],
details: { matches: [], added: [] },
};
}
const active = pi.getActiveTools();
const added = active.includes("Calculator") ? [] : ["Calculator"];
if (added.length > 0) pi.setActiveTools([...active, ...added]);
return {
content: [{ type: "text", text: "Success. Found 1 matching tool(s)" }],
details: { matches: ["Calculator"], added },
};
},
});
pi.on("session_start", () => {
pi.setActiveTools(["tool_search"]);
});
}
@@ -99,6 +99,7 @@ const OpenAICompletionsCompatSchema = Type.Object({
vercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),
supportsStrictMode: Type.Optional(Type.Boolean()),
sendSessionAffinityHeaders: Type.Optional(Type.Boolean()),
deferredToolsMode: Type.Optional(Type.Literal("kimi")),
sessionAffinityFormat: Type.Optional(
Type.Union([Type.Literal("openai"), Type.Literal("openai-nosession"), Type.Literal("openrouter")]),
),