understudy 0.1.0__tar.gz → 0.4.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.
Files changed (47) hide show
  1. {understudy-0.1.0 → understudy-0.4.0}/PKG-INFO +135 -26
  2. understudy-0.4.0/README.md +269 -0
  3. {understudy-0.1.0 → understudy-0.4.0}/pyproject.toml +7 -4
  4. {understudy-0.1.0 → understudy-0.4.0}/src/understudy/__init__.py +36 -8
  5. {understudy-0.1.0 → understudy-0.4.0}/src/understudy/adk/__init__.py +73 -13
  6. understudy-0.4.0/src/understudy/check.py +296 -0
  7. understudy-0.4.0/src/understudy/cli.py +837 -0
  8. understudy-0.4.0/src/understudy/compare.py +186 -0
  9. {understudy-0.1.0 → understudy-0.4.0}/src/understudy/http/__init__.py +0 -6
  10. understudy-0.4.0/src/understudy/judges.py +214 -0
  11. understudy-0.4.0/src/understudy/langgraph/__init__.py +162 -0
  12. understudy-0.4.0/src/understudy/langgraph/tools.py +100 -0
  13. understudy-0.4.0/src/understudy/metrics/__init__.py +16 -0
  14. understudy-0.4.0/src/understudy/metrics/builtins.py +112 -0
  15. understudy-0.4.0/src/understudy/metrics/registry.py +90 -0
  16. understudy-0.4.0/src/understudy/metrics/templates/clarity.jinja +13 -0
  17. understudy-0.4.0/src/understudy/metrics/templates/policy_compliance.jinja +17 -0
  18. understudy-0.4.0/src/understudy/metrics/templates/tone_empathy.jinja +13 -0
  19. {understudy-0.1.0 → understudy-0.4.0}/src/understudy/mocks.py +3 -5
  20. {understudy-0.1.0 → understudy-0.4.0}/src/understudy/models.py +4 -2
  21. understudy-0.4.0/src/understudy/reports.py +281 -0
  22. understudy-0.4.0/src/understudy/runner.py +337 -0
  23. understudy-0.4.0/src/understudy/server/__init__.py +142 -0
  24. understudy-0.4.0/src/understudy/server/models.py +123 -0
  25. understudy-0.4.0/src/understudy/server/sessions.py +192 -0
  26. understudy-0.4.0/src/understudy/server/ui_simulator.py +160 -0
  27. understudy-0.4.0/src/understudy/storage.py +490 -0
  28. {understudy-0.1.0 → understudy-0.4.0}/src/understudy/suite.py +28 -8
  29. {understudy-0.1.0 → understudy-0.4.0}/src/understudy/templates/base.html +1 -5
  30. understudy-0.4.0/src/understudy/templates/comparison.html +150 -0
  31. understudy-0.4.0/src/understudy/templates/index.html +271 -0
  32. {understudy-0.1.0 → understudy-0.4.0}/src/understudy/templates/run_detail.html +62 -1
  33. {understudy-0.1.0 → understudy-0.4.0}/src/understudy/trace.py +49 -20
  34. understudy-0.1.0/README.md +0 -168
  35. understudy-0.1.0/src/understudy/check.py +0 -138
  36. understudy-0.1.0/src/understudy/cli.py +0 -258
  37. understudy-0.1.0/src/understudy/judges.py +0 -105
  38. understudy-0.1.0/src/understudy/loaders/__init__.py +0 -0
  39. understudy-0.1.0/src/understudy/personas/__init__.py +0 -0
  40. understudy-0.1.0/src/understudy/reports.py +0 -191
  41. understudy-0.1.0/src/understudy/runner.py +0 -171
  42. understudy-0.1.0/src/understudy/storage.py +0 -206
  43. understudy-0.1.0/src/understudy/templates/index.html +0 -110
  44. understudy-0.1.0/src/understudy/templates/metrics.html +0 -128
  45. {understudy-0.1.0 → understudy-0.4.0}/src/understudy/prompts/__init__.py +0 -0
  46. {understudy-0.1.0 → understudy-0.4.0}/src/understudy/prompts/rubrics.py +0 -0
  47. {understudy-0.1.0 → understudy-0.4.0}/src/understudy/simulator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: understudy
3
- Version: 0.1.0
3
+ Version: 0.4.0
4
4
  Summary: Simulation and trace-based evaluation for agentic systems
5
5
  Keywords: agent,evaluation,simulation,testing,llm
6
6
  Author: Gaurav Sood
@@ -14,20 +14,27 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Classifier: Topic :: Software Development :: Testing
15
15
  Requires-Dist: pydantic>=2.0
16
16
  Requires-Dist: pyyaml>=6.0
17
+ Requires-Dist: litellm>=1.50
17
18
  Requires-Dist: google-adk>=1.0 ; extra == 'adk'
18
- Requires-Dist: understudy[adk,judges,http,reports] ; extra == 'all'
19
+ Requires-Dist: python-dotenv>=1.0 ; extra == 'adk'
20
+ Requires-Dist: understudy[adk,http,langgraph,server,reports] ; extra == 'all'
19
21
  Requires-Dist: understudy[all,docs] ; extra == 'dev'
20
22
  Requires-Dist: pytest>=8.0 ; extra == 'dev'
21
23
  Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
24
+ Requires-Dist: pytest-cov>=4.0 ; extra == 'dev'
22
25
  Requires-Dist: ruff>=0.4 ; extra == 'dev'
23
26
  Requires-Dist: sphinx>=7.0 ; extra == 'docs'
24
27
  Requires-Dist: furo>=2024.0 ; extra == 'docs'
25
28
  Requires-Dist: myst-parser>=3.0 ; extra == 'docs'
26
29
  Requires-Dist: sphinx-autodoc-typehints>=2.0 ; extra == 'docs'
27
30
  Requires-Dist: httpx>=0.27 ; extra == 'http'
28
- Requires-Dist: litellm>=1.50 ; extra == 'judges'
31
+ Requires-Dist: langgraph>=0.2 ; extra == 'langgraph'
32
+ Requires-Dist: langchain-core>=0.3 ; extra == 'langgraph'
33
+ Requires-Dist: langchain-openai>=0.3 ; extra == 'langgraph'
29
34
  Requires-Dist: jinja2>=3.0 ; extra == 'reports'
30
35
  Requires-Dist: click>=8.0 ; extra == 'reports'
36
+ Requires-Dist: fastapi>=0.115 ; extra == 'server'
37
+ Requires-Dist: uvicorn>=0.32 ; extra == 'server'
31
38
  Requires-Python: >=3.12
32
39
  Project-URL: Homepage, https://github.com/gojiplus/understudy
33
40
  Project-URL: Documentation, https://gojiplus.github.io/understudy
@@ -38,17 +45,37 @@ Provides-Extra: all
38
45
  Provides-Extra: dev
39
46
  Provides-Extra: docs
40
47
  Provides-Extra: http
41
- Provides-Extra: judges
48
+ Provides-Extra: langgraph
42
49
  Provides-Extra: reports
50
+ Provides-Extra: server
43
51
  Description-Content-Type: text/markdown
44
52
 
45
- # understudy
53
+ ## understudy: Scenario Testing for AI Agents
46
54
 
47
55
  [![PyPI version](https://badge.fury.io/py/understudy.svg)](https://badge.fury.io/py/understudy)
56
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/understudy?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/project/understudy)
48
57
  [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
58
+ [![Documentation](https://github.com/gojiplus/understudy/actions/workflows/docs.yml/badge.svg)](https://gojiplus.github.io/understudy/)
49
59
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
50
60
 
51
- Test your AI agents with simulated users.
61
+
62
+ Understudy is a scenario-driven testing framework for AI agents that simulates realistic multi-turn users, runs those scenes against an agent through a simple app adapter, records a structured execution trace of messages, tool calls, and handoffs, and then evaluates behavior with deterministic checks, optional LLM judges, and run reports.
63
+
64
+ ## How It Works
65
+
66
+ Testing with understudy is **4 steps**:
67
+
68
+ 1. **Wrap your agent** — Adapt your agent (ADK, LangGraph, HTTP) to understudy's interface
69
+ 2. **Mock your tools** — Register handlers that return test data instead of calling real services
70
+ 3. **Write scenes** — YAML files defining what the simulated user wants and what you expect
71
+ 4. **Run and assert** — Execute simulations, check traces, generate reports
72
+
73
+ The key insight: **assert against the trace, not the prose**. Don't check what the agent said—check what it did (tool calls).
74
+
75
+ **See real examples:**
76
+ - [Example scene](https://github.com/gojiplus/understudy/blob/main/example/scenes/return_eligible_backpack.yaml) — YAML defining a test scenario
77
+ - [ADK test file](https://github.com/gojiplus/understudy/blob/main/example/adk/test_returns.py) — pytest assertions against traces
78
+ - [LangGraph test file](https://github.com/gojiplus/understudy/blob/main/example/langgraph/test_returns.py) — same tests, different framework
52
79
 
53
80
  ## Installation
54
81
 
@@ -106,21 +133,21 @@ expectations:
106
133
  required_tools:
107
134
  - lookup_order
108
135
  - create_return
109
- allowed_terminal_states:
110
- - return_created
136
+ forbidden_tools:
137
+ - issue_refund
111
138
  ```
112
139
 
113
140
  ### 4. Run simulation
114
141
 
115
142
  ```python
116
- from understudy import Scene, run, check
143
+ from understudy import Scene, run
117
144
 
118
145
  scene = Scene.from_file("scenes/return_backpack.yaml")
119
146
  trace = run(app, scene, mocks=mocks)
120
147
 
121
148
  assert trace.called("lookup_order")
122
149
  assert trace.called("create_return")
123
- assert trace.terminal_state == "return_created"
150
+ assert not trace.called("issue_refund")
124
151
  ```
125
152
 
126
153
  Or with pytest (define `app` and `mocks` fixtures in conftest.py):
@@ -129,27 +156,109 @@ Or with pytest (define `app` and `mocks` fixtures in conftest.py):
129
156
  pytest test_returns.py -v
130
157
  ```
131
158
 
132
- ## CLI Commands
159
+ ## Suites and Batch Runs
160
+
161
+ Run multiple scenes with multiple simulations per scene:
162
+
163
+ ```python
164
+ from understudy import Suite, RunStorage
165
+
166
+ suite = Suite.from_directory("scenes/")
167
+ storage = RunStorage()
168
+
169
+ # Run each scene 3 times and tag for comparison
170
+ results = suite.run(
171
+ app,
172
+ mocks=mocks,
173
+ storage=storage,
174
+ n_sims=3,
175
+ tags={"version": "v1"},
176
+ )
177
+ print(f"{results.pass_count}/{len(results.results)} passed")
178
+ ```
179
+
180
+ ## Simulation and Evaluation
133
181
 
134
- After running simulations, use the CLI to inspect results:
182
+ Understudy separates simulation (generating traces) from evaluation (checking traces). Use together or separately:
183
+
184
+ ### Combined (most common)
135
185
 
136
186
  ```bash
137
- # List all saved runs
138
- understudy list
187
+ understudy run \
188
+ --app mymodule:agent_app \
189
+ --scene ./scenes/ \
190
+ --n-sims 3 \
191
+ --junit results.xml
192
+ ```
139
193
 
140
- # Show aggregate metrics (pass rate, avg turns, tool usage, terminal states)
141
- understudy summary
194
+ ### Separate workflows
195
+
196
+ Generate traces only:
197
+
198
+ ```bash
199
+ understudy simulate \
200
+ --app mymodule:agent_app \
201
+ --scenes ./scenes/ \
202
+ --output ./traces/ \
203
+ --n-sims 3
204
+ ```
205
+
206
+ Evaluate existing traces:
207
+
208
+ ```bash
209
+ understudy evaluate \
210
+ --traces ./traces/ \
211
+ --output ./results/ \
212
+ --junit results.xml
213
+ ```
214
+
215
+ Python API:
216
+
217
+ ```python
218
+ from understudy import simulate_batch, evaluate_batch
219
+
220
+ # Generate traces
221
+ traces = simulate_batch(
222
+ app=agent_app,
223
+ scenes="./scenes/",
224
+ n_sims=3,
225
+ output="./traces/",
226
+ )
227
+
228
+ # Evaluate later
229
+ results = evaluate_batch(
230
+ traces="./traces/",
231
+ output="./results/",
232
+ )
233
+ ```
234
+
235
+ ## CLI Commands
236
+
237
+ ```bash
238
+ # Run simulations
239
+ understudy run --app mymodule:app --scene ./scenes/
240
+ understudy simulate --app mymodule:app --scenes ./scenes/
241
+ understudy evaluate --traces ./traces/
142
242
 
143
- # Show details for a specific run
243
+ # View results
244
+ understudy list
144
245
  understudy show <run_id>
246
+ understudy summary
145
247
 
146
- # Generate static HTML report
147
- understudy report --output report.html
248
+ # Compare runs by tag
249
+ understudy compare --tag version --before v1 --after v2
148
250
 
149
- # Start interactive report browser
251
+ # Generate reports
252
+ understudy report -o report.html
253
+ understudy compare --tag version --before v1 --after v2 --html comparison.html
254
+
255
+ # Interactive browser
150
256
  understudy serve --port 8080
151
257
 
152
- # Delete runs
258
+ # HTTP simulator server (for browser/UI testing)
259
+ understudy serve-api --port 8000
260
+
261
+ # Cleanup
153
262
  understudy delete <run_id>
154
263
  understudy clear
155
264
  ```
@@ -185,11 +294,10 @@ from understudy.judges import (
185
294
  ## Report Contents
186
295
 
187
296
  The `understudy summary` command shows:
188
- - **Pass rate** - percentage of scenes that passed all expectations
189
- - **Avg turns** - average conversation length
190
- - **Tool usage** - distribution of tool calls across runs
191
- - **Terminal states** - breakdown of how conversations ended
192
- - **Agents** - which agents were invoked
297
+ - **Pass rate** — percentage of scenes that passed all expectations
298
+ - **Avg turns** — average conversation length
299
+ - **Tool usage** — distribution of tool calls across runs
300
+ - **Agents** — which agents were invoked
193
301
 
194
302
  The HTML report (`understudy report`) includes:
195
303
  - All metrics above
@@ -204,6 +312,7 @@ See the [full documentation](https://gojiplus.github.io/understudy) for:
204
312
  - [Installation guide](https://gojiplus.github.io/understudy/installation.html)
205
313
  - [Writing scenes](https://gojiplus.github.io/understudy/tutorial/scenes.html)
206
314
  - [ADK integration](https://gojiplus.github.io/understudy/adk-integration.html)
315
+ - [LangGraph integration](https://gojiplus.github.io/understudy/langgraph-integration.html)
207
316
  - [HTTP client for deployed agents](https://gojiplus.github.io/understudy/tutorial/http.html)
208
317
  - [API reference](https://gojiplus.github.io/understudy/api/index.html)
209
318
 
@@ -0,0 +1,269 @@
1
+ ## understudy: Scenario Testing for AI Agents
2
+
3
+ [![PyPI version](https://badge.fury.io/py/understudy.svg)](https://badge.fury.io/py/understudy)
4
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/understudy?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/project/understudy)
5
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
6
+ [![Documentation](https://github.com/gojiplus/understudy/actions/workflows/docs.yml/badge.svg)](https://gojiplus.github.io/understudy/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+
9
+
10
+ Understudy is a scenario-driven testing framework for AI agents that simulates realistic multi-turn users, runs those scenes against an agent through a simple app adapter, records a structured execution trace of messages, tool calls, and handoffs, and then evaluates behavior with deterministic checks, optional LLM judges, and run reports.
11
+
12
+ ## How It Works
13
+
14
+ Testing with understudy is **4 steps**:
15
+
16
+ 1. **Wrap your agent** — Adapt your agent (ADK, LangGraph, HTTP) to understudy's interface
17
+ 2. **Mock your tools** — Register handlers that return test data instead of calling real services
18
+ 3. **Write scenes** — YAML files defining what the simulated user wants and what you expect
19
+ 4. **Run and assert** — Execute simulations, check traces, generate reports
20
+
21
+ The key insight: **assert against the trace, not the prose**. Don't check what the agent said—check what it did (tool calls).
22
+
23
+ **See real examples:**
24
+ - [Example scene](https://github.com/gojiplus/understudy/blob/main/example/scenes/return_eligible_backpack.yaml) — YAML defining a test scenario
25
+ - [ADK test file](https://github.com/gojiplus/understudy/blob/main/example/adk/test_returns.py) — pytest assertions against traces
26
+ - [LangGraph test file](https://github.com/gojiplus/understudy/blob/main/example/langgraph/test_returns.py) — same tests, different framework
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install understudy[all]
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ### 1. Wrap your agent
37
+
38
+ ```python
39
+ from understudy.adk import ADKApp
40
+ from my_agent import agent
41
+
42
+ app = ADKApp(agent=agent)
43
+ ```
44
+
45
+ ### 2. Mock your tools
46
+
47
+ Your agent has tools that call external services. Mock them for testing:
48
+
49
+ ```python
50
+ from understudy.mocks import MockToolkit
51
+
52
+ mocks = MockToolkit()
53
+
54
+ @mocks.handle("lookup_order")
55
+ def lookup_order(order_id: str) -> dict:
56
+ return {"order_id": order_id, "items": [...], "status": "delivered"}
57
+
58
+ @mocks.handle("create_return")
59
+ def create_return(order_id: str, item_sku: str, reason: str) -> dict:
60
+ return {"return_id": "RET-001", "status": "created"}
61
+ ```
62
+
63
+ ### 3. Write a scene
64
+
65
+ Create `scenes/return_backpack.yaml`:
66
+
67
+ ```yaml
68
+ id: return_eligible_backpack
69
+ description: Customer wants to return a backpack
70
+
71
+ starting_prompt: "I'd like to return an item please."
72
+ conversation_plan: |
73
+ Goal: Return the hiking backpack from order ORD-10031.
74
+ - Provide order ID when asked
75
+ - Return reason: too small
76
+
77
+ persona: cooperative
78
+ max_turns: 15
79
+
80
+ expectations:
81
+ required_tools:
82
+ - lookup_order
83
+ - create_return
84
+ forbidden_tools:
85
+ - issue_refund
86
+ ```
87
+
88
+ ### 4. Run simulation
89
+
90
+ ```python
91
+ from understudy import Scene, run
92
+
93
+ scene = Scene.from_file("scenes/return_backpack.yaml")
94
+ trace = run(app, scene, mocks=mocks)
95
+
96
+ assert trace.called("lookup_order")
97
+ assert trace.called("create_return")
98
+ assert not trace.called("issue_refund")
99
+ ```
100
+
101
+ Or with pytest (define `app` and `mocks` fixtures in conftest.py):
102
+
103
+ ```bash
104
+ pytest test_returns.py -v
105
+ ```
106
+
107
+ ## Suites and Batch Runs
108
+
109
+ Run multiple scenes with multiple simulations per scene:
110
+
111
+ ```python
112
+ from understudy import Suite, RunStorage
113
+
114
+ suite = Suite.from_directory("scenes/")
115
+ storage = RunStorage()
116
+
117
+ # Run each scene 3 times and tag for comparison
118
+ results = suite.run(
119
+ app,
120
+ mocks=mocks,
121
+ storage=storage,
122
+ n_sims=3,
123
+ tags={"version": "v1"},
124
+ )
125
+ print(f"{results.pass_count}/{len(results.results)} passed")
126
+ ```
127
+
128
+ ## Simulation and Evaluation
129
+
130
+ Understudy separates simulation (generating traces) from evaluation (checking traces). Use together or separately:
131
+
132
+ ### Combined (most common)
133
+
134
+ ```bash
135
+ understudy run \
136
+ --app mymodule:agent_app \
137
+ --scene ./scenes/ \
138
+ --n-sims 3 \
139
+ --junit results.xml
140
+ ```
141
+
142
+ ### Separate workflows
143
+
144
+ Generate traces only:
145
+
146
+ ```bash
147
+ understudy simulate \
148
+ --app mymodule:agent_app \
149
+ --scenes ./scenes/ \
150
+ --output ./traces/ \
151
+ --n-sims 3
152
+ ```
153
+
154
+ Evaluate existing traces:
155
+
156
+ ```bash
157
+ understudy evaluate \
158
+ --traces ./traces/ \
159
+ --output ./results/ \
160
+ --junit results.xml
161
+ ```
162
+
163
+ Python API:
164
+
165
+ ```python
166
+ from understudy import simulate_batch, evaluate_batch
167
+
168
+ # Generate traces
169
+ traces = simulate_batch(
170
+ app=agent_app,
171
+ scenes="./scenes/",
172
+ n_sims=3,
173
+ output="./traces/",
174
+ )
175
+
176
+ # Evaluate later
177
+ results = evaluate_batch(
178
+ traces="./traces/",
179
+ output="./results/",
180
+ )
181
+ ```
182
+
183
+ ## CLI Commands
184
+
185
+ ```bash
186
+ # Run simulations
187
+ understudy run --app mymodule:app --scene ./scenes/
188
+ understudy simulate --app mymodule:app --scenes ./scenes/
189
+ understudy evaluate --traces ./traces/
190
+
191
+ # View results
192
+ understudy list
193
+ understudy show <run_id>
194
+ understudy summary
195
+
196
+ # Compare runs by tag
197
+ understudy compare --tag version --before v1 --after v2
198
+
199
+ # Generate reports
200
+ understudy report -o report.html
201
+ understudy compare --tag version --before v1 --after v2 --html comparison.html
202
+
203
+ # Interactive browser
204
+ understudy serve --port 8080
205
+
206
+ # HTTP simulator server (for browser/UI testing)
207
+ understudy serve-api --port 8000
208
+
209
+ # Cleanup
210
+ understudy delete <run_id>
211
+ understudy clear
212
+ ```
213
+
214
+ ## LLM Judges
215
+
216
+ For qualities that can't be checked deterministically:
217
+
218
+ ```python
219
+ from understudy.judges import Judge
220
+
221
+ empathy_judge = Judge(
222
+ rubric="The agent acknowledged frustration and was empathetic while enforcing policy.",
223
+ samples=5,
224
+ )
225
+
226
+ result = empathy_judge.evaluate(trace)
227
+ assert result.score == 1
228
+ ```
229
+
230
+ Built-in rubrics:
231
+
232
+ ```python
233
+ from understudy.judges import (
234
+ TOOL_USAGE_CORRECTNESS,
235
+ POLICY_COMPLIANCE,
236
+ TONE_EMPATHY,
237
+ ADVERSARIAL_ROBUSTNESS,
238
+ TASK_COMPLETION,
239
+ )
240
+ ```
241
+
242
+ ## Report Contents
243
+
244
+ The `understudy summary` command shows:
245
+ - **Pass rate** — percentage of scenes that passed all expectations
246
+ - **Avg turns** — average conversation length
247
+ - **Tool usage** — distribution of tool calls across runs
248
+ - **Agents** — which agents were invoked
249
+
250
+ The HTML report (`understudy report`) includes:
251
+ - All metrics above
252
+ - Full conversation transcripts
253
+ - Tool call details with arguments
254
+ - Expectation check results
255
+ - Judge evaluation results (when used)
256
+
257
+ ## Documentation
258
+
259
+ See the [full documentation](https://gojiplus.github.io/understudy) for:
260
+ - [Installation guide](https://gojiplus.github.io/understudy/installation.html)
261
+ - [Writing scenes](https://gojiplus.github.io/understudy/tutorial/scenes.html)
262
+ - [ADK integration](https://gojiplus.github.io/understudy/adk-integration.html)
263
+ - [LangGraph integration](https://gojiplus.github.io/understudy/langgraph-integration.html)
264
+ - [HTTP client for deployed agents](https://gojiplus.github.io/understudy/tutorial/http.html)
265
+ - [API reference](https://gojiplus.github.io/understudy/api/index.html)
266
+
267
+ ## License
268
+
269
+ MIT
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "understudy"
7
- version = "0.1.0"
7
+ version = "0.4.0"
8
8
  description = "Simulation and trace-based evaluation for agentic systems"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -25,12 +25,14 @@ classifiers = [
25
25
  dependencies = [
26
26
  "pydantic>=2.0",
27
27
  "pyyaml>=6.0",
28
+ "litellm>=1.50",
28
29
  ]
29
30
 
30
31
  [project.optional-dependencies]
31
- adk = ["google-adk>=1.0"]
32
- judges = ["litellm>=1.50"]
32
+ adk = ["google-adk>=1.0", "python-dotenv>=1.0"]
33
33
  http = ["httpx>=0.27"]
34
+ langgraph = ["langgraph>=0.2", "langchain-core>=0.3", "langchain-openai>=0.3"]
35
+ server = ["fastapi>=0.115", "uvicorn>=0.32"]
34
36
  reports = ["jinja2>=3.0", "click>=8.0"]
35
37
  docs = [
36
38
  "sphinx>=7.0",
@@ -38,11 +40,12 @@ docs = [
38
40
  "myst-parser>=3.0",
39
41
  "sphinx-autodoc-typehints>=2.0",
40
42
  ]
41
- all = ["understudy[adk,judges,http,reports]"]
43
+ all = ["understudy[adk,http,langgraph,server,reports]"]
42
44
  dev = [
43
45
  "understudy[all,docs]",
44
46
  "pytest>=8.0",
45
47
  "pytest-asyncio>=0.23",
48
+ "pytest-cov>=4.0",
46
49
  "ruff>=0.4",
47
50
  ]
48
51
 
@@ -5,8 +5,12 @@ You write scenes, run rehearsals, and check the performance —
5
5
  not by reading the script, but by inspecting what actually happened.
6
6
  """
7
7
 
8
- from .check import CheckItem, CheckResult, check
9
- from .judges import Judge, JudgeResult
8
+ from importlib.metadata import version
9
+
10
+ from .check import CheckItem, CheckResult, EvaluationResult, check, evaluate, evaluate_batch
11
+ from .compare import ComparisonResult, SceneComparison, compare_runs
12
+ from .judges import FailureAnalysis, FailureAnalyzer, Judge, JudgeResult
13
+ from .metrics import MetricRegistry, MetricResult
10
14
  from .mocks import MockToolkit, ToolError
11
15
  from .models import Expectations, Persona, PersonaPreset, Scene
12
16
  from .prompts import (
@@ -18,12 +22,13 @@ from .prompts import (
18
22
  TONE_EMPATHY,
19
23
  TOOL_USAGE_CORRECTNESS,
20
24
  )
21
- from .runner import AgentApp, AgentResponse, run
22
- from .storage import RunStorage
25
+ from .runner import AgentApp, AgentResponse, LiteLLMBackend, run, simulate, simulate_batch
26
+ from .simulator import Simulator, SimulatorBackend
27
+ from .storage import EvaluationStorage, RunStorage, TraceStorage
23
28
  from .suite import SceneResult, Suite, SuiteResults
24
- from .trace import AgentTransfer, ToolCall, Trace, Turn
29
+ from .trace import AgentTransfer, StateSnapshot, ToolCall, Trace, TraceMetrics, Turn, TurnMetrics
25
30
 
26
- __version__ = "0.1.0"
31
+ __version__ = version("understudy")
27
32
 
28
33
  __all__ = [
29
34
  # models
@@ -36,26 +41,49 @@ __all__ = [
36
41
  "Turn",
37
42
  "ToolCall",
38
43
  "AgentTransfer",
39
- # runner
44
+ "TraceMetrics",
45
+ "TurnMetrics",
46
+ "StateSnapshot",
47
+ # metrics
48
+ "MetricRegistry",
49
+ "MetricResult",
50
+ # runner / simulate
40
51
  "run",
52
+ "simulate",
53
+ "simulate_batch",
41
54
  "AgentApp",
42
55
  "AgentResponse",
43
- # check
56
+ "LiteLLMBackend",
57
+ # check / evaluate
44
58
  "check",
59
+ "evaluate",
60
+ "evaluate_batch",
45
61
  "CheckResult",
46
62
  "CheckItem",
63
+ "EvaluationResult",
47
64
  # suite
48
65
  "Suite",
49
66
  "SuiteResults",
50
67
  "SceneResult",
51
68
  # storage
52
69
  "RunStorage",
70
+ "TraceStorage",
71
+ "EvaluationStorage",
72
+ # compare
73
+ "compare_runs",
74
+ "ComparisonResult",
75
+ "SceneComparison",
53
76
  # judges
54
77
  "Judge",
55
78
  "JudgeResult",
79
+ "FailureAnalyzer",
80
+ "FailureAnalysis",
56
81
  # mocks
57
82
  "MockToolkit",
58
83
  "ToolError",
84
+ # simulator
85
+ "Simulator",
86
+ "SimulatorBackend",
59
87
  # rubrics
60
88
  "TOOL_USAGE_CORRECTNESS",
61
89
  "POLICY_COMPLIANCE",