@oh-my-pi/pi-ai 17.2.3 → 17.2.5
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 +26 -0
- package/dist/types/auth-storage.d.ts +8 -0
- package/dist/types/dialect/examples.d.ts +10 -2
- package/dist/types/dialect/inventory.d.ts +6 -9
- package/dist/types/dialect/rendering.d.ts +9 -0
- package/dist/types/dialect/types.d.ts +0 -1
- package/dist/types/error/flags.d.ts +5 -0
- package/dist/types/providers/openai-codex-responses.d.ts +15 -1
- package/dist/types/providers/openai-shared.d.ts +24 -6
- package/dist/types/providers/transform-messages.d.ts +1 -1
- package/dist/types/types.d.ts +6 -4
- package/dist/types/utils/harmony-leak.d.ts +9 -0
- package/dist/types/utils/schema/typescript.d.ts +8 -2
- package/package.json +4 -4
- package/src/auth-broker/discover.ts +2 -1
- package/src/auth-storage.ts +149 -15
- package/src/dialect/examples.ts +24 -12
- package/src/dialect/gemini.ts +17 -31
- package/src/dialect/harmony.ts +1 -2
- package/src/dialect/inventory.ts +20 -63
- package/src/dialect/rendering.ts +54 -0
- package/src/dialect/types.ts +0 -1
- package/src/error/flags.ts +8 -0
- package/src/providers/anthropic.ts +78 -33
- package/src/providers/cursor.ts +6 -3
- package/src/providers/openai-codex-responses.ts +191 -36
- package/src/providers/openai-completions.ts +7 -1
- package/src/providers/openai-responses.ts +5 -6
- package/src/providers/openai-shared.ts +114 -40
- package/src/providers/transform-messages.ts +1 -1
- package/src/stream.ts +2 -2
- package/src/types.ts +9 -8
- package/src/utils/harmony-leak.ts +12 -0
- package/src/utils/schema/typescript.ts +21 -7
- package/src/utils.ts +1 -1
|
@@ -300,7 +300,7 @@ function normalizeAnthropicTargetToolCallId<TApi extends Api>(
|
|
|
300
300
|
* credential redaction is enabled. Exported so hosts can route the same shapes
|
|
301
301
|
* through reversible obfuscation (keyed placeholders restored before local tool
|
|
302
302
|
* execution) instead of the irreversible `[*_token_redacted]` rewrite below —
|
|
303
|
-
* an irreversible placeholder echoed back in edit-tool `
|
|
303
|
+
* an irreversible placeholder echoed back in edit-tool `old_string` can never
|
|
304
304
|
* match the real bytes on disk.
|
|
305
305
|
*/
|
|
306
306
|
export const SENSITIVE_TOKEN_RE =
|
package/src/stream.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from "@oh-my-pi/pi-catalog/model-thinking";
|
|
16
16
|
import { CATALOG_PROVIDERS, type ProviderCatalogEntry } from "@oh-my-pi/pi-catalog/provider-models";
|
|
17
17
|
import { CODEX_BASE_URL } from "@oh-my-pi/pi-catalog/wire/codex";
|
|
18
|
-
import { $env, $pickenv,
|
|
18
|
+
import { $env, $pickenv, getProviderInFlightRoot, isEnoent, logger, withExtraCaFetch } from "@oh-my-pi/pi-utils";
|
|
19
19
|
import { getCustomApi } from "./api-registry";
|
|
20
20
|
import { createAuthRetryKeyState, isApiKeyResolver, resolveNextAuthRetryKey } from "./auth-retry";
|
|
21
21
|
import * as AIError from "./error";
|
|
@@ -189,7 +189,7 @@ function resolveProviderInFlightLimit(
|
|
|
189
189
|
|
|
190
190
|
function providerInFlightRoot(): string {
|
|
191
191
|
if (providerInFlightRootOverride) return providerInFlightRootOverride;
|
|
192
|
-
return
|
|
192
|
+
return getProviderInFlightRoot();
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
function providerInFlightSegment(provider: string): string {
|
package/src/types.ts
CHANGED
|
@@ -214,10 +214,10 @@ export function resolveModelServiceTier(
|
|
|
214
214
|
|
|
215
215
|
/**
|
|
216
216
|
* True when the tier should be sent on the wire as the provider's service-tier
|
|
217
|
-
* request field. OpenAI / OpenAI-Codex accept
|
|
218
|
-
* (Gemini API + Vertex) and OpenRouter accept `flex`/`priority`;
|
|
219
|
-
* Serverless realizes only its Priority serving path. Anthropic is
|
|
220
|
-
* realizes `priority` via `speed: "fast"
|
|
217
|
+
* request field. OpenAI / OpenAI-Codex accept every {@link ServiceTier};
|
|
218
|
+
* Google (Gemini API + Vertex) and OpenRouter accept `flex`/`priority`;
|
|
219
|
+
* Fireworks Serverless realizes only its Priority serving path. Anthropic is
|
|
220
|
+
* absent because it realizes `priority` via `speed: "fast"`.
|
|
221
221
|
*/
|
|
222
222
|
export function shouldSendServiceTier(
|
|
223
223
|
serviceTier: ServiceTier | null | undefined,
|
|
@@ -225,12 +225,11 @@ export function shouldSendServiceTier(
|
|
|
225
225
|
): boolean {
|
|
226
226
|
if (!serviceTier) return false;
|
|
227
227
|
const provider = typeof target === "string" ? target : target?.provider;
|
|
228
|
-
if (provider === "openai" || provider === "openai-codex"
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
if (typeof target !== "string" && target && isOpenAIServiceTierModel(target)) {
|
|
228
|
+
if (provider === "openai" || provider === "openai-codex") return true;
|
|
229
|
+
if (provider === "openrouter") {
|
|
232
230
|
return serviceTier === "flex" || serviceTier === "scale" || serviceTier === "priority";
|
|
233
231
|
}
|
|
232
|
+
if (typeof target !== "string" && target && isOpenAIServiceTierModel(target)) return true;
|
|
234
233
|
if (provider === "google") {
|
|
235
234
|
return serviceTier === "flex" || serviceTier === "priority";
|
|
236
235
|
}
|
|
@@ -844,6 +843,8 @@ export interface AssistantRetryRecovery {
|
|
|
844
843
|
export interface ContextSnapshot {
|
|
845
844
|
promptTokens: number; // authoritative provider prompt/input tokens
|
|
846
845
|
nonMessageTokens: number; // estimated non-message total at send time
|
|
846
|
+
/** Estimated prompt tokens removed by local history rewrites after this provider snapshot was recorded. */
|
|
847
|
+
historyRewriteTokensRemoved?: number;
|
|
847
848
|
lastMessageTimestamp?: number;
|
|
848
849
|
}
|
|
849
850
|
|
|
@@ -35,6 +35,18 @@ export function escapeHarmonyControlTokens(text: string): string {
|
|
|
35
35
|
return text.replace(HARMONY_CONTROL_TOKEN_ESCAPE_RE, "<\\|$1\\|>");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Escape reserved Harmony control tokens inside a JSON document string (e.g.
|
|
40
|
+
* `function_call.arguments`). Doubles the backslash so the document remains
|
|
41
|
+
* valid JSON whose *decoded* strings carry the inert `<\|token\|>` spelling.
|
|
42
|
+
* `<|` cannot occur outside a string literal in valid JSON, so the blanket
|
|
43
|
+
* replace never corrupts structure; malformed documents are escaped
|
|
44
|
+
* best-effort.
|
|
45
|
+
*/
|
|
46
|
+
export function escapeHarmonyControlTokensInJson(text: string): string {
|
|
47
|
+
return text.replace(HARMONY_CONTROL_TOKEN_ESCAPE_RE, "<\\\\|$1\\\\|>");
|
|
48
|
+
}
|
|
49
|
+
|
|
38
50
|
/**
|
|
39
51
|
* Whether requests to `model` are served by a Harmony-dialect backend
|
|
40
52
|
* (gpt-5.x / gpt-oss), which rejects reserved control-token spellings appearing
|
|
@@ -12,15 +12,22 @@
|
|
|
12
12
|
import { isJsonObject } from "./types";
|
|
13
13
|
|
|
14
14
|
export interface JsonSchemaToTsOptions {
|
|
15
|
-
/** Indentation unit for nested object bodies. Default two spaces. */
|
|
15
|
+
/** Indentation unit for nested object bodies. Default two spaces (none in `harmony` style). */
|
|
16
16
|
readonly indent?: string;
|
|
17
|
-
/** Emit `description` keywords as
|
|
17
|
+
/** Emit `description` keywords as comments on object properties. Default true. */
|
|
18
18
|
readonly comments?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Output flavor. `default` renders JSDoc comments, `;` delimiters, and
|
|
21
|
+
* indented bodies; `harmony` renders the flat OpenAI-Harmony convention —
|
|
22
|
+
* `//` line comments, `,` delimiters, no indentation.
|
|
23
|
+
*/
|
|
24
|
+
readonly style?: "default" | "harmony";
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
interface Ctx {
|
|
22
28
|
readonly indent: string;
|
|
23
29
|
readonly comments: boolean;
|
|
30
|
+
readonly harmony: boolean;
|
|
24
31
|
readonly defs: Record<string, unknown> | undefined;
|
|
25
32
|
readonly seen: Set<unknown>;
|
|
26
33
|
}
|
|
@@ -48,7 +55,11 @@ function joinUnion(parts: readonly string[]): string {
|
|
|
48
55
|
return unique.length > 0 ? unique.join(" | ") : "never";
|
|
49
56
|
}
|
|
50
57
|
|
|
51
|
-
function
|
|
58
|
+
function emitDescription(lines: string[], description: string, ctx: Ctx, pad: string): void {
|
|
59
|
+
if (ctx.harmony) {
|
|
60
|
+
for (const line of description.split("\n")) lines.push(`${pad}// ${line}`.trimEnd());
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
52
63
|
// `* /` keeps a stray closing token inside the description from ending the comment.
|
|
53
64
|
const safe = description.replace(/\*\//g, "* /");
|
|
54
65
|
if (!safe.includes("\n")) {
|
|
@@ -85,6 +96,7 @@ function convertObject(node: Record<string, unknown>, ctx: Ctx, pad: string): st
|
|
|
85
96
|
const required = new Set(
|
|
86
97
|
Array.isArray(node.required) ? node.required.filter((key): key is string => typeof key === "string") : [],
|
|
87
98
|
);
|
|
99
|
+
const delimiter = ctx.harmony ? "," : ";";
|
|
88
100
|
for (const key in properties) {
|
|
89
101
|
const value = properties[key];
|
|
90
102
|
if (
|
|
@@ -93,11 +105,11 @@ function convertObject(node: Record<string, unknown>, ctx: Ctx, pad: string): st
|
|
|
93
105
|
typeof value.description === "string" &&
|
|
94
106
|
value.description.length > 0
|
|
95
107
|
) {
|
|
96
|
-
|
|
108
|
+
emitDescription(body, value.description, ctx, childPad);
|
|
97
109
|
}
|
|
98
110
|
const optional = required.has(key) ? "" : "?";
|
|
99
111
|
const name = SAFE_KEY.test(key) ? key : JSON.stringify(key);
|
|
100
|
-
body.push(`${childPad}${name}${optional}: ${convert(value, ctx, childPad)}
|
|
112
|
+
body.push(`${childPad}${name}${optional}: ${convert(value, ctx, childPad)}${delimiter}`);
|
|
101
113
|
}
|
|
102
114
|
}
|
|
103
115
|
|
|
@@ -110,7 +122,7 @@ function convertObject(node: Record<string, unknown>, ctx: Ctx, pad: string): st
|
|
|
110
122
|
|
|
111
123
|
// Named properties alongside a free-form value schema → index signature.
|
|
112
124
|
if (isJsonObject(additional)) {
|
|
113
|
-
body.push(`${childPad}[key: string]: ${convert(additional, ctx, childPad)}
|
|
125
|
+
body.push(`${childPad}[key: string]: ${convert(additional, ctx, childPad)}${ctx.harmony ? "," : ";"}`);
|
|
114
126
|
}
|
|
115
127
|
return `{\n${body.join("\n")}\n${pad}}`;
|
|
116
128
|
}
|
|
@@ -188,9 +200,11 @@ export function jsonSchemaToTypeScript(schema: unknown, options?: JsonSchemaToTs
|
|
|
188
200
|
}
|
|
189
201
|
}
|
|
190
202
|
}
|
|
203
|
+
const harmony = options?.style === "harmony";
|
|
191
204
|
const ctx: Ctx = {
|
|
192
|
-
indent: options?.indent ?? " ",
|
|
205
|
+
indent: options?.indent ?? (harmony ? "" : " "),
|
|
193
206
|
comments: options?.comments ?? true,
|
|
207
|
+
harmony,
|
|
194
208
|
defs,
|
|
195
209
|
seen: new Set(),
|
|
196
210
|
};
|
package/src/utils.ts
CHANGED
|
@@ -403,7 +403,7 @@ function sanitizeOpenAIResponsesReasoningItemForReplay(
|
|
|
403
403
|
function sanitizeOpenAIResponsesImageGenerationCallForReplay(
|
|
404
404
|
item: Record<string, unknown>,
|
|
405
405
|
): ResponseInputItem.ImageGenerationCall | undefined {
|
|
406
|
-
if (typeof item.id !== "string" || item.
|
|
406
|
+
if (typeof item.id !== "string" || typeof item.result !== "string" || item.result.length === 0) {
|
|
407
407
|
return undefined;
|
|
408
408
|
}
|
|
409
409
|
return {
|