agentic-lab 0.2.0__tar.gz → 0.3.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.
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/PKG-INFO +3 -1
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/pyproject.toml +13 -1
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/__init__.py +30 -2
- agentic_lab-0.3.0/src/agentlab/bundle.py +247 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/cli.py +224 -1
- agentic_lab-0.3.0/src/agentlab/core/autoenvelope.py +201 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/context.py +20 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/run.py +13 -0
- agentic_lab-0.3.0/src/agentlab/decorators.py +177 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/cli.py +150 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/suite.py +8 -0
- agentic_lab-0.3.0/src/agentlab/feedback.py +290 -0
- agentic_lab-0.3.0/src/agentlab/integrations/__init__.py +133 -0
- agentic_lab-0.3.0/src/agentlab/integrations/langchain/__init__.py +99 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/integrations/langchain/_callbacks.py +23 -1
- agentic_lab-0.3.0/src/agentlab/integrations/openai_agents.py +162 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/http_capture.py +35 -7
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/_autoemit.py +48 -19
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/promote.py +204 -34
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/recorder.py +30 -0
- agentic_lab-0.3.0/src/agentlab/replay/whatif.py +366 -0
- agentic_lab-0.3.0/src/agentlab/session.py +217 -0
- agentic_lab-0.3.0/src/agentlab/share.py +358 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/storage/jsonl_store.py +11 -7
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/storage/proto_store.py +10 -7
- agentic_lab-0.3.0/src/agentlab/ui/_assets/dist/assets/index-DrMO8lw4.css +1 -0
- agentic_lab-0.3.0/src/agentlab/ui/_assets/dist/assets/index-DvaPDz-C.js +215 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_assets/dist/index.html +2 -2
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_dto.py +3 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_mapping.py +15 -2
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_server/app.py +46 -2
- agentic_lab-0.3.0/src/agentlab/ui/_server/diff.py +129 -0
- agentic_lab-0.3.0/src/agentlab/ui/_server/feedback.py +127 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_server/replay_runner.py +152 -8
- agentic_lab-0.3.0/src/agentlab/ui/_server/whatif.py +142 -0
- agentic_lab-0.2.0/src/agentlab/integrations/__init__.py +0 -6
- agentic_lab-0.2.0/src/agentlab/integrations/langchain/__init__.py +0 -15
- agentic_lab-0.2.0/src/agentlab/ui/_assets/dist/assets/index-Cwxk3vuT.css +0 -1
- agentic_lab-0.2.0/src/agentlab/ui/_assets/dist/assets/index-w0GuiiTW.js +0 -211
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/README.md +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/_defaults.toml +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/_proto/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/_proto/trace_pb2.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/_proto/trace_pb2.pyi +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/bridges/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/bridges/otel_genai.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/config.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/_clock.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/_fmt.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/_replay_seam.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/agent.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/attributes.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/budget.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/continuation.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/cost.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/determinism.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/env.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/eval.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/guardrail.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/handles.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/handoff.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/ids.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/link.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/llm.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/memory.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/message.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/op.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/op_names.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/retrieval.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/schema.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/span.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/span_event.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/status.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/step.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/tool.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/core/types.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/errors.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/_readable_trace.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/_score_parser.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/errors.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/evaluator.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/fakes.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/judge_prompts/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/judge_prompts/citation_grounding.md +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/judge_prompts/faithfulness.md +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/judges.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/probes/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/probes/_base.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/probes/contains_substring.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/probes/conversation_length.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/probes/latency.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/probes/token_budget.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/probes/tool_usage.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/registry.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/rubrics.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/run.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/run_executor.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/runner.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/evals/types.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/README.md +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/_shared/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/_shared/datasets.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/_shared/llm.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/_shared/wikipedia.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/autonomous/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/autonomous/triage_agent.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/hybrid/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/hybrid/incident_responder.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/quickstart.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/workflows/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/workflows/research_assistant.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/examples/workflows/scholar_swarm.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/integrations/langchain/_helpers.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/canonical.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/format.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/http_format.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/otel_attributes.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/proto/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/proto/codec.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/proto/framing.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/proto/reader.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/proto/writer.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/reader.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/redaction.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/io/writer.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/base.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/errors.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/factory.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/matchers/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/matchers/_common.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/matchers/anthropic.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/matchers/bedrock.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/matchers/google.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/matchers/openai.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/pricing.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/providers/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/providers/openrouter.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/llm/types.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/py.typed +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/pytest.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/replay/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/replay/api.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/replay/cache.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/replay/context.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/replay/diff.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/replay/drift.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/replay/session.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/replay/unfreeze.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/storage/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/storage/base.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/storage/factory.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/storage/names.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/README.md +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_agents.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_assets/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_assets/dist/favicon.svg +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_errors.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_runtime.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_server/__init__.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_server/agents.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_server/agents_overview.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_server/dashboard.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_server/evals.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_server/validation.py +0 -0
- {agentic_lab-0.2.0 → agentic_lab-0.3.0}/src/agentlab/ui/_server/watcher.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: agentic-lab
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Universal record-and-replay for LLM agents.
|
|
5
5
|
Keywords: llm,agents,replay,tracing,evals
|
|
6
6
|
Author: Ambuj Agrawal, Garima Luthra
|
|
@@ -35,6 +35,7 @@ Requires-Dist: playwright>=1.59,<2.0 ; extra == 'dev'
|
|
|
35
35
|
Requires-Dist: langchain-core>=0.3,<1.0 ; extra == 'langchain'
|
|
36
36
|
Requires-Dist: langchain-openai>=0.3.35 ; extra == 'langchain'
|
|
37
37
|
Requires-Dist: langgraph>=1.0.1 ; extra == 'langchain'
|
|
38
|
+
Requires-Dist: openai-agents>=0.0.1 ; extra == 'openai-agents'
|
|
38
39
|
Requires-Dist: starlette>=0.40,<1.0 ; extra == 'ui'
|
|
39
40
|
Requires-Dist: uvicorn>=0.30,<1.0 ; extra == 'ui'
|
|
40
41
|
Requires-Dist: anyio>=4.4,<5.0 ; extra == 'ui'
|
|
@@ -50,6 +51,7 @@ Project-URL: Examples, https://github.com/ambuj-krishna-agrawal/agentlab/tree/ma
|
|
|
50
51
|
Project-URL: Changelog, https://github.com/ambuj-krishna-agrawal/agentlab/blob/main/CHANGELOG.md
|
|
51
52
|
Provides-Extra: dev
|
|
52
53
|
Provides-Extra: langchain
|
|
54
|
+
Provides-Extra: openai-agents
|
|
53
55
|
Provides-Extra: ui
|
|
54
56
|
Description-Content-Type: text/markdown
|
|
55
57
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "agentic-lab"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "Universal record-and-replay for LLM agents."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -43,6 +43,9 @@ langchain = [
|
|
|
43
43
|
"langchain-openai>=0.3.35",
|
|
44
44
|
"langgraph>=1.0.1",
|
|
45
45
|
]
|
|
46
|
+
openai-agents = [
|
|
47
|
+
"openai-agents>=0.0.1",
|
|
48
|
+
]
|
|
46
49
|
dev = [
|
|
47
50
|
"pytest>=8.2,<9.0",
|
|
48
51
|
"pytest-asyncio>=0.23,<1.0",
|
|
@@ -176,6 +179,9 @@ disable_error_code = [
|
|
|
176
179
|
"no-any-return",
|
|
177
180
|
"arg-type",
|
|
178
181
|
"unused-ignore",
|
|
182
|
+
"untyped-decorator",
|
|
183
|
+
"union-attr",
|
|
184
|
+
"operator",
|
|
179
185
|
]
|
|
180
186
|
|
|
181
187
|
[[tool.mypy.overrides]]
|
|
@@ -192,6 +198,12 @@ module = ["agentlab.examples.*"]
|
|
|
192
198
|
ignore_errors = true
|
|
193
199
|
follow_imports = "skip"
|
|
194
200
|
|
|
201
|
+
[[tool.mypy.overrides]]
|
|
202
|
+
# Optional integration deps that are not test/runtime dependencies of the
|
|
203
|
+
# core package (imported lazily inside ``install()``).
|
|
204
|
+
module = ["agents.*", "langchain_core.*"]
|
|
205
|
+
ignore_missing_imports = true
|
|
206
|
+
|
|
195
207
|
[tool.pytest.ini_options]
|
|
196
208
|
minversion = "8.0"
|
|
197
209
|
addopts = [
|
|
@@ -38,6 +38,10 @@ without stability guarantees.
|
|
|
38
38
|
from importlib.metadata import PackageNotFoundError as _PackageNotFoundError
|
|
39
39
|
from importlib.metadata import version as _version
|
|
40
40
|
|
|
41
|
+
# Subpackage exposed for ``al.integrations.install(...)`` and lazy
|
|
42
|
+
# ``al.integrations.langchain``. The package ``__init__`` is dependency
|
|
43
|
+
# free; optional adapters import their framework only inside ``install()``.
|
|
44
|
+
from agentlab import integrations
|
|
41
45
|
from agentlab.bridges.otel_genai import ExportTarget, export_trace
|
|
42
46
|
from agentlab.core import (
|
|
43
47
|
AgentHandle,
|
|
@@ -97,7 +101,6 @@ from agentlab.core import (
|
|
|
97
101
|
ToolHandle,
|
|
98
102
|
ToolKind,
|
|
99
103
|
ToolSpan,
|
|
100
|
-
agent,
|
|
101
104
|
continuation,
|
|
102
105
|
detached,
|
|
103
106
|
eval_,
|
|
@@ -112,10 +115,17 @@ from agentlab.core import (
|
|
|
112
115
|
retriever,
|
|
113
116
|
start_run,
|
|
114
117
|
start_span,
|
|
115
|
-
step,
|
|
116
118
|
tool,
|
|
117
119
|
)
|
|
118
120
|
from agentlab.core import llm as _llm_emitter
|
|
121
|
+
|
|
122
|
+
# Dual-use decorators. These shadow the bare ``agent`` / ``step``
|
|
123
|
+
# context-managers imported from ``agentlab.core`` above with wrappers
|
|
124
|
+
# that work as *both* context managers and (sync/async-aware)
|
|
125
|
+
# decorators, and add the new ``run`` decorator. ``with al.agent(...)``
|
|
126
|
+
# behaves identically; ``@al.agent(...)`` / ``@al.step(...)`` /
|
|
127
|
+
# ``@al.run(...)`` are the new spellings.
|
|
128
|
+
from agentlab.decorators import agent, run, step
|
|
119
129
|
from agentlab.evals import (
|
|
120
130
|
AgentRunner,
|
|
121
131
|
BaseEvaluator,
|
|
@@ -175,6 +185,13 @@ from agentlab.replay import (
|
|
|
175
185
|
replay,
|
|
176
186
|
replay_run,
|
|
177
187
|
)
|
|
188
|
+
from agentlab.session import (
|
|
189
|
+
_maybe_autostart_from_env,
|
|
190
|
+
active_recording,
|
|
191
|
+
init,
|
|
192
|
+
is_active,
|
|
193
|
+
shutdown,
|
|
194
|
+
)
|
|
178
195
|
from agentlab.ui import create_app, serve
|
|
179
196
|
|
|
180
197
|
# ``agentlab.recorder`` transitively imports ``agentlab.llm._autoemit``;
|
|
@@ -193,6 +210,11 @@ try:
|
|
|
193
210
|
except _PackageNotFoundError: # pragma: no cover — only when running uninstalled
|
|
194
211
|
__version__ = "0.0.0+unknown"
|
|
195
212
|
|
|
213
|
+
# ``AGENTLAB=1 python my_agent.py`` records everything with zero code
|
|
214
|
+
# change. This must run *after* the whole public surface is imported so
|
|
215
|
+
# the ambient ``record()`` it opens sees a fully-initialised package.
|
|
216
|
+
_maybe_autostart_from_env()
|
|
217
|
+
|
|
196
218
|
__all__ = [
|
|
197
219
|
"CURRENT_SCHEMA_VERSION",
|
|
198
220
|
"AgentHandle",
|
|
@@ -294,6 +316,7 @@ __all__ = [
|
|
|
294
316
|
"TraceHeader",
|
|
295
317
|
"UnfreezeRef",
|
|
296
318
|
"__version__",
|
|
319
|
+
"active_recording",
|
|
297
320
|
"agent",
|
|
298
321
|
"bundled_rubric",
|
|
299
322
|
"continuation",
|
|
@@ -306,6 +329,9 @@ __all__ = [
|
|
|
306
329
|
"guardrail",
|
|
307
330
|
"handoff",
|
|
308
331
|
"in_recording",
|
|
332
|
+
"init",
|
|
333
|
+
"integrations",
|
|
334
|
+
"is_active",
|
|
309
335
|
"list_bundled_rubrics",
|
|
310
336
|
"llm",
|
|
311
337
|
"memory",
|
|
@@ -320,7 +346,9 @@ __all__ = [
|
|
|
320
346
|
"replay",
|
|
321
347
|
"replay_run",
|
|
322
348
|
"retriever",
|
|
349
|
+
"run",
|
|
323
350
|
"serve",
|
|
351
|
+
"shutdown",
|
|
324
352
|
"start_run",
|
|
325
353
|
"start_span",
|
|
326
354
|
"step",
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""Portable single-file trace bundles (``*.agentlab``).
|
|
2
|
+
|
|
3
|
+
A ``.agentlab`` file is a plain ZIP archive of one trace directory plus a
|
|
4
|
+
small ``bundle.json`` manifest. Because the archive contains
|
|
5
|
+
``http.jsonl`` (the raw on-the-wire exchanges), the recipient can not
|
|
6
|
+
only *view* the trace but **replay it deterministically** — no API keys,
|
|
7
|
+
no network. This is the lightweight, on-brand version of "team
|
|
8
|
+
collaboration": send a colleague the exact failing run.
|
|
9
|
+
|
|
10
|
+
::
|
|
11
|
+
|
|
12
|
+
agentlab bundle brave-fog ./bug.agentlab # author
|
|
13
|
+
agentlab open ./bug.agentlab # teammate → imports + prints key
|
|
14
|
+
|
|
15
|
+
Layout inside the archive::
|
|
16
|
+
|
|
17
|
+
bundle.json — manifest (format version, ulid, name, ...)
|
|
18
|
+
trace/header.json
|
|
19
|
+
trace/spans.jsonl
|
|
20
|
+
trace/footer.json — absent ⇒ the run crashed
|
|
21
|
+
trace/http.jsonl — present iff HTTP was captured
|
|
22
|
+
trace/... — every other file in the source dir, 1:1
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import json
|
|
28
|
+
import zipfile
|
|
29
|
+
from dataclasses import asdict, dataclass
|
|
30
|
+
from datetime import UTC, datetime
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
from typing import TYPE_CHECKING
|
|
33
|
+
|
|
34
|
+
from agentlab.errors import TraceCorruptionError, TraceKeyNotFoundError
|
|
35
|
+
from agentlab.io.format import HEADER_FILENAME
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from agentlab.storage.base import TraceStore
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"BUNDLE_FORMAT_VERSION",
|
|
42
|
+
"BUNDLE_SUFFIX",
|
|
43
|
+
"BundleManifest",
|
|
44
|
+
"bundle_trace",
|
|
45
|
+
"open_bundle",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
BUNDLE_SUFFIX = ".agentlab"
|
|
49
|
+
BUNDLE_MANIFEST = "bundle.json"
|
|
50
|
+
BUNDLE_FORMAT_VERSION = 1
|
|
51
|
+
_TRACE_PREFIX = "trace/"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass(frozen=True, slots=True)
|
|
55
|
+
class BundleManifest:
|
|
56
|
+
"""Self-describing header written into every bundle."""
|
|
57
|
+
|
|
58
|
+
format_version: int
|
|
59
|
+
trace_ulid: str
|
|
60
|
+
trace_name: str
|
|
61
|
+
dir_name: str
|
|
62
|
+
created_at: str
|
|
63
|
+
file_count: int
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def bundle_trace(
|
|
67
|
+
source_dir: Path,
|
|
68
|
+
out: Path,
|
|
69
|
+
*,
|
|
70
|
+
force: bool = False,
|
|
71
|
+
) -> Path:
|
|
72
|
+
"""Archive the trace directory ``source_dir`` into a ``.agentlab`` file.
|
|
73
|
+
|
|
74
|
+
Returns the written path. Raises :class:`TraceKeyNotFoundError` if
|
|
75
|
+
``source_dir`` is not a trace directory, and refuses to clobber an
|
|
76
|
+
existing ``out`` unless ``force`` is set.
|
|
77
|
+
"""
|
|
78
|
+
source_dir = source_dir.expanduser().resolve()
|
|
79
|
+
header_path = source_dir / HEADER_FILENAME
|
|
80
|
+
if not header_path.is_file():
|
|
81
|
+
raise TraceKeyNotFoundError(
|
|
82
|
+
f"{source_dir} is not a trace directory (no {HEADER_FILENAME}); cannot bundle.",
|
|
83
|
+
trace_key=str(source_dir),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
out = out.expanduser()
|
|
87
|
+
if out.suffix != BUNDLE_SUFFIX:
|
|
88
|
+
out = out.with_suffix(BUNDLE_SUFFIX)
|
|
89
|
+
if out.exists() and not force:
|
|
90
|
+
raise FileExistsError(
|
|
91
|
+
f"bundle target {out} already exists; pass --force to overwrite.",
|
|
92
|
+
)
|
|
93
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
|
|
95
|
+
files = sorted(p for p in source_dir.rglob("*") if p.is_file())
|
|
96
|
+
ulid, name = _read_identity(header_path)
|
|
97
|
+
|
|
98
|
+
manifest = BundleManifest(
|
|
99
|
+
format_version=BUNDLE_FORMAT_VERSION,
|
|
100
|
+
trace_ulid=ulid,
|
|
101
|
+
trace_name=name,
|
|
102
|
+
dir_name=source_dir.name,
|
|
103
|
+
created_at=datetime.now(UTC).isoformat(),
|
|
104
|
+
file_count=len(files),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Write atomically: build a temp file then rename so a crash never
|
|
108
|
+
# leaves a half-written ``.agentlab`` that looks valid.
|
|
109
|
+
tmp = out.with_name(out.name + ".tmp")
|
|
110
|
+
try:
|
|
111
|
+
with zipfile.ZipFile(tmp, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
|
112
|
+
zf.writestr(BUNDLE_MANIFEST, json.dumps(asdict(manifest), indent=2))
|
|
113
|
+
for file_path in files:
|
|
114
|
+
arcname = _TRACE_PREFIX + file_path.relative_to(source_dir).as_posix()
|
|
115
|
+
zf.write(file_path, arcname)
|
|
116
|
+
tmp.replace(out)
|
|
117
|
+
finally:
|
|
118
|
+
tmp.unlink(missing_ok=True)
|
|
119
|
+
return out
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def open_bundle(
|
|
123
|
+
bundle_path: Path,
|
|
124
|
+
store: TraceStore,
|
|
125
|
+
*,
|
|
126
|
+
force: bool = False,
|
|
127
|
+
) -> Path:
|
|
128
|
+
"""Extract a ``.agentlab`` bundle into ``store``'s root and return the dir.
|
|
129
|
+
|
|
130
|
+
The trace is placed under its original directory name so it resolves
|
|
131
|
+
by ULID / name exactly as it did for the author. Refuses to
|
|
132
|
+
overwrite an existing trace directory unless ``force`` is set.
|
|
133
|
+
"""
|
|
134
|
+
bundle_path = bundle_path.expanduser().resolve()
|
|
135
|
+
if not bundle_path.is_file():
|
|
136
|
+
raise TraceKeyNotFoundError(
|
|
137
|
+
f"bundle {bundle_path} does not exist.",
|
|
138
|
+
trace_key=str(bundle_path),
|
|
139
|
+
)
|
|
140
|
+
root = _store_root(store)
|
|
141
|
+
|
|
142
|
+
with zipfile.ZipFile(bundle_path) as zf:
|
|
143
|
+
manifest = _read_manifest(zf, bundle_path)
|
|
144
|
+
dir_name = manifest.dir_name or manifest.trace_ulid
|
|
145
|
+
if not dir_name:
|
|
146
|
+
raise TraceCorruptionError(
|
|
147
|
+
f"bundle {bundle_path} has no usable trace directory name.",
|
|
148
|
+
)
|
|
149
|
+
target = root / dir_name
|
|
150
|
+
if target.exists():
|
|
151
|
+
if not force:
|
|
152
|
+
raise FileExistsError(
|
|
153
|
+
f"a trace directory already exists at {target}; pass --force to overwrite it.",
|
|
154
|
+
)
|
|
155
|
+
_rmtree(target)
|
|
156
|
+
|
|
157
|
+
members = [n for n in zf.namelist() if n.startswith(_TRACE_PREFIX)]
|
|
158
|
+
if not members:
|
|
159
|
+
raise TraceCorruptionError(
|
|
160
|
+
f"bundle {bundle_path} contains no trace/ payload.",
|
|
161
|
+
)
|
|
162
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
163
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
164
|
+
for member in members:
|
|
165
|
+
_safe_extract_member(zf, member, target)
|
|
166
|
+
|
|
167
|
+
return target
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# ---------------------------------------------------------------------------
|
|
171
|
+
# Helpers
|
|
172
|
+
# ---------------------------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _read_identity(header_path: Path) -> tuple[str, str]:
|
|
176
|
+
"""Return ``(trace_ulid, trace_name)`` from a JSON header (best effort)."""
|
|
177
|
+
try:
|
|
178
|
+
data = json.loads(header_path.read_text(encoding="utf-8"))
|
|
179
|
+
except (OSError, json.JSONDecodeError):
|
|
180
|
+
return "", ""
|
|
181
|
+
ulid = str(data.get("trace_ulid", "") or "")
|
|
182
|
+
name = str(data.get("name", "") or "")
|
|
183
|
+
return ulid, name
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _read_manifest(zf: zipfile.ZipFile, bundle_path: Path) -> BundleManifest:
|
|
187
|
+
try:
|
|
188
|
+
raw = zf.read(BUNDLE_MANIFEST)
|
|
189
|
+
except KeyError as exc:
|
|
190
|
+
raise TraceCorruptionError(
|
|
191
|
+
f"bundle {bundle_path} is missing its {BUNDLE_MANIFEST}; "
|
|
192
|
+
f"it may not be an AgentLab bundle.",
|
|
193
|
+
) from exc
|
|
194
|
+
try:
|
|
195
|
+
data = json.loads(raw)
|
|
196
|
+
except json.JSONDecodeError as exc:
|
|
197
|
+
raise TraceCorruptionError(
|
|
198
|
+
f"bundle {bundle_path} has a corrupt {BUNDLE_MANIFEST}.",
|
|
199
|
+
) from exc
|
|
200
|
+
return BundleManifest(
|
|
201
|
+
format_version=int(data.get("format_version", 0)),
|
|
202
|
+
trace_ulid=str(data.get("trace_ulid", "")),
|
|
203
|
+
trace_name=str(data.get("trace_name", "")),
|
|
204
|
+
dir_name=str(data.get("dir_name", "")),
|
|
205
|
+
created_at=str(data.get("created_at", "")),
|
|
206
|
+
file_count=int(data.get("file_count", 0)),
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _safe_extract_member(zf: zipfile.ZipFile, member: str, target: Path) -> None:
|
|
211
|
+
"""Extract one ``trace/<rel>`` member into ``target`` (zip-slip safe)."""
|
|
212
|
+
rel = member[len(_TRACE_PREFIX) :]
|
|
213
|
+
if not rel or rel.endswith("/"):
|
|
214
|
+
return
|
|
215
|
+
dest = (target / rel).resolve()
|
|
216
|
+
# Defend against path traversal in a malicious bundle.
|
|
217
|
+
if not _is_within(dest, target.resolve()):
|
|
218
|
+
raise TraceCorruptionError(
|
|
219
|
+
f"bundle member {member!r} escapes the target directory; refusing.",
|
|
220
|
+
)
|
|
221
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
222
|
+
with zf.open(member) as src, dest.open("wb") as out_fp:
|
|
223
|
+
out_fp.write(src.read())
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _is_within(path: Path, root: Path) -> bool:
|
|
227
|
+
try:
|
|
228
|
+
path.relative_to(root)
|
|
229
|
+
except ValueError:
|
|
230
|
+
return False
|
|
231
|
+
return True
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _store_root(store: TraceStore) -> Path:
|
|
235
|
+
root = getattr(store, "root", None)
|
|
236
|
+
if root is None:
|
|
237
|
+
raise TraceCorruptionError(
|
|
238
|
+
"the configured trace store has no local root; "
|
|
239
|
+
"`agentlab open` only supports local stores.",
|
|
240
|
+
)
|
|
241
|
+
return Path(root)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _rmtree(path: Path) -> None:
|
|
245
|
+
import shutil # noqa: PLC0415
|
|
246
|
+
|
|
247
|
+
shutil.rmtree(path)
|
|
@@ -89,6 +89,90 @@ _log = logging.getLogger("agentlab.cli")
|
|
|
89
89
|
# ---------------------------------------------------------------------------
|
|
90
90
|
|
|
91
91
|
|
|
92
|
+
def _add_share_subparsers(
|
|
93
|
+
sub: argparse._SubParsersAction[argparse.ArgumentParser],
|
|
94
|
+
) -> None:
|
|
95
|
+
"""Wire the portable-bundle ``bundle`` / ``open`` / ``push`` / ``pull`` subcommands."""
|
|
96
|
+
p = sub.add_parser(
|
|
97
|
+
"bundle",
|
|
98
|
+
help="Pack a trace into a portable single-file .agentlab bundle (replayable).",
|
|
99
|
+
)
|
|
100
|
+
p.add_argument("key")
|
|
101
|
+
p.add_argument("out", type=Path, help="Output path (``.agentlab`` is appended if missing).")
|
|
102
|
+
p.add_argument("--force", action="store_true", help="Overwrite an existing bundle.")
|
|
103
|
+
|
|
104
|
+
p = sub.add_parser(
|
|
105
|
+
"open",
|
|
106
|
+
help="Import a .agentlab bundle into the store and print its key.",
|
|
107
|
+
)
|
|
108
|
+
p.add_argument("bundle", type=Path, help="Path to a .agentlab bundle.")
|
|
109
|
+
p.add_argument(
|
|
110
|
+
"--force",
|
|
111
|
+
action="store_true",
|
|
112
|
+
help="Overwrite an existing trace directory with the same name.",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
p = sub.add_parser(
|
|
116
|
+
"push",
|
|
117
|
+
help="Pack a trace into a bundle and upload it to a remote URL (http(s):// or file://).",
|
|
118
|
+
)
|
|
119
|
+
p.add_argument("key")
|
|
120
|
+
p.add_argument(
|
|
121
|
+
"url",
|
|
122
|
+
help=(
|
|
123
|
+
"Destination URL. For HTTPS use a presigned S3 / GCS / Azure URL "
|
|
124
|
+
"(or any server that accepts PUT)."
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
p.add_argument(
|
|
128
|
+
"--auth",
|
|
129
|
+
default=None,
|
|
130
|
+
help=(
|
|
131
|
+
"Bearer token sent as ``Authorization: Bearer <auth>``. "
|
|
132
|
+
"Falls back to $AGENTLAB_BUNDLE_AUTH when omitted."
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
p.add_argument(
|
|
136
|
+
"--force",
|
|
137
|
+
action="store_true",
|
|
138
|
+
help="For file:// destinations, overwrite an existing file.",
|
|
139
|
+
)
|
|
140
|
+
p.add_argument(
|
|
141
|
+
"--max-bytes",
|
|
142
|
+
type=int,
|
|
143
|
+
default=None,
|
|
144
|
+
help="Refuse to upload bundles larger than this (default: 1 GiB).",
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
p = sub.add_parser(
|
|
148
|
+
"pull",
|
|
149
|
+
help="Download a .agentlab bundle from a URL and import it into the store.",
|
|
150
|
+
)
|
|
151
|
+
p.add_argument(
|
|
152
|
+
"url",
|
|
153
|
+
help="Source URL (http(s):// or file://).",
|
|
154
|
+
)
|
|
155
|
+
p.add_argument(
|
|
156
|
+
"--auth",
|
|
157
|
+
default=None,
|
|
158
|
+
help=(
|
|
159
|
+
"Bearer token sent as ``Authorization: Bearer <auth>``. "
|
|
160
|
+
"Falls back to $AGENTLAB_BUNDLE_AUTH when omitted."
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
p.add_argument(
|
|
164
|
+
"--force",
|
|
165
|
+
action="store_true",
|
|
166
|
+
help="Overwrite an existing trace directory with the same name.",
|
|
167
|
+
)
|
|
168
|
+
p.add_argument(
|
|
169
|
+
"--max-bytes",
|
|
170
|
+
type=int,
|
|
171
|
+
default=None,
|
|
172
|
+
help="Refuse to download bundles larger than this (default: 1 GiB).",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
92
176
|
def build_parser() -> argparse.ArgumentParser:
|
|
93
177
|
"""Build the top-level argparse parser (extracted for tests)."""
|
|
94
178
|
from agentlab import __version__ # noqa: PLC0415 — avoid circular import on cold start
|
|
@@ -176,10 +260,19 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
176
260
|
p.add_argument("--port", type=int, default=7861)
|
|
177
261
|
p.add_argument("--open-browser", action="store_true")
|
|
178
262
|
|
|
263
|
+
_add_share_subparsers(sub)
|
|
264
|
+
|
|
179
265
|
p = sub.add_parser("promote", help="Generate a pytest replay-test scaffold.")
|
|
180
266
|
p.add_argument("key")
|
|
181
267
|
p.add_argument("--out", type=Path, required=True)
|
|
182
|
-
p.add_argument(
|
|
268
|
+
p.add_argument(
|
|
269
|
+
"--agent",
|
|
270
|
+
default=None,
|
|
271
|
+
help=(
|
|
272
|
+
"module.path:callable_name. Optional when the run was recorded "
|
|
273
|
+
"via @al.run (the entrypoint is read from the trace)."
|
|
274
|
+
),
|
|
275
|
+
)
|
|
183
276
|
p.add_argument("--name", default=None)
|
|
184
277
|
p.add_argument("--force", action="store_true")
|
|
185
278
|
|
|
@@ -277,6 +370,10 @@ def _dispatch(args: argparse.Namespace) -> int:
|
|
|
277
370
|
"gc": _cmd_gc,
|
|
278
371
|
"serve": _cmd_serve,
|
|
279
372
|
"promote": _cmd_promote,
|
|
373
|
+
"bundle": _cmd_bundle,
|
|
374
|
+
"open": _cmd_open,
|
|
375
|
+
"push": _cmd_push,
|
|
376
|
+
"pull": _cmd_pull,
|
|
280
377
|
}
|
|
281
378
|
if args.command == "eval":
|
|
282
379
|
from agentlab.evals.cli import dispatch as _eval_dispatch # noqa: PLC0415
|
|
@@ -497,6 +594,132 @@ def _cmd_export(args: argparse.Namespace) -> int:
|
|
|
497
594
|
)
|
|
498
595
|
|
|
499
596
|
|
|
597
|
+
# ---------------------------------------------------------------------------
|
|
598
|
+
# bundle / open
|
|
599
|
+
# ---------------------------------------------------------------------------
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def _cmd_bundle(args: argparse.Namespace) -> int:
|
|
603
|
+
from agentlab.bundle import bundle_trace # noqa: PLC0415
|
|
604
|
+
|
|
605
|
+
store = _resolve_store(args)
|
|
606
|
+
handle = store.resolve(args.key)
|
|
607
|
+
if handle.backend_locator is None:
|
|
608
|
+
raise TraceKeyNotFoundError(
|
|
609
|
+
"trace has no on-disk locator; cannot bundle.",
|
|
610
|
+
trace_key=args.key,
|
|
611
|
+
)
|
|
612
|
+
try:
|
|
613
|
+
out = bundle_trace(Path(handle.backend_locator), args.out, force=args.force)
|
|
614
|
+
except FileExistsError as exc:
|
|
615
|
+
sys.stderr.write(f"agentlab bundle: {exc}\n")
|
|
616
|
+
return EXIT_USER_ERROR
|
|
617
|
+
if args.json_output:
|
|
618
|
+
print(json.dumps({"out": str(out), "key": args.key}))
|
|
619
|
+
elif not args.quiet:
|
|
620
|
+
print(f"wrote bundle {out}")
|
|
621
|
+
return EXIT_OK
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
def _cmd_open(args: argparse.Namespace) -> int:
|
|
625
|
+
from agentlab.bundle import open_bundle # noqa: PLC0415
|
|
626
|
+
|
|
627
|
+
store = _resolve_store(args)
|
|
628
|
+
try:
|
|
629
|
+
target = open_bundle(args.bundle, store, force=args.force)
|
|
630
|
+
except FileExistsError as exc:
|
|
631
|
+
sys.stderr.write(f"agentlab open: {exc}\n")
|
|
632
|
+
return EXIT_USER_ERROR
|
|
633
|
+
handle = store.resolve(str(target))
|
|
634
|
+
key = handle.trace_ulid or handle.name or target.name
|
|
635
|
+
if args.json_output:
|
|
636
|
+
print(json.dumps({"imported": str(target), "key": key}))
|
|
637
|
+
elif not args.quiet:
|
|
638
|
+
print(f"imported trace {key}")
|
|
639
|
+
print(f" at: {target}")
|
|
640
|
+
print(f"\nView: agentlab show {key}")
|
|
641
|
+
print(f"Replay: agentlab replay {key}")
|
|
642
|
+
return EXIT_OK
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def _cmd_push(args: argparse.Namespace) -> int:
|
|
646
|
+
from agentlab.share import BundleTransportError, push_bundle # noqa: PLC0415
|
|
647
|
+
|
|
648
|
+
store = _resolve_store(args)
|
|
649
|
+
handle = store.resolve(args.key)
|
|
650
|
+
if handle.backend_locator is None:
|
|
651
|
+
raise TraceKeyNotFoundError(
|
|
652
|
+
"trace has no on-disk locator; cannot push.",
|
|
653
|
+
trace_key=args.key,
|
|
654
|
+
)
|
|
655
|
+
push_kwargs: dict[str, Any] = {
|
|
656
|
+
"auth": args.auth,
|
|
657
|
+
"force": args.force,
|
|
658
|
+
}
|
|
659
|
+
if args.max_bytes is not None:
|
|
660
|
+
push_kwargs["max_bytes"] = args.max_bytes
|
|
661
|
+
try:
|
|
662
|
+
result = push_bundle(
|
|
663
|
+
Path(handle.backend_locator),
|
|
664
|
+
args.url,
|
|
665
|
+
**push_kwargs,
|
|
666
|
+
)
|
|
667
|
+
except BundleTransportError as exc:
|
|
668
|
+
sys.stderr.write(f"agentlab push: {exc.message}\n")
|
|
669
|
+
return EXIT_USER_ERROR
|
|
670
|
+
if args.json_output:
|
|
671
|
+
print(
|
|
672
|
+
json.dumps(
|
|
673
|
+
{
|
|
674
|
+
"url": result.url,
|
|
675
|
+
"bytes": result.bytes_uploaded,
|
|
676
|
+
"key": args.key,
|
|
677
|
+
},
|
|
678
|
+
),
|
|
679
|
+
)
|
|
680
|
+
elif not args.quiet:
|
|
681
|
+
print(f"pushed {result.bytes_uploaded} bytes → {result.url}")
|
|
682
|
+
print(f"\nReceiver: agentlab pull {result.url}")
|
|
683
|
+
return EXIT_OK
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def _cmd_pull(args: argparse.Namespace) -> int:
|
|
687
|
+
from agentlab.share import BundleTransportError, pull_bundle # noqa: PLC0415
|
|
688
|
+
|
|
689
|
+
store = _resolve_store(args)
|
|
690
|
+
pull_kwargs: dict[str, Any] = {
|
|
691
|
+
"auth": args.auth,
|
|
692
|
+
"force": args.force,
|
|
693
|
+
}
|
|
694
|
+
if args.max_bytes is not None:
|
|
695
|
+
pull_kwargs["max_bytes"] = args.max_bytes
|
|
696
|
+
try:
|
|
697
|
+
result = pull_bundle(args.url, store, **pull_kwargs)
|
|
698
|
+
except (BundleTransportError, FileExistsError) as exc:
|
|
699
|
+
msg = exc.message if isinstance(exc, BundleTransportError) else str(exc)
|
|
700
|
+
sys.stderr.write(f"agentlab pull: {msg}\n")
|
|
701
|
+
return EXIT_USER_ERROR
|
|
702
|
+
handle = store.resolve(str(result.trace_dir))
|
|
703
|
+
key = handle.trace_ulid or handle.name or result.trace_dir.name
|
|
704
|
+
if args.json_output:
|
|
705
|
+
print(
|
|
706
|
+
json.dumps(
|
|
707
|
+
{
|
|
708
|
+
"url": result.url,
|
|
709
|
+
"bytes": result.bytes_downloaded,
|
|
710
|
+
"imported": str(result.trace_dir),
|
|
711
|
+
"key": key,
|
|
712
|
+
},
|
|
713
|
+
),
|
|
714
|
+
)
|
|
715
|
+
elif not args.quiet:
|
|
716
|
+
print(f"pulled {result.bytes_downloaded} bytes from {result.url}")
|
|
717
|
+
print(f"imported trace {key}")
|
|
718
|
+
print(f"\nView: agentlab show {key}")
|
|
719
|
+
print(f"Replay: agentlab replay {key}")
|
|
720
|
+
return EXIT_OK
|
|
721
|
+
|
|
722
|
+
|
|
500
723
|
# ---------------------------------------------------------------------------
|
|
501
724
|
# gc
|
|
502
725
|
# ---------------------------------------------------------------------------
|