ai-agent-inspector 1.0.0__tar.gz → 1.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.
- {ai_agent_inspector-1.0.0/ai_agent_inspector.egg-info → ai_agent_inspector-1.1.0}/PKG-INFO +122 -26
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/README.md +120 -25
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/agent_inspector/__init__.py +30 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/agent_inspector/cli.py +22 -3
- ai_agent_inspector-1.1.0/agent_inspector/ui/static/app.css +1203 -0
- ai_agent_inspector-1.1.0/agent_inspector/ui/templates/index.html +749 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0/ai_agent_inspector.egg-info}/PKG-INFO +122 -26
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/ai_agent_inspector.egg-info/SOURCES.txt +4 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/ai_agent_inspector.egg-info/requires.txt +1 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/pyproject.toml +2 -1
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_api.py +47 -0
- ai_agent_inspector-1.1.0/tests/test_autogen_adapter.py +402 -0
- ai_agent_inspector-1.1.0/tests/test_cli.py +91 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_config.py +22 -0
- ai_agent_inspector-1.1.0/tests/test_crewai_adapter.py +549 -0
- ai_agent_inspector-1.1.0/tests/test_multi_agent.py +958 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_storage.py +51 -0
- ai_agent_inspector-1.0.0/agent_inspector/ui/static/app.css +0 -630
- ai_agent_inspector-1.0.0/agent_inspector/ui/templates/index.html +0 -441
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/LICENSE +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/agent_inspector/ui/static/app.js +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/ai_agent_inspector.egg-info/dependency_links.txt +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/ai_agent_inspector.egg-info/entry_points.txt +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/ai_agent_inspector.egg-info/top_level.txt +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/setup.cfg +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_api_auth.py +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_api_error_paths.py +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_exporter.py +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_exporters.py +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_init_imports.py +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_interfaces.py +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_langchain_adapter.py +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_processing.py +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_queue.py +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_trace.py +0 -0
- {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_ui_setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ai-agent-inspector
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: Framework-agnostic observability for AI agents
|
|
5
5
|
Author-email: Agent Inspector Team <team@agentinspector.dev>
|
|
6
6
|
License: MIT
|
|
@@ -27,6 +27,7 @@ Requires-Dist: uvicorn[standard]>=0.24.0
|
|
|
27
27
|
Requires-Dist: cryptography>=41.0.0
|
|
28
28
|
Requires-Dist: jinja2>=3.1.0
|
|
29
29
|
Requires-Dist: python-dotenv>=1.0.0
|
|
30
|
+
Requires-Dist: openai>=2.16.0
|
|
30
31
|
Provides-Extra: langchain
|
|
31
32
|
Requires-Dist: langchain>=0.1.0; extra == "langchain"
|
|
32
33
|
Provides-Extra: otel
|
|
@@ -154,7 +155,7 @@ Traditional tools model systems as function calls and spans. Agent Inspector mod
|
|
|
154
155
|
|
|
155
156
|
### Storage
|
|
156
157
|
- **SQLite** – WAL mode for concurrent access; runs and steps tables; indexes on run_id and timestamp.
|
|
157
|
-
- **Pruning** – CLI `prune --retention-days N` and optional `--vacuum`; API/DB support for retention.
|
|
158
|
+
- **Pruning** – CLI `prune --retention-days N` and optional `--retention-max-bytes BYTES`, `--vacuum`; API/DB support for retention by age and by size.
|
|
158
159
|
- **Backup** – CLI `backup /path/to/backup.db` for full DB copy.
|
|
159
160
|
- **Export to JSON** – **API** `GET /v1/runs/{run_id}/export` returns run metadata + timeline with decoded event data; **CLI** `agent-inspector export <run_id> [--output file.json]` and `agent-inspector export --all [--limit N] [--output file.json]` for backup or migration.
|
|
160
161
|
|
|
@@ -506,6 +507,7 @@ export TRACE_ENCRYPTION_KEY=your-secret-key-here
|
|
|
506
507
|
# Storage
|
|
507
508
|
export TRACE_DB_PATH=agent_inspector.db
|
|
508
509
|
export TRACE_RETENTION_DAYS=30
|
|
510
|
+
export TRACE_RETENTION_MAX_BYTES=
|
|
509
511
|
|
|
510
512
|
# API
|
|
511
513
|
export TRACE_API_HOST=127.0.0.1
|
|
@@ -604,24 +606,78 @@ search_flights_agent("Find flights from SFO to JFK")
|
|
|
604
606
|
This example makes real LLM calls and runs multiple scenarios.
|
|
605
607
|
|
|
606
608
|
```bash
|
|
607
|
-
cp
|
|
609
|
+
cp examples/.env.example examples/.env
|
|
608
610
|
```
|
|
609
611
|
|
|
610
|
-
Set these in
|
|
611
|
-
- `
|
|
612
|
-
- `
|
|
613
|
-
- `OPENAI_MODEL`
|
|
612
|
+
Set these in `examples/.env`:
|
|
613
|
+
- `OPENAI_API_KEY` - Your API key
|
|
614
|
+
- `OPENAI_BASE_URL` - API endpoint (e.g., `https://api.openai.com/v1` or your custom provider)
|
|
615
|
+
- `OPENAI_MODEL` - Model name (e.g., `gpt-4o-mini`, `glm-4.7`)
|
|
616
|
+
- `OPENAI_TEMPERATURE` - Temperature setting (default: 0.2)
|
|
617
|
+
- `OPENAI_TIMEOUT` - Timeout in seconds (default: 120)
|
|
618
|
+
|
|
619
|
+
Install dependencies:
|
|
620
|
+
```bash
|
|
621
|
+
uv add openai python-dotenv
|
|
622
|
+
```
|
|
614
623
|
|
|
615
624
|
Run a single question:
|
|
616
625
|
```bash
|
|
617
|
-
python examples/real_agent.py "What is 13 * (7 + 5)?"
|
|
626
|
+
uv run python examples/real_agent.py "What is 13 * (7 + 5)?"
|
|
618
627
|
```
|
|
619
628
|
|
|
620
629
|
Run the full scenario suite:
|
|
621
630
|
```bash
|
|
622
|
-
python examples/real_agent.py --suite
|
|
631
|
+
uv run python examples/real_agent.py --suite
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### Multi-Agent Example
|
|
635
|
+
|
|
636
|
+
This example demonstrates a realistic multi-agent customer support system with:
|
|
637
|
+
- **Agent spawning** with different models per agent
|
|
638
|
+
- **Intelligent routing** to specialized agents (billing, technical, triage, manager)
|
|
639
|
+
- **Tool execution** with realistic operations (profile lookup, billing history, system logs)
|
|
640
|
+
- **Agent communication** with handoffs for escalations
|
|
641
|
+
- **Detailed responses** with contextual, professional customer service replies
|
|
642
|
+
- **Escalation workflow** where complex issues get manager oversight
|
|
643
|
+
|
|
644
|
+
```bash
|
|
645
|
+
cp examples/.env.example examples/.env
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
Configure in `examples/.env`:
|
|
649
|
+
- `OPENAI_API_KEY` - Your API key
|
|
650
|
+
- `OPENAI_BASE_URL` - API endpoint
|
|
651
|
+
- `OPENAI_MODEL` - Default model for all agents
|
|
652
|
+
- `MODEL_TRIAGE` - Model for triage agent (optional, falls back to `OPENAI_MODEL`)
|
|
653
|
+
- `MODEL_BILLING` - Model for billing agent (optional)
|
|
654
|
+
- `MODEL_TECHNICAL` - Model for technical agent (optional)
|
|
655
|
+
- `MODEL_MANAGER` - Model for manager agent (optional)
|
|
656
|
+
|
|
657
|
+
Install dependencies:
|
|
658
|
+
```bash
|
|
659
|
+
uv add openai python-dotenv
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
Run in simulated mode (no API needed):
|
|
663
|
+
```bash
|
|
664
|
+
python examples/multi_agent.py
|
|
623
665
|
```
|
|
624
666
|
|
|
667
|
+
Run with real LLM calls:
|
|
668
|
+
```bash
|
|
669
|
+
uv run python examples/multi_agent.py
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
The example traces:
|
|
673
|
+
- Customer requests with routing analysis
|
|
674
|
+
- Agent-specific tool usage with realistic results
|
|
675
|
+
- Detailed, contextual responses for each customer issue
|
|
676
|
+
- Escalation flows with manager handoffs
|
|
677
|
+
- Task assignment and completion tracking
|
|
678
|
+
|
|
679
|
+
Note: Without `openai` package and valid API key, this example will use simulated responses with realistic agent behavior. Install `openai` with `uv add openai` and configure `OPENAI_API_KEY` in `examples/.env` for real LLM calls. Use `uv run python` to execute the script with uv's virtual environment.
|
|
680
|
+
|
|
625
681
|
### With LangChain (Automatic)
|
|
626
682
|
|
|
627
683
|
```python
|
|
@@ -716,6 +772,30 @@ with trace.run("planning_agent", user_id="user123") as main_ctx:
|
|
|
716
772
|
trace.final(answer="I've booked your flight. Confirmation: CONF-12345")
|
|
717
773
|
```
|
|
718
774
|
|
|
775
|
+
### Async / asyncio
|
|
776
|
+
|
|
777
|
+
Context is propagated via `contextvars`, so tracing works with asyncio as long as each task has its own `trace.run()` (one run per task). Do not share a single run across concurrent tasks.
|
|
778
|
+
|
|
779
|
+
```python
|
|
780
|
+
import asyncio
|
|
781
|
+
from agent_inspector import trace
|
|
782
|
+
|
|
783
|
+
async def agent_task(name: str, query: str):
|
|
784
|
+
with trace.run(name):
|
|
785
|
+
trace.llm(model="gpt-4", prompt=query, response=f"Processed: {query}")
|
|
786
|
+
trace.final(answer=f"Done: {query}")
|
|
787
|
+
return name
|
|
788
|
+
|
|
789
|
+
async def main():
|
|
790
|
+
results = await asyncio.gather(
|
|
791
|
+
agent_task("agent_1", "Query A"),
|
|
792
|
+
agent_task("agent_2", "Query B"),
|
|
793
|
+
)
|
|
794
|
+
return results
|
|
795
|
+
|
|
796
|
+
asyncio.run(main())
|
|
797
|
+
```
|
|
798
|
+
|
|
719
799
|
### Memory Operations
|
|
720
800
|
|
|
721
801
|
```python
|
|
@@ -836,35 +916,41 @@ result = chain.run("Your query", callbacks=callbacks)
|
|
|
836
916
|
|
|
837
917
|
### Creating Custom Adapters
|
|
838
918
|
|
|
839
|
-
|
|
919
|
+
Use the Trace SDK directly when your framework has no LangChain-style callback API. Checklist:
|
|
920
|
+
|
|
921
|
+
1. **Entry point** – Wrap agent execution in `trace.run("run_name")` so there is an active context.
|
|
922
|
+
2. **LLM calls** – Where your framework invokes the model, call `context.llm(model=..., prompt=..., response=...)`.
|
|
923
|
+
3. **Tool calls** – Where tools are executed, call `context.tool(tool_name=..., tool_args=..., tool_result=...)`.
|
|
924
|
+
4. **Final answer** – When the agent finishes, call `context.final(answer=...)`.
|
|
925
|
+
5. **Errors** – On failure, call `context.error(error_type=..., error_message=..., critical=...)`.
|
|
926
|
+
|
|
927
|
+
Template:
|
|
840
928
|
|
|
841
929
|
```python
|
|
842
|
-
from agent_inspector import
|
|
930
|
+
from agent_inspector import trace, get_trace
|
|
843
931
|
|
|
844
932
|
class CustomAdapter:
|
|
845
|
-
def __init__(self,
|
|
846
|
-
self.trace =
|
|
847
|
-
|
|
848
|
-
def on_llm_call(self, model, prompt, response):
|
|
849
|
-
"""Handle LLM calls in your framework."""
|
|
933
|
+
def __init__(self, trace_instance=None):
|
|
934
|
+
self.trace = trace_instance or get_trace()
|
|
935
|
+
|
|
936
|
+
def on_llm_call(self, model: str, prompt: str, response: str):
|
|
850
937
|
context = self.trace.get_active_context()
|
|
851
938
|
if context:
|
|
852
939
|
context.llm(model=model, prompt=prompt, response=response)
|
|
853
|
-
|
|
854
|
-
def on_tool_call(self, tool_name,
|
|
855
|
-
"""Handle tool calls in your framework."""
|
|
940
|
+
|
|
941
|
+
def on_tool_call(self, tool_name: str, tool_args: dict, tool_result: str):
|
|
856
942
|
context = self.trace.get_active_context()
|
|
857
943
|
if context:
|
|
858
|
-
context.tool(tool_name=tool_name, tool_args=
|
|
944
|
+
context.tool(tool_name=tool_name, tool_args=tool_args, tool_result=tool_result)
|
|
859
945
|
|
|
860
|
-
# Use
|
|
861
|
-
with trace.run("
|
|
946
|
+
# Use: always run inside trace.run() so get_active_context() returns a context
|
|
947
|
+
with trace.run("my_agent"):
|
|
862
948
|
adapter = CustomAdapter()
|
|
863
|
-
|
|
864
|
-
# Your framework code
|
|
865
949
|
adapter.on_llm_call("gpt-4", "Hello", "Hi there!")
|
|
866
950
|
```
|
|
867
951
|
|
|
952
|
+
For LangChain-like frameworks, extend `BaseCallbackHandler` and pass the handler into the framework's callback list; see the LangChain adapter source for the pattern.
|
|
953
|
+
|
|
868
954
|
---
|
|
869
955
|
|
|
870
956
|
## Development
|
|
@@ -952,8 +1038,12 @@ Releases are automated with [Release Please](https://github.com/googleapis/relea
|
|
|
952
1038
|
- **fix:** – bug fix (bumps patch version)
|
|
953
1039
|
- **feat!:** or **BREAKING CHANGE:** – breaking change (bumps major version)
|
|
954
1040
|
|
|
1041
|
+
To force a specific version, add `Release-As: X.Y.Z` in the commit message footer (e.g. `Release-As: 1.1.0`).
|
|
1042
|
+
|
|
955
1043
|
When you merge the Release PR, a tag is created and the [publish workflow](.github/workflows/publish.yml) publishes to PyPI (OIDC).
|
|
956
1044
|
|
|
1045
|
+
**First release:** Release Please only creates Release PRs for commits *since* the latest release. If you have no release yet, create the initial tag so it has a baseline: `git tag v1.0.0 && git push origin v1.0.0`. After that, new `feat:`/`fix:` commits (not `docs:` or `chore:`) will get Release PRs.
|
|
1046
|
+
|
|
957
1047
|
---
|
|
958
1048
|
|
|
959
1049
|
## Contributing
|
|
@@ -1011,8 +1101,8 @@ agent-inspector server [--host HOST] [--port PORT]
|
|
|
1011
1101
|
# View statistics
|
|
1012
1102
|
agent-inspector stats
|
|
1013
1103
|
|
|
1014
|
-
# Prune old traces
|
|
1015
|
-
agent-inspector prune [--retention-days N] [--vacuum]
|
|
1104
|
+
# Prune old traces (optionally by size: --retention-max-bytes BYTES)
|
|
1105
|
+
agent-inspector prune [--retention-days N] [--retention-max-bytes BYTES] [--vacuum]
|
|
1016
1106
|
|
|
1017
1107
|
# Vacuum database
|
|
1018
1108
|
agent-inspector vacuum
|
|
@@ -1047,6 +1137,12 @@ Agent Inspector is designed for minimal overhead:
|
|
|
1047
1137
|
- Background thread: ~5MB (batch processing)
|
|
1048
1138
|
- Database: Varies with trace volume
|
|
1049
1139
|
|
|
1140
|
+
### Scaling and alerting
|
|
1141
|
+
|
|
1142
|
+
- **Single process / moderate load** – Use the default SQLite storage with sampling and retention (e.g. `retention_days`, optional `retention_max_bytes`). Suitable for one or a few worker processes.
|
|
1143
|
+
- **High throughput or many writers** – Use an OTLP or custom exporter to send traces to a central backend (e.g. Jaeger, Tempo, Grafana). The built-in UI and API then serve only that process; aggregate viewing is in your backend.
|
|
1144
|
+
- **Alerting** – The SDK does not push alerts. Use the API from your own checks: e.g. `GET /v1/stats` for `failed_runs`, `recent_runs_24h`, or `queue.events_dropped` (when the default Trace is in use), and alert when thresholds are exceeded. Optionally run `agent-inspector prune` on a schedule to enforce retention.
|
|
1145
|
+
|
|
1050
1146
|
---
|
|
1051
1147
|
|
|
1052
1148
|
## Security
|
|
@@ -107,7 +107,7 @@ Traditional tools model systems as function calls and spans. Agent Inspector mod
|
|
|
107
107
|
|
|
108
108
|
### Storage
|
|
109
109
|
- **SQLite** – WAL mode for concurrent access; runs and steps tables; indexes on run_id and timestamp.
|
|
110
|
-
- **Pruning** – CLI `prune --retention-days N` and optional `--vacuum`; API/DB support for retention.
|
|
110
|
+
- **Pruning** – CLI `prune --retention-days N` and optional `--retention-max-bytes BYTES`, `--vacuum`; API/DB support for retention by age and by size.
|
|
111
111
|
- **Backup** – CLI `backup /path/to/backup.db` for full DB copy.
|
|
112
112
|
- **Export to JSON** – **API** `GET /v1/runs/{run_id}/export` returns run metadata + timeline with decoded event data; **CLI** `agent-inspector export <run_id> [--output file.json]` and `agent-inspector export --all [--limit N] [--output file.json]` for backup or migration.
|
|
113
113
|
|
|
@@ -459,6 +459,7 @@ export TRACE_ENCRYPTION_KEY=your-secret-key-here
|
|
|
459
459
|
# Storage
|
|
460
460
|
export TRACE_DB_PATH=agent_inspector.db
|
|
461
461
|
export TRACE_RETENTION_DAYS=30
|
|
462
|
+
export TRACE_RETENTION_MAX_BYTES=
|
|
462
463
|
|
|
463
464
|
# API
|
|
464
465
|
export TRACE_API_HOST=127.0.0.1
|
|
@@ -557,24 +558,78 @@ search_flights_agent("Find flights from SFO to JFK")
|
|
|
557
558
|
This example makes real LLM calls and runs multiple scenarios.
|
|
558
559
|
|
|
559
560
|
```bash
|
|
560
|
-
cp
|
|
561
|
+
cp examples/.env.example examples/.env
|
|
561
562
|
```
|
|
562
563
|
|
|
563
|
-
Set these in
|
|
564
|
-
- `
|
|
565
|
-
- `
|
|
566
|
-
- `OPENAI_MODEL`
|
|
564
|
+
Set these in `examples/.env`:
|
|
565
|
+
- `OPENAI_API_KEY` - Your API key
|
|
566
|
+
- `OPENAI_BASE_URL` - API endpoint (e.g., `https://api.openai.com/v1` or your custom provider)
|
|
567
|
+
- `OPENAI_MODEL` - Model name (e.g., `gpt-4o-mini`, `glm-4.7`)
|
|
568
|
+
- `OPENAI_TEMPERATURE` - Temperature setting (default: 0.2)
|
|
569
|
+
- `OPENAI_TIMEOUT` - Timeout in seconds (default: 120)
|
|
570
|
+
|
|
571
|
+
Install dependencies:
|
|
572
|
+
```bash
|
|
573
|
+
uv add openai python-dotenv
|
|
574
|
+
```
|
|
567
575
|
|
|
568
576
|
Run a single question:
|
|
569
577
|
```bash
|
|
570
|
-
python examples/real_agent.py "What is 13 * (7 + 5)?"
|
|
578
|
+
uv run python examples/real_agent.py "What is 13 * (7 + 5)?"
|
|
571
579
|
```
|
|
572
580
|
|
|
573
581
|
Run the full scenario suite:
|
|
574
582
|
```bash
|
|
575
|
-
python examples/real_agent.py --suite
|
|
583
|
+
uv run python examples/real_agent.py --suite
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### Multi-Agent Example
|
|
587
|
+
|
|
588
|
+
This example demonstrates a realistic multi-agent customer support system with:
|
|
589
|
+
- **Agent spawning** with different models per agent
|
|
590
|
+
- **Intelligent routing** to specialized agents (billing, technical, triage, manager)
|
|
591
|
+
- **Tool execution** with realistic operations (profile lookup, billing history, system logs)
|
|
592
|
+
- **Agent communication** with handoffs for escalations
|
|
593
|
+
- **Detailed responses** with contextual, professional customer service replies
|
|
594
|
+
- **Escalation workflow** where complex issues get manager oversight
|
|
595
|
+
|
|
596
|
+
```bash
|
|
597
|
+
cp examples/.env.example examples/.env
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
Configure in `examples/.env`:
|
|
601
|
+
- `OPENAI_API_KEY` - Your API key
|
|
602
|
+
- `OPENAI_BASE_URL` - API endpoint
|
|
603
|
+
- `OPENAI_MODEL` - Default model for all agents
|
|
604
|
+
- `MODEL_TRIAGE` - Model for triage agent (optional, falls back to `OPENAI_MODEL`)
|
|
605
|
+
- `MODEL_BILLING` - Model for billing agent (optional)
|
|
606
|
+
- `MODEL_TECHNICAL` - Model for technical agent (optional)
|
|
607
|
+
- `MODEL_MANAGER` - Model for manager agent (optional)
|
|
608
|
+
|
|
609
|
+
Install dependencies:
|
|
610
|
+
```bash
|
|
611
|
+
uv add openai python-dotenv
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
Run in simulated mode (no API needed):
|
|
615
|
+
```bash
|
|
616
|
+
python examples/multi_agent.py
|
|
576
617
|
```
|
|
577
618
|
|
|
619
|
+
Run with real LLM calls:
|
|
620
|
+
```bash
|
|
621
|
+
uv run python examples/multi_agent.py
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
The example traces:
|
|
625
|
+
- Customer requests with routing analysis
|
|
626
|
+
- Agent-specific tool usage with realistic results
|
|
627
|
+
- Detailed, contextual responses for each customer issue
|
|
628
|
+
- Escalation flows with manager handoffs
|
|
629
|
+
- Task assignment and completion tracking
|
|
630
|
+
|
|
631
|
+
Note: Without `openai` package and valid API key, this example will use simulated responses with realistic agent behavior. Install `openai` with `uv add openai` and configure `OPENAI_API_KEY` in `examples/.env` for real LLM calls. Use `uv run python` to execute the script with uv's virtual environment.
|
|
632
|
+
|
|
578
633
|
### With LangChain (Automatic)
|
|
579
634
|
|
|
580
635
|
```python
|
|
@@ -669,6 +724,30 @@ with trace.run("planning_agent", user_id="user123") as main_ctx:
|
|
|
669
724
|
trace.final(answer="I've booked your flight. Confirmation: CONF-12345")
|
|
670
725
|
```
|
|
671
726
|
|
|
727
|
+
### Async / asyncio
|
|
728
|
+
|
|
729
|
+
Context is propagated via `contextvars`, so tracing works with asyncio as long as each task has its own `trace.run()` (one run per task). Do not share a single run across concurrent tasks.
|
|
730
|
+
|
|
731
|
+
```python
|
|
732
|
+
import asyncio
|
|
733
|
+
from agent_inspector import trace
|
|
734
|
+
|
|
735
|
+
async def agent_task(name: str, query: str):
|
|
736
|
+
with trace.run(name):
|
|
737
|
+
trace.llm(model="gpt-4", prompt=query, response=f"Processed: {query}")
|
|
738
|
+
trace.final(answer=f"Done: {query}")
|
|
739
|
+
return name
|
|
740
|
+
|
|
741
|
+
async def main():
|
|
742
|
+
results = await asyncio.gather(
|
|
743
|
+
agent_task("agent_1", "Query A"),
|
|
744
|
+
agent_task("agent_2", "Query B"),
|
|
745
|
+
)
|
|
746
|
+
return results
|
|
747
|
+
|
|
748
|
+
asyncio.run(main())
|
|
749
|
+
```
|
|
750
|
+
|
|
672
751
|
### Memory Operations
|
|
673
752
|
|
|
674
753
|
```python
|
|
@@ -789,35 +868,41 @@ result = chain.run("Your query", callbacks=callbacks)
|
|
|
789
868
|
|
|
790
869
|
### Creating Custom Adapters
|
|
791
870
|
|
|
792
|
-
|
|
871
|
+
Use the Trace SDK directly when your framework has no LangChain-style callback API. Checklist:
|
|
872
|
+
|
|
873
|
+
1. **Entry point** – Wrap agent execution in `trace.run("run_name")` so there is an active context.
|
|
874
|
+
2. **LLM calls** – Where your framework invokes the model, call `context.llm(model=..., prompt=..., response=...)`.
|
|
875
|
+
3. **Tool calls** – Where tools are executed, call `context.tool(tool_name=..., tool_args=..., tool_result=...)`.
|
|
876
|
+
4. **Final answer** – When the agent finishes, call `context.final(answer=...)`.
|
|
877
|
+
5. **Errors** – On failure, call `context.error(error_type=..., error_message=..., critical=...)`.
|
|
878
|
+
|
|
879
|
+
Template:
|
|
793
880
|
|
|
794
881
|
```python
|
|
795
|
-
from agent_inspector import
|
|
882
|
+
from agent_inspector import trace, get_trace
|
|
796
883
|
|
|
797
884
|
class CustomAdapter:
|
|
798
|
-
def __init__(self,
|
|
799
|
-
self.trace =
|
|
800
|
-
|
|
801
|
-
def on_llm_call(self, model, prompt, response):
|
|
802
|
-
"""Handle LLM calls in your framework."""
|
|
885
|
+
def __init__(self, trace_instance=None):
|
|
886
|
+
self.trace = trace_instance or get_trace()
|
|
887
|
+
|
|
888
|
+
def on_llm_call(self, model: str, prompt: str, response: str):
|
|
803
889
|
context = self.trace.get_active_context()
|
|
804
890
|
if context:
|
|
805
891
|
context.llm(model=model, prompt=prompt, response=response)
|
|
806
|
-
|
|
807
|
-
def on_tool_call(self, tool_name,
|
|
808
|
-
"""Handle tool calls in your framework."""
|
|
892
|
+
|
|
893
|
+
def on_tool_call(self, tool_name: str, tool_args: dict, tool_result: str):
|
|
809
894
|
context = self.trace.get_active_context()
|
|
810
895
|
if context:
|
|
811
|
-
context.tool(tool_name=tool_name, tool_args=
|
|
896
|
+
context.tool(tool_name=tool_name, tool_args=tool_args, tool_result=tool_result)
|
|
812
897
|
|
|
813
|
-
# Use
|
|
814
|
-
with trace.run("
|
|
898
|
+
# Use: always run inside trace.run() so get_active_context() returns a context
|
|
899
|
+
with trace.run("my_agent"):
|
|
815
900
|
adapter = CustomAdapter()
|
|
816
|
-
|
|
817
|
-
# Your framework code
|
|
818
901
|
adapter.on_llm_call("gpt-4", "Hello", "Hi there!")
|
|
819
902
|
```
|
|
820
903
|
|
|
904
|
+
For LangChain-like frameworks, extend `BaseCallbackHandler` and pass the handler into the framework's callback list; see the LangChain adapter source for the pattern.
|
|
905
|
+
|
|
821
906
|
---
|
|
822
907
|
|
|
823
908
|
## Development
|
|
@@ -905,8 +990,12 @@ Releases are automated with [Release Please](https://github.com/googleapis/relea
|
|
|
905
990
|
- **fix:** – bug fix (bumps patch version)
|
|
906
991
|
- **feat!:** or **BREAKING CHANGE:** – breaking change (bumps major version)
|
|
907
992
|
|
|
993
|
+
To force a specific version, add `Release-As: X.Y.Z` in the commit message footer (e.g. `Release-As: 1.1.0`).
|
|
994
|
+
|
|
908
995
|
When you merge the Release PR, a tag is created and the [publish workflow](.github/workflows/publish.yml) publishes to PyPI (OIDC).
|
|
909
996
|
|
|
997
|
+
**First release:** Release Please only creates Release PRs for commits *since* the latest release. If you have no release yet, create the initial tag so it has a baseline: `git tag v1.0.0 && git push origin v1.0.0`. After that, new `feat:`/`fix:` commits (not `docs:` or `chore:`) will get Release PRs.
|
|
998
|
+
|
|
910
999
|
---
|
|
911
1000
|
|
|
912
1001
|
## Contributing
|
|
@@ -964,8 +1053,8 @@ agent-inspector server [--host HOST] [--port PORT]
|
|
|
964
1053
|
# View statistics
|
|
965
1054
|
agent-inspector stats
|
|
966
1055
|
|
|
967
|
-
# Prune old traces
|
|
968
|
-
agent-inspector prune [--retention-days N] [--vacuum]
|
|
1056
|
+
# Prune old traces (optionally by size: --retention-max-bytes BYTES)
|
|
1057
|
+
agent-inspector prune [--retention-days N] [--retention-max-bytes BYTES] [--vacuum]
|
|
969
1058
|
|
|
970
1059
|
# Vacuum database
|
|
971
1060
|
agent-inspector vacuum
|
|
@@ -1000,6 +1089,12 @@ Agent Inspector is designed for minimal overhead:
|
|
|
1000
1089
|
- Background thread: ~5MB (batch processing)
|
|
1001
1090
|
- Database: Varies with trace volume
|
|
1002
1091
|
|
|
1092
|
+
### Scaling and alerting
|
|
1093
|
+
|
|
1094
|
+
- **Single process / moderate load** – Use the default SQLite storage with sampling and retention (e.g. `retention_days`, optional `retention_max_bytes`). Suitable for one or a few worker processes.
|
|
1095
|
+
- **High throughput or many writers** – Use an OTLP or custom exporter to send traces to a central backend (e.g. Jaeger, Tempo, Grafana). The built-in UI and API then serve only that process; aggregate viewing is in your backend.
|
|
1096
|
+
- **Alerting** – The SDK does not push alerts. Use the API from your own checks: e.g. `GET /v1/stats` for `failed_runs`, `recent_runs_24h`, or `queue.events_dropped` (when the default Trace is in use), and alert when thresholds are exceeded. Optionally run `agent-inspector prune` on a schedule to enforce retention.
|
|
1097
|
+
|
|
1003
1098
|
---
|
|
1004
1099
|
|
|
1005
1100
|
## Security
|
|
@@ -43,6 +43,11 @@ from .core.interfaces import Exporter, Sampler
|
|
|
43
43
|
from .core.trace import (
|
|
44
44
|
Trace,
|
|
45
45
|
TraceContext,
|
|
46
|
+
agent_communication,
|
|
47
|
+
agent_handoff,
|
|
48
|
+
agent_join,
|
|
49
|
+
agent_leave,
|
|
50
|
+
agent_spawn,
|
|
46
51
|
error,
|
|
47
52
|
final,
|
|
48
53
|
get_trace,
|
|
@@ -51,6 +56,8 @@ from .core.trace import (
|
|
|
51
56
|
memory_write,
|
|
52
57
|
run,
|
|
53
58
|
set_trace,
|
|
59
|
+
task_assign,
|
|
60
|
+
task_complete,
|
|
54
61
|
tool,
|
|
55
62
|
)
|
|
56
63
|
|
|
@@ -71,6 +78,11 @@ except ImportError:
|
|
|
71
78
|
|
|
72
79
|
# Event types
|
|
73
80
|
from .core.events import (
|
|
81
|
+
AgentCommunicationEvent,
|
|
82
|
+
AgentHandoffEvent,
|
|
83
|
+
AgentJoinEvent,
|
|
84
|
+
AgentLeaveEvent,
|
|
85
|
+
AgentSpawnEvent,
|
|
74
86
|
BaseEvent,
|
|
75
87
|
ErrorEvent,
|
|
76
88
|
EventStatus,
|
|
@@ -81,6 +93,8 @@ from .core.events import (
|
|
|
81
93
|
MemoryWriteEvent,
|
|
82
94
|
RunEndEvent,
|
|
83
95
|
RunStartEvent,
|
|
96
|
+
TaskAssignmentEvent,
|
|
97
|
+
TaskCompletionEvent,
|
|
84
98
|
ToolCallEvent,
|
|
85
99
|
)
|
|
86
100
|
|
|
@@ -100,6 +114,14 @@ __all__ = [
|
|
|
100
114
|
"final",
|
|
101
115
|
"get_trace",
|
|
102
116
|
"set_trace",
|
|
117
|
+
# Multi-agent tracing
|
|
118
|
+
"agent_spawn",
|
|
119
|
+
"agent_join",
|
|
120
|
+
"agent_leave",
|
|
121
|
+
"agent_communication",
|
|
122
|
+
"agent_handoff",
|
|
123
|
+
"task_assign",
|
|
124
|
+
"task_complete",
|
|
103
125
|
# Configuration
|
|
104
126
|
"TraceConfig",
|
|
105
127
|
"Profile",
|
|
@@ -121,6 +143,14 @@ __all__ = [
|
|
|
121
143
|
"MemoryWriteEvent",
|
|
122
144
|
"ErrorEvent",
|
|
123
145
|
"FinalAnswerEvent",
|
|
146
|
+
# Multi-agent event types
|
|
147
|
+
"AgentSpawnEvent",
|
|
148
|
+
"AgentJoinEvent",
|
|
149
|
+
"AgentLeaveEvent",
|
|
150
|
+
"AgentCommunicationEvent",
|
|
151
|
+
"AgentHandoffEvent",
|
|
152
|
+
"TaskAssignmentEvent",
|
|
153
|
+
"TaskCompletionEvent",
|
|
124
154
|
# Adapters (optional)
|
|
125
155
|
"enable_langchain",
|
|
126
156
|
# API server (optional)
|
|
@@ -113,18 +113,30 @@ def cmd_prune(args):
|
|
|
113
113
|
# Override with CLI arguments
|
|
114
114
|
if args.retention_days is not None:
|
|
115
115
|
config.retention_days = args.retention_days
|
|
116
|
+
if getattr(args, "retention_max_bytes", None) is not None:
|
|
117
|
+
config.retention_max_bytes = args.retention_max_bytes
|
|
116
118
|
|
|
117
119
|
# Initialize database
|
|
118
120
|
db = Database(config)
|
|
119
121
|
db.initialize()
|
|
120
122
|
|
|
121
|
-
# Prune
|
|
123
|
+
# Prune by age first
|
|
122
124
|
deleted_count = db.prune_old_runs(retention_days=config.retention_days)
|
|
125
|
+
size_deleted = 0
|
|
126
|
+
|
|
127
|
+
# Then prune by size if configured
|
|
128
|
+
max_bytes = config.retention_max_bytes
|
|
129
|
+
if max_bytes is not None and max_bytes > 0:
|
|
130
|
+
size_deleted = db.prune_by_size(max_bytes)
|
|
131
|
+
deleted_count += size_deleted
|
|
132
|
+
if size_deleted > 0:
|
|
133
|
+
print(f"✅ Pruned {size_deleted} runs by size (max_bytes={max_bytes})")
|
|
123
134
|
|
|
124
135
|
if deleted_count > 0:
|
|
125
|
-
|
|
136
|
+
if size_deleted == 0: # only age-based pruning had effect
|
|
137
|
+
print(f"✅ Pruned {deleted_count} old runs")
|
|
126
138
|
|
|
127
|
-
# Optionally vacuum to reclaim space
|
|
139
|
+
# Optionally vacuum to reclaim space (prune_by_size already vacuums)
|
|
128
140
|
if args.vacuum:
|
|
129
141
|
print("💾 Running VACUUM to reclaim disk space...")
|
|
130
142
|
if db.vacuum():
|
|
@@ -408,6 +420,13 @@ Examples:
|
|
|
408
420
|
type=int,
|
|
409
421
|
help="Retention period in days (default: from config)",
|
|
410
422
|
)
|
|
423
|
+
prune_parser.add_argument(
|
|
424
|
+
"--retention-max-bytes",
|
|
425
|
+
type=int,
|
|
426
|
+
default=None,
|
|
427
|
+
metavar="BYTES",
|
|
428
|
+
help="Prune oldest runs until DB size is at or below BYTES (optional; from config if set)",
|
|
429
|
+
)
|
|
411
430
|
prune_parser.add_argument(
|
|
412
431
|
"--vacuum",
|
|
413
432
|
action="store_true",
|