@gajae-code/ai 0.16.4 → 0.16.6
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 +7 -0
- package/dist/types/types.d.ts +2 -2
- package/dist/types/utils/provider-response.d.ts +1 -0
- package/package.json +3 -3
- package/src/model-thinking.ts +12 -1
- package/src/providers/amazon-bedrock.ts +10 -2
- package/src/providers/anthropic.ts +6 -1
- package/src/providers/azure-openai-responses.ts +5 -2
- package/src/providers/cursor.ts +1 -1
- package/src/providers/google-gemini-cli.ts +6 -1
- package/src/providers/google-shared.ts +1 -1
- package/src/providers/kiro-api-key.ts +5 -2
- package/src/providers/kiro-codewhisperer.ts +10 -2
- package/src/providers/mock.ts +1 -0
- package/src/providers/ollama.ts +1 -1
- package/src/providers/openai-codex-responses.ts +10 -2
- package/src/providers/openai-completions.ts +11 -2
- package/src/providers/openai-responses.ts +10 -2
- package/src/types.d.ts +2 -2
- package/src/types.ts +2 -0
- package/src/utils/provider-response.d.ts +1 -0
- package/src/utils/provider-response.ts +9 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.16.6] - 2026-09-07
|
|
6
|
+
|
|
7
|
+
## [0.16.5] - 2026-09-07
|
|
8
|
+
|
|
9
|
+
- Documented `GJC_OPENAI_CODE_WEBSOCKET_V2` as a switch that enables a websocket v2 path. No code read it under that name, under the legacy `PI_CODEX_WEBSOCKET_V2`, or under the `PI_OPENAI_CODE_WEBSOCKET_V2` the historical entry records; the v2 beta header has been unconditional for websocket transport. The documentation row is removed rather than reintroducing a knob, and the test that claimed to gate on it no longer writes an environment variable nothing reads.
|
|
10
|
+
- Maintenance reasoning now fails closed for Anthropic models routed through an unverified custom endpoint and for raw reasoning-enabled models without thinking metadata. This prevents unsupported thinking controls and avoids a synchronous missing-metadata crash before provider wire transformation.
|
|
11
|
+
|
|
5
12
|
## [0.16.4] - 2026-09-05
|
|
6
13
|
|
|
7
14
|
### Added
|
package/dist/types/types.d.ts
CHANGED
|
@@ -256,12 +256,12 @@ export interface StreamOptions {
|
|
|
256
256
|
* Return undefined to keep the payload unchanged.
|
|
257
257
|
* The `scope` parameter carries the per-attempt identity for execution attribution.
|
|
258
258
|
*/
|
|
259
|
-
onPayload?: (payload: unknown, model?: Model<Api>, scope?: AttemptScopeRef) => unknown | undefined | Promise<unknown | undefined>;
|
|
259
|
+
onPayload?: (payload: unknown, model?: Model<Api>, scope?: AttemptScopeRef, signal?: AbortSignal) => unknown | undefined | Promise<unknown | undefined>;
|
|
260
260
|
/**
|
|
261
261
|
* Optional callback for provider response metadata after headers are received.
|
|
262
262
|
* The `scope` parameter carries the per-attempt identity for execution attribution.
|
|
263
263
|
*/
|
|
264
|
-
onResponse?: (response: ProviderResponseMetadata, model?: Model<Api>, scope?: AttemptScopeRef) => void | Promise<void>;
|
|
264
|
+
onResponse?: (response: ProviderResponseMetadata, model?: Model<Api>, scope?: AttemptScopeRef, signal?: AbortSignal) => void | Promise<void>;
|
|
265
265
|
/**
|
|
266
266
|
* Internal dispatch-admission hook. Providers invoke this immediately before
|
|
267
267
|
* submitting an outbound request; stream forwarding retains a first-response
|
|
@@ -3,4 +3,5 @@ export declare function normalizeProviderResponse(response: Response, requestId?
|
|
|
3
3
|
export declare function notifyProviderResponse(options: {
|
|
4
4
|
onResponse?: StreamOptions["onResponse"];
|
|
5
5
|
attemptScope?: AttemptScopeRef;
|
|
6
|
+
signal?: AbortSignal;
|
|
6
7
|
} | undefined, response: Response, model?: Model<Api>, requestId?: string | null, metadata?: Record<string, unknown>): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/ai",
|
|
4
|
-
"version": "0.16.
|
|
4
|
+
"version": "0.16.6",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://gajae-code.com",
|
|
7
7
|
"author": "Yeachan-Heo and Gajae Code Contributors",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@anthropic-ai/sdk": "^0.94.0",
|
|
42
42
|
"@bufbuild/protobuf": "^2.12.0",
|
|
43
|
-
"@gajae-code/natives": "0.16.
|
|
44
|
-
"@gajae-code/utils": "0.16.
|
|
43
|
+
"@gajae-code/natives": "0.16.6",
|
|
44
|
+
"@gajae-code/utils": "0.16.6",
|
|
45
45
|
"openai": "^6.36.0",
|
|
46
46
|
"partial-json": "^0.1.7",
|
|
47
47
|
"zod": "4.4.3"
|
package/src/model-thinking.ts
CHANGED
|
@@ -242,7 +242,6 @@ export function modelSupportsReasoningControl<TApi extends Api>(
|
|
|
242
242
|
resolvedBaseUrl?: string,
|
|
243
243
|
): boolean {
|
|
244
244
|
if (!model.reasoning) return false;
|
|
245
|
-
|
|
246
245
|
if (model.api === "openai-completions") {
|
|
247
246
|
const completionsModel = model as ApiModel<"openai-completions">;
|
|
248
247
|
const explicitSupport = completionsModel.compat?.supportsReasoningEffort;
|
|
@@ -366,6 +365,18 @@ export function clampThinkingLevelForModel<TApi extends Api>(
|
|
|
366
365
|
if (!modelSupportsReasoningControl(model) || requested === undefined) {
|
|
367
366
|
return undefined;
|
|
368
367
|
}
|
|
368
|
+
if (model.api === "anthropic-messages" && model.provider === "anthropic") {
|
|
369
|
+
const baseUrl = model.baseUrl;
|
|
370
|
+
try {
|
|
371
|
+
const url = new URL(baseUrl || "https://api.anthropic.com");
|
|
372
|
+
if (url.protocol !== "https:" || url.hostname !== "api.anthropic.com") return undefined;
|
|
373
|
+
} catch {
|
|
374
|
+
return undefined;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (!model.thinking) {
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
369
380
|
|
|
370
381
|
const levels = getSupportedEfforts(model);
|
|
371
382
|
if (levels.includes(requested)) {
|
|
@@ -220,7 +220,7 @@ export const streamBedrock: StreamFunction<"bedrock-converse-stream"> = (
|
|
|
220
220
|
if (tc.any || tc.tool) additionalModelRequestFields = undefined;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
|
|
223
|
+
let commandInput: ConverseStreamRequest = {
|
|
224
224
|
messages: convertMessages(context, model, cacheRetention),
|
|
225
225
|
system: buildSystemPrompt(context.systemPrompt, model, cacheRetention),
|
|
226
226
|
inferenceConfig: {
|
|
@@ -231,7 +231,15 @@ export const streamBedrock: StreamFunction<"bedrock-converse-stream"> = (
|
|
|
231
231
|
toolConfig,
|
|
232
232
|
additionalModelRequestFields,
|
|
233
233
|
};
|
|
234
|
-
options?.onPayload?.(
|
|
234
|
+
const replacementPayload = await options?.onPayload?.(
|
|
235
|
+
commandInput,
|
|
236
|
+
model,
|
|
237
|
+
options?.attemptScope,
|
|
238
|
+
options?.signal,
|
|
239
|
+
);
|
|
240
|
+
if (replacementPayload !== undefined) {
|
|
241
|
+
commandInput = replacementPayload as typeof commandInput;
|
|
242
|
+
}
|
|
235
243
|
|
|
236
244
|
const host = `bedrock-runtime.${region}.amazonaws.com`;
|
|
237
245
|
const url = `https://${host}/model/${encodeURIComponent(model.id)}/converse-stream`;
|
|
@@ -1999,7 +1999,12 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1999
1999
|
if (dropFastMode) {
|
|
2000
2000
|
dropAnthropicFastMode(nextParams);
|
|
2001
2001
|
}
|
|
2002
|
-
const replacementPayload = await options?.onPayload?.(
|
|
2002
|
+
const replacementPayload = await options?.onPayload?.(
|
|
2003
|
+
nextParams,
|
|
2004
|
+
model,
|
|
2005
|
+
options?.attemptScope,
|
|
2006
|
+
options?.signal,
|
|
2007
|
+
);
|
|
2003
2008
|
if (replacementPayload !== undefined) {
|
|
2004
2009
|
nextParams = replacementPayload as typeof nextParams;
|
|
2005
2010
|
}
|
|
@@ -129,9 +129,12 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
|
|
|
129
129
|
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
130
130
|
const client = createClient(model, apiKey, options);
|
|
131
131
|
const { baseUrl } = resolveAzureConfig(model, options);
|
|
132
|
-
|
|
132
|
+
let params = buildParams(model, context, options, deploymentName, baseUrl);
|
|
133
133
|
const idleTimeoutMs = options?.streamIdleTimeoutMs ?? getOpenAIStreamIdleTimeoutMs();
|
|
134
|
-
options?.onPayload?.(params, model, options?.attemptScope);
|
|
134
|
+
const replacementPayload = await options?.onPayload?.(params, model, options?.attemptScope, options?.signal);
|
|
135
|
+
if (replacementPayload !== undefined) {
|
|
136
|
+
params = replacementPayload as typeof params;
|
|
137
|
+
}
|
|
135
138
|
rawRequestDump = {
|
|
136
139
|
provider: model.provider,
|
|
137
140
|
api: output.api,
|
package/src/providers/cursor.ts
CHANGED
|
@@ -3575,7 +3575,7 @@ async function buildGrpcRequest(
|
|
|
3575
3575
|
|
|
3576
3576
|
if (options?.onPayload) {
|
|
3577
3577
|
const payload = toJson(AgentRunRequestSchema, runRequest);
|
|
3578
|
-
const replacement = await options.onPayload(payload, model, options.attemptScope);
|
|
3578
|
+
const replacement = await options.onPayload(payload, model, options.attemptScope, options.signal);
|
|
3579
3579
|
if (replacement !== undefined) {
|
|
3580
3580
|
runRequest = fromJson(AgentRunRequestSchema, replacement as JsonValue);
|
|
3581
3581
|
}
|
|
@@ -354,7 +354,12 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
354
354
|
const endpoints = baseUrl ? [baseUrl] : isAntigravity ? ANTIGRAVITY_ENDPOINT_FALLBACKS : [DEFAULT_ENDPOINT];
|
|
355
355
|
|
|
356
356
|
let requestBody = buildRequest(model, context, projectId, options, isAntigravity);
|
|
357
|
-
const replacementPayload = await options?.onPayload?.(
|
|
357
|
+
const replacementPayload = await options?.onPayload?.(
|
|
358
|
+
requestBody,
|
|
359
|
+
model,
|
|
360
|
+
options?.attemptScope,
|
|
361
|
+
options?.signal,
|
|
362
|
+
);
|
|
358
363
|
if (replacementPayload !== undefined) {
|
|
359
364
|
requestBody = replacementPayload as typeof requestBody;
|
|
360
365
|
}
|
|
@@ -899,7 +899,7 @@ export function streamGoogleGenAI<T extends "google-generative-ai" | "google-ver
|
|
|
899
899
|
try {
|
|
900
900
|
const plan = await prepare();
|
|
901
901
|
let params = plan.params;
|
|
902
|
-
const replacement = await options?.onPayload?.(params, model, options?.attemptScope);
|
|
902
|
+
const replacement = await options?.onPayload?.(params, model, options?.attemptScope, options?.signal);
|
|
903
903
|
if (replacement !== undefined) {
|
|
904
904
|
params = replacement as GenerateContentParameters;
|
|
905
905
|
}
|
|
@@ -611,8 +611,11 @@ export const streamKiroApiKey: StreamFunction<"kiro-codewhisperer-stream"> = (
|
|
|
611
611
|
const configuredBaseUrl = model.baseUrl;
|
|
612
612
|
const usesExplicitBaseUrl = Boolean(configuredBaseUrl) && !isRegionDerivedKiroApiBaseUrl(configuredBaseUrl);
|
|
613
613
|
const endpoint = configuredBaseUrl || kiroApiBaseUrl(kiroApiRegion(options));
|
|
614
|
-
|
|
615
|
-
options?.onPayload?.(request, model, options?.attemptScope);
|
|
614
|
+
let request = buildApiKeyRequest(model, context, options);
|
|
615
|
+
const replacementPayload = await options?.onPayload?.(request, model, options?.attemptScope, options?.signal);
|
|
616
|
+
if (replacementPayload !== undefined) {
|
|
617
|
+
request = replacementPayload;
|
|
618
|
+
}
|
|
616
619
|
|
|
617
620
|
const response = await fetch(endpoint, {
|
|
618
621
|
method: "POST",
|
|
@@ -193,11 +193,19 @@ export const streamKiroCodeWhisperer: StreamFunction<"kiro-codewhisperer-stream"
|
|
|
193
193
|
|
|
194
194
|
// Build request
|
|
195
195
|
const conversationState = buildConversationState(context, model, options);
|
|
196
|
-
|
|
196
|
+
let requestBody: GenerateAssistantResponseRequest = {
|
|
197
197
|
conversationState,
|
|
198
198
|
};
|
|
199
199
|
|
|
200
|
-
options?.onPayload?.(
|
|
200
|
+
const replacementPayload = await options?.onPayload?.(
|
|
201
|
+
requestBody,
|
|
202
|
+
model,
|
|
203
|
+
options?.attemptScope,
|
|
204
|
+
options?.signal,
|
|
205
|
+
);
|
|
206
|
+
if (replacementPayload !== undefined) {
|
|
207
|
+
requestBody = replacementPayload as typeof requestBody;
|
|
208
|
+
}
|
|
201
209
|
|
|
202
210
|
const host = `${STREAMING_SERVICE_NAME}.${region}.amazonaws.com`;
|
|
203
211
|
const url = `https://${host}/`;
|
package/src/providers/mock.ts
CHANGED
package/src/providers/ollama.ts
CHANGED
|
@@ -407,7 +407,7 @@ export const streamOllama: StreamFunction<"ollama-chat"> = (
|
|
|
407
407
|
const baseUrl = normalizeBaseUrl(model.baseUrl);
|
|
408
408
|
let body = createChatBody(model, context, options);
|
|
409
409
|
const sentForcedToolChoice = body.tool_choice === "required";
|
|
410
|
-
const replacementPayload = await options.onPayload?.(body, model, options?.attemptScope);
|
|
410
|
+
const replacementPayload = await options.onPayload?.(body, model, options?.attemptScope, options?.signal);
|
|
411
411
|
if (replacementPayload !== undefined) {
|
|
412
412
|
body = replacementPayload as typeof body;
|
|
413
413
|
}
|
|
@@ -717,8 +717,16 @@ async function buildCodexRequestContext(
|
|
|
717
717
|
const baseUrl = model.baseUrl || CODEX_BASE_URL;
|
|
718
718
|
const url = resolveCodexResponsesUrl(baseUrl);
|
|
719
719
|
const promptCacheKey = normalizeOpenAIResponsesPromptCacheKey(options?.sessionId);
|
|
720
|
-
|
|
721
|
-
options?.onPayload?.(
|
|
720
|
+
let transformedBody = await buildTransformedCodexRequestBody(model, context, options);
|
|
721
|
+
const replacementPayload = await options?.onPayload?.(
|
|
722
|
+
transformedBody,
|
|
723
|
+
model,
|
|
724
|
+
options?.attemptScope,
|
|
725
|
+
options?.signal,
|
|
726
|
+
);
|
|
727
|
+
if (replacementPayload !== undefined) {
|
|
728
|
+
transformedBody = replacementPayload as typeof transformedBody;
|
|
729
|
+
}
|
|
722
730
|
|
|
723
731
|
const requestHeaders = { ...(model.headers ?? {}), ...(options?.headers ?? {}) };
|
|
724
732
|
const rawRequestDump: RawHttpRequestDump = {
|
|
@@ -624,15 +624,24 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
|
|
624
624
|
const createCompletionsStream = async (toolStrictModeOverride?: ToolStrictModeOverride) => {
|
|
625
625
|
clearCapturedErrorResponse();
|
|
626
626
|
const effectiveToolStrictModeOverride = disableStrictTools ? "none" : toolStrictModeOverride;
|
|
627
|
-
const { params, toolStrictMode } = buildParams(
|
|
627
|
+
const { params: builtParams, toolStrictMode } = buildParams(
|
|
628
628
|
model,
|
|
629
629
|
context,
|
|
630
630
|
options,
|
|
631
631
|
baseUrl,
|
|
632
632
|
effectiveToolStrictModeOverride,
|
|
633
633
|
);
|
|
634
|
+
let params = builtParams;
|
|
634
635
|
appliedToolStrictMode = toolStrictMode;
|
|
635
|
-
options?.onPayload?.(
|
|
636
|
+
const replacementPayload = await options?.onPayload?.(
|
|
637
|
+
params,
|
|
638
|
+
undefined,
|
|
639
|
+
options?.attemptScope,
|
|
640
|
+
options?.signal,
|
|
641
|
+
);
|
|
642
|
+
if (replacementPayload !== undefined) {
|
|
643
|
+
params = replacementPayload as typeof params;
|
|
644
|
+
}
|
|
636
645
|
rawRequestDump = {
|
|
637
646
|
provider: model.provider,
|
|
638
647
|
api: output.api,
|
|
@@ -395,9 +395,17 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
|
|
|
395
395
|
);
|
|
396
396
|
const premiumRequestsTotal = copilotPremiumRequests;
|
|
397
397
|
const providerSessionState = getOpenAIResponsesProviderSessionState(model, options?.providerSessionState);
|
|
398
|
-
|
|
398
|
+
let { params } = buildParams(model, context, options, providerSessionState, cacheRetention, baseUrl);
|
|
399
399
|
const idleTimeoutMs = options?.streamIdleTimeoutMs ?? getOpenAIStreamIdleTimeoutMs(model.provider, model.id);
|
|
400
|
-
options?.onPayload?.(
|
|
400
|
+
const replacementPayload = await options?.onPayload?.(
|
|
401
|
+
params,
|
|
402
|
+
undefined,
|
|
403
|
+
options?.attemptScope,
|
|
404
|
+
options?.signal,
|
|
405
|
+
);
|
|
406
|
+
if (replacementPayload !== undefined) {
|
|
407
|
+
params = replacementPayload as typeof params;
|
|
408
|
+
}
|
|
401
409
|
rawRequestDump = {
|
|
402
410
|
provider: model.provider,
|
|
403
411
|
api: output.api,
|
package/src/types.d.ts
CHANGED
|
@@ -256,12 +256,12 @@ export interface StreamOptions {
|
|
|
256
256
|
* Return undefined to keep the payload unchanged.
|
|
257
257
|
* The `scope` parameter carries the per-attempt identity for execution attribution.
|
|
258
258
|
*/
|
|
259
|
-
onPayload?: (payload: unknown, model?: Model<Api>, scope?: AttemptScopeRef) => unknown | undefined | Promise<unknown | undefined>;
|
|
259
|
+
onPayload?: (payload: unknown, model?: Model<Api>, scope?: AttemptScopeRef, signal?: AbortSignal) => unknown | undefined | Promise<unknown | undefined>;
|
|
260
260
|
/**
|
|
261
261
|
* Optional callback for provider response metadata after headers are received.
|
|
262
262
|
* The `scope` parameter carries the per-attempt identity for execution attribution.
|
|
263
263
|
*/
|
|
264
|
-
onResponse?: (response: ProviderResponseMetadata, model?: Model<Api>, scope?: AttemptScopeRef) => void | Promise<void>;
|
|
264
|
+
onResponse?: (response: ProviderResponseMetadata, model?: Model<Api>, scope?: AttemptScopeRef, signal?: AbortSignal) => void | Promise<void>;
|
|
265
265
|
/**
|
|
266
266
|
* Internal dispatch-admission hook. Providers invoke this immediately before
|
|
267
267
|
* submitting an outbound request; stream forwarding retains a first-response
|
package/src/types.ts
CHANGED
|
@@ -464,6 +464,7 @@ export interface StreamOptions {
|
|
|
464
464
|
payload: unknown,
|
|
465
465
|
model?: Model<Api>,
|
|
466
466
|
scope?: AttemptScopeRef,
|
|
467
|
+
signal?: AbortSignal,
|
|
467
468
|
) => unknown | undefined | Promise<unknown | undefined>;
|
|
468
469
|
/**
|
|
469
470
|
* Optional callback for provider response metadata after headers are received.
|
|
@@ -473,6 +474,7 @@ export interface StreamOptions {
|
|
|
473
474
|
response: ProviderResponseMetadata,
|
|
474
475
|
model?: Model<Api>,
|
|
475
476
|
scope?: AttemptScopeRef,
|
|
477
|
+
signal?: AbortSignal,
|
|
476
478
|
) => void | Promise<void>;
|
|
477
479
|
/**
|
|
478
480
|
* Internal dispatch-admission hook. Providers invoke this immediately before
|
|
@@ -3,4 +3,5 @@ export declare function normalizeProviderResponse(response: Response, requestId?
|
|
|
3
3
|
export declare function notifyProviderResponse(options: {
|
|
4
4
|
onResponse?: StreamOptions["onResponse"];
|
|
5
5
|
attemptScope?: AttemptScopeRef;
|
|
6
|
+
signal?: AbortSignal;
|
|
6
7
|
} | undefined, response: Response, model?: Model<Api>, requestId?: string | null, metadata?: Record<string, unknown>): Promise<void>;
|
|
@@ -19,12 +19,19 @@ export function normalizeProviderResponse(
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export async function notifyProviderResponse(
|
|
22
|
-
options:
|
|
22
|
+
options:
|
|
23
|
+
| { onResponse?: StreamOptions["onResponse"]; attemptScope?: AttemptScopeRef; signal?: AbortSignal }
|
|
24
|
+
| undefined,
|
|
23
25
|
response: Response,
|
|
24
26
|
model?: Model<Api>,
|
|
25
27
|
requestId?: string | null,
|
|
26
28
|
metadata?: Record<string, unknown>,
|
|
27
29
|
): Promise<void> {
|
|
28
30
|
if (!options?.onResponse) return;
|
|
29
|
-
await options.onResponse(
|
|
31
|
+
await options.onResponse(
|
|
32
|
+
normalizeProviderResponse(response, requestId, metadata),
|
|
33
|
+
model,
|
|
34
|
+
options.attemptScope,
|
|
35
|
+
options.signal,
|
|
36
|
+
);
|
|
30
37
|
}
|