From af10fdad5d4f0f8b61ca2a00636f09d4654043ef Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Sat, 29 Aug 2026 11:12:38 -0700 Subject: [PATCH 1/2] feat: bake xAI frontier model context limits HumanLayer-Session: https://app.dev.codelayer.gg/sessions/01a04e9f-cea4-7179-aa8b-38264baa9737 --- .../scripts/update-model-catalog.ts | 12 ++++--- .../fold-agent/src/Catalog/BakedCatalog.ts | 6 ++-- .../test/Catalog/BakedCatalog.vi.test.ts | 21 +++++++++++ packages/fold-xai/src/XaiModelCatalog.ts | 35 +++++++++++++++++++ packages/fold-xai/test/Xai.vi.test.ts | 20 +++++++++++ 5 files changed, 87 insertions(+), 7 deletions(-) diff --git a/packages/fold-agent/scripts/update-model-catalog.ts b/packages/fold-agent/scripts/update-model-catalog.ts index 6bbbe20..8ff9dcb 100644 --- a/packages/fold-agent/scripts/update-model-catalog.ts +++ b/packages/fold-agent/scripts/update-model-catalog.ts @@ -1,8 +1,8 @@ /** * 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 - * from `/tmp/models_dev_api.json` when present (avoid hammering models.dev while iterating), - * otherwise fetches `https://models.dev/api.json` fresh. + * `openai` providers, plus the static xAI frontier entries from `fold-xai`, all 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. * * Usage: `bun packages/fold-agent/scripts/update-model-catalog.ts` */ @@ -85,17 +85,19 @@ const entrySource = (entry: ModelCatalogEntry): string => const fileSource = (entries: ReadonlyArray): 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 and openai providers, plus static xAI frontier entries.', '// Regenerate: bun packages/fold-agent/scripts/update-model-catalog.ts', "import type { ModelCatalogEntry } from '@humanlayer/fold-core'", + "import { XAI_FRONTIER_MODEL_CATALOG } from '@humanlayer/fold-xai'", '', '/**', ' * 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.', + ' * at generation time, plus every xAI model Fold can launch.', ' */', 'export const bakedModelCatalog: ReadonlyArray = [', ...entries.map(entrySource), + '\t...XAI_FRONTIER_MODEL_CATALOG,', ']', '', ].join('\n') diff --git a/packages/fold-agent/src/Catalog/BakedCatalog.ts b/packages/fold-agent/src/Catalog/BakedCatalog.ts index 80e275b..36885c0 100644 --- a/packages/fold-agent/src/Catalog/BakedCatalog.ts +++ b/packages/fold-agent/src/Catalog/BakedCatalog.ts @@ -1,12 +1,13 @@ // 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 and openai providers, plus static xAI frontier entries. // Regenerate: bun packages/fold-agent/scripts/update-model-catalog.ts import type { ModelCatalogEntry } from '@humanlayer/fold-core' +import { XAI_FRONTIER_MODEL_CATALOG } from '@humanlayer/fold-xai' /** * 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. + * at generation time, plus every xAI model Fold can launch. */ export const bakedModelCatalog: ReadonlyArray = [ { @@ -1001,4 +1002,5 @@ export const bakedModelCatalog: ReadonlyArray = [ toolCall: false, pricing: { inputPerMTokens: 0.1, outputPerMTokens: 0, cacheReadPerMTokens: null, cacheWritePerMTokens: null }, }, + ...XAI_FRONTIER_MODEL_CATALOG, ] diff --git a/packages/fold-agent/test/Catalog/BakedCatalog.vi.test.ts b/packages/fold-agent/test/Catalog/BakedCatalog.vi.test.ts index 1f9427e..8ba8b9a 100644 --- a/packages/fold-agent/test/Catalog/BakedCatalog.vi.test.ts +++ b/packages/fold-agent/test/Catalog/BakedCatalog.vi.test.ts @@ -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) @@ -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') +}) diff --git a/packages/fold-xai/src/XaiModelCatalog.ts b/packages/fold-xai/src/XaiModelCatalog.ts index fe017b8..6ccbf1d 100644 --- a/packages/fold-xai/src/XaiModelCatalog.ts +++ b/packages/fold-xai/src/XaiModelCatalog.ts @@ -1,3 +1,4 @@ +import type { ModelCatalogEntry } from '@humanlayer/fold-core' import { Schema } from 'effect' /** Model IDs verified for xAI subscription-backed OAuth sessions. */ @@ -17,5 +18,39 @@ export const XAI_FRONTIER_MODELS = [ { modelId: 'grok-4.6', label: 'Grok 4.6' }, ] as const satisfies ReadonlyArray +/** + * Offline model metadata for the xAI OAuth models Fold can launch. This supplements the generated + * models.dev snapshot so embedding hosts can install the correct context limit without fetching a + * live catalog before a session starts. + */ +export const XAI_FRONTIER_MODEL_CATALOG = [ + { + providerId: 'xai', + modelId: 'grok-4.5', + name: 'Grok 4.5', + contextWindow: 500_000, + maxInputTokens: null, + maxOutputTokens: 500_000, + 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: 500_000, + maxInputTokens: null, + maxOutputTokens: 500_000, + reasoning: true, + reasoningEfforts: ['low', 'medium', 'high', 'xhigh'], + vision: true, + toolCall: true, + pricing: { inputPerMTokens: 2, outputPerMTokens: 6, cacheReadPerMTokens: 0.5, cacheWritePerMTokens: null }, + }, +] as const satisfies ReadonlyArray + /** The model selected when an embedding host does not provide one. */ export const DEFAULT_XAI_MODEL_ID: XaiFrontierModelId = 'grok-4.6' diff --git a/packages/fold-xai/test/Xai.vi.test.ts b/packages/fold-xai/test/Xai.vi.test.ts index 43874bc..730bfd5 100644 --- a/packages/fold-xai/test/Xai.vi.test.ts +++ b/packages/fold-xai/test/Xai.vi.test.ts @@ -9,6 +9,7 @@ import { DEFAULT_XAI_MODEL_ID, makeXaiAuthStore, normalizeXaiChatCompletionUsage, + XAI_FRONTIER_MODEL_CATALOG, XAI_FRONTIER_MODELS, XAI_BROWSER_REDIRECT_URI, XAI_CLIENT_ID, @@ -93,6 +94,25 @@ describe('xaiModel', () => { expect(Schema.is(XaiFrontierModelId)('grok-unverified')).toBe(false) }) + it('exports the verified context windows as model-catalog entries', () => { + expect(XAI_FRONTIER_MODEL_CATALOG).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + providerId: 'xai', + modelId: 'grok-4.5', + contextWindow: 500_000, + reasoningEfforts: ['low', 'medium', 'high'], + }), + expect.objectContaining({ + providerId: 'xai', + modelId: 'grok-4.6', + contextWindow: 500_000, + reasoningEfforts: ['low', 'medium', 'high', 'xhigh'], + }), + ]), + ) + }) + it('returns a FoldModel-compatible OpenAI snapshot', () => { const model = xaiModel() expect(model.activeModel).toMatchObject({ From 33867ca3da9b915455896aaff50712d87af3ac0e Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Sat, 29 Aug 2026 11:21:02 -0700 Subject: [PATCH 2/2] refactor: generate xAI baked catalog entries HumanLayer-Session: https://app.dev.codelayer.gg/sessions/01a04e9f-cea4-7179-aa8b-38264baa9737 --- .../scripts/update-model-catalog.ts | 20 +++++------ .../fold-agent/src/Catalog/BakedCatalog.ts | 34 +++++++++++++++--- packages/fold-xai/src/XaiModelCatalog.ts | 35 ------------------- packages/fold-xai/test/Xai.vi.test.ts | 20 ----------- 4 files changed, 38 insertions(+), 71 deletions(-) diff --git a/packages/fold-agent/scripts/update-model-catalog.ts b/packages/fold-agent/scripts/update-model-catalog.ts index 8ff9dcb..a19517a 100644 --- a/packages/fold-agent/scripts/update-model-catalog.ts +++ b/packages/fold-agent/scripts/update-model-catalog.ts @@ -1,8 +1,8 @@ /** - * Regenerates `src/Catalog/BakedCatalog.ts` - the baked models.dev snapshot for the `anthropic` and - * `openai` providers, plus the static xAI frontier entries from `fold-xai`, all 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. + * 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. * * Usage: `bun packages/fold-agent/scripts/update-model-catalog.ts` */ @@ -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 = new Set(['anthropic', 'openai']) +const BAKED_PROVIDERS: ReadonlySet = new Set(['anthropic', 'openai', 'xai']) const scriptDir = dirname(fileURLToPath(import.meta.url)) const targetPath = join(scriptDir, '..', 'src', 'Catalog', 'BakedCatalog.ts') @@ -85,19 +85,17 @@ const entrySource = (entry: ModelCatalogEntry): string => const fileSource = (entries: ReadonlyArray): string => [ '// GENERATED by scripts/update-model-catalog.ts - do not edit by hand.', - '// Baked models.dev snapshot for the anthropic and openai providers, plus static xAI frontier entries.', + '// 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'", - "import { XAI_FRONTIER_MODEL_CATALOG } from '@humanlayer/fold-xai'", '', '/**', ' * 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, plus every xAI model Fold can launch.', + ' * models.dev catalog is available (D15). Covers every anthropic, openai, and xai model models.dev', + ' * knew at generation time.', ' */', 'export const bakedModelCatalog: ReadonlyArray = [', ...entries.map(entrySource), - '\t...XAI_FRONTIER_MODEL_CATALOG,', ']', '', ].join('\n') @@ -114,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', }) } diff --git a/packages/fold-agent/src/Catalog/BakedCatalog.ts b/packages/fold-agent/src/Catalog/BakedCatalog.ts index 36885c0..9fb45d3 100644 --- a/packages/fold-agent/src/Catalog/BakedCatalog.ts +++ b/packages/fold-agent/src/Catalog/BakedCatalog.ts @@ -1,13 +1,12 @@ // GENERATED by scripts/update-model-catalog.ts - do not edit by hand. -// Baked models.dev snapshot for the anthropic and openai providers, plus static xAI frontier entries. +// 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' -import { XAI_FRONTIER_MODEL_CATALOG } from '@humanlayer/fold-xai' /** * 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, plus every xAI model Fold can launch. + * models.dev catalog is available (D15). Covers every anthropic, openai, and xai model models.dev + * knew at generation time. */ export const bakedModelCatalog: ReadonlyArray = [ { @@ -1002,5 +1001,30 @@ export const bakedModelCatalog: ReadonlyArray = [ toolCall: false, pricing: { inputPerMTokens: 0.1, outputPerMTokens: 0, cacheReadPerMTokens: null, cacheWritePerMTokens: null }, }, - ...XAI_FRONTIER_MODEL_CATALOG, + { + 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 }, + }, ] diff --git a/packages/fold-xai/src/XaiModelCatalog.ts b/packages/fold-xai/src/XaiModelCatalog.ts index 6ccbf1d..fe017b8 100644 --- a/packages/fold-xai/src/XaiModelCatalog.ts +++ b/packages/fold-xai/src/XaiModelCatalog.ts @@ -1,4 +1,3 @@ -import type { ModelCatalogEntry } from '@humanlayer/fold-core' import { Schema } from 'effect' /** Model IDs verified for xAI subscription-backed OAuth sessions. */ @@ -18,39 +17,5 @@ export const XAI_FRONTIER_MODELS = [ { modelId: 'grok-4.6', label: 'Grok 4.6' }, ] as const satisfies ReadonlyArray -/** - * Offline model metadata for the xAI OAuth models Fold can launch. This supplements the generated - * models.dev snapshot so embedding hosts can install the correct context limit without fetching a - * live catalog before a session starts. - */ -export const XAI_FRONTIER_MODEL_CATALOG = [ - { - providerId: 'xai', - modelId: 'grok-4.5', - name: 'Grok 4.5', - contextWindow: 500_000, - maxInputTokens: null, - maxOutputTokens: 500_000, - 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: 500_000, - maxInputTokens: null, - maxOutputTokens: 500_000, - reasoning: true, - reasoningEfforts: ['low', 'medium', 'high', 'xhigh'], - vision: true, - toolCall: true, - pricing: { inputPerMTokens: 2, outputPerMTokens: 6, cacheReadPerMTokens: 0.5, cacheWritePerMTokens: null }, - }, -] as const satisfies ReadonlyArray - /** The model selected when an embedding host does not provide one. */ export const DEFAULT_XAI_MODEL_ID: XaiFrontierModelId = 'grok-4.6' diff --git a/packages/fold-xai/test/Xai.vi.test.ts b/packages/fold-xai/test/Xai.vi.test.ts index 730bfd5..43874bc 100644 --- a/packages/fold-xai/test/Xai.vi.test.ts +++ b/packages/fold-xai/test/Xai.vi.test.ts @@ -9,7 +9,6 @@ import { DEFAULT_XAI_MODEL_ID, makeXaiAuthStore, normalizeXaiChatCompletionUsage, - XAI_FRONTIER_MODEL_CATALOG, XAI_FRONTIER_MODELS, XAI_BROWSER_REDIRECT_URI, XAI_CLIENT_ID, @@ -94,25 +93,6 @@ describe('xaiModel', () => { expect(Schema.is(XaiFrontierModelId)('grok-unverified')).toBe(false) }) - it('exports the verified context windows as model-catalog entries', () => { - expect(XAI_FRONTIER_MODEL_CATALOG).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - providerId: 'xai', - modelId: 'grok-4.5', - contextWindow: 500_000, - reasoningEfforts: ['low', 'medium', 'high'], - }), - expect.objectContaining({ - providerId: 'xai', - modelId: 'grok-4.6', - contextWindow: 500_000, - reasoningEfforts: ['low', 'medium', 'high', 'xhigh'], - }), - ]), - ) - }) - it('returns a FoldModel-compatible OpenAI snapshot', () => { const model = xaiModel() expect(model.activeModel).toMatchObject({