feat(advisor): topic threads, per-question delete/copy, fullscreen
Adds case management to the Compliance Advisor widget. - topic threads: cases group into threads; the left menu shows each thread's first question as the Thema with expandable follow-ups. Send = follow-up to the active thread (carries the thread's prior Q&A as history for contextual answers); "+" starts a new topic. - delete: a trash action per question (menu + stacked view). - copy: single Q&A (question + answer + evidence + footnotes) or a whole thread, as Markdown to the clipboard (pure formatters in copy.ts). - fullscreen: compact -> panel -> fullscreen view. - route.ts consumes an optional bounded `history` so follow-ups are contextual for both the widget and the workspace consumer. Tests: copy formatter unit tests + Playwright specs (threads/new-topic, delete, fullscreen, copy affordance). No deploy. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -75,12 +75,28 @@ function answerSystem(
|
||||
return s
|
||||
}
|
||||
|
||||
// Prior thread turns for contextual follow-ups. Validated + bounded (last 8 turns ~ 4 Q&A).
|
||||
function parseHistory(raw: unknown): ChatMessage[] {
|
||||
if (!Array.isArray(raw)) return []
|
||||
const turns: ChatMessage[] = []
|
||||
for (const t of raw) {
|
||||
if (!t || typeof t !== 'object') continue
|
||||
const role = (t as { role?: unknown }).role
|
||||
const content = (t as { content?: unknown }).content
|
||||
if ((role === 'user' || role === 'assistant') && typeof content === 'string' && content.trim()) {
|
||||
turns.push({ role, content })
|
||||
}
|
||||
}
|
||||
return turns.slice(-8)
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json()
|
||||
const question = String(body.question ?? body.message ?? '').trim()
|
||||
const context: string | null = body.context ?? null
|
||||
const audience = typeof body.audience === 'string' ? body.audience.trim() : ''
|
||||
const history = parseHistory(body.history)
|
||||
const country = (['DE', 'AT', 'CH', 'EU'] as const).includes(body.country)
|
||||
? (body.country as Country)
|
||||
: undefined
|
||||
@@ -98,6 +114,7 @@ export async function POST(request: NextRequest) {
|
||||
const legacySoul = await readSoulFile('compliance-advisor')
|
||||
const legacyStream = await streamAdvisorAnswer([
|
||||
{ role: 'system', content: answerSystem(legacySoul, country, numberedEvidenceForPrompt(legacyEvidence), false, audience) },
|
||||
...history,
|
||||
{ role: 'user', content: question },
|
||||
])
|
||||
if (!legacyStream) {
|
||||
@@ -141,6 +158,7 @@ export async function POST(request: NextRequest) {
|
||||
const soul = await readSoulFile('compliance-advisor')
|
||||
const messages: ChatMessage[] = [
|
||||
{ role: 'system', content: answerSystem(soul, country, numberedEvidenceForPrompt(evidence), true, audience) },
|
||||
...history,
|
||||
{ role: 'user', content: question },
|
||||
]
|
||||
const answer = await completeAdvisorAnswer(messages)
|
||||
|
||||
Reference in New Issue
Block a user