@victor-software-house/pi-openai-proxy 4.9.0 → 4.9.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.
package/README.md CHANGED
@@ -113,17 +113,32 @@ curl http://localhost:4141/v1/chat/completions \
113
113
  | `stop` | Via passthrough |
114
114
  | `user` | Via passthrough |
115
115
  | `stream_options.include_usage` | Final usage chunk in SSE stream |
116
- | `tools` / `tool_choice` | JSON Schema -> TypeBox conversion (supported subset) |
116
+ | `tools` / `tool_choice` | JSON Schema -> TypeBox conversion (supported subset); `tool_choice` translated per-provider |
117
117
  | `tool_calls` in messages | Assistant tool call + tool result roundtrip |
118
+ | `parallel_tool_calls` | Forwarded to OpenAI/Codex; translated to `disable_parallel_tool_use` for Anthropic |
118
119
  | `reasoning_effort` | Maps to pi's `ThinkingLevel` (`none`, `minimal`, `low`, `medium`, `high`, `xhigh`) |
119
- | `response_format` | `text`, `json_object`, and `json_schema` via passthrough |
120
- | `top_p` | Via passthrough |
121
- | `frequency_penalty` | Via passthrough |
122
- | `presence_penalty` | Via passthrough |
123
- | `seed` | Via passthrough |
120
+ | `response_format` | `text`, `json_object`, and `json_schema` (OpenAI-compatible APIs only) |
121
+ | `top_p` | Supported by OpenAI, Anthropic (native), and Google (translated to `topP`) |
122
+ | `frequency_penalty` | OpenAI and Google only (translated to `frequencyPenalty`) |
123
+ | `presence_penalty` | OpenAI and Google only (translated to `presencePenalty`) |
124
+ | `seed` | OpenAI and Google only (translated to nested `generationConfig.seed`) |
125
+ | `stop` | Translated per-provider (`stop_sequences` for Anthropic, `stopSequences` for Google) |
126
+ | `metadata` | OpenAI passthrough; arbitrary keys silently skipped for other providers |
127
+ | `prediction` | OpenAI passthrough only (speculative decoding) |
124
128
 
125
129
  **Not supported:** `n > 1`, `logprobs`, `logit_bias`, remote image URLs (disabled by default).
126
130
 
131
+ ### Provider-aware field translation
132
+
133
+ Fields are translated to each provider's native format, not blindly forwarded:
134
+
135
+ - **OpenAI / Mistral**: All fields passed directly (same wire format)
136
+ - **Codex (Responses API)**: Only `tool_choice` and `parallel_tool_calls` (other fields rejected by API)
137
+ - **Anthropic**: `tool_choice` → `{ type }` format, `stop` → `stop_sequences`, `user` → `metadata.user_id`, `parallel_tool_calls: false` → `disable_parallel_tool_use`
138
+ - **Google**: Nested into `generationConfig` with camelCase names (`topP`, `stopSequences`, `seed`, etc.)
139
+
140
+ Fields that have no equivalent in a target provider are silently skipped — the request succeeds and the provider's default applies.
141
+
127
142
  ## Model Naming and Exposure
128
143
 
129
144
  ### Public model IDs
package/dist/index.mjs CHANGED
@@ -324,6 +324,8 @@ const toolArgsSchema = z.record(z.string().trim(), z.unknown());
324
324
  function convertMessages(messages) {
325
325
  const systemParts = [];
326
326
  const piMessages = [];
327
+ const toolCallNames = /* @__PURE__ */ new Map();
328
+ for (const msg of messages) if (msg !== void 0 && msg.role === "assistant" && "tool_calls" in msg) for (const tc of msg.tool_calls ?? []) toolCallNames.set(tc.id, tc.function.name);
327
329
  for (let i = 0; i < messages.length; i++) {
328
330
  const msg = messages[i];
329
331
  if (msg === void 0) continue;
@@ -337,7 +339,8 @@ function convertMessages(messages) {
337
339
  const assistantMsg = convertAssistantMessage(msg.content ?? null, msg.tool_calls);
338
340
  piMessages.push(assistantMsg);
339
341
  } else if (msg.role === "tool") {
340
- const toolMsg = convertToolMessage(msg.content, msg.tool_call_id);
342
+ const toolName = toolCallNames.get(msg.tool_call_id) ?? "";
343
+ const toolMsg = convertToolMessage(msg.content, msg.tool_call_id, toolName);
341
344
  piMessages.push(toolMsg);
342
345
  }
343
346
  }
@@ -442,11 +445,11 @@ function convertAssistantMessage(textContent, toolCalls) {
442
445
  timestamp: Date.now()
443
446
  };
444
447
  }
445
- function convertToolMessage(content, toolCallId) {
448
+ function convertToolMessage(content, toolCallId, toolName) {
446
449
  return {
447
450
  role: "toolResult",
448
451
  toolCallId,
449
- toolName: "",
452
+ toolName,
450
453
  content: [{
451
454
  type: "text",
452
455
  text: content
@@ -1679,6 +1682,7 @@ function createRoutes(config, configReader = fileConfigReader) {
1679
1682
  const conversion = convertMessages(request.messages);
1680
1683
  if (!conversion.ok) return c.json(invalidRequest(conversion.message, conversion.param), 400);
1681
1684
  const context = conversion.context;
1685
+ if (model.api === "openai-codex-responses" && context.systemPrompt === void 0) context.systemPrompt = "";
1682
1686
  if (request.tools !== void 0 && request.tools.length > 0) {
1683
1687
  const toolConversion = convertTools(request.tools);
1684
1688
  if (!toolConversion.ok) return c.json(unsupportedParameter(toolConversion.param, toolConversion.message), 422);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@victor-software-house/pi-openai-proxy",
3
- "version": "4.9.0",
3
+ "version": "4.9.2",
4
4
  "description": "OpenAI-compatible HTTP proxy for pi's multi-provider model registry",
5
5
  "license": "MIT",
6
6
  "author": "Victor Software House",