message post processing formatting

This commit is contained in:
dal 2025-07-08 10:55:19 -06:00
parent 68607d71c4
commit fe4d2c9a83
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
3 changed files with 78 additions and 34 deletions

View File

@ -93,6 +93,12 @@ pnpm run test:watch
- **No implicit returns** - All code paths must return
- **Consistent file casing** - Enforced by TypeScript
### Type Safety and Zod Best Practices
- We care deeply about type safety and we use Zod schemas and then export them as types
- We prefer using type abstractions over `.parse()` method calls
- Always export Zod schemas as TypeScript types to leverage static type checking
- Avoid runtime type checking when compile-time type checks are sufficient
### Biome Rules (Key Enforcements)
- **`useImportType: "warn"`** - Use type-only imports when possible
- **`noExplicitAny: "error"`** - Never use `any` type

View File

@ -4,7 +4,7 @@ import postProcessingWorkflow, {
import { eq, getDb, messages } from '@buster/database';
import { logger, schemaTask } from '@trigger.dev/sdk/v3';
import { initLogger, wrapTraced } from 'braintrust';
import { z } from 'zod';
import { z } from 'zod/v4';
import {
buildWorkflowInput,
fetchConversationHistory,
@ -13,23 +13,18 @@ import {
fetchUserDatasets,
sendSlackNotification,
} from './helpers';
import {
DataFetchError,
MessageNotFoundError,
TaskInputSchema,
type TaskOutputSchema,
} from './types';
import { DataFetchError, MessageNotFoundError, TaskInputSchema } from './types';
import type { TaskInput, TaskOutput } from './types';
// Schema for the subset of fields we want to save to the database
const PostProcessingDbDataSchema = z.object({
summaryMessage: z.string().optional(),
summaryTitle: z.string().optional(),
formattedMessage: z.string().nullable().optional(),
confidence_score: z.enum(['low', 'high']),
summary_message: z.string(),
summary_title: z.string(),
assumptions: z
.array(
z.object({
descriptiveTitle: z.string(),
descriptive_title: z.string(),
classification: z.enum([
'fieldMapping',
'tableRelationship',
@ -59,9 +54,8 @@ const PostProcessingDbDataSchema = z.object({
})
)
.optional(),
message: z.string().optional(),
toolCalled: z.string(),
userName: z.string().nullable().optional(),
tool_called: z.string(),
user_name: z.string().nullable().optional(),
});
type PostProcessingDbData = z.infer<typeof PostProcessingDbDataSchema>;
@ -73,18 +67,53 @@ function extractDbFields(
workflowOutput: PostProcessingWorkflowOutput,
userName: string | null
): PostProcessingDbData {
const extracted = {
summaryMessage: workflowOutput.summaryMessage,
summaryTitle: workflowOutput.summaryTitle,
formattedMessage: workflowOutput.formattedMessage,
assumptions: workflowOutput.assumptions,
message: workflowOutput.message,
toolCalled: workflowOutput.toolCalled || 'unknown', // Provide default if missing
userName,
logger.log('Extracting database fields from workflow output', {
workflowOutput,
});
// Check if there are any major assumptions
const hasMajorAssumptions =
workflowOutput.assumptions?.some((assumption) => assumption.label === 'major') ?? false;
// Determine confidence score based on rules:
// - Low if toolCalled is 'flagChat'
// - Low if there are any major assumptions
// - High otherwise
let confidence_score: 'low' | 'high' = 'high';
if (workflowOutput.toolCalled === 'flagChat' || hasMajorAssumptions) {
confidence_score = 'low';
}
// Determine summary message and title
let summaryMessage: string;
let summaryTitle: string;
if (!hasMajorAssumptions && workflowOutput.flagChatMessage) {
// If no major assumptions, use flagChatMessage as summaryMessage
summaryMessage = workflowOutput.flagChatMessage;
summaryTitle = 'No Major Assumptions Identified';
} else {
// Otherwise use the provided summary fields or defaults
summaryMessage = workflowOutput.summaryMessage || 'No summary available';
summaryTitle = workflowOutput.summaryTitle || 'Summary';
}
const extracted: PostProcessingDbData = {
summary_message: summaryMessage,
summary_title: summaryTitle,
confidence_score,
assumptions: workflowOutput.assumptions?.map((assumption) => ({
descriptive_title: assumption.descriptiveTitle,
classification: assumption.classification,
explanation: assumption.explanation,
label: assumption.label,
})),
tool_called: workflowOutput.toolCalled || 'unknown', // Provide default if missing
user_name: userName,
};
// Validate the extracted data matches our schema
return PostProcessingDbDataSchema.parse(extracted);
return extracted;
}
/**
@ -263,21 +292,18 @@ export const messagePostProcessingTask: ReturnType<
logger.log('Checking Slack notification conditions', {
messageId: payload.messageId,
organizationId: messageContext.organizationId,
summaryTitle: dbData.summaryTitle,
summaryMessage: dbData.summaryMessage,
formattedMessage: dbData.formattedMessage,
toolCalled: dbData.toolCalled,
summaryTitle: dbData.summary_title,
summaryMessage: dbData.summary_message,
toolCalled: dbData.tool_called,
});
const slackResult = await sendSlackNotification({
organizationId: messageContext.organizationId,
userName: messageContext.userName,
chatId: messageContext.chatId,
summaryTitle: dbData.summaryTitle,
summaryMessage: dbData.summaryMessage,
formattedMessage: dbData.formattedMessage,
toolCalled: dbData.toolCalled,
message: dbData.message,
summaryTitle: dbData.summary_title,
summaryMessage: dbData.summary_message,
toolCalled: dbData.tool_called,
});
if (slackResult.sent) {

View File

@ -224,13 +224,25 @@ No conversation history available for analysis.`);
throw new Error('No tool was called by the flag chat agent');
}
// Handle different tool responses
let flagChatMessage: string | undefined;
let flagChatTitle: string | undefined;
if (toolCall.toolName === 'noIssuesFound') {
flagChatMessage = toolCall.args.message;
flagChatTitle = 'No Issues Found';
} else if (toolCall.toolName === 'flagChat') {
flagChatMessage = toolCall.args.summary_message;
flagChatTitle = toolCall.args.summary_title;
}
return {
// Pass through all input fields
...inputData,
// Add new fields from this step
toolCalled: toolCall.toolName,
flagChatMessage: toolCall.args.summary_message,
flagChatTitle: toolCall.args.summary_title,
flagChatMessage,
flagChatTitle,
};
} catch (error) {
console.error('Failed to analyze chat for flagging:', error);