gentui 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.
- gentui-0.1.0/LICENSE +21 -0
- gentui-0.1.0/PKG-INFO +293 -0
- gentui-0.1.0/README.md +262 -0
- gentui-0.1.0/pyproject.toml +69 -0
- gentui-0.1.0/pyproject.toml.orig +58 -0
- gentui-0.1.0/src/gentui/__init__.py +1 -0
- gentui-0.1.0/src/gentui/cli.py +102 -0
- gentui-0.1.0/src/gentui/config.py +148 -0
- gentui-0.1.0/src/gentui/plugins.py +136 -0
- gentui-0.1.0/src/gentui/tui/__init__.py +0 -0
- gentui-0.1.0/src/gentui/tui/agentcore_client.py +182 -0
- gentui-0.1.0/src/gentui/tui/agui_client.py +119 -0
- gentui-0.1.0/src/gentui/tui/app.py +403 -0
- gentui-0.1.0/src/gentui/tui/branding.py +87 -0
- gentui-0.1.0/src/gentui/tui/commands.py +43 -0
- gentui-0.1.0/src/gentui/tui/splash.py +60 -0
- gentui-0.1.0/src/gentui/tui/widgets/__init__.py +3 -0
- gentui-0.1.0/src/gentui/tui/widgets/base.py +94 -0
- gentui-0.1.0/src/gentui/tui/widgets/chart.py +109 -0
- gentui-0.1.0/src/gentui/tui/widgets/command.py +143 -0
- gentui-0.1.0/src/gentui/tui/widgets/generic.py +80 -0
- gentui-0.1.0/src/gentui/tui/widgets/interrupt.py +65 -0
- gentui-0.1.0/src/gentui/tui/widgets/output.py +57 -0
- gentui-0.1.0/src/gentui/tui/widgets/plan.py +33 -0
- gentui-0.1.0/src/gentui/tui/widgets/registry.py +37 -0
- gentui-0.1.0/src/gentui/tui/widgets/table.py +34 -0
gentui-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gentui contributors
|
|
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.
|
gentui-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gentui
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A terminal interface for prototyping agents really quickly, for any AG-UI backend
|
|
5
|
+
Keywords: ag-ui,agents,ai-agents,tui,terminal,textual,llm,human-in-the-loop,strands,agentcore
|
|
6
|
+
Author: rahrajlat
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Software Development
|
|
16
|
+
Classifier: Topic :: Terminals
|
|
17
|
+
Requires-Dist: ag-ui-protocol>=1.0.0
|
|
18
|
+
Requires-Dist: httpx>=0.28.1
|
|
19
|
+
Requires-Dist: jsonpatch>=1.33
|
|
20
|
+
Requires-Dist: textual>=8.2.8
|
|
21
|
+
Requires-Dist: textual-plotext>=1.0.1
|
|
22
|
+
Requires-Dist: boto3>=1.40 ; extra == 'agentcore'
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Project-URL: Homepage, https://github.com/rahrajlat/Gentui
|
|
25
|
+
Project-URL: Repository, https://github.com/rahrajlat/Gentui
|
|
26
|
+
Project-URL: Documentation, https://github.com/rahrajlat/Gentui#readme
|
|
27
|
+
Project-URL: Issues, https://github.com/rahrajlat/Gentui/issues
|
|
28
|
+
Project-URL: Changelog, https://github.com/rahrajlat/Gentui/blob/main/CHANGELOG.md
|
|
29
|
+
Provides-Extra: agentcore
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
<div align="center">
|
|
33
|
+
|
|
34
|
+
<img src="https://raw.githubusercontent.com/rahrajlat/Gentui/main/docs/assets/hero.gif" alt="Gentui: generative UI for your terminal" width="640">
|
|
35
|
+
|
|
36
|
+
**A terminal interface for prototyping agents really quickly.**<br>
|
|
37
|
+
Point it at any [AG-UI](https://docs.ag-ui.com) backend and get streaming chat, the model's chain of thought,<br>
|
|
38
|
+
tables, charts and human approval, with no frontend to build.
|
|
39
|
+
|
|
40
|
+
[](https://github.com/rahrajlat/Gentui/actions/workflows/ci.yml)
|
|
41
|
+
[](https://www.python.org)
|
|
42
|
+
[](https://textual.textualize.io)
|
|
43
|
+
[](https://docs.ag-ui.com)
|
|
44
|
+
[-FF9900?style=flat-square)](https://strandsagents.com)
|
|
45
|
+
[](https://docs.astral.sh/uv/)
|
|
46
|
+
[](https://github.com/rahrajlat/Gentui/blob/main/LICENSE)
|
|
47
|
+
[](#status)
|
|
48
|
+
[](#contributing)
|
|
49
|
+
[](https://github.com/rahrajlat/Gentui/stargazers)
|
|
50
|
+
[](https://github.com/rahrajlat/Gentui/commits/main)
|
|
51
|
+
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Why Gentui?
|
|
57
|
+
|
|
58
|
+
**Gentui is a terminal interface for prototyping agents really quickly.**
|
|
59
|
+
|
|
60
|
+
While developing agents locally, we end up spending our time on the frontend: a Streamlit or Chainlit
|
|
61
|
+
app, or a React project, just to talk to the agent. Each one is a separate frontend to build and keep
|
|
62
|
+
running, and it slows down the thing you actually want to iterate on: the agent.
|
|
63
|
+
|
|
64
|
+
Gentui removes that step. Install it, point it at your agent's AG-UI endpoint, and you get a chat, the
|
|
65
|
+
model's **chain of thought**, **human-in-the-loop (HITL)** and **approval** flows out of the box, with no
|
|
66
|
+
frontend code to write. Change your agent, restart it, and you're prototyping again in seconds.
|
|
67
|
+
|
|
68
|
+
When the agent calls a tool, the **tool call becomes the UI**: a command to approve, a table, a chart,
|
|
69
|
+
a live plan. Nothing risky runs until you click **Approve**.
|
|
70
|
+
|
|
71
|
+
**Tested with the [Strands Agents](https://strandsagents.com) framework (AWS's open-source agent SDK) over
|
|
72
|
+
AG-UI**: both on a custom backend (the [example backend](https://github.com/rahrajlat/Gentui/tree/main/examples/strands-backend)) and on the official
|
|
73
|
+
[`ag-ui-strands`](https://pypi.org/project/ag-ui-strands/) adapter.
|
|
74
|
+
|
|
75
|
+
<div align="center">
|
|
76
|
+
<img src="https://raw.githubusercontent.com/rahrajlat/Gentui/main/docs/assets/demo.gif" alt="Gentui demo: ask, review the proposed command, approve, see the output, chart it" width="860">
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
## Features
|
|
80
|
+
|
|
81
|
+
- **Works with any AG-UI backend.** Streaming text, tool calls and shared state work out of the box;
|
|
82
|
+
unknown tools show as a readable card, never an error.
|
|
83
|
+
- **Chain of thought, visible.** Reasoning streams into a collapsible "Thought for 3s" block.
|
|
84
|
+
- **Human-in-the-loop approval** built on AG-UI **interrupts**: the run ends waiting, your click
|
|
85
|
+
`resume`s it. The model can't approve its own commands.
|
|
86
|
+
- **Generative widgets from tool calls:** command card, command output, tables, terminal charts
|
|
87
|
+
(line, bar, scatter, histogram), live plan checklist.
|
|
88
|
+
- **Developer-friendly:** an event inspector (`d`) showing the raw SSE payloads exactly as sent, `/theme`,
|
|
89
|
+
`/clear`, `/reasoning`, and a clear [backend contract](https://github.com/rahrajlat/Gentui/blob/main/docs/tool-contract.md).
|
|
90
|
+
- **Yours to customise:** TOML config, hot-reloaded CSS, your own themes, and Python plugins that add
|
|
91
|
+
widgets, slash commands and event hooks.
|
|
92
|
+
- **Runs agents on AWS too:** invoke agents hosted on Amazon Bedrock AgentCore Runtime (AG-UI protocol) via boto3.
|
|
93
|
+
- **Backend-agnostic by design.** The client has no framework code; a complete example backend lives in
|
|
94
|
+
[`examples/strands-backend`](https://github.com/rahrajlat/Gentui/tree/main/examples/strands-backend).
|
|
95
|
+
|
|
96
|
+
## The sample agent: natural language → shell
|
|
97
|
+
|
|
98
|
+
The demo above is the bundled example backend, a **natural-language-to-shell agent** built on Strands
|
|
99
|
+
Agents. You describe a task in plain English, it proposes a single shell command, and **nothing runs
|
|
100
|
+
until you approve it**.
|
|
101
|
+
|
|
102
|
+
1. **Ask:** "what are the 5 largest files here?"
|
|
103
|
+
2. **Review:** a shell sub-agent writes one command for your shell (bash, or PowerShell on Windows)
|
|
104
|
+
with a one-line explanation and a risk level: `safe`, `caution` or `dangerous`.
|
|
105
|
+
3. **Decide:** **Approve**, **Edit** the command first, or **Reject**. Your decision is sent back as an
|
|
106
|
+
AG-UI `resume`; the model can't approve for itself or change the command.
|
|
107
|
+
4. **Run:** only the approved command executes, with a timeout, an output cap and a fixed working
|
|
108
|
+
directory. A denylist (recursive delete of root or home, disk formatting, shutdown, fork bombs,
|
|
109
|
+
interactive programs) is checked at proposal, at approval and again right before running. It is a
|
|
110
|
+
safety net, not a sandbox.
|
|
111
|
+
|
|
112
|
+
The same agent can also show results as a **table** or **chart**, lay out multi-step work as a live
|
|
113
|
+
**plan**, explain what a command does (an explainer sub-agent), and keep **long-term memory** between
|
|
114
|
+
conversations. It's a worked example of the [backend contract](https://github.com/rahrajlat/Gentui/blob/main/docs/tool-contract.md): copy it, or add your
|
|
115
|
+
own tools with [this guide](https://github.com/rahrajlat/Gentui/blob/main/examples/strands-backend/docs/add_a_tool.md).
|
|
116
|
+
|
|
117
|
+
## Quick start
|
|
118
|
+
|
|
119
|
+
Requires [uv](https://docs.astral.sh/uv/) and Python 3.12.
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
git clone https://github.com/rahrajlat/Gentui.git && cd Gentui
|
|
123
|
+
uv sync
|
|
124
|
+
uv run gentui http://localhost:8000/agent # your AG-UI endpoint
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
No backend yet? Run the [sample natural-language-to-shell agent](#the-sample-agent-natural-language--shell)
|
|
128
|
+
(Strands Agents + FastAPI) in another terminal. It uses a local
|
|
129
|
+
[Ollama](https://ollama.com) by default; see its [README](https://github.com/rahrajlat/Gentui/blob/main/examples/strands-backend/README.md) for other providers:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
cd examples/strands-backend
|
|
133
|
+
uv sync && cp .env.example .env
|
|
134
|
+
uv run server # http://localhost:8000/agent
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
uv run gentui https://my.host/agent --token sk-... # bearer auth
|
|
139
|
+
uv run gentui URL -H "X-Org: acme" --theme nord --dev # extra header, theme, event inspector
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
| Key / command | Does |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `Enter` | send |
|
|
145
|
+
| `d` or `Ctrl+D` | toggle the event inspector |
|
|
146
|
+
| `Ctrl+Q` | quit |
|
|
147
|
+
| `/help` `/theme <name>` `/clear` `/reasoning` `/dev` `/quit` | slash commands |
|
|
148
|
+
|
|
149
|
+
## Agents on Amazon Bedrock AgentCore Runtime
|
|
150
|
+
|
|
151
|
+
Gentui can also invoke an agent hosted on **[AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/what-is-bedrock-agentcore.html)**
|
|
152
|
+
with boto3's `invoke_agent_runtime`, using your normal AWS credentials. The runtime must be deployed with the
|
|
153
|
+
**AG-UI protocol**.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
uv sync --extra agentcore # boto3 is an optional extra
|
|
157
|
+
uv run gentui arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/my_agent-AbCdEfGhIj
|
|
158
|
+
uv run gentui ARN --profile dev --region eu-west-1 # optional: AWS profile and region
|
|
159
|
+
uv run gentui ARN --qualifier prod # optional: a specific runtime endpoint
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`--profile` and `--region` are both **optional**. Without them, Gentui uses the standard AWS credential chain
|
|
163
|
+
(`AWS_PROFILE`, SSO, environment variables) and the region in the ARN. They can also be set in `gentui.toml` as
|
|
164
|
+
`aws_profile` and `region`.
|
|
165
|
+
|
|
166
|
+
One conversation is one runtime session, errors come with fixes (missing credentials, denied access, wrong ARN), and
|
|
167
|
+
busy sessions are retried. Details, IAM permissions and troubleshooting: **[docs/agentcore.md](https://github.com/rahrajlat/Gentui/blob/main/docs/agentcore.md)**.
|
|
168
|
+
Both a runtime ARN and an endpoint ARN (`.../runtime-endpoint/DEFAULT`) work.
|
|
169
|
+
|
|
170
|
+
## Configure
|
|
171
|
+
|
|
172
|
+
Put options in `./gentui.toml` or `~/.config/gentui/config.toml`. Flags, `GENTUI_URL` and
|
|
173
|
+
`GENTUI_TOKEN` override the file. Every option is documented in
|
|
174
|
+
[`gentui.example.toml`](https://github.com/rahrajlat/Gentui/blob/main/gentui.example.toml): backend URL, token and headers, props sent with every run,
|
|
175
|
+
title, welcome text, theme, splash and logo, reasoning on/off, timestamps, plugins and widget mapping.
|
|
176
|
+
|
|
177
|
+
> **Stateless backends** (ones that rebuild context from the message list, like the official
|
|
178
|
+
> `ag-ui-strands` adapter) need `send_history = true`. By default only the newest message is sent and
|
|
179
|
+
> the backend is expected to keep history per `threadId`.
|
|
180
|
+
|
|
181
|
+
## Customise
|
|
182
|
+
|
|
183
|
+
- **Theme:** `theme = "nord"` or `/theme <name>`. The default is `gentui` (teal and violet); `claude`
|
|
184
|
+
(coral) is also built in.
|
|
185
|
+
- **Your own styling:** `css = "my.tcss"` loads a [Textual CSS](https://textual.textualize.io/guide/CSS/)
|
|
186
|
+
file on top of the defaults and **hot-reloads while the app runs**. Useful selectors: `.user`,
|
|
187
|
+
`.assistant`, `.thinking`, `.error`, `ToolWidget`, `#chat`, `#prompt`.
|
|
188
|
+
- **Your own widget for a backend tool:** subclass `ToolWidget`, then map it without any plugin file:
|
|
189
|
+
|
|
190
|
+
```toml
|
|
191
|
+
[widgets]
|
|
192
|
+
show_map = "my_widgets:MapWidget"
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Plugins
|
|
196
|
+
|
|
197
|
+
A plugin is a plain Python file. Drop it in `./gentui_plugins/` or `~/.config/gentui/plugins/`, list it in
|
|
198
|
+
`plugins = [...]` or `--plugin`, or ship it as a pip package using the `gentui.plugins` entry point.
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from gentui.plugins import on_event, register_command
|
|
202
|
+
from gentui.tui.widgets.base import ToolWidget
|
|
203
|
+
from gentui.tui.widgets.registry import register_widget
|
|
204
|
+
|
|
205
|
+
@register_widget("weather") # render tool calls named "weather"
|
|
206
|
+
class Weather(ToolWidget):
|
|
207
|
+
def on_end(self, args): self.show(f"☀ {args['city']}")
|
|
208
|
+
|
|
209
|
+
@register_command("ping", "say pong") # adds /ping
|
|
210
|
+
def ping(app, args): app.notify("pong")
|
|
211
|
+
|
|
212
|
+
@on_event("TOOL_CALL_RESULT") # hook any AG-UI event
|
|
213
|
+
async def audit(app, event): ...
|
|
214
|
+
|
|
215
|
+
def setup(app): ... # optional, runs once the app is mounted
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Plugins can mount any Textual widget into the conversation with `await app.mount_chat(widget)`. A
|
|
219
|
+
broken plugin is reported in a toast and never stops the app.
|
|
220
|
+
|
|
221
|
+
## Build a backend for it
|
|
222
|
+
|
|
223
|
+
Any language works: serve a `POST` endpoint that streams AG-UI events. To get the rich widgets, name
|
|
224
|
+
your tools like this (anything else shows a JSON card). The full contract, including the approval
|
|
225
|
+
flow and a checklist, is in **[docs/tool-contract.md](https://github.com/rahrajlat/Gentui/blob/main/docs/tool-contract.md)**.
|
|
226
|
+
|
|
227
|
+
| Tool name | Arguments / result | Renders |
|
|
228
|
+
|---|---|---|
|
|
229
|
+
| `propose_command` | `{command, explanation, risk: safe\|caution\|dangerous}` | command card with Approve / Edit / Reject; the decision goes back as an AG-UI `resume` of the run's interrupt |
|
|
230
|
+
| `run_command` | result JSON `{command, exit_code, timed_out, truncated, output}` | output card |
|
|
231
|
+
| `show_table` | `{title, columns, rows}` | table |
|
|
232
|
+
| `show_chart` | `{type: line\|bar\|scatter\|histogram, title, x, series: [{name, values}], x_label, y_label, bins}` | terminal chart (plotext) |
|
|
233
|
+
| `search_memory` | `{query}` | quiet "◌ recalled …" line |
|
|
234
|
+
| `todo_write` + state `plan` | state `{"plan": [{content, status}]}` | live checklist |
|
|
235
|
+
|
|
236
|
+
## How it works
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
gentui (Textual) ── POST /agent (RunAgentInput) ──▶ any AG-UI backend
|
|
240
|
+
▲ │
|
|
241
|
+
│◀────────────── SSE: AG-UI events ─────────────────┘
|
|
242
|
+
|
|
243
|
+
Approve ▸ `resume` of the run's interrupt ──▶ backend runs the approved command
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
| Piece | File |
|
|
247
|
+
|---|---|
|
|
248
|
+
| SSE client (RunAgentInput in, typed events out) | [`tui/agui_client.py`](https://github.com/rahrajlat/Gentui/blob/main/src/gentui/tui/agui_client.py) |
|
|
249
|
+
| Event → widget dispatch, interrupts, chat | [`tui/app.py`](https://github.com/rahrajlat/Gentui/blob/main/src/gentui/tui/app.py) |
|
|
250
|
+
| Widgets and the tool-name registry | [`tui/widgets/`](https://github.com/rahrajlat/Gentui/tree/main/src/gentui/tui/widgets) |
|
|
251
|
+
| Logo, splash and animation | [`tui/branding.py`](https://github.com/rahrajlat/Gentui/blob/main/src/gentui/tui/branding.py), [`tui/splash.py`](https://github.com/rahrajlat/Gentui/blob/main/src/gentui/tui/splash.py) |
|
|
252
|
+
| Plugin API and config | [`plugins.py`](https://github.com/rahrajlat/Gentui/blob/main/src/gentui/plugins.py), [`config.py`](https://github.com/rahrajlat/Gentui/blob/main/src/gentui/config.py) |
|
|
253
|
+
|
|
254
|
+
AG-UI events used: `RUN_STARTED/FINISHED/ERROR` (with `outcome: interrupt`), `TEXT_MESSAGE_*`,
|
|
255
|
+
`REASONING_*`, `TOOL_CALL_START/ARGS/END/RESULT`, `STATE_SNAPSHOT`, `STATE_DELTA`. The request side
|
|
256
|
+
uses `messages`, `forwardedProps` and `resume`.
|
|
257
|
+
|
|
258
|
+
## Status
|
|
259
|
+
|
|
260
|
+
Gentui is **alpha**. What has been verified, and what has not:
|
|
261
|
+
|
|
262
|
+
- Built and tested against the **Strands Agents** framework (AWS) over AG-UI: the
|
|
263
|
+
[example backend](https://github.com/rahrajlat/Gentui/tree/main/examples/strands-backend) and the official
|
|
264
|
+
[`ag-ui-strands`](https://pypi.org/project/ag-ui-strands/) adapter (with `send_history = true`).
|
|
265
|
+
- AgentCore Runtime support works against a real runtime (confirmed by the author with a plain runtime ARN) and only
|
|
266
|
+
covers runtimes using the AG-UI protocol. Other setups are covered by tests with a fake boto3 client and botocore's `Stubber`.
|
|
267
|
+
- Backends on other frameworks, the interrupt flow against a backend other than the example, other model
|
|
268
|
+
providers than Ollama, and native Windows are **untested**.
|
|
269
|
+
- The look relies on Unicode box-drawing and block characters. If glyphs are missing, try a terminal
|
|
270
|
+
font such as DejaVu Sans Mono, Cascadia or JetBrains Mono.
|
|
271
|
+
|
|
272
|
+
**Ideas, not built yet:** `show_form` / `ask_approval` tools, a composable JSON-tree UI tool, persistent
|
|
273
|
+
threads, a `--demo` mode and record/replay of runs.
|
|
274
|
+
|
|
275
|
+
## Development
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
uv run pytest -q # TUI tests: headless, no LLM or backend needed
|
|
279
|
+
cd examples/strands-backend && uv run pytest -q # the example backend's own tests
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The images above are generated from the app's own code:
|
|
283
|
+
`uv run --with pillow python docs/assets/build_assets.py`.
|
|
284
|
+
|
|
285
|
+
## Contributing
|
|
286
|
+
|
|
287
|
+
Issues and pull requests are welcome. Please run both test suites before opening a PR, and add a test
|
|
288
|
+
with any behaviour change. Because Gentui is a client for an open protocol, changes that keep it
|
|
289
|
+
backend-agnostic are the easiest to accept.
|
|
290
|
+
|
|
291
|
+
## License
|
|
292
|
+
|
|
293
|
+
[MIT](https://github.com/rahrajlat/Gentui/blob/main/LICENSE). Free to use, modify and distribute, including the [example backend](https://github.com/rahrajlat/Gentui/tree/main/examples/strands-backend).
|
gentui-0.1.0/README.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="https://raw.githubusercontent.com/rahrajlat/Gentui/main/docs/assets/hero.gif" alt="Gentui: generative UI for your terminal" width="640">
|
|
4
|
+
|
|
5
|
+
**A terminal interface for prototyping agents really quickly.**<br>
|
|
6
|
+
Point it at any [AG-UI](https://docs.ag-ui.com) backend and get streaming chat, the model's chain of thought,<br>
|
|
7
|
+
tables, charts and human approval, with no frontend to build.
|
|
8
|
+
|
|
9
|
+
[](https://github.com/rahrajlat/Gentui/actions/workflows/ci.yml)
|
|
10
|
+
[](https://www.python.org)
|
|
11
|
+
[](https://textual.textualize.io)
|
|
12
|
+
[](https://docs.ag-ui.com)
|
|
13
|
+
[-FF9900?style=flat-square)](https://strandsagents.com)
|
|
14
|
+
[](https://docs.astral.sh/uv/)
|
|
15
|
+
[](https://github.com/rahrajlat/Gentui/blob/main/LICENSE)
|
|
16
|
+
[](#status)
|
|
17
|
+
[](#contributing)
|
|
18
|
+
[](https://github.com/rahrajlat/Gentui/stargazers)
|
|
19
|
+
[](https://github.com/rahrajlat/Gentui/commits/main)
|
|
20
|
+
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Why Gentui?
|
|
26
|
+
|
|
27
|
+
**Gentui is a terminal interface for prototyping agents really quickly.**
|
|
28
|
+
|
|
29
|
+
While developing agents locally, we end up spending our time on the frontend: a Streamlit or Chainlit
|
|
30
|
+
app, or a React project, just to talk to the agent. Each one is a separate frontend to build and keep
|
|
31
|
+
running, and it slows down the thing you actually want to iterate on: the agent.
|
|
32
|
+
|
|
33
|
+
Gentui removes that step. Install it, point it at your agent's AG-UI endpoint, and you get a chat, the
|
|
34
|
+
model's **chain of thought**, **human-in-the-loop (HITL)** and **approval** flows out of the box, with no
|
|
35
|
+
frontend code to write. Change your agent, restart it, and you're prototyping again in seconds.
|
|
36
|
+
|
|
37
|
+
When the agent calls a tool, the **tool call becomes the UI**: a command to approve, a table, a chart,
|
|
38
|
+
a live plan. Nothing risky runs until you click **Approve**.
|
|
39
|
+
|
|
40
|
+
**Tested with the [Strands Agents](https://strandsagents.com) framework (AWS's open-source agent SDK) over
|
|
41
|
+
AG-UI**: both on a custom backend (the [example backend](https://github.com/rahrajlat/Gentui/tree/main/examples/strands-backend)) and on the official
|
|
42
|
+
[`ag-ui-strands`](https://pypi.org/project/ag-ui-strands/) adapter.
|
|
43
|
+
|
|
44
|
+
<div align="center">
|
|
45
|
+
<img src="https://raw.githubusercontent.com/rahrajlat/Gentui/main/docs/assets/demo.gif" alt="Gentui demo: ask, review the proposed command, approve, see the output, chart it" width="860">
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- **Works with any AG-UI backend.** Streaming text, tool calls and shared state work out of the box;
|
|
51
|
+
unknown tools show as a readable card, never an error.
|
|
52
|
+
- **Chain of thought, visible.** Reasoning streams into a collapsible "Thought for 3s" block.
|
|
53
|
+
- **Human-in-the-loop approval** built on AG-UI **interrupts**: the run ends waiting, your click
|
|
54
|
+
`resume`s it. The model can't approve its own commands.
|
|
55
|
+
- **Generative widgets from tool calls:** command card, command output, tables, terminal charts
|
|
56
|
+
(line, bar, scatter, histogram), live plan checklist.
|
|
57
|
+
- **Developer-friendly:** an event inspector (`d`) showing the raw SSE payloads exactly as sent, `/theme`,
|
|
58
|
+
`/clear`, `/reasoning`, and a clear [backend contract](https://github.com/rahrajlat/Gentui/blob/main/docs/tool-contract.md).
|
|
59
|
+
- **Yours to customise:** TOML config, hot-reloaded CSS, your own themes, and Python plugins that add
|
|
60
|
+
widgets, slash commands and event hooks.
|
|
61
|
+
- **Runs agents on AWS too:** invoke agents hosted on Amazon Bedrock AgentCore Runtime (AG-UI protocol) via boto3.
|
|
62
|
+
- **Backend-agnostic by design.** The client has no framework code; a complete example backend lives in
|
|
63
|
+
[`examples/strands-backend`](https://github.com/rahrajlat/Gentui/tree/main/examples/strands-backend).
|
|
64
|
+
|
|
65
|
+
## The sample agent: natural language → shell
|
|
66
|
+
|
|
67
|
+
The demo above is the bundled example backend, a **natural-language-to-shell agent** built on Strands
|
|
68
|
+
Agents. You describe a task in plain English, it proposes a single shell command, and **nothing runs
|
|
69
|
+
until you approve it**.
|
|
70
|
+
|
|
71
|
+
1. **Ask:** "what are the 5 largest files here?"
|
|
72
|
+
2. **Review:** a shell sub-agent writes one command for your shell (bash, or PowerShell on Windows)
|
|
73
|
+
with a one-line explanation and a risk level: `safe`, `caution` or `dangerous`.
|
|
74
|
+
3. **Decide:** **Approve**, **Edit** the command first, or **Reject**. Your decision is sent back as an
|
|
75
|
+
AG-UI `resume`; the model can't approve for itself or change the command.
|
|
76
|
+
4. **Run:** only the approved command executes, with a timeout, an output cap and a fixed working
|
|
77
|
+
directory. A denylist (recursive delete of root or home, disk formatting, shutdown, fork bombs,
|
|
78
|
+
interactive programs) is checked at proposal, at approval and again right before running. It is a
|
|
79
|
+
safety net, not a sandbox.
|
|
80
|
+
|
|
81
|
+
The same agent can also show results as a **table** or **chart**, lay out multi-step work as a live
|
|
82
|
+
**plan**, explain what a command does (an explainer sub-agent), and keep **long-term memory** between
|
|
83
|
+
conversations. It's a worked example of the [backend contract](https://github.com/rahrajlat/Gentui/blob/main/docs/tool-contract.md): copy it, or add your
|
|
84
|
+
own tools with [this guide](https://github.com/rahrajlat/Gentui/blob/main/examples/strands-backend/docs/add_a_tool.md).
|
|
85
|
+
|
|
86
|
+
## Quick start
|
|
87
|
+
|
|
88
|
+
Requires [uv](https://docs.astral.sh/uv/) and Python 3.12.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git clone https://github.com/rahrajlat/Gentui.git && cd Gentui
|
|
92
|
+
uv sync
|
|
93
|
+
uv run gentui http://localhost:8000/agent # your AG-UI endpoint
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
No backend yet? Run the [sample natural-language-to-shell agent](#the-sample-agent-natural-language--shell)
|
|
97
|
+
(Strands Agents + FastAPI) in another terminal. It uses a local
|
|
98
|
+
[Ollama](https://ollama.com) by default; see its [README](https://github.com/rahrajlat/Gentui/blob/main/examples/strands-backend/README.md) for other providers:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
cd examples/strands-backend
|
|
102
|
+
uv sync && cp .env.example .env
|
|
103
|
+
uv run server # http://localhost:8000/agent
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
uv run gentui https://my.host/agent --token sk-... # bearer auth
|
|
108
|
+
uv run gentui URL -H "X-Org: acme" --theme nord --dev # extra header, theme, event inspector
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
| Key / command | Does |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `Enter` | send |
|
|
114
|
+
| `d` or `Ctrl+D` | toggle the event inspector |
|
|
115
|
+
| `Ctrl+Q` | quit |
|
|
116
|
+
| `/help` `/theme <name>` `/clear` `/reasoning` `/dev` `/quit` | slash commands |
|
|
117
|
+
|
|
118
|
+
## Agents on Amazon Bedrock AgentCore Runtime
|
|
119
|
+
|
|
120
|
+
Gentui can also invoke an agent hosted on **[AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/what-is-bedrock-agentcore.html)**
|
|
121
|
+
with boto3's `invoke_agent_runtime`, using your normal AWS credentials. The runtime must be deployed with the
|
|
122
|
+
**AG-UI protocol**.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
uv sync --extra agentcore # boto3 is an optional extra
|
|
126
|
+
uv run gentui arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/my_agent-AbCdEfGhIj
|
|
127
|
+
uv run gentui ARN --profile dev --region eu-west-1 # optional: AWS profile and region
|
|
128
|
+
uv run gentui ARN --qualifier prod # optional: a specific runtime endpoint
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`--profile` and `--region` are both **optional**. Without them, Gentui uses the standard AWS credential chain
|
|
132
|
+
(`AWS_PROFILE`, SSO, environment variables) and the region in the ARN. They can also be set in `gentui.toml` as
|
|
133
|
+
`aws_profile` and `region`.
|
|
134
|
+
|
|
135
|
+
One conversation is one runtime session, errors come with fixes (missing credentials, denied access, wrong ARN), and
|
|
136
|
+
busy sessions are retried. Details, IAM permissions and troubleshooting: **[docs/agentcore.md](https://github.com/rahrajlat/Gentui/blob/main/docs/agentcore.md)**.
|
|
137
|
+
Both a runtime ARN and an endpoint ARN (`.../runtime-endpoint/DEFAULT`) work.
|
|
138
|
+
|
|
139
|
+
## Configure
|
|
140
|
+
|
|
141
|
+
Put options in `./gentui.toml` or `~/.config/gentui/config.toml`. Flags, `GENTUI_URL` and
|
|
142
|
+
`GENTUI_TOKEN` override the file. Every option is documented in
|
|
143
|
+
[`gentui.example.toml`](https://github.com/rahrajlat/Gentui/blob/main/gentui.example.toml): backend URL, token and headers, props sent with every run,
|
|
144
|
+
title, welcome text, theme, splash and logo, reasoning on/off, timestamps, plugins and widget mapping.
|
|
145
|
+
|
|
146
|
+
> **Stateless backends** (ones that rebuild context from the message list, like the official
|
|
147
|
+
> `ag-ui-strands` adapter) need `send_history = true`. By default only the newest message is sent and
|
|
148
|
+
> the backend is expected to keep history per `threadId`.
|
|
149
|
+
|
|
150
|
+
## Customise
|
|
151
|
+
|
|
152
|
+
- **Theme:** `theme = "nord"` or `/theme <name>`. The default is `gentui` (teal and violet); `claude`
|
|
153
|
+
(coral) is also built in.
|
|
154
|
+
- **Your own styling:** `css = "my.tcss"` loads a [Textual CSS](https://textual.textualize.io/guide/CSS/)
|
|
155
|
+
file on top of the defaults and **hot-reloads while the app runs**. Useful selectors: `.user`,
|
|
156
|
+
`.assistant`, `.thinking`, `.error`, `ToolWidget`, `#chat`, `#prompt`.
|
|
157
|
+
- **Your own widget for a backend tool:** subclass `ToolWidget`, then map it without any plugin file:
|
|
158
|
+
|
|
159
|
+
```toml
|
|
160
|
+
[widgets]
|
|
161
|
+
show_map = "my_widgets:MapWidget"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Plugins
|
|
165
|
+
|
|
166
|
+
A plugin is a plain Python file. Drop it in `./gentui_plugins/` or `~/.config/gentui/plugins/`, list it in
|
|
167
|
+
`plugins = [...]` or `--plugin`, or ship it as a pip package using the `gentui.plugins` entry point.
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from gentui.plugins import on_event, register_command
|
|
171
|
+
from gentui.tui.widgets.base import ToolWidget
|
|
172
|
+
from gentui.tui.widgets.registry import register_widget
|
|
173
|
+
|
|
174
|
+
@register_widget("weather") # render tool calls named "weather"
|
|
175
|
+
class Weather(ToolWidget):
|
|
176
|
+
def on_end(self, args): self.show(f"☀ {args['city']}")
|
|
177
|
+
|
|
178
|
+
@register_command("ping", "say pong") # adds /ping
|
|
179
|
+
def ping(app, args): app.notify("pong")
|
|
180
|
+
|
|
181
|
+
@on_event("TOOL_CALL_RESULT") # hook any AG-UI event
|
|
182
|
+
async def audit(app, event): ...
|
|
183
|
+
|
|
184
|
+
def setup(app): ... # optional, runs once the app is mounted
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Plugins can mount any Textual widget into the conversation with `await app.mount_chat(widget)`. A
|
|
188
|
+
broken plugin is reported in a toast and never stops the app.
|
|
189
|
+
|
|
190
|
+
## Build a backend for it
|
|
191
|
+
|
|
192
|
+
Any language works: serve a `POST` endpoint that streams AG-UI events. To get the rich widgets, name
|
|
193
|
+
your tools like this (anything else shows a JSON card). The full contract, including the approval
|
|
194
|
+
flow and a checklist, is in **[docs/tool-contract.md](https://github.com/rahrajlat/Gentui/blob/main/docs/tool-contract.md)**.
|
|
195
|
+
|
|
196
|
+
| Tool name | Arguments / result | Renders |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| `propose_command` | `{command, explanation, risk: safe\|caution\|dangerous}` | command card with Approve / Edit / Reject; the decision goes back as an AG-UI `resume` of the run's interrupt |
|
|
199
|
+
| `run_command` | result JSON `{command, exit_code, timed_out, truncated, output}` | output card |
|
|
200
|
+
| `show_table` | `{title, columns, rows}` | table |
|
|
201
|
+
| `show_chart` | `{type: line\|bar\|scatter\|histogram, title, x, series: [{name, values}], x_label, y_label, bins}` | terminal chart (plotext) |
|
|
202
|
+
| `search_memory` | `{query}` | quiet "◌ recalled …" line |
|
|
203
|
+
| `todo_write` + state `plan` | state `{"plan": [{content, status}]}` | live checklist |
|
|
204
|
+
|
|
205
|
+
## How it works
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
gentui (Textual) ── POST /agent (RunAgentInput) ──▶ any AG-UI backend
|
|
209
|
+
▲ │
|
|
210
|
+
│◀────────────── SSE: AG-UI events ─────────────────┘
|
|
211
|
+
|
|
212
|
+
Approve ▸ `resume` of the run's interrupt ──▶ backend runs the approved command
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
| Piece | File |
|
|
216
|
+
|---|---|
|
|
217
|
+
| SSE client (RunAgentInput in, typed events out) | [`tui/agui_client.py`](https://github.com/rahrajlat/Gentui/blob/main/src/gentui/tui/agui_client.py) |
|
|
218
|
+
| Event → widget dispatch, interrupts, chat | [`tui/app.py`](https://github.com/rahrajlat/Gentui/blob/main/src/gentui/tui/app.py) |
|
|
219
|
+
| Widgets and the tool-name registry | [`tui/widgets/`](https://github.com/rahrajlat/Gentui/tree/main/src/gentui/tui/widgets) |
|
|
220
|
+
| Logo, splash and animation | [`tui/branding.py`](https://github.com/rahrajlat/Gentui/blob/main/src/gentui/tui/branding.py), [`tui/splash.py`](https://github.com/rahrajlat/Gentui/blob/main/src/gentui/tui/splash.py) |
|
|
221
|
+
| Plugin API and config | [`plugins.py`](https://github.com/rahrajlat/Gentui/blob/main/src/gentui/plugins.py), [`config.py`](https://github.com/rahrajlat/Gentui/blob/main/src/gentui/config.py) |
|
|
222
|
+
|
|
223
|
+
AG-UI events used: `RUN_STARTED/FINISHED/ERROR` (with `outcome: interrupt`), `TEXT_MESSAGE_*`,
|
|
224
|
+
`REASONING_*`, `TOOL_CALL_START/ARGS/END/RESULT`, `STATE_SNAPSHOT`, `STATE_DELTA`. The request side
|
|
225
|
+
uses `messages`, `forwardedProps` and `resume`.
|
|
226
|
+
|
|
227
|
+
## Status
|
|
228
|
+
|
|
229
|
+
Gentui is **alpha**. What has been verified, and what has not:
|
|
230
|
+
|
|
231
|
+
- Built and tested against the **Strands Agents** framework (AWS) over AG-UI: the
|
|
232
|
+
[example backend](https://github.com/rahrajlat/Gentui/tree/main/examples/strands-backend) and the official
|
|
233
|
+
[`ag-ui-strands`](https://pypi.org/project/ag-ui-strands/) adapter (with `send_history = true`).
|
|
234
|
+
- AgentCore Runtime support works against a real runtime (confirmed by the author with a plain runtime ARN) and only
|
|
235
|
+
covers runtimes using the AG-UI protocol. Other setups are covered by tests with a fake boto3 client and botocore's `Stubber`.
|
|
236
|
+
- Backends on other frameworks, the interrupt flow against a backend other than the example, other model
|
|
237
|
+
providers than Ollama, and native Windows are **untested**.
|
|
238
|
+
- The look relies on Unicode box-drawing and block characters. If glyphs are missing, try a terminal
|
|
239
|
+
font such as DejaVu Sans Mono, Cascadia or JetBrains Mono.
|
|
240
|
+
|
|
241
|
+
**Ideas, not built yet:** `show_form` / `ask_approval` tools, a composable JSON-tree UI tool, persistent
|
|
242
|
+
threads, a `--demo` mode and record/replay of runs.
|
|
243
|
+
|
|
244
|
+
## Development
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
uv run pytest -q # TUI tests: headless, no LLM or backend needed
|
|
248
|
+
cd examples/strands-backend && uv run pytest -q # the example backend's own tests
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The images above are generated from the app's own code:
|
|
252
|
+
`uv run --with pillow python docs/assets/build_assets.py`.
|
|
253
|
+
|
|
254
|
+
## Contributing
|
|
255
|
+
|
|
256
|
+
Issues and pull requests are welcome. Please run both test suites before opening a PR, and add a test
|
|
257
|
+
with any behaviour change. Because Gentui is a client for an open protocol, changes that keep it
|
|
258
|
+
backend-agnostic are the easiest to accept.
|
|
259
|
+
|
|
260
|
+
## License
|
|
261
|
+
|
|
262
|
+
[MIT](https://github.com/rahrajlat/Gentui/blob/main/LICENSE). Free to use, modify and distribute, including the [example backend](https://github.com/rahrajlat/Gentui/tree/main/examples/strands-backend).
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "gentui"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A terminal interface for prototyping agents really quickly, for any AG-UI backend"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
keywords = [
|
|
9
|
+
"ag-ui",
|
|
10
|
+
"agents",
|
|
11
|
+
"ai-agents",
|
|
12
|
+
"tui",
|
|
13
|
+
"terminal",
|
|
14
|
+
"textual",
|
|
15
|
+
"llm",
|
|
16
|
+
"human-in-the-loop",
|
|
17
|
+
"strands",
|
|
18
|
+
"agentcore",
|
|
19
|
+
]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 3 - Alpha",
|
|
22
|
+
"Environment :: Console",
|
|
23
|
+
"Intended Audience :: Developers",
|
|
24
|
+
"Operating System :: OS Independent",
|
|
25
|
+
"Programming Language :: Python :: 3",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Topic :: Software Development",
|
|
28
|
+
"Topic :: Terminals",
|
|
29
|
+
]
|
|
30
|
+
requires-python = ">=3.12"
|
|
31
|
+
dependencies = [
|
|
32
|
+
"ag-ui-protocol>=1.0.0",
|
|
33
|
+
"httpx>=0.28.1",
|
|
34
|
+
"jsonpatch>=1.33",
|
|
35
|
+
"textual>=8.2.8",
|
|
36
|
+
"textual-plotext>=1.0.1",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[[project.authors]]
|
|
40
|
+
name = "rahrajlat"
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://github.com/rahrajlat/Gentui"
|
|
44
|
+
Repository = "https://github.com/rahrajlat/Gentui"
|
|
45
|
+
Documentation = "https://github.com/rahrajlat/Gentui#readme"
|
|
46
|
+
Issues = "https://github.com/rahrajlat/Gentui/issues"
|
|
47
|
+
Changelog = "https://github.com/rahrajlat/Gentui/blob/main/CHANGELOG.md"
|
|
48
|
+
|
|
49
|
+
[project.scripts]
|
|
50
|
+
gentui = "gentui.cli:main"
|
|
51
|
+
tui = "gentui.cli:main"
|
|
52
|
+
|
|
53
|
+
[project.optional-dependencies]
|
|
54
|
+
agentcore = ["boto3>=1.40"]
|
|
55
|
+
|
|
56
|
+
[build-system]
|
|
57
|
+
requires = ["uv_build>=0.12.22,<0.13.0"]
|
|
58
|
+
build-backend = "uv_build"
|
|
59
|
+
|
|
60
|
+
[dependency-groups]
|
|
61
|
+
dev = [
|
|
62
|
+
"boto3>=1.43.108",
|
|
63
|
+
"pytest>=9.1.1",
|
|
64
|
+
"pytest-asyncio>=1.4.0",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
asyncio_mode = "auto"
|
|
69
|
+
testpaths = ["tests"]
|