@oh-my-pi/pi-ai 16.3.13 → 16.3.14

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 CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.3.14] - 2026-07-09
6
+
7
+ ### Changed
8
+
9
+ - Updated Codex reasoning effort mapping to support shifted wire tiers for newer models
10
+
11
+ ### Fixed
12
+
13
+ - Fixed the Codex Responses request transformer bypassing catalog/compat reasoning effort maps: the clamped user effort is now remapped to the provider wire tier (GPT-5.6's shifted five-tier scale sends `max` for user `xhigh` and `xhigh` for `high`), failing loudly if a map produces a value outside the Codex wire vocabulary.
14
+
5
15
  ## [16.3.13] - 2026-07-09
6
16
 
7
17
  ### Changed
@@ -1,13 +1,16 @@
1
- import type { Api, Model } from "../../types.js";
1
+ import type { Model } from "../../types.js";
2
2
  /** Reasoning replay scope for the Codex Responses API (`reasoning.context`). */
3
3
  export type CodexReasoningContext = "auto" | "current_turn" | "all_turns";
4
+ /** User-facing effort levels accepted by Codex request options. */
5
+ type CodexCallerEffort = "minimal" | "low" | "medium" | "high" | "xhigh";
4
6
  export interface ReasoningConfig {
5
- effort: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
7
+ effort: "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
6
8
  summary?: "auto" | "concise" | "detailed";
7
9
  context?: CodexReasoningContext;
8
10
  }
9
11
  export interface CodexRequestOptions {
10
- reasoningEffort?: ReasoningConfig["effort"];
12
+ /** User-facing effort; the wire-only `max` tier is reached via the model's effort map. */
13
+ reasoningEffort?: CodexCallerEffort | "none";
11
14
  reasoningSummary?: ReasoningConfig["summary"] | null;
12
15
  /** 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. */
13
16
  reasoningContext?: CodexReasoningContext;
@@ -49,6 +52,7 @@ export interface RequestBody {
49
52
  }
50
53
  /** Returns whether a Codex request can use the text-only Responses Lite transport. */
51
54
  export declare function shouldUseCodexResponsesLite(body: RequestBody, requested: boolean | undefined): boolean;
52
- export declare function transformRequestBody(body: RequestBody, model: Model<Api>, options?: CodexRequestOptions, prompt?: {
55
+ export declare function transformRequestBody(body: RequestBody, model: Model<"openai-codex-responses">, options?: CodexRequestOptions, prompt?: {
53
56
  developerMessages: string[];
54
57
  }): Promise<RequestBody>;
58
+ export {};
@@ -1,3 +1,4 @@
1
+ import type { Effort } from "@oh-my-pi/pi-catalog/effort";
1
2
  import type { OpenAICompat, OpenAIReasoningDisableMode, OpenAIStreamMarkupHealingPattern, OpenRouterRouting, ResolvedOpenAICompat, ResolvedOpenAIResponsesCompat, ResolvedOpenAISharedCompat, VercelGatewayRouting } from "@oh-my-pi/pi-catalog/types";
2
3
  import { type Api, type AssistantMessage, type CacheRetention, type Context, type ImageContent, type Message, type MessageAttribution, type Model, type ServiceTier, type StopReason, type StreamOptions, type TextContent, type TextSignatureV1, type ThinkingContent, type Tool, type ToolCall, type ToolResultMessage, type Usage } from "../types.js";
3
4
  import { kStreamingLastParseLen, kStreamingPartialJson } from "../utils/block-symbols.js";
@@ -275,6 +276,15 @@ export interface OpenAICompatPolicy {
275
276
  emptyLengthFinishIsContextError: boolean;
276
277
  };
277
278
  }
279
+ /**
280
+ * Map a user-facing effort to the provider wire value: explicit compat
281
+ * override first, then the model's baked `thinking.effortMap`, else identity.
282
+ * Shared by the chat-completions/Responses policy resolver and the Codex
283
+ * request transformer.
284
+ */
285
+ export declare function mapOpenAIReasoningEffort(model: Pick<Model, "thinking">, compat: {
286
+ reasoningEffortMap?: Partial<Record<Effort, string>>;
287
+ } | undefined, effort: string): string;
278
288
  export declare function resolveOpenAICompatPolicy<TApi extends Api>(model: Model<TApi>, options: ResolveOpenAICompatPolicyOptions): OpenAICompatPolicy;
279
289
  export declare function applyChatCompletionsCompatPolicy(params: OpenAICompletionsParams, policy: OpenAICompatPolicy): void;
280
290
  export declare function applyChatCompletionsReasoningParams(params: OpenAICompletionsParams, model: Model<"openai-completions">, compat: ResolvedOpenAICompat, options: (ChatCompletionsReasoningOptions & {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-ai",
4
- "version": "16.3.13",
4
+ "version": "16.3.14",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -38,9 +38,9 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@bufbuild/protobuf": "^2.12.0",
41
- "@oh-my-pi/pi-catalog": "16.3.13",
42
- "@oh-my-pi/pi-utils": "16.3.13",
43
- "@oh-my-pi/pi-wire": "16.3.13",
41
+ "@oh-my-pi/pi-catalog": "16.3.14",
42
+ "@oh-my-pi/pi-utils": "16.3.14",
43
+ "@oh-my-pi/pi-wire": "16.3.14",
44
44
  "arktype": "^2.2.0",
45
45
  "zod": "^4"
46
46
  },
@@ -1,19 +1,33 @@
1
- import type { Effort } from "@oh-my-pi/pi-catalog/effort";
1
+ import { Effort } from "@oh-my-pi/pi-catalog/effort";
2
2
  import { supportsAllTurnsReasoningContext, supportsCodexReasoningSummary } from "@oh-my-pi/pi-catalog/identity";
3
3
  import { requireSupportedEffort } from "@oh-my-pi/pi-catalog/model-thinking";
4
- import type { Api, Model } from "../../types";
4
+ import type { Model } from "../../types";
5
+ import { mapOpenAIReasoningEffort } from "../openai-shared";
5
6
 
6
7
  /** Reasoning replay scope for the Codex Responses API (`reasoning.context`). */
7
8
  export type CodexReasoningContext = "auto" | "current_turn" | "all_turns";
8
9
 
10
+ /** User-facing effort levels accepted by Codex request options. */
11
+ type CodexCallerEffort = "minimal" | "low" | "medium" | "high" | "xhigh";
12
+
13
+ /** Caller literal → catalog `Effort` bridge (the enum is nominal). */
14
+ const EFFORT_BY_NAME: Record<CodexCallerEffort, Effort> = {
15
+ minimal: Effort.Minimal,
16
+ low: Effort.Low,
17
+ medium: Effort.Medium,
18
+ high: Effort.High,
19
+ xhigh: Effort.XHigh,
20
+ };
21
+
9
22
  export interface ReasoningConfig {
10
- effort: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
23
+ effort: "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
11
24
  summary?: "auto" | "concise" | "detailed";
12
25
  context?: CodexReasoningContext;
13
26
  }
14
27
 
15
28
  export interface CodexRequestOptions {
16
- reasoningEffort?: ReasoningConfig["effort"];
29
+ /** User-facing effort; the wire-only `max` tier is reached via the model's effort map. */
30
+ reasoningEffort?: CodexCallerEffort | "none";
17
31
  reasoningSummary?: ReasoningConfig["summary"] | null;
18
32
  /** 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. */
19
33
  reasoningContext?: CodexReasoningContext;
@@ -80,10 +94,40 @@ export function shouldUseCodexResponsesLite(body: RequestBody, requested: boolea
80
94
  return requested === true && !containsInputImage(body.input);
81
95
  }
82
96
 
83
- function getReasoningConfig(model: Model<Api>, options: CodexRequestOptions): ReasoningConfig {
97
+ /**
98
+ * Clamp a user-facing effort to the model's ladder, then remap to the wire
99
+ * tier (e.g. GPT-5.6's shifted five-tier scale sends `max` for user `xhigh`).
100
+ * A mapped value outside the Codex wire vocabulary is a broken compat/model
101
+ * effort map — fail loudly rather than silently sending a different tier.
102
+ */
103
+ function mapCodexWireEffort(
104
+ model: Model<"openai-codex-responses">,
105
+ effort: CodexCallerEffort,
106
+ ): ReasoningConfig["effort"] {
107
+ const mapped = mapOpenAIReasoningEffort(model, model.compat, requireSupportedEffort(model, EFFORT_BY_NAME[effort]));
108
+ switch (mapped) {
109
+ case "none":
110
+ case "minimal":
111
+ case "low":
112
+ case "medium":
113
+ case "high":
114
+ case "xhigh":
115
+ case "max":
116
+ return mapped;
117
+ default:
118
+ throw new Error(
119
+ `Effort map for ${model.provider}/${model.id} produced invalid Codex reasoning effort "${mapped}"`,
120
+ );
121
+ }
122
+ }
123
+
124
+ function getReasoningConfig(
125
+ model: Model<"openai-codex-responses">,
126
+ effort: NonNullable<CodexRequestOptions["reasoningEffort"]>,
127
+ options: CodexRequestOptions,
128
+ ): ReasoningConfig {
84
129
  const config: ReasoningConfig = {
85
- effort:
86
- options.reasoningEffort === "none" ? "none" : requireSupportedEffort(model, options.reasoningEffort as Effort),
130
+ effort: effort === "none" ? "none" : mapCodexWireEffort(model, effort),
87
131
  };
88
132
  // `reasoning.summary` is accepted only from gpt-5.4 onward; earlier Codex ids
89
133
  // (gpt-5.1-codex, gpt-5.3-codex, gpt-5.3-codex-spark) reject it with
@@ -216,7 +260,7 @@ function stripImageDetails(input: InputItem[]): void {
216
260
 
217
261
  export async function transformRequestBody(
218
262
  body: RequestBody,
219
- model: Model<Api>,
263
+ model: Model<"openai-codex-responses">,
220
264
  options: CodexRequestOptions = {},
221
265
  prompt?: { developerMessages: string[] },
222
266
  ): Promise<RequestBody> {
@@ -300,7 +344,7 @@ export async function transformRequestBody(
300
344
  }
301
345
 
302
346
  if (options.reasoningEffort !== undefined) {
303
- const reasoningConfig = getReasoningConfig(model, options);
347
+ const reasoningConfig = getReasoningConfig(model, options.reasoningEffort, options);
304
348
  body.reasoning = {
305
349
  ...body.reasoning,
306
350
  ...reasoningConfig,
@@ -695,13 +695,19 @@ export interface OpenAICompatPolicy {
695
695
  };
696
696
  }
697
697
 
698
- function mapOpenAIReasoningEffort(
698
+ /**
699
+ * Map a user-facing effort to the provider wire value: explicit compat
700
+ * override first, then the model's baked `thinking.effortMap`, else identity.
701
+ * Shared by the chat-completions/Responses policy resolver and the Codex
702
+ * request transformer.
703
+ */
704
+ export function mapOpenAIReasoningEffort(
699
705
  model: Pick<Model, "thinking">,
700
- compat: OpenAICompatPolicyCompat,
706
+ compat: { reasoningEffortMap?: Partial<Record<Effort, string>> } | undefined,
701
707
  effort: string,
702
708
  ): string {
703
709
  const level = effort as Effort;
704
- return compat.reasoningEffortMap?.[level] ?? model.thinking?.effortMap?.[level] ?? effort;
710
+ return compat?.reasoningEffortMap?.[level] ?? model.thinking?.effortMap?.[level] ?? effort;
705
711
  }
706
712
 
707
713
  function isImplicitDisableWhenNotRequested(disableMode: OpenAIReasoningDisableMode): boolean {