@victor-software-house/pi-openai-proxy 4.8.0 → 4.9.1
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 +21 -6
- package/dist/index.mjs +39 -6
- package/package.json +1 -1
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`
|
|
120
|
-
| `top_p` |
|
|
121
|
-
| `frequency_penalty` |
|
|
122
|
-
| `presence_penalty` |
|
|
123
|
-
| `seed` |
|
|
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
|
@@ -1247,15 +1247,23 @@ const REASONING_EFFORT_MAP = {
|
|
|
1247
1247
|
xhigh: "xhigh"
|
|
1248
1248
|
};
|
|
1249
1249
|
/**
|
|
1250
|
-
* APIs that
|
|
1251
|
-
*
|
|
1250
|
+
* APIs that accept the full set of OpenAI passthrough fields (stop, seed, top_p,
|
|
1251
|
+
* tool_choice, frequency_penalty, etc.) as top-level payload properties.
|
|
1252
1252
|
*/
|
|
1253
|
-
const
|
|
1253
|
+
const OPENAI_FULL_PASSTHROUGH_APIS = new Set([
|
|
1254
1254
|
"openai-completions",
|
|
1255
1255
|
"openai-responses",
|
|
1256
1256
|
"azure-openai-responses",
|
|
1257
1257
|
"mistral-conversations"
|
|
1258
1258
|
]);
|
|
1259
|
+
/**
|
|
1260
|
+
* The Codex Responses API accepts only tool_choice and parallel_tool_calls.
|
|
1261
|
+
* Other standard fields (top_p, seed, stop, user, frequency_penalty, etc.)
|
|
1262
|
+
* are rejected with "Unsupported parameter". The SDK hardcodes
|
|
1263
|
+
* tool_choice: "auto" and parallel_tool_calls: true; onPayload overrides
|
|
1264
|
+
* those when the client sends explicit values.
|
|
1265
|
+
*/
|
|
1266
|
+
const CODEX_APIS = new Set(["openai-codex-responses"]);
|
|
1259
1267
|
const ANTHROPIC_APIS = new Set(["anthropic-messages"]);
|
|
1260
1268
|
const GOOGLE_APIS = new Set([
|
|
1261
1269
|
"google-generative-ai",
|
|
@@ -1462,13 +1470,36 @@ function patchGooglePayload(payload, request) {
|
|
|
1462
1470
|
return patched;
|
|
1463
1471
|
}
|
|
1464
1472
|
/**
|
|
1473
|
+
* Collect the restricted set of fields the Codex Responses API accepts.
|
|
1474
|
+
*
|
|
1475
|
+
* Only tool_choice and parallel_tool_calls are supported. The SDK hardcodes
|
|
1476
|
+
* tool_choice: "auto" and parallel_tool_calls: true; these overrides let clients
|
|
1477
|
+
* control tool behavior explicitly.
|
|
1478
|
+
*
|
|
1479
|
+
* @internal Exported for unit testing only.
|
|
1480
|
+
*/
|
|
1481
|
+
function collectCodexPayloadFields(request) {
|
|
1482
|
+
const fields = {};
|
|
1483
|
+
let hasFields = false;
|
|
1484
|
+
if (request.tool_choice !== void 0) {
|
|
1485
|
+
fields["tool_choice"] = request.tool_choice;
|
|
1486
|
+
hasFields = true;
|
|
1487
|
+
}
|
|
1488
|
+
if (request.parallel_tool_calls !== void 0) {
|
|
1489
|
+
fields["parallel_tool_calls"] = request.parallel_tool_calls;
|
|
1490
|
+
hasFields = true;
|
|
1491
|
+
}
|
|
1492
|
+
return hasFields ? fields : void 0;
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1465
1495
|
* Collect API-specific payload fields from an OpenAI request.
|
|
1466
1496
|
*
|
|
1467
1497
|
* Dispatches to the appropriate translator based on the target API:
|
|
1468
|
-
* - OpenAI
|
|
1498
|
+
* - OpenAI full passthrough: all standard fields (same names)
|
|
1499
|
+
* - Codex: restricted to tool_choice + parallel_tool_calls only
|
|
1469
1500
|
* - Anthropic: translated field names and formats
|
|
1470
1501
|
* - Google: nested generationConfig patching (handled separately in onPayload)
|
|
1471
|
-
* - Others (Bedrock
|
|
1502
|
+
* - Others (Bedrock): no passthrough
|
|
1472
1503
|
*
|
|
1473
1504
|
* For Google APIs, returns undefined (patching is done directly in onPayload
|
|
1474
1505
|
* via patchGooglePayload because the payload structure is nested).
|
|
@@ -1476,7 +1507,8 @@ function patchGooglePayload(payload, request) {
|
|
|
1476
1507
|
* @internal Exported for unit testing only.
|
|
1477
1508
|
*/
|
|
1478
1509
|
function collectPayloadFields(request, api) {
|
|
1479
|
-
if (
|
|
1510
|
+
if (OPENAI_FULL_PASSTHROUGH_APIS.has(api)) return collectOpenAIPayloadFields(request);
|
|
1511
|
+
if (CODEX_APIS.has(api)) return collectCodexPayloadFields(request);
|
|
1480
1512
|
if (ANTHROPIC_APIS.has(api)) return collectAnthropicPayloadFields(request);
|
|
1481
1513
|
}
|
|
1482
1514
|
/**
|
|
@@ -1647,6 +1679,7 @@ function createRoutes(config, configReader = fileConfigReader) {
|
|
|
1647
1679
|
const conversion = convertMessages(request.messages);
|
|
1648
1680
|
if (!conversion.ok) return c.json(invalidRequest(conversion.message, conversion.param), 400);
|
|
1649
1681
|
const context = conversion.context;
|
|
1682
|
+
if (model.api === "openai-codex-responses" && context.systemPrompt === void 0) context.systemPrompt = "";
|
|
1650
1683
|
if (request.tools !== void 0 && request.tools.length > 0) {
|
|
1651
1684
|
const toolConversion = convertTools(request.tools);
|
|
1652
1685
|
if (!toolConversion.ok) return c.json(unsupportedParameter(toolConversion.param, toolConversion.message), 422);
|
package/package.json
CHANGED