mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
feat(coach): surface per-turn token usage on the chat stream
Carry an additive usage envelope on the SSE done frame
({ totalTokens, model }) and persist tokensUsed + model onto
coach_messages (migration 0182, nullable, no backfill) so the
per-message token footer survives a conversation reload. Add an
optional reasoning frame the client can render inside the thinking
disclosure when a reasoning-capable provider emits a summary; providers
without reasoning emit none. Extend CoachMessageDTO and regenerate the
OpenAPI contract. Server-authoritative: clients display the count, never
recompute it.
This commit is contained in:
1 parent
a5b3715a46
commit
f46e9d3bb8
9 files changed
+242
-29
No files matched your search
+24
-5
@@ -4148,11 +4148,14 @@ paths:
|
||||
summary: Send a Coach turn (streaming reply)
|
||||
description: "v1.18.0 — sends a user turn and streams the assistant reply as Server-Sent Events. The response is
|
||||
`text/event-stream`, not JSON: one `data: <json>\\n\\n` frame per event. Frame `type` is one of `token` (a chunk
|
||||
of reply text: `{ type, token }`), `provenance` (the evidence envelope: `{ type, metricSource }`), `done` (`{
|
||||
type, conversationId, messageId }`), or `error` (`{ type, code, message }`). The HTTP status is 200 even for a
|
||||
provider/refusal outcome — clients dispatch on the `error` frame, not the status. Clients ignore unknown frame
|
||||
types (additive evolution). Omitting `conversationId` starts a new conversation. Coach-gated; budget- and
|
||||
rate-limited. Auth via cookie or Bearer."
|
||||
of reply text: `{ type, token }`), `provenance` (the evidence envelope: `{ type, metricSource }`), `suggestion`
|
||||
(a cadence-suggestion card: `{ type, suggestion }`), `reasoning` (v1.18.9, optional reasoning-summary text: `{
|
||||
type, text }` — emitted only by reasoning-capable providers; absent otherwise), `done` (`{ type, conversationId,
|
||||
messageId, usage? }` — v1.18.9 adds the optional `usage` envelope `{ totalTokens, promptTokens?,
|
||||
completionTokens?, model }`, server-authoritative; clients display it, never recompute), or `error` (`{ type,
|
||||
code, message }`). The HTTP status is 200 even for a provider/refusal outcome — clients dispatch on the `error`
|
||||
frame, not the status. Clients ignore unknown frame types (additive evolution). Omitting `conversationId` starts
|
||||
a new conversation. Coach-gated; budget- and rate-limited. Auth via cookie or Bearer."
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
@@ -15702,6 +15705,20 @@ components:
|
||||
- type: string
|
||||
- type: "null"
|
||||
description: Coach prompt version that produced the reply; null for user turns.
|
||||
tokensUsed:
|
||||
anyOf:
|
||||
- type: integer
|
||||
minimum: -9007199254740991
|
||||
maximum: 9007199254740991
|
||||
- type: "null"
|
||||
description: v1.18.9 — total tokens this assistant turn cost, persisted so the per-message token footer survives a
|
||||
reload. Null on user turns, refusals, and pre-feature rows.
|
||||
model:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
description: v1.18.9 — the provider model that produced the reply (e.g. gpt-4o). Null when unknown (user turns,
|
||||
refusals, older rows).
|
||||
required:
|
||||
- id
|
||||
- role
|
||||
@@ -15710,6 +15727,8 @@ components:
|
||||
- metricSource
|
||||
- providerType
|
||||
- promptVersion
|
||||
- tokensUsed
|
||||
- model
|
||||
additionalProperties: false
|
||||
description: One Coach chat message, decrypted server-side. Ordered oldest-first within a conversation.
|
||||
CoachProvenance:
|
||||
|
||||
Reference in new issue
Block a user