95 lines
3.5 KiB
TypeScript
95 lines
3.5 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest'
|
|
import { OpenAIProvider } from '../../src/providers/openai'
|
|
import { createProvider } from '../../src/providers/factory'
|
|
import type { MagpieConfig } from '../../src/config/types'
|
|
|
|
let lastConstructorOptions: Record<string, unknown> = {}
|
|
let lastCreateOptions: Record<string, unknown> = {}
|
|
|
|
vi.mock('openai', () => ({
|
|
default: class MockOpenAI {
|
|
chat = {
|
|
completions: {
|
|
create: vi.fn().mockImplementation((opts: Record<string, unknown>) => {
|
|
lastCreateOptions = opts
|
|
return Promise.resolve({
|
|
choices: [{ message: { content: 'Mock response' } }]
|
|
})
|
|
})
|
|
}
|
|
}
|
|
constructor(options: Record<string, unknown>) {
|
|
lastConstructorOptions = options
|
|
}
|
|
}
|
|
}))
|
|
|
|
describe('OpenAIProvider', () => {
|
|
it('should have correct name', () => {
|
|
const provider = new OpenAIProvider({ apiKey: 'test', model: 'gpt-4o' })
|
|
expect(provider.name).toBe('openai')
|
|
})
|
|
|
|
it('should call chat and return response', async () => {
|
|
const provider = new OpenAIProvider({ apiKey: 'test', model: 'gpt-4o' })
|
|
const result = await provider.chat([{ role: 'user', content: 'Hello' }])
|
|
expect(result).toBe('Mock response')
|
|
})
|
|
|
|
it('should pass baseURL to SDK when provided', () => {
|
|
new OpenAIProvider({ apiKey: 'test', model: 'gpt-4o', baseURL: 'https://my-proxy.example.com/v1' })
|
|
expect(lastConstructorOptions.baseURL).toBe('https://my-proxy.example.com/v1')
|
|
})
|
|
|
|
it('should not set baseURL when not provided', () => {
|
|
new OpenAIProvider({ apiKey: 'test', model: 'gpt-4o' })
|
|
expect(lastConstructorOptions.baseURL).toBeUndefined()
|
|
})
|
|
})
|
|
|
|
describe('OpenRouter via OpenAI client', () => {
|
|
const baseConfig: MagpieConfig = {
|
|
providers: {},
|
|
defaults: { max_rounds: 3, output_format: 'markdown' },
|
|
reviewers: {},
|
|
summarizer: { model: 'openrouter/anthropic/claude-3.5-sonnet', prompt: '' },
|
|
analyzer: { model: 'openrouter/anthropic/claude-3.5-sonnet', prompt: '' }
|
|
}
|
|
|
|
it('strips the openrouter/ prefix from the model and defaults baseURL to OpenRouter', async () => {
|
|
const config: MagpieConfig = {
|
|
...baseConfig,
|
|
providers: { openrouter: { api_key: 'or-key' } }
|
|
}
|
|
const provider = createProvider('openrouter/anthropic/claude-3.5-sonnet', config)
|
|
expect(lastConstructorOptions.apiKey).toBe('or-key')
|
|
expect(lastConstructorOptions.baseURL).toBe('https://openrouter.ai/api/v1')
|
|
|
|
// Invoke chat() so the stripped model reaches chat.completions.create
|
|
await provider.chat([{ role: 'user', content: 'hi' }])
|
|
expect(lastCreateOptions.model).toBe('anthropic/claude-3.5-sonnet')
|
|
})
|
|
|
|
it('honors a custom base_url from config and forwards the stripped model', async () => {
|
|
const config: MagpieConfig = {
|
|
...baseConfig,
|
|
providers: {
|
|
openrouter: { api_key: 'or-key', base_url: 'https://my-openrouter-proxy.example.com/v1' }
|
|
}
|
|
}
|
|
const provider = createProvider('openrouter/meta-llama/llama-3-70b-instruct', config)
|
|
expect(lastConstructorOptions.baseURL).toBe('https://my-openrouter-proxy.example.com/v1')
|
|
|
|
await provider.chat([{ role: 'user', content: 'hi' }])
|
|
expect(lastCreateOptions.model).toBe('meta-llama/llama-3-70b-instruct')
|
|
})
|
|
|
|
it('throws when the model is just "openrouter/" with no ID after it', () => {
|
|
const config: MagpieConfig = {
|
|
...baseConfig,
|
|
providers: { openrouter: { api_key: 'or-key' } }
|
|
}
|
|
expect(() => createProvider('openrouter/', config)).toThrow(/must include a model ID/)
|
|
})
|
|
})
|