Skip to content

NickAkhmetov/Update Say & See to udi-yac 0.9.0, fix chart field changes - #4111

Merged
NickAkhmetov merged 2 commits into
mainfrom
NickAkhmetov/udi-yac-0.9
Oct 28, 2026
Merged

NickAkhmetov merged 2 commits into
mainfrom
NickAkhmetov/udi-yac-0.9

Conversation

@NickAkhmetov

@NickAkhmetov NickAkhmetov commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Upgrades the Say & See chat from udi-yac 0.5.0 to 0.9.0 and the agent from udiagent 0.6.0 to 0.9.0. It also updates the Flask integration in routes_udi.py to support what the new client sends and calls.

Backend

  • Active filters reach the agent. udi-yac 0.9 sends its active filters (chat, chart brush and host) with every /v1/yac/completions request. Orchestrator.run() now takes them as context, so the agent can answer "which filters are on?" and build on them. Flask previously dropped the field. udiagent ignores malformed entries and treats a missing field as "no active filters", so older clients keep working.
  • Fix changing a chart's field. The chat calls POST /v1/yac/vis_instantiate to rebuild a template-generated chart with a different field. Flask has had no such route since 0.5.0, so every field change failed. The new route is a port of udiagent's reference server handler. It makes no LLM call and needs no OpenAI key. Its errors use udiagent's {code, error, errors} shape, because the chat reads code and removes a chart's tweak controls on unknown_template.
    • The route imports two underscore-prefixed helpers from udiagent.vis_generate, so a future udiagent refactor could break it. A test exercises the real helpers to catch that. The alternative was installing udiagent[server], which pulls in FastAPI.

Frontend

  • Record links. Table rows and chart points link to their donor, sample or dataset page (/browse/<type>/<uuid>). Aggregated rows have no uuid and get no link. The panel description said results "do not link to individual … pages", so it has been updated.
  • Saved sessions. The Session menu gains Saved in browser, which saves sessions in localStorage. The public and consortium data packages use separate stores.

Not adopted: udi-yac 0.9's issue reporting (issueReporting prop and POST /v1/yac/issue_report). It stays off.

Dependencies: udiagent 0.9 requires openai>=3.33, so the lock moves openai from 2.32.0 to 3.39.0. Only udiagent uses it.

Design Documentation/Original Tickets

N/A

Testing

Tests added in context/app/test_routes_udi.py:

  • filters are passed through to the orchestrator;
  • a real vis_instantiate round trip against udiagent's bar-chart template;
  • the five error cases: 400 bad_request, 404 unknown_template, 400 bad_schema, 422 missing_bindings and 422 invalid_bindings.

A test in SaySeePanel.spec.tsx covers the record links, including the aggregated-row case.

Check Result
uv run pytest context/app 362 passed
pnpm test 1048 passed, 7 skipped (144 files)
pnpm tsc clean
pnpm lint clean
uv run ruff check context / ruff format --check clean

Manual, against a local Flask server:

  • vis_instantiate with the real HuBMAP datapackage.json: returns 200 with CORS headers, and 422 invalid_bindings for an unknown field.
  • The Say & See page renders udi-yac 0.9.0 with the portal's styling intact.

Not yet tested by hand, because they need an OpenAI key and an LLM round trip:

  • the agent acting on the active filters;
  • clicking a field change on a generated chart;
  • the Saved in browser menu.

Screenshots/Video

To be added.

Checklist

  • Code follows the project's coding standards
    • Lint checks pass locally
    • New CHANGELOG-udi-yac-0.9.md is present in the root directory, describing the change(s) in full sentences.
  • Unit tests covering the new feature have been added
  • All existing tests pass
  • Any relevant documentation in JIRA/Confluence has been updated to reflect the new feature
  • Any new functionalities have appropriate analytics functionalities added

Additional Notes

udi-yac emits its own events, and they reach Matomo/GA through the existing onEvent mapping, so no new analytics code was needed. Field changes emit visualization_tweaked or visualization_tweak_failed. Loading a saved session emits session_imported with source: "browser". udi-yac emits no event when a session is saved.

🤖 Generated with Claude Code

@NickAkhmetov

Copy link
Copy Markdown
Collaborator Author

set to draft pending merge of hms-dbmi/udi-yac#230 and updated release; revisions in that stack fix some of the code duplication/dependency on private functions that is currently necessary when integrating into a monolith.

NickAkhmetov added a commit to hms-dbmi/udi-yac that referenced this pull request Oct 27, 2026
A host that serves the chat from its own backend had to copy the
reference server's vis_instantiate handler, importing two private
helpers from udiagent.vis_generate, because the logic lived inside the
FastAPI route and importing udiagent.server.app starts a whole server.
The HuBMAP portal's Flask app did exactly that (hubmapconsortium/portal-ui#4111),
and its copy had already drifted: a malformed body got 400 there and 422 here.

udiagent.api now holds the endpoint's logic. vis_instantiate takes the
parsed JSON body and returns the response body. A failure raises
EndpointError, which carries the status and the {code, error, errors}
body the chat reads. The FastAPI route is now a single call to it, so a
host and the reference server give the same answers. The private helpers
stay private: the public API is the endpoint, not its parts.

The request and response models move from udiagent.server.models to
udiagent.models, so a host can validate with them without the [server]
extra. They need only pydantic, now declared directly (openai already
installs it).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
john-conroy
john-conroy previously approved these changes Oct 27, 2026
Comment thread pnpm-lock.yaml

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the only real lockfile change is udi-yac 0.5.0 → 0.9.0. The removed libc: lines make me think it was regenerated with a different pnpm than the pinned pnpm@10.27.0. Could you regenerate it via corepack so the next install doesn't add them back?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out the pinned pnpm 10.27.0 is what drops those lines. The libc: entries came in with dependabot's vite bump (#4105), which runs a newer pnpm, and 10.27.0 removes them on any re-resolve. I rebuilt the lock as main's plus only the udi-yac change (now 0.5.0 → 0.10.0), so the libc lines are back. It passes --frozen-lockfile with 10.27.0 and pnpm install --lockfile-only leaves it unchanged. This will recur between dependabot and 10.27.0 until we bump packageManager; I'll do that separately.

…r user

The /v1/yac/vis_instantiate route now calls udiagent's public
vis_instantiate API (udi-yac#228) instead of porting its private helpers
and handler, so the portal and the reference server can't drift. A
malformed body now returns 422 with per-field errors, as upstream does;
5xx errors are still logged with their traceback.

Saved Say & See sessions are stored per signed-in user, so people sharing
a browser don't see each other's consortium sessions.

pnpm-lock.yaml is main's lock plus only the udi-yac change, keeping the
libc entries that pnpm 10.27.0 would otherwise drop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@NickAkhmetov
NickAkhmetov marked this pull request as ready for review October 28, 2026 12:15
@NickAkhmetov
NickAkhmetov merged commit 89251ad into main Oct 28, 2026
9 checks passed
@NickAkhmetov
NickAkhmetov deleted the NickAkhmetov/udi-yac-0.9 branch October 28, 2026 12:16
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.

2 participants