dyui 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dyui-0.1.0/.gitignore +19 -0
- dyui-0.1.0/LICENSE +21 -0
- dyui-0.1.0/PKG-INFO +143 -0
- dyui-0.1.0/README.md +108 -0
- dyui-0.1.0/conftest.py +6 -0
- dyui-0.1.0/dyui/__init__.py +61 -0
- dyui-0.1.0/dyui/__version__.py +1 -0
- dyui-0.1.0/dyui/cli.py +347 -0
- dyui-0.1.0/dyui/emit.py +290 -0
- dyui-0.1.0/dyui/events.py +86 -0
- dyui-0.1.0/dyui/scaffold.py +275 -0
- dyui-0.1.0/dyui/server.py +198 -0
- dyui-0.1.0/dyui/static/index.html +375 -0
- dyui-0.1.0/dyui/stream.py +106 -0
- dyui-0.1.0/dyui/templates.py +105 -0
- dyui-0.1.0/examples/__init__.py +0 -0
- dyui-0.1.0/examples/agent.py +131 -0
- dyui-0.1.0/examples/cards/invoice.html +11 -0
- dyui-0.1.0/examples/cards/weather.html +9 -0
- dyui-0.1.0/examples/html_agent.py +98 -0
- dyui-0.1.0/pyproject.toml +55 -0
- dyui-0.1.0/tests/test_cli.py +83 -0
- dyui-0.1.0/tests/test_emit.py +106 -0
- dyui-0.1.0/tests/test_events.py +28 -0
- dyui-0.1.0/tests/test_server.py +61 -0
- dyui-0.1.0/tests/test_templates_and_ui.py +77 -0
dyui-0.1.0/.gitignore
ADDED
dyui-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Soham Das
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
dyui-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dyui
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A dynamic-UI layer for any LangGraph agent: emit live UI cards from any node or tool, with any LLM.
|
|
5
|
+
Project-URL: Homepage, https://github.com/sohammmzzz/dyui
|
|
6
|
+
Project-URL: Repository, https://github.com/sohammmzzz/dyui
|
|
7
|
+
Project-URL: Issues, https://github.com/sohammmzzz/dyui/issues
|
|
8
|
+
Author-email: Soham Das <sohamdas1300@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,dynamic-ui,fastapi,generative-ui,langgraph,llm,sse,ui
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: langgraph>=0.2.40
|
|
25
|
+
Requires-Dist: pydantic>=2
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: fastapi>=0.110; extra == 'dev'
|
|
28
|
+
Requires-Dist: httpx; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
30
|
+
Requires-Dist: uvicorn; extra == 'dev'
|
|
31
|
+
Provides-Extra: server
|
|
32
|
+
Requires-Dist: fastapi>=0.110; extra == 'server'
|
|
33
|
+
Requires-Dist: uvicorn>=0.30; extra == 'server'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# dyui
|
|
37
|
+
|
|
38
|
+
A **dynamic-UI layer for any LangGraph agent.** Emit rich, live UI "cards" from
|
|
39
|
+
inside any LangGraph node or tool — with any LLM, or none — and render them on
|
|
40
|
+
the frontend with the companion [`@dyui/react`](../react) package.
|
|
41
|
+
|
|
42
|
+
DyUI rides LangGraph's first-class `custom` stream channel, so it is
|
|
43
|
+
transport-, model-, and framework-agnostic. No voice, no vendor lock-in.
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install dyui # core (langgraph + pydantic)
|
|
49
|
+
pip install "dyui[server]" # + FastAPI SSE server
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Emit cards from your agent
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from dyui import emit, ui_tool, Card
|
|
56
|
+
|
|
57
|
+
# 1. Imperative — call anywhere inside a node:
|
|
58
|
+
emit("table", {"columns": ["city", "temp"], "rows": [["Berlin", 21]]},
|
|
59
|
+
title="Weather", icon="cloud", accent="cyan")
|
|
60
|
+
|
|
61
|
+
# 2. Decorator — auto pending -> done/error around a tool:
|
|
62
|
+
@ui_tool("stat", title="Sum", icon="plus", accent="emerald")
|
|
63
|
+
def add(a: float, b: float) -> dict:
|
|
64
|
+
return {"label": f"{a} + {b}", "value": a + b}
|
|
65
|
+
|
|
66
|
+
# 3. Context manager — long-running work, one card updated in place:
|
|
67
|
+
with Card("progress", title="Indexing") as card:
|
|
68
|
+
for i in range(1, 4):
|
|
69
|
+
card.progress({"value": i, "max": 3})
|
|
70
|
+
card.done({"value": 3, "max": 3, "label": "Done"})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Each event carries an `id` (auto or yours), a `component` key (which card to
|
|
74
|
+
render), `props` (the data), a `status` (`pending`/`active`/`done`/`error`), and
|
|
75
|
+
an optional `surface` (which screen region hosts it).
|
|
76
|
+
|
|
77
|
+
## Zero frontend: serve a UI from Python
|
|
78
|
+
|
|
79
|
+
You don't need React or npm to get a working screen. `create_dyui_app` hosts a
|
|
80
|
+
built-in single-file UI at `GET /` that renders every built-in card **and your
|
|
81
|
+
raw HTML cards**:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from dyui.server import create_dyui_app
|
|
85
|
+
|
|
86
|
+
app = create_dyui_app(my_compiled_graph) # uvicorn my_module:app --port 8008
|
|
87
|
+
# open http://localhost:8008 -> a live dynamic-UI screen, no frontend code
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Use your own HTML files as cards
|
|
91
|
+
|
|
92
|
+
Drop `.html` templates in a folder and emit them by name — placeholders are
|
|
93
|
+
filled from props (`{{ key }}` escaped, `{{{ key }}}` raw):
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from dyui import HtmlTemplates
|
|
97
|
+
|
|
98
|
+
cards = HtmlTemplates("cards") # ./cards/invoice.html, weather.html, ...
|
|
99
|
+
|
|
100
|
+
def my_node(state):
|
|
101
|
+
cards.emit("invoice",
|
|
102
|
+
{"number": 42, "total": "$1,200", "customer": "Acme"},
|
|
103
|
+
title="Invoice", accent="emerald")
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
That's the whole "one Python file + HTML templates → dynamic UI" path. See
|
|
107
|
+
[`examples/html_agent.py`](examples/html_agent.py) and [`examples/cards/`](examples/cards).
|
|
108
|
+
|
|
109
|
+
> The served UI renders the built-in card types and `html` cards. Bespoke
|
|
110
|
+
> *React* card components live in `@dyui/react`; for the no-frontend path, model
|
|
111
|
+
> custom cards as HTML templates instead.
|
|
112
|
+
|
|
113
|
+
## Connect a React frontend (optional)
|
|
114
|
+
|
|
115
|
+
`POST /dyui/stream` is a plain SSE endpoint the `@dyui/react` `useDyUIAgent` hook
|
|
116
|
+
consumes directly. Or mount onto an existing app:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from dyui.server import add_dyui_routes
|
|
120
|
+
add_dyui_routes(existing_fastapi_app, my_graph, path="/agent/stream")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Test / consume without HTTP
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from dyui import collect_events
|
|
127
|
+
for ev in collect_events(my_graph, {"query": "berlin"}):
|
|
128
|
+
print(ev["status"], ev["component"], ev["props"])
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Built-in card components (frontend)
|
|
132
|
+
|
|
133
|
+
`text`, `markdown`, `table`, `stat`, `progress`, `list`, `keyvalue`, `json`,
|
|
134
|
+
`alert`, `image`, and `html` (sanitized custom markup). Register your own React
|
|
135
|
+
components for any `component` key to fully customise — see the React package.
|
|
136
|
+
|
|
137
|
+
## Development
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
python -m venv .venv && . .venv/Scripts/activate # Windows
|
|
141
|
+
pip install -e ".[dev]"
|
|
142
|
+
pytest
|
|
143
|
+
```
|
dyui-0.1.0/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# dyui
|
|
2
|
+
|
|
3
|
+
A **dynamic-UI layer for any LangGraph agent.** Emit rich, live UI "cards" from
|
|
4
|
+
inside any LangGraph node or tool — with any LLM, or none — and render them on
|
|
5
|
+
the frontend with the companion [`@dyui/react`](../react) package.
|
|
6
|
+
|
|
7
|
+
DyUI rides LangGraph's first-class `custom` stream channel, so it is
|
|
8
|
+
transport-, model-, and framework-agnostic. No voice, no vendor lock-in.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install dyui # core (langgraph + pydantic)
|
|
14
|
+
pip install "dyui[server]" # + FastAPI SSE server
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Emit cards from your agent
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from dyui import emit, ui_tool, Card
|
|
21
|
+
|
|
22
|
+
# 1. Imperative — call anywhere inside a node:
|
|
23
|
+
emit("table", {"columns": ["city", "temp"], "rows": [["Berlin", 21]]},
|
|
24
|
+
title="Weather", icon="cloud", accent="cyan")
|
|
25
|
+
|
|
26
|
+
# 2. Decorator — auto pending -> done/error around a tool:
|
|
27
|
+
@ui_tool("stat", title="Sum", icon="plus", accent="emerald")
|
|
28
|
+
def add(a: float, b: float) -> dict:
|
|
29
|
+
return {"label": f"{a} + {b}", "value": a + b}
|
|
30
|
+
|
|
31
|
+
# 3. Context manager — long-running work, one card updated in place:
|
|
32
|
+
with Card("progress", title="Indexing") as card:
|
|
33
|
+
for i in range(1, 4):
|
|
34
|
+
card.progress({"value": i, "max": 3})
|
|
35
|
+
card.done({"value": 3, "max": 3, "label": "Done"})
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Each event carries an `id` (auto or yours), a `component` key (which card to
|
|
39
|
+
render), `props` (the data), a `status` (`pending`/`active`/`done`/`error`), and
|
|
40
|
+
an optional `surface` (which screen region hosts it).
|
|
41
|
+
|
|
42
|
+
## Zero frontend: serve a UI from Python
|
|
43
|
+
|
|
44
|
+
You don't need React or npm to get a working screen. `create_dyui_app` hosts a
|
|
45
|
+
built-in single-file UI at `GET /` that renders every built-in card **and your
|
|
46
|
+
raw HTML cards**:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from dyui.server import create_dyui_app
|
|
50
|
+
|
|
51
|
+
app = create_dyui_app(my_compiled_graph) # uvicorn my_module:app --port 8008
|
|
52
|
+
# open http://localhost:8008 -> a live dynamic-UI screen, no frontend code
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Use your own HTML files as cards
|
|
56
|
+
|
|
57
|
+
Drop `.html` templates in a folder and emit them by name — placeholders are
|
|
58
|
+
filled from props (`{{ key }}` escaped, `{{{ key }}}` raw):
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from dyui import HtmlTemplates
|
|
62
|
+
|
|
63
|
+
cards = HtmlTemplates("cards") # ./cards/invoice.html, weather.html, ...
|
|
64
|
+
|
|
65
|
+
def my_node(state):
|
|
66
|
+
cards.emit("invoice",
|
|
67
|
+
{"number": 42, "total": "$1,200", "customer": "Acme"},
|
|
68
|
+
title="Invoice", accent="emerald")
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
That's the whole "one Python file + HTML templates → dynamic UI" path. See
|
|
72
|
+
[`examples/html_agent.py`](examples/html_agent.py) and [`examples/cards/`](examples/cards).
|
|
73
|
+
|
|
74
|
+
> The served UI renders the built-in card types and `html` cards. Bespoke
|
|
75
|
+
> *React* card components live in `@dyui/react`; for the no-frontend path, model
|
|
76
|
+
> custom cards as HTML templates instead.
|
|
77
|
+
|
|
78
|
+
## Connect a React frontend (optional)
|
|
79
|
+
|
|
80
|
+
`POST /dyui/stream` is a plain SSE endpoint the `@dyui/react` `useDyUIAgent` hook
|
|
81
|
+
consumes directly. Or mount onto an existing app:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from dyui.server import add_dyui_routes
|
|
85
|
+
add_dyui_routes(existing_fastapi_app, my_graph, path="/agent/stream")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Test / consume without HTTP
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from dyui import collect_events
|
|
92
|
+
for ev in collect_events(my_graph, {"query": "berlin"}):
|
|
93
|
+
print(ev["status"], ev["component"], ev["props"])
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Built-in card components (frontend)
|
|
97
|
+
|
|
98
|
+
`text`, `markdown`, `table`, `stat`, `progress`, `list`, `keyvalue`, `json`,
|
|
99
|
+
`alert`, `image`, and `html` (sanitized custom markup). Register your own React
|
|
100
|
+
components for any `component` key to fully customise — see the React package.
|
|
101
|
+
|
|
102
|
+
## Development
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
python -m venv .venv && . .venv/Scripts/activate # Windows
|
|
106
|
+
pip install -e ".[dev]"
|
|
107
|
+
pytest
|
|
108
|
+
```
|
dyui-0.1.0/conftest.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""DyUI -- a dynamic-UI layer for any LangGraph agent.
|
|
2
|
+
|
|
3
|
+
Emit rich, live UI "cards" from inside any LangGraph node or tool, with any LLM
|
|
4
|
+
(or none). The companion ``@dyui/react`` package renders those cards on the
|
|
5
|
+
frontend via a pluggable registry that also supports raw custom HTML.
|
|
6
|
+
|
|
7
|
+
Quick start (agent side)::
|
|
8
|
+
|
|
9
|
+
from dyui import emit, ui_tool
|
|
10
|
+
|
|
11
|
+
@ui_tool("stat", title="Weather", icon="cloud", accent="cyan")
|
|
12
|
+
def get_weather(city: str) -> dict:
|
|
13
|
+
return {"label": city, "value": 21, "unit": "C"}
|
|
14
|
+
|
|
15
|
+
# or imperatively, anywhere inside a node:
|
|
16
|
+
emit("table", {"columns": ["a", "b"], "rows": [[1, 2]]}, title="Results")
|
|
17
|
+
|
|
18
|
+
Quick start (server side)::
|
|
19
|
+
|
|
20
|
+
from dyui.server import create_dyui_app
|
|
21
|
+
app = create_dyui_app(my_compiled_graph) # uvicorn dyui_app:app
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from .__version__ import __version__
|
|
25
|
+
from .emit import Card, emit, ui_tool, update
|
|
26
|
+
from .events import DYUI_KEY, Status, UIEvent, is_dyui_payload, parse_envelope
|
|
27
|
+
from .stream import astream_ui, collect, collect_events
|
|
28
|
+
from .templates import HtmlTemplates, render_template
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"__version__",
|
|
32
|
+
"emit",
|
|
33
|
+
"update",
|
|
34
|
+
"ui_tool",
|
|
35
|
+
"Card",
|
|
36
|
+
"UIEvent",
|
|
37
|
+
"Status",
|
|
38
|
+
"DYUI_KEY",
|
|
39
|
+
"is_dyui_payload",
|
|
40
|
+
"parse_envelope",
|
|
41
|
+
"astream_ui",
|
|
42
|
+
"collect",
|
|
43
|
+
"collect_events",
|
|
44
|
+
"HtmlTemplates",
|
|
45
|
+
"render_template",
|
|
46
|
+
# lazily provided (need FastAPI):
|
|
47
|
+
"create_dyui_app",
|
|
48
|
+
"add_dyui_routes",
|
|
49
|
+
"mount_ui",
|
|
50
|
+
"render_ui_page",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def __getattr__(name: str):
|
|
55
|
+
# Lazily expose the FastAPI helpers so importing ``dyui`` never hard-requires
|
|
56
|
+
# FastAPI (it's an optional ``[server]`` extra).
|
|
57
|
+
if name in {"create_dyui_app", "add_dyui_routes", "mount_ui", "render_ui_page"}:
|
|
58
|
+
from . import server
|
|
59
|
+
|
|
60
|
+
return getattr(server, name)
|
|
61
|
+
raise AttributeError(f"module 'dyui' has no attribute {name!r}")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|