Compare commits

...

4 Commits

Author SHA1 Message Date
Vincent Koc
14952ace58 Changelog: note Anthropic service tier follow-up 2026-03-13 13:59:46 -07:00
Vincent Koc
dff1dae6e0 Tests: cover explicit Anthropic service tiers 2026-03-13 13:59:13 -07:00
Vincent Koc
5c7a640f8d Runner: wire explicit Anthropic service tiers 2026-03-13 13:58:40 -07:00
Vincent Koc
2bcef84052 Anthropic: add explicit service tier wrapper 2026-03-13 13:58:08 -07:00
4 changed files with 112 additions and 0 deletions

View File

@ -41,6 +41,7 @@ Docs: https://docs.openclaw.ai
- Agents/compaction: preserve safeguard compaction summary language continuity via default and configurable custom instructions so persona drift is reduced after auto-compaction. (#10456) Thanks @keepitmello.
- Agents/tool warnings: distinguish gated core tools like `apply_patch` from plugin-only unknown entries in `tools.profile` warnings, so unavailable core tools now report current runtime/provider/model/config gating instead of suggesting a missing plugin.
- Slack/probe: keep `auth.test()` bot and team metadata mapping stable while simplifying the probe result path. (#44775) Thanks @Cafexss.
- Anthropic/service tiers: support explicit `serviceTier` model params for direct Anthropic API-key models and let them override `/fast` defaults when both are set. Thanks @vincentkoc.
- Dashboard/chat UI: restore the `chat-new-messages` class on the New messages scroll pill so the button uses its existing compact styling instead of rendering as a full-screen SVG overlay. (#44856) Thanks @Astro-Han.
- Windows/gateway status: reuse the installed service command environment when reading runtime status, so startup-fallback gateways keep reporting the configured port and running state in `gateway status --json` instead of falling back to `gateway port unknown`.

View File

@ -1734,6 +1734,63 @@ describe("applyExtraParamsToAgent", () => {
expect(payload.service_tier).toBe("standard_only");
});
it("injects configured Anthropic service_tier into direct Anthropic payloads", () => {
const payload = runResponsesPayloadMutationCase({
applyProvider: "anthropic",
applyModelId: "claude-sonnet-4-5",
cfg: {
agents: {
defaults: {
models: {
"anthropic/claude-sonnet-4-5": {
params: {
serviceTier: "standard_only",
},
},
},
},
},
},
model: {
api: "anthropic-messages",
provider: "anthropic",
id: "claude-sonnet-4-5",
baseUrl: "https://api.anthropic.com",
} as unknown as Model<"anthropic-messages">,
payload: {},
});
expect(payload.service_tier).toBe("standard_only");
});
it("lets explicit Anthropic service_tier override fast mode defaults", () => {
const payload = runResponsesPayloadMutationCase({
applyProvider: "anthropic",
applyModelId: "claude-sonnet-4-5",
cfg: {
agents: {
defaults: {
models: {
"anthropic/claude-sonnet-4-5": {
params: {
fastMode: true,
serviceTier: "standard_only",
},
},
},
},
},
},
model: {
api: "anthropic-messages",
provider: "anthropic",
id: "claude-sonnet-4-5",
baseUrl: "https://api.anthropic.com",
} as unknown as Model<"anthropic-messages">,
payload: {},
});
expect(payload.service_tier).toBe("standard_only");
});
it("does not inject Anthropic fast mode service_tier for OAuth auth", () => {
const payload = runResponsesPayloadMutationCase({
applyProvider: "anthropic",

View File

@ -75,6 +75,17 @@ function resolveAnthropicFastServiceTier(enabled: boolean): AnthropicServiceTier
return enabled ? "auto" : "standard_only";
}
function normalizeAnthropicServiceTier(value: unknown): AnthropicServiceTier | undefined {
if (typeof value !== "string") {
return undefined;
}
const normalized = value.trim().toLowerCase();
if (normalized === "auto" || normalized === "standard_only") {
return normalized;
}
return undefined;
}
function requiresAnthropicToolPayloadCompatibilityForModel(model: {
api?: unknown;
provider?: unknown;
@ -350,12 +361,47 @@ export function createAnthropicFastModeWrapper(
};
}
export function createAnthropicServiceTierWrapper(
baseStreamFn: StreamFn | undefined,
serviceTier: AnthropicServiceTier,
): StreamFn {
const underlying = baseStreamFn ?? streamSimple;
return (model, context, options) => {
if (
model.api !== "anthropic-messages" ||
model.provider !== "anthropic" ||
!isAnthropicPublicApiBaseUrl(model.baseUrl) ||
isAnthropicOAuthApiKey(options?.apiKey)
) {
return underlying(model, context, options);
}
return streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
if (payloadObj.service_tier === undefined) {
payloadObj.service_tier = serviceTier;
}
});
};
}
export function resolveAnthropicFastMode(
extraParams: Record<string, unknown> | undefined,
): boolean | undefined {
return resolveFastModeParam(extraParams);
}
export function resolveAnthropicServiceTier(
extraParams: Record<string, unknown> | undefined,
): AnthropicServiceTier | undefined {
const raw = extraParams?.serviceTier ?? extraParams?.service_tier;
const normalized = normalizeAnthropicServiceTier(raw);
if (raw !== undefined && normalized === undefined) {
const rawSummary = typeof raw === "string" ? raw : typeof raw;
log.warn(`ignoring invalid Anthropic service tier param: ${rawSummary}`);
}
return normalized;
}
export function createBedrockNoCacheWrapper(baseStreamFn: StreamFn | undefined): StreamFn {
const underlying = baseStreamFn ?? streamSimple;
return (model, context, options) =>

View File

@ -6,10 +6,12 @@ import type { OpenClawConfig } from "../../config/config.js";
import {
createAnthropicBetaHeadersWrapper,
createAnthropicFastModeWrapper,
createAnthropicServiceTierWrapper,
createAnthropicToolPayloadCompatibilityWrapper,
createBedrockNoCacheWrapper,
isAnthropicBedrockModel,
resolveAnthropicFastMode,
resolveAnthropicServiceTier,
resolveAnthropicBetas,
resolveCacheRetention,
} from "./anthropic-stream-wrappers.js";
@ -447,6 +449,12 @@ export function applyExtraParamsToAgent(
agent.streamFn = createAnthropicFastModeWrapper(agent.streamFn, anthropicFastMode);
}
const anthropicServiceTier = resolveAnthropicServiceTier(merged);
if (anthropicServiceTier) {
log.debug(`applying Anthropic service_tier=${anthropicServiceTier} for ${provider}/${modelId}`);
agent.streamFn = createAnthropicServiceTierWrapper(agent.streamFn, anthropicServiceTier);
}
const openAIFastMode = resolveOpenAIFastMode(merged);
if (openAIFastMode) {
log.debug(`applying OpenAI fast mode for ${provider}/${modelId}`);