diff --git a/extensions/telegram/src/bot-handlers.runtime.ts b/extensions/telegram/src/bot-handlers.runtime.ts index e354a3cc89d..795998e6a1d 100644 --- a/extensions/telegram/src/bot-handlers.runtime.ts +++ b/extensions/telegram/src/bot-handlers.runtime.ts @@ -1342,8 +1342,14 @@ export const registerTelegramHandlers = ({ resolvedThreadId, senderId, }); - const configuredModelData = buildConfiguredModelsProviderData(cfg, sessionState.agentId); - let modelData = configuredModelData; + const configuredModelData = buildConfiguredModelsProviderData( + runtimeCfg, + sessionState.agentId, + ); + let modelData = await telegramDeps.buildModelsProviderData( + runtimeCfg, + sessionState.agentId, + ); let { byProvider, providers } = modelData; const editMessageWithButtons = async ( @@ -1434,12 +1440,12 @@ export const registerTelegramHandlers = ({ if (modelCallback.type === "select") { let selection = resolveModelSelection({ callback: modelCallback, - providers, - byProvider, + providers: configuredModelData.providers, + byProvider: configuredModelData.byProvider, }); let selectionAllowed = selection.kind === "resolved" && - Boolean(byProvider.get(selection.provider)?.has(selection.model)); + Boolean(configuredModelData.byProvider.get(selection.provider)?.has(selection.model)); if (!selectionAllowed || selection.kind !== "resolved") { modelData = await telegramDeps.buildModelsProviderData( diff --git a/extensions/telegram/src/bot.create-telegram-bot.test.ts b/extensions/telegram/src/bot.create-telegram-bot.test.ts index ecbabefdb20..25fadfa1ce6 100644 --- a/extensions/telegram/src/bot.create-telegram-bot.test.ts +++ b/extensions/telegram/src/bot.create-telegram-bot.test.ts @@ -197,7 +197,31 @@ describe("createTelegramBot", () => { expect(answerCallbackQuerySpy).toHaveBeenCalledWith("cbq-1"); }); it("reloads callback model routing bindings without recreating the bot", async () => { + const buildModelsProviderDataMock = + telegramBotDepsForTest.buildModelsProviderData as unknown as ReturnType; let boundAgentId = "agent-a"; + buildModelsProviderDataMock.mockImplementation( + async (_cfg: OpenClawConfig, agentId?: string) => { + if (agentId === "agent-b") { + return { + byProvider: new Map([ + ["anthropic", new Set(["claude-opus-4-5"])], + ["openai", new Set(["gpt-4.1"])], + ]), + providers: ["anthropic", "openai"], + resolvedDefault: { provider: "anthropic", model: "claude-opus-4-5" }, + }; + } + return { + byProvider: new Map([ + ["gemini", new Set(["gemini-2.5-pro"])], + ["openai", new Set(["gpt-4.1"])], + ]), + providers: ["gemini", "openai"], + resolvedDefault: { provider: "openai", model: "gpt-4.1" }, + }; + }, + ); loadConfig.mockImplementation(() => ({ agents: { defaults: { @@ -238,20 +262,32 @@ describe("createTelegramBot", () => { }); }; + buildModelsProviderDataMock.mockClear(); editMessageTextSpy.mockClear(); await sendModelCallback(1); + expect(buildModelsProviderDataMock).toHaveBeenCalled(); + expect(buildModelsProviderDataMock.mock.calls.at(-1)?.[1]).toBe("agent-a"); expect(editMessageTextSpy).toHaveBeenCalledTimes(1); - expect(editMessageTextSpy.mock.calls.at(-1)?.[3]).toEqual( - expect.objectContaining({ - reply_markup: { - inline_keyboard: [[{ text: "openai (1)", callback_data: "mdl_list_openai_1" }]], - }, - }), + expect( + editMessageTextSpy.mock.calls.at(-1)?.[3]?.reply_markup?.inline_keyboard?.flat(), + ).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + text: "gemini (1)", + callback_data: "mdl_list_gemini_1", + }), + expect.objectContaining({ + text: "openai (1)", + callback_data: "mdl_list_openai_1", + }), + ]), ); boundAgentId = "agent-b"; + buildModelsProviderDataMock.mockClear(); editMessageTextSpy.mockClear(); await sendModelCallback(2); + expect(buildModelsProviderDataMock.mock.calls.at(-1)?.[1]).toBe("agent-b"); expect(editMessageTextSpy).toHaveBeenCalledTimes(1); expect( editMessageTextSpy.mock.calls.at(-1)?.[3]?.reply_markup?.inline_keyboard?.flat(), diff --git a/src/agents/gigachat-stream.tool-calls.test.ts b/src/agents/gigachat-stream.tool-calls.test.ts index ffb31f7354c..dd0f2b697ef 100644 --- a/src/agents/gigachat-stream.tool-calls.test.ts +++ b/src/agents/gigachat-stream.tool-calls.test.ts @@ -653,6 +653,37 @@ describe("createGigachatStreamFn tool calling", () => { expect(clientConfigs[0]?.password).toBeUndefined(); }); + it("honors explicit basic auth scopes when metadata provides one", async () => { + request.mockResolvedValueOnce({ + status: 200, + data: createSseStream(['data: {"choices":[{"delta":{"content":"done"}}]}', "data: [DONE]"]), + }); + + const streamFn = createGigachatStreamFn({ + baseUrl: "https://gigachat.ift.sberdevices.ru/v1", + authMode: "basic", + scope: "GIGACHAT_API_B2B", + }); + + const stream = await streamFn( + { api: "gigachat", provider: "gigachat", id: "GigaChat-2-Max" } as never, + { messages: [], tools: [] } as never, + { apiKey: "basic-user:basic-password" } as never, + ); + + await expect(stream.result()).resolves.toMatchObject({ + content: [{ type: "text", text: "done" }], + }); + + expect(clientConfigs).toHaveLength(1); + expect(clientConfigs[0]).toMatchObject({ + user: "basic-user", + password: "basic-password", + scope: "GIGACHAT_API_B2B", + }); + expect(clientConfigs[0]?.credentials).toBeUndefined(); + }); + it("falls back to the SDK default oauth scope when no metadata scope is available", async () => { request.mockResolvedValueOnce({ status: 200, diff --git a/src/agents/gigachat-stream.ts b/src/agents/gigachat-stream.ts index 86fc8dbfd52..eb5c3060763 100644 --- a/src/agents/gigachat-stream.ts +++ b/src/agents/gigachat-stream.ts @@ -544,6 +544,7 @@ export function createGigachatStreamFn(opts: GigachatStreamOptions): StreamFn { profanityCheck: undefined, timeout: 120, }; + const configuredScope = opts.scope?.trim(); if (insecureTls) { clientConfig.httpsAgent = new https.Agent({ rejectUnauthorized: false }); @@ -553,10 +554,14 @@ export function createGigachatStreamFn(opts: GigachatStreamOptions): StreamFn { const { user, password } = parseGigachatBasicCredentials(apiKey); clientConfig.user = user; clientConfig.password = password; - log.debug(`GigaChat auth: basic mode`); + if (configuredScope) { + clientConfig.scope = configuredScope; + } + log.debug( + `GigaChat auth: basic mode${clientConfig.scope ? ` scope=${clientConfig.scope}` : ""}`, + ); } else { clientConfig.credentials = apiKey; - const configuredScope = opts.scope?.trim(); if (configuredScope) { clientConfig.scope = configuredScope; }