Skip to content
Open
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
29 changes: 25 additions & 4 deletions packages/web/src/features/mcp/askCodebase.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { sew } from "@/middleware/sew";
import { getConfiguredLanguageModels, getAISDKLanguageModelAndOptions, generateChatNameFromMessage, updateChatMessages } from "@/features/chat/utils.server";
import { LanguageModelInfo, SBChatMessage, SearchScope } from "@/features/chat/types";
import { convertLLMOutputToPortableMarkdown, getAnswerPartFromAssistantMessage, getLanguageModelKey } from "@/features/chat/utils";
import { convertLLMOutputToPortableMarkdown, getAnswerPartFromAssistantMessage } from "@/features/chat/utils";
import { ErrorCode } from "@/lib/errorCodes";
import { ServiceError, ServiceErrorException } from "@/lib/serviceError";
import { withOptionalAuth } from "@/middleware/withAuth";
Expand Down Expand Up @@ -57,14 +57,35 @@ export const askCodebase = (params: AskCodebaseParams): Promise<AskCodebaseResul

let languageModelConfig = configuredModels[0];
if (requestedLanguageModel) {
const matchingModel = configuredModels.find(
(m) => getLanguageModelKey(m) === getLanguageModelKey(requestedLanguageModel)
const candidates = configuredModels.filter(
(m) => m.provider === requestedLanguageModel.provider &&
m.model === requestedLanguageModel.model
);
const displayNameProvided = requestedLanguageModel.displayName !== undefined;
const matchingModel = displayNameProvided
? candidates.find((m) => m.displayName === requestedLanguageModel.displayName)
: candidates.length === 1 ? candidates[0] : undefined;
Comment on lines +60 to +67
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the chat route and its request schema to see if the same key-based
# match has the same displayName-undefined failure mode.
fd -t f 'route.ts' packages/web/src/app/api | xargs rg -nP -C5 'getLanguageModelKey'
rg -nP -C3 'languageModel\s*[:?]' packages/web/src/app/api/\(server\)/chat
rg -nP -C3 'displayName' packages/web/src/app/api/\(server\)/chat

Repository: sourcebot-dev/sourcebot

Length of output: 4415


🏁 Script executed:

# First, let's understand what getLanguageModelKey does
rg -nP -A10 'function getLanguageModelKey|export.*getLanguageModelKey|const getLanguageModelKey' packages/web/src/features/chat/utils.ts

Repository: sourcebot-dev/sourcebot

Length of output: 629


🏁 Script executed:

# Check the full schema definition for languageModel in the chat request
rg -nP -A20 'additionalChatRequestParamsSchema' packages/web/src/features/chat/types.ts

Repository: sourcebot-dev/sourcebot

Length of output: 342


🏁 Script executed:

# Look at the languageModelInfoSchema to understand displayName handling
rg -nP -B5 -A15 'languageModelInfoSchema' packages/web/src/features/chat/types.ts

Repository: sourcebot-dev/sourcebot

Length of output: 1251


🏁 Script executed:

# Compare the MCP request schema for askCodebase
rg -nP -B5 -A15 'languageModel' packages/web/src/features/mcp/types.ts | head -60

Repository: sourcebot-dev/sourcebot

Length of output: 49


🏁 Script executed:

# Look at the actual MCP askCodebase.ts around lines 60-67 to see the fix
cat -n packages/web/src/features/mcp/askCodebase.ts | sed -n '50,80p'

Repository: sourcebot-dev/sourcebot

Length of output: 2263


🏁 Script executed:

# Check if there are other routes/API handlers using getLanguageModelKey with similar pattern
rg -nP 'getLanguageModelKey' packages/web/src --type ts --type tsx -B3 -A3

Repository: sourcebot-dev/sourcebot

Length of output: 95


🏁 Script executed:

# Look for any tests or comments explaining the bug that was fixed
rg -nP 'displayName|getLanguageModelKey' packages/web/src/features/chat/utils.ts -B5 -A5 | head -40

Repository: sourcebot-dev/sourcebot

Length of output: 1070


Chat route has the same bug and needs the same fix.

The chat route at packages/web/src/app/api/(server)/chat/route.ts:70 uses getLanguageModelKey(model) === getLanguageModelKey(languageModel) which breaks when displayName is undefined and multiple configured models share the same provider/model. Since getLanguageModelKey includes displayName in the key (${provider}-${model}-${displayName}), an undefined value creates a key like "provider-model-undefined" that never matches any configured model, causing a 400 error.

The MCP fix at lines 64-67 explicitly handles this by checking if displayName is provided and only accepting a single candidate when it's not. The chat route should be updated to use the same logic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/features/mcp/askCodebase.ts` around lines 60 - 67, The chat
route currently compares models using getLanguageModelKey(languageModel) which
fails when displayName is undefined; update the matching logic in the chat route
(where configuredModels, languageModel, and getLanguageModelKey are used) to
mirror the MCP fix: filter configuredModels by provider and model to produce
candidates, check if displayName is provided on languageModel, and if so find
the candidate with the same displayName, otherwise only accept a match when
candidates.length === 1; replace the current getLanguageModelKey equality check
with this displayName-aware candidate selection to avoid `"...-undefined"`
mismatches.

if (!matchingModel) {
const available = candidates
.map((m) => m.displayName)
.filter((n): n is string => n !== undefined)
.map((n) => `"${n}"`)
.join(', ');
let message: string;
if (candidates.length === 0) {
message = `Language model '${requestedLanguageModel.provider}/${requestedLanguageModel.model}' is not configured.`;
} else if (displayNameProvided) {
message = `Language model '${requestedLanguageModel.provider}/${requestedLanguageModel.model}' is configured but displayName '${requestedLanguageModel.displayName}' was not found.`
+ (available ? ` Available: ${available}.` : '');
} else {
message = available
? `Multiple configurations found for '${requestedLanguageModel.provider}/${requestedLanguageModel.model}'. Provide a displayName to disambiguate. Available: ${available}.`
: `Multiple configurations found for '${requestedLanguageModel.provider}/${requestedLanguageModel.model}' but none define a displayName. Add a unique displayName to each configuration entry to enable disambiguation.`;
}
return {
statusCode: StatusCodes.BAD_REQUEST,
errorCode: ErrorCode.INVALID_REQUEST_BODY,
message: `Language model '${requestedLanguageModel.provider}/${requestedLanguageModel.model}' is not configured.`,
message,
} satisfies ServiceError;
}
languageModelConfig = matchingModel;
Expand Down
Loading