@fractaal/pi-ai 0.84.8 → 0.85.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/dist/api/openai-codex-compaction-history.d.ts +10 -0
- package/dist/api/openai-codex-compaction-history.d.ts.map +1 -0
- package/dist/api/openai-codex-compaction-history.js +116 -0
- package/dist/api/openai-codex-compaction-history.js.map +1 -0
- package/dist/api/openai-codex-responses.d.ts +17 -3
- package/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/dist/api/openai-codex-responses.js +62 -25
- package/dist/api/openai-codex-responses.js.map +1 -1
- package/dist/utils/overflow.d.ts.map +1 -1
- package/dist/utils/overflow.js +1 -1
- package/dist/utils/overflow.js.map +1 -1
- package/package.json +1 -1
|
@@ -15,8 +15,10 @@ import { formatProviderError, normalizeProviderError } from "../utils/error-body
|
|
|
15
15
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
16
16
|
import { headersToRecord } from "../utils/headers.js";
|
|
17
17
|
import { resolveHttpProxyUrlForTarget } from "../utils/node-http-proxy.js";
|
|
18
|
+
import { isRetryableAssistantError, retryAssistantCall } from "../utils/retry.js";
|
|
18
19
|
import { uuidv7 } from "../utils/uuid.js";
|
|
19
20
|
import { createGrammarToolInputProperties } from "./constrained-sampling.js";
|
|
21
|
+
import { retainCodexCompactionInput, trimCodexCompactionToolOutputs } from "./openai-codex-compaction-history.js";
|
|
20
22
|
import { clampOpenAIPromptCacheKey } from "./openai-prompt-cache.js";
|
|
21
23
|
import { convertResponsesMessages, convertResponsesTools, processResponsesStream } from "./openai-responses-shared.js";
|
|
22
24
|
import { buildBaseOptions } from "./simple-options.js";
|
|
@@ -44,6 +46,7 @@ const CODEX_RESPONSE_STATUSES = new Set([
|
|
|
44
46
|
"queued",
|
|
45
47
|
"in_progress",
|
|
46
48
|
]);
|
|
49
|
+
export { estimateOpenAINativeCompactionTokens } from "./openai-codex-compaction-history.js";
|
|
47
50
|
export function isOpenAICodexProvider(provider) {
|
|
48
51
|
if (!provider || provider.id !== "openai-codex")
|
|
49
52
|
return false;
|
|
@@ -207,11 +210,15 @@ export const stream = (model, context, options) => {
|
|
|
207
210
|
timestamp: Date.now(),
|
|
208
211
|
};
|
|
209
212
|
try {
|
|
210
|
-
const
|
|
211
|
-
if (!
|
|
213
|
+
const transportAuth = options?.authMode === "transport";
|
|
214
|
+
if (transportAuth && (!options?.fetch || options.fetch === globalThis.fetch)) {
|
|
215
|
+
throw new Error("Codex transport auth requires a custom fetch that owns authentication");
|
|
216
|
+
}
|
|
217
|
+
const apiKey = transportAuth ? undefined : options?.apiKey;
|
|
218
|
+
if (!transportAuth && !apiKey) {
|
|
212
219
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
213
220
|
}
|
|
214
|
-
const accountId = extractAccountId(apiKey);
|
|
221
|
+
const accountId = apiKey ? extractAccountId(apiKey) : undefined;
|
|
215
222
|
const grammarToolInputProperties = createGrammarToolInputProperties(context.tools, model.compat?.supportsOpenAIGrammarTools ?? false);
|
|
216
223
|
const cacheSessionId = options?.cacheRetention === "none" ? undefined : options?.sessionId;
|
|
217
224
|
const codexSessionId = clampOpenAIPromptCacheKey(cacheSessionId);
|
|
@@ -226,7 +233,7 @@ export const stream = (model, context, options) => {
|
|
|
226
233
|
const bodyJson = JSON.stringify(body);
|
|
227
234
|
const httpTimeoutMs = normalizeTimeoutMs(options?.timeoutMs);
|
|
228
235
|
const websocketConnectTimeoutMs = normalizeTimeoutMs(options?.websocketConnectTimeoutMs);
|
|
229
|
-
const transport = options?.transport || "auto";
|
|
236
|
+
const transport = transportAuth ? "sse" : options?.transport || "auto";
|
|
230
237
|
let startEmitted = false;
|
|
231
238
|
const websocketDisabledForSession = transport !== "sse" && isWebSocketSseFallbackActive(cacheSessionId);
|
|
232
239
|
if (websocketDisabledForSession) {
|
|
@@ -413,13 +420,14 @@ export const stream = (model, context, options) => {
|
|
|
413
420
|
};
|
|
414
421
|
function buildSimpleCodexOptions(model, context, options) {
|
|
415
422
|
const apiKey = options?.apiKey;
|
|
416
|
-
if (!apiKey) {
|
|
423
|
+
if (!apiKey && options?.authMode !== "transport") {
|
|
417
424
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
418
425
|
}
|
|
419
426
|
const base = buildBaseOptions(model, context, options, apiKey);
|
|
420
427
|
const clampedReasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : undefined;
|
|
421
428
|
return {
|
|
422
429
|
...base,
|
|
430
|
+
authMode: options?.authMode,
|
|
423
431
|
reasoningEffort: clampedReasoning === "off" ? undefined : clampedReasoning,
|
|
424
432
|
nativeCompactionCheckpoint: options?.nativeCompactionCheckpoint,
|
|
425
433
|
};
|
|
@@ -427,12 +435,25 @@ function buildSimpleCodexOptions(model, context, options) {
|
|
|
427
435
|
export const streamSimple = (model, context, options) => stream(model, context, buildSimpleCodexOptions(model, context, options));
|
|
428
436
|
export async function compactOpenAICodexResponses(model, context, options) {
|
|
429
437
|
const items = [];
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
438
|
+
const base = buildSimpleCodexOptions(model, context, options);
|
|
439
|
+
let transport = base.transport;
|
|
440
|
+
const produce = () => {
|
|
441
|
+
items.length = 0;
|
|
442
|
+
return stream(model, context, {
|
|
443
|
+
...base,
|
|
444
|
+
transport,
|
|
445
|
+
// Own one bounded stream retry budget; do not multiply HTTP retries.
|
|
446
|
+
maxRetries: 0,
|
|
447
|
+
nativeCompaction: true,
|
|
448
|
+
onNativeCompactionItem: (item) => items.push(item),
|
|
449
|
+
}).result();
|
|
450
|
+
};
|
|
451
|
+
const policy = { enabled: true, maxRetries: 2, baseDelayMs: BASE_DELAY_MS };
|
|
452
|
+
let result = await retryAssistantCall(produce, policy, options?.signal);
|
|
453
|
+
if (transport !== "sse" && base.authMode !== "transport" && isRetryableAssistantError(result)) {
|
|
454
|
+
transport = "sse";
|
|
455
|
+
result = await retryAssistantCall(produce, policy, options?.signal);
|
|
456
|
+
}
|
|
436
457
|
if (result.stopReason !== "stop") {
|
|
437
458
|
throw new OpenAICodexNativeCompactionError(result.errorMessage || `OpenAI native compaction did not complete (${result.stopReason})`, getOpenAICodexResponseFailure(result));
|
|
438
459
|
}
|
|
@@ -443,13 +464,12 @@ export async function compactOpenAICodexResponses(model, context, options) {
|
|
|
443
464
|
if (!isOpenAINativeCompactionItem(item)) {
|
|
444
465
|
throw new Error("OpenAI native compaction returned a malformed checkpoint item");
|
|
445
466
|
}
|
|
446
|
-
closeOpenAICodexWebSocketSessions(options?.sessionId);
|
|
447
467
|
return {
|
|
448
|
-
item: {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
},
|
|
468
|
+
item: { ...item },
|
|
469
|
+
retainedInput: retainCodexCompactionInput(buildRequestBody(model, {
|
|
470
|
+
...context,
|
|
471
|
+
messages: (options?.nativeCompactionRetainedMessages ?? context.messages).filter((message) => message.role === "user"),
|
|
472
|
+
}, base, undefined).input ?? []),
|
|
453
473
|
tokensBefore: result.usage.input + result.usage.cacheRead,
|
|
454
474
|
usage: result.usage,
|
|
455
475
|
};
|
|
@@ -461,7 +481,12 @@ function buildRequestBody(model, context, options, cacheSessionId, grammarToolIn
|
|
|
461
481
|
const supportsStrictMode = model.compat?.supportsStrictMode ?? true;
|
|
462
482
|
const supportsOpenAIGrammarTools = model.compat?.supportsOpenAIGrammarTools ?? false;
|
|
463
483
|
const toolPlacement = splitDeferredTools(context, model.compat?.supportsToolSearch ?? false);
|
|
464
|
-
const
|
|
484
|
+
const checkpoint = options?.nativeCompactionCheckpoint;
|
|
485
|
+
const checkpointInput = checkpoint ? [...(checkpoint.retainedInput ?? []), checkpoint.item] : [];
|
|
486
|
+
const requestContext = options?.nativeCompaction
|
|
487
|
+
? trimCodexCompactionToolOutputs(context, checkpointInput, model.contextWindow)
|
|
488
|
+
: context;
|
|
489
|
+
const messages = convertResponsesMessages(model, requestContext, CODEX_TOOL_CALL_PROVIDERS, {
|
|
465
490
|
includeSystemPrompt: false,
|
|
466
491
|
grammarToolInputProperties,
|
|
467
492
|
deferredTools: toolPlacement.deferred,
|
|
@@ -471,15 +496,16 @@ function buildRequestBody(model, context, options, cacheSessionId, grammarToolIn
|
|
|
471
496
|
supportsOpenAIGrammarTools,
|
|
472
497
|
},
|
|
473
498
|
});
|
|
474
|
-
const checkpoint = options?.nativeCompactionCheckpoint;
|
|
475
499
|
if (checkpoint) {
|
|
476
|
-
if (checkpoint.provider !== model.provider ||
|
|
477
|
-
throw new Error(`OpenAI native compaction checkpoint requires
|
|
500
|
+
if (checkpoint.provider !== model.provider || model.api !== "openai-codex-responses") {
|
|
501
|
+
throw new Error(`OpenAI native compaction checkpoint requires the openai-codex Responses route; current model is ${model.provider}/${model.id}`);
|
|
478
502
|
}
|
|
479
503
|
if (!isOpenAINativeCompactionItem(checkpoint.item)) {
|
|
480
504
|
throw new Error("Stored OpenAI native compaction checkpoint is malformed");
|
|
481
505
|
}
|
|
482
|
-
messages.unshift(
|
|
506
|
+
messages.unshift(...(checkpoint.retainedInput ?? []), {
|
|
507
|
+
...checkpoint.item,
|
|
508
|
+
});
|
|
483
509
|
}
|
|
484
510
|
if (options?.nativeCompaction) {
|
|
485
511
|
messages.push({ type: "compaction_trigger" });
|
|
@@ -1266,7 +1292,7 @@ async function processWebSocketStream(url, body, headers, output, stream, model,
|
|
|
1266
1292
|
}
|
|
1267
1293
|
try {
|
|
1268
1294
|
socket.send(JSON.stringify({ type: "response.create", ...requestBody }));
|
|
1269
|
-
await processResponsesStream(startWebSocketOutputOnFirstEvent(mapCodexEvents(parseWebSocket(socket, options?.signal, idleTimeoutMs)), onStart), output, stream, model, {
|
|
1295
|
+
await processResponsesStream(startWebSocketOutputOnFirstEvent(mapCodexEvents(parseWebSocket(socket, options?.signal, idleTimeoutMs), options?.onNativeCompactionItem), onStart), output, stream, model, {
|
|
1270
1296
|
serviceTier: options?.serviceTier,
|
|
1271
1297
|
grammarToolInputProperties,
|
|
1272
1298
|
resolveServiceTier: resolveCodexServiceTier,
|
|
@@ -1275,6 +1301,10 @@ async function processWebSocketStream(url, body, headers, output, stream, model,
|
|
|
1275
1301
|
if (options?.signal?.aborted) {
|
|
1276
1302
|
keepConnection = false;
|
|
1277
1303
|
}
|
|
1304
|
+
else if (options?.nativeCompaction && entry) {
|
|
1305
|
+
// The rewritten history must be sent in full, but the connection stays reusable.
|
|
1306
|
+
entry.continuation = undefined;
|
|
1307
|
+
}
|
|
1278
1308
|
else if (useCachedContext && entry && output.responseId) {
|
|
1279
1309
|
const responseItems = convertResponsesMessages(model, { messages: [output] }, CODEX_TOOL_CALL_PROVIDERS, {
|
|
1280
1310
|
includeSystemPrompt: false,
|
|
@@ -1354,8 +1384,15 @@ function buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token)
|
|
|
1354
1384
|
headers.set(key, value);
|
|
1355
1385
|
}
|
|
1356
1386
|
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1387
|
+
if (token && accountId) {
|
|
1388
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
1389
|
+
headers.set("chatgpt-account-id", accountId);
|
|
1390
|
+
}
|
|
1391
|
+
else {
|
|
1392
|
+
// The custom transport supplies its own authentication, never a copied token.
|
|
1393
|
+
headers.delete("Authorization");
|
|
1394
|
+
headers.delete("chatgpt-account-id");
|
|
1395
|
+
}
|
|
1359
1396
|
headers.set("originator", "pi");
|
|
1360
1397
|
const userAgent = _os ? `pi (${_os.platform()} ${_os.release()}; ${_os.arch()})` : "pi (browser)";
|
|
1361
1398
|
headers.set("User-Agent", userAgent);
|