@oh-my-pi/pi-ai 16.3.14 → 16.4.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/CHANGELOG.md +45 -0
- package/README.md +5 -2
- package/dist/types/auth-broker/remote-store.d.ts +1 -0
- package/dist/types/auth-broker/wire-schemas.d.ts +8 -0
- package/dist/types/auth-storage.d.ts +5 -0
- package/dist/types/providers/azure-openai-responses.d.ts +1 -1
- package/dist/types/providers/ollama.d.ts +1 -1
- package/dist/types/providers/openai-chat-server-schema.d.ts +1 -1
- package/dist/types/providers/openai-chat-wire.d.ts +1 -1
- package/dist/types/providers/openai-codex/request-transformer.d.ts +43 -5
- package/dist/types/providers/openai-codex-responses.d.ts +51 -8
- package/dist/types/providers/openai-completions.d.ts +1 -1
- package/dist/types/providers/openai-responses-wire.d.ts +16 -7
- package/dist/types/providers/openai-responses.d.ts +1 -1
- package/dist/types/providers/openai-shared.d.ts +38 -8
- package/dist/types/registry/novita.d.ts +6 -0
- package/dist/types/registry/oauth/device-code.d.ts +25 -0
- package/dist/types/registry/oauth/index.d.ts +1 -25
- package/dist/types/registry/oauth/xai-oauth.d.ts +9 -25
- package/dist/types/registry/registry.d.ts +4 -1
- package/dist/types/registry/xai-oauth.d.ts +0 -1
- package/dist/types/types.d.ts +26 -3
- package/package.json +4 -4
- package/src/auth-broker/remote-store.ts +60 -5
- package/src/auth-broker/server.ts +1 -0
- package/src/auth-broker/wire-schemas.ts +1 -0
- package/src/auth-gateway/server.ts +2 -2
- package/src/auth-storage.ts +198 -60
- package/src/error/flags.ts +4 -1
- package/src/error/rate-limit.ts +7 -1
- package/src/providers/amazon-bedrock.ts +1 -0
- package/src/providers/anthropic-messages-server.ts +18 -0
- package/src/providers/azure-openai-responses.ts +3 -3
- package/src/providers/ollama.ts +1 -1
- package/src/providers/openai-chat-server-schema.ts +1 -1
- package/src/providers/openai-chat-server.ts +8 -1
- package/src/providers/openai-chat-wire.ts +1 -1
- package/src/providers/openai-codex/request-transformer.ts +94 -41
- package/src/providers/openai-codex-responses.ts +475 -39
- package/src/providers/openai-completions.ts +38 -12
- package/src/providers/openai-responses-server.ts +8 -1
- package/src/providers/openai-responses-wire.ts +16 -7
- package/src/providers/openai-responses.ts +18 -5
- package/src/providers/openai-shared.ts +125 -13
- package/src/registry/novita.ts +22 -0
- package/src/registry/oauth/__tests__/xai-oauth.test.ts +191 -80
- package/src/registry/oauth/device-code.ts +92 -0
- package/src/registry/oauth/index.ts +1 -91
- package/src/registry/oauth/xai-oauth.ts +231 -191
- package/src/registry/registry.ts +2 -0
- package/src/registry/xai-oauth.ts +0 -1
- package/src/stream.ts +7 -1
- package/src/types.ts +29 -3
|
@@ -209,7 +209,7 @@ export const openaiChatRequestSchema = type({
|
|
|
209
209
|
"frequency_penalty?": "number",
|
|
210
210
|
"logit_bias?": type({ "[string]": "number" }),
|
|
211
211
|
"user?": "string",
|
|
212
|
-
"reasoning_effort?": "'minimal' | 'low' | 'medium' | 'high' | 'xhigh'",
|
|
212
|
+
"reasoning_effort?": "'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'",
|
|
213
213
|
"parallel_tool_calls?": "boolean",
|
|
214
214
|
"service_tier?": "'auto' | 'default' | 'flex' | 'scale' | 'priority'",
|
|
215
215
|
"metadata?": type({ "[string]": "unknown" }),
|
|
@@ -35,7 +35,14 @@ export type { ParsedRequest };
|
|
|
35
35
|
type ReasoningEffort = NonNullable<ParsedRequest["options"]["reasoning"]>;
|
|
36
36
|
|
|
37
37
|
function isReasoningEffort(value: unknown): value is ReasoningEffort {
|
|
38
|
-
return
|
|
38
|
+
return (
|
|
39
|
+
value === "minimal" ||
|
|
40
|
+
value === "low" ||
|
|
41
|
+
value === "medium" ||
|
|
42
|
+
value === "high" ||
|
|
43
|
+
value === "xhigh" ||
|
|
44
|
+
value === "max"
|
|
45
|
+
);
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
function isServiceTier(value: unknown): value is ServiceTier {
|
|
@@ -116,7 +116,7 @@ export type Metadata = {
|
|
|
116
116
|
};
|
|
117
117
|
|
|
118
118
|
/** Constrains effort on reasoning for reasoning models. */
|
|
119
|
-
export type ReasoningEffort = "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | null;
|
|
119
|
+
export type ReasoningEffort = "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | null;
|
|
120
120
|
|
|
121
121
|
/** JSON object response format (older JSON mode). */
|
|
122
122
|
export interface ResponseFormatJSONObject {
|
|
@@ -8,7 +8,7 @@ import { mapOpenAIReasoningEffort } from "../openai-shared";
|
|
|
8
8
|
export type CodexReasoningContext = "auto" | "current_turn" | "all_turns";
|
|
9
9
|
|
|
10
10
|
/** User-facing effort levels accepted by Codex request options. */
|
|
11
|
-
type CodexCallerEffort = "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
11
|
+
type CodexCallerEffort = "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
12
12
|
|
|
13
13
|
/** Caller literal → catalog `Effort` bridge (the enum is nominal). */
|
|
14
14
|
const EFFORT_BY_NAME: Record<CodexCallerEffort, Effort> = {
|
|
@@ -17,23 +17,31 @@ const EFFORT_BY_NAME: Record<CodexCallerEffort, Effort> = {
|
|
|
17
17
|
medium: Effort.Medium,
|
|
18
18
|
high: Effort.High,
|
|
19
19
|
xhigh: Effort.XHigh,
|
|
20
|
+
max: Effort.Max,
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
export interface ReasoningConfig {
|
|
23
24
|
effort: "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
24
25
|
summary?: "auto" | "concise" | "detailed";
|
|
25
26
|
context?: CodexReasoningContext;
|
|
27
|
+
/** Pro reasoning serving mode (gpt-5.6+ catalog pro aliases). */
|
|
28
|
+
mode?: "pro";
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export interface CodexRequestOptions {
|
|
29
|
-
/** User-facing effort; the wire
|
|
32
|
+
/** User-facing effort; maps 1:1 onto the wire tier of the same name. */
|
|
30
33
|
reasoningEffort?: CodexCallerEffort | "none";
|
|
31
34
|
reasoningSummary?: ReasoningConfig["summary"] | null;
|
|
32
35
|
/** Explicit `reasoning.context` override; defaults to `all_turns` when unset. The `all_turns` value is gated to gpt-5.4+ Codex models — older ids reject it, so it is suppressed and `context` omitted. */
|
|
33
36
|
reasoningContext?: CodexReasoningContext;
|
|
34
37
|
textVerbosity?: "low" | "medium" | "high";
|
|
35
38
|
include?: string[];
|
|
36
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Responses Lite transport override; defaults to the model's
|
|
41
|
+
* `useResponsesLite`. Lite moves instructions/tools into input items,
|
|
42
|
+
* strips image detail, and disables parallel tool calling (codex-rs
|
|
43
|
+
* `use_responses_lite`).
|
|
44
|
+
*/
|
|
37
45
|
responsesLite?: boolean;
|
|
38
46
|
}
|
|
39
47
|
|
|
@@ -46,6 +54,8 @@ export interface InputItem {
|
|
|
46
54
|
name?: string;
|
|
47
55
|
output?: unknown;
|
|
48
56
|
arguments?: unknown;
|
|
57
|
+
/** `additional_tools` developer item payload (Responses Lite). */
|
|
58
|
+
tools?: unknown;
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
export interface RequestBody {
|
|
@@ -56,6 +66,8 @@ export interface RequestBody {
|
|
|
56
66
|
input?: InputItem[];
|
|
57
67
|
tools?: unknown;
|
|
58
68
|
tool_choice?: unknown;
|
|
69
|
+
/** Concurrent reasoning-summary delivery (codex-rs `StreamOptions`). */
|
|
70
|
+
stream_options?: { reasoning_summary_delivery: "sequential_cutoff" };
|
|
59
71
|
// Sampling controls (temperature/top_p/top_k/min_p/presence_penalty/
|
|
60
72
|
// repetition_penalty/frequency_penalty/stop) are intentionally absent: the
|
|
61
73
|
// Codex backend rejects every one with a 400 `Unsupported parameter`, so
|
|
@@ -74,29 +86,22 @@ export interface RequestBody {
|
|
|
74
86
|
[key: string]: unknown;
|
|
75
87
|
}
|
|
76
88
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (containsInputImage(item)) return true;
|
|
88
|
-
}
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** Returns whether a Codex request can use the text-only Responses Lite transport. */
|
|
93
|
-
export function shouldUseCodexResponsesLite(body: RequestBody, requested: boolean | undefined): boolean {
|
|
94
|
-
return requested === true && !containsInputImage(body.input);
|
|
89
|
+
/**
|
|
90
|
+
* Resolve whether a Codex request uses the Responses Lite transport: an
|
|
91
|
+
* explicit option wins, otherwise the model's catalog flag (codex-rs
|
|
92
|
+
* `model_info.use_responses_lite`) decides.
|
|
93
|
+
*/
|
|
94
|
+
export function resolveCodexResponsesLite(
|
|
95
|
+
model: Model<"openai-codex-responses">,
|
|
96
|
+
requested: boolean | undefined,
|
|
97
|
+
): boolean {
|
|
98
|
+
return requested ?? model.useResponsesLite === true;
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
/**
|
|
98
102
|
* Clamp a user-facing effort to the model's ladder, then remap to the wire
|
|
99
|
-
* tier
|
|
103
|
+
* tier. User efforts map 1:1 onto wire tiers; the effort map only covers
|
|
104
|
+
* host quirks where a wire tier genuinely does not exist (e.g. `minimal→none`).
|
|
100
105
|
* A mapped value outside the Codex wire vocabulary is a broken compat/model
|
|
101
106
|
* effort map — fail loudly rather than silently sending a different tier.
|
|
102
107
|
*/
|
|
@@ -240,24 +245,62 @@ function repairToolCallPairs(input: InputItem[]): InputItem[] {
|
|
|
240
245
|
* `detail` from every input image (message content and tool outputs) before
|
|
241
246
|
* sending, letting the server choose.
|
|
242
247
|
*/
|
|
243
|
-
function stripImageDetails(input:
|
|
248
|
+
function stripImageDetails(input: unknown[]): void {
|
|
244
249
|
for (const item of input) {
|
|
245
|
-
|
|
250
|
+
if (!item || typeof item !== "object") continue;
|
|
251
|
+
const content = "content" in item ? item.content : undefined;
|
|
252
|
+
const output = "output" in item ? item.output : undefined;
|
|
253
|
+
for (const collection of [content, output]) {
|
|
246
254
|
if (!Array.isArray(collection)) continue;
|
|
247
255
|
for (const part of collection) {
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
(part as { type?: unknown }).type === "input_image" &&
|
|
252
|
-
"detail" in part
|
|
253
|
-
) {
|
|
254
|
-
part.detail = undefined;
|
|
255
|
-
}
|
|
256
|
+
if (!part || typeof part !== "object") continue;
|
|
257
|
+
if (!("type" in part) || part.type !== "input_image") continue;
|
|
258
|
+
if ("detail" in part) part.detail = undefined;
|
|
256
259
|
}
|
|
257
260
|
}
|
|
258
261
|
}
|
|
259
262
|
}
|
|
260
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Structural view of a Responses-style body mutated by the Lite rewrite.
|
|
266
|
+
* Loose (`unknown`) property types let the turn transformer (`RequestBody`)
|
|
267
|
+
* and the agent's remote-compaction payloads reuse one shaper.
|
|
268
|
+
*/
|
|
269
|
+
export interface CodexLiteShapedBody {
|
|
270
|
+
instructions?: unknown;
|
|
271
|
+
tools?: unknown;
|
|
272
|
+
input?: unknown;
|
|
273
|
+
parallel_tool_calls?: unknown;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Applies the Responses Lite body contract in place (codex-rs
|
|
278
|
+
* `build_responses_request` with `use_responses_lite`): strips pinned image
|
|
279
|
+
* detail, forces parallel tool calling off, moves tools into a leading
|
|
280
|
+
* `additional_tools` developer item and the base instructions into a
|
|
281
|
+
* developer message, then omits top-level `instructions`/`tools`. Shared by
|
|
282
|
+
* normal turns and both remote-compaction paths — codex-rs routes
|
|
283
|
+
* `/responses/compact` through the same builder.
|
|
284
|
+
*/
|
|
285
|
+
export function applyCodexResponsesLiteShape(body: CodexLiteShapedBody): void {
|
|
286
|
+
const input = Array.isArray(body.input) ? body.input : [];
|
|
287
|
+
stripImageDetails(input);
|
|
288
|
+
body.parallel_tool_calls = false;
|
|
289
|
+
const prefix: InputItem[] = [
|
|
290
|
+
{ type: "additional_tools", role: "developer", tools: Array.isArray(body.tools) ? body.tools : [] },
|
|
291
|
+
];
|
|
292
|
+
if (typeof body.instructions === "string" && body.instructions.length > 0) {
|
|
293
|
+
prefix.push({
|
|
294
|
+
type: "message",
|
|
295
|
+
role: "developer",
|
|
296
|
+
content: [{ type: "input_text", text: body.instructions }],
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
body.input = [...prefix, ...input];
|
|
300
|
+
delete body.instructions;
|
|
301
|
+
delete body.tools;
|
|
302
|
+
}
|
|
303
|
+
|
|
261
304
|
export async function transformRequestBody(
|
|
262
305
|
body: RequestBody,
|
|
263
306
|
model: Model<"openai-codex-responses">,
|
|
@@ -331,16 +374,9 @@ export async function transformRequestBody(
|
|
|
331
374
|
}
|
|
332
375
|
}
|
|
333
376
|
|
|
334
|
-
const responsesLite =
|
|
377
|
+
const responsesLite = resolveCodexResponsesLite(model, options.responsesLite);
|
|
335
378
|
if (responsesLite) {
|
|
336
|
-
|
|
337
|
-
stripImageDetails(body.input);
|
|
338
|
-
}
|
|
339
|
-
// Responses Lite does not support parallel tool calling; codex-rs forces
|
|
340
|
-
// it off (`prompt.parallel_tool_calls && !use_responses_lite`).
|
|
341
|
-
if (body.tools !== undefined) {
|
|
342
|
-
body.parallel_tool_calls = false;
|
|
343
|
-
}
|
|
379
|
+
applyCodexResponsesLiteShape(body);
|
|
344
380
|
}
|
|
345
381
|
|
|
346
382
|
if (options.reasoningEffort !== undefined) {
|
|
@@ -367,6 +403,23 @@ export async function transformRequestBody(
|
|
|
367
403
|
} else {
|
|
368
404
|
delete body.reasoning;
|
|
369
405
|
}
|
|
406
|
+
// Catalog pro aliases (`gpt-5.6-*-pro`): applied after the effort branch so
|
|
407
|
+
// the mode is sent even when no effort is set (the branch above deletes
|
|
408
|
+
// `body.reasoning` in that case) — mode and effort are independent fields.
|
|
409
|
+
if (model.reasoningMode) {
|
|
410
|
+
body.reasoning = { ...body.reasoning, mode: model.reasoningMode };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Concurrent reasoning summaries (codex-rs `concurrent_reasoning_summaries`
|
|
414
|
+
// feature): `sequential_cutoff` lets the server stream output without
|
|
415
|
+
// blocking on summary generation. Only meaningful when a summary is
|
|
416
|
+
// requested; codex-rs additionally gates on its OpenAI provider check,
|
|
417
|
+
// which is inherent here.
|
|
418
|
+
if (body.reasoning?.summary !== undefined) {
|
|
419
|
+
body.stream_options = { reasoning_summary_delivery: "sequential_cutoff" };
|
|
420
|
+
} else {
|
|
421
|
+
delete body.stream_options;
|
|
422
|
+
}
|
|
370
423
|
|
|
371
424
|
body.text = {
|
|
372
425
|
...body.text,
|