Skip to content

feat(agent): stream chat responses as typed SSE events - #379

Merged
lightningpixel merged 8 commits into
mainfrom
feat/300-agent-streaming
Oct 20, 2026
Merged

lightningpixel merged 8 commits into
mainfrom
feat/300-agent-streaming

Conversation

@lightningpixel

Copy link
Copy Markdown
Owner

Closes #300 (T-B1).

Summary

  • agent_chat returns a StreamingResponse (SSE) emitting typed events: token, thinking, tool_start, tool_done, done. Falls back gracefully when a provider ignores "stream": true.
  • New src/shared/utils/agentStream.ts parses the event stream; ChatPanel.tsx renders text and thinking progressively and shows tool calls as in progress until they complete.
  • Errors arrive as a done event after any already-streamed text.

Tests

  • api/tests/test_agent_streaming.py (14 tests) and agentStream.test.ts (5 tests, wired into test:node).
  • npm run test: all green (Python 289 OK, Node 210 pass). ESLint clean on changed files.

Manual check

  • Long answer streams word by word
  • Thinking block fills progressively (thinking on) / absent (off)
  • Tool calls show in-progress then result (list_models, inspect_mesh + repair_mesh)
  • Invalid provider URL → clean error, no stuck spinner

lightningpixel and others added 8 commits October 19, 2026 10:07
inspect_mesh diagnoses a mesh without modifying it: watertight, manifold,
winding, holes, degenerate and self-intersecting faces, parts, bounds,
area, volume, UVs/normals, and a printable verdict. suggest_fixes derives
registry op calls (repair, decimate) from that report, and lists the
problems no op can fix yet as notes.

Topology is checked on a position-merged copy, so UV seams do not read
as holes. Self-intersections go through pymeshlab; when they are not
checked, printable is null rather than a claim. Normals are read from
the file itself, since trimesh computes them on demand.

MeshOpResult.file_path is now optional so analysis ops return only
their report. Both ops are exposed to the agent and the MCP server.

Closes #296
feat(mesh-ops): read-only inspect_mesh and suggest_fixes ops (T-A2)
…point

Four registry ops derive their 4x4 matrix from the mesh itself:
recenter (centroid to origin), drop_to_ground (lowest point to y = 0),
auto_orient (smallest rotation that turns the thinnest PCA axis
vertical; meshes without a clear flat side are left alone) and
normalize_scale (size in m/cm/mm along the longest side or one axis).

apply_transforms composes requested steps in a fixed order (orient,
scale, recenter, ground) on one load and writes a single file, so
grounding is never undone by a later step. POST /optimize/arrange
exposes it, validating each step's params through the registry, and
the raw /transform endpoint now shares the same bake helper.

The agent and MCP server get an arrange_mesh tool that takes every
step at once and reports the new mesh path for chaining.

The world-space geometry helpers move to mesh_ops/geometry.py, shared
with inspect_mesh, and the registry exposes resolve_params.

Closes #297
feat(mesh-ops): semantic transforms and one-call arrange (T-A3)
repair_mesh diagnoses the mesh, runs only the steps the diagnostic calls
for (degenerate faces, non-manifold edges, hole filling without a size
limit, face re-orientation), then re-checks, for up to three passes or
until a pass makes no progress. A mesh left inside-out is flipped at the
end. Each pass is judged on position-merged geometry, exactly as
inspect_mesh sees it, so UV seams are not mistaken for holes and a
reported success holds once the file is re-inspected.

A sound mesh is left alone and no file is written. With fill_holes off,
only face-preserving fixes run, since removing faces would open holes.
The result reports the before/after topology, the remaining issues, and
whether textures were dropped (the output is geometry only).

inspect_mesh now flags inside-out meshes, and suggest_fixes proposes
repair_mesh, which also makes inconsistent winding fixable. The legacy
repair op is unchanged for the mesh-repair workflow node.

The generic /optimize/op route now writes its output next to the input
under a unique name, like the dedicated edit routes.

The agent and MCP server get a repair_mesh tool.

Closes #298
The shipped venv installs only api/requirements.txt, which never pulls
scipy: trimesh treats it as optional. inspect_mesh, suggest_fixes and
repair_mesh need it for connected parts, hole loops and vertex merging,
so they failed outside a dev environment, where torch brings it in.

repair_mesh re-oriented faces with trimesh.repair.fix_winding, which
needs networkx, also undeclared; it now uses pymeshlab, and skips the
step for a pass while the mesh is still non-manifold.

A guard test runs every mesh-op family with networkx unimportable,
including the re-orientation path, and checks scipy stays declared.
feat(mesh-ops): diagnostic-driven repair_mesh composite op (T-A4)
agent_chat now returns a StreamingResponse emitting token, thinking,
tool_start, tool_done and done events. ChatPanel consumes the stream so
text appears progressively and tool calls show an in-progress state.

Closes #300
@lightningpixel
lightningpixel merged commit a471509 into main Oct 20, 2026
8 checks passed
@lightningpixel
lightningpixel deleted the feat/300-agent-streaming branch October 20, 2026 12:22
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.

T-B1 — Response streaming (SSE)

1 participant