a2ui-project / a2ui-project/a2ui
[BUG]: Python's DataModel fails 7 of the 37 shared data model conformance cases
- Langage dominant
- TypeScript
- Étoiles
- 16.4k
- Forks
- 1.3k
- Merge moyen
- 2 j 13 h
- PR mergées (30 j)
- 134
Description
- [x] I have searched the existing issues to make sure this bug has not already been reported.
## Describe the Bug
`conformance/core/data_model.yaml` is the one dataset every implementation of the data model is measured against. The Dart client and `web_core` both run it. The Python client does not — `agent_sdks/python/a2ui_core` is measured solely by its own `tests/test_state.py` — and it fails 7 of the suite's 37 cases.
Four distinct defects, all of them already spelled out in the suite:
| # | Python | Dart and `web_core` | Suite case |
| --- | --- | --- | --- |
| 1 | `/user/name` holds a string; `set('/user/name/first', 'Alice')` **replaces that string** with `{"first": "Alice"}` and reports nothing | `A2uiDataError` | `test_data_model_rejects_write_through_primitive`, `..._list_primitive` |
| 2 | `set('/items/foo', 'bar')` raises a bare `ValueError` | `A2uiDataError` | `test_data_model_rejects_non_numeric_list_segment`, `..._intermediate` |
| 3 | `set('/foo/', 'bar')` stores `{"foo": {"": "bar"}}`, so `get('/foo')` does not return what was just written to it | `/foo/` and `/foo` are the same path | `test_data_model_normalizes_trailing_slash` |
| 4 | An observer of `/a/b` is notified whenever anything under `/a` moves, and rewriting a value with itself notifies everyone | notified only when the watched value changes | `test_data_model_does_not_notify_unchanged_descendant`, `..._same_value_rewrite` |
The first is the one that loses data. Auto-vivification treats anything that is not a `dict` or a `list` as free to overwrite:
```python
if token not in current or not isinstance(current[token], (dict, list)):
current[token] = [] if is_next_numeric else {}
```
so a malformed path from the agent does not fail — it silently deletes whatever the write passed through. `web_core` was made to reject the same write in #2499, and Dart does in #2439; Python is the one left.
The second matters less in isolation, but `ValueError` is outside the `A2uiError` hierarchy, so a consumer that catches `A2uiError` around a data model write catches the list errors on Dart and web and misses them on Python.
## Steps to Reproduce
```python
from a2ui.core.state import DataModel
model = DataModel({"user": {"name": "Alice"}})
model.set("/user/name/first", "Bob")
print(model.get("/")) # {'user': {'name': {'first': 'Bob'}}} — "Alice" is gone
```
The same write on Dart and on `web_core` raises `A2uiDataError`.
## Expected Behavior
Python behaves as the suite already says, on all 37 cases: only an absent or null segment is filled in, a primitive in the way is an error, every rejected write raises inside the `A2uiError` hierarchy, `/foo/` addresses `/foo`, and an observer hears about a path only when its value changes.
## Environment Details
- **OS**: macOS 15
- **Browser/Platform**: Python 3.14
- **SDK/Package Name & Version**: `a2ui-core` (Python) at `main`
- **Protocol Version**: v0.9
## Additional Context
The suite's own header already excludes what cannot hold across languages — prototype pollution guards, the `undefined` distinction, leading-zero list indices, the auto-vivify index cap — so none of these seven are Python being asked to imitate a detail of another runtime.
Sending a PR that adds the harness and fixes all seven.
Guide de contribution
Ouvrir le guide de contribution
Évaluation
Cette issue n'a pas encore été évaluée.