understudy 0.4.0__tar.gz → 0.6.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.
- understudy-0.6.0/LICENSE +21 -0
- {understudy-0.4.0 → understudy-0.6.0}/PKG-INFO +52 -24
- {understudy-0.4.0 → understudy-0.6.0}/README.md +41 -7
- understudy-0.6.0/pyproject.toml +207 -0
- understudy-0.6.0/pyproject.toml.orig +151 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/__init__.py +135 -51
- understudy-0.6.0/src/understudy/adk/__init__.py +426 -0
- understudy-0.6.0/src/understudy/adk/instrumentation.py +65 -0
- understudy-0.6.0/src/understudy/agentic/__init__.py +53 -0
- understudy-0.6.0/src/understudy/agentic/check.py +209 -0
- understudy-0.6.0/src/understudy/agentic/metrics.py +171 -0
- understudy-0.6.0/src/understudy/agentic/models.py +276 -0
- understudy-0.6.0/src/understudy/agentic/runner.py +160 -0
- understudy-0.6.0/src/understudy/assertions.py +44 -0
- understudy-0.6.0/src/understudy/batch.py +54 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/check.py +97 -49
- understudy-0.6.0/src/understudy/cli.py +1557 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/compare.py +6 -1
- understudy-0.6.0/src/understudy/diff.py +241 -0
- understudy-0.6.0/src/understudy/execution.py +320 -0
- understudy-0.6.0/src/understudy/http/__init__.py +369 -0
- understudy-0.6.0/src/understudy/instrumentation.py +39 -0
- understudy-0.6.0/src/understudy/judge_backends.py +150 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/judges.py +77 -24
- understudy-0.6.0/src/understudy/langchain/__init__.py +35 -0
- understudy-0.6.0/src/understudy/langchain/middleware.py +51 -0
- understudy-0.6.0/src/understudy/langgraph/__init__.py +448 -0
- understudy-0.6.0/src/understudy/langgraph/instrumentation.py +58 -0
- understudy-0.6.0/src/understudy/langgraph/tools.py +77 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/metrics/builtins.py +25 -9
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/metrics/registry.py +7 -7
- understudy-0.6.0/src/understudy/mocks.py +105 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/models.py +70 -15
- understudy-0.6.0/src/understudy/py.typed +0 -0
- understudy-0.6.0/src/understudy/pytest_plugin.py +181 -0
- understudy-0.6.0/src/understudy/replay.py +199 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/reports.py +23 -9
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/runner.py +99 -51
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/server/__init__.py +3 -2
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/server/sessions.py +18 -18
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/server/ui_simulator.py +19 -8
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/simulator.py +4 -5
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/storage.py +283 -220
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/suite.py +103 -65
- understudy-0.6.0/src/understudy/templates/init/conftest.py.template +70 -0
- understudy-0.6.0/src/understudy/templates/init/example_scene.yaml +36 -0
- understudy-0.6.0/src/understudy/templates/init/test_agent.py.template +43 -0
- understudy-0.6.0/src/understudy/templates/init/understudy.yaml.template +16 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/templates/run_detail.html +25 -2
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/trace.py +55 -6
- understudy-0.6.0/src/understudy/validation.py +167 -0
- understudy-0.4.0/pyproject.toml +0 -70
- understudy-0.4.0/src/understudy/adk/__init__.py +0 -254
- understudy-0.4.0/src/understudy/cli.py +0 -837
- understudy-0.4.0/src/understudy/http/__init__.py +0 -346
- understudy-0.4.0/src/understudy/langgraph/__init__.py +0 -162
- understudy-0.4.0/src/understudy/langgraph/tools.py +0 -100
- understudy-0.4.0/src/understudy/mocks.py +0 -56
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/metrics/__init__.py +0 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/metrics/templates/clarity.jinja +0 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/metrics/templates/policy_compliance.jinja +0 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/metrics/templates/tone_empathy.jinja +0 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/prompts/__init__.py +4 -4
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/prompts/rubrics.py +0 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/server/models.py +0 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/templates/base.html +0 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/templates/comparison.html +0 -0
- {understudy-0.4.0 → understudy-0.6.0}/src/understudy/templates/index.html +0 -0
understudy-0.6.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Gaurav Sood
|
|
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.
|
|
@@ -1,35 +1,30 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: understudy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.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
|
|
7
7
|
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
8
9
|
Classifier: Development Status :: 3 - Alpha
|
|
9
10
|
Classifier: Intended Audience :: Developers
|
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
15
|
Classifier: Topic :: Software Development :: Testing
|
|
15
16
|
Requires-Dist: pydantic>=2.0
|
|
16
17
|
Requires-Dist: pyyaml>=6.0
|
|
17
18
|
Requires-Dist: litellm>=1.50
|
|
18
|
-
Requires-Dist: google-adk>=
|
|
19
|
+
Requires-Dist: google-adk>=2.11 ; extra == 'adk'
|
|
19
20
|
Requires-Dist: python-dotenv>=1.0 ; extra == 'adk'
|
|
20
|
-
Requires-Dist: understudy[adk,http,langgraph,server,reports] ; extra == 'all'
|
|
21
|
-
Requires-Dist: understudy[all,docs] ; extra == 'dev'
|
|
22
|
-
Requires-Dist: pytest>=8.0 ; extra == 'dev'
|
|
23
|
-
Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
|
|
24
|
-
Requires-Dist: pytest-cov>=4.0 ; extra == 'dev'
|
|
25
|
-
Requires-Dist: ruff>=0.4 ; extra == 'dev'
|
|
26
|
-
Requires-Dist: sphinx>=7.0 ; extra == 'docs'
|
|
27
|
-
Requires-Dist: furo>=2024.0 ; extra == 'docs'
|
|
28
|
-
Requires-Dist: myst-parser>=3.0 ; extra == 'docs'
|
|
29
|
-
Requires-Dist: sphinx-autodoc-typehints>=2.0 ; extra == 'docs'
|
|
21
|
+
Requires-Dist: understudy[adk,http,langgraph,langchain,server,reports] ; extra == 'all'
|
|
30
22
|
Requires-Dist: httpx>=0.27 ; extra == 'http'
|
|
31
|
-
Requires-Dist:
|
|
32
|
-
Requires-Dist:
|
|
23
|
+
Requires-Dist: langchain>=1.4.4 ; extra == 'langchain'
|
|
24
|
+
Requires-Dist: langgraph>=1.2.14 ; extra == 'langchain'
|
|
25
|
+
Requires-Dist: langchain-core>=1.6.9 ; extra == 'langchain'
|
|
26
|
+
Requires-Dist: langgraph>=1.2.14 ; extra == 'langgraph'
|
|
27
|
+
Requires-Dist: langchain-core>=1.6.9 ; extra == 'langgraph'
|
|
33
28
|
Requires-Dist: langchain-openai>=0.3 ; extra == 'langgraph'
|
|
34
29
|
Requires-Dist: jinja2>=3.0 ; extra == 'reports'
|
|
35
30
|
Requires-Dist: click>=8.0 ; extra == 'reports'
|
|
@@ -42,9 +37,8 @@ Project-URL: Repository, https://github.com/gojiplus/understudy
|
|
|
42
37
|
Project-URL: Issues, https://github.com/gojiplus/understudy/issues
|
|
43
38
|
Provides-Extra: adk
|
|
44
39
|
Provides-Extra: all
|
|
45
|
-
Provides-Extra: dev
|
|
46
|
-
Provides-Extra: docs
|
|
47
40
|
Provides-Extra: http
|
|
41
|
+
Provides-Extra: langchain
|
|
48
42
|
Provides-Extra: langgraph
|
|
49
43
|
Provides-Extra: reports
|
|
50
44
|
Provides-Extra: server
|
|
@@ -65,22 +59,50 @@ Understudy is a scenario-driven testing framework for AI agents that simulates r
|
|
|
65
59
|
|
|
66
60
|
Testing with understudy is **4 steps**:
|
|
67
61
|
|
|
68
|
-
1. **Wrap your agent** — Adapt your agent (ADK, LangGraph, HTTP) to understudy's interface
|
|
62
|
+
1. **Wrap your agent** — Adapt your agent (ADK, LangGraph, LangChain, HTTP) to understudy's interface
|
|
69
63
|
2. **Mock your tools** — Register handlers that return test data instead of calling real services
|
|
70
64
|
3. **Write scenes** — YAML files defining what the simulated user wants and what you expect
|
|
71
65
|
4. **Run and assert** — Execute simulations, check traces, generate reports
|
|
72
66
|
|
|
73
67
|
The key insight: **assert against the trace, not the prose**. Don't check what the agent said—check what it did (tool calls).
|
|
74
68
|
|
|
69
|
+
## Two Evaluation Paradigms
|
|
70
|
+
|
|
71
|
+
### Conversational Agent Evaluation
|
|
72
|
+
Simulate multi-turn conversations with personas to test dialog agents.
|
|
73
|
+
- Use case: Customer service bots, assistants, chatbots
|
|
74
|
+
- Assert on tool calls: `trace.called("tool_name")`
|
|
75
|
+
|
|
76
|
+
### Agentic Flow Evaluation
|
|
77
|
+
Evaluate autonomous agents executing multi-step tasks.
|
|
78
|
+
- Use case: Code agents, research agents, task automation
|
|
79
|
+
- Assert on actions: `trace.performed("action")`
|
|
80
|
+
|
|
81
|
+
See [examples/README.md](https://github.com/gojiplus/understudy/blob/main/examples/README.md) for complete examples of both paradigms.
|
|
82
|
+
|
|
75
83
|
**See real examples:**
|
|
76
|
-
- [Example scene](https://github.com/gojiplus/understudy/blob/main/
|
|
77
|
-
- [ADK test file](https://github.com/gojiplus/understudy/blob/main/
|
|
78
|
-
- [LangGraph test file](https://github.com/gojiplus/understudy/blob/main/
|
|
84
|
+
- [Example scene](https://github.com/gojiplus/understudy/blob/main/examples/scenes/return_eligible_backpack.yaml) — YAML defining a test scenario
|
|
85
|
+
- [ADK test file](https://github.com/gojiplus/understudy/blob/main/examples/adk/test_adk_returns.py) — pytest assertions against traces
|
|
86
|
+
- [LangGraph test file](https://github.com/gojiplus/understudy/blob/main/examples/langgraph/test_langgraph_returns.py) — same tests, different framework
|
|
87
|
+
- [Agentic test file](https://github.com/gojiplus/understudy/blob/main/examples/agentic/test_agentic.py) — agentic flow evaluation
|
|
88
|
+
- [Agentic scene](https://github.com/gojiplus/understudy/blob/main/examples/agentic_scenes/code_review_task.yaml) — task-based scenario
|
|
89
|
+
- [Example report](https://htmlpreview.github.io/?https://github.com/gojiplus/understudy/blob/main/examples/langgraph/report/index.html) — HTML report with metrics and transcripts
|
|
90
|
+
|
|
91
|
+
## State injection and next-turn tests
|
|
92
|
+
|
|
93
|
+
Restore a completed checkpoint, inject synthetic history/state, or replay through
|
|
94
|
+
exchange X, then execute and assert only against exchange X+1. ADK and LangGraph
|
|
95
|
+
support isolated checkpoint forks; `LangChainApp` builds an instrumented LangChain
|
|
96
|
+
agent with tool middleware. Traces retain nested invocation identities.
|
|
97
|
+
|
|
98
|
+
See the [state-testing guide](https://gojiplus.github.io/understudy/state-testing.html)
|
|
99
|
+
for `run_turn`, `replay_to`, scoped tool mocks, pytest fixtures, CLI commands, and
|
|
100
|
+
framework-specific restoration limits.
|
|
79
101
|
|
|
80
102
|
## Installation
|
|
81
103
|
|
|
82
104
|
```bash
|
|
83
|
-
pip install understudy[all]
|
|
105
|
+
pip install "understudy[all]"
|
|
84
106
|
```
|
|
85
107
|
|
|
86
108
|
## Quick Start
|
|
@@ -103,9 +125,15 @@ from understudy.mocks import MockToolkit
|
|
|
103
125
|
|
|
104
126
|
mocks = MockToolkit()
|
|
105
127
|
|
|
128
|
+
|
|
106
129
|
@mocks.handle("lookup_order")
|
|
107
130
|
def lookup_order(order_id: str) -> dict:
|
|
108
|
-
return {
|
|
131
|
+
return {
|
|
132
|
+
"order_id": order_id,
|
|
133
|
+
"items": [{"sku": "BACKPACK-01", "quantity": 1}],
|
|
134
|
+
"status": "delivered",
|
|
135
|
+
}
|
|
136
|
+
|
|
109
137
|
|
|
110
138
|
@mocks.handle("create_return")
|
|
111
139
|
def create_return(order_id: str, item_sku: str, reason: str) -> dict:
|
|
@@ -282,7 +310,7 @@ assert result.score == 1
|
|
|
282
310
|
Built-in rubrics:
|
|
283
311
|
|
|
284
312
|
```python
|
|
285
|
-
from understudy.
|
|
313
|
+
from understudy.prompts import (
|
|
286
314
|
TOOL_USAGE_CORRECTNESS,
|
|
287
315
|
POLICY_COMPLIANCE,
|
|
288
316
|
TONE_EMPATHY,
|
|
@@ -13,22 +13,50 @@ Understudy is a scenario-driven testing framework for AI agents that simulates r
|
|
|
13
13
|
|
|
14
14
|
Testing with understudy is **4 steps**:
|
|
15
15
|
|
|
16
|
-
1. **Wrap your agent** — Adapt your agent (ADK, LangGraph, HTTP) to understudy's interface
|
|
16
|
+
1. **Wrap your agent** — Adapt your agent (ADK, LangGraph, LangChain, HTTP) to understudy's interface
|
|
17
17
|
2. **Mock your tools** — Register handlers that return test data instead of calling real services
|
|
18
18
|
3. **Write scenes** — YAML files defining what the simulated user wants and what you expect
|
|
19
19
|
4. **Run and assert** — Execute simulations, check traces, generate reports
|
|
20
20
|
|
|
21
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
22
|
|
|
23
|
+
## Two Evaluation Paradigms
|
|
24
|
+
|
|
25
|
+
### Conversational Agent Evaluation
|
|
26
|
+
Simulate multi-turn conversations with personas to test dialog agents.
|
|
27
|
+
- Use case: Customer service bots, assistants, chatbots
|
|
28
|
+
- Assert on tool calls: `trace.called("tool_name")`
|
|
29
|
+
|
|
30
|
+
### Agentic Flow Evaluation
|
|
31
|
+
Evaluate autonomous agents executing multi-step tasks.
|
|
32
|
+
- Use case: Code agents, research agents, task automation
|
|
33
|
+
- Assert on actions: `trace.performed("action")`
|
|
34
|
+
|
|
35
|
+
See [examples/README.md](https://github.com/gojiplus/understudy/blob/main/examples/README.md) for complete examples of both paradigms.
|
|
36
|
+
|
|
23
37
|
**See real examples:**
|
|
24
|
-
- [Example scene](https://github.com/gojiplus/understudy/blob/main/
|
|
25
|
-
- [ADK test file](https://github.com/gojiplus/understudy/blob/main/
|
|
26
|
-
- [LangGraph test file](https://github.com/gojiplus/understudy/blob/main/
|
|
38
|
+
- [Example scene](https://github.com/gojiplus/understudy/blob/main/examples/scenes/return_eligible_backpack.yaml) — YAML defining a test scenario
|
|
39
|
+
- [ADK test file](https://github.com/gojiplus/understudy/blob/main/examples/adk/test_adk_returns.py) — pytest assertions against traces
|
|
40
|
+
- [LangGraph test file](https://github.com/gojiplus/understudy/blob/main/examples/langgraph/test_langgraph_returns.py) — same tests, different framework
|
|
41
|
+
- [Agentic test file](https://github.com/gojiplus/understudy/blob/main/examples/agentic/test_agentic.py) — agentic flow evaluation
|
|
42
|
+
- [Agentic scene](https://github.com/gojiplus/understudy/blob/main/examples/agentic_scenes/code_review_task.yaml) — task-based scenario
|
|
43
|
+
- [Example report](https://htmlpreview.github.io/?https://github.com/gojiplus/understudy/blob/main/examples/langgraph/report/index.html) — HTML report with metrics and transcripts
|
|
44
|
+
|
|
45
|
+
## State injection and next-turn tests
|
|
46
|
+
|
|
47
|
+
Restore a completed checkpoint, inject synthetic history/state, or replay through
|
|
48
|
+
exchange X, then execute and assert only against exchange X+1. ADK and LangGraph
|
|
49
|
+
support isolated checkpoint forks; `LangChainApp` builds an instrumented LangChain
|
|
50
|
+
agent with tool middleware. Traces retain nested invocation identities.
|
|
51
|
+
|
|
52
|
+
See the [state-testing guide](https://gojiplus.github.io/understudy/state-testing.html)
|
|
53
|
+
for `run_turn`, `replay_to`, scoped tool mocks, pytest fixtures, CLI commands, and
|
|
54
|
+
framework-specific restoration limits.
|
|
27
55
|
|
|
28
56
|
## Installation
|
|
29
57
|
|
|
30
58
|
```bash
|
|
31
|
-
pip install understudy[all]
|
|
59
|
+
pip install "understudy[all]"
|
|
32
60
|
```
|
|
33
61
|
|
|
34
62
|
## Quick Start
|
|
@@ -51,9 +79,15 @@ from understudy.mocks import MockToolkit
|
|
|
51
79
|
|
|
52
80
|
mocks = MockToolkit()
|
|
53
81
|
|
|
82
|
+
|
|
54
83
|
@mocks.handle("lookup_order")
|
|
55
84
|
def lookup_order(order_id: str) -> dict:
|
|
56
|
-
return {
|
|
85
|
+
return {
|
|
86
|
+
"order_id": order_id,
|
|
87
|
+
"items": [{"sku": "BACKPACK-01", "quantity": 1}],
|
|
88
|
+
"status": "delivered",
|
|
89
|
+
}
|
|
90
|
+
|
|
57
91
|
|
|
58
92
|
@mocks.handle("create_return")
|
|
59
93
|
def create_return(order_id: str, item_sku: str, reason: str) -> dict:
|
|
@@ -230,7 +264,7 @@ assert result.score == 1
|
|
|
230
264
|
Built-in rubrics:
|
|
231
265
|
|
|
232
266
|
```python
|
|
233
|
-
from understudy.
|
|
267
|
+
from understudy.prompts import (
|
|
234
268
|
TOOL_USAGE_CORRECTNESS,
|
|
235
269
|
POLICY_COMPLIANCE,
|
|
236
270
|
TONE_EMPATHY,
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.12.5,<0.13"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "understudy"
|
|
7
|
+
version = "0.6.0"
|
|
8
|
+
description = "Simulation and trace-based evaluation for agentic systems"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.12"
|
|
13
|
+
keywords = [
|
|
14
|
+
"agent",
|
|
15
|
+
"evaluation",
|
|
16
|
+
"simulation",
|
|
17
|
+
"testing",
|
|
18
|
+
"llm",
|
|
19
|
+
]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 3 - Alpha",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Programming Language :: Python :: 3.14",
|
|
27
|
+
"Topic :: Software Development :: Testing",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"pydantic>=2.0",
|
|
31
|
+
"pyyaml>=6.0",
|
|
32
|
+
"litellm>=1.50",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[[project.authors]]
|
|
36
|
+
name = "Gaurav Sood"
|
|
37
|
+
|
|
38
|
+
[project.optional-dependencies]
|
|
39
|
+
adk = [
|
|
40
|
+
"google-adk>=2.11",
|
|
41
|
+
"python-dotenv>=1.0",
|
|
42
|
+
]
|
|
43
|
+
http = ["httpx>=0.27"]
|
|
44
|
+
langgraph = [
|
|
45
|
+
"langgraph>=1.2.14",
|
|
46
|
+
"langchain-core>=1.6.9",
|
|
47
|
+
"langchain-openai>=0.3",
|
|
48
|
+
]
|
|
49
|
+
server = [
|
|
50
|
+
"fastapi>=0.115",
|
|
51
|
+
"uvicorn>=0.32",
|
|
52
|
+
]
|
|
53
|
+
reports = [
|
|
54
|
+
"jinja2>=3.0",
|
|
55
|
+
"click>=8.0",
|
|
56
|
+
]
|
|
57
|
+
all = ["understudy[adk,http,langgraph,langchain,server,reports]"]
|
|
58
|
+
langchain = [
|
|
59
|
+
"langchain>=1.4.4",
|
|
60
|
+
"langgraph>=1.2.14",
|
|
61
|
+
"langchain-core>=1.6.9",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[project.scripts]
|
|
65
|
+
understudy = "understudy.cli:main"
|
|
66
|
+
|
|
67
|
+
[project.entry-points.pytest11]
|
|
68
|
+
understudy = "understudy.pytest_plugin"
|
|
69
|
+
|
|
70
|
+
[project.urls]
|
|
71
|
+
Homepage = "https://github.com/gojiplus/understudy"
|
|
72
|
+
Documentation = "https://gojiplus.github.io/understudy"
|
|
73
|
+
Repository = "https://github.com/gojiplus/understudy"
|
|
74
|
+
Issues = "https://github.com/gojiplus/understudy/issues"
|
|
75
|
+
|
|
76
|
+
[tool.ruff]
|
|
77
|
+
line-length = 88
|
|
78
|
+
target-version = "py312"
|
|
79
|
+
|
|
80
|
+
[tool.ruff.lint]
|
|
81
|
+
external = ["DOC"]
|
|
82
|
+
select = [
|
|
83
|
+
"E",
|
|
84
|
+
"W",
|
|
85
|
+
"F",
|
|
86
|
+
"I",
|
|
87
|
+
"B",
|
|
88
|
+
"C4",
|
|
89
|
+
"UP",
|
|
90
|
+
"N",
|
|
91
|
+
"D",
|
|
92
|
+
"S",
|
|
93
|
+
"SIM",
|
|
94
|
+
"T20",
|
|
95
|
+
"PT",
|
|
96
|
+
"RUF",
|
|
97
|
+
"PTH",
|
|
98
|
+
"RET",
|
|
99
|
+
"PIE",
|
|
100
|
+
"FURB",
|
|
101
|
+
"PERF",
|
|
102
|
+
"DTZ",
|
|
103
|
+
"LOG",
|
|
104
|
+
"G",
|
|
105
|
+
"TC",
|
|
106
|
+
"FLY",
|
|
107
|
+
"RSE",
|
|
108
|
+
"SLOT",
|
|
109
|
+
"FA",
|
|
110
|
+
"A",
|
|
111
|
+
"EXE",
|
|
112
|
+
"ICN",
|
|
113
|
+
"PGH",
|
|
114
|
+
"PLE",
|
|
115
|
+
"ARG",
|
|
116
|
+
"SLF",
|
|
117
|
+
]
|
|
118
|
+
ignore = [
|
|
119
|
+
"D203",
|
|
120
|
+
"D213",
|
|
121
|
+
"W191",
|
|
122
|
+
"D206",
|
|
123
|
+
"D300",
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
[tool.ruff.lint.pydocstyle]
|
|
127
|
+
convention = "google"
|
|
128
|
+
|
|
129
|
+
[tool.ruff.lint.per-file-ignores]
|
|
130
|
+
"tests/**" = [
|
|
131
|
+
"S101",
|
|
132
|
+
"D",
|
|
133
|
+
"ARG",
|
|
134
|
+
"SLF",
|
|
135
|
+
"S108",
|
|
136
|
+
]
|
|
137
|
+
"docs/**" = ["D"]
|
|
138
|
+
"examples/**" = [
|
|
139
|
+
"S101",
|
|
140
|
+
"T20",
|
|
141
|
+
"D",
|
|
142
|
+
"ARG",
|
|
143
|
+
"A002",
|
|
144
|
+
]
|
|
145
|
+
"src/understudy/server/models.py" = ["N815"]
|
|
146
|
+
|
|
147
|
+
[tool.pytest.ini_options]
|
|
148
|
+
testpaths = ["tests"]
|
|
149
|
+
asyncio_mode = "auto"
|
|
150
|
+
minversion = "6"
|
|
151
|
+
log_level = "INFO"
|
|
152
|
+
xfail_strict = true
|
|
153
|
+
filterwarnings = ["error"]
|
|
154
|
+
addopts = [
|
|
155
|
+
"--strict-config",
|
|
156
|
+
"--strict-markers",
|
|
157
|
+
"-ra",
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
[tool.coverage.run]
|
|
161
|
+
source = ["src/understudy"]
|
|
162
|
+
branch = true
|
|
163
|
+
|
|
164
|
+
[tool.coverage.report]
|
|
165
|
+
show_missing = true
|
|
166
|
+
fail_under = 80
|
|
167
|
+
|
|
168
|
+
[tool.pyright]
|
|
169
|
+
include = ["src"]
|
|
170
|
+
typeCheckingMode = "standard"
|
|
171
|
+
|
|
172
|
+
[tool.pydoclint]
|
|
173
|
+
style = "google"
|
|
174
|
+
arg-type-hints-in-docstring = false
|
|
175
|
+
check-return-types = false
|
|
176
|
+
check-class-attributes = false
|
|
177
|
+
allow-init-docstring = true
|
|
178
|
+
exclude = '\.venv|tests|docs'
|
|
179
|
+
|
|
180
|
+
[tool.codespell]
|
|
181
|
+
builtin = "clear,rare,en-GB_to_en-US"
|
|
182
|
+
|
|
183
|
+
[dependency-groups]
|
|
184
|
+
docs = [
|
|
185
|
+
"sphinx>=7.0",
|
|
186
|
+
"furo>=2024.0",
|
|
187
|
+
"myst-parser>=3.0",
|
|
188
|
+
"sphinx-autodoc-typehints>=2.0",
|
|
189
|
+
"sphinx-copybutton",
|
|
190
|
+
"py-canon @ git+https://github.com/gojiplus/py-canon@v1",
|
|
191
|
+
]
|
|
192
|
+
test = [
|
|
193
|
+
"pytest>=8.0",
|
|
194
|
+
"pytest-asyncio>=0.23",
|
|
195
|
+
"pytest-cov>=4.0",
|
|
196
|
+
"httpx2>=2.12",
|
|
197
|
+
]
|
|
198
|
+
dev = [
|
|
199
|
+
"pytest>=8.0",
|
|
200
|
+
"pytest-asyncio>=0.23",
|
|
201
|
+
"pytest-cov>=4.0",
|
|
202
|
+
"ruff>=0.4",
|
|
203
|
+
{ include-group = "test" },
|
|
204
|
+
{ include-group = "docs" },
|
|
205
|
+
"pyright>=1.1.390",
|
|
206
|
+
"pydoclint>=0.5",
|
|
207
|
+
]
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.12.5,<0.13"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "understudy"
|
|
7
|
+
version = "0.6.0"
|
|
8
|
+
description = "Simulation and trace-based evaluation for agentic systems"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.12"
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Gaurav Sood" },
|
|
15
|
+
]
|
|
16
|
+
keywords = ["agent", "evaluation", "simulation", "testing", "llm"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
24
|
+
"Topic :: Software Development :: Testing",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"pydantic>=2.0",
|
|
28
|
+
"pyyaml>=6.0",
|
|
29
|
+
"litellm>=1.50",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
adk = ["google-adk>=2.11", "python-dotenv>=1.0"]
|
|
34
|
+
http = ["httpx>=0.27"]
|
|
35
|
+
langgraph = ["langgraph>=1.2.14", "langchain-core>=1.6.9", "langchain-openai>=0.3"]
|
|
36
|
+
server = ["fastapi>=0.115", "uvicorn>=0.32"]
|
|
37
|
+
reports = ["jinja2>=3.0", "click>=8.0"]
|
|
38
|
+
all = ["understudy[adk,http,langgraph,langchain,server,reports]"]
|
|
39
|
+
langchain = [
|
|
40
|
+
"langchain>=1.4.4",
|
|
41
|
+
"langgraph>=1.2.14",
|
|
42
|
+
"langchain-core>=1.6.9",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.scripts]
|
|
46
|
+
understudy = "understudy.cli:main"
|
|
47
|
+
|
|
48
|
+
[project.entry-points.pytest11]
|
|
49
|
+
understudy = "understudy.pytest_plugin"
|
|
50
|
+
|
|
51
|
+
[project.urls]
|
|
52
|
+
Homepage = "https://github.com/gojiplus/understudy"
|
|
53
|
+
Documentation = "https://gojiplus.github.io/understudy"
|
|
54
|
+
Repository = "https://github.com/gojiplus/understudy"
|
|
55
|
+
Issues = "https://github.com/gojiplus/understudy/issues"
|
|
56
|
+
|
|
57
|
+
[tool.ruff]
|
|
58
|
+
line-length = 88
|
|
59
|
+
target-version = "py312"
|
|
60
|
+
|
|
61
|
+
[tool.ruff.lint]
|
|
62
|
+
external = ["DOC"]
|
|
63
|
+
select = [
|
|
64
|
+
"E", "W", "F", "I", "B", "C4", "UP", "N", "D", "S", "SIM", "T20", "PT", "RUF",
|
|
65
|
+
"PTH", "RET", "PIE", "FURB", "PERF", "DTZ", "LOG", "G", "TC", "FLY",
|
|
66
|
+
"RSE", "SLOT", "FA", "A", "EXE", "ICN", "PGH", "PLE", "ARG", "SLF",
|
|
67
|
+
]
|
|
68
|
+
# D203/D213: the google convention the fleet standard picks. W191/D206/D300:
|
|
69
|
+
# ruff's own docs list these as always incompatible with `ruff format`, which
|
|
70
|
+
# the standard also runs.
|
|
71
|
+
ignore = ["D203", "D213", "W191", "D206", "D300"]
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint.pydocstyle]
|
|
74
|
+
convention = "google"
|
|
75
|
+
|
|
76
|
+
[tool.ruff.lint.per-file-ignores]
|
|
77
|
+
# S108: "/tmp/..." strings in tests are inert fixture data, never opened.
|
|
78
|
+
"tests/**" = ["S101", "D", "ARG", "SLF", "S108"]
|
|
79
|
+
"docs/**" = ["D"]
|
|
80
|
+
# Runnable demo scripts: asserts and prints are their idiom, unused callback
|
|
81
|
+
# arguments mirror the framework signatures they demonstrate, and mock tool
|
|
82
|
+
# handlers must reuse the tool's own argument names (`format` included)
|
|
83
|
+
# because mocks are invoked with keyword arguments from the scene.
|
|
84
|
+
"examples/**" = ["S101", "T20", "D", "ARG", "A002"]
|
|
85
|
+
# Request/response models mirror the UI client's camelCase wire schema.
|
|
86
|
+
"src/understudy/server/models.py" = ["N815"]
|
|
87
|
+
|
|
88
|
+
[tool.pytest.ini_options]
|
|
89
|
+
testpaths = ["tests"]
|
|
90
|
+
asyncio_mode = "auto"
|
|
91
|
+
minversion = "6"
|
|
92
|
+
log_level = "INFO"
|
|
93
|
+
xfail_strict = true
|
|
94
|
+
filterwarnings = ["error"]
|
|
95
|
+
addopts = ["--strict-config", "--strict-markers", "-ra"]
|
|
96
|
+
|
|
97
|
+
[tool.coverage.run]
|
|
98
|
+
source = ["src/understudy"]
|
|
99
|
+
branch = true
|
|
100
|
+
|
|
101
|
+
[tool.coverage.report]
|
|
102
|
+
show_missing = true
|
|
103
|
+
fail_under = 80
|
|
104
|
+
|
|
105
|
+
[tool.pyright]
|
|
106
|
+
include = ["src"]
|
|
107
|
+
typeCheckingMode = "standard"
|
|
108
|
+
|
|
109
|
+
[tool.pydoclint]
|
|
110
|
+
style = "google"
|
|
111
|
+
arg-type-hints-in-docstring = false
|
|
112
|
+
check-return-types = false
|
|
113
|
+
check-class-attributes = false
|
|
114
|
+
allow-init-docstring = true
|
|
115
|
+
exclude = '\.venv|tests|docs'
|
|
116
|
+
|
|
117
|
+
[dependency-groups]
|
|
118
|
+
# Development concerns, not user-facing optional features -- extras are for
|
|
119
|
+
# what a consumer might install, and nobody installs understudy to build its
|
|
120
|
+
# docs. CI syncs `--all-extras --all-groups`.
|
|
121
|
+
docs = [
|
|
122
|
+
"sphinx>=7.0",
|
|
123
|
+
"furo>=2024.0",
|
|
124
|
+
"myst-parser>=3.0",
|
|
125
|
+
"sphinx-autodoc-typehints>=2.0",
|
|
126
|
+
"sphinx-copybutton",
|
|
127
|
+
"py-canon @ git+https://github.com/gojiplus/py-canon@v1",
|
|
128
|
+
]
|
|
129
|
+
test = [
|
|
130
|
+
"pytest>=8.0",
|
|
131
|
+
"pytest-asyncio>=0.23",
|
|
132
|
+
"pytest-cov>=4.0",
|
|
133
|
+
# starlette's TestClient (reached through fastapi.testclient) warns that
|
|
134
|
+
# driving it with httpx is deprecated and names httpx2 as the replacement.
|
|
135
|
+
# Under filterwarnings = ["error"] that notice fails collection of
|
|
136
|
+
# tests/test_server.py outright.
|
|
137
|
+
"httpx2>=2.12",
|
|
138
|
+
]
|
|
139
|
+
dev = [
|
|
140
|
+
"pytest>=8.0",
|
|
141
|
+
"pytest-asyncio>=0.23",
|
|
142
|
+
"pytest-cov>=4.0",
|
|
143
|
+
"ruff>=0.4",
|
|
144
|
+
{ include-group = "test" },
|
|
145
|
+
{ include-group = "docs" },
|
|
146
|
+
"pyright>=1.1.390",
|
|
147
|
+
"pydoclint>=0.5",
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
[tool.codespell]
|
|
151
|
+
builtin = "clear,rare,en-GB_to_en-US"
|