Skip to content

fix(py-agent): compile Event userMessage and nested action slots in Express - #3635

Merged
ditman merged 4 commits into
fix/ts-express-actionsfrom
fix/py-express-actions
Oct 28, 2026
Merged

ditman merged 4 commits into
fix/ts-express-actionsfrom
fix/py-express-actions

Conversation

@ditman

@ditman ditman commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Python mirror of the @a2ui/agent fix for points 1 and 2 of #3598; same contract, same error categories.

  • Event(name, context?, userMessage=?, context=?): userMessage → v1.0 event.userMessage; ExpressValidationError before v1.0 and for a non-string/non-$path value; ExpressInvalidParamError for a keyword Event does not define; ExpressDuplicateParamError for a context given twice.
  • New is_action_slot(schema) and CatalogSchemaHelper.resolve_subschema(schema, key) in _shared/schema_helper.py (items / properties[key] / additionalProperties, through common-type and in-document $refs and allOf/oneOf/anyOf, cycle-guarded). _compile_value threads a _ValueSlot through dict entries, list items and variables and ORs is_action with the slot. Top-level action detection is now schema-based first, with the pre-existing action/submitAction name fallback kept so inline test catalogs that declare "action": {} keep working.
  • ExpressDecompiler writes userMessage= back.

Tests

11 compiler + 3 decompiler round-trip tests. pytest python/a2ui_agent: all green (1557 passed); the new conformance cases from the last PR of this stack pass against this branch (157 passed, 3 xfailed). pyink --check and mypy clean.

Pre-launch Checklist

One time:

For this PR:

  • I have updated the relevant CHANGELOG.md file.
  • I updated/added relevant documentation.
  • My code changes (if any) have tests.
  • If my branch is on a fork, I have verified that scripts/e2e_test.sh passes.

If you need help, consider asking for advice on the discussion board.


Stack created with GitHub Stacks CLI • Give Feedback 💬

@github-project-automation github-project-automation Bot moved this to Todo in A2UI Oct 26, 2026
@ditman
ditman added this pull request to stack #3638 October 26, 2026 20:01

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request updates the Express compiler and decompiler to support userMessage and context keyword arguments on Event(...) signatures, compiling them to v1.0 action fields, and correctly decompiling them back. It also introduces support for compiling nested function calls in action slots (e.g., inside list items or object properties) as functionCall actions by resolving sub-schemas. A review comment identifies two issues in the newly added resolve_subschema method: it prematurely returns additionalProperties fallback before checking combinators like allOf/oneOf/anyOf, and it fails to dereference local $refs of the resolved property schema, which can prevent proper action slot detection.

Comment thread python/a2ui_agent/src/a2ui/inference_formats/_shared/schema_helper.py Outdated
@ditman
ditman force-pushed the fix/py-express-actions branch from daefc3f to 6ddf7de Compare October 26, 2026 20:32
@ditman
ditman force-pushed the fix/py-express-actions branch from 6ddf7de to 45a2f52 Compare October 26, 2026 21:38
@ditman
ditman requested a review from nan-yu October 26, 2026 21:39
@ditman
ditman marked this pull request as ready for review October 26, 2026 21:40

@nan-yu nan-yu left a comment

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.

This is a faithful port of #3634: the same _ValueSlot threading, the same Event keyword handling, and the decompiler puts userMessage= in the same place. It also fixes Python so that a function call inside an event context stays a plain call, as in TS. The Express tests pass on the branch (169 passed).

One bug carried over from the TS PR and two smaller points, inline:

Must address

  1. userMessage rejects function calls, which v1.0 allows (DynamicString), so a decompile and recompile round trip fails. This is the same bug as in #3634.

Should address
2. Python still treats a property named action or submitAction as an action slot whatever its schema says. TS doesn't.
3. The error for a bad userMessage shows a Python repr to the model.

Comment thread python/a2ui_agent/src/a2ui/inference_formats/express/compiler.py
Comment thread python/a2ui_agent/src/a2ui/inference_formats/express/compiler.py Outdated
Comment thread python/a2ui_agent/src/a2ui/inference_formats/express/compiler.py Outdated

@nan-yu nan-yu left a comment

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.

LGTM

@ditman
ditman force-pushed the fix/py-express-actions branch from 2365a32 to 0b20162 Compare October 28, 2026 21:27
…xpress

`Event("reply", userMessage="...")` silently dropped `userMessage`, and a
function call used as the action of a list item (for example
`Links([{label: "Docs", action: openUrl("...")}])`) compiled to a bare
`{"@call": ...}` that the processor rejects.

- `Event` now accepts `userMessage=` and `context=` keyword arguments.
  `userMessage` compiles to the v1.0 `event.userMessage` field and raises
  `ExpressValidationError` before v1.0; any other keyword on `Event` is
  rejected rather than dropped.
- `_compile_value` carries the catalog schema at the current position, so an
  `Action`-typed sub-schema inside array items or object properties wraps
  function calls in `functionCall` like a top-level action slot does.
  `CatalogSchemaHelper` gains `resolve_subschema` and the shared
  `is_action_slot` helper to walk the schema.
- `ExpressDecompiler` writes `userMessage=` back so events round-trip.

Fixes points 1 and 2 of #3598 for the Python SDK.
The event's name, userMessage and context entries are DynamicValues, so
a function call among them is a plain call. The action flag of the slot
the event sits in no longer reaches them, which also matches the Dart
compiler.
Action.event.userMessage is a DynamicString: a string, a data binding or
a function call. The compiler accepted only the first two, so an event
the decompiler wrote with a call did not compile back. The error for any
other value now names what the model wrote instead of printing the node.

Whether a property is an action slot now comes from its catalog schema
alone, as in TypeScript; the fallback on the property names `action` and
`submitAction` is gone, and the inline test catalogs that relied on it
declare the common `Action` type.
resolve_subschema took the key "items" to mean the array items, so an
object property named items was never looked up among the properties.
It is now two methods, resolve_item_schema and resolve_property_schema,
and the item slot is resolved once per list rather than once per element.

Before v1.0 the decompiler writes an event carrying a userMessage as a
map literal, since that version's compiler rejects userMessage= and the
literal compiles back as written.
@ditman
ditman force-pushed the fix/py-express-actions branch from 0b20162 to 92e225a Compare October 28, 2026 22:01
@ditman
ditman merged commit dbe96cf into main Oct 28, 2026
105 of 145 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in A2UI Oct 28, 2026
@ditman
ditman deleted the fix/py-express-actions branch October 28, 2026 22:52
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