diff --git a/src/auto-reply/reply/conversation-label-generator.test.ts b/src/auto-reply/reply/conversation-label-generator.test.ts index 819f27f44b5..4b152391efe 100644 --- a/src/auto-reply/reply/conversation-label-generator.test.ts +++ b/src/auto-reply/reply/conversation-label-generator.test.ts @@ -135,6 +135,40 @@ describe("generateConversationLabel", () => { expect(call[2].signal).toBeInstanceOf(AbortSignal); }); + it("applies prepared runtime auth to the completion model", async () => { + resolveDefaultModelForAgent.mockReturnValue({ + provider: "microsoft-foundry", + model: "claude-fable-5", + }); + resolveModelAsync.mockResolvedValue({ + model: { + provider: "microsoft-foundry", + api: "anthropic-messages", + baseUrl: "https://example.services.ai.azure.com/anthropic", + headers: { "x-api-key": "stale-key" }, + }, + authStorage: {}, + modelRegistry: {}, + }); + getRuntimeAuthForModel.mockResolvedValue({ + apiKey: "entra-token", + mode: "api-key", + request: { auth: { mode: "authorization-bearer", token: "entra-token" } }, + }); + requireApiKey.mockReturnValue("entra-token"); + + await generateConversationLabel({ + userMessage: "Need help with invoices", + prompt: "Generate a label", + cfg: {}, + }); + + expect(requireFirstMockCall(completeSimple, "simple completion")[0]).toMatchObject({ + provider: "microsoft-foundry", + headers: { Authorization: "Bearer entra-token" }, + }); + }); + it("omits temperature for Codex Responses simple completions", async () => { resolveDefaultModelForAgent.mockReturnValue({ provider: "openai", model: "gpt-5.5" }); resolveModelAsync.mockResolvedValue({ diff --git a/src/auto-reply/reply/conversation-label-generator.ts b/src/auto-reply/reply/conversation-label-generator.ts index 9516da431b6..39ef493886e 100644 --- a/src/auto-reply/reply/conversation-label-generator.ts +++ b/src/auto-reply/reply/conversation-label-generator.ts @@ -2,6 +2,7 @@ import { resolveModelAsync } from "../../agents/embedded-agent-runner/model.js"; import { requireApiKey } from "../../agents/model-auth.js"; import { resolveDefaultModelForAgent } from "../../agents/model-selection.js"; +import { applyPreparedRuntimeAuthToModel } from "../../agents/provider-request-config.js"; import { prepareModelForSimpleCompletion } from "../../agents/simple-completion-transport.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { logVerbose } from "../../globals.js"; @@ -61,21 +62,20 @@ export async function generateConversationLabel( } const completionModel = prepareModelForSimpleCompletion({ model: resolved.model, cfg }); - const apiKey = requireApiKey( - await getRuntimeAuthForModel({ - model: completionModel, - cfg, - workspaceDir: agentDir, - }), - modelRef.provider, - ); + const runtimeAuth = await getRuntimeAuthForModel({ + model: completionModel, + cfg, + workspaceDir: agentDir, + }); + const apiKey = requireApiKey(runtimeAuth, modelRef.provider); + const runtimeModel = applyPreparedRuntimeAuthToModel(completionModel, runtimeAuth); const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS); try { // Label generation should never block normal reply handling for long. const result = await completeSimple( - completionModel, + runtimeModel, { systemPrompt: prompt, messages: [ @@ -89,7 +89,7 @@ export async function generateConversationLabel( { apiKey, maxTokens: 100, - ...(isCodexSimpleCompletionModel(completionModel) ? {} : { temperature: 0.3 }), + ...(isCodexSimpleCompletionModel(runtimeModel) ? {} : { temperature: 0.3 }), signal: controller.signal, }, );