Add message intent classification (edit/info/help) before routing

Introduces a two-LLM-call pipeline: the first call classifies the user's
message intent as "edit", "info", or "help". Edit messages proceed through
the existing routing → edit → propose flow. Info messages get a generated
response about site content from the manifest. Help messages get a
templated capabilities overview. This handles open-ended questions like
"What can I do?" or "What does my site have on it?" which previously
had no path through the system.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Khalid A
2026-04-17 20:17:22 -05:00
parent 464d2c8230
commit 3cf3694ee7
4 changed files with 120 additions and 2 deletions

View File

@@ -5,7 +5,7 @@ import type { EditJobPayload } from '@dynamic-sites/shared';
import { schemaForRepoRelativePath } from '@dynamic-sites/shared';
import { createProposal, getProposal, updateProposalStatus } from '../db.js';
import { writeContentFile } from '../io/write-content.js';
import { generateEditedJson, routeEditIntent, generateSummary } from '../llm/client.js';
import { generateEditedJson, routeEditIntent, generateSummary, classifyMessageIntent, generateInfoResponse } from '../llm/client.js';
import { buildSectionManifest } from './manifest.js';
import { sendSms } from '../sms/reply.js';
import { SMS_TEMPLATES } from '../sms/templates.js';
@@ -25,6 +25,30 @@ async function handlePropose(job: Extract<EditJobPayload, { kind: 'propose' }>)
const log = logger.child({ jobId: job.id, kind: 'propose' });
try {
// Step 0: Classify message intent (edit, info, or help)
const classification = await classifyMessageIntent({ userMessage: job.message });
log.info({ event: 'classification.result', intent: classification.intent, reason: classification.reason }, 'Message classified');
// ── HELP intent: send help message, no further LLM calls ──
if (classification.intent === 'help') {
if (job.smsReplyMeta) {
await sendSms(job.smsReplyMeta.from, job.smsReplyMeta.to, SMS_TEMPLATES.HELP());
}
return;
}
// ── INFO intent: generate informational response about site ──
if (classification.intent === 'info') {
const manifest = buildSectionManifest();
const infoResponse = await generateInfoResponse({ userMessage: job.message, manifest });
if (job.smsReplyMeta) {
await sendSms(job.smsReplyMeta.from, job.smsReplyMeta.to, infoResponse);
}
return;
}
// ── EDIT intent: proceed with existing edit flow ──
// Step 1: Route — determine which file the edit targets
let repoRelativePath = job.repo_relative_path;