0d3cb498a3
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Has been cancelled
Test and Publish Multi-arch Docker Image / test (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Has been cancelled
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Has been cancelled
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Has been cancelled
Validate Renovate Config / Validate Renovate Configuration (push) Has been cancelled
231 lines
7.4 KiB
TypeScript
231 lines
7.4 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
import { clearCache } from '../../../src/cache';
|
|
import { GroqProvider } from '../../../src/providers/groq/index';
|
|
import { mockProcessEnv } from '../../util/utils';
|
|
|
|
import type { OpenAiChatCompletionProvider } from '../../../src/providers/openai/chat';
|
|
|
|
const GROQ_API_BASE = 'https://api.groq.com/openai/v1';
|
|
|
|
describe('GroqProvider', () => {
|
|
let restoreEnv: () => void;
|
|
|
|
beforeEach(() => {
|
|
restoreEnv = mockProcessEnv({ GROQ_API_KEY: 'test-key' });
|
|
});
|
|
|
|
afterEach(async () => {
|
|
restoreEnv();
|
|
await clearCache();
|
|
});
|
|
|
|
describe('constructor and identification', () => {
|
|
it('should initialize with correct model name', () => {
|
|
const provider = new GroqProvider('mixtral-8x7b-32768', {});
|
|
expect(provider.modelName).toBe('mixtral-8x7b-32768');
|
|
});
|
|
|
|
it('should return correct id', () => {
|
|
const provider = new GroqProvider('mixtral-8x7b-32768', {});
|
|
expect(provider.id()).toBe('groq:mixtral-8x7b-32768');
|
|
});
|
|
|
|
it('should return correct string representation', () => {
|
|
const provider = new GroqProvider('mixtral-8x7b-32768', {});
|
|
expect(provider.toString()).toBe('[Groq Provider mixtral-8x7b-32768]');
|
|
});
|
|
|
|
it('should configure correct API base URL and key envar', () => {
|
|
const provider = new GroqProvider('mixtral-8x7b-32768', {});
|
|
expect((provider as OpenAiChatCompletionProvider).config).toMatchObject({
|
|
apiKeyEnvar: 'GROQ_API_KEY',
|
|
apiBaseUrl: GROQ_API_BASE,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('reasoning model detection', () => {
|
|
it('should identify regular models as non-reasoning', () => {
|
|
const provider = new GroqProvider('mixtral-8x7b-32768', {});
|
|
expect(provider['isReasoningModel']()).toBe(false);
|
|
});
|
|
|
|
it('should not identify retired deepseek-r1 as reasoning model', () => {
|
|
const provider = new GroqProvider('deepseek-r1-distill-llama-70b', {});
|
|
expect(provider['isReasoningModel']()).toBe(false);
|
|
});
|
|
|
|
it('should identify gpt-oss as reasoning model', () => {
|
|
const provider = new GroqProvider('openai/gpt-oss-120b', {});
|
|
expect(provider['isReasoningModel']()).toBe(true);
|
|
});
|
|
|
|
it('should identify qwen as reasoning model', () => {
|
|
const provider = new GroqProvider('qwen/qwen3.6-27b', {});
|
|
expect(provider['isReasoningModel']()).toBe(true);
|
|
});
|
|
|
|
it('should identify o1 models as reasoning (via parent)', () => {
|
|
const provider = new GroqProvider('o1-mini', {});
|
|
expect(provider['isReasoningModel']()).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('temperature support', () => {
|
|
it('should support temperature for regular models', () => {
|
|
const provider = new GroqProvider('mixtral-8x7b-32768', {});
|
|
expect(provider['supportsTemperature']()).toBe(true);
|
|
});
|
|
|
|
it('should support temperature for gpt-oss models', () => {
|
|
const provider = new GroqProvider('openai/gpt-oss-120b', {});
|
|
expect(provider['supportsTemperature']()).toBe(true);
|
|
});
|
|
|
|
it('should support temperature for qwen models', () => {
|
|
const provider = new GroqProvider('qwen/qwen3.6-27b', {});
|
|
expect(provider['supportsTemperature']()).toBe(true);
|
|
});
|
|
|
|
it('should not support temperature for o1 models (via parent)', () => {
|
|
const provider = new GroqProvider('o1-mini', {});
|
|
expect(provider['supportsTemperature']()).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('serialization', () => {
|
|
it('should serialize to JSON correctly without API key', () => {
|
|
const provider = new GroqProvider('mixtral-8x7b-32768', {
|
|
config: {
|
|
temperature: 0.7,
|
|
max_tokens: 100,
|
|
},
|
|
});
|
|
|
|
expect(provider.toJSON()).toEqual({
|
|
provider: 'groq',
|
|
model: 'mixtral-8x7b-32768',
|
|
config: {
|
|
temperature: 0.7,
|
|
max_tokens: 100,
|
|
apiKeyEnvar: 'GROQ_API_KEY',
|
|
apiBaseUrl: GROQ_API_BASE,
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should redact API key in serialization', () => {
|
|
const provider = new GroqProvider('mixtral-8x7b-32768', {
|
|
config: {
|
|
apiKey: 'secret-api-key',
|
|
temperature: 0.7,
|
|
},
|
|
});
|
|
|
|
const json = provider.toJSON();
|
|
expect(json.config.apiKey).toBeUndefined();
|
|
// But the actual provider should still have the key
|
|
expect(provider['apiKey']).toBe('secret-api-key');
|
|
});
|
|
});
|
|
|
|
describe('getOpenAiBody', () => {
|
|
it('should include reasoning_format when configured', async () => {
|
|
const provider = new GroqProvider('openai/gpt-oss-120b', {
|
|
config: {
|
|
reasoning_format: 'parsed',
|
|
},
|
|
});
|
|
|
|
const { body } = await provider['getOpenAiBody']('Test prompt');
|
|
expect(body.reasoning_format).toBe('parsed');
|
|
});
|
|
|
|
it('should include include_reasoning when configured', async () => {
|
|
const provider = new GroqProvider('openai/gpt-oss-120b', {
|
|
config: {
|
|
include_reasoning: true,
|
|
},
|
|
});
|
|
|
|
const { body } = await provider['getOpenAiBody']('Test prompt');
|
|
expect(body.include_reasoning).toBe(true);
|
|
});
|
|
|
|
it('should include compound_custom when configured', async () => {
|
|
const provider = new GroqProvider('groq/compound', {
|
|
config: {
|
|
compound_custom: {
|
|
tools: {
|
|
enabled_tools: ['code_interpreter', 'web_search'],
|
|
wolfram_settings: {
|
|
authorization: 'test-key',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const { body } = await provider['getOpenAiBody']('Test prompt');
|
|
expect(body.compound_custom).toEqual({
|
|
tools: {
|
|
enabled_tools: ['code_interpreter', 'web_search'],
|
|
wolfram_settings: {
|
|
authorization: 'test-key',
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should include search_settings when configured', async () => {
|
|
const provider = new GroqProvider('groq/compound', {
|
|
config: {
|
|
search_settings: {
|
|
exclude_domains: ['example.com'],
|
|
include_domains: ['trusted.com'],
|
|
country: 'US',
|
|
},
|
|
},
|
|
});
|
|
|
|
const { body } = await provider['getOpenAiBody']('Test prompt');
|
|
expect(body.search_settings).toEqual({
|
|
exclude_domains: ['example.com'],
|
|
include_domains: ['trusted.com'],
|
|
country: 'US',
|
|
});
|
|
});
|
|
|
|
it('should handle all Groq-specific parameters together', async () => {
|
|
const provider = new GroqProvider('openai/gpt-oss-120b', {
|
|
config: {
|
|
reasoning_format: 'hidden',
|
|
include_reasoning: false,
|
|
compound_custom: {
|
|
tools: {
|
|
enabled_tools: ['browser_automation'],
|
|
},
|
|
},
|
|
search_settings: {
|
|
exclude_domains: ['spam.com'],
|
|
},
|
|
},
|
|
});
|
|
|
|
const { body } = await provider['getOpenAiBody']('Test prompt');
|
|
expect(body.reasoning_format).toBe('hidden');
|
|
expect(body.include_reasoning).toBe(false);
|
|
expect(body.compound_custom).toBeDefined();
|
|
expect(body.search_settings).toBeDefined();
|
|
});
|
|
|
|
it('should build correct message structure', async () => {
|
|
const provider = new GroqProvider('mixtral-8x7b-32768', {});
|
|
const { body } = await provider['getOpenAiBody']('Test prompt');
|
|
|
|
expect(body.model).toBe('mixtral-8x7b-32768');
|
|
expect(body.messages).toEqual([{ role: 'user', content: 'Test prompt' }]);
|
|
});
|
|
});
|
|
});
|