provy-sdk 0.5.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.
@@ -0,0 +1,218 @@
1
+ Metadata-Version: 2.4
2
+ Name: provy-sdk
3
+ Version: 0.5.0
4
+ Summary: Python SDK for Provy: prove your AI agents actually worked
5
+ Author: Provy
6
+ License: MIT
7
+ Project-URL: Documentation, https://provy.ai/knowledge/index
8
+ Project-URL: Issues, https://github.com/amitgarg73/provy-sdk/issues
9
+ Project-URL: Homepage, https://provy.ai
10
+ Project-URL: Repository, https://github.com/amitgarg73/provy-sdk
11
+ Keywords: observability,llm,agents,opentelemetry,evals,outcome-assurance
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: requests>=2.28
15
+ Requires-Dist: pytz>=2023.3
16
+ Requires-Dist: python-dotenv>=1.0.0
17
+ Provides-Extra: judge
18
+ Requires-Dist: anthropic>=0.25.0; extra == "judge"
19
+ Provides-Extra: engine
20
+ Requires-Dist: supabase>=2.0.0; extra == "engine"
21
+ Provides-Extra: otel
22
+ Requires-Dist: opentelemetry-sdk>=1.20.0; extra == "otel"
23
+ Requires-Dist: opentelemetry-api>=1.20.0; extra == "otel"
24
+ Provides-Extra: all
25
+ Requires-Dist: anthropic>=0.25.0; extra == "all"
26
+ Requires-Dist: supabase>=2.0.0; extra == "all"
27
+ Requires-Dist: opentelemetry-sdk>=1.20.0; extra == "all"
28
+ Requires-Dist: opentelemetry-api>=1.20.0; extra == "all"
29
+
30
+ # provy-sdk
31
+
32
+ Python SDK for [Provy](https://provy.ai): prove your AI agents actually worked.
33
+
34
+ Send every session, agent step and evaluation with an ingest key. No database credentials and no
35
+ Provy-side configuration — close a session and it appears on your dashboard.
36
+
37
+ > **A note on names.** You may see `argus` inside: environment variables such as `ARGUS_INGEST_KEY`
38
+ > and `ARGUS_URL` still work, and OpenTelemetry attributes are still `argus.*`. Argus is the original
39
+ > codename, and those names are kept for wire compatibility so existing integrations keep working.
40
+ > Everything you actually type is `provy`.
41
+
42
+ ## Reliability
43
+
44
+ Telemetry that silently disappears is worse than none, so this client is built not to lose spans:
45
+
46
+ - **Retries** transient failures with backoff, honouring `Retry-After`. A timeout or a 503 delays
47
+ your data rather than destroying it.
48
+ - **Buffers and batches** spans, flushing on a background thread and again at process exit, so a
49
+ short script cannot end with telemetry still in memory.
50
+ - **Never silent.** Anything dropped is counted and logged, and the counts are readable at
51
+ `client.buffer_stats`.
52
+ - **Never raises into your agent.** A failed send is our problem, not a crash in your pipeline.
53
+
54
+ ---
55
+
56
+ ## Install
57
+
58
+ ```bash
59
+ pip install provy-sdk
60
+ ```
61
+ ```
62
+
63
+ The base install is the ingest client only (just `requests`). Optional extras:
64
+
65
+ | Extra | Adds | For |
66
+ |---|---|---|
67
+ | `provy-sdk[otel]` | OpenTelemetry SDK | streaming existing OTel spans via `ProvyExporter` |
68
+ | `provy-sdk[judge]` | `anthropic` | running the LLM-as-judge in your own pipeline (circuit breakers) |
69
+ | `provy-sdk[engine]` | `supabase` | the legacy direct-to-database path (prefer the ingest API instead) |
70
+
71
+ ---
72
+
73
+ ## Connect
74
+
75
+ Get an ingest key from Provy: **Agent Fleets → your fleet → Reveal key**. Set it in your environment:
76
+
77
+ ```bash
78
+ export PROVY_API_KEY=argus_... # key values are still prefixed argus_
79
+ # optional, defaults to the hosted app:
80
+ export PROVY_URL=https://provy.ai
81
+ ```
82
+
83
+ That key authenticates your fleet. It is the only credential you need.
84
+
85
+ ---
86
+
87
+ ## Quickstart — direct ingest
88
+
89
+ ```python
90
+ from provy import ProvyClient
91
+
92
+ provy = ProvyClient() # reads PROVY_API_KEY from the environment
93
+
94
+ session_id = provy.open_session("premarket")
95
+
96
+ provy.trace(
97
+ session_id = session_id,
98
+ agent = "research",
99
+ step_type = "agent_step", # llm_call | tool_call | agent_step | decision | error
100
+ outcome = "Generated AAPL thesis",
101
+ latency_ms = 1240,
102
+ tokens_in = 800,
103
+ tokens_out = 150,
104
+ )
105
+
106
+ provy.close_session(session_id, result_summary="Trade plan ready")
107
+ ```
108
+
109
+ Open **Sessions** in Provy — your run appears within seconds.
110
+
111
+ The decorator form auto-traces a function:
112
+
113
+ ```python
114
+ @provy.trace_fn(agent="research", step_type="agent_step")
115
+ def run_research(ticker):
116
+ ...
117
+
118
+ run_research("AAPL", session_id=session_id)
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Already on OpenTelemetry?
124
+
125
+ If your pipeline emits OTel spans (LangChain, CrewAI, AutoGen, LlamaIndex, or raw OTel), attach the exporter and stream them — no per-step calls:
126
+
127
+ ```bash
128
+ pip install "provy-sdk[otel]"
129
+ ```
130
+
131
+ ```python
132
+ from opentelemetry.sdk.trace import TracerProvider
133
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
134
+ from provy import ProvyExporter
135
+
136
+ provider = TracerProvider()
137
+ provider.add_span_processor(BatchSpanProcessor(ProvyExporter(api_key="provy_...")))
138
+ ```
139
+
140
+ Provy auto-detects the convention (OpenInference, OpenLLMetry/Traceloop, Langfuse, OTel GenAI) and builds the session from your spans.
141
+
142
+ ---
143
+
144
+ ## Quality scoring
145
+
146
+ By default Provy runs the LLM-as-judge **server-side** on the traces you send — no SDK code, no key of yours. Configure criteria in **Eval Manager** and scores appear on the Quality page.
147
+
148
+ Run the judge **in your own pipeline** only when you want the verdict before an output is used (circuit breakers):
149
+
150
+ ```bash
151
+ pip install "provy-sdk[judge]" # adds anthropic
152
+ ```
153
+
154
+ ```python
155
+ from provy import evaluate_session_outputs
156
+
157
+ evaluate_session_outputs(session_id, {"research": research_output_text})
158
+ ```
159
+
160
+ Needs `ANTHROPIC_API_KEY` in your environment. Same judge core as the server side.
161
+
162
+ ---
163
+
164
+ ## Business outcomes
165
+
166
+ For metrics you compute yourself (no LLM), write them with `write_eval()` — they land in **Outcomes**:
167
+
168
+ ```python
169
+ from provy import write_eval
170
+
171
+ write_eval(
172
+ session_id = session_id,
173
+ eval_name = "approval_rate",
174
+ agent = "risk",
175
+ score = 0.6,
176
+ passed = True,
177
+ threshold = 0.2,
178
+ reasoning = "3 of 5 proposals approved",
179
+ )
180
+ ```
181
+
182
+ ---
183
+
184
+ ## API reference
185
+
186
+ ### `ProvyClient(ingest_key=None, base_url=None)`
187
+ Reads `PROVY_API_KEY` / `PROVY_URL` from the environment when arguments are omitted (legacy `ARGUS_INGEST_KEY` / `ARGUS_URL` still work).
188
+
189
+ | Method | When to call |
190
+ |---|---|
191
+ | `open_session(session_type, external_id=None, metadata=None)` | start of a run; returns `session_id` |
192
+ | `trace(session_id, agent, step_type, outcome, ...)` | each step; returns the span id |
193
+ | `close_session(session_id, status="completed", result_summary=None, terminal_reason=None)` | end of the run |
194
+ | `trace_fn(agent, step_type="agent_step")` | decorator that auto-traces a function |
195
+
196
+ ### `ProvyExporter(api_key, endpoint=None)`
197
+ OTel `SpanExporter`. Attach to any `TracerProvider`. Needs the `otel` extra.
198
+
199
+ ### `evaluate_session_outputs(session_id, agent_outputs)`
200
+ Client-side LLM-as-judge. Needs the `judge` extra and `ANTHROPIC_API_KEY`.
201
+
202
+ ### `write_eval(session_id, eval_name, agent, score, passed, threshold, reasoning, layer=5)`
203
+ Writes one business-outcome eval row.
204
+
205
+ > **Legacy:** `TraceLogger` (direct database writes via the `engine` extra) predates the ingest API. New pipelines should use `ProvyClient`. `TraceLogger` remains for existing internal pipelines.
206
+
207
+ ---
208
+
209
+ ## Examples
210
+
211
+ - `examples/otel_quickstart.py` — stream OTel spans to Provy
212
+ - `examples/github-actions-otel.yml` — run a pipeline in GitHub Actions and stream to Provy
213
+
214
+ ---
215
+
216
+ ## Support
217
+
218
+ Open an issue at [github.com/amitgarg73/provy-sdk](https://github.com/amitgarg73/provy-sdk/issues).
@@ -0,0 +1,189 @@
1
+ # provy-sdk
2
+
3
+ Python SDK for [Provy](https://provy.ai): prove your AI agents actually worked.
4
+
5
+ Send every session, agent step and evaluation with an ingest key. No database credentials and no
6
+ Provy-side configuration — close a session and it appears on your dashboard.
7
+
8
+ > **A note on names.** You may see `argus` inside: environment variables such as `ARGUS_INGEST_KEY`
9
+ > and `ARGUS_URL` still work, and OpenTelemetry attributes are still `argus.*`. Argus is the original
10
+ > codename, and those names are kept for wire compatibility so existing integrations keep working.
11
+ > Everything you actually type is `provy`.
12
+
13
+ ## Reliability
14
+
15
+ Telemetry that silently disappears is worse than none, so this client is built not to lose spans:
16
+
17
+ - **Retries** transient failures with backoff, honouring `Retry-After`. A timeout or a 503 delays
18
+ your data rather than destroying it.
19
+ - **Buffers and batches** spans, flushing on a background thread and again at process exit, so a
20
+ short script cannot end with telemetry still in memory.
21
+ - **Never silent.** Anything dropped is counted and logged, and the counts are readable at
22
+ `client.buffer_stats`.
23
+ - **Never raises into your agent.** A failed send is our problem, not a crash in your pipeline.
24
+
25
+ ---
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ pip install provy-sdk
31
+ ```
32
+ ```
33
+
34
+ The base install is the ingest client only (just `requests`). Optional extras:
35
+
36
+ | Extra | Adds | For |
37
+ |---|---|---|
38
+ | `provy-sdk[otel]` | OpenTelemetry SDK | streaming existing OTel spans via `ProvyExporter` |
39
+ | `provy-sdk[judge]` | `anthropic` | running the LLM-as-judge in your own pipeline (circuit breakers) |
40
+ | `provy-sdk[engine]` | `supabase` | the legacy direct-to-database path (prefer the ingest API instead) |
41
+
42
+ ---
43
+
44
+ ## Connect
45
+
46
+ Get an ingest key from Provy: **Agent Fleets → your fleet → Reveal key**. Set it in your environment:
47
+
48
+ ```bash
49
+ export PROVY_API_KEY=argus_... # key values are still prefixed argus_
50
+ # optional, defaults to the hosted app:
51
+ export PROVY_URL=https://provy.ai
52
+ ```
53
+
54
+ That key authenticates your fleet. It is the only credential you need.
55
+
56
+ ---
57
+
58
+ ## Quickstart — direct ingest
59
+
60
+ ```python
61
+ from provy import ProvyClient
62
+
63
+ provy = ProvyClient() # reads PROVY_API_KEY from the environment
64
+
65
+ session_id = provy.open_session("premarket")
66
+
67
+ provy.trace(
68
+ session_id = session_id,
69
+ agent = "research",
70
+ step_type = "agent_step", # llm_call | tool_call | agent_step | decision | error
71
+ outcome = "Generated AAPL thesis",
72
+ latency_ms = 1240,
73
+ tokens_in = 800,
74
+ tokens_out = 150,
75
+ )
76
+
77
+ provy.close_session(session_id, result_summary="Trade plan ready")
78
+ ```
79
+
80
+ Open **Sessions** in Provy — your run appears within seconds.
81
+
82
+ The decorator form auto-traces a function:
83
+
84
+ ```python
85
+ @provy.trace_fn(agent="research", step_type="agent_step")
86
+ def run_research(ticker):
87
+ ...
88
+
89
+ run_research("AAPL", session_id=session_id)
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Already on OpenTelemetry?
95
+
96
+ If your pipeline emits OTel spans (LangChain, CrewAI, AutoGen, LlamaIndex, or raw OTel), attach the exporter and stream them — no per-step calls:
97
+
98
+ ```bash
99
+ pip install "provy-sdk[otel]"
100
+ ```
101
+
102
+ ```python
103
+ from opentelemetry.sdk.trace import TracerProvider
104
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
105
+ from provy import ProvyExporter
106
+
107
+ provider = TracerProvider()
108
+ provider.add_span_processor(BatchSpanProcessor(ProvyExporter(api_key="provy_...")))
109
+ ```
110
+
111
+ Provy auto-detects the convention (OpenInference, OpenLLMetry/Traceloop, Langfuse, OTel GenAI) and builds the session from your spans.
112
+
113
+ ---
114
+
115
+ ## Quality scoring
116
+
117
+ By default Provy runs the LLM-as-judge **server-side** on the traces you send — no SDK code, no key of yours. Configure criteria in **Eval Manager** and scores appear on the Quality page.
118
+
119
+ Run the judge **in your own pipeline** only when you want the verdict before an output is used (circuit breakers):
120
+
121
+ ```bash
122
+ pip install "provy-sdk[judge]" # adds anthropic
123
+ ```
124
+
125
+ ```python
126
+ from provy import evaluate_session_outputs
127
+
128
+ evaluate_session_outputs(session_id, {"research": research_output_text})
129
+ ```
130
+
131
+ Needs `ANTHROPIC_API_KEY` in your environment. Same judge core as the server side.
132
+
133
+ ---
134
+
135
+ ## Business outcomes
136
+
137
+ For metrics you compute yourself (no LLM), write them with `write_eval()` — they land in **Outcomes**:
138
+
139
+ ```python
140
+ from provy import write_eval
141
+
142
+ write_eval(
143
+ session_id = session_id,
144
+ eval_name = "approval_rate",
145
+ agent = "risk",
146
+ score = 0.6,
147
+ passed = True,
148
+ threshold = 0.2,
149
+ reasoning = "3 of 5 proposals approved",
150
+ )
151
+ ```
152
+
153
+ ---
154
+
155
+ ## API reference
156
+
157
+ ### `ProvyClient(ingest_key=None, base_url=None)`
158
+ Reads `PROVY_API_KEY` / `PROVY_URL` from the environment when arguments are omitted (legacy `ARGUS_INGEST_KEY` / `ARGUS_URL` still work).
159
+
160
+ | Method | When to call |
161
+ |---|---|
162
+ | `open_session(session_type, external_id=None, metadata=None)` | start of a run; returns `session_id` |
163
+ | `trace(session_id, agent, step_type, outcome, ...)` | each step; returns the span id |
164
+ | `close_session(session_id, status="completed", result_summary=None, terminal_reason=None)` | end of the run |
165
+ | `trace_fn(agent, step_type="agent_step")` | decorator that auto-traces a function |
166
+
167
+ ### `ProvyExporter(api_key, endpoint=None)`
168
+ OTel `SpanExporter`. Attach to any `TracerProvider`. Needs the `otel` extra.
169
+
170
+ ### `evaluate_session_outputs(session_id, agent_outputs)`
171
+ Client-side LLM-as-judge. Needs the `judge` extra and `ANTHROPIC_API_KEY`.
172
+
173
+ ### `write_eval(session_id, eval_name, agent, score, passed, threshold, reasoning, layer=5)`
174
+ Writes one business-outcome eval row.
175
+
176
+ > **Legacy:** `TraceLogger` (direct database writes via the `engine` extra) predates the ingest API. New pipelines should use `ProvyClient`. `TraceLogger` remains for existing internal pipelines.
177
+
178
+ ---
179
+
180
+ ## Examples
181
+
182
+ - `examples/otel_quickstart.py` — stream OTel spans to Provy
183
+ - `examples/github-actions-otel.yml` — run a pipeline in GitHub Actions and stream to Provy
184
+
185
+ ---
186
+
187
+ ## Support
188
+
189
+ Open an issue at [github.com/amitgarg73/provy-sdk](https://github.com/amitgarg73/provy-sdk/issues).
@@ -0,0 +1,95 @@
1
+ """
2
+ Provy SDK.
3
+
4
+ Importing `provy` is lightweight: it pulls in only the ingest client (REST + OTel
5
+ exporter), which needs `requests`. The optional pieces load on first use and tell
6
+ you which extra to install if it is missing:
7
+
8
+ - the LLM-as-judge → pip install "provy-sdk[judge]" (anthropic)
9
+ - the local eval/RCA engine → pip install "provy-sdk[engine]" (supabase; legacy
10
+ direct-DB path — prefer the ingest API)
11
+
12
+ So a tenant who just wants to send traces installs the base package and nothing else.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import importlib
17
+ from typing import Any
18
+
19
+ # ── Eager, lightweight (ingest client — requests only) ──────────────────────────
20
+ from provy.client import ProvyClient, ProvyExporter
21
+ from provy.session import TraceLogger
22
+ from provy.evals import write_eval
23
+
24
+ __version__ = "0.5.0"
25
+
26
+ # ── Lazy, heavy (loaded on first access; mapped to the extra that provides them) ──
27
+ # name -> (module, attribute, extra)
28
+ _LAZY: dict[str, tuple[str, str, str]] = {
29
+ # LLM-as-judge (anthropic)
30
+ "evaluate_session_outputs": ("provy.judge", "evaluate_session_outputs", "judge"),
31
+ # local eval + pattern + RCA engine (supabase, legacy direct-DB)
32
+ "EvalResult": ("provy.engine", "EvalResult", "engine"),
33
+ "Incident": ("provy.engine", "Incident", "engine"),
34
+ "run_evals_from_config": ("provy.engine", "run_evals_from_config", "engine"),
35
+ "run_all_detectors": ("provy.engine", "run_all_detectors", "engine"),
36
+ "run_quality_detectors": ("provy.engine", "run_quality_detectors", "engine"),
37
+ "run_evals_and_persist": ("provy.engine", "run_evals_and_persist", "engine"),
38
+ "run_detectors_and_persist": ("provy.engine", "run_detectors_and_persist", "engine"),
39
+ "compute_shadow_cb_fires": ("provy.engine", "compute_shadow_cb_fires", "engine"),
40
+ "build_annotated_call_stack":("provy.engine", "build_annotated_call_stack", "engine"),
41
+ "generate_fix_suggestion": ("provy.engine", "generate_fix_suggestion", "engine"),
42
+ "summarize_incident": ("provy.engine", "summarize_incident", "engine"),
43
+ "load_pipeline_config": ("provy.engine", "load_pipeline_config", "engine"),
44
+ "load_eval_configs": ("provy.engine", "load_eval_configs", "engine"),
45
+ "load_pipeline_agents": ("provy.engine", "load_pipeline_agents", "engine"),
46
+ "register_eval": ("provy.engine", "register_eval", "engine"),
47
+ "get_registry": ("provy.engine", "get_registry", "engine"),
48
+ }
49
+
50
+
51
+ def __getattr__(name: str) -> Any: # PEP 562 — module-level lazy attributes
52
+ target = _LAZY.get(name)
53
+ if target is None:
54
+ raise AttributeError(f"module 'provy' has no attribute {name!r}")
55
+ module, attr, extra = target
56
+ try:
57
+ mod = importlib.import_module(module)
58
+ except ImportError as exc:
59
+ raise ImportError(
60
+ f"{name!r} needs the '{extra}' extra. Install it with: "
61
+ f'pip install "provy-sdk[{extra}]"'
62
+ ) from exc
63
+ return getattr(mod, attr)
64
+
65
+
66
+ def __dir__() -> list[str]:
67
+ return sorted(__all__)
68
+
69
+
70
+ __all__ = [
71
+ # ingest client (base)
72
+ "ProvyClient",
73
+ "ProvyExporter",
74
+ "TraceLogger",
75
+ "write_eval",
76
+ # LLM-as-judge (extra: judge)
77
+ "evaluate_session_outputs",
78
+ # local engine (extra: engine)
79
+ "EvalResult",
80
+ "Incident",
81
+ "run_evals_from_config",
82
+ "run_all_detectors",
83
+ "run_quality_detectors",
84
+ "run_evals_and_persist",
85
+ "run_detectors_and_persist",
86
+ "compute_shadow_cb_fires",
87
+ "build_annotated_call_stack",
88
+ "generate_fix_suggestion",
89
+ "summarize_incident",
90
+ "load_pipeline_config",
91
+ "load_eval_configs",
92
+ "load_pipeline_agents",
93
+ "register_eval",
94
+ "get_registry",
95
+ ]