agentos-python 0.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.
- agentos/__init__.py +58 -0
- agentos/client.py +273 -0
- agentos/compat/__init__.py +4 -0
- agentos/compat/braintrust.py +97 -0
- agentos/compat/helicone.py +72 -0
- agentos/compat/langfuse.py +317 -0
- agentos/compat/langsmith.py +79 -0
- agentos/compat/otel.py +167 -0
- agentos/compat/weave.py +93 -0
- agentos/config.py +34 -0
- agentos/decorators.py +372 -0
- agentos/events.py +449 -0
- agentos/integrations/__init__.py +15 -0
- agentos/integrations/anthropic.py +130 -0
- agentos/integrations/crewai.py +221 -0
- agentos/integrations/langchain.py +307 -0
- agentos/integrations/litellm.py +123 -0
- agentos/integrations/llamaindex.py +174 -0
- agentos/integrations/openai.py +149 -0
- agentos/integrations/pydantic_ai.py +236 -0
- agentos/py.typed +0 -0
- agentos/tracing.py +168 -0
- agentos/transport.py +285 -0
- agentos/types.py +101 -0
- agentos_python-0.1.0.dist-info/METADATA +155 -0
- agentos_python-0.1.0.dist-info/RECORD +28 -0
- agentos_python-0.1.0.dist-info/WHEEL +4 -0
- agentos_python-0.1.0.dist-info/licenses/LICENSE +21 -0
agentos/events.py
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
"""Event builder functions — one per event type.
|
|
2
|
+
|
|
3
|
+
Each function returns a dict matching the ``EventInput`` wire format
|
|
4
|
+
with dot-notation property keys. All builders:
|
|
5
|
+
|
|
6
|
+
- Auto-generate ``event_id``, ``timestamp``, ``span_id`` if not provided
|
|
7
|
+
- Pull ``trace_id`` from the current context if not provided
|
|
8
|
+
- Accept ``**extra`` for custom properties
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from agentos.tracing import (
|
|
16
|
+
generate_event_id,
|
|
17
|
+
generate_span_id,
|
|
18
|
+
generate_trace_id,
|
|
19
|
+
get_current_context,
|
|
20
|
+
utcnow_iso,
|
|
21
|
+
)
|
|
22
|
+
from agentos.types import EventType
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _base_envelope(
|
|
26
|
+
event_type: EventType,
|
|
27
|
+
agent_id: str,
|
|
28
|
+
*,
|
|
29
|
+
trace_id: str | None = None,
|
|
30
|
+
span_id: str | None = None,
|
|
31
|
+
parent_span_id: str | None = None,
|
|
32
|
+
task_run_id: str | None = None,
|
|
33
|
+
user_id: str | None = None,
|
|
34
|
+
session_id: str | None = None,
|
|
35
|
+
environment: str | None = None,
|
|
36
|
+
) -> dict[str, Any]:
|
|
37
|
+
"""Build the common event envelope, filling from context if available."""
|
|
38
|
+
ctx = get_current_context()
|
|
39
|
+
|
|
40
|
+
envelope: dict[str, Any] = {
|
|
41
|
+
"event_id": generate_event_id(),
|
|
42
|
+
"event_type": event_type.value,
|
|
43
|
+
"timestamp": utcnow_iso(),
|
|
44
|
+
"trace_id": trace_id or (ctx.trace_id if ctx else generate_trace_id()),
|
|
45
|
+
"span_id": span_id or (ctx.span_id if ctx else generate_span_id()),
|
|
46
|
+
"agent_id": agent_id,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# Optional fields — prefer explicit args, then context, then omit
|
|
50
|
+
pid = parent_span_id or (ctx.parent_span_id if ctx else None)
|
|
51
|
+
if pid:
|
|
52
|
+
envelope["parent_span_id"] = pid
|
|
53
|
+
|
|
54
|
+
trid = task_run_id or (ctx.task_run_id if ctx else None)
|
|
55
|
+
if trid:
|
|
56
|
+
envelope["task_run_id"] = trid
|
|
57
|
+
|
|
58
|
+
uid = user_id or (ctx.user_id if ctx else None)
|
|
59
|
+
if uid:
|
|
60
|
+
envelope["user_id"] = uid
|
|
61
|
+
|
|
62
|
+
sid = session_id or (ctx.session_id if ctx else None)
|
|
63
|
+
if sid:
|
|
64
|
+
envelope["session_id"] = sid
|
|
65
|
+
|
|
66
|
+
if environment:
|
|
67
|
+
envelope["environment"] = environment
|
|
68
|
+
|
|
69
|
+
return envelope
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _set_if_not_none(d: dict[str, Any], key: str, value: Any) -> None:
|
|
73
|
+
"""Set key in dict only if value is not None."""
|
|
74
|
+
if value is not None:
|
|
75
|
+
d[key] = value
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ---------------------------------------------------------------------------
|
|
79
|
+
# Event builders
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def llm_call(
|
|
84
|
+
agent_id: str,
|
|
85
|
+
*,
|
|
86
|
+
model: str,
|
|
87
|
+
system: str,
|
|
88
|
+
trace_id: str | None = None,
|
|
89
|
+
span_id: str | None = None,
|
|
90
|
+
parent_span_id: str | None = None,
|
|
91
|
+
task_run_id: str | None = None,
|
|
92
|
+
user_id: str | None = None,
|
|
93
|
+
session_id: str | None = None,
|
|
94
|
+
environment: str | None = None,
|
|
95
|
+
response_model: str | None = None,
|
|
96
|
+
temperature: float | None = None,
|
|
97
|
+
top_p: float | None = None,
|
|
98
|
+
max_tokens: int | None = None,
|
|
99
|
+
stop_sequences: list[str] | None = None,
|
|
100
|
+
response_id: str | None = None,
|
|
101
|
+
finish_reason: str | None = None,
|
|
102
|
+
input_tokens: int | None = None,
|
|
103
|
+
output_tokens: int | None = None,
|
|
104
|
+
total_tokens: int | None = None,
|
|
105
|
+
input: list[dict[str, Any]] | None = None,
|
|
106
|
+
output: list[dict[str, Any]] | None = None,
|
|
107
|
+
duration_ms: float | None = None,
|
|
108
|
+
error: dict[str, str] | None = None,
|
|
109
|
+
stream: bool | None = None,
|
|
110
|
+
**extra: Any,
|
|
111
|
+
) -> dict[str, Any]:
|
|
112
|
+
"""Build an ``agent.llm_call`` event."""
|
|
113
|
+
event = _base_envelope(
|
|
114
|
+
EventType.LLM_CALL,
|
|
115
|
+
agent_id,
|
|
116
|
+
trace_id=trace_id,
|
|
117
|
+
span_id=span_id,
|
|
118
|
+
parent_span_id=parent_span_id,
|
|
119
|
+
task_run_id=task_run_id,
|
|
120
|
+
user_id=user_id,
|
|
121
|
+
session_id=session_id,
|
|
122
|
+
environment=environment,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
props: dict[str, Any] = {
|
|
126
|
+
"gen_ai.system": system,
|
|
127
|
+
"gen_ai.request.model": model,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
_set_if_not_none(props, "gen_ai.response.model", response_model)
|
|
131
|
+
_set_if_not_none(props, "gen_ai.request.temperature", temperature)
|
|
132
|
+
_set_if_not_none(props, "gen_ai.request.top_p", top_p)
|
|
133
|
+
_set_if_not_none(props, "gen_ai.request.max_tokens", max_tokens)
|
|
134
|
+
_set_if_not_none(props, "gen_ai.request.stop_sequences", stop_sequences)
|
|
135
|
+
_set_if_not_none(props, "gen_ai.response.id", response_id)
|
|
136
|
+
_set_if_not_none(props, "gen_ai.response.finish_reason", finish_reason)
|
|
137
|
+
_set_if_not_none(props, "gen_ai.usage.input_tokens", input_tokens)
|
|
138
|
+
_set_if_not_none(props, "gen_ai.usage.output_tokens", output_tokens)
|
|
139
|
+
_set_if_not_none(props, "gen_ai.usage.total_tokens", total_tokens)
|
|
140
|
+
_set_if_not_none(props, "input", input)
|
|
141
|
+
_set_if_not_none(props, "output", output)
|
|
142
|
+
_set_if_not_none(props, "duration_ms", duration_ms)
|
|
143
|
+
_set_if_not_none(props, "error", error)
|
|
144
|
+
_set_if_not_none(props, "stream", stream)
|
|
145
|
+
|
|
146
|
+
props.update(extra)
|
|
147
|
+
event["properties"] = props
|
|
148
|
+
return event
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def tool_call(
|
|
152
|
+
agent_id: str,
|
|
153
|
+
*,
|
|
154
|
+
tool_name: str,
|
|
155
|
+
trace_id: str | None = None,
|
|
156
|
+
span_id: str | None = None,
|
|
157
|
+
parent_span_id: str | None = None,
|
|
158
|
+
task_run_id: str | None = None,
|
|
159
|
+
user_id: str | None = None,
|
|
160
|
+
session_id: str | None = None,
|
|
161
|
+
environment: str | None = None,
|
|
162
|
+
description: str | None = None,
|
|
163
|
+
input: Any | None = None,
|
|
164
|
+
output: Any | None = None,
|
|
165
|
+
status: str | None = None,
|
|
166
|
+
duration_ms: float | None = None,
|
|
167
|
+
error: dict[str, str] | None = None,
|
|
168
|
+
**extra: Any,
|
|
169
|
+
) -> dict[str, Any]:
|
|
170
|
+
"""Build an ``agent.tool_call`` event."""
|
|
171
|
+
event = _base_envelope(
|
|
172
|
+
EventType.TOOL_CALL,
|
|
173
|
+
agent_id,
|
|
174
|
+
trace_id=trace_id,
|
|
175
|
+
span_id=span_id,
|
|
176
|
+
parent_span_id=parent_span_id,
|
|
177
|
+
task_run_id=task_run_id,
|
|
178
|
+
user_id=user_id,
|
|
179
|
+
session_id=session_id,
|
|
180
|
+
environment=environment,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
props: dict[str, Any] = {"tool.name": tool_name}
|
|
184
|
+
_set_if_not_none(props, "tool.description", description)
|
|
185
|
+
_set_if_not_none(props, "tool.input", input)
|
|
186
|
+
_set_if_not_none(props, "tool.output", output)
|
|
187
|
+
_set_if_not_none(props, "tool.status", status)
|
|
188
|
+
_set_if_not_none(props, "duration_ms", duration_ms)
|
|
189
|
+
_set_if_not_none(props, "error", error)
|
|
190
|
+
|
|
191
|
+
props.update(extra)
|
|
192
|
+
event["properties"] = props
|
|
193
|
+
return event
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def handoff(
|
|
197
|
+
agent_id: str,
|
|
198
|
+
*,
|
|
199
|
+
target_agent_id: str,
|
|
200
|
+
trace_id: str | None = None,
|
|
201
|
+
span_id: str | None = None,
|
|
202
|
+
parent_span_id: str | None = None,
|
|
203
|
+
task_run_id: str | None = None,
|
|
204
|
+
user_id: str | None = None,
|
|
205
|
+
session_id: str | None = None,
|
|
206
|
+
environment: str | None = None,
|
|
207
|
+
reason: str | None = None,
|
|
208
|
+
context: dict[str, Any] | None = None,
|
|
209
|
+
**extra: Any,
|
|
210
|
+
) -> dict[str, Any]:
|
|
211
|
+
"""Build an ``agent.handoff`` event."""
|
|
212
|
+
event = _base_envelope(
|
|
213
|
+
EventType.AGENT_HANDOFF,
|
|
214
|
+
agent_id,
|
|
215
|
+
trace_id=trace_id,
|
|
216
|
+
span_id=span_id,
|
|
217
|
+
parent_span_id=parent_span_id,
|
|
218
|
+
task_run_id=task_run_id,
|
|
219
|
+
user_id=user_id,
|
|
220
|
+
session_id=session_id,
|
|
221
|
+
environment=environment,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
props: dict[str, Any] = {"target_agent_id": target_agent_id}
|
|
225
|
+
_set_if_not_none(props, "reason", reason)
|
|
226
|
+
_set_if_not_none(props, "context", context)
|
|
227
|
+
|
|
228
|
+
props.update(extra)
|
|
229
|
+
event["properties"] = props
|
|
230
|
+
return event
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def retrieval_query(
|
|
234
|
+
agent_id: str,
|
|
235
|
+
*,
|
|
236
|
+
source: str,
|
|
237
|
+
trace_id: str | None = None,
|
|
238
|
+
span_id: str | None = None,
|
|
239
|
+
parent_span_id: str | None = None,
|
|
240
|
+
task_run_id: str | None = None,
|
|
241
|
+
user_id: str | None = None,
|
|
242
|
+
session_id: str | None = None,
|
|
243
|
+
environment: str | None = None,
|
|
244
|
+
query: str | None = None,
|
|
245
|
+
top_k: int | None = None,
|
|
246
|
+
results_count: int | None = None,
|
|
247
|
+
documents: list[dict[str, Any]] | None = None,
|
|
248
|
+
duration_ms: float | None = None,
|
|
249
|
+
**extra: Any,
|
|
250
|
+
) -> dict[str, Any]:
|
|
251
|
+
"""Build an ``agent.retrieval_query`` event."""
|
|
252
|
+
event = _base_envelope(
|
|
253
|
+
EventType.RETRIEVAL_QUERY,
|
|
254
|
+
agent_id,
|
|
255
|
+
trace_id=trace_id,
|
|
256
|
+
span_id=span_id,
|
|
257
|
+
parent_span_id=parent_span_id,
|
|
258
|
+
task_run_id=task_run_id,
|
|
259
|
+
user_id=user_id,
|
|
260
|
+
session_id=session_id,
|
|
261
|
+
environment=environment,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
props: dict[str, Any] = {"retrieval.source": source}
|
|
265
|
+
_set_if_not_none(props, "retrieval.query", query)
|
|
266
|
+
_set_if_not_none(props, "retrieval.top_k", top_k)
|
|
267
|
+
_set_if_not_none(props, "retrieval.results_count", results_count)
|
|
268
|
+
_set_if_not_none(props, "retrieval.documents", documents)
|
|
269
|
+
_set_if_not_none(props, "duration_ms", duration_ms)
|
|
270
|
+
|
|
271
|
+
props.update(extra)
|
|
272
|
+
event["properties"] = props
|
|
273
|
+
return event
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def eval_result(
|
|
277
|
+
agent_id: str,
|
|
278
|
+
*,
|
|
279
|
+
eval_name: str,
|
|
280
|
+
score: float,
|
|
281
|
+
trace_id: str | None = None,
|
|
282
|
+
span_id: str | None = None,
|
|
283
|
+
parent_span_id: str | None = None,
|
|
284
|
+
task_run_id: str | None = None,
|
|
285
|
+
user_id: str | None = None,
|
|
286
|
+
session_id: str | None = None,
|
|
287
|
+
environment: str | None = None,
|
|
288
|
+
label: str | None = None,
|
|
289
|
+
reason: str | None = None,
|
|
290
|
+
evaluator: str | None = None,
|
|
291
|
+
ref_span_id: str | None = None,
|
|
292
|
+
ref_task_run_id: str | None = None,
|
|
293
|
+
**extra: Any,
|
|
294
|
+
) -> dict[str, Any]:
|
|
295
|
+
"""Build an ``agent.eval`` event."""
|
|
296
|
+
event = _base_envelope(
|
|
297
|
+
EventType.EVAL,
|
|
298
|
+
agent_id,
|
|
299
|
+
trace_id=trace_id,
|
|
300
|
+
span_id=span_id,
|
|
301
|
+
parent_span_id=parent_span_id,
|
|
302
|
+
task_run_id=task_run_id,
|
|
303
|
+
user_id=user_id,
|
|
304
|
+
session_id=session_id,
|
|
305
|
+
environment=environment,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
props: dict[str, Any] = {
|
|
309
|
+
"eval.name": eval_name,
|
|
310
|
+
"eval.score": score,
|
|
311
|
+
}
|
|
312
|
+
_set_if_not_none(props, "eval.label", label)
|
|
313
|
+
_set_if_not_none(props, "eval.reason", reason)
|
|
314
|
+
_set_if_not_none(props, "eval.evaluator", evaluator)
|
|
315
|
+
_set_if_not_none(props, "eval.ref_span_id", ref_span_id)
|
|
316
|
+
_set_if_not_none(props, "eval.ref_task_run_id", ref_task_run_id)
|
|
317
|
+
|
|
318
|
+
props.update(extra)
|
|
319
|
+
event["properties"] = props
|
|
320
|
+
return event
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def security_alert(
|
|
324
|
+
agent_id: str,
|
|
325
|
+
*,
|
|
326
|
+
alert_type: str,
|
|
327
|
+
severity: str,
|
|
328
|
+
trace_id: str | None = None,
|
|
329
|
+
span_id: str | None = None,
|
|
330
|
+
parent_span_id: str | None = None,
|
|
331
|
+
task_run_id: str | None = None,
|
|
332
|
+
user_id: str | None = None,
|
|
333
|
+
session_id: str | None = None,
|
|
334
|
+
environment: str | None = None,
|
|
335
|
+
description: str | None = None,
|
|
336
|
+
scanner: str | None = None,
|
|
337
|
+
ref_span_id: str | None = None,
|
|
338
|
+
action_taken: str | None = None,
|
|
339
|
+
details: dict[str, Any] | None = None,
|
|
340
|
+
**extra: Any,
|
|
341
|
+
) -> dict[str, Any]:
|
|
342
|
+
"""Build an ``agent.security_alert`` event."""
|
|
343
|
+
event = _base_envelope(
|
|
344
|
+
EventType.SECURITY_ALERT,
|
|
345
|
+
agent_id,
|
|
346
|
+
trace_id=trace_id,
|
|
347
|
+
span_id=span_id,
|
|
348
|
+
parent_span_id=parent_span_id,
|
|
349
|
+
task_run_id=task_run_id,
|
|
350
|
+
user_id=user_id,
|
|
351
|
+
session_id=session_id,
|
|
352
|
+
environment=environment,
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
props: dict[str, Any] = {
|
|
356
|
+
"security.alert_type": alert_type,
|
|
357
|
+
"security.severity": severity,
|
|
358
|
+
}
|
|
359
|
+
_set_if_not_none(props, "security.description", description)
|
|
360
|
+
_set_if_not_none(props, "security.scanner", scanner)
|
|
361
|
+
_set_if_not_none(props, "security.ref_span_id", ref_span_id)
|
|
362
|
+
_set_if_not_none(props, "security.action_taken", action_taken)
|
|
363
|
+
_set_if_not_none(props, "security.details", details)
|
|
364
|
+
|
|
365
|
+
props.update(extra)
|
|
366
|
+
event["properties"] = props
|
|
367
|
+
return event
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def flag_check(
|
|
371
|
+
agent_id: str,
|
|
372
|
+
*,
|
|
373
|
+
flag_key: str,
|
|
374
|
+
flag_value: Any,
|
|
375
|
+
trace_id: str | None = None,
|
|
376
|
+
span_id: str | None = None,
|
|
377
|
+
parent_span_id: str | None = None,
|
|
378
|
+
task_run_id: str | None = None,
|
|
379
|
+
user_id: str | None = None,
|
|
380
|
+
session_id: str | None = None,
|
|
381
|
+
environment: str | None = None,
|
|
382
|
+
variant: str | None = None,
|
|
383
|
+
experiment_id: str | None = None,
|
|
384
|
+
reason: str | None = None,
|
|
385
|
+
**extra: Any,
|
|
386
|
+
) -> dict[str, Any]:
|
|
387
|
+
"""Build an ``agent.flag_check`` event."""
|
|
388
|
+
event = _base_envelope(
|
|
389
|
+
EventType.FLAG_CHECK,
|
|
390
|
+
agent_id,
|
|
391
|
+
trace_id=trace_id,
|
|
392
|
+
span_id=span_id,
|
|
393
|
+
parent_span_id=parent_span_id,
|
|
394
|
+
task_run_id=task_run_id,
|
|
395
|
+
user_id=user_id,
|
|
396
|
+
session_id=session_id,
|
|
397
|
+
environment=environment,
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
props: dict[str, Any] = {
|
|
401
|
+
"flag.key": flag_key,
|
|
402
|
+
"flag.value": flag_value,
|
|
403
|
+
}
|
|
404
|
+
_set_if_not_none(props, "flag.variant", variant)
|
|
405
|
+
_set_if_not_none(props, "flag.experiment_id", experiment_id)
|
|
406
|
+
_set_if_not_none(props, "flag.reason", reason)
|
|
407
|
+
|
|
408
|
+
props.update(extra)
|
|
409
|
+
event["properties"] = props
|
|
410
|
+
return event
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def business_event(
|
|
414
|
+
agent_id: str,
|
|
415
|
+
*,
|
|
416
|
+
event_name: str,
|
|
417
|
+
trace_id: str | None = None,
|
|
418
|
+
span_id: str | None = None,
|
|
419
|
+
parent_span_id: str | None = None,
|
|
420
|
+
task_run_id: str | None = None,
|
|
421
|
+
user_id: str | None = None,
|
|
422
|
+
session_id: str | None = None,
|
|
423
|
+
environment: str | None = None,
|
|
424
|
+
revenue: float | None = None,
|
|
425
|
+
currency: str | None = None,
|
|
426
|
+
metadata: dict[str, Any] | None = None,
|
|
427
|
+
**extra: Any,
|
|
428
|
+
) -> dict[str, Any]:
|
|
429
|
+
"""Build an ``agent.business_event`` event."""
|
|
430
|
+
event = _base_envelope(
|
|
431
|
+
EventType.BUSINESS_EVENT,
|
|
432
|
+
agent_id,
|
|
433
|
+
trace_id=trace_id,
|
|
434
|
+
span_id=span_id,
|
|
435
|
+
parent_span_id=parent_span_id,
|
|
436
|
+
task_run_id=task_run_id,
|
|
437
|
+
user_id=user_id,
|
|
438
|
+
session_id=session_id,
|
|
439
|
+
environment=environment,
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
props: dict[str, Any] = {"business.event_name": event_name}
|
|
443
|
+
_set_if_not_none(props, "business.revenue", revenue)
|
|
444
|
+
_set_if_not_none(props, "business.currency", currency)
|
|
445
|
+
_set_if_not_none(props, "business.metadata", metadata)
|
|
446
|
+
|
|
447
|
+
props.update(extra)
|
|
448
|
+
event["properties"] = props
|
|
449
|
+
return event
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Framework integrations for automatic LLM call tracing.
|
|
2
|
+
|
|
3
|
+
Usage::
|
|
4
|
+
|
|
5
|
+
from agentos import wrap_openai
|
|
6
|
+
import openai
|
|
7
|
+
|
|
8
|
+
client = openai.OpenAI()
|
|
9
|
+
client = wrap_openai(client, api_key="aos_...", agent_id="my-agent")
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from agentos.integrations.anthropic import wrap_anthropic
|
|
13
|
+
from agentos.integrations.openai import wrap_openai
|
|
14
|
+
|
|
15
|
+
__all__ = ["wrap_openai", "wrap_anthropic"]
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Anthropic SDK integration — wraps client to auto-capture LLM calls.
|
|
2
|
+
|
|
3
|
+
Usage::
|
|
4
|
+
|
|
5
|
+
from agentos.integrations.anthropic import wrap_anthropic
|
|
6
|
+
import anthropic
|
|
7
|
+
|
|
8
|
+
client = anthropic.Anthropic()
|
|
9
|
+
client = wrap_anthropic(client, api_key="aos_...", agent_id="my-agent")
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
import time
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger("agentos.integrations.anthropic")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def wrap_anthropic(
|
|
22
|
+
anthropic_client: Any,
|
|
23
|
+
*,
|
|
24
|
+
api_key: str | None = None,
|
|
25
|
+
agent_id: str = "default",
|
|
26
|
+
base_url: str = "https://api.agentos.dev",
|
|
27
|
+
capture_content: bool = True,
|
|
28
|
+
**kwargs: Any,
|
|
29
|
+
) -> Any:
|
|
30
|
+
"""Wrap an Anthropic client to auto-capture LLM calls.
|
|
31
|
+
|
|
32
|
+
Returns the same client with patched ``messages.create`` method.
|
|
33
|
+
"""
|
|
34
|
+
from agentos.client import AgentOS, get_client
|
|
35
|
+
|
|
36
|
+
aos_client = None
|
|
37
|
+
if api_key:
|
|
38
|
+
aos_client = AgentOS(api_key=api_key, base_url=base_url, flush_interval=0, **kwargs)
|
|
39
|
+
else:
|
|
40
|
+
aos_client = get_client()
|
|
41
|
+
|
|
42
|
+
if aos_client is None:
|
|
43
|
+
logger.warning("No AgentOS client configured — wrap_anthropic is a no-op")
|
|
44
|
+
return anthropic_client
|
|
45
|
+
|
|
46
|
+
original_create = anthropic_client.messages.create
|
|
47
|
+
|
|
48
|
+
def patched_create(*args: Any, **call_kwargs: Any) -> Any:
|
|
49
|
+
model_arg = call_kwargs.get("model", "unknown")
|
|
50
|
+
messages = call_kwargs.get("messages")
|
|
51
|
+
stream = call_kwargs.get("stream", False)
|
|
52
|
+
|
|
53
|
+
start = time.monotonic()
|
|
54
|
+
try:
|
|
55
|
+
response = original_create(*args, **call_kwargs)
|
|
56
|
+
|
|
57
|
+
if stream:
|
|
58
|
+
return response
|
|
59
|
+
|
|
60
|
+
duration_ms = (time.monotonic() - start) * 1000
|
|
61
|
+
|
|
62
|
+
data: dict[str, Any] = {
|
|
63
|
+
"model": getattr(response, "model", model_arg),
|
|
64
|
+
"system": "anthropic",
|
|
65
|
+
"duration_ms": duration_ms,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Usage
|
|
69
|
+
usage = getattr(response, "usage", None)
|
|
70
|
+
if usage:
|
|
71
|
+
data["input_tokens"] = getattr(usage, "input_tokens", None)
|
|
72
|
+
data["output_tokens"] = getattr(usage, "output_tokens", None)
|
|
73
|
+
|
|
74
|
+
# Finish reason
|
|
75
|
+
stop_reason = getattr(response, "stop_reason", None)
|
|
76
|
+
if stop_reason:
|
|
77
|
+
reason_map = {"end_turn": "stop", "max_tokens": "length", "tool_use": "tool_calls"}
|
|
78
|
+
data["finish_reason"] = reason_map.get(stop_reason, stop_reason)
|
|
79
|
+
|
|
80
|
+
# Response ID
|
|
81
|
+
resp_id = getattr(response, "id", None)
|
|
82
|
+
if resp_id:
|
|
83
|
+
data["response_id"] = resp_id
|
|
84
|
+
|
|
85
|
+
# Content
|
|
86
|
+
if capture_content:
|
|
87
|
+
if messages:
|
|
88
|
+
data["input"] = [
|
|
89
|
+
{"role": m.get("role", ""), "content": m.get("content")}
|
|
90
|
+
for m in messages
|
|
91
|
+
if isinstance(m, dict)
|
|
92
|
+
]
|
|
93
|
+
content_blocks = getattr(response, "content", [])
|
|
94
|
+
if content_blocks:
|
|
95
|
+
text_parts = []
|
|
96
|
+
for block in content_blocks:
|
|
97
|
+
if getattr(block, "type", None) == "text":
|
|
98
|
+
text_parts.append(getattr(block, "text", ""))
|
|
99
|
+
if text_parts:
|
|
100
|
+
data["output"] = [{"role": "assistant", "content": "\n".join(text_parts)}]
|
|
101
|
+
|
|
102
|
+
# Temperature from call args
|
|
103
|
+
if "temperature" in call_kwargs:
|
|
104
|
+
data["temperature"] = call_kwargs["temperature"]
|
|
105
|
+
if "max_tokens" in call_kwargs:
|
|
106
|
+
data["max_tokens"] = call_kwargs["max_tokens"]
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
aos_client.llm_call(agent_id, **data)
|
|
110
|
+
except Exception:
|
|
111
|
+
logger.exception("Failed to capture Anthropic call")
|
|
112
|
+
|
|
113
|
+
return response
|
|
114
|
+
|
|
115
|
+
except Exception as exc:
|
|
116
|
+
duration_ms = (time.monotonic() - start) * 1000
|
|
117
|
+
try:
|
|
118
|
+
aos_client.llm_call(
|
|
119
|
+
agent_id,
|
|
120
|
+
model=model_arg,
|
|
121
|
+
system="anthropic",
|
|
122
|
+
duration_ms=duration_ms,
|
|
123
|
+
error={"type": type(exc).__name__, "message": str(exc)},
|
|
124
|
+
)
|
|
125
|
+
except Exception:
|
|
126
|
+
logger.exception("Failed to capture Anthropic error")
|
|
127
|
+
raise
|
|
128
|
+
|
|
129
|
+
anthropic_client.messages.create = patched_create
|
|
130
|
+
return anthropic_client
|