Skip to content

Commit e006f0e

Browse files
committed
Update fireworks api to pass on reasoning effort, default medium
1 parent f644a79 commit e006f0e

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed

web/src/llm-api/__tests__/fireworks-deployment.test.ts

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,162 @@ describe('Fireworks deployment routing', () => {
379379
}
380380
})
381381

382+
it('transforms reasoning to reasoning_effort (defaults to medium)', async () => {
383+
const fetchedBodies: Record<string, unknown>[] = []
384+
385+
const mockFetch = mock(async (_url: string | URL | Request, init?: RequestInit) => {
386+
const body = JSON.parse(init?.body as string)
387+
fetchedBodies.push(body)
388+
return new Response(JSON.stringify({ ok: true }), { status: 200 })
389+
}) as unknown as typeof globalThis.fetch
390+
391+
await createFireworksRequestWithFallback({
392+
body: {
393+
...minimalBody,
394+
reasoning: { enabled: true },
395+
} as never,
396+
originalModel: 'z-ai/glm-5.1',
397+
fetch: mockFetch,
398+
logger,
399+
useCustomDeployment: false,
400+
sessionId: 'test-user-id',
401+
})
402+
403+
expect(fetchedBodies).toHaveLength(1)
404+
expect(fetchedBodies[0].reasoning_effort).toBe('medium')
405+
expect(fetchedBodies[0].reasoning).toBeUndefined()
406+
})
407+
408+
it('uses reasoning.effort value when specified', async () => {
409+
const fetchedBodies: Record<string, unknown>[] = []
410+
411+
const mockFetch = mock(async (_url: string | URL | Request, init?: RequestInit) => {
412+
const body = JSON.parse(init?.body as string)
413+
fetchedBodies.push(body)
414+
return new Response(JSON.stringify({ ok: true }), { status: 200 })
415+
}) as unknown as typeof globalThis.fetch
416+
417+
await createFireworksRequestWithFallback({
418+
body: {
419+
...minimalBody,
420+
reasoning: { effort: 'high' },
421+
} as never,
422+
originalModel: 'z-ai/glm-5.1',
423+
fetch: mockFetch,
424+
logger,
425+
useCustomDeployment: false,
426+
sessionId: 'test-user-id',
427+
})
428+
429+
expect(fetchedBodies).toHaveLength(1)
430+
expect(fetchedBodies[0].reasoning_effort).toBe('high')
431+
expect(fetchedBodies[0].reasoning).toBeUndefined()
432+
})
433+
434+
it('skips reasoning_effort when reasoning.enabled is false', async () => {
435+
const fetchedBodies: Record<string, unknown>[] = []
436+
437+
const mockFetch = mock(async (_url: string | URL | Request, init?: RequestInit) => {
438+
const body = JSON.parse(init?.body as string)
439+
fetchedBodies.push(body)
440+
return new Response(JSON.stringify({ ok: true }), { status: 200 })
441+
}) as unknown as typeof globalThis.fetch
442+
443+
await createFireworksRequestWithFallback({
444+
body: {
445+
...minimalBody,
446+
reasoning: { enabled: false, effort: 'high' },
447+
} as never,
448+
originalModel: 'z-ai/glm-5.1',
449+
fetch: mockFetch,
450+
logger,
451+
useCustomDeployment: false,
452+
sessionId: 'test-user-id',
453+
})
454+
455+
expect(fetchedBodies).toHaveLength(1)
456+
expect(fetchedBodies[0].reasoning_effort).toBeUndefined()
457+
expect(fetchedBodies[0].reasoning).toBeUndefined()
458+
})
459+
460+
it('preserves reasoning_effort when tools are present (Fireworks supports both)', async () => {
461+
const fetchedBodies: Record<string, unknown>[] = []
462+
463+
const mockFetch = mock(async (_url: string | URL | Request, init?: RequestInit) => {
464+
const body = JSON.parse(init?.body as string)
465+
fetchedBodies.push(body)
466+
return new Response(JSON.stringify({ ok: true }), { status: 200 })
467+
}) as unknown as typeof globalThis.fetch
468+
469+
await createFireworksRequestWithFallback({
470+
body: {
471+
...minimalBody,
472+
reasoning: { effort: 'high' },
473+
tools: [{ type: 'function', function: { name: 'test', arguments: '{}' } }],
474+
} as never,
475+
originalModel: 'z-ai/glm-5.1',
476+
fetch: mockFetch,
477+
logger,
478+
useCustomDeployment: false,
479+
sessionId: 'test-user-id',
480+
})
481+
482+
expect(fetchedBodies).toHaveLength(1)
483+
expect(fetchedBodies[0].reasoning_effort).toBe('high')
484+
expect(fetchedBodies[0].reasoning).toBeUndefined()
485+
})
486+
487+
it('passes through reasoning_effort when set directly without reasoning object', async () => {
488+
const fetchedBodies: Record<string, unknown>[] = []
489+
490+
const mockFetch = mock(async (_url: string | URL | Request, init?: RequestInit) => {
491+
const body = JSON.parse(init?.body as string)
492+
fetchedBodies.push(body)
493+
return new Response(JSON.stringify({ ok: true }), { status: 200 })
494+
}) as unknown as typeof globalThis.fetch
495+
496+
await createFireworksRequestWithFallback({
497+
body: {
498+
...minimalBody,
499+
reasoning_effort: 'low',
500+
} as never,
501+
originalModel: 'z-ai/glm-5.1',
502+
fetch: mockFetch,
503+
logger,
504+
useCustomDeployment: false,
505+
sessionId: 'test-user-id',
506+
})
507+
508+
expect(fetchedBodies).toHaveLength(1)
509+
expect(fetchedBodies[0].reasoning_effort).toBe('low')
510+
})
511+
512+
it('preserves directly-set reasoning_effort when tools are present', async () => {
513+
const fetchedBodies: Record<string, unknown>[] = []
514+
515+
const mockFetch = mock(async (_url: string | URL | Request, init?: RequestInit) => {
516+
const body = JSON.parse(init?.body as string)
517+
fetchedBodies.push(body)
518+
return new Response(JSON.stringify({ ok: true }), { status: 200 })
519+
}) as unknown as typeof globalThis.fetch
520+
521+
await createFireworksRequestWithFallback({
522+
body: {
523+
...minimalBody,
524+
reasoning_effort: 'low',
525+
tools: [{ type: 'function', function: { name: 'test', arguments: '{}' } }],
526+
} as never,
527+
originalModel: 'z-ai/glm-5.1',
528+
fetch: mockFetch,
529+
logger,
530+
useCustomDeployment: false,
531+
sessionId: 'test-user-id',
532+
})
533+
534+
expect(fetchedBodies).toHaveLength(1)
535+
expect(fetchedBodies[0].reasoning_effort).toBe('low')
536+
})
537+
382538
it('logs when trying deployment and when falling back on 5xx', async () => {
383539
const spy = spyDeploymentHours(true)
384540
let callCount = 0

web/src/llm-api/fireworks.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,20 @@ function createFireworksRequest(params: {
9797
model: modelIdOverride ?? getFireworksModelId(originalModel),
9898
}
9999

100+
// Transform OpenRouter-style `reasoning` object into Fireworks' `reasoning_effort`.
101+
// Unlike OpenAI, Fireworks supports reasoning_effort together with function tools
102+
// (e.g. GLM-4.5/5.1 and Kimi K2 are designed for interleaved reasoning + tool use).
103+
if (fireworksBody.reasoning && typeof fireworksBody.reasoning === 'object') {
104+
const reasoning = fireworksBody.reasoning as {
105+
enabled?: boolean
106+
effort?: 'high' | 'medium' | 'low'
107+
}
108+
if (reasoning.enabled ?? true) {
109+
fireworksBody.reasoning_effort = reasoning.effort ?? 'medium'
110+
}
111+
}
112+
delete fireworksBody.reasoning
113+
100114
// Strip OpenRouter-specific / internal fields
101115
delete fireworksBody.provider
102116
delete fireworksBody.transforms

0 commit comments

Comments
 (0)