






























@@ -144,15 +144,27 @@ function resolvedModelSupportsCaching(modelArn: string): boolean {
144144return matchesPiAiPromptCachingModelId(modelArn);
145145}
146146147+function isOpus47BedrockModelRef(modelRef: string): boolean {
148+return /(?:^|[/.:])(?:(?:us|eu|ap|apac|au|jp|global)\.)?anthropic\.claude-opus-4[.-]7(?:$|[-.:/])/i.test(
149+modelRef,
150+);
151+}
152+147153/**
148154 * Resolve the underlying foundation model for an application inference profile
149155 * via GetInferenceProfile. Results are cached so we only call the API once per
150- * profile ARN. Returns true if the underlying model supports prompt caching.
156+ * profile ARN. Returns traits needed for request shaping when the model id is
157+ * otherwise opaque.
151158 *
152159 * Region is extracted from the profile ARN itself to avoid mismatches when
153160 * the OpenClaw config region differs from the profile's home region.
154161 */
155-const appProfileCacheEligibleCache = new Map<string, boolean>();
162+type BedrockAppProfileTraits = {
163+cacheEligible: boolean;
164+omitTemperature: boolean;
165+};
166+167+const appProfileTraitsCache = new Map<string, BedrockAppProfileTraits>();
156168157169type BedrockGetInferenceProfileResponse = {
158170models?: Array<{ modelArn?: string }>;
@@ -169,7 +181,7 @@ type BedrockControlPlaneFactory = (region: string | undefined) => BedrockControl
169181let bedrockControlPlaneOverride: BedrockControlPlaneFactory | undefined;
170182171183export function resetBedrockAppProfileCacheEligibilityForTest(): void {
172-appProfileCacheEligibleCache.clear();
184+appProfileTraitsCache.clear();
173185}
174186175187export function setBedrockAppProfileControlPlaneForTest(
@@ -190,27 +202,34 @@ async function createBedrockControlPlane(region: string | undefined): Promise<Be
190202};
191203}
192204193-async function resolveAppProfileCacheEligible(
205+async function resolveAppProfileTraits(
194206modelId: string,
195207fallbackRegion: string | undefined,
196-): Promise<boolean> {
197-if (appProfileCacheEligibleCache.has(modelId)) {
198-return appProfileCacheEligibleCache.get(modelId)!;
208+): Promise<BedrockAppProfileTraits> {
209+const cached = appProfileTraitsCache.get(modelId);
210+if (cached) {
211+return cached;
199212}
200213try {
201214const region = extractRegionFromArn(modelId) ?? fallbackRegion;
202215const controlPlane = await createBedrockControlPlane(region);
203216const resp = await controlPlane.getInferenceProfile({ inferenceProfileIdentifier: modelId });
204217const models = resp.models ?? [];
205-const eligible =
206-models.length > 0 &&
207-models.every((m: { modelArn?: string }) => resolvedModelSupportsCaching(m.modelArn ?? ""));
208-appProfileCacheEligibleCache.set(modelId, eligible);
209-return eligible;
218+const modelArns = models.map((m: { modelArn?: string }) => m.modelArn ?? "");
219+const traits = {
220+cacheEligible:
221+models.length > 0 && modelArns.every((modelArn) => resolvedModelSupportsCaching(modelArn)),
222+omitTemperature: modelArns.some(isOpus47BedrockModelRef),
223+};
224+appProfileTraitsCache.set(modelId, traits);
225+return traits;
210226} catch {
211227// Transient failures (throttling, network, IAM) should not be cached —
212228// return the heuristic fallback but allow retry on the next request.
213-return isAnthropicBedrockModel(modelId);
229+return {
230+cacheEligible: isAnthropicBedrockModel(modelId),
231+omitTemperature: isOpus47BedrockModelRef(modelId),
232+};
214233}
215234}
216235@@ -279,6 +298,8 @@ export function registerAmazonBedrockPlugin(api: OpenClawPluginApi): void {
279298/ValidationException.*(?:exceeds? the (?:maximum|max) (?:number of )?(?:input )?tokens)/i,
280299/ModelStreamErrorException.*(?:Input is too long|too many input tokens)/i,
281300] as const;
301+const deprecatedTemperatureValidationRe =
302+/ValidationException[\s\S]*(?:invalid_request_error[\s\S]*)?temperature[\s\S]*deprecated|ValidationException[\s\S]*deprecated[\s\S]*temperature/i;
282303const anthropicByModelReplayHooks = ANTHROPIC_BY_MODEL_REPLAY_HOOKS;
283304const startupPluginConfig = (api.pluginConfig ?? {}) as AmazonBedrockPluginConfig;
284305@@ -306,6 +327,26 @@ export function registerAmazonBedrockPlugin(api: OpenClawPluginApi): void {
306327return createBedrockNoCacheWrapper(streamFn);
307328};
308329330+function omitDeprecatedOpus47Temperature<TOptions extends object>(
331+modelId: string,
332+options: TOptions,
333+): TOptions {
334+if (!isOpus47BedrockModelRef(modelId) || !("temperature" in options)) {
335+return options;
336+}
337+const next = { ...options } as typeof options & { temperature?: unknown };
338+delete next.temperature;
339+return next;
340+}
341+342+function omitDeprecatedOpus47PayloadTemperature(payload: Record<string, unknown>): void {
343+const inferenceConfig = payload.inferenceConfig;
344+if (!inferenceConfig || typeof inferenceConfig !== "object") {
345+return;
346+}
347+delete (inferenceConfig as Record<string, unknown>).temperature;
348+}
349+309350/** Extract the AWS region from a bedrock-runtime baseUrl. */
310351function extractRegionFromBaseUrl(baseUrl: string | undefined): string | undefined {
311352if (!baseUrl) {
@@ -386,12 +427,13 @@ export function registerAmazonBedrockPlugin(api: OpenClawPluginApi): void {
386427const region = resolveBedrockRegion(config) ?? extractRegionFromBaseUrl(model?.baseUrl);
387428const mayNeedCacheInjection =
388429isBedrockAppInferenceProfile(modelId) && !piAiWouldInjectCachePoints(modelId);
430+const shouldOmitTemperature = isOpus47BedrockModelRef(modelId);
389431390432// For known Anthropic models (heuristic match), enable injection immediately.
391433// For opaque profile IDs, we'll resolve via GetInferenceProfile on first call.
392434const heuristicMatch = needsCachePointInjection(modelId);
393435394-if (!region && !mayNeedCacheInjection) {
436+if (!region && !mayNeedCacheInjection && !shouldOmitTemperature) {
395437return wrapped;
396438}
397439@@ -400,7 +442,10 @@ export function registerAmazonBedrockPlugin(api: OpenClawPluginApi): void {
400442return wrapped;
401443}
402444return (streamModel, context, options) => {
403-const merged = Object.assign({}, options, region ? { region } : {});
445+const merged = omitDeprecatedOpus47Temperature(
446+modelId,
447+Object.assign({}, options, region ? { region } : {}),
448+);
404449405450if (!mayNeedCacheInjection) {
406451return underlying(streamModel, context, merged);
@@ -416,25 +461,46 @@ export function registerAmazonBedrockPlugin(api: OpenClawPluginApi): void {
416461// want caching enabled, so defaulting to "short" is the safer behavior.
417462const cacheRetention =
418463typeof merged.cacheRetention === "string" ? merged.cacheRetention : "short";
464+const originalOnPayload = merged.onPayload as
465+| ((payload: unknown, model: unknown) => unknown)
466+| undefined;
419467420468if (heuristicMatch) {
421-// Fast path: ARN heuristic already identified this as Claude.
422-return streamWithPayloadPatch(underlying, streamModel, context, merged, (payload) => {
423-injectBedrockCachePoints(payload, cacheRetention);
469+// Fast path: ARN heuristic already identified this as Claude, but the
470+// concrete target may still need profile traits for Opus 4.7 payloads.
471+const mayNeedTemperatureTrait = "temperature" in merged;
472+return underlying(streamModel, context, {
473+ ...merged,
474+onPayload: async (payload: unknown, payloadModel: unknown) => {
475+if (payload && typeof payload === "object") {
476+const payloadRecord = payload as Record<string, unknown>;
477+injectBedrockCachePoints(payloadRecord, cacheRetention);
478+if (mayNeedTemperatureTrait) {
479+const traits = await resolveAppProfileTraits(modelId, region);
480+if (traits.omitTemperature) {
481+omitDeprecatedOpus47PayloadTemperature(payloadRecord);
482+}
483+}
484+}
485+return originalOnPayload?.(payload, payloadModel);
486+},
424487});
425488}
426489427490// Slow path: opaque profile ID — resolve underlying model via API (cached).
428491// pi-ai's onPayload supports async, so we await the resolution inline.
429-const originalOnPayload = merged.onPayload as
430-| ((payload: unknown, model: unknown) => unknown)
431-| undefined;
432492return underlying(streamModel, context, {
433493 ...merged,
434494onPayload: async (payload: unknown, payloadModel: unknown) => {
435-const eligible = await resolveAppProfileCacheEligible(modelId, region);
436-if (eligible && payload && typeof payload === "object") {
437-injectBedrockCachePoints(payload as Record<string, unknown>, cacheRetention);
495+const traits = await resolveAppProfileTraits(modelId, region);
496+if (payload && typeof payload === "object") {
497+const payloadRecord = payload as Record<string, unknown>;
498+if (traits.cacheEligible) {
499+injectBedrockCachePoints(payloadRecord, cacheRetention);
500+}
501+if (traits.omitTemperature) {
502+omitDeprecatedOpus47PayloadTemperature(payloadRecord);
503+}
438504}
439505return originalOnPayload?.(payload, payloadModel);
440506},
@@ -450,6 +516,9 @@ export function registerAmazonBedrockPlugin(api: OpenClawPluginApi): void {
450516if (/ModelNotReadyException/i.test(errorMessage)) {
451517return "overloaded";
452518}
519+if (deprecatedTemperatureValidationRe.test(errorMessage)) {
520+return "format";
521+}
453522return undefined;
454523},
455524resolveThinkingProfile: ({ modelId }) => ({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。