151 lines
4.4 KiB
TypeScript
151 lines
4.4 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import { modelPatch } from './utils'
|
|
|
|
/**
|
|
* Baseline request body used as starting point for each provider test.
|
|
* Mirrors what OpenAIClient builds before calling modelPatch.
|
|
*/
|
|
function baseBody(model: string) {
|
|
return {
|
|
model,
|
|
temperature: 0.7,
|
|
messages: [],
|
|
tools: [],
|
|
parallel_tool_calls: false,
|
|
tool_choice: 'required' as unknown,
|
|
}
|
|
}
|
|
|
|
describe('modelPatch', () => {
|
|
it('returns body unchanged when model is missing', () => {
|
|
const body = { temperature: 0.7 }
|
|
expect(modelPatch(body)).toBe(body)
|
|
expect(body).toEqual({ temperature: 0.7 })
|
|
})
|
|
|
|
it('qwen: bumps temperature and disables thinking', () => {
|
|
const body = baseBody('qwen-max')
|
|
modelPatch(body)
|
|
expect(body.temperature).toBe(1.0)
|
|
expect(body).toMatchObject({ enable_thinking: false })
|
|
})
|
|
|
|
it('qwen: keeps higher caller-provided temperature', () => {
|
|
const body = baseBody('qwen-max')
|
|
body.temperature = 1.5
|
|
modelPatch(body)
|
|
expect(body.temperature).toBe(1.5)
|
|
})
|
|
|
|
it('claude: disables thinking and converts tool_choice "required" -> { type: "any" }', () => {
|
|
const body = baseBody('claude-3-5-sonnet')
|
|
modelPatch(body)
|
|
expect(body).toMatchObject({
|
|
thinking: { type: 'disabled' },
|
|
tool_choice: { type: 'any' },
|
|
})
|
|
})
|
|
|
|
it('claude: converts named tool_choice to { type: "tool", name }', () => {
|
|
const body = baseBody('claude-3-5-sonnet')
|
|
body.tool_choice = { type: 'function', function: { name: 'doStuff' } }
|
|
modelPatch(body)
|
|
expect(body.tool_choice).toEqual({ type: 'tool', name: 'doStuff' })
|
|
})
|
|
|
|
it('claude-opus-4-7: drops temperature', () => {
|
|
const body = baseBody('claude-opus-4-7')
|
|
modelPatch(body)
|
|
expect(body).not.toHaveProperty('temperature')
|
|
})
|
|
|
|
it('claude-opus-47 (alt id form): drops temperature', () => {
|
|
// Provider sometimes ships ids with the dot stripped; modelPatch normalizes.
|
|
const body = baseBody('claude-opus-47-20251029')
|
|
modelPatch(body)
|
|
expect(body).not.toHaveProperty('temperature')
|
|
})
|
|
|
|
it('grok: removes tool_choice and disables reasoning/thinking', () => {
|
|
const body = baseBody('grok-4')
|
|
modelPatch(body)
|
|
expect(body).not.toHaveProperty('tool_choice')
|
|
expect(body).toMatchObject({
|
|
thinking: { type: 'disabled', effort: 'minimal' },
|
|
reasoning: { enabled: false, effort: 'low' },
|
|
})
|
|
})
|
|
|
|
it('gpt-5: sets verbosity=low and reasoning_effort=low', () => {
|
|
const body = baseBody('gpt-5')
|
|
modelPatch(body)
|
|
expect(body).toMatchObject({ verbosity: 'low', reasoning_effort: 'low' })
|
|
})
|
|
|
|
it('gpt-5-mini: low effort, temperature=1', () => {
|
|
const body = baseBody('gpt-5-mini')
|
|
modelPatch(body)
|
|
expect(body).toMatchObject({
|
|
verbosity: 'low',
|
|
reasoning_effort: 'low',
|
|
temperature: 1,
|
|
})
|
|
})
|
|
|
|
it('gpt-5.1 (gpt-51): disables reasoning', () => {
|
|
const body = baseBody('gpt-5.1')
|
|
modelPatch(body)
|
|
expect(body).toMatchObject({ verbosity: 'low', reasoning_effort: 'none' })
|
|
})
|
|
|
|
it('gpt-5.4 (gpt-54): drops reasoning_effort', () => {
|
|
const body = baseBody('gpt-5.4')
|
|
modelPatch(body)
|
|
expect(body).toMatchObject({ verbosity: 'low' })
|
|
expect(body).not.toHaveProperty('reasoning_effort')
|
|
})
|
|
|
|
it('gpt-5.5 (gpt-55): drops reasoning_effort and temperature', () => {
|
|
const body = baseBody('gpt-5.5')
|
|
modelPatch(body)
|
|
expect(body).toMatchObject({ verbosity: 'low' })
|
|
expect(body).not.toHaveProperty('reasoning_effort')
|
|
expect(body).not.toHaveProperty('temperature')
|
|
})
|
|
|
|
it('gemini: sets reasoning_effort=minimal', () => {
|
|
const body = baseBody('gemini-2.5-pro')
|
|
modelPatch(body)
|
|
expect(body).toMatchObject({ reasoning_effort: 'minimal' })
|
|
})
|
|
|
|
it('deepseek: removes tool_choice', () => {
|
|
const body = baseBody('deepseek-chat')
|
|
modelPatch(body)
|
|
expect(body).not.toHaveProperty('tool_choice')
|
|
})
|
|
|
|
it('minimax: clamps temperature into (0, 1] and removes parallel_tool_calls', () => {
|
|
const body = baseBody('minimax-m2')
|
|
body.temperature = 0
|
|
modelPatch(body)
|
|
expect(body.temperature).toBeGreaterThan(0)
|
|
expect(body.temperature).toBeLessThanOrEqual(1)
|
|
expect(body).not.toHaveProperty('parallel_tool_calls')
|
|
})
|
|
|
|
it('minimax: caps temperature at 1', () => {
|
|
const body = baseBody('minimax-m2')
|
|
body.temperature = 2
|
|
modelPatch(body)
|
|
expect(body.temperature).toBe(1)
|
|
})
|
|
|
|
it('normalizes provider-prefixed model id (openai/gpt-5)', () => {
|
|
const body = baseBody('openai/gpt-5')
|
|
modelPatch(body)
|
|
expect(body).toMatchObject({ verbosity: 'low', reasoning_effort: 'low' })
|
|
})
|
|
})
|