Skip to content

feat(routes): PATCH /v1/sessions/{id} for partial cost_data update#3

Merged
rdwj merged 2 commits into
mainfrom
feat/patch-sessions-2
Apr 28, 2026
Merged

feat(routes): PATCH /v1/sessions/{id} for partial cost_data update#3
rdwj merged 2 commits into
mainfrom
feat/patch-sessions-2

Conversation

@rdwj
Copy link
Copy Markdown
Contributor

@rdwj rdwj commented Apr 28, 2026

Summary

Lands the platform half of the cost-tracking round-trip from #2: a PATCH /v1/sessions/{session_id} route that delegates to SessionStore.update() for a shallow merge of cost_data (write-wins per top-level key). 404 if the session does not exist — no auto-create on PATCH, matching the wire contract in the issue.

The handler is a thin veneer; persistence and merge semantics live in fipsagents.server.sessions.SqliteSessionStore / PostgresSessionStore, consistent with the rest of this service.

Wire contract

PATCH /v1/sessions/{session_id}
Content-Type: application/json
Authorization: Bearer <jwt>

{ "cost_data": { "input_tokens": 1234, "output_tokens": 567, ... } }
  • 200 with {"session_id", "messages": [...]} on success
  • 404 if the session does not exist
  • cost_data: null is a no-op merge but still confirms existence (200/404 same as a populated body)
  • Auth: same require_user dependency as the sibling sessions endpoints

Companion

  • fips-agents/agent-template#116 already ships the agent-side HttpSessionStore.update() that speaks this wire shape; its e2e tests pass against this endpoint.

Test plan

  • pytest -q — 43 passed (5 new in test_sessions.py)
  • make lint
  • gitleaks protect --staged --no-banner --redact — no leaks
  • Two-call merge with overlapping keys ({a:1} then {b:2,a:5} -> {a:5,b:2})
  • PATCH on missing session returns 404
  • PATCH with cost_data: null is a no-op on existing accumulator state
  • PATCH with cost_data: null on missing session still 404
  • PATCH echoes persisted message history alongside the merge

Closes #2

Assisted-by: Claude Code (Opus 4.7)

Adds the partial-update wire shape called out in #2 so agents can
record per-turn token usage as an accumulator without rewriting the
message history. The handler is a thin delegate over
SessionStore.update() — shallow merge per top-level key, write-wins,
404 when the session does not exist. No parallel persistence; the
SQLite and Postgres backends from fipsagents.server.sessions own the
merge semantics, matching the platform-as-veneer principle.

The wire contract is already exercised end-to-end by the agent-side
HttpSessionStore.update() in fips-agents/agent-template#116, so this
commit lights up the platform half of the round-trip. Five new pytest
cases drive the route via httpx.AsyncClient against a real
SQLite-backed store: two-call merge with overlapping keys, missing
session 404, cost_data null no-op merge, missing-session 404 with null
body, and message echo on PATCH for non-empty histories.

Closes #2
Refs: fips-agents/agent-template#116

Assisted-by: Claude Code (Opus 4.7)
The PATCH /v1/sessions/{id} route delegates to SessionStore.update()
which is new in fipsagents 0.14.0. Pinning the lower bound prevents
running against an older fipsagents that lacks the ABC method.

Refs #2.

Assisted-by: Claude Code (Opus 4.7)
@rdwj rdwj merged commit 9b85ee6 into main Apr 28, 2026
@rdwj rdwj deleted the feat/patch-sessions-2 branch April 28, 2026 02:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: PATCH /v1/sessions/{id} for partial-update (cost_data merge)

1 participant