






















@@ -2,15 +2,23 @@
22// session write-lock behavior.
33import { Type } from "typebox";
44import { beforeEach, describe, expect, it, vi } from "vitest";
5+import type { Context, Model, SimpleStreamOptions } from "../../llm/types.js";
5667const thinkingMocks = vi.hoisted(() => ({
78resolveThinkingDefaultForModel: vi.fn(() => "medium"),
89}));
10+const streamMocks = vi.hoisted(() => ({
11+streamSimple: vi.fn(
12+(_model: Model, _context: Context, _options?: SimpleStreamOptions) => "stream",
13+),
14+}));
9151016vi.mock("../../auto-reply/thinking.js", () => ({
1117resolveThinkingDefaultForModel: thinkingMocks.resolveThinkingDefaultForModel,
1218}));
13-import type { Model } from "../../llm/types.js";
19+vi.mock("../../llm/stream.js", () => ({
20+streamSimple: streamMocks.streamSimple,
21+}));
1422import { AuthStorage } from "./auth-storage.js";
1523import { createExtensionRuntime } from "./extensions/loader.js";
1624import type { LoadExtensionsResult, ToolDefinition } from "./extensions/types.js";
@@ -34,6 +42,11 @@ const testModel: Model = {
3442maxTokens: 1000,
3543};
364445+function createModelWithoutBaseUrl(overrides: Partial<Model>): Model {
46+const { baseUrl: _baseUrl, ...model } = { ...testModel, ...overrides };
47+return model as unknown as Model;
48+}
49+3750function createEmptyResourceLoader(): ResourceLoader {
3851return createResourceLoaderWithHandlers(new Map());
3952}
@@ -74,6 +87,84 @@ function createResourceLoaderWithHandlers(
7487};
7588}
768990+async function createSessionAndStreamModel(model: Model): Promise<SimpleStreamOptions> {
91+streamMocks.streamSimple.mockClear();
92+const { session } = await createAgentSession({
93+ model,
94+resourceLoader: createEmptyResourceLoader(),
95+sessionManager: SessionManager.inMemory(),
96+settingsManager: SettingsManager.inMemory(),
97+modelRegistry: ModelRegistry.inMemory(AuthStorage.inMemory()),
98+});
99+100+await session.agent.streamFn?.(
101+model,
102+{
103+messages: [],
104+systemPrompt: "",
105+tools: [],
106+},
107+{},
108+);
109+110+return streamMocks.streamSimple.mock.lastCall?.[2] ?? {};
111+}
112+113+describe("createAgentSession attribution headers", () => {
114+it("tolerates Bedrock models that do not expose baseUrl", async () => {
115+const options = await createSessionAndStreamModel(
116+createModelWithoutBaseUrl({
117+id: "global.anthropic.claude-sonnet-4-6",
118+provider: "amazon-bedrock",
119+api: "bedrock-converse-stream",
120+}),
121+);
122+123+expect(streamMocks.streamSimple).toHaveBeenCalledOnce();
124+expect(options.headers).toBeUndefined();
125+});
126+127+it("keeps OpenRouter attribution headers for provider and endpoint matches", async () => {
128+const providerOptions = await createSessionAndStreamModel({
129+ ...testModel,
130+provider: "openrouter",
131+baseUrl: "https://example.test",
132+});
133+const endpointOptions = await createSessionAndStreamModel({
134+ ...testModel,
135+provider: "custom-openai",
136+baseUrl: "https://openrouter.ai/api/v1",
137+});
138+139+expect(providerOptions.headers).toMatchObject({
140+"HTTP-Referer": "https://openclaw.ai",
141+"X-OpenRouter-Title": "OpenClaw",
142+"X-OpenRouter-Categories": "cli-agent",
143+});
144+expect(endpointOptions.headers).toMatchObject({
145+"HTTP-Referer": "https://openclaw.ai",
146+"X-OpenRouter-Title": "OpenClaw",
147+"X-OpenRouter-Categories": "cli-agent",
148+});
149+});
150+151+it("keeps Cloudflare attribution headers for provider and endpoint matches", async () => {
152+const providerOptions = await createSessionAndStreamModel({
153+ ...testModel,
154+provider: "cloudflare-workers-ai",
155+baseUrl: "https://example.test",
156+});
157+const endpointOptions = await createSessionAndStreamModel({
158+ ...testModel,
159+provider: "custom-openai",
160+baseUrl: "https://gateway.ai.cloudflare.com/v1/account/gateway/openai",
161+});
162+163+expect(providerOptions.headers).toMatchObject({ "User-Agent": "openclaw" });
164+expect(endpointOptions.headers).toMatchObject({ "User-Agent": "openclaw" });
165+});
166+});
167+77168describe("createAgentSession tool defaults", () => {
78169it("forwards max thinking budgets from settings to the agent", async () => {
79170const { session } = await createAgentSession({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。