GigaChat: tighten auth handling

This commit is contained in:
Alexander Davydov 2026-03-16 17:22:58 +03:00
parent dd1b3398d1
commit 1ae292ce26
4 changed files with 72 additions and 2 deletions

View File

@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
const updateToken = vi.fn(async () => {});
const request = vi.fn();
const clientConfigs: Array<Record<string, unknown>> = [];
vi.mock("gigachat", () => {
class MockGigaChat {
@ -10,6 +11,10 @@ vi.mock("gigachat", () => {
_accessToken = { access_token: "test-token" };
updateToken = updateToken;
constructor(config: Record<string, unknown>) {
clientConfigs.push(config);
}
}
return { GigaChat: MockGigaChat };
@ -24,6 +29,7 @@ function createSseStream(lines: string[]): Readable {
describe("createGigachatStreamFn tool calling", () => {
beforeEach(() => {
vi.clearAllMocks();
clientConfigs.length = 0;
});
it("round-trips sanitized tool names for streamed function calls", async () => {
@ -273,4 +279,37 @@ describe("createGigachatStreamFn tool calling", () => {
expect(updateToken).not.toHaveBeenCalled();
expect(request).not.toHaveBeenCalled();
});
it("honors oauth auth mode even when credentials contain a colon", async () => {
request.mockResolvedValueOnce({
status: 200,
data: createSseStream(['data: {"choices":[{"delta":{"content":"done"}}]}', "data: [DONE]"]),
});
const streamFn = createGigachatStreamFn({
baseUrl: "https://gigachat.devices.sberbank.ru/api/v1",
authMode: "oauth",
scope: "GIGACHAT_API_PERS",
});
const stream = streamFn(
{ api: "gigachat", provider: "gigachat", id: "GigaChat-2-Max" } as never,
{
messages: [],
tools: [],
} as never,
{ apiKey: "oauth:credential:with:colon" } as never,
);
const event = await stream.result();
expect(event.content).toEqual([{ type: "text", text: "done" }]);
expect(clientConfigs).toHaveLength(1);
expect(clientConfigs[0]).toMatchObject({
credentials: "oauth:credential:with:colon",
scope: "GIGACHAT_API_PERS",
});
expect(clientConfigs[0]?.user).toBeUndefined();
expect(clientConfigs[0]?.password).toBeUndefined();
});
});

View File

@ -586,7 +586,6 @@ export function createGigachatStreamFn(opts: GigachatStreamOptions): StreamFn {
// Build auth config
const apiKey = options?.apiKey ?? "";
const isUserPassCredentials = apiKey.includes(":");
const clientConfig: GigaChatClientConfig = {
baseUrl: effectiveBaseUrl,
@ -601,7 +600,7 @@ export function createGigachatStreamFn(opts: GigachatStreamOptions): StreamFn {
}
// Set credentials based on auth mode
if (isUserPassCredentials) {
if (opts.authMode === "basic") {
const { user, password } = parseGigachatBasicCredentials(apiKey);
clientConfig.user = user;
clientConfig.password = password;

View File

@ -638,6 +638,23 @@ describe("onboard (non-interactive): provider auth", () => {
});
});
it.each(["gigachat-basic", "gigachat-business", "gigachat-personal"] as const)(
'rejects "%s" auth choice in non-interactive mode',
async (authChoice) => {
await withOnboardEnv(
`openclaw-onboard-${authChoice}-non-interactive-`,
async ({ runtime }) => {
await expect(
runNonInteractiveOnboardingWithDefaults(runtime, {
authChoice,
skipSkills: true,
}),
).rejects.toThrow(`Auth choice "${authChoice}" requires interactive mode.`);
},
);
},
);
it("stores LiteLLM API key and sets default model", async () => {
await withOnboardEnv("openclaw-onboard-litellm-", async (env) => {
const cfg = await runOnboardingAndReadConfig(env, {

View File

@ -189,6 +189,21 @@ export async function applyNonInteractiveAuthChoice(params: {
return simpleApiKeyChoice;
}
if (
authChoice === "gigachat-basic" ||
authChoice === "gigachat-business" ||
authChoice === "gigachat-personal"
) {
runtime.error(
[
`Auth choice "${authChoice}" requires interactive mode.`,
'Use "--gigachat-api-key" for non-interactive personal OAuth onboarding, or run interactive onboarding for business/basic GigaChat setup.',
].join("\n"),
);
runtime.exit(1);
return null;
}
if (authChoice === "cloudflare-ai-gateway-api-key") {
const accountId = opts.cloudflareAiGatewayAccountId?.trim() ?? "";
const gatewayId = opts.cloudflareAiGatewayGatewayId?.trim() ?? "";