@oh-my-pi/pi-ai 17.2.13 → 17.2.15
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 +13 -0
- package/dist/types/providers/azure-openai-responses.d.ts +1 -0
- package/dist/types/providers/openai-codex/request-transformer.d.ts +2 -0
- package/dist/types/providers/openai-responses.d.ts +1 -0
- package/dist/types/providers/openai-shared.d.ts +10 -0
- package/dist/types/registry/amazon-bedrock.d.ts +1 -2
- package/dist/types/registry/aws.d.ts +3 -1
- package/dist/types/registry/registry.d.ts +1 -1
- package/dist/types/types.d.ts +6 -0
- package/package.json +5 -5
- package/src/providers/azure-openai-responses.ts +1 -0
- package/src/providers/google-antigravity-forced-tool.md +1 -0
- package/src/providers/google-gemini-cli.ts +8 -0
- package/src/providers/google-shared.ts +12 -7
- package/src/providers/openai-codex/request-transformer.ts +9 -4
- package/src/providers/openai-codex-responses.ts +1 -0
- package/src/providers/openai-reasoning-fallback.ts +21 -8
- package/src/providers/openai-responses.ts +73 -12
- package/src/providers/openai-shared.ts +26 -0
- package/src/registry/amazon-bedrock.ts +1 -1
- package/src/registry/aws.ts +3 -2
- package/src/stream.ts +31 -15
- package/src/types.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [17.2.15] - 2026-08-12
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed an issue where AWS_BEDROCK_SKIP_AUTH failed to expose Amazon Bedrock models when AWS credential files were unavailable.
|
|
10
|
+
- Fixed an issue where forceReasoningOff was ignored by Anthropic and Google transports, which allowed native thinking alongside a caller-supplied external scratchpad.
|
|
11
|
+
|
|
12
|
+
## [17.2.14] - 2026-08-11
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Added `forceReasoningOff` and `disableReasoning` options to disable reasoning in OpenAI and Azure OpenAI models
|
|
17
|
+
|
|
5
18
|
## [17.2.13] - 2026-08-11
|
|
6
19
|
|
|
7
20
|
### Changed
|
|
@@ -13,6 +13,8 @@ export interface ReasoningConfig {
|
|
|
13
13
|
export interface CodexRequestOptions {
|
|
14
14
|
/** User-facing effort; maps 1:1 onto the wire tier of the same name. */
|
|
15
15
|
reasoningEffort?: CodexCallerEffort | "none";
|
|
16
|
+
/** Suppress native reasoning by sending `reasoning.effort: "none"`. */
|
|
17
|
+
reasoningOff?: boolean;
|
|
16
18
|
reasoningSummary?: ReasoningConfig["summary"] | null;
|
|
17
19
|
/** Explicit `reasoning.context` override. Omitted by default; Responses Lite forces `all_turns` as required by that transport. */
|
|
18
20
|
reasoningContext?: CodexReasoningContext;
|
|
@@ -114,6 +114,7 @@ type OpenAIResponsesSamplingParams = ResponseCreateParamsStreaming & {
|
|
|
114
114
|
export declare const streamOpenAIResponses: StreamFunction<"openai-responses">;
|
|
115
115
|
export declare function buildParams(model: Model<"openai-responses">, context: Context, options: OpenAIResponsesOptions | undefined, providerSessionState: OpenAIResponsesProviderSessionState | undefined, strictToolsScope?: OpenAIStrictToolsScope, disableStrictToolsOverride?: boolean, statefulCacheBaseline?: ResponseInput): {
|
|
116
116
|
params: OpenAIResponsesSamplingParams;
|
|
117
|
+
trailingScaffoldingItems: number;
|
|
117
118
|
strictToolsApplied: boolean;
|
|
118
119
|
};
|
|
119
120
|
/**
|
|
@@ -505,6 +505,8 @@ export declare function appendMessageContentPart(item: ResponseOutputMessage, pa
|
|
|
505
505
|
export declare function appendMessageTextDelta(item: ResponseOutputMessage, block: TextContent, delta: string, stream: AssistantMessageEventStream, output: AssistantMessage, contentIndex: number, partType: "output_text" | "refusal"): void;
|
|
506
506
|
/** Chooses final message text while treating non-empty terminal content as authoritative. */
|
|
507
507
|
export declare function finalizeMessageText(item: ResponseOutputMessage, streamedText: string): string;
|
|
508
|
+
export declare const JUICE_EFFORT_MAP: Record<string, number>;
|
|
509
|
+
export declare function getJuiceValue(effort?: string): number;
|
|
508
510
|
export declare function accumulateToolCallArgumentsDelta(block: ResponsesToolCallBlock, delta: string, stream: AssistantMessageEventStream, output: AssistantMessage, contentIndex: number): void;
|
|
509
511
|
/**
|
|
510
512
|
* Finalize streamed function-call arguments from the authoritative `.done`
|
|
@@ -591,6 +593,14 @@ type ReasoningOptions = {
|
|
|
591
593
|
export interface ApplyResponsesCompatPolicyOptions {
|
|
592
594
|
reasoningSummary?: "auto" | "detailed" | "concise" | null;
|
|
593
595
|
mapEffort?: (effort: string) => string;
|
|
596
|
+
/**
|
|
597
|
+
* Suppress native reasoning by sending `reasoning.effort: "none"` — the only
|
|
598
|
+
* disable level the Responses API defines (`"off"` is not a wire value and
|
|
599
|
+
* 400s everywhere). Gateways that reject `none` for a given model are
|
|
600
|
+
* handled by the reasoning-effort fallback retry, which clamps to the
|
|
601
|
+
* lowest level the error reports as allowed.
|
|
602
|
+
*/
|
|
603
|
+
forceReasoningOff?: boolean;
|
|
594
604
|
}
|
|
595
605
|
export declare function applyResponsesCompatPolicy<P extends ResponseCreateParamsStreaming>(params: P, policy: OpenAICompatPolicy, options: ApplyResponsesCompatPolicyOptions | undefined): void;
|
|
596
606
|
/**
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { resolveAwsRegistryApiKey } from "./aws.js";
|
|
2
1
|
export declare const amazonBedrockProvider: {
|
|
3
2
|
readonly id: "amazon-bedrock";
|
|
4
3
|
readonly name: "Amazon Bedrock";
|
|
5
|
-
readonly envKeys:
|
|
4
|
+
readonly envKeys: () => string | undefined;
|
|
6
5
|
readonly mapSimpleOptions: (options: import("../index.js").SimpleStreamOptions) => {
|
|
7
6
|
region: string | undefined;
|
|
8
7
|
profile: string | undefined;
|
|
@@ -8,6 +8,8 @@ export interface AwsBedrockProviderOptions extends Readonly<Record<string, unkno
|
|
|
8
8
|
}
|
|
9
9
|
export declare function hasAwsCredentialSource(): boolean;
|
|
10
10
|
/** Registry key marker for AWS transports that resolve their own bearer/IAM credentials. */
|
|
11
|
-
export declare function resolveAwsRegistryApiKey(
|
|
11
|
+
export declare function resolveAwsRegistryApiKey(options?: {
|
|
12
|
+
allowSkipAuth?: boolean;
|
|
13
|
+
}): string | undefined;
|
|
12
14
|
/** Resolve a real AWS bearer token while filtering the registry's auth marker. */
|
|
13
15
|
export declare function resolveAwsBearerToken(apiKey?: string, bearerToken?: string): string | undefined;
|
|
@@ -25,7 +25,7 @@ declare const ALL: ({
|
|
|
25
25
|
} | {
|
|
26
26
|
readonly id: "amazon-bedrock";
|
|
27
27
|
readonly name: "Amazon Bedrock";
|
|
28
|
-
readonly envKeys:
|
|
28
|
+
readonly envKeys: () => string | undefined;
|
|
29
29
|
readonly mapSimpleOptions: (options: import("../index.js").SimpleStreamOptions) => {
|
|
30
30
|
region: string | undefined;
|
|
31
31
|
profile: string | undefined;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -294,6 +294,12 @@ export interface StreamOptions {
|
|
|
294
294
|
* `false` so `previous_response_id` cannot explain a result.
|
|
295
295
|
*/
|
|
296
296
|
statefulResponses?: boolean;
|
|
297
|
+
/**
|
|
298
|
+
* Disable native reasoning when the caller supplies an external scratchpad.
|
|
299
|
+
* OpenAI Responses emits `reasoning: { effort: "none" }`; Anthropic and
|
|
300
|
+
* Google transports use their native thinking-off controls.
|
|
301
|
+
*/
|
|
302
|
+
forceReasoningOff?: boolean;
|
|
297
303
|
/**
|
|
298
304
|
* Provider-scoped mutable state store for this agent session.
|
|
299
305
|
* Providers can use this to persist transport/session state between turns.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-ai",
|
|
4
|
-
"version": "17.2.
|
|
4
|
+
"version": "17.2.15",
|
|
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,10 +38,10 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@bufbuild/protobuf": "^2.12.1",
|
|
41
|
-
"@oh-my-pi/omptype": "17.2.
|
|
42
|
-
"@oh-my-pi/pi-catalog": "17.2.
|
|
43
|
-
"@oh-my-pi/pi-utils": "17.2.
|
|
44
|
-
"@oh-my-pi/pi-wire": "17.2.
|
|
41
|
+
"@oh-my-pi/omptype": "17.2.15",
|
|
42
|
+
"@oh-my-pi/pi-catalog": "17.2.15",
|
|
43
|
+
"@oh-my-pi/pi-utils": "17.2.15",
|
|
44
|
+
"@oh-my-pi/pi-wire": "17.2.15"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@bufbuild/protoc-gen-es": "^2.12.1",
|
|
@@ -65,6 +65,7 @@ export interface AzureOpenAIResponsesOptions extends StreamOptions {
|
|
|
65
65
|
azureDeploymentName?: string;
|
|
66
66
|
toolChoice?: ToolChoice;
|
|
67
67
|
serviceTier?: ServiceTier;
|
|
68
|
+
disableReasoning?: boolean;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
type AzureOpenAIResponsesSamplingParams = ResponseCreateParamsStreaming & {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
TOOL-ONLY TURN. This turn accepts a tool call and nothing else; a text reply here is discarded unread and you will be re-prompted. Emit the tool call now.
|
|
@@ -36,6 +36,7 @@ import { armPreResponseTimeout, getStreamFirstEventTimeoutMs, iterateWithIdleTim
|
|
|
36
36
|
// the stream provider trusts the access token threaded through `options.apiKey`.
|
|
37
37
|
import { normalizeSchemaForCCA } from "../utils/schema";
|
|
38
38
|
import { StreamMarkupHealing, type StreamMarkupHealingEvent } from "../utils/stream-markup-healing";
|
|
39
|
+
import forcedToolDirective from "./google-antigravity-forced-tool.md" with { type: "text" };
|
|
39
40
|
import type { Content, FunctionCallingConfigMode, ThinkingConfig } from "./google-shared";
|
|
40
41
|
import {
|
|
41
42
|
convertMessages,
|
|
@@ -1347,6 +1348,13 @@ export function buildRequest(
|
|
|
1347
1348
|
},
|
|
1348
1349
|
};
|
|
1349
1350
|
}
|
|
1351
|
+
// Cloud Code Assist drops `toolConfig` on Antigravity's Gemini routes:
|
|
1352
|
+
// the backend answers in text under `mode: "ANY"` and still emits calls
|
|
1353
|
+
// under `"NONE"`. Claude routes implement it, so only Gemini needs the
|
|
1354
|
+
// forced choice restated in the transcript.
|
|
1355
|
+
if (isAntigravity && !isClaudeModel(model.id) && request.toolConfig?.functionCallingConfig.mode === "ANY") {
|
|
1356
|
+
contents.push({ role: "user", parts: [{ text: forcedToolDirective }] });
|
|
1357
|
+
}
|
|
1350
1358
|
}
|
|
1351
1359
|
// Antigravity's default tool mode is VALIDATED (verified for Gemini and
|
|
1352
1360
|
// Claude); an explicit non-auto tool choice above wins.
|
|
@@ -858,13 +858,18 @@ export function buildGoogleGenerateContentParams<T extends "google-generative-ai
|
|
|
858
858
|
config.toolConfig = undefined;
|
|
859
859
|
}
|
|
860
860
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
861
|
+
const thinking = options.thinking;
|
|
862
|
+
if (
|
|
863
|
+
thinking &&
|
|
864
|
+
model.reasoning &&
|
|
865
|
+
(thinking.enabled || thinking.level !== undefined || thinking.budgetTokens !== undefined)
|
|
866
|
+
) {
|
|
867
|
+
const cfg: ThinkingConfig = { includeThoughts: thinking.enabled && !options.hideThinkingSummary };
|
|
868
|
+
if (thinking.level !== undefined) {
|
|
869
|
+
// GoogleThinkingLevel mirrors the SDK's ThinkingLevel string enum values 1:1.
|
|
870
|
+
cfg.thinkingLevel = thinking.level as ThinkingLevel;
|
|
871
|
+
} else if (thinking.budgetTokens !== undefined) {
|
|
872
|
+
cfg.thinkingBudget = thinking.budgetTokens;
|
|
868
873
|
}
|
|
869
874
|
config.thinkingConfig = cfg;
|
|
870
875
|
}
|
|
@@ -32,6 +32,8 @@ export interface ReasoningConfig {
|
|
|
32
32
|
export interface CodexRequestOptions {
|
|
33
33
|
/** User-facing effort; maps 1:1 onto the wire tier of the same name. */
|
|
34
34
|
reasoningEffort?: CodexCallerEffort | "none";
|
|
35
|
+
/** Suppress native reasoning by sending `reasoning.effort: "none"`. */
|
|
36
|
+
reasoningOff?: boolean;
|
|
35
37
|
reasoningSummary?: ReasoningConfig["summary"] | null;
|
|
36
38
|
/** Explicit `reasoning.context` override. Omitted by default; Responses Lite forces `all_turns` as required by that transport. */
|
|
37
39
|
reasoningContext?: CodexReasoningContext;
|
|
@@ -454,9 +456,12 @@ export async function transformRequestBody(
|
|
|
454
456
|
applyCodexResponsesLiteShape(body);
|
|
455
457
|
}
|
|
456
458
|
|
|
457
|
-
if (options.reasoningEffort !== undefined || responsesLite) {
|
|
458
|
-
const reasoningConfig =
|
|
459
|
-
|
|
459
|
+
if (options.reasoningOff || options.reasoningEffort !== undefined || responsesLite) {
|
|
460
|
+
const reasoningConfig: Partial<ReasoningConfig> = options.reasoningOff
|
|
461
|
+
? { effort: "none" }
|
|
462
|
+
: options.reasoningEffort !== undefined
|
|
463
|
+
? getReasoningConfig(model, options.reasoningEffort, options)
|
|
464
|
+
: {};
|
|
460
465
|
body.reasoning = {
|
|
461
466
|
...body.reasoning,
|
|
462
467
|
...reasoningConfig,
|
|
@@ -478,7 +483,7 @@ export async function transformRequestBody(
|
|
|
478
483
|
// Catalog pro aliases (`gpt-5.6-*-pro`): applied after the effort branch so
|
|
479
484
|
// the mode is sent even when no effort is set (the branch above deletes
|
|
480
485
|
// `body.reasoning` in that case) — mode and effort are independent fields.
|
|
481
|
-
if (model.reasoningMode) {
|
|
486
|
+
if (model.reasoningMode && !options.reasoningOff) {
|
|
482
487
|
body.reasoning = { ...body.reasoning, mode: model.reasoningMode };
|
|
483
488
|
}
|
|
484
489
|
|
|
@@ -1529,6 +1529,7 @@ export async function buildTransformedCodexRequestBody(
|
|
|
1529
1529
|
}
|
|
1530
1530
|
const codexOptions: CodexRequestOptions = {
|
|
1531
1531
|
reasoningEffort: options?.reasoning,
|
|
1532
|
+
reasoningOff: options?.forceReasoningOff,
|
|
1532
1533
|
reasoningSummary: options?.reasoningSummary,
|
|
1533
1534
|
reasoningContext: options?.reasoningContext,
|
|
1534
1535
|
textVerbosity: options?.textVerbosity,
|
|
@@ -132,7 +132,13 @@ function collectMessageParts(error: unknown, captured: CapturedHttpErrorResponse
|
|
|
132
132
|
return parts.join("\n");
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Text that identifies a 400 as being about the reasoning-effort field.
|
|
137
|
+
* OpenAI-compatible gateways (cliproxy, …) never name the field — they reject
|
|
138
|
+
* the value alone with `level "none" not supported, valid levels: low, …` — so
|
|
139
|
+
* the allowed-level phrasing counts as a mention too.
|
|
140
|
+
*/
|
|
141
|
+
const REASONING_EFFORT_FIELD_PATTERN = /reasoning[_. ]effort|reasoning value|(?:valid|supported|allowed) levels?/i;
|
|
136
142
|
|
|
137
143
|
function mentionsReasoningEffort(error: unknown, captured: CapturedHttpErrorResponse | undefined): boolean {
|
|
138
144
|
const param = capturedStringField(captured, "param");
|
|
@@ -168,10 +174,13 @@ function isInvalidReasoningEffortError(
|
|
|
168
174
|
if (/(?:unsupported|not supported)[^\n]*(?:reasoning[_. ]effort|reasoning value)/i.test(message)) {
|
|
169
175
|
return true;
|
|
170
176
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
177
|
+
// Gateways put the rejected value first (`level "none" not supported`), the
|
|
178
|
+
// official API puts the verdict first (`Unsupported value: 'none'`).
|
|
179
|
+
const quoted = `["'\`]${escapeRegExp(currentEffort)}["'\`]`;
|
|
180
|
+
return (
|
|
181
|
+
new RegExp(`(?:invalid|unsupported|not supported)[^\\n]*${quoted}`, "i").test(message) ||
|
|
182
|
+
new RegExp(`${quoted}[^\\n]*(?:invalid|unsupported|not supported)`, "i").test(message)
|
|
183
|
+
);
|
|
175
184
|
}
|
|
176
185
|
|
|
177
186
|
function escapeRegExp(value: string): string {
|
|
@@ -186,9 +195,12 @@ function parseKnownReasoningValues(text: string): Set<string> {
|
|
|
186
195
|
values.add(quotedMatch[1]!.toLowerCase());
|
|
187
196
|
quotedMatch = quotedPattern.exec(text);
|
|
188
197
|
}
|
|
189
|
-
const allowedMatch =
|
|
198
|
+
const allowedMatch =
|
|
199
|
+
/(?:must be|one of|allowed values?|supported values?(?: are)?|expected|(?:valid|supported|allowed) levels?(?: are)?)[^.\n]+/i.exec(
|
|
200
|
+
text,
|
|
201
|
+
);
|
|
190
202
|
if (allowedMatch) {
|
|
191
|
-
const allowedText = allowedMatch[
|
|
203
|
+
const allowedText = allowedMatch[0]!;
|
|
192
204
|
const barePattern = /\b(none|minimal|low|medium|high|xhigh|max)\b/gi;
|
|
193
205
|
let bareMatch = barePattern.exec(allowedText);
|
|
194
206
|
while (bareMatch !== null) {
|
|
@@ -201,7 +213,8 @@ function parseKnownReasoningValues(text: string): Set<string> {
|
|
|
201
213
|
|
|
202
214
|
function parseAllowedReasoningValues(message: string, currentEffort: string): Set<string> | undefined {
|
|
203
215
|
const values = parseKnownReasoningValues(message);
|
|
204
|
-
const hasAllowedCue =
|
|
216
|
+
const hasAllowedCue =
|
|
217
|
+
/must be|one of|allowed values?|supported values?|expected|(?:valid|supported|allowed) levels?/i.test(message);
|
|
205
218
|
values.delete(currentEffort.toLowerCase());
|
|
206
219
|
if (!hasAllowedCue && values.size === 0) return undefined;
|
|
207
220
|
return values;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { scheduler } from "node:timers/promises";
|
|
2
2
|
import { hostMatchesUrl } from "@oh-my-pi/pi-catalog/hosts";
|
|
3
|
+
import { bareModelId, parseOpenAIModel, semverGte } from "@oh-my-pi/pi-catalog/identity";
|
|
3
4
|
import { $flag, logger, structuredCloneJSON } from "@oh-my-pi/pi-utils";
|
|
4
5
|
import * as AIError from "../error";
|
|
5
6
|
import { getEnvApiKey } from "../stream";
|
|
@@ -80,6 +81,7 @@ import {
|
|
|
80
81
|
createInitialResponsesAssistantMessage,
|
|
81
82
|
createOpenAIStrictToolsState,
|
|
82
83
|
disableStrictToolsForScope,
|
|
84
|
+
getJuiceValue,
|
|
83
85
|
getOpenAIPromptCacheKey,
|
|
84
86
|
getOpenAIResponsesRoutingSessionId,
|
|
85
87
|
getOpenAIStrictToolsScope,
|
|
@@ -298,14 +300,23 @@ interface OpenAIResponsesChainedParams {
|
|
|
298
300
|
*/
|
|
299
301
|
function buildOpenAIResponsesChainedParams(
|
|
300
302
|
params: OpenAIResponsesSamplingParams,
|
|
303
|
+
trailingScaffoldingItems: number,
|
|
301
304
|
chain: OpenAIResponsesChainState,
|
|
302
305
|
): OpenAIResponsesChainedParams {
|
|
306
|
+
const historyParams =
|
|
307
|
+
trailingScaffoldingItems > 0 && Array.isArray(params.input)
|
|
308
|
+
? { ...params, input: params.input.slice(0, params.input.length - trailingScaffoldingItems) }
|
|
309
|
+
: params;
|
|
303
310
|
const deltaInput = chain.canAppend
|
|
304
|
-
? buildResponsesDeltaInput(chain.lastParams, chain.lastResponseItems,
|
|
311
|
+
? buildResponsesDeltaInput(chain.lastParams, chain.lastResponseItems, historyParams)
|
|
305
312
|
: null;
|
|
306
313
|
if (deltaInput && deltaInput.length > 0 && chain.lastResponseId) {
|
|
314
|
+
const scaffolding =
|
|
315
|
+
historyParams !== params && Array.isArray(params.input)
|
|
316
|
+
? params.input.slice(params.input.length - trailingScaffoldingItems)
|
|
317
|
+
: [];
|
|
307
318
|
return {
|
|
308
|
-
params: { ...params, previous_response_id: chain.lastResponseId, input: deltaInput },
|
|
319
|
+
params: { ...params, previous_response_id: chain.lastResponseId, input: [...deltaInput, ...scaffolding] },
|
|
309
320
|
previousResponseId: chain.lastResponseId,
|
|
310
321
|
};
|
|
311
322
|
}
|
|
@@ -462,8 +473,9 @@ const streamOpenAIResponsesOnce = (
|
|
|
462
473
|
false,
|
|
463
474
|
chainState?.canAppend ? chainState.lastParams?.input : undefined,
|
|
464
475
|
);
|
|
465
|
-
const params = builtParams
|
|
476
|
+
const { params, trailingScaffoldingItems } = builtParams;
|
|
466
477
|
let activeParams = params;
|
|
478
|
+
let activeTrailingScaffoldingItems = trailingScaffoldingItems;
|
|
467
479
|
const resolvedBaseUrl = (baseUrl ?? "https://api.openai.com/v1").replace(/\/+$/, "");
|
|
468
480
|
const requestReasoningEffortFallbacks = new Map<string, OpenAIReasoningEffortFallback>();
|
|
469
481
|
const attemptedReasoningEffortFallbacks = new Set<string>();
|
|
@@ -490,7 +502,9 @@ const streamOpenAIResponsesOnce = (
|
|
|
490
502
|
}
|
|
491
503
|
applyReasoningEffortFallbackForRequest(params);
|
|
492
504
|
let chained: OpenAIResponsesChainedParams =
|
|
493
|
-
chainState && !chainState.disabled
|
|
505
|
+
chainState && !chainState.disabled
|
|
506
|
+
? buildOpenAIResponsesChainedParams(params, trailingScaffoldingItems, chainState)
|
|
507
|
+
: { params };
|
|
494
508
|
sentPreviousResponseId = chained.previousResponseId;
|
|
495
509
|
const idleTimeoutMs =
|
|
496
510
|
options?.streamIdleTimeoutMs ?? getOpenAIStreamIdleTimeoutMs(model.compat.streamIdleTimeoutMs);
|
|
@@ -586,7 +600,9 @@ const streamOpenAIResponsesOnce = (
|
|
|
586
600
|
const reasoningEffortFallback =
|
|
587
601
|
activeReasoningEffortFallbackKey && activeRequestParams && !requestSignal.aborted
|
|
588
602
|
? resolveOpenAIReasoningEffortFallback(error, capturedErrorResponse, activeRequestParams, {
|
|
589
|
-
explicitDisable:
|
|
603
|
+
explicitDisable:
|
|
604
|
+
options?.forceReasoningOff === true ||
|
|
605
|
+
(options?.disableReasoning === true && options.reasoning === undefined),
|
|
590
606
|
})
|
|
591
607
|
: undefined;
|
|
592
608
|
if (reasoningEffortFallback !== undefined && activeReasoningEffortFallbackKey) {
|
|
@@ -632,7 +648,11 @@ const streamOpenAIResponsesOnce = (
|
|
|
632
648
|
if (chainState && !chainState.disabled) fallbackParams.store = true;
|
|
633
649
|
let fallbackChained: OpenAIResponsesChainedParams =
|
|
634
650
|
chainState && !chainState.disabled
|
|
635
|
-
? buildOpenAIResponsesChainedParams(
|
|
651
|
+
? buildOpenAIResponsesChainedParams(
|
|
652
|
+
fallbackParams,
|
|
653
|
+
fallbackBuilt.trailingScaffoldingItems,
|
|
654
|
+
chainState,
|
|
655
|
+
)
|
|
636
656
|
: { params: fallbackParams };
|
|
637
657
|
sentPreviousResponseId = fallbackChained.previousResponseId;
|
|
638
658
|
fallbackChained = {
|
|
@@ -642,7 +662,7 @@ const streamOpenAIResponsesOnce = (
|
|
|
642
662
|
chained = fallbackChained;
|
|
643
663
|
activeRawRequestDump.body = chained.params;
|
|
644
664
|
activeParams = fallbackParams;
|
|
645
|
-
|
|
665
|
+
activeTrailingScaffoldingItems = fallbackBuilt.trailingScaffoldingItems;
|
|
646
666
|
continue;
|
|
647
667
|
}
|
|
648
668
|
if (!chainState || !sentPreviousResponseId || requestSignal.aborted) {
|
|
@@ -688,6 +708,7 @@ const streamOpenAIResponsesOnce = (
|
|
|
688
708
|
chained = { params: retryParams };
|
|
689
709
|
activeRawRequestDump.body = retryParams;
|
|
690
710
|
activeParams = currentParams;
|
|
711
|
+
activeTrailingScaffoldingItems = currentBuilt.trailingScaffoldingItems;
|
|
691
712
|
activeStrictToolsApplied = currentBuilt.strictToolsApplied;
|
|
692
713
|
}
|
|
693
714
|
}
|
|
@@ -824,7 +845,17 @@ const streamOpenAIResponsesOnce = (
|
|
|
824
845
|
if (replayableResponseItems) {
|
|
825
846
|
if (providerSessionState) providerSessionState.nativeHistoryReplayWarmed = true;
|
|
826
847
|
if (chainState) {
|
|
827
|
-
chainState.lastParams = structuredCloneJSON(
|
|
848
|
+
chainState.lastParams = structuredCloneJSON(
|
|
849
|
+
activeTrailingScaffoldingItems > 0 && Array.isArray(activeParams.input)
|
|
850
|
+
? {
|
|
851
|
+
...activeParams,
|
|
852
|
+
input: activeParams.input.slice(
|
|
853
|
+
0,
|
|
854
|
+
activeParams.input.length - activeTrailingScaffoldingItems,
|
|
855
|
+
),
|
|
856
|
+
}
|
|
857
|
+
: activeParams,
|
|
858
|
+
);
|
|
828
859
|
chainState.lastPromptCacheBreakpointPolicy = promptCacheBreakpointPolicy;
|
|
829
860
|
if (output.responseId) {
|
|
830
861
|
chainState.lastResponseId = output.responseId;
|
|
@@ -843,7 +874,14 @@ const streamOpenAIResponsesOnce = (
|
|
|
843
874
|
// baseline, but `lastParams` still records the successful wire controls
|
|
844
875
|
// without re-enabling `previous_response_id` chaining.
|
|
845
876
|
chainState.canAppend = false;
|
|
846
|
-
chainState.lastParams = structuredCloneJSON(
|
|
877
|
+
chainState.lastParams = structuredCloneJSON(
|
|
878
|
+
activeTrailingScaffoldingItems > 0 && Array.isArray(activeParams.input)
|
|
879
|
+
? {
|
|
880
|
+
...activeParams,
|
|
881
|
+
input: activeParams.input.slice(0, activeParams.input.length - activeTrailingScaffoldingItems),
|
|
882
|
+
}
|
|
883
|
+
: activeParams,
|
|
884
|
+
);
|
|
847
885
|
chainState.lastPromptCacheBreakpointPolicy = promptCacheBreakpointPolicy;
|
|
848
886
|
chainState.lastResponseId = undefined;
|
|
849
887
|
chainState.lastResponseItems = undefined;
|
|
@@ -899,6 +937,17 @@ function isOfficialOpenAIResponsesEndpoint(model: Model<"openai-responses">): bo
|
|
|
899
937
|
}
|
|
900
938
|
}
|
|
901
939
|
|
|
940
|
+
/**
|
|
941
|
+
* GPT-5.6+ family check for Responses routes. The model id classifies the
|
|
942
|
+
* reasoning family regardless of the provider/host serving it — a cliproxy or
|
|
943
|
+
* other OpenAI-compatible gateway carrying `gpt-5.6-sol` gets the same
|
|
944
|
+
* scaffolding as the official endpoint.
|
|
945
|
+
*/
|
|
946
|
+
function isGpt56PlusResponsesModel(model: Model<"openai-responses">): boolean {
|
|
947
|
+
const parsed = parseOpenAIModel(bareModelId(model.requestModelId ?? model.id));
|
|
948
|
+
return parsed !== null && semverGte(parsed.version, "5.6");
|
|
949
|
+
}
|
|
950
|
+
|
|
902
951
|
function isResponsesPromptCacheableContentBlock(block: unknown): block is ResponseInputContent {
|
|
903
952
|
if (typeof block !== "object" || block === null || !("type" in block)) return false;
|
|
904
953
|
return block.type === "input_text" || block.type === "input_image" || block.type === "input_file";
|
|
@@ -1090,7 +1139,7 @@ export function buildParams(
|
|
|
1090
1139
|
strictToolsScope?: OpenAIStrictToolsScope,
|
|
1091
1140
|
disableStrictToolsOverride = false,
|
|
1092
1141
|
statefulCacheBaseline?: ResponseInput,
|
|
1093
|
-
): { params: OpenAIResponsesSamplingParams; strictToolsApplied: boolean } {
|
|
1142
|
+
): { params: OpenAIResponsesSamplingParams; trailingScaffoldingItems: number; strictToolsApplied: boolean } {
|
|
1094
1143
|
const policy = resolveOpenAICompatPolicy(model, {
|
|
1095
1144
|
endpoint: "responses",
|
|
1096
1145
|
reasoning: options?.reasoning,
|
|
@@ -1244,6 +1293,7 @@ export function buildParams(
|
|
|
1244
1293
|
: options?.reasoningSummary;
|
|
1245
1294
|
applyResponsesCompatPolicy(params, reasoningPolicy, {
|
|
1246
1295
|
reasoningSummary,
|
|
1296
|
+
forceReasoningOff: options?.forceReasoningOff,
|
|
1247
1297
|
mapEffort: effort =>
|
|
1248
1298
|
model.compat.reasoningEffortMap?.[effort as NonNullable<OpenAIResponsesOptions["reasoning"]>] ??
|
|
1249
1299
|
model.thinking?.effortMap?.[effort as NonNullable<OpenAIResponsesOptions["reasoning"]>] ??
|
|
@@ -1253,7 +1303,7 @@ export function buildParams(
|
|
|
1253
1303
|
// mode survives every policy branch (disabled/omitted effort included) while
|
|
1254
1304
|
// keeping whatever effort/summary the policy produced — mode and effort are
|
|
1255
1305
|
// independent wire fields.
|
|
1256
|
-
if (model.reasoningMode) {
|
|
1306
|
+
if (model.reasoningMode && !options?.forceReasoningOff) {
|
|
1257
1307
|
params.reasoning = { ...params.reasoning, mode: model.reasoningMode };
|
|
1258
1308
|
}
|
|
1259
1309
|
|
|
@@ -1266,7 +1316,18 @@ export function buildParams(
|
|
|
1266
1316
|
applyOpenAIExtraBody(params, options?.extraBody);
|
|
1267
1317
|
applyOpenAIResponsesPromptCachePolicy(params, model, options, statefulCacheBaseline);
|
|
1268
1318
|
|
|
1269
|
-
|
|
1319
|
+
let trailingScaffoldingItems = 0;
|
|
1320
|
+
if (options?.forceReasoningOff && isGpt56PlusResponsesModel(model)) {
|
|
1321
|
+
const effort = options.reasoning ?? "medium";
|
|
1322
|
+
const juice = getJuiceValue(effort);
|
|
1323
|
+
messages.push({
|
|
1324
|
+
role: "developer",
|
|
1325
|
+
content: [{ type: "input_text", text: `# Juice: ${juice} !important` }],
|
|
1326
|
+
});
|
|
1327
|
+
trailingScaffoldingItems = 1;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
return { params, trailingScaffoldingItems, strictToolsApplied };
|
|
1270
1331
|
}
|
|
1271
1332
|
|
|
1272
1333
|
/**
|
|
@@ -2428,6 +2428,20 @@ export function finalizeMessageText(item: ResponseOutputMessage, streamedText: s
|
|
|
2428
2428
|
if (!item.content?.length) return streamedText || "";
|
|
2429
2429
|
return item.content.map(part => (part.type === "output_text" ? (part.text ?? "") : (part.refusal ?? ""))).join("");
|
|
2430
2430
|
}
|
|
2431
|
+
export const JUICE_EFFORT_MAP: Record<string, number> = {
|
|
2432
|
+
none: 0,
|
|
2433
|
+
minimal: 2,
|
|
2434
|
+
low: 4,
|
|
2435
|
+
medium: 8,
|
|
2436
|
+
high: 48,
|
|
2437
|
+
xhigh: 112,
|
|
2438
|
+
max: 960,
|
|
2439
|
+
};
|
|
2440
|
+
|
|
2441
|
+
export function getJuiceValue(effort?: string): number {
|
|
2442
|
+
if (!effort) return 8;
|
|
2443
|
+
return JUICE_EFFORT_MAP[effort] ?? 8;
|
|
2444
|
+
}
|
|
2431
2445
|
|
|
2432
2446
|
export function accumulateToolCallArgumentsDelta(
|
|
2433
2447
|
block: ResponsesToolCallBlock,
|
|
@@ -3308,6 +3322,14 @@ type ReasoningOptions = {
|
|
|
3308
3322
|
export interface ApplyResponsesCompatPolicyOptions {
|
|
3309
3323
|
reasoningSummary?: "auto" | "detailed" | "concise" | null;
|
|
3310
3324
|
mapEffort?: (effort: string) => string;
|
|
3325
|
+
/**
|
|
3326
|
+
* Suppress native reasoning by sending `reasoning.effort: "none"` — the only
|
|
3327
|
+
* disable level the Responses API defines (`"off"` is not a wire value and
|
|
3328
|
+
* 400s everywhere). Gateways that reject `none` for a given model are
|
|
3329
|
+
* handled by the reasoning-effort fallback retry, which clamps to the
|
|
3330
|
+
* lowest level the error reports as allowed.
|
|
3331
|
+
*/
|
|
3332
|
+
forceReasoningOff?: boolean;
|
|
3311
3333
|
}
|
|
3312
3334
|
|
|
3313
3335
|
export function applyResponsesCompatPolicy<P extends ResponseCreateParamsStreaming>(
|
|
@@ -3316,6 +3338,10 @@ export function applyResponsesCompatPolicy<P extends ResponseCreateParamsStreami
|
|
|
3316
3338
|
options: ApplyResponsesCompatPolicyOptions | undefined,
|
|
3317
3339
|
): void {
|
|
3318
3340
|
const reasoning = policy.reasoning;
|
|
3341
|
+
if (options?.forceReasoningOff) {
|
|
3342
|
+
params.reasoning = { effort: "none" } as P["reasoning"];
|
|
3343
|
+
return;
|
|
3344
|
+
}
|
|
3319
3345
|
if (!reasoning.modelSupported) return;
|
|
3320
3346
|
if (reasoning.includeEncryptedReasoning) {
|
|
3321
3347
|
const include = params.include ?? [];
|
|
@@ -5,7 +5,7 @@ export const amazonBedrockProvider = {
|
|
|
5
5
|
id: "amazon-bedrock",
|
|
6
6
|
name: "Amazon Bedrock",
|
|
7
7
|
// Amazon Bedrock accepts bearer tokens, IAM keys, profiles, ECS/IRSA credential chains.
|
|
8
|
-
envKeys: resolveAwsRegistryApiKey,
|
|
8
|
+
envKeys: () => resolveAwsRegistryApiKey({ allowSkipAuth: true }),
|
|
9
9
|
mapSimpleOptions: options => {
|
|
10
10
|
const awsOptions = options.providerOptions as AwsBedrockProviderOptions | undefined;
|
|
11
11
|
return {
|
package/src/registry/aws.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import { $env } from "@oh-my-pi/pi-utils";
|
|
2
|
+
import { $env, $flag } from "@oh-my-pi/pi-utils";
|
|
3
3
|
import { hasConfiguredAwsProfile } from "../utils/aws-profile";
|
|
4
4
|
import { AUTHENTICATED_SENTINEL } from "./types";
|
|
5
5
|
|
|
@@ -53,7 +53,8 @@ export function hasAwsCredentialSource(): boolean {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/** Registry key marker for AWS transports that resolve their own bearer/IAM credentials. */
|
|
56
|
-
export function resolveAwsRegistryApiKey(): string | undefined {
|
|
56
|
+
export function resolveAwsRegistryApiKey(options?: { allowSkipAuth?: boolean }): string | undefined {
|
|
57
|
+
if (options?.allowSkipAuth && $flag("AWS_BEDROCK_SKIP_AUTH")) return AUTHENTICATED_SENTINEL;
|
|
57
58
|
return hasAwsCredentialSource() ? AUTHENTICATED_SENTINEL : undefined;
|
|
58
59
|
}
|
|
59
60
|
|
package/src/stream.ts
CHANGED
|
@@ -1408,6 +1408,17 @@ function resolveOpenAiReasoningEffort<TApi extends Api>(
|
|
|
1408
1408
|
return requireSupportedEffort(model, reasoning);
|
|
1409
1409
|
}
|
|
1410
1410
|
|
|
1411
|
+
function resolveGoogleThinkingOff<TApi extends Api>(model: Model<TApi>): NonNullable<GoogleOptions["thinking"]> {
|
|
1412
|
+
const thinking: NonNullable<GoogleOptions["thinking"]> = { enabled: false };
|
|
1413
|
+
if (!model.reasoning || !model.thinking) return thinking;
|
|
1414
|
+
if (model.thinking.mode === "budget" && (!model.thinking.requiresEffort || model.thinking.suppressWhenOff)) {
|
|
1415
|
+
thinking.budgetTokens = 0;
|
|
1416
|
+
} else if (model.thinking.mode === "google-level" && model.thinking.suppressWhenOff) {
|
|
1417
|
+
thinking.level = "MINIMAL";
|
|
1418
|
+
}
|
|
1419
|
+
return thinking;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1411
1422
|
const castApi = <TApi extends Api>(api: OptionsForApi<TApi>): OptionsForApi<Api> => api as OptionsForApi<Api>;
|
|
1412
1423
|
|
|
1413
1424
|
/**
|
|
@@ -1428,13 +1439,13 @@ function normalizeMandatoryReasoningOptions<TApi extends Api>(
|
|
|
1428
1439
|
!model.reasoning ||
|
|
1429
1440
|
!model.thinking?.requiresEffort ||
|
|
1430
1441
|
model.thinking.suppressWhenOff ||
|
|
1431
|
-
(options?.reasoning !== undefined && !options.disableReasoning)
|
|
1442
|
+
(options?.reasoning !== undefined && !options.disableReasoning && !options.forceReasoningOff)
|
|
1432
1443
|
) {
|
|
1433
1444
|
return options;
|
|
1434
1445
|
}
|
|
1435
1446
|
const floor = minimumSupportedEffort(model);
|
|
1436
1447
|
if (floor === undefined) return options;
|
|
1437
|
-
return { ...options, reasoning: floor, disableReasoning: undefined };
|
|
1448
|
+
return { ...options, reasoning: floor, disableReasoning: undefined, forceReasoningOff: undefined };
|
|
1438
1449
|
}
|
|
1439
1450
|
|
|
1440
1451
|
function supportsExplicitOpenAIResponsesPromptCache(compat: unknown): boolean {
|
|
@@ -1515,12 +1526,12 @@ function mapOptionsForApi<TApi extends Api>(
|
|
|
1515
1526
|
switch (model.api) {
|
|
1516
1527
|
case "anthropic-messages": {
|
|
1517
1528
|
// Explicitly disable thinking when reasoning is not specified, the caller
|
|
1518
|
-
// disabled it, or the model doesn't
|
|
1519
|
-
// SimpleStreamOptions
|
|
1520
|
-
//
|
|
1521
|
-
// models already clamp
|
|
1529
|
+
// disabled it, an external scratchpad replaces it, or the model doesn't
|
|
1530
|
+
// support it. These SimpleStreamOptions flags never reach AnthropicOptions
|
|
1531
|
+
// on their own, so fold them into thinkingEnabled here (mandatory-reasoning
|
|
1532
|
+
// models already clamp them away in normalizeMandatoryReasoningOptions).
|
|
1522
1533
|
const reasoning = options?.reasoning;
|
|
1523
|
-
if (!reasoning || !model.reasoning || options?.disableReasoning) {
|
|
1534
|
+
if (!reasoning || !model.reasoning || options?.disableReasoning || options?.forceReasoningOff) {
|
|
1524
1535
|
return castApi<"anthropic-messages">({
|
|
1525
1536
|
...base,
|
|
1526
1537
|
requestModelId: resolveWireModelId(model, undefined),
|
|
@@ -1692,6 +1703,7 @@ function mapOptionsForApi<TApi extends Api>(
|
|
|
1692
1703
|
openrouterVariant: options?.openrouterVariant,
|
|
1693
1704
|
maxTokensExplicit: rawOptions?.maxTokens !== undefined,
|
|
1694
1705
|
disableReasoning: options?.disableReasoning,
|
|
1706
|
+
forceReasoningOff: options?.forceReasoningOff,
|
|
1695
1707
|
textVerbosity: options?.textVerbosity,
|
|
1696
1708
|
promptCache: options?.promptCache,
|
|
1697
1709
|
statefulResponses: options?.statefulResponses,
|
|
@@ -1706,6 +1718,8 @@ function mapOptionsForApi<TApi extends Api>(
|
|
|
1706
1718
|
reasoningSummary: options?.hideThinkingSummary ? null : undefined,
|
|
1707
1719
|
promptCache: options?.promptCache,
|
|
1708
1720
|
statefulResponses: options?.statefulResponses,
|
|
1721
|
+
disableReasoning: options?.disableReasoning || options?.forceReasoningOff,
|
|
1722
|
+
forceReasoningOff: options?.forceReasoningOff,
|
|
1709
1723
|
});
|
|
1710
1724
|
|
|
1711
1725
|
case "openai-codex-responses":
|
|
@@ -1718,17 +1732,18 @@ function mapOptionsForApi<TApi extends Api>(
|
|
|
1718
1732
|
codexCompaction: options?.codexCompaction,
|
|
1719
1733
|
reasoningSummary: options?.hideThinkingSummary ? null : undefined,
|
|
1720
1734
|
textVerbosity: options?.textVerbosity,
|
|
1735
|
+
forceReasoningOff: options?.forceReasoningOff,
|
|
1721
1736
|
});
|
|
1722
1737
|
|
|
1723
1738
|
case "google-generative-ai": {
|
|
1724
|
-
// Explicitly disable thinking when reasoning is
|
|
1725
|
-
//
|
|
1739
|
+
// Explicitly disable thinking when reasoning is absent, unsupported, or
|
|
1740
|
+
// replaced by the caller's external scratchpad. Gemini defaults thinking on.
|
|
1726
1741
|
const reasoning = options?.reasoning;
|
|
1727
|
-
if (!reasoning || !model.reasoning) {
|
|
1742
|
+
if (!reasoning || !model.reasoning || options?.disableReasoning || options?.forceReasoningOff) {
|
|
1728
1743
|
return castApi<"google-generative-ai">({
|
|
1729
1744
|
...base,
|
|
1730
1745
|
serviceTier: options?.serviceTier,
|
|
1731
|
-
thinking:
|
|
1746
|
+
thinking: resolveGoogleThinkingOff(model),
|
|
1732
1747
|
toolChoice: mapGoogleToolChoice(options?.toolChoice),
|
|
1733
1748
|
cachedContent: options?.cachedContent,
|
|
1734
1749
|
});
|
|
@@ -1768,7 +1783,7 @@ function mapOptionsForApi<TApi extends Api>(
|
|
|
1768
1783
|
case "google-gemini-cli": {
|
|
1769
1784
|
const reasoning = options?.reasoning;
|
|
1770
1785
|
const toolChoice = mapGoogleToolChoice(options?.toolChoice);
|
|
1771
|
-
if (reasoning && model.reasoning) {
|
|
1786
|
+
if (reasoning && model.reasoning && !options?.disableReasoning && !options?.forceReasoningOff) {
|
|
1772
1787
|
const effort = requireSupportedEffort(model, reasoning);
|
|
1773
1788
|
|
|
1774
1789
|
// Gemini 3+ models use thinkingLevel instead of thinkingBudget
|
|
@@ -1827,13 +1842,14 @@ function mapOptionsForApi<TApi extends Api>(
|
|
|
1827
1842
|
}
|
|
1828
1843
|
|
|
1829
1844
|
case "google-vertex": {
|
|
1830
|
-
// Explicitly disable thinking when reasoning is
|
|
1845
|
+
// Explicitly disable thinking when reasoning is absent, unsupported, or
|
|
1846
|
+
// replaced by the caller's external scratchpad.
|
|
1831
1847
|
const reasoning = options?.reasoning;
|
|
1832
|
-
if (!reasoning || !model.reasoning) {
|
|
1848
|
+
if (!reasoning || !model.reasoning || options?.disableReasoning || options?.forceReasoningOff) {
|
|
1833
1849
|
return castApi<"google-vertex">({
|
|
1834
1850
|
...base,
|
|
1835
1851
|
serviceTier: options?.serviceTier,
|
|
1836
|
-
thinking:
|
|
1852
|
+
thinking: resolveGoogleThinkingOff(model),
|
|
1837
1853
|
toolChoice: mapGoogleToolChoice(options?.toolChoice),
|
|
1838
1854
|
cachedContent: options?.cachedContent,
|
|
1839
1855
|
});
|
package/src/types.ts
CHANGED
|
@@ -478,6 +478,12 @@ export interface StreamOptions {
|
|
|
478
478
|
* `false` so `previous_response_id` cannot explain a result.
|
|
479
479
|
*/
|
|
480
480
|
statefulResponses?: boolean;
|
|
481
|
+
/**
|
|
482
|
+
* Disable native reasoning when the caller supplies an external scratchpad.
|
|
483
|
+
* OpenAI Responses emits `reasoning: { effort: "none" }`; Anthropic and
|
|
484
|
+
* Google transports use their native thinking-off controls.
|
|
485
|
+
*/
|
|
486
|
+
forceReasoningOff?: boolean;
|
|
481
487
|
/**
|
|
482
488
|
* Provider-scoped mutable state store for this agent session.
|
|
483
489
|
* Providers can use this to persist transport/session state between turns.
|