trace-agent-sdk 0.0.1__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.
@@ -0,0 +1,5 @@
1
+ __all__ = ["__version__", "TraceAgentClient", "RunSession"]
2
+
3
+ __version__ = "0.1.0"
4
+
5
+ from trace_agent_sdk.client import TraceAgentClient, RunSession
@@ -0,0 +1,616 @@
1
+ from __future__ import annotations
2
+
3
+ import difflib
4
+ import inspect
5
+ import json
6
+ import os
7
+ import subprocess
8
+ import time
9
+ from dataclasses import dataclass, field
10
+ from pathlib import Path
11
+ from typing import Any, Callable
12
+ from uuid import uuid4
13
+
14
+ import httpx
15
+
16
+ from trace_agent_sdk.redaction import stable_hash, summarize_value
17
+
18
+
19
+ TYPE_MAP = {
20
+ str: "string",
21
+ int: "integer",
22
+ float: "number",
23
+ bool: "boolean",
24
+ dict: "object",
25
+ list: "array",
26
+ }
27
+
28
+
29
+ @dataclass
30
+ class ToolBinding:
31
+ func: Callable[..., Any]
32
+ name: str
33
+ description: str
34
+ redact_fields: list[str]
35
+ input_schema: dict[str, Any]
36
+
37
+
38
+ @dataclass
39
+ class TurnResult:
40
+ assistant_message: dict[str, Any]
41
+ executed_tools: list[dict[str, Any]] = field(default_factory=list)
42
+
43
+
44
+ class CommandSession:
45
+ def __init__(self, run: "RunSession") -> None:
46
+ self.session = run
47
+
48
+ def run(
49
+ self,
50
+ command: str | list[str],
51
+ *,
52
+ cwd: str | None = None,
53
+ timeout: float | None = None,
54
+ check: bool = False,
55
+ shell: bool = False,
56
+ encoding: str = "utf-8",
57
+ ) -> subprocess.CompletedProcess[str]:
58
+ started_at = time.perf_counter()
59
+ completed = subprocess.run(
60
+ command,
61
+ cwd=cwd,
62
+ timeout=timeout,
63
+ shell=shell,
64
+ capture_output=True,
65
+ text=True,
66
+ encoding=encoding,
67
+ check=False,
68
+ )
69
+ duration_ms = int((time.perf_counter() - started_at) * 1000)
70
+ output_payload = {"stdout": completed.stdout, "stderr": completed.stderr}
71
+ status = "succeeded" if completed.returncode == 0 else "failed"
72
+ error_code = "CommandError" if completed.returncode != 0 else None
73
+ error_summary = None
74
+ if completed.returncode != 0:
75
+ error_summary = summarize_value(
76
+ {
77
+ "command": self.session._render_command(command),
78
+ "returncode": completed.returncode,
79
+ "stderr": completed.stderr,
80
+ }
81
+ )
82
+
83
+ self.session.record_command(
84
+ command,
85
+ status=status,
86
+ cwd=cwd,
87
+ output=output_payload,
88
+ duration_ms=duration_ms,
89
+ error_code=error_code,
90
+ error_summary=error_summary,
91
+ exit_code=completed.returncode,
92
+ stdout_summary=summarize_value(completed.stdout) if completed.stdout else None,
93
+ stderr_summary=summarize_value(completed.stderr) if completed.stderr else None,
94
+ )
95
+ if check and completed.returncode != 0:
96
+ raise subprocess.CalledProcessError(
97
+ completed.returncode,
98
+ completed.args,
99
+ output=completed.stdout,
100
+ stderr=completed.stderr,
101
+ )
102
+ return completed
103
+
104
+
105
+ class FileSession:
106
+ def __init__(self, run: "RunSession") -> None:
107
+ self.session = run
108
+
109
+ def read_text(self, path: str, *, encoding: str = "utf-8") -> str:
110
+ path_obj = Path(path)
111
+ content = path_obj.read_text(encoding=encoding)
112
+ self.session.record_file_read(path, content=content)
113
+ return content
114
+
115
+ def write_text(self, path: str, content: str, *, encoding: str = "utf-8") -> None:
116
+ path_obj = Path(path)
117
+ before_content = path_obj.read_text(encoding=encoding) if path_obj.exists() else None
118
+ change_type = "update" if before_content is not None else "create"
119
+ path_obj.parent.mkdir(parents=True, exist_ok=True)
120
+ path_obj.write_text(content, encoding=encoding)
121
+ self.session.record_file_write(
122
+ path,
123
+ change_type=change_type,
124
+ before_content=before_content,
125
+ after_content=content,
126
+ )
127
+
128
+ def delete(self, path: str, *, encoding: str = "utf-8") -> None:
129
+ path_obj = Path(path)
130
+ if not path_obj.exists():
131
+ return
132
+ before_content = path_obj.read_text(encoding=encoding)
133
+ path_obj.unlink()
134
+ self.session.record_file_write(
135
+ path,
136
+ change_type="delete",
137
+ before_content=before_content,
138
+ after_content=None,
139
+ )
140
+
141
+ def patch_text(
142
+ self,
143
+ path: str,
144
+ updated_content: str,
145
+ *,
146
+ encoding: str = "utf-8",
147
+ diff_text: str | None = None,
148
+ ) -> None:
149
+ path_obj = Path(path)
150
+ before_content = path_obj.read_text(encoding=encoding) if path_obj.exists() else None
151
+ change_type = "update" if before_content is not None else "create"
152
+ path_obj.parent.mkdir(parents=True, exist_ok=True)
153
+ path_obj.write_text(updated_content, encoding=encoding)
154
+ self.session.record_file_write(
155
+ path,
156
+ change_type=change_type,
157
+ before_content=before_content,
158
+ after_content=updated_content,
159
+ )
160
+ self.session.record_patch(
161
+ path,
162
+ diff_text=diff_text,
163
+ before_content=before_content,
164
+ after_content=updated_content,
165
+ )
166
+
167
+
168
+ class ArtifactSession:
169
+ def __init__(self, run: "RunSession") -> None:
170
+ self.session = run
171
+
172
+ def capture(
173
+ self,
174
+ kind: str,
175
+ label: str,
176
+ summary: str,
177
+ *,
178
+ metadata: dict[str, Any] | None = None,
179
+ content: Any | None = None,
180
+ ) -> dict[str, Any]:
181
+ return self.session.record_artifact(
182
+ kind,
183
+ label,
184
+ summary,
185
+ metadata=metadata,
186
+ content=content,
187
+ )
188
+
189
+
190
+ class RunSession:
191
+ def __init__(self, client: "TraceAgentClient", run_id: str, agent_name: str, goal: str, metadata: dict[str, Any] | None = None) -> None:
192
+ self.client = client
193
+ self.run_id = run_id
194
+ self.agent_name = agent_name
195
+ self.goal = goal
196
+ self.metadata = metadata or {}
197
+ self.tools: dict[str, ToolBinding] = {}
198
+ self.messages: list[dict[str, Any]] = []
199
+ self.commands = CommandSession(self)
200
+ self.files = FileSession(self)
201
+ self.artifacts = ArtifactSession(self)
202
+
203
+ def record_command(
204
+ self,
205
+ command: str | list[str],
206
+ *,
207
+ status: str = "succeeded",
208
+ cwd: str | None = None,
209
+ output: Any | None = None,
210
+ duration_ms: int | None = None,
211
+ error_code: str | None = None,
212
+ error_summary: str | None = None,
213
+ exit_code: int | None = None,
214
+ stdout_summary: str | None = None,
215
+ stderr_summary: str | None = None,
216
+ step_id: str | None = None,
217
+ parent_step_id: str | None = None,
218
+ metadata: dict[str, Any] | None = None,
219
+ ) -> dict[str, Any]:
220
+ command_text = self._render_command(command)
221
+ payload = {
222
+ "observations": [
223
+ {
224
+ "kind": "command",
225
+ "step_id": step_id or self._step_id("command"),
226
+ "parent_step_id": parent_step_id,
227
+ "status": status,
228
+ "command_id": str(uuid4()),
229
+ "command": command_text,
230
+ "cwd": cwd,
231
+ "output_summary": summarize_value(output) if output is not None else None,
232
+ "duration_ms": duration_ms,
233
+ "error_code": error_code,
234
+ "error_summary": error_summary,
235
+ "exit_code": exit_code,
236
+ "stdout_summary": stdout_summary,
237
+ "stderr_summary": stderr_summary,
238
+ "metadata": metadata or {},
239
+ }
240
+ ]
241
+ }
242
+ return self.client._request("POST", f"/api/runs/{self.run_id}/observations", payload)
243
+
244
+ def record_file_read(
245
+ self,
246
+ path: str,
247
+ *,
248
+ content: Any | None = None,
249
+ content_hash: str | None = None,
250
+ size_bytes: int | None = None,
251
+ step_id: str | None = None,
252
+ parent_step_id: str | None = None,
253
+ ) -> dict[str, Any]:
254
+ payload = {
255
+ "observations": [
256
+ {
257
+ "kind": "file_read",
258
+ "step_id": step_id or self._step_id("file-read"),
259
+ "parent_step_id": parent_step_id,
260
+ "path": path,
261
+ "content_hash": content_hash or (stable_hash(content) if content is not None else None),
262
+ "size_bytes": size_bytes if size_bytes is not None else self._size_bytes(content),
263
+ "extension": os.path.splitext(path)[1] or None,
264
+ }
265
+ ]
266
+ }
267
+ return self.client._request("POST", f"/api/runs/{self.run_id}/observations", payload)
268
+
269
+ def record_file_write(
270
+ self,
271
+ path: str,
272
+ *,
273
+ change_type: str = "update",
274
+ before_content: Any | None = None,
275
+ after_content: Any | None = None,
276
+ before_hash: str | None = None,
277
+ after_hash: str | None = None,
278
+ step_id: str | None = None,
279
+ parent_step_id: str | None = None,
280
+ ) -> dict[str, Any]:
281
+ diff_summary, line_additions, line_deletions = self._diff_details(before_content, after_content)
282
+ payload = {
283
+ "observations": [
284
+ {
285
+ "kind": "file_write",
286
+ "step_id": step_id or self._step_id("file-write"),
287
+ "parent_step_id": parent_step_id,
288
+ "path": path,
289
+ "change_type": change_type,
290
+ "before_hash": before_hash or (stable_hash(before_content) if before_content is not None else None),
291
+ "after_hash": after_hash or (stable_hash(after_content) if after_content is not None else None),
292
+ "content_hash": stable_hash(after_content) if after_content is not None else after_hash,
293
+ "diff_summary": diff_summary,
294
+ "line_additions": line_additions,
295
+ "line_deletions": line_deletions,
296
+ "extension": os.path.splitext(path)[1] or None,
297
+ "size_bytes": self._size_bytes(after_content),
298
+ }
299
+ ]
300
+ }
301
+ return self.client._request("POST", f"/api/runs/{self.run_id}/observations", payload)
302
+
303
+ def record_patch(
304
+ self,
305
+ path: str,
306
+ *,
307
+ diff_text: str | None = None,
308
+ before_content: Any | None = None,
309
+ after_content: Any | None = None,
310
+ before_hash: str | None = None,
311
+ after_hash: str | None = None,
312
+ step_id: str | None = None,
313
+ parent_step_id: str | None = None,
314
+ ) -> dict[str, Any]:
315
+ generated_summary, line_additions, line_deletions = self._diff_details(before_content, after_content)
316
+ diff_summary = summarize_value(diff_text) if diff_text is not None else generated_summary
317
+ payload = {
318
+ "observations": [
319
+ {
320
+ "kind": "patch",
321
+ "step_id": step_id or self._step_id("patch"),
322
+ "parent_step_id": parent_step_id,
323
+ "path": path,
324
+ "before_hash": before_hash or (stable_hash(before_content) if before_content is not None else None),
325
+ "after_hash": after_hash or (stable_hash(after_content) if after_content is not None else None),
326
+ "diff_summary": diff_summary,
327
+ "line_additions": line_additions,
328
+ "line_deletions": line_deletions,
329
+ }
330
+ ]
331
+ }
332
+ return self.client._request("POST", f"/api/runs/{self.run_id}/observations", payload)
333
+
334
+ def record_artifact(
335
+ self,
336
+ kind: str,
337
+ label: str,
338
+ summary: str,
339
+ *,
340
+ metadata: dict[str, Any] | None = None,
341
+ content: Any | None = None,
342
+ content_hash: str | None = None,
343
+ step_id: str | None = None,
344
+ parent_step_id: str | None = None,
345
+ ) -> dict[str, Any]:
346
+ payload = {
347
+ "observations": [
348
+ {
349
+ "kind": "artifact",
350
+ "step_id": step_id or self._step_id("artifact"),
351
+ "parent_step_id": parent_step_id,
352
+ "artifact": {
353
+ "kind": kind,
354
+ "label": label,
355
+ "summary": summary,
356
+ "metadata": {
357
+ **(metadata or {}),
358
+ "content_hash": content_hash or (stable_hash(content) if content is not None else None),
359
+ },
360
+ },
361
+ }
362
+ ]
363
+ }
364
+ return self.client._request("POST", f"/api/runs/{self.run_id}/observations", payload)
365
+
366
+ def tool(
367
+ self,
368
+ *,
369
+ name: str | None = None,
370
+ description: str | None = None,
371
+ redact_fields: list[str] | None = None,
372
+ ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
373
+ def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
374
+ tool_name = name or func.__name__
375
+ self.tools[tool_name] = ToolBinding(
376
+ func=func,
377
+ name=tool_name,
378
+ description=description or (inspect.getdoc(func) or f"Tool {tool_name}"),
379
+ redact_fields=redact_fields or [],
380
+ input_schema=self._build_input_schema(func),
381
+ )
382
+ return func
383
+
384
+ return decorator
385
+
386
+ def create_model_turn(
387
+ self,
388
+ messages: list[dict[str, Any]] | None,
389
+ *,
390
+ model: str,
391
+ provider: str = "openai",
392
+ tool_choice: str | dict[str, Any] = "auto",
393
+ ) -> TurnResult:
394
+ if messages:
395
+ self.messages.extend(messages)
396
+
397
+ executed_tools: list[dict[str, Any]] = []
398
+ while True:
399
+ response = self.client._request(
400
+ "POST",
401
+ f"/api/runs/{self.run_id}/turns",
402
+ {
403
+ "provider": provider,
404
+ "model": model,
405
+ "messages": self.messages,
406
+ "tools": [self._tool_payload(tool) for tool in self.tools.values()],
407
+ "tool_choice": tool_choice,
408
+ },
409
+ )
410
+ if response["status"] == "message":
411
+ assistant_message = response["assistant_message"] or {"role": "assistant", "content": ""}
412
+ self.messages.append(assistant_message)
413
+ return TurnResult(assistant_message=assistant_message, executed_tools=executed_tools)
414
+
415
+ tool_calls = response["tool_calls"]
416
+ assistant_message = {
417
+ "role": "assistant",
418
+ "content": None,
419
+ "tool_calls": [
420
+ {
421
+ "id": call["id"],
422
+ "type": "function",
423
+ "function": {"name": call["name"], "arguments": json.dumps(call["arguments"])},
424
+ }
425
+ for call in tool_calls
426
+ ],
427
+ }
428
+ self.messages.append(assistant_message)
429
+ results_payload = []
430
+ for call in tool_calls:
431
+ result_payload, tool_message = self._execute_tool(call)
432
+ executed_tools.append(result_payload)
433
+ results_payload.append(result_payload)
434
+ self.messages.append(tool_message)
435
+
436
+ self.client._request("POST", f"/api/runs/{self.run_id}/tool-results", {"results": results_payload})
437
+
438
+ def finish(self, final_output: Any | None = None, artifacts: list[dict[str, Any]] | None = None) -> dict[str, Any]:
439
+ return self.client._request(
440
+ "POST",
441
+ f"/api/runs/{self.run_id}/finish",
442
+ {"final_output": final_output, "artifacts": artifacts or []},
443
+ )
444
+
445
+ def fail(self, error_code: str, error_summary: str) -> dict[str, Any]:
446
+ return self.client._request(
447
+ "POST",
448
+ f"/api/runs/{self.run_id}/fail",
449
+ {"error_code": error_code, "error_summary": error_summary},
450
+ )
451
+
452
+ def as_langchain_callback(
453
+ self,
454
+ *,
455
+ record_chain_events: bool = True,
456
+ record_retriever_events: bool = True,
457
+ ) -> Any:
458
+ """Return a LangChain callback handler bound to this run.
459
+
460
+ Usage:
461
+ callback = run.as_langchain_callback()
462
+ chain.invoke({...}, config={"callbacks": [callback]})
463
+ """
464
+ from trace_agent_langchain import TraceAgentLangChainCallback
465
+
466
+ return TraceAgentLangChainCallback(
467
+ self,
468
+ record_chain_events=record_chain_events,
469
+ record_retriever_events=record_retriever_events,
470
+ )
471
+
472
+ def _execute_tool(self, call: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
473
+ binding = self.tools.get(call["name"])
474
+ if binding is None:
475
+ raise KeyError(f"Tool '{call['name']}' is not registered in this run.")
476
+
477
+ arguments = call.get("arguments") or {}
478
+ started_at = time.perf_counter()
479
+ try:
480
+ output = binding.func(**arguments)
481
+ duration_ms = int((time.perf_counter() - started_at) * 1000)
482
+ result_payload = {
483
+ "call_id": call["id"],
484
+ "name": binding.name,
485
+ "step_id": call["step_id"],
486
+ "status": "succeeded",
487
+ "args": arguments,
488
+ "output_summary": summarize_value(output, binding.redact_fields),
489
+ "output_hash": stable_hash(output),
490
+ "duration_ms": duration_ms,
491
+ "artifacts": [],
492
+ }
493
+ tool_message = {
494
+ "role": "tool",
495
+ "tool_call_id": call["id"],
496
+ "content": json.dumps(output, ensure_ascii=True, default=str),
497
+ }
498
+ return result_payload, tool_message
499
+ except Exception as exc:
500
+ duration_ms = int((time.perf_counter() - started_at) * 1000)
501
+ result_payload = {
502
+ "call_id": call["id"],
503
+ "name": binding.name,
504
+ "step_id": call["step_id"],
505
+ "status": "failed",
506
+ "args": arguments,
507
+ "output_summary": None,
508
+ "output_hash": stable_hash({"error": str(exc)}),
509
+ "error_code": type(exc).__name__,
510
+ "error_summary": str(exc),
511
+ "duration_ms": duration_ms,
512
+ "artifacts": [],
513
+ }
514
+ tool_message = {
515
+ "role": "tool",
516
+ "tool_call_id": call["id"],
517
+ "content": json.dumps({"error": str(exc)}, ensure_ascii=True),
518
+ }
519
+ return result_payload, tool_message
520
+
521
+ def _build_input_schema(self, func: Callable[..., Any]) -> dict[str, Any]:
522
+ signature = inspect.signature(func)
523
+ properties: dict[str, Any] = {}
524
+ required: list[str] = []
525
+ for parameter_name, parameter in signature.parameters.items():
526
+ annotation = parameter.annotation if parameter.annotation is not inspect._empty else str
527
+ properties[parameter_name] = {"type": TYPE_MAP.get(annotation, "string")}
528
+ if parameter.default is inspect._empty:
529
+ required.append(parameter_name)
530
+ return {"type": "object", "properties": properties, "required": required}
531
+
532
+ @staticmethod
533
+ def _tool_payload(tool: ToolBinding) -> dict[str, Any]:
534
+ return {
535
+ "name": tool.name,
536
+ "description": tool.description,
537
+ "input_schema": tool.input_schema,
538
+ "redact_fields": tool.redact_fields,
539
+ }
540
+
541
+ @staticmethod
542
+ def _render_command(command: str | list[str]) -> str:
543
+ if isinstance(command, str):
544
+ return command
545
+ return " ".join(str(part) for part in command)
546
+
547
+ @staticmethod
548
+ def _step_id(prefix: str) -> str:
549
+ return f"{prefix}-{uuid4().hex[:8]}"
550
+
551
+ @staticmethod
552
+ def _size_bytes(value: Any | None) -> int | None:
553
+ if value is None:
554
+ return None
555
+ if isinstance(value, bytes):
556
+ return len(value)
557
+ return len(str(value).encode("utf-8"))
558
+
559
+ @staticmethod
560
+ def _diff_details(before_content: Any | None, after_content: Any | None) -> tuple[str | None, int | None, int | None]:
561
+ if before_content is None and after_content is None:
562
+ return None, None, None
563
+
564
+ before_lines = str(before_content or "").splitlines()
565
+ after_lines = str(after_content or "").splitlines()
566
+ diff_lines = list(difflib.unified_diff(before_lines, after_lines, lineterm="", n=1))
567
+ if not diff_lines:
568
+ return "No textual diff.", 0, 0
569
+
570
+ line_additions = sum(
571
+ 1 for line in diff_lines if line.startswith("+") and not line.startswith("+++")
572
+ )
573
+ line_deletions = sum(
574
+ 1 for line in diff_lines if line.startswith("-") and not line.startswith("---")
575
+ )
576
+ preview = "\n".join(diff_lines[:10])
577
+ return summarize_value(preview), line_additions, line_deletions
578
+
579
+
580
+ class TraceAgentClient:
581
+ def __init__(
582
+ self,
583
+ base_url: str,
584
+ api_key: str | None = None,
585
+ timeout: float = 30.0,
586
+ http_client: httpx.Client | None = None,
587
+ ) -> None:
588
+ if http_client is not None:
589
+ self.http = http_client
590
+ return
591
+
592
+ headers = {"Content-Type": "application/json"}
593
+ if api_key:
594
+ headers["Authorization"] = f"Bearer {api_key}"
595
+ self.http = httpx.Client(base_url=base_url.rstrip("/"), headers=headers, timeout=timeout)
596
+
597
+ def start_run(self, agent_name: str, goal: str, metadata: dict[str, Any] | None = None) -> RunSession:
598
+ response = self._request("POST", "/api/runs", {"agent_name": agent_name, "goal": goal, "metadata": metadata or {}})
599
+ return RunSession(self, response["run_id"], agent_name, goal, metadata)
600
+
601
+ def _request(self, method: str, path: str, payload: dict[str, Any] | None = None) -> dict[str, Any]:
602
+ response = self.http.request(method, path, json=payload)
603
+ if response.is_error:
604
+ detail = response.text
605
+ try:
606
+ body = response.json()
607
+ if isinstance(body, dict):
608
+ detail = body.get("detail", detail)
609
+ except Exception:
610
+ pass
611
+ raise httpx.HTTPStatusError(
612
+ f"{response.status_code} for {path}: {detail}",
613
+ request=response.request,
614
+ response=response,
615
+ )
616
+ return response.json()
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import json
5
+ from typing import Any
6
+
7
+
8
+ def stable_hash(value: Any) -> str:
9
+ raw = json.dumps(value, ensure_ascii=True, sort_keys=True, default=str)
10
+ return hashlib.sha256(raw.encode("utf-8")).hexdigest()
11
+
12
+
13
+ def redact_payload(value: Any, redact_fields: list[str] | None = None) -> Any:
14
+ redact_fields = set(redact_fields or [])
15
+ if isinstance(value, dict):
16
+ redacted: dict[str, Any] = {}
17
+ for key, inner in value.items():
18
+ if key in redact_fields:
19
+ redacted[key] = "[redacted]"
20
+ else:
21
+ redacted[key] = redact_payload(inner, list(redact_fields))
22
+ return redacted
23
+ if isinstance(value, list):
24
+ return [redact_payload(item, list(redact_fields)) for item in value]
25
+ if isinstance(value, str):
26
+ if len(value) <= 160:
27
+ return value
28
+ return f"{value[:157]}..."
29
+ return value
30
+
31
+
32
+ def summarize_value(value: Any, redact_fields: list[str] | None = None, max_length: int = 220) -> str:
33
+ redacted = redact_payload(value, redact_fields)
34
+ if isinstance(redacted, str):
35
+ return redacted
36
+ rendered = json.dumps(redacted, ensure_ascii=True, sort_keys=True, default=str)
37
+ if len(rendered) <= max_length:
38
+ return rendered
39
+ return f"{rendered[: max_length - 3]}..."
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: trace-agent-sdk
3
+ Version: 0.0.1
4
+ Summary: TraceAgent SDK for instrumenting AI agents.
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: httpx<1.0.0,>=0.27.0
7
+ Requires-Dist: pydantic<3.0.0,>=2.8.0
8
+ Provides-Extra: dev
9
+ Requires-Dist: pytest<9.0.0,>=8.3.2; extra == "dev"
@@ -0,0 +1,7 @@
1
+ trace_agent_sdk/__init__.py,sha256=hjbc5GVdbWzhsw6auG-Z1uCzRyrMxIix1XuUkLo1OEw,148
2
+ trace_agent_sdk/client.py,sha256=HmJUgltUHoLkbVU3_Jtnxu44vci27CqBcu_1NCpIqbg,22634
3
+ trace_agent_sdk/redaction.py,sha256=tkhegqsHGJ8It9oNRS5Q2hRITc7XsapC70GTmWGhV4I,1347
4
+ trace_agent_sdk-0.0.1.dist-info/METADATA,sha256=Ld4KgeAcubvpF4HZd4jW57vw8oQbtfMWODd54joAajo,282
5
+ trace_agent_sdk-0.0.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
6
+ trace_agent_sdk-0.0.1.dist-info/top_level.txt,sha256=k4tULAp1RyGZKzE8cYiNi1HKfE263zHOaYE0RYp3cx0,16
7
+ trace_agent_sdk-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ trace_agent_sdk