54 lines
2.2 KiB
TypeScript
54 lines
2.2 KiB
TypeScript
import type {
|
|
ConversationOutcomesRepository,
|
|
MessagesRepository,
|
|
SentMessageRecord,
|
|
SuccessfulMessagesRepository,
|
|
} from '../database/repositories.js';
|
|
import type { Logger } from '../utils/logger.js';
|
|
import { classifyConversationOutcome, type OutcomeContext, type ConversationOutcome } from './conversation-outcome.js';
|
|
|
|
export interface ConversationOutcomeDependencies {
|
|
messages: MessagesRepository;
|
|
successful: SuccessfulMessagesRepository;
|
|
outcomes: ConversationOutcomesRepository;
|
|
logger: Logger;
|
|
}
|
|
|
|
/** Canonical reply/outcome path shared by inbox and Matrix ingestion. */
|
|
export function recordConversationReply(
|
|
message: SentMessageRecord,
|
|
responseSnippet: string,
|
|
context: OutcomeContext,
|
|
deps: ConversationOutcomeDependencies,
|
|
): ConversationOutcome {
|
|
const outcome = classifyConversationOutcome(responseSnippet, context);
|
|
deps.messages.markGotResponse(message.id);
|
|
deps.outcomes.record(message.id, outcome, responseSnippet || null);
|
|
|
|
if (outcome === 'boundary') {
|
|
deps.outcomes.suppressRecipient(message.recipient, 'boundary');
|
|
}
|
|
|
|
const terminalNegative = outcome === 'boundary' || outcome === 'declined' || outcome === 'upsell';
|
|
if (terminalNegative || (context.authoritativeTranscript && (outcome !== 'engaged' || !context.learningEligible))) {
|
|
deps.successful.deleteBySentMessageId(message.id);
|
|
}
|
|
|
|
if (outcome === 'engaged' && context.learningEligible && !deps.successful.existsByMessage(message.message)) {
|
|
deps.successful.insert({
|
|
sentMessageId: message.id,
|
|
originalMessage: message.message,
|
|
imageAnalysis: message.postImageAnalysis ?? null,
|
|
postTitle: message.postTitle ?? null,
|
|
responseSnippet: responseSnippet || null,
|
|
subreddit: message.subreddit ?? null,
|
|
});
|
|
deps.logger.info(`Engaged response from u/${message.recipient}; stored for learning.`);
|
|
} else if (outcome !== 'engaged') {
|
|
deps.logger.info(`Response from u/${message.recipient} classified as ${outcome}; excluded from learning examples.`);
|
|
} else if (!context.learningEligible) {
|
|
deps.logger.info(`Response from u/${message.recipient} is not terminal; deferred from learning examples.`);
|
|
}
|
|
return outcome;
|
|
}
|