Skip to content

Commit 7a9ba8b

Browse files
waleedlatif1claude
andcommitted
refactor(knowledge): lock embedding model to KB_EMBEDDING_MODEL env var
Remove the user-facing model picker from the KB create modal and the embeddingModel field from the create/update API schemas. The active model is now selected server-side via KB_EMBEDDING_MODEL, which collapses Azure routing to a single deployment (KB_OPENAI_MODEL_NAME) and drops the per-model AZURE_OPENAI_DEPLOYMENT_TEXT_EMBEDDING_3_* env vars and SUPPORTED_EMBEDDING_MODEL_IDS / UI-only label+description registry fields. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 14538a1 commit 7a9ba8b

8 files changed

Lines changed: 29 additions & 88 deletions

File tree

apps/sim/app/api/knowledge/[id]/route.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ const logger = createLogger('KnowledgeBaseByIdAPI')
2727
const UpdateKnowledgeBaseSchema = z.object({
2828
name: z.string().min(1, 'Name is required').optional(),
2929
description: z.string().optional(),
30-
embeddingModel: z.literal('text-embedding-3-small').optional(),
31-
embeddingDimension: z.literal(1536).optional(),
3230
workspaceId: z.string().nullable().optional(),
3331
chunkingConfig: z
3432
.object({

apps/sim/app/api/knowledge/route.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@ import { getSession } from '@/lib/auth'
66
import { PlatformEvents } from '@/lib/core/telemetry'
77
import { generateRequestId } from '@/lib/core/utils/request'
88
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
9-
import {
10-
DEFAULT_EMBEDDING_MODEL,
11-
EMBEDDING_DIMENSIONS,
12-
SUPPORTED_EMBEDDING_MODEL_IDS,
13-
} from '@/lib/knowledge/embeddings'
9+
import { EMBEDDING_DIMENSIONS, getConfiguredEmbeddingModel } from '@/lib/knowledge/embeddings'
1410
import {
1511
createKnowledgeBase,
1612
getKnowledgeBases,
@@ -25,10 +21,6 @@ const CreateKnowledgeBaseSchema = z.object({
2521
name: z.string().min(1, 'Name is required'),
2622
description: z.string().optional(),
2723
workspaceId: z.string().min(1, 'Workspace ID is required'),
28-
embeddingModel: z
29-
.enum(SUPPORTED_EMBEDDING_MODEL_IDS as [string, ...string[]])
30-
.default(DEFAULT_EMBEDDING_MODEL),
31-
embeddingDimension: z.literal(EMBEDDING_DIMENSIONS).default(EMBEDDING_DIMENSIONS),
3224
chunkingConfig: z
3325
.object({
3426
maxSize: z.number().min(100).max(4000).default(1024),
@@ -125,9 +117,13 @@ export const POST = withRouteHandler(async (req: NextRequest) => {
125117
try {
126118
const validatedData = CreateKnowledgeBaseSchema.parse(body)
127119

120+
const embeddingModel = getConfiguredEmbeddingModel()
121+
128122
const createData = {
129123
...validatedData,
130124
userId: session.user.id,
125+
embeddingModel,
126+
embeddingDimension: EMBEDDING_DIMENSIONS,
131127
}
132128

133129
const newKnowledgeBase = await createKnowledgeBase(createData, requestId)
@@ -173,8 +169,8 @@ export const POST = withRouteHandler(async (req: NextRequest) => {
173169
metadata: {
174170
name: validatedData.name,
175171
description: validatedData.description,
176-
embeddingModel: validatedData.embeddingModel,
177-
embeddingDimension: validatedData.embeddingDimension,
172+
embeddingModel,
173+
embeddingDimension: EMBEDDING_DIMENSIONS,
178174
chunkingStrategy: validatedData.chunkingConfig.strategy,
179175
chunkMaxSize: validatedData.chunkingConfig.maxSize,
180176
chunkMinSize: validatedData.chunkingConfig.minSize,

apps/sim/app/api/v1/knowledge/route.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { z } from 'zod'
44
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
5-
import {
6-
DEFAULT_EMBEDDING_MODEL,
7-
EMBEDDING_DIMENSIONS,
8-
SUPPORTED_EMBEDDING_MODEL_IDS,
9-
} from '@/lib/knowledge/embeddings'
5+
import { EMBEDDING_DIMENSIONS, getConfiguredEmbeddingModel } from '@/lib/knowledge/embeddings'
106
import { createKnowledgeBase, getKnowledgeBases } from '@/lib/knowledge/service'
117
import {
128
authenticateRequest,
@@ -34,9 +30,6 @@ const CreateKBSchema = z.object({
3430
workspaceId: z.string().min(1, 'Workspace ID is required'),
3531
name: z.string().min(1, 'Name is required').max(255, 'Name must be 255 characters or less'),
3632
description: z.string().max(1000, 'Description must be 1000 characters or less').optional(),
37-
embeddingModel: z
38-
.enum(SUPPORTED_EMBEDDING_MODEL_IDS as [string, ...string[]])
39-
.default(DEFAULT_EMBEDDING_MODEL),
4033
chunkingConfig: ChunkingConfigSchema.optional().default({
4134
maxSize: 1024,
4235
minSize: 100,
@@ -89,7 +82,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
8982
const validation = validateSchema(CreateKBSchema, body.data)
9083
if (!validation.success) return validation.response
9184

92-
const { workspaceId, name, description, embeddingModel, chunkingConfig } = validation.data
85+
const { workspaceId, name, description, chunkingConfig } = validation.data
9386

9487
const accessError = await validateWorkspaceAccess(rateLimit, userId, workspaceId, 'write')
9588
if (accessError) return accessError
@@ -100,7 +93,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
10093
description,
10194
workspaceId,
10295
userId,
103-
embeddingModel,
96+
embeddingModel: getConfiguredEmbeddingModel(),
10497
embeddingDimension: EMBEDDING_DIMENSIONS,
10598
chunkingConfig: chunkingConfig ?? { maxSize: 1024, minSize: 100, overlap: 200 },
10699
},

apps/sim/app/workspace/[workspaceId]/knowledge/components/create-base-modal/create-base-modal.tsx

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@ import {
2222
} from '@/components/emcn'
2323
import type { StrategyOptions } from '@/lib/chunkers/types'
2424
import { cn } from '@/lib/core/utils/cn'
25-
import {
26-
DEFAULT_EMBEDDING_MODEL,
27-
SUPPORTED_EMBEDDING_MODEL_IDS,
28-
SUPPORTED_EMBEDDING_MODELS,
29-
} from '@/lib/knowledge/embedding-models'
3025
import { formatFileSize, validateKnowledgeBaseFile } from '@/lib/uploads/utils/file-utils'
3126
import { ACCEPT_ATTRIBUTE } from '@/lib/uploads/utils/validation'
3227
import { useKnowledgeUpload } from '@/app/workspace/[workspaceId]/knowledge/hooks/use-knowledge-upload'
@@ -57,13 +52,6 @@ const STRATEGY_COMBOBOX_OPTIONS: ComboboxOption[] = STRATEGY_OPTIONS.map((o) =>
5752
value: o.value,
5853
}))
5954

60-
const EMBEDDING_MODEL_OPTIONS: ComboboxOption[] = SUPPORTED_EMBEDDING_MODEL_IDS.map((id) => ({
61-
label: SUPPORTED_EMBEDDING_MODELS[id].label,
62-
value: id,
63-
}))
64-
65-
const EMBEDDING_MODEL_VALUES = SUPPORTED_EMBEDDING_MODEL_IDS as [string, ...string[]]
66-
6755
const FormSchema = z
6856
.object({
6957
name: z
@@ -87,7 +75,6 @@ const FormSchema = z
8775
strategy: z.enum(['auto', 'text', 'regex', 'recursive', 'sentence', 'token']).default('auto'),
8876
regexPattern: z.string().optional(),
8977
customSeparators: z.string().optional(),
90-
embeddingModel: z.enum(EMBEDDING_MODEL_VALUES).default(DEFAULT_EMBEDDING_MODEL),
9178
})
9279
.refine(
9380
(data) => {
@@ -187,14 +174,12 @@ export const CreateBaseModal = memo(function CreateBaseModal({
187174
strategy: 'auto',
188175
regexPattern: '',
189176
customSeparators: '',
190-
embeddingModel: DEFAULT_EMBEDDING_MODEL,
191177
},
192178
mode: 'onSubmit',
193179
})
194180

195181
const nameValue = watch('name')
196182
const strategyValue = watch('strategy')
197-
const embeddingModelValue = watch('embeddingModel')
198183

199184
useEffect(() => {
200185
if (open) {
@@ -213,7 +198,6 @@ export const CreateBaseModal = memo(function CreateBaseModal({
213198
strategy: 'auto',
214199
regexPattern: '',
215200
customSeparators: '',
216-
embeddingModel: DEFAULT_EMBEDDING_MODEL,
217201
})
218202
}
219203
}, [open, reset])
@@ -331,7 +315,6 @@ export const CreateBaseModal = memo(function CreateBaseModal({
331315
name: data.name,
332316
description: data.description || undefined,
333317
workspaceId: workspaceId,
334-
embeddingModel: data.embeddingModel,
335318
chunkingConfig: {
336319
maxSize: data.maxChunkSize,
337320
minSize: data.minChunkSize,
@@ -477,23 +460,6 @@ export const CreateBaseModal = memo(function CreateBaseModal({
477460
</p>
478461
</div>
479462

480-
<div className='flex flex-col gap-2'>
481-
<Label>Embedding Model</Label>
482-
<Combobox
483-
options={EMBEDDING_MODEL_OPTIONS}
484-
value={embeddingModelValue}
485-
onChange={(value) =>
486-
setValue('embeddingModel', value as FormValues['embeddingModel'])
487-
}
488-
dropdownWidth='trigger'
489-
align='start'
490-
/>
491-
<p className='text-[var(--text-muted)] text-xs'>
492-
{SUPPORTED_EMBEDDING_MODELS[embeddingModelValue]?.description ??
493-
'Choose how documents are vectorized. Cannot be changed after creation.'}
494-
</p>
495-
</div>
496-
497463
<div className='flex flex-col gap-2'>
498464
<Label>Chunking Strategy</Label>
499465
<Combobox

apps/sim/hooks/queries/kb/knowledge.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,6 @@ export interface CreateKnowledgeBaseParams {
697697
name: string
698698
description?: string
699699
workspaceId: string
700-
embeddingModel?: string
701700
chunkingConfig: {
702701
maxSize: number
703702
minSize: number

apps/sim/lib/core/config/env.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,8 @@ export const env = createEnv({
122122
AZURE_ANTHROPIC_ENDPOINT: z.string().url().optional(), // Azure Anthropic service endpoint
123123
AZURE_ANTHROPIC_API_KEY: z.string().min(1).optional(), // Azure Anthropic API key
124124
AZURE_ANTHROPIC_API_VERSION: z.string().min(1).optional(), // Azure Anthropic API version (e.g. 2023-06-01)
125-
KB_OPENAI_MODEL_NAME: z.string().optional(), // Knowledge base OpenAI model name (works with both regular OpenAI and Azure OpenAI). Used as the Azure deployment for text-embedding-3-small (legacy/default).
126-
AZURE_OPENAI_DEPLOYMENT_TEXT_EMBEDDING_3_SMALL: z.string().optional(), // Azure deployment name serving text-embedding-3-small. If unset, falls back to KB_OPENAI_MODEL_NAME.
127-
AZURE_OPENAI_DEPLOYMENT_TEXT_EMBEDDING_3_LARGE: z.string().optional(), // Azure deployment name serving text-embedding-3-large. Required to use 3-large via Azure.
125+
KB_OPENAI_MODEL_NAME: z.string().optional(), // Azure deployment name serving the configured KB embedding model (used only when AZURE_OPENAI_* credentials are set).
126+
KB_EMBEDDING_MODEL: z.string().optional(), // Embedding model used for all new knowledge bases. Must be one of the supported model ids; defaults to text-embedding-3-small.
128127
WAND_OPENAI_MODEL_NAME: z.string().optional(), // Wand generation OpenAI model name (works with both regular OpenAI and Azure OpenAI)
129128
OCR_AZURE_ENDPOINT: z.string().url().optional(), // Azure Mistral OCR service endpoint
130129
OCR_AZURE_MODEL_NAME: z.string().optional(), // Azure Mistral OCR model name for document processing

apps/sim/lib/knowledge/embedding-models.ts

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/**
2-
* Client-safe registry of embedding models supported by the platform.
3-
* Kept free of server imports so it can be imported into UI code.
2+
* Registry of embedding models supported by the platform.
3+
* Selection happens server-side via the `KB_EMBEDDING_MODEL` env var; this
4+
* registry exists to resolve provider, tokenizer, and pricing metadata at
5+
* runtime for any model recorded on a knowledge base row.
46
*/
57

68
export const EMBEDDING_DIMENSIONS = 1536
@@ -19,9 +21,6 @@ export interface EmbeddingModelInfo {
1921
pricingId: string
2022
/** Provider id for `estimateTokenCount` so token counts match the embedding provider's tokenization. */
2123
tokenizerProvider: TokenizerProviderId
22-
label: string
23-
/** Short user-facing description shown in the KB creation UI. */
24-
description: string
2524
}
2625

2726
export const SUPPORTED_EMBEDDING_MODELS: Record<string, EmbeddingModelInfo> = {
@@ -30,31 +29,21 @@ export const SUPPORTED_EMBEDDING_MODELS: Record<string, EmbeddingModelInfo> = {
3029
supportsCustomDimensions: true,
3130
pricingId: 'text-embedding-3-small',
3231
tokenizerProvider: 'openai',
33-
label: 'OpenAI text-embedding-3-small',
34-
description: 'Cheapest. Good for English-heavy retrieval at low cost.',
3532
},
3633
'text-embedding-3-large': {
3734
provider: 'openai',
3835
supportsCustomDimensions: true,
3936
pricingId: 'text-embedding-3-large',
4037
tokenizerProvider: 'openai',
41-
label: 'OpenAI text-embedding-3-large',
42-
description: 'Slightly better quality than 3-small at ~6.5× the cost.',
4338
},
4439
'gemini-embedding-001': {
4540
provider: 'gemini',
4641
supportsCustomDimensions: true,
4742
pricingId: 'gemini-embedding-001',
4843
tokenizerProvider: 'google',
49-
label: 'Google gemini-embedding-001',
50-
description: 'Strong multilingual retrieval. Good cost/quality balance.',
5144
},
5245
}
5346

54-
export const SUPPORTED_EMBEDDING_MODEL_IDS = Object.keys(SUPPORTED_EMBEDDING_MODELS) as Array<
55-
keyof typeof SUPPORTED_EMBEDDING_MODELS
56-
>
57-
5847
export function getEmbeddingModelInfo(model: string): EmbeddingModelInfo {
5948
const info = SUPPORTED_EMBEDDING_MODELS[model]
6049
if (!info) {

apps/sim/lib/knowledge/embeddings.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export {
2222
DEFAULT_EMBEDDING_MODEL,
2323
EMBEDDING_DIMENSIONS,
2424
getEmbeddingModelInfo,
25-
SUPPORTED_EMBEDDING_MODEL_IDS,
2625
SUPPORTED_EMBEDDING_MODELS,
2726
} from '@/lib/knowledge/embedding-models'
2827

@@ -174,19 +173,21 @@ function buildGeminiProvider(modelName: string, apiKey: string): ResolvedProvide
174173
}
175174

176175
/**
177-
* Resolve the Azure deployment name for a given OpenAI embedding model.
178-
* Returns null if no deployment is configured for that model — caller falls
179-
* back to direct OpenAI rather than risk routing to a wrong-model deployment
180-
* (which would silently produce mismatched vectors).
176+
* Returns the embedding model to use for new knowledge bases.
177+
* Sourced from the `KB_EMBEDDING_MODEL` env var; falls back to the default if
178+
* unset or set to an unsupported model.
181179
*/
182-
function resolveAzureDeployment(embeddingModel: string): string | null {
183-
if (embeddingModel === 'text-embedding-3-small') {
184-
return env.AZURE_OPENAI_DEPLOYMENT_TEXT_EMBEDDING_3_SMALL || env.KB_OPENAI_MODEL_NAME || null
180+
export function getConfiguredEmbeddingModel(): string {
181+
const configured = env.KB_EMBEDDING_MODEL
182+
if (configured && SUPPORTED_EMBEDDING_MODELS[configured]) {
183+
return configured
185184
}
186-
if (embeddingModel === 'text-embedding-3-large') {
187-
return env.AZURE_OPENAI_DEPLOYMENT_TEXT_EMBEDDING_3_LARGE || null
185+
if (configured) {
186+
logger.warn(
187+
`KB_EMBEDDING_MODEL="${configured}" is not a supported embedding model — falling back to ${DEFAULT_EMBEDDING_MODEL}`
188+
)
188189
}
189-
return null
190+
return DEFAULT_EMBEDDING_MODEL
190191
}
191192

192193
async function resolveProvider(
@@ -198,7 +199,7 @@ async function resolveProvider(
198199
const azureApiVersion = env.AZURE_OPENAI_API_VERSION
199200
const isOpenAIModel = SUPPORTED_EMBEDDING_MODELS[embeddingModel]?.provider === 'openai'
200201
const azureDeployment =
201-
isOpenAIModel && azureApiKey && azureEndpoint ? resolveAzureDeployment(embeddingModel) : null
202+
isOpenAIModel && azureApiKey && azureEndpoint ? env.KB_OPENAI_MODEL_NAME || null : null
202203

203204
if (azureDeployment) {
204205
return {

0 commit comments

Comments
 (0)