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.
Files changed (36) hide show
  1. {ai_agent_inspector-1.0.0/ai_agent_inspector.egg-info → ai_agent_inspector-1.1.0}/PKG-INFO +122 -26
  2. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/README.md +120 -25
  3. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/agent_inspector/__init__.py +30 -0
  4. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/agent_inspector/cli.py +22 -3
  5. ai_agent_inspector-1.1.0/agent_inspector/ui/static/app.css +1203 -0
  6. ai_agent_inspector-1.1.0/agent_inspector/ui/templates/index.html +749 -0
  7. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0/ai_agent_inspector.egg-info}/PKG-INFO +122 -26
  8. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/ai_agent_inspector.egg-info/SOURCES.txt +4 -0
  9. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/ai_agent_inspector.egg-info/requires.txt +1 -0
  10. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/pyproject.toml +2 -1
  11. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_api.py +47 -0
  12. ai_agent_inspector-1.1.0/tests/test_autogen_adapter.py +402 -0
  13. ai_agent_inspector-1.1.0/tests/test_cli.py +91 -0
  14. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_config.py +22 -0
  15. ai_agent_inspector-1.1.0/tests/test_crewai_adapter.py +549 -0
  16. ai_agent_inspector-1.1.0/tests/test_multi_agent.py +958 -0
  17. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_storage.py +51 -0
  18. ai_agent_inspector-1.0.0/agent_inspector/ui/static/app.css +0 -630
  19. ai_agent_inspector-1.0.0/agent_inspector/ui/templates/index.html +0 -441
  20. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/LICENSE +0 -0
  21. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/agent_inspector/ui/static/app.js +0 -0
  22. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/ai_agent_inspector.egg-info/dependency_links.txt +0 -0
  23. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/ai_agent_inspector.egg-info/entry_points.txt +0 -0
  24. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/ai_agent_inspector.egg-info/top_level.txt +0 -0
  25. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/setup.cfg +0 -0
  26. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_api_auth.py +0 -0
  27. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_api_error_paths.py +0 -0
  28. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_exporter.py +0 -0
  29. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_exporters.py +0 -0
  30. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_init_imports.py +0 -0
  31. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_interfaces.py +0 -0
  32. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_langchain_adapter.py +0 -0
  33. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_processing.py +0 -0
  34. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_queue.py +0 -0
  35. {ai_agent_inspector-1.0.0 → ai_agent_inspector-1.1.0}/tests/test_trace.py +0 -0
  36. {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.0.0
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 .env.example .env
609
+ cp examples/.env.example examples/.env
608
610
  ```
609
611
 
610
- Set these in `.env`:
611
- - `OPENAI_BASE_URL`
612
- - `OPENAI_API_KEY`
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
- Create a new adapter by extending `BaseCallbackHandler` (for LangChain-like frameworks) or by using the Trace SDK directly:
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 Trace, get_trace
930
+ from agent_inspector import trace, get_trace
843
931
 
844
932
  class CustomAdapter:
845
- def __init__(self, trace: Trace = None):
846
- self.trace = trace or get_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, args, result):
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=args, tool_result=result)
944
+ context.tool(tool_name=tool_name, tool_args=tool_args, tool_result=tool_result)
859
945
 
860
- # Use your adapter
861
- with trace.run("custom_agent"):
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 .env.example .env
561
+ cp examples/.env.example examples/.env
561
562
  ```
562
563
 
563
- Set these in `.env`:
564
- - `OPENAI_BASE_URL`
565
- - `OPENAI_API_KEY`
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
- Create a new adapter by extending `BaseCallbackHandler` (for LangChain-like frameworks) or by using the Trace SDK directly:
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 Trace, get_trace
882
+ from agent_inspector import trace, get_trace
796
883
 
797
884
  class CustomAdapter:
798
- def __init__(self, trace: Trace = None):
799
- self.trace = trace or get_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, args, result):
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=args, tool_result=result)
896
+ context.tool(tool_name=tool_name, tool_args=tool_args, tool_result=tool_result)
812
897
 
813
- # Use your adapter
814
- with trace.run("custom_agent"):
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 old runs
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
- print(f"✅ Pruned {deleted_count} old runs")
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",