| |
| |
| |
| |
| |
|
|
| import { describe, it, expect } from 'vitest'; |
| import { convertToRestPayload } from './apiConversionUtils.js'; |
| import { |
| FunctionCallingConfigMode, |
| HarmCategory, |
| HarmBlockThreshold, |
| type GenerateContentParameters, |
| } from '@google/genai'; |
|
|
| describe('apiConversionUtils', () => { |
| describe('convertToRestPayload', () => { |
| it('handles minimal requests with no config', () => { |
| const req: GenerateContentParameters = { |
| model: 'gemini-3-flash', |
| contents: [{ role: 'user', parts: [{ text: 'Hello' }] }], |
| }; |
|
|
| const result = convertToRestPayload(req); |
|
|
| expect(result).toStrictEqual({ |
| contents: [{ role: 'user', parts: [{ text: 'Hello' }] }], |
| }); |
| expect(result['generationConfig']).toBeUndefined(); |
| }); |
|
|
| it('normalizes string systemInstruction to REST format', () => { |
| const req: GenerateContentParameters = { |
| model: 'gemini-3-flash', |
| contents: [{ role: 'user', parts: [{ text: 'Hello' }] }], |
| config: { |
| systemInstruction: 'You are a helpful assistant.', |
| }, |
| }; |
|
|
| const result = convertToRestPayload(req); |
|
|
| expect(result['systemInstruction']).toStrictEqual({ |
| parts: [{ text: 'You are a helpful assistant.' }], |
| }); |
| expect(result['generationConfig']).toBeUndefined(); |
| }); |
|
|
| it('preserves object-based systemInstruction', () => { |
| const sysInstruction = { parts: [{ text: 'Object instruction' }] }; |
| const req: GenerateContentParameters = { |
| model: 'gemini-3-flash', |
| contents: [{ role: 'user', parts: [{ text: 'Hello' }] }], |
| config: { |
| systemInstruction: sysInstruction, |
| }, |
| }; |
|
|
| const result = convertToRestPayload(req); |
|
|
| expect(result['systemInstruction']).toStrictEqual(sysInstruction); |
| }); |
|
|
| it('hoists capabilities (tools, safety, cachedContent) to the root level', () => { |
| const req: GenerateContentParameters = { |
| model: 'gemini-3-flash', |
| contents: [{ role: 'user', parts: [{ text: 'Hello' }] }], |
| config: { |
| tools: [{ functionDeclarations: [{ name: 'myTool' }] }], |
| toolConfig: { |
| functionCallingConfig: { mode: FunctionCallingConfigMode.ANY }, |
| }, |
| safetySettings: [ |
| { |
| category: HarmCategory.HARM_CATEGORY_HARASSMENT, |
| threshold: HarmBlockThreshold.BLOCK_NONE, |
| }, |
| ], |
| cachedContent: 'cached-content-id', |
| }, |
| }; |
|
|
| const result = convertToRestPayload(req); |
|
|
| expect(result['tools']).toBeDefined(); |
| expect(result['toolConfig']).toBeDefined(); |
| expect(result['safetySettings']).toBeDefined(); |
| expect(result['cachedContent']).toBe('cached-content-id'); |
| |
| expect(result['generationConfig']).toBeUndefined(); |
| }); |
|
|
| it('retains pure hyperparameters in generationConfig', () => { |
| const req: GenerateContentParameters = { |
| model: 'gemini-3-flash', |
| contents: [{ role: 'user', parts: [{ text: 'Hello' }] }], |
| config: { |
| temperature: 0.7, |
| topP: 0.9, |
| maxOutputTokens: 100, |
| }, |
| }; |
|
|
| const result = convertToRestPayload(req); |
|
|
| expect(result['generationConfig']).toStrictEqual({ |
| temperature: 0.7, |
| topP: 0.9, |
| maxOutputTokens: 100, |
| }); |
| }); |
|
|
| it('strips JS-specific abortSignal from the final payload', () => { |
| const req: GenerateContentParameters = { |
| model: 'gemini-3-flash', |
| contents: [{ role: 'user', parts: [{ text: 'Hello' }] }], |
| config: { |
| temperature: 0.5, |
| abortSignal: new AbortController().signal, |
| }, |
| }; |
|
|
| const result = convertToRestPayload(req); |
|
|
| expect(result['generationConfig']).toStrictEqual({ |
| temperature: 0.5, |
| }); |
| expect(result['abortSignal']).toBeUndefined(); |
| |
| expect(result['generationConfig']?.abortSignal).toBeUndefined(); |
| }); |
|
|
| it('handles a complex kitchen-sink request correctly', () => { |
| const req: GenerateContentParameters = { |
| model: 'gemini-3-flash', |
| contents: [{ role: 'user', parts: [{ text: 'Kitchen sink' }] }], |
| config: { |
| systemInstruction: 'Be witty.', |
| temperature: 0.8, |
| tools: [{ functionDeclarations: [{ name: 'test' }] }], |
| abortSignal: new AbortController().signal, |
| safetySettings: [ |
| { |
| category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, |
| threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, |
| }, |
| ], |
| topK: 40, |
| }, |
| }; |
|
|
| const result = convertToRestPayload(req); |
|
|
| |
| expect(result['contents']).toBeDefined(); |
| expect(result['systemInstruction']).toStrictEqual({ |
| parts: [{ text: 'Be witty.' }], |
| }); |
| expect(result['tools']).toStrictEqual([ |
| { functionDeclarations: [{ name: 'test' }] }, |
| ]); |
| expect(result['safetySettings']).toStrictEqual([ |
| { |
| category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, |
| threshold: HarmBlockThreshold.BLOCK_LOW_AND_ABOVE, |
| }, |
| ]); |
| expect(result['abortSignal']).toBeUndefined(); |
|
|
| |
| expect(result['generationConfig']).toStrictEqual({ |
| temperature: 0.8, |
| topK: 40, |
| }); |
| }); |
| }); |
| }); |
|
|