feat: opus 5.5 (#3930)

* feat: opus 5.5

* fix: bad typing
This commit is contained in:
Daniel Salazar
2026-09-22 12:31:44 -07:00
committed by GitHub
parent 6debd7df76
commit 300fb115a1
4 changed files with 73 additions and 10 deletions
@@ -829,6 +829,29 @@ describe('ClaudeProvider.complete request shape', () => {
expect('temperature' in args).toBe(false);
});
it('forwards reasoning_effort as adaptive thinking + output_config effort on opus 5.5', async () => {
const { provider } = makeProvider();
messagesCreateMock.mockResolvedValueOnce(baseResponse);
await withTestActor(() =>
provider.complete({
model: 'claude-opus-5-5',
messages: [{ role: 'user', content: 'hi' }],
reasoning_effort: 'low',
temperature: 0.5,
} as never),
);
const [args] = messagesCreateMock.mock.calls[0]!;
// Opus 5.5 rejects disabled thinking, `budget_tokens`, and sampling params.
expect(args.thinking).toEqual({
type: 'adaptive',
display: 'summarized',
});
expect(args.output_config).toEqual({ effort: 'low' });
expect('temperature' in args).toBe(false);
});
it('builds an enabled thinking budget from reasoning_effort on older Sonnet models', async () => {
const { provider } = makeProvider();
messagesCreateMock.mockResolvedValueOnce(baseResponse);
@@ -898,6 +921,22 @@ describe('ClaudeProvider model resolution', () => {
);
});
it('routes the bare claude-opus alias to opus 5.5 rather than opus 5', async () => {
const { provider } = makeProvider();
messagesCreateMock.mockResolvedValueOnce(baseResponse);
await withTestActor(() =>
provider.complete({
model: 'claude-opus',
messages: [{ role: 'user', content: 'hi' }],
}),
);
expect(messagesCreateMock.mock.calls[0]![0].model).toBe(
'claude-opus-5-5',
);
});
it('falls back to the default model when given an unknown id', async () => {
const { provider } = makeProvider();
messagesCreateMock.mockResolvedValueOnce(baseResponse);
@@ -367,6 +367,7 @@ export class ClaudeProvider implements IChatProvider {
'claude-opus-4-7',
'claude-opus-4-8',
'claude-opus-5',
'claude-opus-5-5',
].includes(modelUsed.id);
const resolvedTemperature = omitsTemperature
? undefined
@@ -377,6 +378,7 @@ export class ClaudeProvider implements IChatProvider {
'claude-fable-5-1',
'claude-fable-5',
'claude-sonnet-5',
'claude-opus-5-5',
'claude-opus-5',
'claude-opus-4-8',
'claude-opus-4-7',
@@ -812,6 +814,7 @@ export class ClaudeProvider implements IChatProvider {
if (
modelId === 'claude-fable-5-1' ||
modelId === 'claude-fable-5' ||
modelId === 'claude-opus-5-5' ||
modelId === 'claude-opus-5' ||
modelId === 'claude-opus-4-8' ||
modelId === 'claude-opus-4-7'
@@ -102,6 +102,36 @@ export const CLAUDE_MODELS: IChatModel[] = [
context: 1000000,
max_tokens: 64000,
},
{
puterId: 'anthropic:anthropic/claude-opus-5-5',
id: 'claude-opus-5-5',
modalities: { input: ['text', 'image', 'pdf'], output: ['text'] },
open_weights: false,
tool_call: true,
knowledge: '2026-06',
release_date: '2026-09-22',
aliases: [
'claude-opus',
'claude-opus-latest',
'claude-opus-5-5-latest',
'claude-opus-5.5',
'anthropic/claude-opus-5-5',
],
name: 'Claude Opus 5.5',
costs_currency: 'usd-cents',
input_cost_key: 'input_tokens',
output_cost_key: 'output_tokens',
costs: {
tokens: 1_000_000,
input_tokens: 400,
ephemeral_5m_input_tokens: 400 * 1.25,
ephemeral_1h_input_tokens: 400 * 2,
cache_read_input_tokens: 400 * 0.1,
output_tokens: 2000,
},
context: 1000000,
max_tokens: 128000,
},
{
puterId: 'anthropic:anthropic/claude-opus-5',
id: 'claude-opus-5',
@@ -110,12 +140,7 @@ export const CLAUDE_MODELS: IChatModel[] = [
tool_call: true,
knowledge: '2026-05',
release_date: '2026-07-24',
aliases: [
'claude-opus',
'claude-opus-latest',
'claude-opus-5-latest',
'anthropic/claude-opus-5',
],
aliases: ['claude-opus-5-latest', 'anthropic/claude-opus-5'],
name: 'Claude Opus 5',
costs_currency: 'usd-cents',
input_cost_key: 'input_tokens',
@@ -21,9 +21,7 @@ import { Readable } from 'node:stream';
import { makeActor } from '../../core/actor.js';
import { HttpError } from '../../core/http/HttpError.js';
import type { UserRow } from '../../stores/user/UserStore.js';
import type { LayerInstances } from '../../types';
import { isUniqueViolation } from '../../util/dbError.js';
import type { puterServices } from '../index';
import { actorHasSubscription } from '../metering/enforcement.js';
import { PuterService } from '../types.js';
@@ -123,8 +121,6 @@ const validateField = (field: ProfileField, value: unknown): string | null => {
* hosting middleware asks before streaming any file of that site.
*/
export class ProfileService extends PuterService {
declare protected services: LayerInstances<typeof puterServices>;
#ownerUserId: number | null = null;
#dirReady: Promise<void> | null = null;