fix: Fix chat title generation. Also improve how errors are reported

This commit is contained in:
bkellam 2025-07-24 12:05:39 -07:00
parent d46615c4b2
commit e150310c98
3 changed files with 89 additions and 96 deletions

View file

@ -59,6 +59,11 @@ export const sew = async <T>(fn: () => Promise<T>): Promise<T | ServiceError> =>
} catch (e) {
Sentry.captureException(e);
logger.error(e);
if (e instanceof Error) {
return unexpectedError(e.message);
}
return unexpectedError(`An unexpected error occurred. Please try again later.`);
}
}

View file

@ -10,21 +10,21 @@ import { isServiceError } from "@/lib/utils";
import { prisma } from "@/prisma";
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';
import { AnthropicProviderOptions, createAnthropic } from '@ai-sdk/anthropic';
import { createAzure } from '@ai-sdk/azure';
import { createDeepSeek } from '@ai-sdk/deepseek';
import { createGoogleGenerativeAI } from '@ai-sdk/google';
import { createVertex } from '@ai-sdk/google-vertex';
import { createVertexAnthropic } from '@ai-sdk/google-vertex/anthropic';
import { createOpenAI, OpenAIResponsesProviderOptions } from "@ai-sdk/openai";
import { createMistral } from '@ai-sdk/mistral';
import { createXai } from '@ai-sdk/xai';
import { createOpenAI, OpenAIResponsesProviderOptions } from "@ai-sdk/openai";
import { LanguageModelV2 as AISDKLanguageModelV2 } from "@ai-sdk/provider";
import { createXai } from '@ai-sdk/xai';
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import * as Sentry from "@sentry/nextjs";
import { getTokenFromConfig } from "@sourcebot/crypto";
import { OrgRole } from "@sourcebot/db";
import { createLogger } from "@sourcebot/logger";
import { LanguageModel } from "@sourcebot/schemas/v3/index.type";
import { createAzure } from '@ai-sdk/azure';
import { createDeepSeek } from '@ai-sdk/deepseek';
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import {
createUIMessageStream,
createUIMessageStreamResponse,
@ -139,7 +139,6 @@ const chatHandler = ({ messages, id, selectedRepos, languageModelId }: ChatHandl
const { model, providerOptions, headers } = await getAISDKLanguageModelAndOptions(languageModelConfig, org.id);
// @todo: refactor this
if (
messages.length === 1 &&
messages[0].role === "user" &&
@ -149,16 +148,11 @@ const chatHandler = ({ messages, id, selectedRepos, languageModelId }: ChatHandl
const content = messages[0].parts[0].text;
const title = await generateChatTitle(content, model);
if (title) {
updateChatName({
await updateChatName({
chatId: id,
name: title,
}, domain);
}
else {
logger.error("Failed to generate chat title.");
}
}
const traceId = randomUUID();
@ -184,7 +178,6 @@ const chatHandler = ({ messages, id, selectedRepos, languageModelId }: ChatHandl
}
}).filter(message => message !== undefined);
try {
const stream = createUIMessageStream<SBChatMessage>({
execute: async ({ writer }) => {
writer.write({
@ -247,22 +240,10 @@ const chatHandler = ({ messages, id, selectedRepos, languageModelId }: ChatHandl
return createUIMessageStreamResponse({
stream,
});
} catch (error) {
logger.error(error)
logger.error("Error stack:", error instanceof Error ? error.stack : "No stack trace")
Sentry.captureException(error);
return serviceErrorResponse({
statusCode: StatusCodes.INTERNAL_SERVER_ERROR,
errorCode: ErrorCode.UNEXPECTED_ERROR,
message: error instanceof Error ? error.message : "Unknown error",
});
}
}, /* minRequiredRole = */ OrgRole.GUEST), /* allowSingleTenantUnauthedAccess = */ true
));
const generateChatTitle = async (message: string, model: AISDKLanguageModelV2) => {
try {
const prompt = `Convert this question into a short topic title (max 50 characters).
Rules:
@ -282,14 +263,9 @@ User question: ${message}`;
const result = await generateText({
model,
prompt,
maxOutputTokens: 20,
});
return result.text;
} catch (error) {
logger.error("Error generating summary:", error)
return undefined;
}
}
const getAISDKLanguageModelAndOptions = async (config: LanguageModel, orgId: number): Promise<{

View file

@ -1,7 +1,9 @@
'use client';
import { Button } from '@/components/ui/button';
import { serviceErrorSchema } from '@/lib/serviceError';
import { AlertCircle, X } from "lucide-react";
import { useMemo } from 'react';
interface ErrorBannerProps {
error: Error;
@ -14,6 +16,16 @@ export const ErrorBanner = ({ error, isVisible, onClose }: ErrorBannerProps) =>
return null;
}
const errorMessage = useMemo(() => {
try {
const errorJson = JSON.parse(error.message);
const serviceError = serviceErrorSchema.parse(errorJson);
return serviceError.message;
} catch {
return error.message;
}
}, [error]);
return (
<div className="bg-red-50 border-b border-red-200 dark:bg-red-950/20 dark:border-red-800">
<div className="max-w-5xl mx-auto px-4 py-3">
@ -24,7 +36,7 @@ export const ErrorBanner = ({ error, isVisible, onClose }: ErrorBannerProps) =>
Error occurred
</span>
<span className="text-sm text-red-600 dark:text-red-400">
{error.message || "An unexpected error occurred. Please try again."}
{errorMessage}
</span>
</div>
<Button