evalguard-python 1.1.0__py3-none-any.whl
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.
- evalguard/__init__.py +42 -0
- evalguard/anthropic.py +182 -0
- evalguard/bedrock.py +280 -0
- evalguard/client.py +516 -0
- evalguard/crewai.py +189 -0
- evalguard/fastapi.py +273 -0
- evalguard/guardrails.py +160 -0
- evalguard/langchain.py +218 -0
- evalguard/nemoclaw.py +251 -0
- evalguard/openai.py +194 -0
- evalguard/types.py +142 -0
- evalguard_python-1.1.0.dist-info/METADATA +362 -0
- evalguard_python-1.1.0.dist-info/RECORD +15 -0
- evalguard_python-1.1.0.dist-info/WHEEL +5 -0
- evalguard_python-1.1.0.dist-info/top_level.txt +1 -0
evalguard/client.py
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
"""EvalGuard Python SDK — HTTP client for the EvalGuard API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
from urllib.parse import urlparse
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
from .types import (
|
|
12
|
+
BenchmarkResult,
|
|
13
|
+
ComplianceReport,
|
|
14
|
+
DriftReport,
|
|
15
|
+
EvalResult,
|
|
16
|
+
FirewallResult,
|
|
17
|
+
SecurityScanResult,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class EvalGuardError(Exception):
|
|
22
|
+
"""Base exception for EvalGuard API errors."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, message: str, status_code: int | None = None, body: Any = None):
|
|
25
|
+
super().__init__(message)
|
|
26
|
+
self.status_code = status_code
|
|
27
|
+
self.body = body
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class EvalGuardClient:
|
|
31
|
+
"""Client for the EvalGuard REST API.
|
|
32
|
+
|
|
33
|
+
Example::
|
|
34
|
+
|
|
35
|
+
from evalguard import EvalGuardClient
|
|
36
|
+
|
|
37
|
+
client = EvalGuardClient(api_key="eg_live_...")
|
|
38
|
+
result = client.run_eval({
|
|
39
|
+
"model": "gpt-4o",
|
|
40
|
+
"prompt": "Answer: {{input}}",
|
|
41
|
+
"cases": [{"input": "hello", "expectedOutput": "hello"}],
|
|
42
|
+
"scorers": ["exact-match"],
|
|
43
|
+
})
|
|
44
|
+
print(result)
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
api_key: str,
|
|
50
|
+
base_url: str = "https://api.evalguard.ai",
|
|
51
|
+
timeout: float = 120.0,
|
|
52
|
+
) -> None:
|
|
53
|
+
self.api_key = api_key
|
|
54
|
+
self.base_url = base_url.rstrip("/")
|
|
55
|
+
self.timeout = timeout
|
|
56
|
+
|
|
57
|
+
# Enforce HTTPS for non-local URLs
|
|
58
|
+
parsed = urlparse(self.base_url)
|
|
59
|
+
is_local = parsed.hostname in ('localhost', '127.0.0.1')
|
|
60
|
+
if parsed.scheme != 'https' and not is_local:
|
|
61
|
+
raise ValueError(
|
|
62
|
+
"EvalGuard: base_url must use HTTPS. "
|
|
63
|
+
"Only localhost/127.0.0.1 may use HTTP."
|
|
64
|
+
)
|
|
65
|
+
self.session = requests.Session()
|
|
66
|
+
self.session.headers.update(
|
|
67
|
+
{
|
|
68
|
+
"Authorization": f"Bearer {api_key}",
|
|
69
|
+
"Content-Type": "application/json",
|
|
70
|
+
"User-Agent": "evalguard-python/1.0.0",
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# ── Helpers ──────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
def _url(self, path: str) -> str:
|
|
77
|
+
return f"{self.base_url}{path}"
|
|
78
|
+
|
|
79
|
+
def _request(self, method: str, path: str, **kwargs: Any) -> Any:
|
|
80
|
+
kwargs.setdefault("timeout", self.timeout)
|
|
81
|
+
max_retries = 3
|
|
82
|
+
last_error = None
|
|
83
|
+
|
|
84
|
+
for attempt in range(max_retries + 1):
|
|
85
|
+
try:
|
|
86
|
+
response = self.session.request(method, self._url(path), **kwargs)
|
|
87
|
+
|
|
88
|
+
if response.status_code == 429:
|
|
89
|
+
retry_after = int(response.headers.get("Retry-After", 0))
|
|
90
|
+
delay = retry_after if retry_after > 0 else min(2 ** attempt, 60)
|
|
91
|
+
if attempt < max_retries:
|
|
92
|
+
time.sleep(delay)
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
if response.status_code >= 500 and attempt < max_retries:
|
|
96
|
+
time.sleep(2 ** attempt)
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
if not response.ok:
|
|
100
|
+
raise EvalGuardError(
|
|
101
|
+
f"API error {response.status_code}: {response.text[:500]}",
|
|
102
|
+
status_code=response.status_code,
|
|
103
|
+
body=response.text[:500],
|
|
104
|
+
)
|
|
105
|
+
if response.status_code == 204:
|
|
106
|
+
return None
|
|
107
|
+
return response.json()
|
|
108
|
+
except EvalGuardError:
|
|
109
|
+
raise
|
|
110
|
+
except Exception as e:
|
|
111
|
+
last_error = e
|
|
112
|
+
if attempt < max_retries:
|
|
113
|
+
time.sleep(2 ** attempt)
|
|
114
|
+
continue
|
|
115
|
+
raise EvalGuardError(f"Request failed: {e}")
|
|
116
|
+
|
|
117
|
+
raise EvalGuardError(f"Request failed after {max_retries} retries: {last_error}")
|
|
118
|
+
|
|
119
|
+
def _get(self, path: str, params: Dict[str, Any] | None = None) -> Any:
|
|
120
|
+
return self._request("GET", path, params=params)
|
|
121
|
+
|
|
122
|
+
def _post(self, path: str, json: Any = None) -> Any:
|
|
123
|
+
return self._request("POST", path, json=json)
|
|
124
|
+
|
|
125
|
+
# ── Eval endpoints ───────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
def run_eval(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
128
|
+
"""Run an evaluation with the given config and return results."""
|
|
129
|
+
return self._post("/v1/evals/run", json=config)
|
|
130
|
+
|
|
131
|
+
def get_eval(self, run_id: str) -> Dict[str, Any]:
|
|
132
|
+
"""Fetch a specific eval run by ID."""
|
|
133
|
+
return self._get(f"/v1/evals/{run_id}")
|
|
134
|
+
|
|
135
|
+
def list_evals(self, project_id: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
136
|
+
"""List eval runs, optionally filtered by project."""
|
|
137
|
+
params = {}
|
|
138
|
+
if project_id:
|
|
139
|
+
params["projectId"] = project_id
|
|
140
|
+
return self._get("/v1/evals", params=params)
|
|
141
|
+
|
|
142
|
+
# ── Security scan endpoints ──────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
def run_scan(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
145
|
+
"""Run a security scan (red-team) against a model."""
|
|
146
|
+
return self._post("/v1/scans/run", json=config)
|
|
147
|
+
|
|
148
|
+
def get_scan(self, scan_id: str) -> Dict[str, Any]:
|
|
149
|
+
"""Fetch a specific security scan by ID."""
|
|
150
|
+
return self._get(f"/v1/scans/{scan_id}")
|
|
151
|
+
|
|
152
|
+
# ── Scorers & plugins ────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
def list_scorers(self) -> List[Dict[str, Any]]:
|
|
155
|
+
"""List all available evaluation scorers."""
|
|
156
|
+
return self._get("/v1/scorers")
|
|
157
|
+
|
|
158
|
+
def list_plugins(self) -> List[Dict[str, Any]]:
|
|
159
|
+
"""List all available security plugins."""
|
|
160
|
+
return self._get("/v1/plugins")
|
|
161
|
+
|
|
162
|
+
# ── Firewall ─────────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
def check_firewall(
|
|
165
|
+
self,
|
|
166
|
+
input_text: str,
|
|
167
|
+
rules: Optional[List[Dict[str, Any]]] = None,
|
|
168
|
+
) -> Dict[str, Any]:
|
|
169
|
+
"""Check input text against firewall rules."""
|
|
170
|
+
payload: Dict[str, Any] = {"input": input_text}
|
|
171
|
+
if rules is not None:
|
|
172
|
+
payload["rules"] = rules
|
|
173
|
+
return self._post("/v1/firewall/check", json=payload)
|
|
174
|
+
|
|
175
|
+
# ── Benchmarks ───────────────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
def run_benchmarks(
|
|
178
|
+
self,
|
|
179
|
+
suites: List[str],
|
|
180
|
+
model: str,
|
|
181
|
+
) -> Dict[str, Any]:
|
|
182
|
+
"""Run benchmark suites against a model."""
|
|
183
|
+
return self._post("/v1/benchmarks/run", json={"suites": suites, "model": model})
|
|
184
|
+
|
|
185
|
+
# ── Export ────────────────────────────────────────────────────────────
|
|
186
|
+
|
|
187
|
+
def export_dpo(self, run_id: str) -> str:
|
|
188
|
+
"""Export eval results as DPO training data (JSONL)."""
|
|
189
|
+
resp = self.session.get(
|
|
190
|
+
self._url(f"/v1/evals/{run_id}/export/dpo"),
|
|
191
|
+
timeout=self.timeout,
|
|
192
|
+
)
|
|
193
|
+
if not resp.ok:
|
|
194
|
+
raise EvalGuardError(
|
|
195
|
+
f"Export error {resp.status_code}", status_code=resp.status_code
|
|
196
|
+
)
|
|
197
|
+
return resp.text
|
|
198
|
+
|
|
199
|
+
def export_burp(self, scan_id: str) -> str:
|
|
200
|
+
"""Export security scan results as Burp Suite XML."""
|
|
201
|
+
resp = self.session.get(
|
|
202
|
+
self._url(f"/v1/scans/{scan_id}/export/burp"),
|
|
203
|
+
timeout=self.timeout,
|
|
204
|
+
)
|
|
205
|
+
if not resp.ok:
|
|
206
|
+
raise EvalGuardError(
|
|
207
|
+
f"Export error {resp.status_code}", status_code=resp.status_code
|
|
208
|
+
)
|
|
209
|
+
return resp.text
|
|
210
|
+
|
|
211
|
+
# ── Compliance ───────────────────────────────────────────────────────
|
|
212
|
+
|
|
213
|
+
def get_compliance_report(
|
|
214
|
+
self, scan_id: str, framework: str
|
|
215
|
+
) -> Dict[str, Any]:
|
|
216
|
+
"""Map scan results to a compliance framework report."""
|
|
217
|
+
return self._get(
|
|
218
|
+
f"/v1/scans/{scan_id}/compliance",
|
|
219
|
+
params={"framework": framework},
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# ── Drift detection ──────────────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
def detect_drift(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
225
|
+
"""Detect performance drift between baseline and current results."""
|
|
226
|
+
return self._post("/v1/drift/detect", json=config)
|
|
227
|
+
|
|
228
|
+
# ── Guardrails ───────────────────────────────────────────────────────
|
|
229
|
+
|
|
230
|
+
def generate_guardrails(self, config: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
231
|
+
"""Auto-generate firewall rules from scan findings."""
|
|
232
|
+
return self._post("/v1/guardrails/generate", json=config)
|
|
233
|
+
|
|
234
|
+
# ── Smart Routing ───────────────────────────────────────────────────
|
|
235
|
+
|
|
236
|
+
def smart_route(self, test_cases: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
237
|
+
"""Route test cases to optimal model tiers by complexity."""
|
|
238
|
+
return self._post("/v1/smart-routing/test-cases", json={"testCases": test_cases})
|
|
239
|
+
|
|
240
|
+
# ── Autopilot ───────────────────────────────────────────────────────
|
|
241
|
+
|
|
242
|
+
def autopilot(self, description: str, depth: str, project_id: str, compliance_frameworks: Optional[List[str]] = None) -> Dict[str, Any]:
|
|
243
|
+
"""Launch automated audit pipeline."""
|
|
244
|
+
return self._post("/v1/autopilot", json={"description": description, "depth": depth, "projectId": project_id, "complianceFrameworks": compliance_frameworks})
|
|
245
|
+
|
|
246
|
+
def get_autopilot_config(self) -> Dict[str, Any]:
|
|
247
|
+
"""Get autopilot depth configurations."""
|
|
248
|
+
return self._get("/v1/autopilot")
|
|
249
|
+
|
|
250
|
+
# ── Pipelines ───────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
def create_pipeline(self, template_id: str, project_id: str) -> Dict[str, Any]:
|
|
253
|
+
"""Create eval pipeline from template."""
|
|
254
|
+
return self._post("/v1/pipelines", json={"templateId": template_id, "projectId": project_id})
|
|
255
|
+
|
|
256
|
+
def list_pipelines(self) -> List[Dict[str, Any]]:
|
|
257
|
+
"""List pipeline templates."""
|
|
258
|
+
return self._get("/v1/pipelines")
|
|
259
|
+
|
|
260
|
+
# ── Leaderboard ─────────────────────────────────────────────────────
|
|
261
|
+
|
|
262
|
+
def get_leaderboard(self, category: str = "overall") -> Dict[str, Any]:
|
|
263
|
+
"""Get model safety/performance leaderboard."""
|
|
264
|
+
return self._get("/v1/leaderboard", params={"category": category})
|
|
265
|
+
|
|
266
|
+
# ── Cost / FinOps ───────────────────────────────────────────────────
|
|
267
|
+
|
|
268
|
+
def get_cost(self, project_id: str, period: str = "30d") -> Dict[str, Any]:
|
|
269
|
+
"""Get cost analytics."""
|
|
270
|
+
return self._get("/v1/cost", params={"projectId": project_id, "period": period})
|
|
271
|
+
|
|
272
|
+
def get_cost_savings(self, project_id: str, period: str = "30d") -> Dict[str, Any]:
|
|
273
|
+
"""Get ROI / cost savings report."""
|
|
274
|
+
return self._get("/v1/cost/savings", params={"projectId": project_id, "period": period})
|
|
275
|
+
|
|
276
|
+
def get_cost_forecast(self, project_id: str) -> Dict[str, Any]:
|
|
277
|
+
"""Get cost forecast."""
|
|
278
|
+
return self._get("/v1/cost/forecast", params={"projectId": project_id})
|
|
279
|
+
|
|
280
|
+
# ── Security (extended) ─────────────────────────────────────────────
|
|
281
|
+
|
|
282
|
+
def get_security_effectiveness(self, project_id: str) -> Dict[str, Any]:
|
|
283
|
+
"""Get attack effectiveness analytics."""
|
|
284
|
+
return self._get("/v1/security/effectiveness", params={"projectId": project_id})
|
|
285
|
+
|
|
286
|
+
def get_security_report(self, scan_id: str) -> Dict[str, Any]:
|
|
287
|
+
"""Get full security assessment report."""
|
|
288
|
+
return self._get("/v1/security/report", params={"scanId": scan_id})
|
|
289
|
+
|
|
290
|
+
# ── Support ─────────────────────────────────────────────────────────
|
|
291
|
+
|
|
292
|
+
def submit_ticket(self, type: str, subject: str, description: str, priority: str = "medium", metadata: Optional[Dict] = None) -> Dict[str, Any]:
|
|
293
|
+
"""Submit a support ticket."""
|
|
294
|
+
return self._post("/v1/support", json={"type": type, "subject": subject, "description": description, "priority": priority, "metadata": metadata or {}})
|
|
295
|
+
|
|
296
|
+
def list_tickets(self, status: Optional[str] = None) -> Dict[str, Any]:
|
|
297
|
+
"""List user support tickets."""
|
|
298
|
+
params = {"status": status} if status else {}
|
|
299
|
+
return self._get("/v1/support", params=params)
|
|
300
|
+
|
|
301
|
+
# ── Traces ──────────────────────────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
def list_traces(self, project_id: str) -> List[Dict[str, Any]]:
|
|
304
|
+
"""List traces for a project."""
|
|
305
|
+
return self._get("/v1/traces", params={"projectId": project_id})
|
|
306
|
+
|
|
307
|
+
def search_traces(self, project_id: str, query: str) -> List[Dict[str, Any]]:
|
|
308
|
+
"""Search traces."""
|
|
309
|
+
return self._get("/v1/traces/search", params={"projectId": project_id, "q": query})
|
|
310
|
+
|
|
311
|
+
def ingest_otlp(self, resource_spans: List[Dict]) -> Dict[str, Any]:
|
|
312
|
+
"""Ingest OTLP traces."""
|
|
313
|
+
return self._post("/v1/ingest/otlp/traces", json={"resourceSpans": resource_spans})
|
|
314
|
+
|
|
315
|
+
# ── Monitoring ──────────────────────────────────────────────────────
|
|
316
|
+
|
|
317
|
+
def get_monitoring_analytics(self, project_id: str) -> Dict[str, Any]:
|
|
318
|
+
"""Get monitoring analytics."""
|
|
319
|
+
return self._get("/v1/monitoring/analytics", params={"projectId": project_id})
|
|
320
|
+
|
|
321
|
+
def get_monitoring_alerts(self, project_id: str) -> Dict[str, Any]:
|
|
322
|
+
"""Get monitoring alerts."""
|
|
323
|
+
return self._get("/v1/monitoring/alerts", params={"projectId": project_id})
|
|
324
|
+
|
|
325
|
+
# ── Compliance (extended) ───────────────────────────────────────────
|
|
326
|
+
|
|
327
|
+
def check_compliance(self, project_id: str, framework: Optional[str] = None) -> Dict[str, Any]:
|
|
328
|
+
"""Run compliance check."""
|
|
329
|
+
params = {"projectId": project_id}
|
|
330
|
+
if framework: params["framework"] = framework
|
|
331
|
+
return self._get("/v1/compliance/check", params=params)
|
|
332
|
+
|
|
333
|
+
def get_compliance_gaps(self, project_id: str) -> Dict[str, Any]:
|
|
334
|
+
"""Get compliance gaps."""
|
|
335
|
+
return self._get("/v1/compliance/gaps", params={"projectId": project_id})
|
|
336
|
+
|
|
337
|
+
# ── Prompts ─────────────────────────────────────────────────────────
|
|
338
|
+
|
|
339
|
+
def create_prompt(self, project_id: str, name: str, content: str, model: str = "gpt-4o", tags: Optional[List[str]] = None) -> Dict[str, Any]:
|
|
340
|
+
"""Create a prompt template."""
|
|
341
|
+
return self._post("/v1/prompts", json={"projectId": project_id, "name": name, "content": content, "model": model, "tags": tags or []})
|
|
342
|
+
|
|
343
|
+
def list_prompts(self, project_id: str) -> List[Dict[str, Any]]:
|
|
344
|
+
"""List prompts."""
|
|
345
|
+
return self._get("/v1/prompts", params={"projectId": project_id})
|
|
346
|
+
|
|
347
|
+
# ── Datasets ────────────────────────────────────────────────────────
|
|
348
|
+
|
|
349
|
+
def create_dataset(self, project_id: str, name: str, cases: List[Dict] = None, description: str = "") -> Dict[str, Any]:
|
|
350
|
+
"""Create a dataset."""
|
|
351
|
+
return self._post("/v1/datasets", json={"projectId": project_id, "name": name, "cases": cases or [], "description": description})
|
|
352
|
+
|
|
353
|
+
def list_datasets(self, project_id: str) -> List[Dict[str, Any]]:
|
|
354
|
+
"""List datasets."""
|
|
355
|
+
return self._get("/v1/datasets", params={"projectId": project_id})
|
|
356
|
+
|
|
357
|
+
# ── NL Pipeline ─────────────────────────────────────────────────────
|
|
358
|
+
|
|
359
|
+
def ask(self, question: str, project_id: Optional[str] = None) -> Dict[str, Any]:
|
|
360
|
+
"""Ask the AI copilot."""
|
|
361
|
+
return self._post("/v1/ask", json={"question": question, "projectId": project_id})
|
|
362
|
+
|
|
363
|
+
def generate_eval_suite(self, description: str, project_id: Optional[str] = None) -> Dict[str, Any]:
|
|
364
|
+
"""Generate eval test suite from description."""
|
|
365
|
+
return self._post("/v1/generate-eval-suite", json={"description": description, "projectId": project_id})
|
|
366
|
+
|
|
367
|
+
# ── AI SBOM ─────────────────────────────────────────────────────────
|
|
368
|
+
|
|
369
|
+
def get_ai_sbom(self, project_id: str) -> Dict[str, Any]:
|
|
370
|
+
"""Get AI System Bill of Materials."""
|
|
371
|
+
return self._get("/v1/ai-sbom", params={"projectId": project_id})
|
|
372
|
+
|
|
373
|
+
# ── Threat Intelligence ─────────────────────────────────────────────
|
|
374
|
+
|
|
375
|
+
def get_threat_intelligence(self, project_id: str) -> Dict[str, Any]:
|
|
376
|
+
"""Get threat intelligence data."""
|
|
377
|
+
return self._get("/v1/threat-intelligence", params={"projectId": project_id})
|
|
378
|
+
|
|
379
|
+
# ── Audit Logs ──────────────────────────────────────────────────────
|
|
380
|
+
|
|
381
|
+
def get_audit_logs(self, org_id: str) -> List[Dict[str, Any]]:
|
|
382
|
+
"""Get audit logs."""
|
|
383
|
+
return self._get("/v1/audit-logs", params={"orgId": org_id})
|
|
384
|
+
|
|
385
|
+
# ── Notifications ───────────────────────────────────────────────────
|
|
386
|
+
|
|
387
|
+
def list_notifications(self) -> List[Dict[str, Any]]:
|
|
388
|
+
"""List notifications."""
|
|
389
|
+
return self._get("/v1/notifications")
|
|
390
|
+
|
|
391
|
+
# ── Templates ───────────────────────────────────────────────────────
|
|
392
|
+
|
|
393
|
+
def list_templates(self) -> List[Dict[str, Any]]:
|
|
394
|
+
"""List eval templates."""
|
|
395
|
+
return self._get("/v1/templates")
|
|
396
|
+
|
|
397
|
+
# ── Marketplace ─────────────────────────────────────────────────────
|
|
398
|
+
|
|
399
|
+
def get_marketplace(self) -> Dict[str, Any]:
|
|
400
|
+
"""Get marketplace."""
|
|
401
|
+
return self._get("/v1/marketplace")
|
|
402
|
+
|
|
403
|
+
# ── Missing methods (parity with JS SDK) ───────────────────────────
|
|
404
|
+
|
|
405
|
+
def get_eval_run(self, run_id: str) -> Dict[str, Any]:
|
|
406
|
+
"""Get a specific eval run by ID."""
|
|
407
|
+
return self._get(f"/v1/evals/{run_id}")
|
|
408
|
+
|
|
409
|
+
def get_trace(self, trace_id: str) -> Dict[str, Any]:
|
|
410
|
+
"""Get a specific trace by ID."""
|
|
411
|
+
return self._get(f"/v1/traces/{trace_id}")
|
|
412
|
+
|
|
413
|
+
def trace(self, project_id: str, session_id: str, steps: List[Dict] = None) -> Dict[str, Any]:
|
|
414
|
+
"""Create a trace."""
|
|
415
|
+
return self._post("/v1/traces", json={"projectId": project_id, "sessionId": session_id, "steps": steps or []})
|
|
416
|
+
|
|
417
|
+
def security_scan(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
418
|
+
"""Start a security scan (alias for run_scan)."""
|
|
419
|
+
return self._post("/v1/security", json=config)
|
|
420
|
+
|
|
421
|
+
def create_annotation(self, project_id: str, log_id: str, label: str, score: float = None, notes: str = None) -> Dict[str, Any]:
|
|
422
|
+
"""Create an annotation on a log entry."""
|
|
423
|
+
body: Dict[str, Any] = {"projectId": project_id, "logId": log_id, "label": label}
|
|
424
|
+
if score is not None:
|
|
425
|
+
body["score"] = score
|
|
426
|
+
if notes:
|
|
427
|
+
body["notes"] = notes
|
|
428
|
+
return self._post("/v1/annotations", json=body)
|
|
429
|
+
|
|
430
|
+
def list_annotations(self, project_id: str) -> List[Dict[str, Any]]:
|
|
431
|
+
"""List annotations for a project."""
|
|
432
|
+
return self._get(f"/v1/annotations?projectId={project_id}")
|
|
433
|
+
|
|
434
|
+
def list_eval_schedules(self, project_id: str) -> List[Dict[str, Any]]:
|
|
435
|
+
"""List eval schedules for a project."""
|
|
436
|
+
return self._get(f"/v1/eval-schedules?projectId={project_id}")
|
|
437
|
+
|
|
438
|
+
def list_incidents(self, project_id: str) -> List[Dict[str, Any]]:
|
|
439
|
+
"""List incidents for a project."""
|
|
440
|
+
return self._get(f"/v1/incidents?projectId={project_id}")
|
|
441
|
+
|
|
442
|
+
def list_feature_flags(self, project_id: str) -> List[Dict[str, Any]]:
|
|
443
|
+
"""List feature flags for a project."""
|
|
444
|
+
return self._get(f"/v1/feature-flags?projectId={project_id}")
|
|
445
|
+
|
|
446
|
+
def list_guardrails(self, project_id: str) -> Dict[str, Any]:
|
|
447
|
+
"""List guardrails for a project."""
|
|
448
|
+
return self._get(f"/v1/guardrails?projectId={project_id}")
|
|
449
|
+
|
|
450
|
+
def list_team(self, org_id: str) -> List[Dict[str, Any]]:
|
|
451
|
+
"""List team members for an organization."""
|
|
452
|
+
return self._get(f"/v1/team?orgId={org_id}")
|
|
453
|
+
|
|
454
|
+
def list_webhooks(self, org_id: str) -> List[Dict[str, Any]]:
|
|
455
|
+
"""List webhooks for an organization."""
|
|
456
|
+
return self._get(f"/v1/webhooks?orgId={org_id}")
|
|
457
|
+
|
|
458
|
+
def get_gateway_health(self) -> Dict[str, Any]:
|
|
459
|
+
"""Get gateway health status."""
|
|
460
|
+
return self._get("/v1/gateway/health")
|
|
461
|
+
|
|
462
|
+
def get_gateway_stats(self, project_id: str) -> Dict[str, Any]:
|
|
463
|
+
"""Get gateway usage statistics."""
|
|
464
|
+
return self._get(f"/v1/gateway/stats?projectId={project_id}")
|
|
465
|
+
|
|
466
|
+
def get_gateway_config(self, project_id: str) -> Dict[str, Any]:
|
|
467
|
+
"""Get gateway configuration."""
|
|
468
|
+
return self._get(f"/v1/gateway?projectId={project_id}")
|
|
469
|
+
|
|
470
|
+
def get_monitoring_drift(self, project_id: str) -> Dict[str, Any]:
|
|
471
|
+
"""Get drift detection status."""
|
|
472
|
+
return self._get(f"/v1/monitoring/drift?projectId={project_id}")
|
|
473
|
+
|
|
474
|
+
def get_monitoring_sla(self, project_id: str) -> Dict[str, Any]:
|
|
475
|
+
"""Get SLA monitoring data."""
|
|
476
|
+
return self._get(f"/v1/monitoring/sla?projectId={project_id}")
|
|
477
|
+
|
|
478
|
+
def get_cost_budget(self, project_id: str) -> Dict[str, Any]:
|
|
479
|
+
"""Get cost budget for a project."""
|
|
480
|
+
return self._get(f"/v1/cost/budget?projectId={project_id}")
|
|
481
|
+
|
|
482
|
+
def get_siem_connectors(self, project_id: str) -> Dict[str, Any]:
|
|
483
|
+
"""Get SIEM connector configuration."""
|
|
484
|
+
return self._get(f"/v1/siem?projectId={project_id}")
|
|
485
|
+
|
|
486
|
+
def get_settings(self, project_id: str) -> Dict[str, Any]:
|
|
487
|
+
"""Get project settings."""
|
|
488
|
+
return self._get(f"/v1/settings?projectId={project_id}")
|
|
489
|
+
|
|
490
|
+
def get_model_cards(self, project_id: str) -> Dict[str, Any]:
|
|
491
|
+
"""Get model cards for compliance."""
|
|
492
|
+
return self._get(f"/v1/compliance/model-cards?projectId={project_id}")
|
|
493
|
+
|
|
494
|
+
def export_compliance(self, project_id: str, format: str = "json") -> Dict[str, Any]:
|
|
495
|
+
"""Export compliance report."""
|
|
496
|
+
return self._get(f"/v1/compliance/export?projectId={project_id}&format={format}")
|
|
497
|
+
|
|
498
|
+
def export_results(self, run_id: str, format: str, project_id: str) -> Dict[str, Any]:
|
|
499
|
+
"""Export eval results in specified format."""
|
|
500
|
+
return self._get(f"/v1/exports?runId={run_id}&format={format}&projectId={project_id}")
|
|
501
|
+
|
|
502
|
+
def generate_ai_sbom(self, project_id: str) -> Dict[str, Any]:
|
|
503
|
+
"""Generate AI Software Bill of Materials."""
|
|
504
|
+
return self._post("/v1/ai-sbom/generate", json={"projectId": project_id})
|
|
505
|
+
|
|
506
|
+
def ingest_otlp_traces(self, resource_spans: List[Dict]) -> Dict[str, Any]:
|
|
507
|
+
"""Ingest OpenTelemetry traces."""
|
|
508
|
+
return self._post("/v1/ingest/otlp/traces", json={"resourceSpans": resource_spans})
|
|
509
|
+
|
|
510
|
+
def ingest_otlp_logs(self, resource_logs: List[Dict]) -> Dict[str, Any]:
|
|
511
|
+
"""Ingest OpenTelemetry logs."""
|
|
512
|
+
return self._post("/v1/ingest/otlp/logs", json={"resourceLogs": resource_logs})
|
|
513
|
+
|
|
514
|
+
def ingest_otlp_metrics(self, resource_metrics: List[Dict]) -> Dict[str, Any]:
|
|
515
|
+
"""Ingest OpenTelemetry metrics."""
|
|
516
|
+
return self._post("/v1/ingest/otlp/metrics", json={"resourceMetrics": resource_metrics})
|