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