fix(agno): replace the obsolete AGUI shims with Agno 3.x public handlers - #2624
Merged
Conversation
The agentic generative UI progress card picked its active row as the first pending step, so a streamed in_progress step rendered as a future gray row while a later pending step got the spinner, the Processing label and the animated background. Resolve the active row in one place: the in_progress step when the agent streams one, otherwise the first pending step so servers that only emit pending/completed keep their current rendering. Later pending steps render as future rows. The contract test executes the resolver with Node and pins the render wiring in both the source page and the generated Dojo catalog, which is regenerated here.
…rogress Agno's update_session_state assigns each top-level key wholesale, so a partial steps list truncated the plan to the steps sent. The prompt also only allowed pending and completed, so the Dojo page never showed an active row. The instructions now require the full steps list on every call and the pending, in_progress, completed sequence per step, pinned by a contract test.
The Dojo serves apps/dojo/src/files.json, but the agno example contracts only inspected the source files. Add byte-for-byte parity assertions between every agno:: page.tsx and Python entry and its source, plus the haiku page in the tool-based generative UI contract, so drift in the served copy fails the suite.
The stale-commit assertNotIn matched a single-line literal that Prettier always wraps at that indent, so reintroducing the stale agentState commit would have passed. Parse the onConfirm body of the write_document render, assert the reviewed args.document commit through a whitespace and trailing-comma normalizer, and reject any agentState reference in that body. The confirm_changes fragments use the same normalizer.
…lient tool Agno appends AG-UI client tools after the agent's own tools and keeps the first duplicate name, so the non-silent native stub won over the silent client tool and every background change emitted a spurious "needs external execution" assistant message. The Dojo page's useFrontendTool registration is authoritative, as it already is for generate_task_steps and generate_haiku. Extend the dedup contract so the same-name guard covers every demo module with a matching Dojo page, instead of only the HITL and haiku demos.
…kkeeping Agno 3.0.5 only strips current_session_id, current_user_id and current_run_id from session_state before the upsert when the agent has a db. Without one those keys stayed in the live state dict that the AG-UI handler snapshots in on_run_completed, so the Dojo received them next to the recipe. shared_state was the only enable_agentic_state demo without an InMemoryDb. Pin it with a startup assertion that every stateful demo carries a db and a route-level regression that posts a recipe state through the AgentOS app and checks the final STATE_SNAPSHOT keeps recipe and drops the bookkeeping keys.
…pause and resume The model double used to raise inside the model call when the resumed request lacked the correlated tool result. The stock Agno router folds that exception into a RAW RunError payload and still ends the stream with RUN_FINISHED over HTTP 200, so the regression could pass on a broken route. The double now only records what it receives, and the test asserts no RUN_ERROR or RunError payload in either response, the paused stream ending with the write_document tool call events then RUN_FINISHED, the resumed stream ending with RUN_FINISHED, and the correlated tool result reaching the second model call.
…ties they claim The pyproject check matched a dotted module path against a file that only holds distribution names, reasoning_summary was matched as a whole-file substring, and the page test never proved the dropdown items sat inside the supportsReasoningModelSelection branch. Resolve the Agent(...) keywords through the AST (following hoisted names), parse the agno extras from [project].dependencies, slice the balanced conditional branch, and pin reasoning_effort. Each strengthened assertion has a mutation regression.
…schema The runtime passes tool arguments to the handler as parsed JSON without validating them against the Zod parameters, so a malformed call could be stored as a haiku and bypass the image allowlist and gradient guard. The handler now runs the same strict schema as the streaming preview, skips the state update on failure, and tells the model which fields to fix.
…AGUI guards Resolve ast.ImportFrom.level against the importing file's package so relative imports map to the same dotted names the absolute checks use, and replace the exact server.local_agui/ResumeAwareAGUI adapter check with a structural one: server/ may hold only __init__.py and api/, and example modules may import nothing under server outside server.api.
The strict HAIKU_SCHEMA dropped every .describe() the previous schema carried, and Zod refinements do not serialize to JSON Schema, so the model saw gradient as a bare string with no hint that only linear, radial or conic gradients without url(...) pass the render gate. Restore field descriptions, add a tool description, and pin both in the contract test through the same zod-to-json-schema conversion the runtime uses (skipped when dojo node_modules are not installed).
…denylist The adoption guard let three shims through: any agno.os.interfaces.agui submodule other than media or utils, a shim under server/api/, and a second binding of the AGUI name imported next to the stock one. Replace the denylist with a rule that examples may import only the AGUI symbol, require that AGUI is bound solely by the stock import, and extend the layout guard to server/api/ so only the mounted demo modules may live there. Add mutation regressions for each bypass form.
Tie EXPECTED_DOJO_MOUNTS to the modules under server/api and to the names exported by server/api/__init__.py so the three inventories must agree, and sweep every demo module for a persisted db, recording the one deliberately stateless demo in STATELESS_DEMOS and asserting that set is exactly the demos without a db.
Bound the client tool name search to each hook call's own balanced argument object, skipping strings, template literals and comments, and fail loud on a hook without exactly one name. Pin the client tools the four Dojo pages must register so the guard cannot pass on an empty set, route the native tool checks through _native_tool_names (Toolkit, Function, plain callable; anything else fails loud), and derive the haiku fixture's image_name enum from the page's VALID_IMAGE_NAMES instead of a hand-written value. Add regression tests for the bypass forms.
The stock Agno router folds a raised exception into a RAW RunError payload and still ends with RUN_FINISHED over HTTP 200, so a route test that only checks status or the terminal event passes on a failed run. Add assert_stream_ok (status 200, no RUN_ERROR or RunError, RUN_STARTED first, expected terminal event, balanced tool-call and text-message lifecycles), route both TestClient assertions through it, and add negative controls built from literal SSE fixtures that prove each guard rejects its input. Route tests now use a unique thread id per run.
…ription and strictness invariants
Three review rounds surfaced defects that only appear after the runtime's
zodToJsonSchema(schema, {}) conversion: dropped describe() text, a shared
Zod instance emitting $ref, refine-only constraints. Add a generic invariant
walker over the converted JSON schema and apply it to generate_haiku,
write_document and change_background, reporting each break with its JSON
path. The Node dependency check now fails loud instead of skipping.
Every agno:: key must carry exactly one python entry byte-equal to its server/api module (or none for the frontend-only v1 demo), its feature entries must be exactly the generator-selected files under the feature directory and byte-equal to them, and the key set must equal the api modules plus the frontend-only demos. Negative controls prove swapped, dropped, extra, mis-keyed and mis-typed entries each fail.
…tive UI demo Register a null renderer for update_session_state scoped to the agentic_generative_ui agent so the Dojo chat shows only the progress card, and cut the Agno prompt to one state write per step transition (about N + 2 writes instead of 1 + 2N) while keeping the complete-list rule, the three statuses and the ten-step default.
Ruff I001 flagged two demo modules. The generated Dojo catalog is regenerated to keep the server files byte-equal with their sources.
The strict schema gate added to the streaming preview rejected partial argument snapshots, so the in-chat card only appeared once a tool call was complete, and it blanked the card entirely for backends that send just the haiku text. That page is shared by every integration. The preview now renders as soon as there are Japanese lines and sanitizes only the values that reach CSS or an image lookup. The strict schema still gates what the handler stores.
Contributor
Python Preview PackagesVersion
Install with uvAdd the TestPyPI index to your [[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
explicit = trueThen install the packages you need: # Core SDK
uv add 'ag-ui-protocol==0.0.0.dev1788451833' --index testpypi
# Integrations (each already depends on the matching ag-ui-protocol preview)
uv add 'ag-ui-langgraph==0.0.0.dev1788451833' --index testpypi
uv add 'ag-ui-crewai==0.0.0.dev1788451833' --index testpypi
# NOTE: ag-ui-agent-spec depends on pyagentspec (git-only, not on PyPI).
# You will need to install pyagentspec separately from its git repo.
uv add 'ag-ui-agent-spec==0.0.0.dev1788451833' --index testpypi
uv add 'ag_ui_adk==0.0.0.dev1788451833' --index testpypi
uv add 'ag_ui_strands==0.0.0.dev1788451833' --index testpypiInstall with pippip install \
--index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
ag-ui-protocol==0.0.0.dev1788451833
Commit: a6089db |
@ag-ui/a2a-middleware
@ag-ui/a2ui-middleware
@ag-ui/event-throttle-middleware
@ag-ui/mcp-apps-middleware
@ag-ui/mcp-middleware
@ag-ui/a2a
@ag-ui/adk
@ag-ui/ag2
@ag-ui/agno
@ag-ui/aws-strands
@ag-ui/claude-agent-sdk
@ag-ui/claude-managed-agents
@ag-ui/crewai
@ag-ui/langchain
@ag-ui/langgraph
@ag-ui/llamaindex
@ag-ui/mastra
@ag-ui/pydantic-ai
@ag-ui/vercel-ai-sdk
@ag-ui/watsonx
@ag-ui/a2ui-toolkit
create-ag-ui-app
@ag-ui/client
@ag-ui/core
@ag-ui/encoder
@ag-ui/proto
commit: |
…ests The shared Dojo pages are used by every integration and did not need to change for Agno. Most of what this branch had done to them was reformatting, and the rest pushed Agno's own behavior into shared UI, including an integrationId check for "agno" in the reasoning page and a strict haiku schema that blanked the card for backends sending only the poem text. All five pages go back to their state on main, and the example tests added here go with them; the pre-existing example tests are restored unchanged. What remains is the subject of the ticket: the nine demos on Agno's public AGUI interface, mirrored into the generated catalog.
ranst91
enabled auto-merge
September 4, 2026 14:18
mme
approved these changes
Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Moves the Agno Dojo examples onto Agno 3.x public AG-UI surfaces so nothing in the integration reimplements framework behavior.
Agentfrom the publicagno.agentmodule and mounts the stockAGUIinterface; no local router, adapter, or copied handler logic remainsgenerate_task_steps,generate_haiku,change_background) were removed so the client tool is authoritative and silent, and every stateful demo carries a databasein_progressstep as the active row and hides its own state-update tool cards, and the haiku tool schema carries field descriptionsupdate_session_statereplaces top-level keys) and drives the pending, in progress, completed lifecycle; the shared state demo has a database so Agno's transient bookkeeping keys no longer leak into the state snapshotagno.os.interfaces.aguiinternal import and any local adapter underserver/, derive demo inventories from disk instead of hand-typed lists, and walk the converted JSON Schema of every frontend tool for$ref, type, and description invariantsNo CopilotKit-specific extension was retained: every behavior the ticket lists is satisfied by the framework path and covered by the contract tests.
Validation
uv run --frozen python -m unittest discover -s testsinintegrations/agno/python/examples: 161 passed on locked Agno 3.0.5tsc --noEmit, Prettier, and ESLint on the five changed Dojo pages: cleangit diff --checkcleanCI's
e2eworkflow runs the Dojoagnosuite on this pull request. No live-model run was performed locally (no API keys on this machine), and no CI job currently runs the Agno example unit tests; that gap is PNI-407's subject.Follow-ups (out of this PR's subject)
Bucket (d) items surfaced during review are tracked separately rather than fixed here. The ones that most affect the Agno examples: conversation history is single-turn because no demo sets
add_history_to_context(PNI-392), a mid-stream failure on the stock router leaves a tool call unterminated beforeRUN_ERROR(PNI-411), Agno's partial-resume guard has no callers upstream, the predictive prompt ignores the page'swrite_documenttool (PNI-404), and the weather tool's URL encoding, units, error handling, and mock gate.Linear: PNI-403