Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions packages/fold-agent/scripts/update-model-catalog.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* Regenerates `src/Catalog/BakedCatalog.ts` - the baked models.dev snapshot for the `anthropic` and
* `openai` providers, already normalized to core `ModelCatalogEntry` rows. Reads a local snapshot
* Regenerates `src/Catalog/BakedCatalog.ts` - the baked models.dev snapshot for the `anthropic`,
* `openai`, and `xai` providers, normalized to core `ModelCatalogEntry` rows. Reads a local snapshot
* from `/tmp/models_dev_api.json` when present (avoid hammering models.dev while iterating),
* otherwise fetches `https://models.dev/api.json` fresh.
*
Expand All @@ -19,7 +19,7 @@ import { modelCatalogEntriesFromModelsDev } from '../src/Catalog/Normalize'

const SNAPSHOT_PATH = '/tmp/models_dev_api.json'
const MODELS_DEV_URL = 'https://models.dev/api.json'
const BAKED_PROVIDERS: ReadonlySet<string> = new Set(['anthropic', 'openai'])
const BAKED_PROVIDERS: ReadonlySet<string> = new Set(['anthropic', 'openai', 'xai'])

const scriptDir = dirname(fileURLToPath(import.meta.url))
const targetPath = join(scriptDir, '..', 'src', 'Catalog', 'BakedCatalog.ts')
Expand Down Expand Up @@ -85,14 +85,14 @@ const entrySource = (entry: ModelCatalogEntry): string =>
const fileSource = (entries: ReadonlyArray<ModelCatalogEntry>): string =>
[
'// GENERATED by scripts/update-model-catalog.ts - do not edit by hand.',
'// Baked models.dev snapshot for the anthropic and openai providers, normalized to ModelCatalogEntry.',
'// Baked models.dev snapshot for the anthropic, openai, and xai providers, normalized to ModelCatalogEntry.',
'// Regenerate: bun packages/fold-agent/scripts/update-model-catalog.ts',
"import type { ModelCatalogEntry } from '@humanlayer/fold-core'",
'',
'/**',
' * The offline model catalog: the last-resort fallback when neither a cached nor a freshly fetched',
' * models.dev catalog is available (D15). Covers every anthropic and openai model models.dev knew',
' * at generation time.',
' * models.dev catalog is available (D15). Covers every anthropic, openai, and xai model models.dev',
' * knew at generation time.',
' */',
'export const bakedModelCatalog: ReadonlyArray<ModelCatalogEntry> = [',
...entries.map(entrySource),
Expand All @@ -112,7 +112,7 @@ const main = Effect.gen(function* () {

if (entries.length === 0) {
return yield* new CatalogBakeError({
message: 'no anthropic/openai entries survived normalization; refusing to bake',
message: 'no anthropic/openai/xai entries survived normalization; refusing to bake',
})
}

Expand Down
32 changes: 29 additions & 3 deletions packages/fold-agent/src/Catalog/BakedCatalog.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// GENERATED by scripts/update-model-catalog.ts - do not edit by hand.
// Baked models.dev snapshot for the anthropic and openai providers, normalized to ModelCatalogEntry.
// Baked models.dev snapshot for the anthropic, openai, and xai providers, normalized to ModelCatalogEntry.
// Regenerate: bun packages/fold-agent/scripts/update-model-catalog.ts
import type { ModelCatalogEntry } from '@humanlayer/fold-core'

/**
* The offline model catalog: the last-resort fallback when neither a cached nor a freshly fetched
* models.dev catalog is available (D15). Covers every anthropic and openai model models.dev knew
* at generation time.
* models.dev catalog is available (D15). Covers every anthropic, openai, and xai model models.dev
* knew at generation time.
*/
export const bakedModelCatalog: ReadonlyArray<ModelCatalogEntry> = [
{
Expand Down Expand Up @@ -1001,4 +1001,30 @@ export const bakedModelCatalog: ReadonlyArray<ModelCatalogEntry> = [
toolCall: false,
pricing: { inputPerMTokens: 0.1, outputPerMTokens: 0, cacheReadPerMTokens: null, cacheWritePerMTokens: null },
},
{
providerId: 'xai',
modelId: 'grok-4.5',
name: 'Grok 4.5',
contextWindow: 500000,
maxInputTokens: null,
maxOutputTokens: 500000,
reasoning: true,
reasoningEfforts: ['low', 'medium', 'high'],
vision: true,
toolCall: true,
pricing: { inputPerMTokens: 2, outputPerMTokens: 6, cacheReadPerMTokens: 0.3, cacheWritePerMTokens: null },
},
{
providerId: 'xai',
modelId: 'grok-4.6',
name: 'Grok 4.6',
contextWindow: 500000,
maxInputTokens: null,
maxOutputTokens: 500000,
reasoning: true,
reasoningEfforts: ['low', 'medium', 'high', 'xhigh'],
vision: true,
toolCall: true,
pricing: { inputPerMTokens: 2, outputPerMTokens: 6, cacheReadPerMTokens: 0.5, cacheWritePerMTokens: null },
},
]
21 changes: 21 additions & 0 deletions packages/fold-agent/test/Catalog/BakedCatalog.vi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ const openAiTerra: ActiveModel = {
reasoning: { _tag: 'effort', effort: 'max' },
}

const xaiGrok: ActiveModel = {
providerId: 'xai',
providerKind: 'openai-compatible',
modelId: 'grok-4.6',
role: null,
requestedReasoningLevel: 'xhigh',
reasoning: { _tag: 'effort', effort: 'xhigh' },
}

it('resolves a codex-kind gpt-5.6-sol to the baked openai entry', () => {
const entry = lookupCatalogEntry(bakedModelCatalog, codexSol)

Expand All @@ -51,3 +60,15 @@ it('resolves an openai-compatible gpt-5.6-terra to the baked openai entry', () =
expect(entry?.pricing?.outputPerMTokens).toBe(15)
expect(entry?.reasoningEfforts).toContain('max')
})

it('resolves the supported Grok model to the baked xAI entry', () => {
const entry = lookupCatalogEntry(bakedModelCatalog, xaiGrok)

expect(entry).not.toBeNull()
expect(entry?.providerId).toBe('xai')
expect(entry?.modelId).toBe('grok-4.6')
expect(entry?.contextWindow).toBe(500_000)
expect(entry?.pricing?.inputPerMTokens).toBe(2)
expect(entry?.pricing?.outputPerMTokens).toBe(6)
expect(entry?.reasoningEfforts).toContain('xhigh')
})
Loading