AI Advisor Request Lifecycle
🔄 AI Advisor Request Lifecycle
Section titled “🔄 AI Advisor Request Lifecycle”This page follows one question through every layer: the chat panel, the transport, the Hono route, the use case, the Mastra adapter, the agents and tools, the guardrails, and finally the audit row.
📈 Sequence Diagram
Section titled “📈 Sequence Diagram”sequenceDiagram
autonumber
actor User
participant UI as AdvisorPanel and useAdvisorChat
participant RA as RestAdvisorAdapter
participant R as routes/advisor.ts
participant UC as AskAdvisorUC
participant A as MastraAdvisorAdapter
participant S as advisor (supervisor)
participant T as taxAnalyst
participant U as Use case behind a tool
participant L as SqliteAdvisorRunLogAdapter
User->>UI: type a question, Enter
UI->>RA: ask({ message, threadId? }, signal)
RA->>R: POST /api/advisor/stream
R->>R: zValidator(askAdvisorRequestSchema)
R->>UC: execute({ message, threadId })
UC->>UC: read language and base_currency settings, resolve threadId
UC->>A: ask({ message, threadId, locale, baseCurrency })
A->>A: read execution profiles, resolve credentialed chain
alt no usable chain entry
A-->>UC: failed (NO_MODEL_AVAILABLE or VAULT_LOCKED)
else chain resolved
A->>A: derive execution profile, build tool configs, agents and model config
A->>S: stream(message, requestContext, maxSteps, memory)
S->>T: delegate (agents map)
T->>U: tool call, strict input
U-->>T: use case result, budget-gated
T-->>S: answer with verbatim figures
S-->>A: text-delta, tool-*, step-finish, finish or tripwire or error chunks
A-->>UC: AdvisorEvent stream
end
UC-->>R: events (token, tool-start, tool-result, tool-error, then one terminal)
R->>R: toWireEvent, advisorStreamEventSchema.parse
R-->>RA: SSE frames
RA-->>UI: validated AdvisorStreamEvent
UC->>L: appendRun(receipt, outcome) after the terminal event
UI-->>User: streamed Markdown, tool activity, badges, footers
🪜 Step by Step
Section titled “🪜 Step by Step”- UI send.
AdvisorPanel.vuetrims the draft and callsuseAdvisorChat.send. A send while a run is in flight is ignored. The request carriesthreadIdonly after a previous terminal frame supplied one. - Transport.
RestAdvisorAdapter.askissues a plainfetchPOST to/api/advisor/streamand parses the SSE body by hand. Every frame is validated against the sharedadvisorStreamEventSchema; an invalid frame becomes a localinvalid-frametransport error. - Route.
createAdvisorApivalidates the body (message1 to 4000 characters, optional UUIDthreadId; otherwise HTTP 400). It opens an SSE stream, starts a 15-second: keep-alivecomment timer, and iteratesAskAdvisorUC.execute. - Use case.
AskAdvisorUCreadslanguage(defaulten) andbase_currency(defaultUSD) throughIUserSettingsPort, allocates athreadIdif absent, and delegates the run toIAdvisorPort. It persists the audit row when a terminal event passes through, or fromfinallyas anabortedrow if the consumer stops early. - Adapter, chain resolution.
MastraAdvisorAdapterreads the execution-profile setting, thenresolveChainForRequest: readai_advisor_model_chain, drop entries whose credential is unavailable, decrypt keys, derive the run profile. An empty result short-circuits to afailedevent with no model call. - Adapter, agent graph. A fresh
taxAnalystandadvisorpair is built per request, because the resolved chain, budgets, andRunBudgetTrackerare per-request facts.Memoryis the only long-lived object (constructed once againstai-advisor.db). - Agent run.
advisor.stream(...)is called withmaxStepsfrom the profile,lastMessageshistory,semanticRecall: false, working memory off, and a request context carryinglocaleandbaseCurrency(never model inputs). - Tools.
advisordelegates totaxAnalyst, which calls read-only tools. Each tool validates strict input, calls its use case, projects a narrow payload, and passes it throughenforceBudget. - Chunk mapping.
applyChunk(mastraChunk.ts) converts Mastra stream chunks into domainAdvisorEvents and updates the receipt draft. Unrecognised chunk types are dropped silently. Delegation calls (agent-taxAnalyst) are not catalogue tools and are not surfaced as tool events. - Guardrails. Two output processors inspect the supervisor output at the
finishchunk. A trip becomes arefusedevent; a run that touched investment content marks thefinishpayload so the terminal frame carriesdisclaimer: true. - Wire mapping. The route calls
toWireEvent(the single domain-to-wire conversion) and re-validates the result before writing each frame.completedbecomesdone. - Audit. The route deliberately keeps iterating after the terminal frame so the use case can reach
appendRun. One row is written per run. - Cancellation. If the client closes the connection,
stream.onAbortcallsiterator.return(); the adapter’sfinallyaborts the provider call and the use case writes anabortedrow.
[!NOTE] The non-streaming
POST /api/advisor/askruns the same use case, concatenatestokenevents, and returns one JSON answer (see API & Streaming). The UI uses the streaming route only.
🧯 Failure Paths at a Glance
Section titled “🧯 Failure Paths at a Glance”| Where it fails | Result |
|---|---|
| Invalid request body | HTTP 400 from zValidator; no run starts. |
| No usable chain entry | failed with NO_MODEL_AVAILABLE or VAULT_LOCKED; no model call, pre-run receipt. |
| Every chain entry fails after retries | failed with ALL_PROVIDERS_FAILED and a classified cause. |
| A tool rejects its input or its use case throws | tool-error frame (INVALID_TOOL_INPUT or USE_CASE_FAILED); the run may still complete. |
| Guardrail trip | refused with the processorId. |
| Client disconnects or presses Stop | aborted audit row; no terminal frame is needed. |
| Unexpected server error | failed with INTERNAL_ERROR. |
For the provider-side details see Providers & Models; for the frame shapes see API & Streaming.