Chat
POST /api/chat runs the conversational resume agent (resumate-chat-v2) and streams the reply as SSE. It returns incremental text and, when the agent produces a full revised resume, a resume_update event.
Request
{
"mode": "optimize",
"messages": [
{ "role": "user", "content": "Make my summary punchier and quantify impact." }
],
"currentResume": { "basics": { "name": "Ada Lovelace" }, "work": [] },
"config": { "provider": "openai", "model": "gpt-4o-mini" }
}| Field | Type | Required | Notes |
|---|---|---|---|
messages | ChatMessage[] | ✅ | Non-empty array; each { role, content }. role ∈ system · user · assistant · ai |
mode | string | — | create · optimize · analyze · translate · interview · general (default general) |
currentResume | object | — | The resume the agent should reason about / edit |
config | object | — | LLM overrides (provider, model, etc.); resolved server-side |
context | object | — | Arbitrary extra context |
A 400 is returned if messages is missing or empty.
The agent reads the latest user message as the active instruction; earlier messages are history. mode steers behaviour (e.g. optimize vs analyze) but the message content drives the result.
Response stream
The stream interleaves these event types (see SSE events for the full list):
type | Meaning |
|---|---|
run_started | Run created; runId is now valid |
message_chunk | Incremental assistant text in content — concatenate in order |
resume_update | A complete revised resume in data (and payload.resume) |
run_completed | Agent finished successfully |
done | Terminal frame — stop reading |
error | Something failed; error holds the message |
data: {"type":"run_started","runId":"clx..."}
data: {"type":"message_chunk","content":"Here's a tighter summary…","runId":"clx..."}
data: {"type":"resume_update","data":{"basics":{...},"work":[...]},"runId":"clx..."}
data: {"type":"run_completed","runId":"clx..."}
data: {"type":"done","runId":"clx..."}Consuming it
const res = await fetch('http://localhost:3112/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
mode: 'optimize',
messages: [{ role: 'user', content: 'Tighten my summary.' }],
currentResume,
}),
})
const reader = res.body!.getReader()
const decoder = new TextDecoder()
let buffer = ''
let text = ''
for (;;) {
const { value, done } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const frames = buffer.split('\n\n')
buffer = frames.pop() ?? ''
for (const frame of frames) {
const dataLine = frame.split('\n').find((l) => l.startsWith('data: '))
if (!dataLine) continue
const ev = JSON.parse(dataLine.slice(6))
switch (ev.type) {
case 'message_chunk': text += ev.content; break
case 'resume_update': onResume(ev.data); break
case 'error': throw new Error(ev.error)
case 'done': return text
}
}
}resume_update may arrive before the final message_chunk — the agent emits the revised resume as soon as it can extract a complete document from its output. Treat the events as a stream, not a fixed order.
Last updated on