agenta 0.26.0a0__py3-none-any.whl → 0.27.0a0__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.
Potentially problematic release.
This version of agenta might be problematic. Click here for more details.
- agenta/__init__.py +6 -7
- agenta/client/backend/client.py +22 -14
- agenta/client/backend/core/http_client.py +23 -15
- agenta/sdk/__init__.py +27 -6
- agenta/sdk/agenta_init.py +73 -26
- agenta/sdk/config_manager.py +2 -2
- agenta/sdk/context/__init__.py +0 -0
- agenta/sdk/context/routing.py +25 -0
- agenta/sdk/context/tracing.py +3 -0
- agenta/sdk/decorators/__init__.py +0 -0
- agenta/sdk/decorators/{llm_entrypoint.py → routing.py} +137 -124
- agenta/sdk/decorators/tracing.py +228 -76
- agenta/sdk/litellm/__init__.py +1 -0
- agenta/sdk/litellm/litellm.py +277 -0
- agenta/sdk/router.py +0 -7
- agenta/sdk/tracing/__init__.py +1 -0
- agenta/sdk/tracing/attributes.py +181 -0
- agenta/sdk/tracing/context.py +21 -0
- agenta/sdk/tracing/conventions.py +43 -0
- agenta/sdk/tracing/exporters.py +53 -0
- agenta/sdk/tracing/inline.py +1306 -0
- agenta/sdk/tracing/processors.py +65 -0
- agenta/sdk/tracing/spans.py +124 -0
- agenta/sdk/tracing/tracing.py +174 -0
- agenta/sdk/types.py +0 -12
- agenta/sdk/utils/{helper/openai_cost.py → costs.py} +3 -0
- agenta/sdk/utils/debug.py +5 -5
- agenta/sdk/utils/exceptions.py +19 -0
- agenta/sdk/utils/globals.py +3 -5
- agenta/sdk/{tracing/logger.py → utils/logging.py} +3 -5
- agenta/sdk/utils/singleton.py +13 -0
- {agenta-0.26.0a0.dist-info → agenta-0.27.0a0.dist-info}/METADATA +5 -1
- {agenta-0.26.0a0.dist-info → agenta-0.27.0a0.dist-info}/RECORD +35 -25
- agenta/sdk/context.py +0 -41
- agenta/sdk/decorators/base.py +0 -10
- agenta/sdk/tracing/callbacks.py +0 -187
- agenta/sdk/tracing/llm_tracing.py +0 -617
- agenta/sdk/tracing/tasks_manager.py +0 -129
- agenta/sdk/tracing/tracing_context.py +0 -27
- {agenta-0.26.0a0.dist-info → agenta-0.27.0a0.dist-info}/WHEEL +0 -0
- {agenta-0.26.0a0.dist-info → agenta-0.27.0a0.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"""The code for the Agenta SDK"""
|
|
2
2
|
|
|
3
|
-
from agenta.sdk.utils.debug import debug, DEBUG, SHIFT
|
|
4
3
|
import os
|
|
5
4
|
import sys
|
|
6
5
|
import time
|
|
@@ -14,18 +13,17 @@ from pathlib import Path
|
|
|
14
13
|
from tempfile import NamedTemporaryFile
|
|
15
14
|
from typing import Any, Callable, Dict, Optional, Tuple, List
|
|
16
15
|
from importlib.metadata import version
|
|
17
|
-
|
|
18
16
|
from fastapi.middleware.cors import CORSMiddleware
|
|
19
17
|
from fastapi import Body, FastAPI, UploadFile, HTTPException
|
|
20
18
|
|
|
21
19
|
import agenta as ag
|
|
22
|
-
|
|
20
|
+
|
|
21
|
+
from agenta.sdk.context.routing import routing_context_manager, routing_context
|
|
22
|
+
from agenta.sdk.context.tracing import tracing_context
|
|
23
23
|
from agenta.sdk.router import router as router
|
|
24
|
-
from agenta.sdk.
|
|
25
|
-
from agenta.sdk.
|
|
26
|
-
from agenta.sdk.decorators.base import BaseDecorator
|
|
24
|
+
from agenta.sdk.utils.exceptions import suppress
|
|
25
|
+
from agenta.sdk.utils.logging import log
|
|
27
26
|
from agenta.sdk.types import (
|
|
28
|
-
Context,
|
|
29
27
|
DictInput,
|
|
30
28
|
FloatParam,
|
|
31
29
|
InFile,
|
|
@@ -67,36 +65,14 @@ app.add_middleware(
|
|
|
67
65
|
app.include_router(router, prefix="")
|
|
68
66
|
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
route_context = contextvars.ContextVar("route_context", default={})
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@contextmanager
|
|
76
|
-
def route_context_manager(
|
|
77
|
-
config: Optional[Dict[str, Any]] = None,
|
|
78
|
-
environment: Optional[str] = None,
|
|
79
|
-
version: Optional[str] = None,
|
|
80
|
-
variant: Optional[str] = None,
|
|
81
|
-
):
|
|
82
|
-
context = {
|
|
83
|
-
"config": config,
|
|
84
|
-
"environment": environment,
|
|
85
|
-
"version": version,
|
|
86
|
-
"variant": variant,
|
|
87
|
-
}
|
|
88
|
-
token = route_context.set(context)
|
|
89
|
-
try:
|
|
90
|
-
yield
|
|
91
|
-
finally:
|
|
92
|
-
route_context.reset(token)
|
|
68
|
+
log.setLevel("DEBUG")
|
|
93
69
|
|
|
94
70
|
|
|
95
71
|
class PathValidator(BaseModel):
|
|
96
72
|
url: HttpUrl
|
|
97
73
|
|
|
98
74
|
|
|
99
|
-
class route
|
|
75
|
+
class route:
|
|
100
76
|
# This decorator is used to expose specific stages of a workflow (embedding, retrieval, summarization, etc.)
|
|
101
77
|
# as independent endpoints. It is designed for backward compatibility with existing code that uses
|
|
102
78
|
# the @entrypoint decorator, which has certain limitations. By using @route(), we can create new
|
|
@@ -118,7 +94,7 @@ class route(BaseDecorator):
|
|
|
118
94
|
return f
|
|
119
95
|
|
|
120
96
|
|
|
121
|
-
class entrypoint
|
|
97
|
+
class entrypoint:
|
|
122
98
|
"""
|
|
123
99
|
Decorator class to wrap a function for HTTP POST, terminal exposure and enable tracing.
|
|
124
100
|
|
|
@@ -152,10 +128,11 @@ class entrypoint(BaseDecorator):
|
|
|
152
128
|
routes = list()
|
|
153
129
|
|
|
154
130
|
def __init__(
|
|
155
|
-
self,
|
|
131
|
+
self,
|
|
132
|
+
func: Callable[..., Any],
|
|
133
|
+
route_path="",
|
|
134
|
+
config_schema: Optional[BaseModel] = None,
|
|
156
135
|
):
|
|
157
|
-
logging.info(f"Using Agenta Python SDK version {version('agenta')}")
|
|
158
|
-
|
|
159
136
|
DEFAULT_PATH = "generate"
|
|
160
137
|
PLAYGROUND_PATH = "/playground"
|
|
161
138
|
RUN_PATH = "/run"
|
|
@@ -176,8 +153,9 @@ class entrypoint(BaseDecorator):
|
|
|
176
153
|
config_params = config.dict() if config else ag.config.all()
|
|
177
154
|
ingestible_files = self.extract_ingestible_files(func_signature)
|
|
178
155
|
|
|
156
|
+
self.route_path = route_path
|
|
157
|
+
|
|
179
158
|
### --- Playground --- #
|
|
180
|
-
@debug()
|
|
181
159
|
@functools.wraps(func)
|
|
182
160
|
async def wrapper(*args, **kwargs) -> Any:
|
|
183
161
|
func_params, api_config_params = self.split_kwargs(kwargs, config_params)
|
|
@@ -185,11 +163,10 @@ class entrypoint(BaseDecorator):
|
|
|
185
163
|
if not config_schema:
|
|
186
164
|
ag.config.set(**api_config_params)
|
|
187
165
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
)
|
|
192
|
-
with route_context_manager(config=api_config_params):
|
|
166
|
+
with routing_context_manager(
|
|
167
|
+
config=api_config_params,
|
|
168
|
+
environment="playground",
|
|
169
|
+
):
|
|
193
170
|
entrypoint_result = await self.execute_function(
|
|
194
171
|
func,
|
|
195
172
|
True, # inline trace: True
|
|
@@ -242,7 +219,6 @@ class entrypoint(BaseDecorator):
|
|
|
242
219
|
### ---------------------------- #
|
|
243
220
|
|
|
244
221
|
### --- Deployed / Published --- #
|
|
245
|
-
@debug()
|
|
246
222
|
@functools.wraps(func)
|
|
247
223
|
async def wrapper_deployed(*args, **kwargs) -> Any:
|
|
248
224
|
func_params = {
|
|
@@ -256,12 +232,10 @@ class entrypoint(BaseDecorator):
|
|
|
256
232
|
else:
|
|
257
233
|
ag.config.pull(config_name="default")
|
|
258
234
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
with route_context_manager(
|
|
264
|
-
variant=kwargs["config"], environment=kwargs["environment"]
|
|
235
|
+
with routing_context_manager(
|
|
236
|
+
config=config_params,
|
|
237
|
+
variant=kwargs["config"],
|
|
238
|
+
environment=kwargs["environment"],
|
|
265
239
|
):
|
|
266
240
|
entrypoint_result = await self.execute_function(
|
|
267
241
|
func,
|
|
@@ -351,86 +325,108 @@ class entrypoint(BaseDecorator):
|
|
|
351
325
|
if name in func_params and func_params[name] is not None:
|
|
352
326
|
func_params[name] = self.ingest_file(func_params[name])
|
|
353
327
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
"""Execute the function and handle any exceptions."""
|
|
328
|
+
def patch_result(self, result: Any):
|
|
329
|
+
"""
|
|
330
|
+
Patch the result to only include the message if the result is a FuncResponse-style dictionary with message, cost, and usage keys.
|
|
358
331
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
""
|
|
365
|
-
|
|
332
|
+
Example:
|
|
333
|
+
```python
|
|
334
|
+
result = {
|
|
335
|
+
"message": "Hello, world!",
|
|
336
|
+
"cost": 0.5,
|
|
337
|
+
"usage": {
|
|
338
|
+
"prompt_tokens": 10,
|
|
339
|
+
"completion_tokens": 20,
|
|
340
|
+
"total_tokens": 30
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
result = patch_result(result)
|
|
344
|
+
print(result)
|
|
345
|
+
# Output: "Hello, world!"
|
|
346
|
+
```
|
|
347
|
+
"""
|
|
348
|
+
data = (
|
|
349
|
+
result["message"]
|
|
350
|
+
if isinstance(result, dict)
|
|
351
|
+
and all(key in result for key in ["message", "cost", "usage"])
|
|
352
|
+
else result
|
|
353
|
+
)
|
|
366
354
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
355
|
+
if data is None:
|
|
356
|
+
data = (
|
|
357
|
+
"Function executed successfully, but did return None. \n Are you sure you did not forget to return a value?",
|
|
358
|
+
)
|
|
371
359
|
|
|
372
|
-
|
|
373
|
-
|
|
360
|
+
if not isinstance(result, dict):
|
|
361
|
+
data = str(data)
|
|
374
362
|
|
|
375
|
-
|
|
376
|
-
if tracing_context.get() is None:
|
|
377
|
-
token = tracing_context.set(TracingContext())
|
|
363
|
+
return data
|
|
378
364
|
|
|
379
|
-
|
|
365
|
+
async def execute_function(
|
|
366
|
+
self,
|
|
367
|
+
func: Callable[..., Any],
|
|
368
|
+
inline_trace,
|
|
369
|
+
*args,
|
|
370
|
+
**func_params,
|
|
371
|
+
):
|
|
372
|
+
log.info(f"\n--------------------------")
|
|
373
|
+
log.info(
|
|
374
|
+
f"Running application route: {repr(self.route_path if self.route_path != '' else '/')}"
|
|
375
|
+
)
|
|
376
|
+
log.info(f"--------------------------\n")
|
|
380
377
|
|
|
381
|
-
|
|
382
|
-
result = await func(*args, **func_params["params"])
|
|
383
|
-
else:
|
|
384
|
-
result = func(*args, **func_params["params"])
|
|
378
|
+
tracing_context.set(routing_context.get())
|
|
385
379
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
380
|
+
WAIT_FOR_SPANS = True
|
|
381
|
+
TIMEOUT = 1
|
|
382
|
+
TIMESTEP = 0.1
|
|
383
|
+
FINALSTEP = 0.001
|
|
384
|
+
NOFSTEPS = TIMEOUT / TIMESTEP
|
|
389
385
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
remaining_steps -= 1
|
|
386
|
+
data = None
|
|
387
|
+
trace = {}
|
|
393
388
|
|
|
394
|
-
|
|
389
|
+
try:
|
|
390
|
+
result = (
|
|
391
|
+
await func(*args, **func_params["params"])
|
|
392
|
+
if inspect.iscoroutinefunction(func)
|
|
393
|
+
else func(*args, **func_params["params"])
|
|
394
|
+
)
|
|
395
|
+
data = self.patch_result(result)
|
|
396
|
+
except Exception as e:
|
|
397
|
+
log.error(f"Agenta SDK - Routing Exception")
|
|
395
398
|
|
|
396
|
-
|
|
397
|
-
trace = {"trace_id": trace["trace_id"]}
|
|
399
|
+
traceback.print_exc()
|
|
398
400
|
|
|
399
|
-
|
|
400
|
-
tracing_context.reset(token)
|
|
401
|
+
self.handle_exception(e)
|
|
401
402
|
|
|
402
|
-
|
|
403
|
-
|
|
403
|
+
with suppress():
|
|
404
|
+
root_context: Dict[str, Any] = tracing_context.get().get("root")
|
|
404
405
|
|
|
405
|
-
|
|
406
|
+
trace_id = root_context.get("trace_id") if root_context else None
|
|
406
407
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
# PATCH : if result is a legacy dict, clean it up
|
|
412
|
-
if (
|
|
413
|
-
"message" in result.keys()
|
|
414
|
-
and "cost" in result.keys()
|
|
415
|
-
and "usage" in result.keys()
|
|
416
|
-
):
|
|
417
|
-
data = str(result["message"])
|
|
408
|
+
if trace_id is not None:
|
|
409
|
+
if inline_trace:
|
|
410
|
+
if WAIT_FOR_SPANS:
|
|
411
|
+
remaining_steps = NOFSTEPS
|
|
418
412
|
|
|
419
|
-
|
|
413
|
+
while (
|
|
414
|
+
not ag.tracing.is_inline_trace_ready(trace_id)
|
|
415
|
+
and remaining_steps > 0
|
|
416
|
+
):
|
|
417
|
+
await asyncio.sleep(TIMESTEP)
|
|
420
418
|
|
|
421
|
-
|
|
422
|
-
data = (
|
|
423
|
-
"Function executed successfully, but did return None. \n Are you sure you did not forget to return a value?",
|
|
424
|
-
)
|
|
419
|
+
remaining_steps -= 1
|
|
425
420
|
|
|
426
|
-
|
|
421
|
+
await asyncio.sleep(FINALSTEP)
|
|
427
422
|
|
|
428
|
-
|
|
423
|
+
trace = ag.tracing.get_inline_trace(trace_id)
|
|
424
|
+
else:
|
|
425
|
+
trace = {"trace_id": trace_id}
|
|
429
426
|
|
|
430
|
-
|
|
427
|
+
response = BaseResponse(data=data, trace=trace)
|
|
431
428
|
|
|
432
|
-
|
|
433
|
-
self.handle_exception(e)
|
|
429
|
+
return response
|
|
434
430
|
|
|
435
431
|
def handle_exception(self, e: Exception):
|
|
436
432
|
status_code = e.status_code if hasattr(e, "status_code") else 500
|
|
@@ -644,28 +640,45 @@ class entrypoint(BaseDecorator):
|
|
|
644
640
|
}
|
|
645
641
|
)
|
|
646
642
|
|
|
647
|
-
# Set the configuration and environment of the LLM app parent span at run-time
|
|
648
|
-
ag.tracing.update_baggage({"config": args_config_params, "environment": "bash"})
|
|
649
|
-
|
|
650
643
|
loop = asyncio.get_event_loop()
|
|
651
644
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
645
|
+
with routing_context_manager(
|
|
646
|
+
config=args_config_params,
|
|
647
|
+
environment="terminal",
|
|
648
|
+
):
|
|
649
|
+
result = loop.run_until_complete(
|
|
650
|
+
self.execute_function(
|
|
651
|
+
func,
|
|
652
|
+
True, # inline trace: True
|
|
653
|
+
**{"params": args_func_params, "config_params": args_config_params},
|
|
654
|
+
)
|
|
657
655
|
)
|
|
658
|
-
)
|
|
659
656
|
|
|
660
|
-
|
|
657
|
+
SHOW_DETAILS = True
|
|
658
|
+
SHOW_DATA = False
|
|
659
|
+
SHOW_TRACE = False
|
|
660
|
+
|
|
661
|
+
log.info("\n========= Result =========\n")
|
|
662
|
+
|
|
663
|
+
log.info(f"trace_id: {result.trace['trace_id']}")
|
|
664
|
+
if SHOW_DETAILS:
|
|
665
|
+
log.info(f"latency: {result.trace.get('latency')}")
|
|
666
|
+
log.info(f"cost: {result.trace.get('cost')}")
|
|
667
|
+
log.info(f"usage: {list(result.trace.get('usage', {}).values())}")
|
|
668
|
+
|
|
669
|
+
if SHOW_DATA:
|
|
670
|
+
log.info(" ")
|
|
671
|
+
log.info(f"data:")
|
|
672
|
+
log.info(json.dumps(result.data, indent=2))
|
|
661
673
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
674
|
+
if SHOW_TRACE:
|
|
675
|
+
log.info(" ")
|
|
676
|
+
log.info(f"trace:")
|
|
677
|
+
log.info(f"----------------")
|
|
678
|
+
log.info(json.dumps(result.trace.get("spans", []), indent=2))
|
|
679
|
+
log.info(f"----------------")
|
|
666
680
|
|
|
667
|
-
|
|
668
|
-
json.dump(result.trace, trace_file, indent=4)
|
|
681
|
+
log.info("\n==========================\n")
|
|
669
682
|
|
|
670
683
|
def override_config_in_schema(
|
|
671
684
|
self,
|