From 35c939e9277a32987b2a727ec677c3b7406deb31 Mon Sep 17 00:00:00 2001 From: waleed Date: Sat, 4 Apr 2026 11:03:58 -0700 Subject: [PATCH 1/7] improvement(models): tighten model metadata and crawl discovery Made-with: Cursor --- .../models/[provider]/[model]/page.tsx | 5 +- apps/sim/app/(landing)/models/utils.test.ts | 38 +++++++ apps/sim/app/(landing)/models/utils.ts | 31 ++++-- apps/sim/app/llms.txt/route.ts | 98 +++++++------------ apps/sim/app/sitemap.ts | 60 +++++++----- apps/sim/lib/core/config/feature-flags.ts | 2 +- apps/sim/providers/anthropic/core.ts | 6 +- apps/sim/providers/models.ts | 56 +++++++++-- apps/sim/providers/utils.test.ts | 53 ++++++++-- apps/sim/providers/utils.ts | 2 +- 10 files changed, 234 insertions(+), 117 deletions(-) create mode 100644 apps/sim/app/(landing)/models/utils.test.ts diff --git a/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx b/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx index fd7557e37c7..6af1aae5dcf 100644 --- a/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx +++ b/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx @@ -18,6 +18,7 @@ import { formatPrice, formatTokenCount, formatUpdatedAt, + getEffectiveMaxOutputTokens, getModelBySlug, getPricingBounds, getProviderBySlug, @@ -280,8 +281,8 @@ export default async function ModelPage({ label='Max output' value={ model.capabilities.maxOutputTokens - ? `${formatTokenCount(model.capabilities.maxOutputTokens)} tokens` - : 'Standard defaults' + ? `${formatTokenCount(getEffectiveMaxOutputTokens(model.capabilities))} tokens` + : 'Not published' } /> diff --git a/apps/sim/app/(landing)/models/utils.test.ts b/apps/sim/app/(landing)/models/utils.test.ts new file mode 100644 index 00000000000..69916cb998a --- /dev/null +++ b/apps/sim/app/(landing)/models/utils.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, it } from 'vitest' +import { buildModelCapabilityFacts, getEffectiveMaxOutputTokens, getModelBySlug } from './utils' + +describe('model catalog capability facts', () => { + it.concurrent( + 'shows structured outputs support and published max output tokens for gpt-4o', + () => { + const model = getModelBySlug('openai', 'gpt-4o') + + expect(model).not.toBeNull() + expect(model).toBeDefined() + + const capabilityFacts = buildModelCapabilityFacts(model!) + const structuredOutputs = capabilityFacts.find((fact) => fact.label === 'Structured outputs') + const maxOutputTokens = capabilityFacts.find((fact) => fact.label === 'Max output tokens') + + expect(getEffectiveMaxOutputTokens(model!.capabilities)).toBe(16384) + expect(structuredOutputs?.value).toBe('Supported') + expect(maxOutputTokens?.value).toBe('16k') + } + ) + + it.concurrent('preserves native structured outputs labeling for claude models', () => { + const model = getModelBySlug('anthropic', 'claude-sonnet-4-6') + + expect(model).not.toBeNull() + expect(model).toBeDefined() + + const capabilityFacts = buildModelCapabilityFacts(model!) + const structuredOutputs = capabilityFacts.find((fact) => fact.label === 'Structured outputs') + + expect(structuredOutputs?.value).toBe('Supported (native)') + }) + + it.concurrent('does not invent a max output token limit when one is not published', () => { + expect(getEffectiveMaxOutputTokens({})).toBeNull() + }) +}) diff --git a/apps/sim/app/(landing)/models/utils.ts b/apps/sim/app/(landing)/models/utils.ts index cdf79f87b7c..2e327a743f6 100644 --- a/apps/sim/app/(landing)/models/utils.ts +++ b/apps/sim/app/(landing)/models/utils.ts @@ -190,6 +190,16 @@ export function formatCapabilityBoolean( return value ? positive : negative } +function supportsCatalogStructuredOutputs(capabilities: ModelCapabilities): boolean { + // In the catalog, "structured outputs" means Sim can return typed JSON for the model. + // `nativeStructuredOutputs` is narrower and only indicates provider-native schema support. + return !capabilities.deepResearch +} + +export function getEffectiveMaxOutputTokens(capabilities: ModelCapabilities): number | null { + return capabilities.maxOutputTokens ?? null +} + function trimTrailingZeros(value: string): string { return value.replace(/\.0+$/, '').replace(/(\.\d*?)0+$/, '$1') } @@ -326,7 +336,7 @@ function buildCapabilityTags(capabilities: ModelCapabilities): string[] { tags.push('Tool choice') } - if (capabilities.nativeStructuredOutputs) { + if (supportsCatalogStructuredOutputs(capabilities)) { tags.push('Structured outputs') } @@ -384,7 +394,7 @@ function buildBestForLine(model: { return 'Best for long-context retrieval, large documents, and high-memory workflows.' } - if (capabilities.nativeStructuredOutputs) { + if (supportsCatalogStructuredOutputs(capabilities)) { return 'Best for production workflows that need reliable typed outputs.' } @@ -419,7 +429,7 @@ function computeModelRelevanceScore(model: CatalogModel): number { (model.capabilities.reasoningEffort ? 10 : 0) + (model.capabilities.thinking ? 10 : 0) + (model.capabilities.deepResearch ? 8 : 0) + - (model.capabilities.nativeStructuredOutputs ? 4 : 0) + + (supportsCatalogStructuredOutputs(model.capabilities) ? 4 : 0) + (model.contextWindow ?? 0) / 100000 ) } @@ -683,6 +693,7 @@ export function buildModelFaqs(provider: CatalogProvider, model: CatalogModel): export function buildModelCapabilityFacts(model: CatalogModel): CapabilityFact[] { const { capabilities } = model + const supportsStructuredOutputs = supportsCatalogStructuredOutputs(capabilities) return [ { @@ -711,7 +722,11 @@ export function buildModelCapabilityFacts(model: CatalogModel): CapabilityFact[] }, { label: 'Structured outputs', - value: formatCapabilityBoolean(capabilities.nativeStructuredOutputs), + value: supportsStructuredOutputs + ? capabilities.nativeStructuredOutputs + ? 'Supported (native)' + : 'Supported' + : 'Not supported', }, { label: 'Tool choice', @@ -732,8 +747,8 @@ export function buildModelCapabilityFacts(model: CatalogModel): CapabilityFact[] { label: 'Max output tokens', value: capabilities.maxOutputTokens - ? formatTokenCount(capabilities.maxOutputTokens) - : 'Standard defaults', + ? formatTokenCount(getEffectiveMaxOutputTokens(capabilities)) + : 'Not published', }, ] } @@ -752,8 +767,8 @@ export function getProviderCapabilitySummary(provider: CatalogProvider): Capabil const reasoningCount = provider.models.filter( (model) => model.capabilities.reasoningEffort || model.capabilities.thinking ).length - const structuredCount = provider.models.filter( - (model) => model.capabilities.nativeStructuredOutputs + const structuredCount = provider.models.filter((model) => + supportsCatalogStructuredOutputs(model.capabilities) ).length const deepResearchCount = provider.models.filter( (model) => model.capabilities.deepResearch diff --git a/apps/sim/app/llms.txt/route.ts b/apps/sim/app/llms.txt/route.ts index 79c79d086ec..971a036155f 100644 --- a/apps/sim/app/llms.txt/route.ts +++ b/apps/sim/app/llms.txt/route.ts @@ -1,71 +1,43 @@ import { getBaseUrl } from '@/lib/core/utils/urls' +import { ALL_CATALOG_MODELS, MODEL_PROVIDERS_WITH_CATALOGS } from '@/app/(landing)/models/utils' -export async function GET() { +export function GET() { const baseUrl = getBaseUrl() - const llmsContent = `# Sim - -> Sim is the open-source platform to build AI agents and run your agentic workforce. Connect 1,000+ integrations and LLMs to deploy and orchestrate agentic workflows. - -Sim lets teams create agents, workflows, knowledge bases, tables, and docs. Over 100,000 builders use Sim — from startups to Fortune 500 companies. SOC2 compliant. - -## Core Pages - -- [Homepage](${baseUrl}): Product overview, features, and pricing -- [Changelog](${baseUrl}/changelog): Product updates and release notes -- [Sim Blog](${baseUrl}/blog): Announcements, insights, and guides - -## Documentation - -- [Documentation](https://docs.sim.ai): Complete guides and API reference -- [Quickstart](https://docs.sim.ai/quickstart): Get started in 5 minutes -- [API Reference](https://docs.sim.ai/api): REST API documentation - -## Key Concepts - -- **Workspace**: Container for workflows, data sources, and executions -- **Workflow**: Directed graph of blocks defining an agentic process -- **Block**: Individual step (LLM call, tool call, HTTP request, code execution) -- **Trigger**: Event or schedule that initiates workflow execution -- **Execution**: A single run of a workflow with logs and outputs -- **Knowledge Base**: Vector-indexed document store for retrieval-augmented generation - -## Capabilities - -- AI agent creation and deployment -- Agentic workflow orchestration -- 1,000+ integrations (Slack, Gmail, Notion, Airtable, databases, and more) -- Multi-model LLM orchestration (OpenAI, Anthropic, Google, Mistral, xAI, Perplexity) -- Knowledge base creation with retrieval-augmented generation (RAG) -- Table creation and management -- Document creation and processing -- Scheduled and webhook-triggered executions - -## Use Cases - -- AI agent deployment and orchestration -- Knowledge bases and RAG pipelines -- Document creation and processing -- Customer support automation -- Internal operations (sales, marketing, legal, finance) - -## Links - -- [GitHub Repository](https://github.com/simstudioai/sim): Open-source codebase -- [Discord Community](https://discord.gg/Hr4UWYEcTT): Get help and connect with 100,000+ builders -- [X/Twitter](https://x.com/simdotai): Product updates and announcements - -## Optional - -- [Careers](https://jobs.ashbyhq.com/sim): Join the Sim team -- [Terms of Service](${baseUrl}/terms): Legal terms -- [Privacy Policy](${baseUrl}/privacy): Data handling practices -` - - return new Response(llmsContent, { + const content = [ + '# Sim', + '', + '> Sim is the open-source platform to build AI agents and run your agentic workforce.', + '', + '## Preferred URLs', + `- Main site: ${baseUrl}`, + `- Integrations directory: ${baseUrl}/integrations`, + `- Models directory: ${baseUrl}/models`, + `- Blog: ${baseUrl}/blog`, + `- Changelog: ${baseUrl}/changelog`, + '- Docs: https://docs.sim.ai', + '', + '## Public data surfaces', + `- Integration pages: ${baseUrl}/integrations`, + `- Provider pages: ${baseUrl}/models`, + `- Model pages: ${baseUrl}/models`, + `- Providers tracked: ${MODEL_PROVIDERS_WITH_CATALOGS.length}`, + `- Models tracked: ${ALL_CATALOG_MODELS.length}`, + '', + '## Crawl helpers', + `- Sitemap: ${baseUrl}/sitemap.xml`, + `- Robots: ${baseUrl}/robots.txt`, + '', + '## Notes', + '- Prefer canonical URLs on sim.ai when citing product, model, integration, and changelog content.', + '- Use the models directory for pricing, context window, and capability facts.', + '- Use the integrations directory for tool coverage and workflow automation capabilities.', + ].join('\n') + + return new Response(content, { headers: { - 'Content-Type': 'text/markdown; charset=utf-8', - 'Cache-Control': 'public, max-age=86400, s-maxage=86400', + 'Content-Type': 'text/plain; charset=utf-8', + 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400', }, }) } diff --git a/apps/sim/app/sitemap.ts b/apps/sim/app/sitemap.ts index 6c95b859370..a558525950e 100644 --- a/apps/sim/app/sitemap.ts +++ b/apps/sim/app/sitemap.ts @@ -8,6 +8,34 @@ export default async function sitemap(): Promise { const baseUrl = getBaseUrl() const now = new Date() + const integrationPages: MetadataRoute.Sitemap = integrations.map((integration) => ({ + url: `${baseUrl}/integrations/${integration.slug}`, + lastModified: now, + })) + const modelHubPages: MetadataRoute.Sitemap = [ + { + url: `${baseUrl}/integrations`, + lastModified: now, + }, + { + url: `${baseUrl}/models`, + lastModified: now, + }, + { + url: `${baseUrl}/partners`, + lastModified: now, + }, + ] + const providerPages: MetadataRoute.Sitemap = MODEL_PROVIDERS_WITH_CATALOGS.map((provider) => ({ + url: `${baseUrl}${provider.href}`, + lastModified: new Date( + Math.max(...provider.models.map((model) => new Date(model.pricing.updatedAt).getTime())) + ), + })) + const modelPages: MetadataRoute.Sitemap = ALL_CATALOG_MODELS.map((model) => ({ + url: `${baseUrl}${model.href}`, + lastModified: new Date(model.pricing.updatedAt), + })) const staticPages: MetadataRoute.Sitemap = [ { @@ -26,14 +54,6 @@ export default async function sitemap(): Promise { // url: `${baseUrl}/templates`, // lastModified: now, // }, - { - url: `${baseUrl}/integrations`, - lastModified: now, - }, - { - url: `${baseUrl}/models`, - lastModified: now, - }, { url: `${baseUrl}/changelog`, lastModified: now, @@ -54,20 +74,12 @@ export default async function sitemap(): Promise { lastModified: new Date(p.updated ?? p.date), })) - const integrationPages: MetadataRoute.Sitemap = integrations.map((i) => ({ - url: `${baseUrl}/integrations/${i.slug}`, - lastModified: now, - })) - - const providerPages: MetadataRoute.Sitemap = MODEL_PROVIDERS_WITH_CATALOGS.map((provider) => ({ - url: `${baseUrl}${provider.href}`, - lastModified: now, - })) - - const modelPages: MetadataRoute.Sitemap = ALL_CATALOG_MODELS.map((model) => ({ - url: `${baseUrl}${model.href}`, - lastModified: new Date(model.pricing.updatedAt), - })) - - return [...staticPages, ...blogPages, ...integrationPages, ...providerPages, ...modelPages] + return [ + ...staticPages, + ...modelHubPages, + ...integrationPages, + ...providerPages, + ...modelPages, + ...blogPages, + ] } diff --git a/apps/sim/lib/core/config/feature-flags.ts b/apps/sim/lib/core/config/feature-flags.ts index b688924afed..7c604e02a92 100644 --- a/apps/sim/lib/core/config/feature-flags.ts +++ b/apps/sim/lib/core/config/feature-flags.ts @@ -29,7 +29,7 @@ try { } catch { // invalid URL — isHosted stays false } -export const isHosted = appHostname === 'sim.ai' || appHostname.endsWith('.sim.ai') +export const isHosted = true /** * Is billing enforcement enabled diff --git a/apps/sim/providers/anthropic/core.ts b/apps/sim/providers/anthropic/core.ts index 8fe7785c41c..4716e2d8d1f 100644 --- a/apps/sim/providers/anthropic/core.ts +++ b/apps/sim/providers/anthropic/core.ts @@ -293,7 +293,9 @@ export async function executeAnthropicProviderRequest( messages, system: systemPrompt, max_tokens: - Number.parseInt(String(request.maxTokens)) || getMaxOutputTokensForModel(request.model), + Number.parseInt(String(request.maxTokens)) || + getMaxOutputTokensForModel(request.model) || + 4096, temperature: Number.parseFloat(String(request.temperature ?? 0.7)), } @@ -335,7 +337,7 @@ export async function executeAnthropicProviderRequest( const budgetTokens = thinkingConfig.thinking.budget_tokens const minMaxTokens = budgetTokens + 4096 if (payload.max_tokens < minMaxTokens) { - const modelMax = getMaxOutputTokensForModel(request.model) + const modelMax = getMaxOutputTokensForModel(request.model) ?? payload.max_tokens payload.max_tokens = Math.min(minMaxTokens, modelMax) logger.info( `Adjusted max_tokens to ${payload.max_tokens} to satisfy budget_tokens (${budgetTokens}) constraint` diff --git a/apps/sim/providers/models.ts b/apps/sim/providers/models.ts index 3465f223230..c819d27333f 100644 --- a/apps/sim/providers/models.ts +++ b/apps/sim/providers/models.ts @@ -271,6 +271,7 @@ export const PROVIDER_DEFINITIONS: Record = { verbosity: { values: ['low', 'medium', 'high'], }, + maxOutputTokens: 128000, }, contextWindow: 400000, }, @@ -290,6 +291,7 @@ export const PROVIDER_DEFINITIONS: Record = { verbosity: { values: ['low', 'medium', 'high'], }, + maxOutputTokens: 128000, }, contextWindow: 400000, }, @@ -324,6 +326,7 @@ export const PROVIDER_DEFINITIONS: Record = { verbosity: { values: ['low', 'medium', 'high'], }, + maxOutputTokens: 128000, }, contextWindow: 400000, }, @@ -342,6 +345,7 @@ export const PROVIDER_DEFINITIONS: Record = { verbosity: { values: ['low', 'medium', 'high'], }, + maxOutputTokens: 128000, }, contextWindow: 400000, }, @@ -360,6 +364,7 @@ export const PROVIDER_DEFINITIONS: Record = { verbosity: { values: ['low', 'medium', 'high'], }, + maxOutputTokens: 128000, }, contextWindow: 400000, }, @@ -373,6 +378,7 @@ export const PROVIDER_DEFINITIONS: Record = { }, capabilities: { temperature: { min: 0, max: 2 }, + maxOutputTokens: 16384, }, contextWindow: 128000, }, @@ -449,6 +455,7 @@ export const PROVIDER_DEFINITIONS: Record = { reasoningEffort: { values: ['low', 'medium', 'high'], }, + maxOutputTokens: 100000, }, contextWindow: 200000, }, @@ -463,6 +470,7 @@ export const PROVIDER_DEFINITIONS: Record = { }, capabilities: { temperature: { min: 0, max: 2 }, + maxOutputTokens: 16384, }, contextWindow: 128000, }, @@ -509,7 +517,7 @@ export const PROVIDER_DEFINITIONS: Record = { capabilities: { temperature: { min: 0, max: 1 }, nativeStructuredOutputs: true, - maxOutputTokens: 128000, + maxOutputTokens: 64000, thinking: { levels: ['low', 'medium', 'high', 'max'], default: 'high', @@ -741,6 +749,7 @@ export const PROVIDER_DEFINITIONS: Record = { verbosity: { values: ['low', 'medium', 'high'], }, + maxOutputTokens: 128000, }, contextWindow: 400000, }, @@ -759,6 +768,7 @@ export const PROVIDER_DEFINITIONS: Record = { verbosity: { values: ['low', 'medium', 'high'], }, + maxOutputTokens: 128000, }, contextWindow: 400000, }, @@ -777,6 +787,7 @@ export const PROVIDER_DEFINITIONS: Record = { verbosity: { values: ['low', 'medium', 'high'], }, + maxOutputTokens: 128000, }, contextWindow: 400000, }, @@ -795,6 +806,7 @@ export const PROVIDER_DEFINITIONS: Record = { verbosity: { values: ['low', 'medium', 'high'], }, + maxOutputTokens: 128000, }, contextWindow: 400000, }, @@ -813,6 +825,7 @@ export const PROVIDER_DEFINITIONS: Record = { verbosity: { values: ['low', 'medium', 'high'], }, + maxOutputTokens: 128000, }, contextWindow: 400000, }, @@ -831,6 +844,7 @@ export const PROVIDER_DEFINITIONS: Record = { verbosity: { values: ['low', 'medium', 'high'], }, + maxOutputTokens: 128000, }, contextWindow: 400000, }, @@ -1067,6 +1081,7 @@ export const PROVIDER_DEFINITIONS: Record = { levels: ['minimal', 'low', 'medium', 'high'], default: 'high', }, + maxOutputTokens: 65536, }, contextWindow: 1048576, }, @@ -1084,6 +1099,7 @@ export const PROVIDER_DEFINITIONS: Record = { levels: ['minimal', 'low', 'medium', 'high'], default: 'minimal', }, + maxOutputTokens: 65536, }, contextWindow: 1048576, }, @@ -1101,6 +1117,7 @@ export const PROVIDER_DEFINITIONS: Record = { levels: ['minimal', 'low', 'medium', 'high'], default: 'high', }, + maxOutputTokens: 65536, }, contextWindow: 1000000, }, @@ -1114,6 +1131,7 @@ export const PROVIDER_DEFINITIONS: Record = { }, capabilities: { temperature: { min: 0, max: 2 }, + maxOutputTokens: 65536, }, contextWindow: 1048576, }, @@ -1127,6 +1145,7 @@ export const PROVIDER_DEFINITIONS: Record = { }, capabilities: { temperature: { min: 0, max: 2 }, + maxOutputTokens: 65536, }, contextWindow: 1048576, }, @@ -1140,6 +1159,7 @@ export const PROVIDER_DEFINITIONS: Record = { }, capabilities: { temperature: { min: 0, max: 2 }, + maxOutputTokens: 65536, }, contextWindow: 1048576, }, @@ -1153,6 +1173,7 @@ export const PROVIDER_DEFINITIONS: Record = { }, capabilities: { temperature: { min: 0, max: 2 }, + maxOutputTokens: 8192, }, contextWindow: 1048576, }, @@ -1165,6 +1186,7 @@ export const PROVIDER_DEFINITIONS: Record = { }, capabilities: { temperature: { min: 0, max: 2 }, + maxOutputTokens: 8192, }, contextWindow: 1048576, }, @@ -1178,6 +1200,7 @@ export const PROVIDER_DEFINITIONS: Record = { capabilities: { deepResearch: true, memory: false, + maxOutputTokens: 65536, }, contextWindow: 1000000, }, @@ -2094,6 +2117,7 @@ export const PROVIDER_DEFINITIONS: Record = { capabilities: { temperature: { min: 0, max: 1 }, nativeStructuredOutputs: true, + maxOutputTokens: 64000, }, contextWindow: 200000, }, @@ -2107,6 +2131,7 @@ export const PROVIDER_DEFINITIONS: Record = { capabilities: { temperature: { min: 0, max: 1 }, nativeStructuredOutputs: true, + maxOutputTokens: 64000, }, contextWindow: 200000, }, @@ -2120,6 +2145,7 @@ export const PROVIDER_DEFINITIONS: Record = { capabilities: { temperature: { min: 0, max: 1 }, nativeStructuredOutputs: true, + maxOutputTokens: 64000, }, contextWindow: 200000, }, @@ -2133,6 +2159,7 @@ export const PROVIDER_DEFINITIONS: Record = { capabilities: { temperature: { min: 0, max: 1 }, nativeStructuredOutputs: true, + maxOutputTokens: 64000, }, contextWindow: 200000, }, @@ -2337,6 +2364,7 @@ export const PROVIDER_DEFINITIONS: Record = { }, capabilities: { temperature: { min: 0, max: 1 }, + maxOutputTokens: 32768, }, contextWindow: 128000, }, @@ -2373,6 +2401,7 @@ export const PROVIDER_DEFINITIONS: Record = { }, capabilities: { temperature: { min: 0, max: 1 }, + maxOutputTokens: 16384, }, contextWindow: 128000, }, @@ -2385,6 +2414,7 @@ export const PROVIDER_DEFINITIONS: Record = { }, capabilities: { temperature: { min: 0, max: 1 }, + maxOutputTokens: 40000, }, contextWindow: 128000, }, @@ -2397,6 +2427,7 @@ export const PROVIDER_DEFINITIONS: Record = { }, capabilities: { temperature: { min: 0, max: 1 }, + maxOutputTokens: 8192, }, contextWindow: 128000, }, @@ -2409,6 +2440,7 @@ export const PROVIDER_DEFINITIONS: Record = { }, capabilities: { temperature: { min: 0, max: 1 }, + maxOutputTokens: 8192, }, contextWindow: 128000, }, @@ -2421,6 +2453,7 @@ export const PROVIDER_DEFINITIONS: Record = { }, capabilities: { temperature: { min: 0, max: 1 }, + maxOutputTokens: 8192, }, contextWindow: 128000, }, @@ -2860,18 +2893,21 @@ export function getModelsWithoutMemory(): string[] { * * @param modelId - The model ID */ -export function getMaxOutputTokensForModel(modelId: string): number { +export function getMaxOutputTokensForModel(modelId: string): number | null { const normalizedModelId = modelId.toLowerCase() - const STANDARD_MAX_OUTPUT_TOKENS = 4096 + const allModels = Object.values(PROVIDER_DEFINITIONS).flatMap((provider) => provider.models) - for (const provider of Object.values(PROVIDER_DEFINITIONS)) { - for (const model of provider.models) { - const baseModelId = model.id.toLowerCase() - if (normalizedModelId === baseModelId || normalizedModelId.startsWith(`${baseModelId}-`)) { - return model.capabilities.maxOutputTokens || STANDARD_MAX_OUTPUT_TOKENS - } + const exactMatch = allModels.find((model) => model.id.toLowerCase() === normalizedModelId) + if (exactMatch) { + return exactMatch.capabilities.maxOutputTokens ?? null + } + + for (const model of allModels) { + const baseModelId = model.id.toLowerCase() + if (normalizedModelId.startsWith(`${baseModelId}-`)) { + return model.capabilities.maxOutputTokens ?? null } } - return STANDARD_MAX_OUTPUT_TOKENS + return null } diff --git a/apps/sim/providers/utils.test.ts b/apps/sim/providers/utils.test.ts index 9e1491d9aca..338fb621d64 100644 --- a/apps/sim/providers/utils.test.ts +++ b/apps/sim/providers/utils.test.ts @@ -664,6 +664,51 @@ describe('Model Capabilities', () => { describe('Max Output Tokens', () => { describe('getMaxOutputTokensForModel', () => { + it.concurrent('should return published max for OpenAI GPT-4o', () => { + expect(getMaxOutputTokensForModel('gpt-4o')).toBe(16384) + }) + + it.concurrent('should return published max for OpenAI GPT-5.1', () => { + expect(getMaxOutputTokensForModel('gpt-5.1')).toBe(128000) + }) + + it.concurrent('should return published max for OpenAI GPT-5 Chat', () => { + expect(getMaxOutputTokensForModel('gpt-5-chat-latest')).toBe(16384) + }) + + it.concurrent('should return published max for OpenAI o1', () => { + expect(getMaxOutputTokensForModel('o1')).toBe(100000) + }) + + it.concurrent('should return updated max for Claude Sonnet 4.6', () => { + expect(getMaxOutputTokensForModel('claude-sonnet-4-6')).toBe(64000) + }) + + it.concurrent('should return published max for Gemini 2.5 Pro', () => { + expect(getMaxOutputTokensForModel('gemini-2.5-pro')).toBe(65536) + }) + + it.concurrent('should return published max for Azure GPT-5.2', () => { + expect(getMaxOutputTokensForModel('azure/gpt-5.2')).toBe(128000) + }) + + it.concurrent( + 'should return null when DeepSeek output limit is not exact enough to publish', + () => { + expect(getMaxOutputTokensForModel('deepseek-reasoner')).toBeNull() + } + ) + + it.concurrent('should return null when xAI does not publish a max output limit', () => { + expect(getMaxOutputTokensForModel('grok-4-latest')).toBeNull() + }) + + it.concurrent('should return published max for Bedrock Claude Opus 4.1', () => { + expect(getMaxOutputTokensForModel('bedrock/anthropic.claude-opus-4-1-20250805-v1:0')).toBe( + 64000 + ) + }) + it.concurrent('should return correct max for Claude Opus 4.6', () => { expect(getMaxOutputTokensForModel('claude-opus-4-6')).toBe(128000) }) @@ -676,12 +721,8 @@ describe('Max Output Tokens', () => { expect(getMaxOutputTokensForModel('claude-opus-4-1')).toBe(32000) }) - it.concurrent('should return standard default for models without maxOutputTokens', () => { - expect(getMaxOutputTokensForModel('gpt-4o')).toBe(4096) - }) - - it.concurrent('should return standard default for unknown models', () => { - expect(getMaxOutputTokensForModel('unknown-model')).toBe(4096) + it.concurrent('should return null for unknown models', () => { + expect(getMaxOutputTokensForModel('unknown-model')).toBeNull() }) }) }) diff --git a/apps/sim/providers/utils.ts b/apps/sim/providers/utils.ts index 40d4a0ebdf7..e05537d6e86 100644 --- a/apps/sim/providers/utils.ts +++ b/apps/sim/providers/utils.ts @@ -1111,7 +1111,7 @@ export function getThinkingLevelsForModel(model: string): string[] | null { * * @param model - The model ID */ -export function getMaxOutputTokensForModel(model: string): number { +export function getMaxOutputTokensForModel(model: string): number | null { return getMaxOutputTokensForModelFromDefinitions(model) } From e935e29b01a915593bbf54ed7976bebe6143afbf Mon Sep 17 00:00:00 2001 From: waleed Date: Sat, 4 Apr 2026 11:07:36 -0700 Subject: [PATCH 2/7] revert hardcoded FF --- apps/sim/lib/core/config/feature-flags.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sim/lib/core/config/feature-flags.ts b/apps/sim/lib/core/config/feature-flags.ts index 7c604e02a92..b688924afed 100644 --- a/apps/sim/lib/core/config/feature-flags.ts +++ b/apps/sim/lib/core/config/feature-flags.ts @@ -29,7 +29,7 @@ try { } catch { // invalid URL — isHosted stays false } -export const isHosted = true +export const isHosted = appHostname === 'sim.ai' || appHostname.endsWith('.sim.ai') /** * Is billing enforcement enabled From ef14ab8eb4a339b4b7cf53c87cbec69a5fe2cf26 Mon Sep 17 00:00:00 2001 From: waleed Date: Sat, 4 Apr 2026 11:28:49 -0700 Subject: [PATCH 3/7] fix(models): narrow structured output ranking signal Made-with: Cursor --- apps/sim/app/(landing)/models/utils.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/sim/app/(landing)/models/utils.ts b/apps/sim/app/(landing)/models/utils.ts index 2e327a743f6..cf5355c6975 100644 --- a/apps/sim/app/(landing)/models/utils.ts +++ b/apps/sim/app/(landing)/models/utils.ts @@ -191,8 +191,6 @@ export function formatCapabilityBoolean( } function supportsCatalogStructuredOutputs(capabilities: ModelCapabilities): boolean { - // In the catalog, "structured outputs" means Sim can return typed JSON for the model. - // `nativeStructuredOutputs` is narrower and only indicates provider-native schema support. return !capabilities.deepResearch } @@ -394,7 +392,7 @@ function buildBestForLine(model: { return 'Best for long-context retrieval, large documents, and high-memory workflows.' } - if (supportsCatalogStructuredOutputs(capabilities)) { + if (capabilities.nativeStructuredOutputs) { return 'Best for production workflows that need reliable typed outputs.' } @@ -429,7 +427,7 @@ function computeModelRelevanceScore(model: CatalogModel): number { (model.capabilities.reasoningEffort ? 10 : 0) + (model.capabilities.thinking ? 10 : 0) + (model.capabilities.deepResearch ? 8 : 0) + - (supportsCatalogStructuredOutputs(model.capabilities) ? 4 : 0) + + (model.capabilities.nativeStructuredOutputs ? 4 : 0) + (model.contextWindow ?? 0) / 100000 ) } From 8d1e89b1abd6689f8c222b6dc60f4657e5f53660 Mon Sep 17 00:00:00 2001 From: waleed Date: Sat, 4 Apr 2026 11:29:51 -0700 Subject: [PATCH 4/7] fix(models): remove generic best-for copy Made-with: Cursor --- .../models/[provider]/[model]/page.tsx | 3 +- apps/sim/app/(landing)/models/utils.ts | 36 ------------------- 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx b/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx index 6af1aae5dcf..4b1dfd8ba4e 100644 --- a/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx +++ b/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx @@ -199,7 +199,7 @@ export default async function ModelPage({

- {model.summary} {model.bestFor} + {model.summary}

@@ -286,7 +286,6 @@ export default async function ModelPage({ } /> -
diff --git a/apps/sim/app/(landing)/models/utils.ts b/apps/sim/app/(landing)/models/utils.ts index cf5355c6975..1f5c1413f88 100644 --- a/apps/sim/app/(landing)/models/utils.ts +++ b/apps/sim/app/(landing)/models/utils.ts @@ -112,7 +112,6 @@ export interface CatalogModel { capabilities: ModelCapabilities capabilityTags: string[] summary: string - bestFor: string searchText: string } @@ -369,36 +368,6 @@ function buildCapabilityTags(capabilities: ModelCapabilities): string[] { return tags } -function buildBestForLine(model: { - pricing: PricingInfo - capabilities: ModelCapabilities - contextWindow: number | null -}): string { - const { pricing, capabilities, contextWindow } = model - - if (capabilities.deepResearch) { - return 'Best for multi-step research workflows and agent-led web investigation.' - } - - if (capabilities.reasoningEffort || capabilities.thinking) { - return 'Best for reasoning-heavy tasks that need more deliberate model control.' - } - - if (pricing.input <= 0.2 && pricing.output <= 1.25) { - return 'Best for cost-sensitive automations, background tasks, and high-volume workloads.' - } - - if (contextWindow && contextWindow >= 1000000) { - return 'Best for long-context retrieval, large documents, and high-memory workflows.' - } - - if (capabilities.nativeStructuredOutputs) { - return 'Best for production workflows that need reliable typed outputs.' - } - - return 'Best for general-purpose AI workflows inside Sim.' -} - function buildModelSummary( providerName: string, displayName: string, @@ -469,11 +438,6 @@ const rawProviders = Object.values(PROVIDER_DEFINITIONS).map((provider) => { model.contextWindow ?? null, capabilityTags ), - bestFor: buildBestForLine({ - pricing: model.pricing, - capabilities: mergedCapabilities, - contextWindow: model.contextWindow ?? null, - }), searchText: [ provider.name, providerDisplayName, From 8c286290318cec46e65067d628cee64564861a26 Mon Sep 17 00:00:00 2001 From: waleed Date: Sat, 4 Apr 2026 11:39:07 -0700 Subject: [PATCH 5/7] fix(models): restore best-for with stricter criteria Made-with: Cursor --- .../models/[provider]/[model]/page.tsx | 2 + apps/sim/app/(landing)/models/utils.test.ts | 11 ++++++ apps/sim/app/(landing)/models/utils.ts | 37 +++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx b/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx index 4b1dfd8ba4e..aeed6bbb929 100644 --- a/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx +++ b/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx @@ -200,6 +200,7 @@ export default async function ModelPage({

{model.summary} + {model.bestFor ? ` ${model.bestFor}` : ''}

@@ -286,6 +287,7 @@ export default async function ModelPage({ } /> + {model.bestFor ? : null}
diff --git a/apps/sim/app/(landing)/models/utils.test.ts b/apps/sim/app/(landing)/models/utils.test.ts index 69916cb998a..894c74500c9 100644 --- a/apps/sim/app/(landing)/models/utils.test.ts +++ b/apps/sim/app/(landing)/models/utils.test.ts @@ -35,4 +35,15 @@ describe('model catalog capability facts', () => { it.concurrent('does not invent a max output token limit when one is not published', () => { expect(getEffectiveMaxOutputTokens({})).toBeNull() }) + + it.concurrent('keeps best-for copy for clearly differentiated models only', () => { + const researchModel = getModelBySlug('google', 'deep-research-pro-preview-12-2025') + const generalModel = getModelBySlug('xai', 'grok-4-latest') + + expect(researchModel).not.toBeNull() + expect(generalModel).not.toBeNull() + + expect(researchModel?.bestFor).toContain('research workflows') + expect(generalModel?.bestFor).toBeUndefined() + }) }) diff --git a/apps/sim/app/(landing)/models/utils.ts b/apps/sim/app/(landing)/models/utils.ts index 1f5c1413f88..8e649c95c6b 100644 --- a/apps/sim/app/(landing)/models/utils.ts +++ b/apps/sim/app/(landing)/models/utils.ts @@ -112,6 +112,7 @@ export interface CatalogModel { capabilities: ModelCapabilities capabilityTags: string[] summary: string + bestFor?: string searchText: string } @@ -368,6 +369,36 @@ function buildCapabilityTags(capabilities: ModelCapabilities): string[] { return tags } +function buildBestForLine(model: { + pricing: PricingInfo + capabilities: ModelCapabilities + contextWindow: number | null +}): string | null { + const { pricing, capabilities, contextWindow } = model + + if (capabilities.deepResearch) { + return 'Best for multi-step research workflows and agent-led web investigation.' + } + + if (capabilities.reasoningEffort || capabilities.thinking) { + return 'Best for reasoning-heavy tasks that need more deliberate model control.' + } + + if (contextWindow && contextWindow >= 1000000) { + return 'Best for long-context retrieval, large documents, and high-memory workflows.' + } + + if (capabilities.nativeStructuredOutputs) { + return 'Best for production workflows that need reliable typed outputs.' + } + + if (pricing.input <= 0.2 && pricing.output <= 1.25) { + return 'Best for cost-sensitive automations, background tasks, and high-volume workloads.' + } + + return null +} + function buildModelSummary( providerName: string, displayName: string, @@ -414,6 +445,11 @@ const rawProviders = Object.values(PROVIDER_DEFINITIONS).map((provider) => { const shortId = stripProviderPrefix(provider.id, model.id) const mergedCapabilities = { ...provider.capabilities, ...model.capabilities } const capabilityTags = buildCapabilityTags(mergedCapabilities) + const bestFor = buildBestForLine({ + pricing: model.pricing, + capabilities: mergedCapabilities, + contextWindow: model.contextWindow ?? null, + }) const displayName = formatModelDisplayName(provider.id, model.id) const modelSlug = slugify(shortId) const href = `/models/${providerSlug}/${modelSlug}` @@ -438,6 +474,7 @@ const rawProviders = Object.values(PROVIDER_DEFINITIONS).map((provider) => { model.contextWindow ?? null, capabilityTags ), + ...(bestFor ? { bestFor } : {}), searchText: [ provider.name, providerDisplayName, From ca3374b7aa68f5e27b9a649c5ecb85074e33cdf0 Mon Sep 17 00:00:00 2001 From: waleed Date: Sat, 4 Apr 2026 11:44:05 -0700 Subject: [PATCH 6/7] fix --- .../models/[provider]/[model]/page.tsx | 2 - apps/sim/app/llms.txt/route.ts | 85 ++++++++++++------- apps/sim/providers/anthropic/core.ts | 6 +- apps/sim/providers/models.ts | 9 +- apps/sim/providers/utils.test.ts | 16 ++-- apps/sim/providers/utils.ts | 2 +- 6 files changed, 68 insertions(+), 52 deletions(-) diff --git a/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx b/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx index aeed6bbb929..c539d739daf 100644 --- a/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx +++ b/apps/sim/app/(landing)/models/[provider]/[model]/page.tsx @@ -231,13 +231,11 @@ export default async function ModelPage({ ? `${formatPrice(model.pricing.cachedInput)}/1M` : 'N/A' } - compact /> diff --git a/apps/sim/app/llms.txt/route.ts b/apps/sim/app/llms.txt/route.ts index 971a036155f..ddb0f6c7821 100644 --- a/apps/sim/app/llms.txt/route.ts +++ b/apps/sim/app/llms.txt/route.ts @@ -1,38 +1,63 @@ import { getBaseUrl } from '@/lib/core/utils/urls' -import { ALL_CATALOG_MODELS, MODEL_PROVIDERS_WITH_CATALOGS } from '@/app/(landing)/models/utils' export function GET() { const baseUrl = getBaseUrl() - const content = [ - '# Sim', - '', - '> Sim is the open-source platform to build AI agents and run your agentic workforce.', - '', - '## Preferred URLs', - `- Main site: ${baseUrl}`, - `- Integrations directory: ${baseUrl}/integrations`, - `- Models directory: ${baseUrl}/models`, - `- Blog: ${baseUrl}/blog`, - `- Changelog: ${baseUrl}/changelog`, - '- Docs: https://docs.sim.ai', - '', - '## Public data surfaces', - `- Integration pages: ${baseUrl}/integrations`, - `- Provider pages: ${baseUrl}/models`, - `- Model pages: ${baseUrl}/models`, - `- Providers tracked: ${MODEL_PROVIDERS_WITH_CATALOGS.length}`, - `- Models tracked: ${ALL_CATALOG_MODELS.length}`, - '', - '## Crawl helpers', - `- Sitemap: ${baseUrl}/sitemap.xml`, - `- Robots: ${baseUrl}/robots.txt`, - '', - '## Notes', - '- Prefer canonical URLs on sim.ai when citing product, model, integration, and changelog content.', - '- Use the models directory for pricing, context window, and capability facts.', - '- Use the integrations directory for tool coverage and workflow automation capabilities.', - ].join('\n') + const content = `# Sim + +> Sim is the open-source platform to build AI agents and run your agentic workforce. Connect integrations and LLMs to deploy and orchestrate agentic workflows. + +Sim lets teams create agents, workflows, knowledge bases, tables, and docs. It supports both product discovery pages and deeper technical documentation. + +## Preferred URLs + +- [Homepage](${baseUrl}): Product overview and primary entry point +- [Integrations directory](${baseUrl}/integrations): Public catalog of integrations and automation capabilities +- [Models directory](${baseUrl}/models): Public catalog of AI models, pricing, context windows, and capabilities +- [Blog](${baseUrl}/blog): Announcements, guides, and product context +- [Changelog](${baseUrl}/changelog): Product updates and release notes + +## Documentation + +- [Documentation](https://docs.sim.ai): Product guides and technical reference +- [Quickstart](https://docs.sim.ai/quickstart): Fastest path to getting started +- [API Reference](https://docs.sim.ai/api): API documentation + +## Key Concepts + +- **Workspace**: Container for workflows, data sources, and executions +- **Workflow**: Directed graph of blocks defining an agentic process +- **Block**: Individual step such as an LLM call, tool call, HTTP request, or code execution +- **Trigger**: Event or schedule that initiates workflow execution +- **Execution**: A single run of a workflow with logs and outputs +- **Knowledge Base**: Document store used for retrieval-augmented generation + +## Capabilities + +- AI agent creation and deployment +- Agentic workflow orchestration +- Integrations across business tools, databases, and communication platforms +- Multi-model LLM orchestration +- Knowledge bases and retrieval-augmented generation +- Table creation and management +- Document creation and processing +- Scheduled and webhook-triggered executions + +## Use Cases + +- AI agent deployment and orchestration +- Knowledge bases and RAG pipelines +- Customer support automation +- Internal operations workflows across sales, marketing, legal, and finance + +## Additional Links + +- [GitHub Repository](https://github.com/simstudioai/sim): Open-source codebase +- [Docs](https://docs.sim.ai): Canonical documentation source +- [Terms of Service](${baseUrl}/terms): Legal terms +- [Privacy Policy](${baseUrl}/privacy): Data handling practices +- [Sitemap](${baseUrl}/sitemap.xml): Public URL inventory +` return new Response(content, { headers: { diff --git a/apps/sim/providers/anthropic/core.ts b/apps/sim/providers/anthropic/core.ts index 4716e2d8d1f..8fe7785c41c 100644 --- a/apps/sim/providers/anthropic/core.ts +++ b/apps/sim/providers/anthropic/core.ts @@ -293,9 +293,7 @@ export async function executeAnthropicProviderRequest( messages, system: systemPrompt, max_tokens: - Number.parseInt(String(request.maxTokens)) || - getMaxOutputTokensForModel(request.model) || - 4096, + Number.parseInt(String(request.maxTokens)) || getMaxOutputTokensForModel(request.model), temperature: Number.parseFloat(String(request.temperature ?? 0.7)), } @@ -337,7 +335,7 @@ export async function executeAnthropicProviderRequest( const budgetTokens = thinkingConfig.thinking.budget_tokens const minMaxTokens = budgetTokens + 4096 if (payload.max_tokens < minMaxTokens) { - const modelMax = getMaxOutputTokensForModel(request.model) ?? payload.max_tokens + const modelMax = getMaxOutputTokensForModel(request.model) payload.max_tokens = Math.min(minMaxTokens, modelMax) logger.info( `Adjusted max_tokens to ${payload.max_tokens} to satisfy budget_tokens (${budgetTokens}) constraint` diff --git a/apps/sim/providers/models.ts b/apps/sim/providers/models.ts index c819d27333f..5d293dd6047 100644 --- a/apps/sim/providers/models.ts +++ b/apps/sim/providers/models.ts @@ -2893,21 +2893,22 @@ export function getModelsWithoutMemory(): string[] { * * @param modelId - The model ID */ -export function getMaxOutputTokensForModel(modelId: string): number | null { +export function getMaxOutputTokensForModel(modelId: string): number { const normalizedModelId = modelId.toLowerCase() + const STANDARD_MAX_OUTPUT_TOKENS = 4096 const allModels = Object.values(PROVIDER_DEFINITIONS).flatMap((provider) => provider.models) const exactMatch = allModels.find((model) => model.id.toLowerCase() === normalizedModelId) if (exactMatch) { - return exactMatch.capabilities.maxOutputTokens ?? null + return exactMatch.capabilities.maxOutputTokens || STANDARD_MAX_OUTPUT_TOKENS } for (const model of allModels) { const baseModelId = model.id.toLowerCase() if (normalizedModelId.startsWith(`${baseModelId}-`)) { - return model.capabilities.maxOutputTokens ?? null + return model.capabilities.maxOutputTokens || STANDARD_MAX_OUTPUT_TOKENS } } - return null + return STANDARD_MAX_OUTPUT_TOKENS } diff --git a/apps/sim/providers/utils.test.ts b/apps/sim/providers/utils.test.ts index 338fb621d64..0b46003ca4a 100644 --- a/apps/sim/providers/utils.test.ts +++ b/apps/sim/providers/utils.test.ts @@ -692,15 +692,9 @@ describe('Max Output Tokens', () => { expect(getMaxOutputTokensForModel('azure/gpt-5.2')).toBe(128000) }) - it.concurrent( - 'should return null when DeepSeek output limit is not exact enough to publish', - () => { - expect(getMaxOutputTokensForModel('deepseek-reasoner')).toBeNull() - } - ) - - it.concurrent('should return null when xAI does not publish a max output limit', () => { - expect(getMaxOutputTokensForModel('grok-4-latest')).toBeNull() + it.concurrent('should return standard default for models without maxOutputTokens', () => { + expect(getMaxOutputTokensForModel('deepseek-reasoner')).toBe(4096) + expect(getMaxOutputTokensForModel('grok-4-latest')).toBe(4096) }) it.concurrent('should return published max for Bedrock Claude Opus 4.1', () => { @@ -721,8 +715,8 @@ describe('Max Output Tokens', () => { expect(getMaxOutputTokensForModel('claude-opus-4-1')).toBe(32000) }) - it.concurrent('should return null for unknown models', () => { - expect(getMaxOutputTokensForModel('unknown-model')).toBeNull() + it.concurrent('should return standard default for unknown models', () => { + expect(getMaxOutputTokensForModel('unknown-model')).toBe(4096) }) }) }) diff --git a/apps/sim/providers/utils.ts b/apps/sim/providers/utils.ts index e05537d6e86..40d4a0ebdf7 100644 --- a/apps/sim/providers/utils.ts +++ b/apps/sim/providers/utils.ts @@ -1111,7 +1111,7 @@ export function getThinkingLevelsForModel(model: string): string[] | null { * * @param model - The model ID */ -export function getMaxOutputTokensForModel(model: string): number | null { +export function getMaxOutputTokensForModel(model: string): number { return getMaxOutputTokensForModelFromDefinitions(model) } From 569031cfa0c758aeaa6574f6bf07c10a0f02a686 Mon Sep 17 00:00:00 2001 From: waleed Date: Sat, 4 Apr 2026 11:45:40 -0700 Subject: [PATCH 7/7] models --- apps/sim/app/llms.txt/route.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/sim/app/llms.txt/route.ts b/apps/sim/app/llms.txt/route.ts index ddb0f6c7821..89fbc5a67f4 100644 --- a/apps/sim/app/llms.txt/route.ts +++ b/apps/sim/app/llms.txt/route.ts @@ -61,8 +61,8 @@ Sim lets teams create agents, workflows, knowledge bases, tables, and docs. It s return new Response(content, { headers: { - 'Content-Type': 'text/plain; charset=utf-8', - 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400', + 'Content-Type': 'text/markdown; charset=utf-8', + 'Cache-Control': 'public, max-age=86400, s-maxage=86400', }, }) }