GigaChat: tighten auth handling
This commit is contained in:
parent
dd1b3398d1
commit
1ae292ce26
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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, {
|
||||
|
||||
@ -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() ?? "";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user