usegradient 1.3.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.
- gradient_sdk/__init__.py +122 -0
- gradient_sdk/agent.py +368 -0
- gradient_sdk/client.py +1059 -0
- gradient_sdk/context.py +91 -0
- gradient_sdk/credentials.py +92 -0
- gradient_sdk/decorators.py +202 -0
- gradient_sdk/errors.py +30 -0
- gradient_sdk/graph.py +190 -0
- gradient_sdk/integrations/__init__.py +7 -0
- gradient_sdk/integrations/agent.py +263 -0
- gradient_sdk/integrations/anthropic.py +51 -0
- gradient_sdk/integrations/openai.py +105 -0
- gradient_sdk/model_catalog.py +75 -0
- gradient_sdk/models.py +370 -0
- gradient_sdk/py.typed +1 -0
- gradient_sdk/runtime.py +1474 -0
- gradient_sdk/session.py +132 -0
- gradient_sdk/tools.py +156 -0
- gradient_sdk/tracing.py +361 -0
- usegradient-1.3.1.dist-info/METADATA +235 -0
- usegradient-1.3.1.dist-info/RECORD +22 -0
- usegradient-1.3.1.dist-info/WHEEL +4 -0
gradient_sdk/__init__.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from .client import GradientClient, default_machine_template
|
|
2
|
+
from .agent import Agent, AgentResult
|
|
3
|
+
from .context import RunContext, current_context
|
|
4
|
+
from .credentials import (
|
|
5
|
+
DEFAULT_API_URL,
|
|
6
|
+
DEFAULT_PROXY_URL,
|
|
7
|
+
DEFAULT_REGISTRY_URL,
|
|
8
|
+
Credentials,
|
|
9
|
+
load_credentials,
|
|
10
|
+
save_credentials,
|
|
11
|
+
)
|
|
12
|
+
from .decorators import agent, chain, embedding, llm, retriever, task, tool, trace, trace_context, workflow
|
|
13
|
+
from .errors import GradientAPIError, GradientAuthError, GradientError
|
|
14
|
+
from .graph import END, START, StateGraph
|
|
15
|
+
from .integrations import generate, run_agent_loop, wrap_anthropic, wrap_openai
|
|
16
|
+
from .models import (
|
|
17
|
+
AppSecret,
|
|
18
|
+
CreateTraceRunResponse,
|
|
19
|
+
CreateMachineResponse,
|
|
20
|
+
ExecMachineResponse,
|
|
21
|
+
Guest,
|
|
22
|
+
MachineCreateRequest,
|
|
23
|
+
MachineListItem,
|
|
24
|
+
MachineSpec,
|
|
25
|
+
PortEntry,
|
|
26
|
+
ReplayTraceRunResponse,
|
|
27
|
+
Restart,
|
|
28
|
+
ServiceConfig,
|
|
29
|
+
TraceEvent,
|
|
30
|
+
TraceRun,
|
|
31
|
+
TraceSpan,
|
|
32
|
+
WhoamiResponse,
|
|
33
|
+
)
|
|
34
|
+
from .model_catalog import Model, ModelSpec, supported_models
|
|
35
|
+
from .runtime import (
|
|
36
|
+
BuildHandle,
|
|
37
|
+
DeploymentHandle,
|
|
38
|
+
Environment,
|
|
39
|
+
Gradient,
|
|
40
|
+
ProgressEvent,
|
|
41
|
+
RemoteDeployment,
|
|
42
|
+
RemoteRunResult,
|
|
43
|
+
RunHandle,
|
|
44
|
+
Trace,
|
|
45
|
+
TracePolicy,
|
|
46
|
+
)
|
|
47
|
+
from .session import GradientSession, current_session, init
|
|
48
|
+
from .tools import Tool, execute_tool_calls, infer_parameters, tool_from_function
|
|
49
|
+
from .tracing import current_span, get_tracer
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
"AppSecret",
|
|
53
|
+
"Agent",
|
|
54
|
+
"AgentResult",
|
|
55
|
+
"BuildHandle",
|
|
56
|
+
"CreateMachineResponse",
|
|
57
|
+
"CreateTraceRunResponse",
|
|
58
|
+
"Credentials",
|
|
59
|
+
"DEFAULT_API_URL",
|
|
60
|
+
"DEFAULT_PROXY_URL",
|
|
61
|
+
"DEFAULT_REGISTRY_URL",
|
|
62
|
+
"DeploymentHandle",
|
|
63
|
+
"END",
|
|
64
|
+
"GradientAPIError",
|
|
65
|
+
"GradientAuthError",
|
|
66
|
+
"Gradient",
|
|
67
|
+
"GradientClient",
|
|
68
|
+
"GradientError",
|
|
69
|
+
"GradientSession",
|
|
70
|
+
"Environment",
|
|
71
|
+
"ExecMachineResponse",
|
|
72
|
+
"Guest",
|
|
73
|
+
"MachineCreateRequest",
|
|
74
|
+
"MachineListItem",
|
|
75
|
+
"MachineSpec",
|
|
76
|
+
"Model",
|
|
77
|
+
"ModelSpec",
|
|
78
|
+
"PortEntry",
|
|
79
|
+
"ProgressEvent",
|
|
80
|
+
"ReplayTraceRunResponse",
|
|
81
|
+
"RemoteDeployment",
|
|
82
|
+
"RemoteRunResult",
|
|
83
|
+
"Restart",
|
|
84
|
+
"START",
|
|
85
|
+
"ServiceConfig",
|
|
86
|
+
"StateGraph",
|
|
87
|
+
"supported_models",
|
|
88
|
+
"Tool",
|
|
89
|
+
"Trace",
|
|
90
|
+
"TraceEvent",
|
|
91
|
+
"TracePolicy",
|
|
92
|
+
"TraceRun",
|
|
93
|
+
"TraceSpan",
|
|
94
|
+
"WhoamiResponse",
|
|
95
|
+
"agent",
|
|
96
|
+
"chain",
|
|
97
|
+
"current_context",
|
|
98
|
+
"current_session",
|
|
99
|
+
"current_span",
|
|
100
|
+
"default_machine_template",
|
|
101
|
+
"embedding",
|
|
102
|
+
"execute_tool_calls",
|
|
103
|
+
"generate",
|
|
104
|
+
"get_tracer",
|
|
105
|
+
"infer_parameters",
|
|
106
|
+
"init",
|
|
107
|
+
"llm",
|
|
108
|
+
"load_credentials",
|
|
109
|
+
"retriever",
|
|
110
|
+
"run_agent_loop",
|
|
111
|
+
"RunHandle",
|
|
112
|
+
"RunContext",
|
|
113
|
+
"save_credentials",
|
|
114
|
+
"task",
|
|
115
|
+
"tool",
|
|
116
|
+
"tool_from_function",
|
|
117
|
+
"trace",
|
|
118
|
+
"trace_context",
|
|
119
|
+
"workflow",
|
|
120
|
+
"wrap_anthropic",
|
|
121
|
+
"wrap_openai",
|
|
122
|
+
]
|
gradient_sdk/agent.py
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
import json
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Any, Callable, Iterable, Iterator, Mapping
|
|
7
|
+
|
|
8
|
+
from .context import RunContext, use_context
|
|
9
|
+
from .integrations.agent import generate
|
|
10
|
+
from .model_catalog import Model, resolve_model
|
|
11
|
+
from .tools import Tool, execute_tool_calls, tool_from_function
|
|
12
|
+
from .tracing import get_tracer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(slots=True)
|
|
16
|
+
class AgentResult:
|
|
17
|
+
output: Any
|
|
18
|
+
text: str
|
|
19
|
+
messages: list[dict[str, Any]]
|
|
20
|
+
tool_results: list[dict[str, Any]]
|
|
21
|
+
usage: dict[str, int]
|
|
22
|
+
events: list[dict[str, Any]]
|
|
23
|
+
context: RunContext
|
|
24
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
25
|
+
|
|
26
|
+
def __str__(self) -> str:
|
|
27
|
+
return self.text
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Agent:
|
|
31
|
+
__gradient_agent__ = True
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
model: str | Callable[..., Any] | Any,
|
|
36
|
+
*,
|
|
37
|
+
tools: Iterable[Tool | Callable[..., Any]] | None = None,
|
|
38
|
+
instructions: str | None = None,
|
|
39
|
+
memory: Any | None = None,
|
|
40
|
+
middleware: Iterable[Callable[..., Any]] | None = None,
|
|
41
|
+
trace: bool = True,
|
|
42
|
+
gradient: Any | None = None,
|
|
43
|
+
name: str | None = None,
|
|
44
|
+
max_turns: int = 8,
|
|
45
|
+
retries: int = 0,
|
|
46
|
+
fallback_models: Iterable[str | Callable[..., Any] | Any] | None = None,
|
|
47
|
+
structured_output: Callable[[Any], Any] | None = None,
|
|
48
|
+
entrypoint: str | None = None,
|
|
49
|
+
client: Any | None = None,
|
|
50
|
+
) -> None:
|
|
51
|
+
self.model = model
|
|
52
|
+
self.tools = [_coerce_tool(item) for item in tools or []]
|
|
53
|
+
self.instructions = instructions
|
|
54
|
+
self.memory = memory
|
|
55
|
+
self.middleware = list(middleware or [])
|
|
56
|
+
self.trace = trace
|
|
57
|
+
self.gradient = gradient
|
|
58
|
+
self.name = name or _model_name(model)
|
|
59
|
+
self.max_turns = max_turns
|
|
60
|
+
self.retries = retries
|
|
61
|
+
self.fallback_models = list(fallback_models or [])
|
|
62
|
+
self.structured_output = structured_output
|
|
63
|
+
self.entrypoint = entrypoint
|
|
64
|
+
self.client = client
|
|
65
|
+
|
|
66
|
+
def __call__(self, input: Any = None) -> Any:
|
|
67
|
+
return self.run(input).output
|
|
68
|
+
|
|
69
|
+
def bind(self, gradient: Any) -> "Agent":
|
|
70
|
+
self.gradient = gradient
|
|
71
|
+
return self
|
|
72
|
+
|
|
73
|
+
def run(
|
|
74
|
+
self,
|
|
75
|
+
input: Any = None,
|
|
76
|
+
*,
|
|
77
|
+
context: RunContext | Mapping[str, Any] | None = None,
|
|
78
|
+
**metadata: Any,
|
|
79
|
+
) -> AgentResult:
|
|
80
|
+
ctx = RunContext.from_value(
|
|
81
|
+
context,
|
|
82
|
+
metadata={**dict(getattr(context, "metadata", {}) or {}), **metadata}
|
|
83
|
+
if context is not None
|
|
84
|
+
else metadata,
|
|
85
|
+
)
|
|
86
|
+
events: list[dict[str, Any]] = []
|
|
87
|
+
with use_context(ctx):
|
|
88
|
+
with _maybe_agent_span(self.name, self.trace, input) as span:
|
|
89
|
+
result = self._run_loop(input, ctx, events)
|
|
90
|
+
if span is not None:
|
|
91
|
+
span.set_output(result.output)
|
|
92
|
+
self._save_memory(result)
|
|
93
|
+
return result
|
|
94
|
+
|
|
95
|
+
def stream(
|
|
96
|
+
self,
|
|
97
|
+
input: Any = None,
|
|
98
|
+
*,
|
|
99
|
+
context: RunContext | Mapping[str, Any] | None = None,
|
|
100
|
+
**metadata: Any,
|
|
101
|
+
) -> Iterator[dict[str, Any]]:
|
|
102
|
+
result = self.run(input, context=context, **metadata)
|
|
103
|
+
yield from result.events
|
|
104
|
+
|
|
105
|
+
def deploy(self, name: str | None = None, **kwargs: Any) -> Any:
|
|
106
|
+
if self.gradient is None:
|
|
107
|
+
raise RuntimeError("Agent.deploy requires an Agent(..., gradient=gradient) or agent.bind(gradient)")
|
|
108
|
+
return self.gradient.deploy(self, name=name, **kwargs)
|
|
109
|
+
|
|
110
|
+
def evaluate(
|
|
111
|
+
self,
|
|
112
|
+
dataset: Iterable[Mapping[str, Any]] | Any,
|
|
113
|
+
evaluators: Mapping[str, Callable[[Mapping[str, Any]], Any]] | None = None,
|
|
114
|
+
*,
|
|
115
|
+
input_key: str = "input",
|
|
116
|
+
expected_key: str = "expected_output",
|
|
117
|
+
) -> dict[str, Any]:
|
|
118
|
+
rows = list(dataset)
|
|
119
|
+
results: list[dict[str, Any]] = []
|
|
120
|
+
for index, example in enumerate(rows):
|
|
121
|
+
payload = example.get(input_key) if isinstance(example, Mapping) else example
|
|
122
|
+
expected = example.get(expected_key) if isinstance(example, Mapping) else None
|
|
123
|
+
started = self.run(payload)
|
|
124
|
+
scores = {
|
|
125
|
+
name: evaluator(
|
|
126
|
+
{
|
|
127
|
+
"input": payload,
|
|
128
|
+
"output": started.output,
|
|
129
|
+
"text": started.text,
|
|
130
|
+
"expected_output": expected,
|
|
131
|
+
"example": example,
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
for name, evaluator in (evaluators or {}).items()
|
|
135
|
+
}
|
|
136
|
+
results.append(
|
|
137
|
+
{
|
|
138
|
+
"index": index,
|
|
139
|
+
"input": payload,
|
|
140
|
+
"output": started.output,
|
|
141
|
+
"text": started.text,
|
|
142
|
+
"expected_output": expected,
|
|
143
|
+
"scores": scores,
|
|
144
|
+
"usage": started.usage,
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
return {"count": len(results), "results": results}
|
|
148
|
+
|
|
149
|
+
def _run_loop(
|
|
150
|
+
self,
|
|
151
|
+
input: Any,
|
|
152
|
+
context: RunContext,
|
|
153
|
+
events: list[dict[str, Any]],
|
|
154
|
+
) -> AgentResult:
|
|
155
|
+
messages = self._initial_messages(input)
|
|
156
|
+
usage = {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0}
|
|
157
|
+
tool_results: list[dict[str, Any]] = []
|
|
158
|
+
final_text = ""
|
|
159
|
+
output: Any = ""
|
|
160
|
+
|
|
161
|
+
events.append(_event("context", {"context": context.to_event()}))
|
|
162
|
+
events.append(_event("start", {"input": input, "agent": self.name}))
|
|
163
|
+
|
|
164
|
+
for turn in range(max(1, self.max_turns)):
|
|
165
|
+
context.check_cancelled()
|
|
166
|
+
response = self._call_model(messages, context)
|
|
167
|
+
_merge_usage(usage, response.get("usage", {}))
|
|
168
|
+
final_text = str(response.get("text") or response.get("output") or "")
|
|
169
|
+
output = response.get("output", final_text)
|
|
170
|
+
events.append(
|
|
171
|
+
_event(
|
|
172
|
+
"message",
|
|
173
|
+
{"role": "assistant", "content": final_text, "turn": turn + 1},
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
tool_calls = list(response.get("tool_calls") or [])
|
|
177
|
+
if not tool_calls:
|
|
178
|
+
break
|
|
179
|
+
messages.append({"role": "assistant", "content": final_text, "tool_calls": tool_calls})
|
|
180
|
+
events.append(_event("tool_calls", {"calls": tool_calls, "turn": turn + 1}))
|
|
181
|
+
executed = execute_tool_calls(self.tools, tool_calls)
|
|
182
|
+
tool_results.extend(executed)
|
|
183
|
+
events.append(_event("tool_results", {"results": executed, "turn": turn + 1}))
|
|
184
|
+
for item in executed:
|
|
185
|
+
messages.append(
|
|
186
|
+
{
|
|
187
|
+
"role": "tool",
|
|
188
|
+
"tool_call_id": item.get("tool_call_id"),
|
|
189
|
+
"name": item.get("name"),
|
|
190
|
+
"content": json.dumps(item.get("output", item.get("error")), default=str),
|
|
191
|
+
}
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
if self.structured_output is not None:
|
|
195
|
+
output = self.structured_output(output)
|
|
196
|
+
events.append(_event("final", {"output": output, "text": final_text, "usage": usage}))
|
|
197
|
+
return AgentResult(
|
|
198
|
+
output=output,
|
|
199
|
+
text=final_text,
|
|
200
|
+
messages=messages,
|
|
201
|
+
tool_results=tool_results,
|
|
202
|
+
usage=usage,
|
|
203
|
+
events=events,
|
|
204
|
+
context=context,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
def _initial_messages(self, input: Any) -> list[dict[str, Any]]:
|
|
208
|
+
messages: list[dict[str, Any]] = []
|
|
209
|
+
if self.instructions:
|
|
210
|
+
messages.append({"role": "system", "content": self.instructions})
|
|
211
|
+
loaded_memory = _load_memory(self.memory)
|
|
212
|
+
messages.extend(loaded_memory)
|
|
213
|
+
if isinstance(input, Mapping) and isinstance(input.get("messages"), list):
|
|
214
|
+
messages.extend(dict(item) for item in input["messages"])
|
|
215
|
+
elif isinstance(input, list) and all(isinstance(item, Mapping) for item in input):
|
|
216
|
+
messages.extend(dict(item) for item in input)
|
|
217
|
+
else:
|
|
218
|
+
messages.append({"role": "user", "content": input if isinstance(input, str) else json.dumps(input, default=str)})
|
|
219
|
+
return messages
|
|
220
|
+
|
|
221
|
+
def _call_model(self, messages: list[dict[str, Any]], context: RunContext) -> dict[str, Any]:
|
|
222
|
+
def invoke() -> dict[str, Any]:
|
|
223
|
+
return self._call_model_without_middleware(messages, context)
|
|
224
|
+
|
|
225
|
+
call_next = invoke
|
|
226
|
+
for middleware in reversed(self.middleware):
|
|
227
|
+
previous = call_next
|
|
228
|
+
call_next = lambda middleware=middleware, previous=previous: middleware(context, previous)
|
|
229
|
+
return call_next()
|
|
230
|
+
|
|
231
|
+
def _call_model_without_middleware(
|
|
232
|
+
self,
|
|
233
|
+
messages: list[dict[str, Any]],
|
|
234
|
+
context: RunContext,
|
|
235
|
+
) -> dict[str, Any]:
|
|
236
|
+
models = [self.model, *self.fallback_models]
|
|
237
|
+
last_error: BaseException | None = None
|
|
238
|
+
for model in models:
|
|
239
|
+
for _ in range(self.retries + 1):
|
|
240
|
+
try:
|
|
241
|
+
return _normalize_model_result(
|
|
242
|
+
_invoke_model(
|
|
243
|
+
model,
|
|
244
|
+
messages=messages,
|
|
245
|
+
tools=self.tools,
|
|
246
|
+
context=context,
|
|
247
|
+
client=self.client,
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
except Exception as exc:
|
|
251
|
+
last_error = exc
|
|
252
|
+
if last_error is not None:
|
|
253
|
+
raise last_error
|
|
254
|
+
return {"text": "", "tool_calls": [], "usage": {}}
|
|
255
|
+
|
|
256
|
+
def _save_memory(self, result: AgentResult) -> None:
|
|
257
|
+
if self.memory is None:
|
|
258
|
+
return
|
|
259
|
+
if hasattr(self.memory, "save"):
|
|
260
|
+
self.memory.save(result.messages)
|
|
261
|
+
elif isinstance(self.memory, list):
|
|
262
|
+
self.memory[:] = result.messages
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _coerce_tool(value: Tool | Callable[..., Any]) -> Tool:
|
|
266
|
+
if isinstance(value, Tool):
|
|
267
|
+
return value
|
|
268
|
+
tool_obj = getattr(value, "gradient_tool", None)
|
|
269
|
+
if isinstance(tool_obj, Tool):
|
|
270
|
+
return tool_obj
|
|
271
|
+
return tool_from_function(value)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _load_memory(memory: Any | None) -> list[dict[str, Any]]:
|
|
275
|
+
if memory is None:
|
|
276
|
+
return []
|
|
277
|
+
if hasattr(memory, "load"):
|
|
278
|
+
loaded = memory.load()
|
|
279
|
+
return list(loaded or [])
|
|
280
|
+
if isinstance(memory, list):
|
|
281
|
+
return [dict(item) for item in memory if isinstance(item, Mapping)]
|
|
282
|
+
return []
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _invoke_model(
|
|
286
|
+
model: Model | str | Callable[..., Any] | Any,
|
|
287
|
+
*,
|
|
288
|
+
messages: list[dict[str, Any]],
|
|
289
|
+
tools: list[Tool],
|
|
290
|
+
context: RunContext,
|
|
291
|
+
client: Any | None = None,
|
|
292
|
+
) -> Any:
|
|
293
|
+
spec = resolve_model(model)
|
|
294
|
+
if spec is not None:
|
|
295
|
+
return generate(
|
|
296
|
+
provider=spec.provider, # type: ignore[arg-type]
|
|
297
|
+
model=spec.model,
|
|
298
|
+
interface=spec.interface,
|
|
299
|
+
messages=messages,
|
|
300
|
+
tools=tools,
|
|
301
|
+
client=client,
|
|
302
|
+
)
|
|
303
|
+
if callable(model):
|
|
304
|
+
kwargs = {"messages": messages, "tools": tools, "context": context}
|
|
305
|
+
try:
|
|
306
|
+
signature = inspect.signature(model)
|
|
307
|
+
accepted = {key: value for key, value in kwargs.items() if key in signature.parameters}
|
|
308
|
+
if accepted:
|
|
309
|
+
return model(**accepted)
|
|
310
|
+
except (TypeError, ValueError):
|
|
311
|
+
pass
|
|
312
|
+
return model(messages)
|
|
313
|
+
if hasattr(model, "generate"):
|
|
314
|
+
return model.generate(messages=messages, tools=tools, context=context)
|
|
315
|
+
if hasattr(model, "invoke"):
|
|
316
|
+
return model.invoke(messages)
|
|
317
|
+
raise TypeError("Agent model must be a provider string, callable, or object with generate/invoke")
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _normalize_model_result(result: Any) -> dict[str, Any]:
|
|
321
|
+
if isinstance(result, str):
|
|
322
|
+
return {"text": result, "output": result, "tool_calls": [], "usage": {}}
|
|
323
|
+
if isinstance(result, Mapping):
|
|
324
|
+
text = result.get("text", result.get("content", result.get("output", "")))
|
|
325
|
+
return {
|
|
326
|
+
"text": str(text or ""),
|
|
327
|
+
"output": result.get("output", text),
|
|
328
|
+
"tool_calls": list(result.get("tool_calls") or result.get("toolCalls") or []),
|
|
329
|
+
"usage": dict(result.get("usage") or {}),
|
|
330
|
+
}
|
|
331
|
+
return {"text": str(result), "output": result, "tool_calls": [], "usage": {}}
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _merge_usage(target: dict[str, int], incoming: Mapping[str, Any]) -> None:
|
|
335
|
+
for key in ("prompt_tokens", "completion_tokens", "total_tokens"):
|
|
336
|
+
target[key] = int(target.get(key, 0)) + int(incoming.get(key, 0) or 0)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _event(event_type: str, data: Mapping[str, Any]) -> dict[str, Any]:
|
|
340
|
+
return {"type": event_type, "data": dict(data)}
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def _model_name(model: Any) -> str:
|
|
344
|
+
if isinstance(model, str):
|
|
345
|
+
return model
|
|
346
|
+
return getattr(model, "__name__", model.__class__.__name__)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class _maybe_agent_span:
|
|
350
|
+
def __init__(self, name: str, enabled: bool, input: Any) -> None:
|
|
351
|
+
self.name = name
|
|
352
|
+
self.enabled = enabled
|
|
353
|
+
self.input = input
|
|
354
|
+
self._span: Any = None
|
|
355
|
+
|
|
356
|
+
def __enter__(self) -> Any:
|
|
357
|
+
if not self.enabled:
|
|
358
|
+
return None
|
|
359
|
+
tracer = get_tracer()
|
|
360
|
+
self._span = tracer.start_span(f"agent.{self.name}", kind="AGENT")
|
|
361
|
+
entered = self._span.__enter__()
|
|
362
|
+
entered.set_attribute("input.value", json.dumps(self.input, default=str))
|
|
363
|
+
return entered
|
|
364
|
+
|
|
365
|
+
def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> bool:
|
|
366
|
+
if self._span is not None:
|
|
367
|
+
return bool(self._span.__exit__(exc_type, exc, tb))
|
|
368
|
+
return False
|