Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

924 lines
29 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { fetchWithCache } from '../../src/cache';
import { AzureChatCompletionProvider } from '../../src/providers/azure/chat';
import { AzureCompletionProvider } from '../../src/providers/azure/completion';
import { AzureGenericProvider } from '../../src/providers/azure/generic';
import { maybeEmitAzureOpenAiWarning } from '../../src/providers/azure/warnings';
import { HuggingfaceTextGenerationProvider } from '../../src/providers/huggingface';
import { OpenAiCompletionProvider } from '../../src/providers/openai/completion';
import { mockProcessEnv } from '../util/utils';
import type { TestCase, TestSuite } from '../../src/types/index';
vi.mock('../../src/cache', async (importOriginal) => {
return {
...(await importOriginal()),
fetchWithCache: vi.fn(),
};
});
describe('Azure Provider Tests', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(AzureGenericProvider.prototype as any, 'getAuthHeaders').mockResolvedValue({});
});
afterEach(() => {
vi.clearAllMocks();
});
describe('maybeEmitAzureOpenAiWarning', () => {
it('should not emit warning when no Azure providers are used', () => {
const testSuite: TestSuite = {
providers: [new OpenAiCompletionProvider('foo')],
defaultTest: {},
prompts: [],
};
const tests: TestCase[] = [
{
assert: [{ type: 'llm-rubric', value: 'foo bar' }],
},
];
const result = maybeEmitAzureOpenAiWarning(testSuite, tests);
expect(result).toBe(false);
});
it('should not emit warning when Azure provider is used alone, but no model graded eval', () => {
const testSuite: TestSuite = {
providers: [new AzureCompletionProvider('foo', { config: { apiHost: 'test.azure.com' } })],
defaultTest: {},
prompts: [],
};
const tests: TestCase[] = [
{
assert: [{ type: 'equals' }],
},
];
const result = maybeEmitAzureOpenAiWarning(testSuite, tests);
expect(result).toBe(false);
});
it('should emit warning when Azure provider is used alone, but with model graded eval', () => {
const testSuite: TestSuite = {
providers: [new AzureCompletionProvider('foo', { config: { apiHost: 'test.azure.com' } })],
defaultTest: {},
prompts: [],
};
const tests: TestCase[] = [
{
assert: [{ type: 'llm-rubric', value: 'foo bar' }],
},
];
const result = maybeEmitAzureOpenAiWarning(testSuite, tests);
expect(result).toBe(true);
});
it('should emit warning when Azure provider used with non-OpenAI provider', () => {
const testSuite: TestSuite = {
providers: [
new AzureCompletionProvider('foo', { config: { apiHost: 'test.azure.com' } }),
new HuggingfaceTextGenerationProvider('bar'),
],
defaultTest: {},
prompts: [],
};
const tests: TestCase[] = [
{
assert: [{ type: 'llm-rubric', value: 'foo bar' }],
},
];
const result = maybeEmitAzureOpenAiWarning(testSuite, tests);
expect(result).toBe(true);
});
it('should not emit warning when Azure providers are used with a default provider set', () => {
const testSuite: TestSuite = {
providers: [new AzureCompletionProvider('foo', { config: { apiHost: 'test.azure.com' } })],
defaultTest: { options: { provider: 'azureopenai:....' } },
prompts: [],
};
const tests: TestCase[] = [
{
assert: [{ type: 'llm-rubric', value: 'foo bar' }],
},
];
const result = maybeEmitAzureOpenAiWarning(testSuite, tests);
expect(result).toBe(false);
});
it('should not emit warning when both Azure and OpenAI providers are used', () => {
const testSuite: TestSuite = {
providers: [
new AzureCompletionProvider('foo', { config: { apiHost: 'test.azure.com' } }),
new OpenAiCompletionProvider('bar'),
],
defaultTest: {},
prompts: [],
};
const tests: TestCase[] = [
{
assert: [{ type: 'llm-rubric', value: 'foo bar' }],
},
];
const result = maybeEmitAzureOpenAiWarning(testSuite, tests);
expect(result).toBe(false);
});
});
describe('AzureOpenAiGenericProvider', () => {
describe('getApiBaseUrl', () => {
let restoreEnv: () => void;
beforeEach(() => {
restoreEnv = mockProcessEnv({ AZURE_OPENAI_API_HOST: undefined });
});
afterEach(() => {
restoreEnv();
});
it('should return apiBaseUrl if set', () => {
const provider = new AzureGenericProvider('test-deployment', {
config: { apiBaseUrl: 'https://custom.azure.com' },
});
expect(provider.getApiBaseUrl()).toBe('https://custom.azure.com');
});
it('should return apiBaseUrl without trailing slash if set', () => {
const provider = new AzureGenericProvider('test-deployment', {
config: { apiBaseUrl: 'https://custom.azure.com/' },
});
expect(provider.getApiBaseUrl()).toBe('https://custom.azure.com');
});
it('should construct URL from apiHost without protocol', () => {
const provider = new AzureGenericProvider('test-deployment', {
config: { apiHost: 'api.azure.com' },
});
expect(provider.getApiBaseUrl()).toBe('https://api.azure.com');
});
it('should remove protocol from apiHost if present', () => {
const provider = new AzureGenericProvider('test-deployment', {
config: { apiHost: 'https://api.azure.com' },
});
expect(provider.getApiBaseUrl()).toBe('https://api.azure.com');
});
it('should remove trailing slash from apiHost if present', () => {
const provider = new AzureGenericProvider('test-deployment', {
config: { apiHost: 'api.azure.com/' },
});
expect(provider.getApiBaseUrl()).toBe('https://api.azure.com');
});
it('should return undefined if neither apiBaseUrl nor apiHost is set', () => {
const provider = new AzureGenericProvider('test-deployment', {});
expect(provider.getApiBaseUrl()).toBeUndefined();
});
});
});
describe('AzureOpenAiChatCompletionProvider', () => {
describe('config merging', () => {
let provider: AzureChatCompletionProvider;
beforeEach(() => {
provider = new AzureChatCompletionProvider('test-deployment', {
config: {
apiHost: 'test.azure.com',
apiKey: 'test-key',
functions: [{ name: 'provider_func', parameters: {} }],
max_tokens: 100,
temperature: 0.5,
},
});
});
it('should use provider config when no prompt config exists', async () => {
const context = {
prompt: { label: 'test prompt', raw: 'test prompt' },
vars: {},
};
const { body } = await (provider as any).getOpenAiBody('test prompt', context);
expect(body).toMatchObject({
functions: [{ name: 'provider_func', parameters: {} }],
max_tokens: 100,
temperature: 0.5,
});
});
it('should merge prompt config with provider config', async () => {
const context = {
prompt: {
config: {
functions: [{ name: 'prompt_func', parameters: {} }],
temperature: 0.7,
},
label: 'test prompt',
raw: 'test prompt',
},
vars: {},
};
const { body } = await (provider as any).getOpenAiBody('test prompt', context);
expect(body).toMatchObject({
functions: [{ name: 'prompt_func', parameters: {} }],
max_tokens: 100,
temperature: 0.7,
});
});
it('should handle undefined prompt config', async () => {
const context = {
prompt: { label: 'test prompt', raw: 'test prompt' },
vars: {},
};
const { body } = await (provider as any).getOpenAiBody('test prompt', context);
expect(body).toMatchObject({
functions: [{ name: 'provider_func', parameters: {} }],
max_tokens: 100,
temperature: 0.5,
});
});
it('should handle empty prompt config', async () => {
const context = {
prompt: { config: {}, label: 'test prompt', raw: 'test prompt' },
vars: {},
};
const { body } = await (provider as any).getOpenAiBody('test prompt', context);
expect(body).toMatchObject({
functions: [{ name: 'provider_func', parameters: {} }],
max_tokens: 100,
temperature: 0.5,
});
});
it('should handle complex nested config merging', async () => {
const context = {
prompt: {
config: {
response_format: { type: 'json_object' },
tool_choice: { function: { name: 'test' }, type: 'function' },
},
label: 'test prompt',
raw: 'test prompt',
},
vars: {},
};
const { body } = await (provider as any).getOpenAiBody('test prompt', context);
expect(body).toMatchObject({
functions: [{ name: 'provider_func', parameters: {} }],
max_tokens: 100,
response_format: { type: 'json_object' },
temperature: 0.5,
tool_choice: { function: { name: 'test' }, type: 'function' },
});
});
it('should handle json_schema response format', async () => {
const context = {
prompt: {
config: {
response_format: {
type: 'json_schema',
json_schema: {
name: 'test_schema',
strict: true,
schema: {
type: 'object',
properties: {
test: { type: 'string' },
},
required: ['test'],
additionalProperties: false,
},
},
},
},
label: 'test prompt',
raw: 'test prompt',
},
vars: {},
};
const { body } = await (provider as any).getOpenAiBody('test prompt', context);
expect(body.response_format).toMatchObject({
type: 'json_schema',
json_schema: {
name: 'test_schema',
strict: true,
schema: {
type: 'object',
properties: {
test: { type: 'string' },
},
required: ['test'],
additionalProperties: false,
},
},
});
});
it('should render variables in response format', async () => {
const context = {
prompt: {
config: {
response_format: {
type: 'json_schema',
json_schema: {
name: '{{schemaName}}',
strict: true,
schema: {
type: 'object',
properties: {
test: { type: 'string' },
},
},
},
},
},
label: 'test prompt',
raw: 'test prompt',
},
vars: {
schemaName: 'dynamic_schema',
},
};
const { body } = await (provider as any).getOpenAiBody('test prompt', context);
expect(body.response_format.json_schema.name).toBe('dynamic_schema');
});
});
describe('response handling', () => {
let provider: AzureChatCompletionProvider;
beforeEach(() => {
provider = new AzureChatCompletionProvider('test-deployment', {
config: {
apiHost: 'test.azure.com',
apiKey: 'test-key',
},
});
});
afterEach(() => {
vi.resetAllMocks();
});
it('should parse JSON response with json_schema format when finish_reason is not content_filter', async () => {
const mockResponse = {
id: 'mock-id',
object: 'chat.completion',
created: Date.now(),
model: 'gpt-4',
choices: [
{
index: 0,
message: {
role: 'assistant',
content: JSON.stringify({ test: 'value' }),
},
finish_reason: 'stop',
},
],
usage: {
prompt_tokens: 10,
completion_tokens: 20,
total_tokens: 30,
},
};
provider.config.response_format = {
type: 'json_schema',
json_schema: {
name: 'test_response',
strict: true,
schema: {
type: 'object',
properties: {
test: { type: 'string' },
},
required: ['test'],
additionalProperties: false,
},
},
};
vi.mocked(fetchWithCache).mockResolvedValueOnce({
data: mockResponse,
cached: false,
status: 200,
statusText: 'OK',
});
const result = await provider.callApi('test prompt');
expect(result.output).toEqual({ test: 'value' });
});
it('should handle API errors', async () => {
vi.mocked(fetchWithCache).mockRejectedValueOnce(new Error('API Error'));
const result = await provider.callApi('test prompt');
expect(result.error).toBe('API call error: API Error');
});
it('should handle invalid JSON response', async () => {
vi.mocked(fetchWithCache).mockResolvedValueOnce({
data: 'invalid json',
cached: false,
status: 200,
statusText: 'OK',
});
const result = await provider.callApi('test prompt');
expect(result.error).toContain('API returned invalid JSON response');
});
it('should handle tool calls in response', async () => {
const mockResponse = {
id: 'mock-id',
object: 'chat.completion',
created: Date.now(),
model: 'gpt-4',
choices: [
{
index: 0,
message: {
role: 'assistant',
tool_calls: [
{
type: 'function',
function: {
name: 'test',
arguments: '{}',
},
},
],
},
finish_reason: 'stop',
},
],
usage: {
prompt_tokens: 10,
completion_tokens: 20,
total_tokens: 30,
},
};
vi.mocked(fetchWithCache).mockResolvedValueOnce({
data: mockResponse,
cached: false,
status: 200,
statusText: 'OK',
});
const result = await provider.callApi('test prompt');
expect(result.output).toEqual([
{
type: 'function',
function: {
name: 'test',
arguments: '{}',
},
},
]);
});
it('should handle content filter error response', async () => {
const mockResponse = {
error: {
message:
"The response was filtered due to the prompt triggering Azure OpenAI's content management policy.",
code: 'content_filter',
status: 400,
innererror: {
code: 'ResponsibleAIPolicyViolation',
content_filter_result: {
hate: { filtered: true, severity: 'medium' },
jailbreak: { filtered: false, detected: false },
self_harm: { filtered: false, severity: 'safe' },
sexual: { filtered: false, severity: 'safe' },
violence: { filtered: false, severity: 'low' },
},
},
},
};
vi.mocked(fetchWithCache).mockResolvedValueOnce({
data: mockResponse,
cached: false,
status: 400,
statusText: 'Bad Request',
});
const result = await provider.callApi('test prompt');
expect(result.output).toBe(mockResponse.error.message);
expect(result.guardrails).toEqual({
flagged: true,
flaggedInput: true,
flaggedOutput: false,
});
});
});
describe('structured outputs', () => {
let provider: AzureChatCompletionProvider;
beforeEach(() => {
vi.clearAllMocks();
provider = new AzureChatCompletionProvider('test-deployment', {
config: {
apiHost: 'test.azure.com',
apiKey: 'test-key',
},
});
});
afterEach(() => {
vi.clearAllMocks();
vi.restoreAllMocks();
});
it('should parse JSON response when prompt config specifies json_object format', async () => {
const mockResponse = {
id: 'mock-id',
object: 'chat.completion',
created: Date.now(),
model: 'gpt-4',
choices: [
{
index: 0,
message: {
role: 'assistant',
content: '{"result": 42, "explanation": "test"}',
},
finish_reason: 'stop',
},
],
usage: {
prompt_tokens: 10,
completion_tokens: 20,
total_tokens: 30,
},
};
vi.mocked(fetchWithCache).mockResolvedValueOnce({
data: mockResponse,
cached: false,
status: 200,
statusText: 'OK',
});
const result = await provider.callApi('test prompt', {
prompt: {
config: {
response_format: { type: 'json_object' },
},
label: 'test prompt',
raw: 'test prompt',
},
vars: {},
});
expect(result.output).toEqual({
result: 42,
explanation: 'test',
});
});
it('should handle invalid JSON when response format is specified', async () => {
const mockResponse = {
id: 'mock-id',
object: 'chat.completion',
created: Date.now(),
model: 'gpt-4',
choices: [
{
index: 0,
message: {
role: 'assistant',
content: 'Invalid JSON response',
},
finish_reason: 'stop',
},
],
usage: {
prompt_tokens: 10,
completion_tokens: 20,
total_tokens: 30,
},
};
vi.mocked(fetchWithCache).mockResolvedValueOnce({
data: mockResponse,
cached: false,
status: 200,
statusText: 'OK',
});
const result = await provider.callApi('test prompt', {
prompt: {
config: {
response_format: { type: 'json_object' },
},
label: 'test prompt',
raw: 'test prompt',
},
vars: {},
});
// Should still return the original string if JSON parsing fails
expect(result.output).toBe('Invalid JSON response');
});
it('should use correct API URL based on datasources config from prompt', async () => {
const mockResponse = {
id: 'mock-id',
choices: [
{
message: {
role: 'assistant',
content: 'test response',
},
},
],
usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
};
vi.mocked(fetchWithCache).mockResolvedValueOnce({
data: mockResponse,
cached: false,
status: 200,
statusText: 'OK',
});
await provider.callApi('test prompt', {
prompt: {
config: {
dataSources: [{ type: 'test' }],
apiVersion: '2024-custom',
},
label: 'test prompt',
raw: 'test prompt',
},
vars: {},
});
// Verify the URL includes extensions and uses the custom API version
expect(vi.mocked(fetchWithCache).mock.calls[0][0]).toContain(
'/extensions/chat/completions?api-version=2024-custom',
);
});
});
describe('reasoning models', () => {
it('should detect reasoning models with o1 flag', () => {
const provider = new AzureChatCompletionProvider('test-deployment', {
config: {
o1: true,
},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should detect reasoning models with isReasoningModel flag', () => {
const provider = new AzureChatCompletionProvider('test-deployment', {
config: {
isReasoningModel: true,
},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should detect reasoning models with either flag set', () => {
const provider = new AzureChatCompletionProvider('test-deployment', {
config: {
o1: false,
isReasoningModel: true,
},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should auto-detect o1 models by deployment name', () => {
const provider = new AzureChatCompletionProvider('o1-preview', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should auto-detect o3 models by deployment name', () => {
const provider = new AzureChatCompletionProvider('o3-mini', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should auto-detect o4 models by deployment name', () => {
const provider = new AzureChatCompletionProvider('o4-preview', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it.each([
'gpt-5.6-sol',
'gpt-5.6-terra',
'gpt-5.6-luna',
])('should auto-detect %s by deployment name', (model) => {
const provider = new AzureChatCompletionProvider(model, {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should not detect non-reasoning models', () => {
const provider = new AzureChatCompletionProvider('gpt-4o', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(false);
});
it('should auto-detect reasoning models with mixed case', () => {
const provider = new AzureChatCompletionProvider('GPT-5.4', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should auto-detect prefixed o1 deployment names', () => {
const provider = new AzureChatCompletionProvider('prod-o1-preview', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should auto-detect prefixed gpt-5 deployment names', () => {
const provider = new AzureChatCompletionProvider('staging-gpt-5.4', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should not detect non-reasoning models with similar names', () => {
const provider = new AzureChatCompletionProvider('gpt-4-turbo', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(false);
});
// Third-party reasoning model detection tests
it('should auto-detect DeepSeek-R1 reasoning models', () => {
const provider = new AzureChatCompletionProvider('DeepSeek-R1', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should auto-detect deepseek-r1 with different separators', () => {
const providerHyphen = new AzureChatCompletionProvider('deepseek-r1-distill', {
config: {},
});
const providerUnderscore = new AzureChatCompletionProvider('deepseek_r1', {
config: {},
});
expect((providerHyphen as any).isReasoningModel()).toBe(true);
expect((providerUnderscore as any).isReasoningModel()).toBe(true);
});
it('should auto-detect Phi-4-reasoning models', () => {
const provider = new AzureChatCompletionProvider('phi-4-reasoning', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should auto-detect Phi-4-mini-reasoning models', () => {
const provider = new AzureChatCompletionProvider('Phi-4-mini-reasoning', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should auto-detect Grok reasoning models', () => {
const provider = new AzureChatCompletionProvider('grok-3-reasoning', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should auto-detect grok-mini-reasoning models', () => {
const provider = new AzureChatCompletionProvider('grok-3-mini-reasoning', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(true);
});
it('should not detect regular Grok models as reasoning', () => {
const provider = new AzureChatCompletionProvider('grok-3', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(false);
});
it('should not detect regular DeepSeek-V3 as reasoning', () => {
const provider = new AzureChatCompletionProvider('DeepSeek-V3', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(false);
});
it('should not detect regular Phi-4 as reasoning', () => {
const provider = new AzureChatCompletionProvider('Phi-4', {
config: {},
});
expect((provider as any).isReasoningModel()).toBe(false);
});
it('should use max_completion_tokens for reasoning models', async () => {
const provider = new AzureChatCompletionProvider('test-deployment', {
config: {
isReasoningModel: true,
max_completion_tokens: 2000,
max_tokens: 1000,
},
});
const { body } = await (provider as any).getOpenAiBody('test prompt');
expect(body).toHaveProperty('max_completion_tokens', 2000);
expect(body).not.toHaveProperty('max_tokens');
});
it('should use reasoning_effort for reasoning models', async () => {
const provider = new AzureChatCompletionProvider('test-deployment', {
config: {
isReasoningModel: true,
reasoning_effort: 'high',
},
});
const { body } = await (provider as any).getOpenAiBody('test prompt');
expect(body).toHaveProperty('reasoning_effort', 'high');
});
it('should not include temperature for reasoning models', async () => {
const provider = new AzureChatCompletionProvider('test-deployment', {
config: {
isReasoningModel: true,
temperature: 0.7,
},
});
const { body } = await (provider as any).getOpenAiBody('test prompt');
expect(body).not.toHaveProperty('temperature');
});
it('should support variable rendering in reasoning_effort', async () => {
const provider = new AzureChatCompletionProvider('test-deployment', {
config: {
isReasoningModel: true,
reasoning_effort: '{{effort}}' as any,
apiHost: 'test.azure.com',
},
});
const context = {
prompt: { label: 'test prompt', raw: 'test prompt' },
vars: { effort: 'high' as const },
};
const { body } = await (provider as any).getOpenAiBody('test prompt', context);
expect(body).toHaveProperty('reasoning_effort', 'high');
});
});
});
describe('AzureCompletionProvider', () => {
it('should handle basic completion with caching', async () => {
vi.mocked(fetchWithCache).mockResolvedValueOnce({
data: {
choices: [{ text: 'hello' }],
usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
},
cached: false,
} as any);
vi.mocked(fetchWithCache).mockResolvedValueOnce({
data: {
choices: [{ text: 'hello' }],
usage: { total_tokens: 10 },
},
cached: true,
} as any);
const provider = new AzureCompletionProvider('test', {
config: { apiHost: 'test.azure.com' },
});
(provider as any).authHeaders = {};
const result1 = await provider.callApi('test prompt');
const result2 = await provider.callApi('test prompt');
expect(result1.output).toBe('hello');
expect(result2.output).toBe('hello');
expect(result1.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
expect(result2.tokenUsage).toEqual({ cached: 10, total: 10 });
});
});
});