agenta 0.30.0a2__py3-none-any.whl → 0.30.0a4__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.

Files changed (71) hide show
  1. agenta/__init__.py +1 -0
  2. agenta/client/backend/__init__.py +32 -3
  3. agenta/client/backend/access_control/__init__.py +1 -0
  4. agenta/client/backend/access_control/client.py +167 -0
  5. agenta/client/backend/apps/client.py +70 -10
  6. agenta/client/backend/client.py +61 -45
  7. agenta/client/backend/configs/client.py +6 -0
  8. agenta/client/backend/containers/client.py +6 -0
  9. agenta/client/backend/core/file.py +13 -8
  10. agenta/client/backend/environments/client.py +6 -0
  11. agenta/client/backend/evaluations/client.py +14 -1
  12. agenta/client/backend/evaluators/client.py +24 -0
  13. agenta/client/backend/observability/client.py +22 -16
  14. agenta/client/backend/observability_v_1/__init__.py +2 -2
  15. agenta/client/backend/observability_v_1/client.py +203 -0
  16. agenta/client/backend/observability_v_1/types/__init__.py +2 -1
  17. agenta/client/backend/observability_v_1/types/format.py +1 -1
  18. agenta/client/backend/observability_v_1/types/query_analytics_response.py +7 -0
  19. agenta/client/backend/scopes/__init__.py +1 -0
  20. agenta/client/backend/scopes/client.py +114 -0
  21. agenta/client/backend/testsets/client.py +305 -121
  22. agenta/client/backend/types/__init__.py +24 -2
  23. agenta/client/backend/types/analytics_response.py +24 -0
  24. agenta/client/backend/types/app.py +2 -1
  25. agenta/client/backend/types/body_import_testset.py +0 -1
  26. agenta/client/backend/types/bucket_dto.py +26 -0
  27. agenta/client/backend/types/header_dto.py +22 -0
  28. agenta/client/backend/types/legacy_analytics_response.py +29 -0
  29. agenta/client/backend/types/legacy_data_point.py +27 -0
  30. agenta/client/backend/types/metrics_dto.py +24 -0
  31. agenta/client/backend/types/permission.py +1 -0
  32. agenta/client/backend/types/projects_response.py +28 -0
  33. agenta/client/backend/types/provider_key_dto.py +23 -0
  34. agenta/client/backend/types/provider_kind.py +21 -0
  35. agenta/client/backend/types/secret_dto.py +24 -0
  36. agenta/client/backend/types/secret_kind.py +5 -0
  37. agenta/client/backend/types/secret_response_dto.py +27 -0
  38. agenta/client/backend/variants/client.py +66 -0
  39. agenta/client/backend/vault/__init__.py +1 -0
  40. agenta/client/backend/vault/client.py +685 -0
  41. agenta/client/client.py +1 -1
  42. agenta/sdk/__init__.py +1 -0
  43. agenta/sdk/agenta_init.py +47 -118
  44. agenta/sdk/assets.py +57 -46
  45. agenta/sdk/context/exporting.py +25 -0
  46. agenta/sdk/context/routing.py +12 -12
  47. agenta/sdk/context/tracing.py +26 -1
  48. agenta/sdk/decorators/routing.py +279 -243
  49. agenta/sdk/decorators/tracing.py +53 -31
  50. agenta/sdk/managers/config.py +8 -118
  51. agenta/sdk/managers/secrets.py +38 -0
  52. agenta/sdk/middleware/auth.py +128 -93
  53. agenta/sdk/middleware/cache.py +4 -0
  54. agenta/sdk/middleware/config.py +254 -0
  55. agenta/sdk/middleware/cors.py +28 -0
  56. agenta/sdk/middleware/otel.py +40 -0
  57. agenta/sdk/middleware/vault.py +158 -0
  58. agenta/sdk/tracing/exporters.py +40 -2
  59. agenta/sdk/tracing/inline.py +2 -2
  60. agenta/sdk/tracing/processors.py +11 -3
  61. agenta/sdk/tracing/tracing.py +14 -12
  62. agenta/sdk/utils/constants.py +1 -0
  63. agenta/sdk/utils/exceptions.py +20 -19
  64. agenta/sdk/utils/globals.py +4 -8
  65. agenta/sdk/utils/timing.py +58 -0
  66. {agenta-0.30.0a2.dist-info → agenta-0.30.0a4.dist-info}/METADATA +3 -2
  67. {agenta-0.30.0a2.dist-info → agenta-0.30.0a4.dist-info}/RECORD +69 -44
  68. {agenta-0.30.0a2.dist-info → agenta-0.30.0a4.dist-info}/WHEEL +1 -1
  69. agenta/client/backend/types/lm_providers_enum.py +0 -21
  70. agenta/sdk/tracing/context.py +0 -24
  71. {agenta-0.30.0a2.dist-info → agenta-0.30.0a4.dist-info}/entry_points.txt +0 -0
@@ -1,25 +1,32 @@
1
1
  from typing import Type, Any, Callable, Dict, Optional, Tuple, List
2
- from annotated_types import Ge, Le, Gt, Lt
3
- from pydantic import BaseModel, HttpUrl, ValidationError
4
- from json import dumps
5
2
  from inspect import signature, iscoroutinefunction, Signature, Parameter, _empty
6
- from argparse import ArgumentParser
7
3
  from functools import wraps
8
- from asyncio import sleep, get_event_loop
9
- from traceback import format_exc, format_exception
10
- from pathlib import Path
4
+ from traceback import format_exception
5
+ from asyncio import sleep
6
+
11
7
  from tempfile import NamedTemporaryFile
12
- from os import environ
8
+ from annotated_types import Ge, Le, Gt, Lt
9
+ from pydantic import BaseModel, HttpUrl, ValidationError
10
+
11
+ from fastapi import Body, FastAPI, UploadFile, HTTPException, Request
13
12
 
14
- from fastapi.middleware.cors import CORSMiddleware
15
- from fastapi import Body, FastAPI, UploadFile, HTTPException
13
+ from agenta.sdk.middleware.auth import AuthMiddleware
14
+ from agenta.sdk.middleware.otel import OTelMiddleware
15
+ from agenta.sdk.middleware.config import ConfigMiddleware
16
+ from agenta.sdk.middleware.vault import VaultMiddleware
17
+ from agenta.sdk.middleware.cors import CORSMiddleware
16
18
 
17
- from agenta.sdk.middleware.auth import AuthorizationMiddleware
18
- from agenta.sdk.context.routing import routing_context_manager, routing_context
19
- from agenta.sdk.context.tracing import tracing_context
19
+ from agenta.sdk.context.routing import (
20
+ routing_context_manager,
21
+ RoutingContext,
22
+ )
23
+ from agenta.sdk.context.tracing import (
24
+ tracing_context_manager,
25
+ tracing_context,
26
+ TracingContext,
27
+ )
20
28
  from agenta.sdk.router import router
21
- from agenta.sdk.utils import helpers
22
- from agenta.sdk.utils.exceptions import suppress
29
+ from agenta.sdk.utils.exceptions import suppress, display_exception
23
30
  from agenta.sdk.utils.logging import log
24
31
  from agenta.sdk.types import (
25
32
  DictInput,
@@ -39,19 +46,10 @@ from agenta.sdk.types import (
39
46
  import agenta as ag
40
47
 
41
48
 
42
- AGENTA_USE_CORS = str(environ.get("AGENTA_USE_CORS", "true")).lower() in (
43
- "true",
44
- "1",
45
- "t",
46
- )
47
-
48
49
  app = FastAPI()
49
50
  log.setLevel("DEBUG")
50
51
 
51
52
 
52
- _MIDDLEWARES = True
53
-
54
-
55
53
  app.include_router(router, prefix="")
56
54
 
57
55
 
@@ -59,13 +57,17 @@ class PathValidator(BaseModel):
59
57
  url: HttpUrl
60
58
 
61
59
 
62
- class route:
60
+ class route: # pylint: disable=invalid-name
63
61
  # This decorator is used to expose specific stages of a workflow (embedding, retrieval, summarization, etc.)
64
62
  # as independent endpoints. It is designed for backward compatibility with existing code that uses
65
63
  # the @entrypoint decorator, which has certain limitations. By using @route(), we can create new
66
64
  # routes without altering the main workflow entrypoint. This helps in modularizing the services
67
65
  # and provides flexibility in how we expose different functionalities as APIs.
68
- def __init__(self, path, config_schema: BaseModel):
66
+ def __init__(
67
+ self,
68
+ path: Optional[str] = "/",
69
+ config_schema: Optional[BaseModel] = None,
70
+ ):
69
71
  self.config_schema: BaseModel = config_schema
70
72
  path = "/" + path.strip("/").strip()
71
73
  path = "" if path == "/" else path
@@ -73,10 +75,15 @@ class route:
73
75
 
74
76
  self.route_path = path
75
77
 
78
+ self.e = None
79
+
76
80
  def __call__(self, f):
77
81
  self.e = entrypoint(
78
- f, route_path=self.route_path, config_schema=self.config_schema
82
+ f,
83
+ route_path=self.route_path,
84
+ config_schema=self.config_schema,
79
85
  )
86
+
80
87
  return f
81
88
 
82
89
 
@@ -113,223 +120,210 @@ class entrypoint:
113
120
 
114
121
  routes = list()
115
122
 
123
+ _middleware = False
124
+ _run_path = "/run"
125
+ _test_path = "/test"
126
+ # LEGACY
127
+ _legacy_playground_run_path = "/playground/run"
128
+ _legacy_generate_path = "/generate"
129
+ _legacy_generate_deployed_path = "/generate_deployed"
130
+
116
131
  def __init__(
117
132
  self,
118
133
  func: Callable[..., Any],
119
- route_path="",
134
+ route_path: str = "",
120
135
  config_schema: Optional[BaseModel] = None,
121
136
  ):
122
- ### --- Update Middleware --- #
123
- try:
124
- global _MIDDLEWARES # pylint: disable=global-statement
125
-
126
- if _MIDDLEWARES:
127
- app.add_middleware(
128
- AuthorizationMiddleware,
129
- host=ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.host,
130
- resource_id=ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.app_id,
131
- resource_type="application",
132
- )
137
+ self.func = func
138
+ self.route_path = route_path
139
+ self.config_schema = config_schema
133
140
 
134
- if AGENTA_USE_CORS:
135
- app.add_middleware(
136
- CORSMiddleware,
137
- allow_origins=["*"],
138
- allow_methods=["*"],
139
- allow_headers=["*"],
140
- allow_credentials=True,
141
- )
141
+ signature_parameters = signature(func).parameters
142
+ ingestible_files = self.extract_ingestible_files()
143
+ config, default_parameters = self.parse_config()
142
144
 
143
- _MIDDLEWARES = False
145
+ ### --- Middleware --- #
146
+ if not entrypoint._middleware:
147
+ entrypoint._middleware = True
144
148
 
145
- except: # pylint: disable=bare-except
146
- log.warning("Agenta SDK - failed to secure route: %s", route_path)
147
- ### --- Update Middleware --- #
149
+ app.add_middleware(VaultMiddleware)
150
+ app.add_middleware(ConfigMiddleware)
151
+ app.add_middleware(AuthMiddleware)
152
+ app.add_middleware(OTelMiddleware)
153
+ app.add_middleware(CORSMiddleware)
154
+ ### ------------------ #
148
155
 
149
- DEFAULT_PATH = "generate"
150
- PLAYGROUND_PATH = "/playground"
151
- RUN_PATH = "/run"
152
- func_signature = signature(func)
153
- try:
154
- config = (
155
- config_schema() if config_schema else None
156
- ) # we initialize the config object to be able to use it
157
- except ValidationError as e:
158
- raise ValueError(
159
- f"Error initializing config_schema. Please ensure all required fields have default values: {str(e)}"
160
- ) from e
161
- except Exception as e:
162
- raise ValueError(
163
- f"Unexpected error initializing config_schema: {str(e)}"
164
- ) from e
165
-
166
- config_params = config.dict() if config else ag.config.all()
167
- ingestible_files = self.extract_ingestible_files(func_signature)
156
+ ### --- Run --- #
157
+ @wraps(func)
158
+ async def run_wrapper(request: Request, *args, **kwargs) -> Any:
159
+ # LEGACY
160
+ # TODO: Removing this implies breaking changes in :
161
+ # - calls to /generate_deployed
162
+ kwargs = {
163
+ k: v
164
+ for k, v in kwargs.items()
165
+ if k not in ["config", "environment", "app"]
166
+ }
167
+ # LEGACY
168
168
 
169
- self.route_path = route_path
169
+ kwargs, _ = self.split_kwargs(kwargs, default_parameters)
170
170
 
171
- ### --- Playground --- #
171
+ # TODO: Why is this not used in the run_wrapper?
172
+ # self.ingest_files(kwargs, ingestible_files)
173
+
174
+ return await self.execute_wrapper(request, False, *args, **kwargs)
175
+
176
+ self.update_run_wrapper_signature(
177
+ wrapper=run_wrapper,
178
+ ingestible_files=ingestible_files,
179
+ )
180
+
181
+ run_route = f"{entrypoint._run_path}{route_path}"
182
+ app.post(run_route, response_model=BaseResponse)(run_wrapper)
183
+
184
+ # LEGACY
185
+ # TODO: Removing this implies breaking changes in :
186
+ # - calls to /generate_deployed must be replaced with calls to /run
187
+ if route_path == "":
188
+ run_route = entrypoint._legacy_generate_deployed_path
189
+ app.post(run_route, response_model=BaseResponse)(run_wrapper)
190
+ # LEGACY
191
+ ### ----------- #
192
+
193
+ ### --- Test --- #
172
194
  @wraps(func)
173
- async def wrapper(*args, **kwargs) -> Any:
174
- func_params, api_config_params = self.split_kwargs(kwargs, config_params)
175
- self.ingest_files(func_params, ingestible_files)
176
- if not config_schema:
177
- ag.config.set(**api_config_params)
178
-
179
- with routing_context_manager(
180
- config=api_config_params,
181
- ):
182
- entrypoint_result = await self.execute_function(
183
- func,
184
- True, # inline trace: True
185
- *args,
186
- params=func_params,
187
- config_params=config_params,
188
- )
195
+ async def test_wrapper(request: Request, *args, **kwargs) -> Any:
196
+ kwargs, parameters = self.split_kwargs(kwargs, default_parameters)
189
197
 
190
- return entrypoint_result
198
+ request.state.config["parameters"] = parameters
191
199
 
192
- self.update_function_signature(
193
- wrapper=wrapper,
194
- func_signature=func_signature,
195
- config_class=config,
196
- config_dict=config_params,
200
+ # TODO: Why is this only used in the test_wrapper?
201
+ self.ingest_files(kwargs, ingestible_files)
202
+
203
+ return await self.execute_wrapper(request, True, *args, **kwargs)
204
+
205
+ self.update_test_wrapper_signature(
206
+ wrapper=test_wrapper,
197
207
  ingestible_files=ingestible_files,
208
+ config_class=config,
209
+ config_dict=default_parameters,
198
210
  )
199
211
 
200
- #
212
+ test_route = f"{entrypoint._test_path}{route_path}"
213
+ app.post(test_route, response_model=BaseResponse)(test_wrapper)
214
+
215
+ # LEGACY
216
+ # TODO: Removing this implies breaking changes in :
217
+ # - calls to /generate must be replaced with calls to /test
201
218
  if route_path == "":
202
- route = f"/{DEFAULT_PATH}"
203
- app.post(route, response_model=BaseResponse)(wrapper)
204
- entrypoint.routes.append(
205
- {
206
- "func": func.__name__,
207
- "endpoint": route,
208
- "params": (
209
- {**config_params, **func_signature.parameters}
210
- if not config
211
- else func_signature.parameters
212
- ),
213
- "config": config,
214
- }
215
- )
219
+ test_route = entrypoint._legacy_generate_path
220
+ app.post(test_route, response_model=BaseResponse)(test_wrapper)
221
+ # LEGACY
222
+
223
+ # LEGACY
224
+ # TODO: Removing this implies no breaking changes
225
+ if route_path == "":
226
+ test_route = entrypoint._legacy_playground_run_path
227
+ app.post(test_route, response_model=BaseResponse)(test_wrapper)
228
+ # LEGACY
229
+ ### ------------ #
216
230
 
217
- route = f"{PLAYGROUND_PATH}{RUN_PATH}{route_path}"
218
- app.post(route, response_model=BaseResponse)(wrapper)
231
+ ### --- OpenAPI --- #
232
+ test_route = f"{entrypoint._test_path}{route_path}"
219
233
  entrypoint.routes.append(
220
234
  {
221
235
  "func": func.__name__,
222
- "endpoint": route,
236
+ "endpoint": test_route,
223
237
  "params": (
224
- {**config_params, **func_signature.parameters}
238
+ {**default_parameters, **signature_parameters}
225
239
  if not config
226
- else func_signature.parameters
240
+ else signature_parameters
227
241
  ),
228
242
  "config": config,
229
243
  }
230
244
  )
231
- ### ---------------------------- #
232
245
 
233
- ### --- Deployed --- #
234
- @wraps(func)
235
- async def wrapper_deployed(*args, **kwargs) -> Any:
236
- func_params = {
237
- k: v
238
- for k, v in kwargs.items()
239
- if k not in ["config", "environment", "app"]
240
- }
241
- if not config_schema:
242
- if "environment" in kwargs and kwargs["environment"] is not None:
243
- ag.config.pull(environment_name=kwargs["environment"])
244
- elif "config" in kwargs and kwargs["config"] is not None:
245
- ag.config.pull(config_name=kwargs["config"])
246
- else:
247
- ag.config.pull(config_name="default")
248
-
249
- app_id = environ.get("AGENTA_APP_ID")
250
-
251
- with routing_context_manager(
252
- application={
253
- "id": app_id,
254
- "slug": kwargs.get("app"),
255
- },
256
- variant={
257
- "slug": kwargs.get("config"),
258
- },
259
- environment={
260
- "slug": kwargs.get("environment"),
261
- },
262
- ):
263
- entrypoint_result = await self.execute_function(
264
- func,
265
- False, # inline trace: False
266
- *args,
267
- params=func_params,
268
- config_params=config_params,
269
- )
270
-
271
- return entrypoint_result
272
-
273
- self.update_deployed_function_signature(
274
- wrapper_deployed,
275
- func_signature,
276
- ingestible_files,
277
- )
246
+ # LEGACY
278
247
  if route_path == "":
279
- route_deployed = f"/{DEFAULT_PATH}_deployed"
280
- app.post(route_deployed, response_model=BaseResponse)(wrapper_deployed)
281
-
282
- route_deployed = f"{RUN_PATH}{route_path}"
283
- app.post(route_deployed, response_model=BaseResponse)(wrapper_deployed)
284
- ### ---------------- #
248
+ test_route = entrypoint._legacy_generate_path
249
+ entrypoint.routes.append(
250
+ {
251
+ "func": func.__name__,
252
+ "endpoint": test_route,
253
+ "params": (
254
+ {**default_parameters, **signature_parameters}
255
+ if not config
256
+ else signature_parameters
257
+ ),
258
+ "config": config,
259
+ }
260
+ )
261
+ # LEGACY
285
262
 
286
- ### --- Update OpenAPI --- #
287
263
  app.openapi_schema = None # Forces FastAPI to re-generate the schema
288
264
  openapi_schema = app.openapi()
289
265
 
290
- # Inject the current version of the SDK into the openapi_schema
291
- openapi_schema["agenta_sdk"] = {"version": helpers.get_current_version()}
292
-
293
- for route in entrypoint.routes:
266
+ for _route in entrypoint.routes:
294
267
  self.override_schema(
295
268
  openapi_schema=openapi_schema,
296
- func_name=route["func"],
297
- endpoint=route["endpoint"],
298
- params=route["params"],
269
+ func_name=_route["func"],
270
+ endpoint=_route["endpoint"],
271
+ params=_route["params"],
299
272
  )
300
- if route["config"] is not None: # new SDK version
273
+
274
+ if _route["config"] is not None: # new SDK version
301
275
  self.override_config_in_schema(
302
276
  openapi_schema=openapi_schema,
303
- func_name=route["func"],
304
- endpoint=route["endpoint"],
305
- config=route["config"],
277
+ func_name=_route["func"],
278
+ endpoint=_route["endpoint"],
279
+ config=_route["config"],
306
280
  )
281
+ ### --------------- #
307
282
 
308
- def extract_ingestible_files(
309
- self,
310
- func_signature: Signature,
311
- ) -> Dict[str, Parameter]:
283
+ def extract_ingestible_files(self) -> Dict[str, Parameter]:
312
284
  """Extract parameters annotated as InFile from function signature."""
313
285
 
314
286
  return {
315
287
  name: param
316
- for name, param in func_signature.parameters.items()
288
+ for name, param in signature(self.func).parameters.items()
317
289
  if param.annotation is InFile
318
290
  }
319
291
 
292
+ def parse_config(self) -> Dict[str, Any]:
293
+ config = None
294
+ default_parameters = ag.config.all()
295
+
296
+ if self.config_schema:
297
+ try:
298
+ config = self.config_schema() if self.config_schema else None
299
+ default_parameters = config.dict() if config else default_parameters
300
+ except ValidationError as e:
301
+ raise ValueError(
302
+ f"Error initializing config_schema. Please ensure all required fields have default values: {str(e)}"
303
+ ) from e
304
+ except Exception as e:
305
+ raise ValueError(
306
+ f"Unexpected error initializing config_schema: {str(e)}"
307
+ ) from e
308
+
309
+ return config, default_parameters
310
+
320
311
  def split_kwargs(
321
- self, kwargs: Dict[str, Any], config_params: Dict[str, Any]
312
+ self, kwargs: Dict[str, Any], default_parameters: Dict[str, Any]
322
313
  ) -> Tuple[Dict[str, Any], Dict[str, Any]]:
323
- """Split keyword arguments into function parameters and API configuration parameters."""
314
+ arguments = {k: v for k, v in kwargs.items() if k not in default_parameters}
315
+ parameters = {k: v for k, v in kwargs.items() if k in default_parameters}
324
316
 
325
- func_params = {k: v for k, v in kwargs.items() if k not in config_params}
326
- api_config_params = {k: v for k, v in kwargs.items() if k in config_params}
327
- return func_params, api_config_params
317
+ return arguments, parameters
328
318
 
329
- def ingest_file(self, upfile: UploadFile):
319
+ def ingest_file(
320
+ self,
321
+ upfile: UploadFile,
322
+ ):
330
323
  temp_file = NamedTemporaryFile(delete=False)
331
324
  temp_file.write(upfile.file.read())
332
325
  temp_file.close()
326
+
333
327
  return InFile(file_name=upfile.filename, file_path=temp_file.name)
334
328
 
335
329
  def ingest_files(
@@ -343,51 +337,81 @@ class entrypoint:
343
337
  if name in func_params and func_params[name] is not None:
344
338
  func_params[name] = self.ingest_file(func_params[name])
345
339
 
346
- async def execute_function(
340
+ async def execute_wrapper(
347
341
  self,
348
- func: Callable[..., Any],
349
- inline_trace,
342
+ request: Request,
343
+ inline: bool,
350
344
  *args,
351
- **func_params,
345
+ **kwargs,
352
346
  ):
353
- log.info("Agenta SDK - handling route: %s", repr(self.route_path or "/"))
347
+ if not request:
348
+ raise HTTPException(status_code=500, detail="Missing 'request'.")
349
+
350
+ state = request.state
351
+ credentials = state.auth.get("credentials")
352
+ parameters = state.config.get("parameters")
353
+ references = state.config.get("references")
354
+ secrets = state.vault.get("secrets")
355
+
356
+ with routing_context_manager(
357
+ context=RoutingContext(
358
+ parameters=parameters,
359
+ secrets=secrets,
360
+ )
361
+ ):
362
+ with tracing_context_manager(
363
+ context=TracingContext(
364
+ credentials=credentials,
365
+ parameters=parameters,
366
+ references=references,
367
+ )
368
+ ):
369
+ result = await self.execute_function(inline, *args, **kwargs)
354
370
 
355
- tracing_context.set(routing_context.get())
371
+ return result
356
372
 
373
+ async def execute_function(
374
+ self,
375
+ inline: bool,
376
+ *args,
377
+ **kwargs,
378
+ ):
357
379
  try:
358
380
  result = (
359
- await func(*args, **func_params["params"])
360
- if iscoroutinefunction(func)
361
- else func(*args, **func_params["params"])
381
+ await self.func(*args, **kwargs)
382
+ if iscoroutinefunction(self.func)
383
+ else self.func(*args, **kwargs)
362
384
  )
363
385
 
364
- return await self.handle_success(result, inline_trace)
386
+ return await self.handle_success(result, inline)
365
387
 
366
- except Exception as error:
388
+ except Exception as error: # pylint: disable=broad-except
367
389
  self.handle_failure(error)
368
390
 
369
- async def handle_success(self, result: Any, inline_trace: bool):
391
+ async def handle_success(
392
+ self,
393
+ result: Any,
394
+ inline: bool,
395
+ ):
370
396
  data = None
371
397
  tree = None
372
398
 
373
399
  with suppress():
374
400
  data = self.patch_result(result)
375
401
 
376
- if inline_trace:
377
- tree = await self.fetch_inline_trace(inline_trace)
402
+ if inline:
403
+ tree = await self.fetch_inline_trace(inline)
378
404
 
379
- log.info(f"----------------------------------")
380
- log.info(f"Agenta SDK - exiting with success: 200")
381
- log.info(f"----------------------------------")
382
-
383
- return BaseResponse(data=data, tree=tree)
405
+ try:
406
+ return BaseResponse(data=data, tree=tree)
407
+ except:
408
+ return BaseResponse(data=data)
384
409
 
385
- def handle_failure(self, error: Exception):
386
- log.warning("--------------------------------------------------")
387
- log.warning("Agenta SDK - handling application exception below:")
388
- log.warning("--------------------------------------------------")
389
- log.warning(format_exc().strip("\n"))
390
- log.warning("--------------------------------------------------")
410
+ def handle_failure(
411
+ self,
412
+ error: Exception,
413
+ ):
414
+ display_exception("Application Exception")
391
415
 
392
416
  status_code = 500
393
417
  message = str(error)
@@ -396,7 +420,10 @@ class entrypoint:
396
420
 
397
421
  raise HTTPException(status_code=status_code, detail=detail)
398
422
 
399
- def patch_result(self, result: Any):
423
+ def patch_result(
424
+ self,
425
+ result: Any,
426
+ ):
400
427
  """
401
428
  Patch the result to only include the message if the result is a FuncResponse-style dictionary with message, cost, and usage keys.
402
429
 
@@ -433,7 +460,10 @@ class entrypoint:
433
460
 
434
461
  return data
435
462
 
436
- async def fetch_inline_trace(self, inline_trace):
463
+ async def fetch_inline_trace(
464
+ self,
465
+ inline,
466
+ ):
437
467
  WAIT_FOR_SPANS = True
438
468
  TIMEOUT = 1
439
469
  TIMESTEP = 0.1
@@ -442,12 +472,14 @@ class entrypoint:
442
472
 
443
473
  trace = None
444
474
 
445
- root_context: Dict[str, Any] = tracing_context.get().get("root")
475
+ context = tracing_context.get()
476
+
477
+ link = context.link
446
478
 
447
- trace_id = root_context.get("trace_id") if root_context else None
479
+ trace_id = link.get("tree_id") if link else None
448
480
 
449
481
  if trace_id is not None:
450
- if inline_trace:
482
+ if inline:
451
483
  if WAIT_FOR_SPANS:
452
484
  remaining_steps = NOFSTEPS
453
485
 
@@ -467,6 +499,27 @@ class entrypoint:
467
499
 
468
500
  return trace
469
501
 
502
+ # --- OpenAPI --- #
503
+
504
+ def add_request_to_signature(
505
+ self,
506
+ wrapper: Callable[..., Any],
507
+ ):
508
+ original_sig = signature(wrapper)
509
+ parameters = [
510
+ Parameter(
511
+ "request",
512
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
513
+ annotation=Request,
514
+ ),
515
+ *original_sig.parameters.values(),
516
+ ]
517
+ new_sig = Signature(
518
+ parameters,
519
+ return_annotation=original_sig.return_annotation,
520
+ )
521
+ wrapper.__signature__ = new_sig
522
+
470
523
  def update_wrapper_signature(
471
524
  self, wrapper: Callable[..., Any], updated_params: List
472
525
  ):
@@ -483,10 +536,9 @@ class entrypoint:
483
536
  wrapper_signature = wrapper_signature.replace(parameters=updated_params)
484
537
  wrapper.__signature__ = wrapper_signature # type: ignore
485
538
 
486
- def update_function_signature(
539
+ def update_test_wrapper_signature(
487
540
  self,
488
541
  wrapper: Callable[..., Any],
489
- func_signature: Signature,
490
542
  config_class: Type[BaseModel], # TODO: change to our type
491
543
  config_dict: Dict[str, Any],
492
544
  ingestible_files: Dict[str, Parameter],
@@ -498,19 +550,19 @@ class entrypoint:
498
550
  self.add_config_params_to_parser(updated_params, config_class)
499
551
  else:
500
552
  self.deprecated_add_config_params_to_parser(updated_params, config_dict)
501
- self.add_func_params_to_parser(updated_params, func_signature, ingestible_files)
553
+ self.add_func_params_to_parser(updated_params, ingestible_files)
502
554
  self.update_wrapper_signature(wrapper, updated_params)
555
+ self.add_request_to_signature(wrapper)
503
556
 
504
- def update_deployed_function_signature(
557
+ def update_run_wrapper_signature(
505
558
  self,
506
559
  wrapper: Callable[..., Any],
507
- func_signature: Signature,
508
560
  ingestible_files: Dict[str, Parameter],
509
561
  ) -> None:
510
562
  """Update the function signature to include new parameters."""
511
563
 
512
564
  updated_params: List[Parameter] = []
513
- self.add_func_params_to_parser(updated_params, func_signature, ingestible_files)
565
+ self.add_func_params_to_parser(updated_params, ingestible_files)
514
566
  for param in [
515
567
  "config",
516
568
  "environment",
@@ -524,6 +576,7 @@ class entrypoint:
524
576
  )
525
577
  )
526
578
  self.update_wrapper_signature(wrapper, updated_params)
579
+ self.add_request_to_signature(wrapper)
527
580
 
528
581
  def add_config_params_to_parser(
529
582
  self, updated_params: list, config_class: Type[BaseModel]
@@ -564,11 +617,10 @@ class entrypoint:
564
617
  def add_func_params_to_parser(
565
618
  self,
566
619
  updated_params: list,
567
- func_signature: Signature,
568
620
  ingestible_files: Dict[str, Parameter],
569
621
  ) -> None:
570
622
  """Add function parameters to function signature."""
571
- for name, param in func_signature.parameters.items():
623
+ for name, param in signature(self.func).parameters.items():
572
624
  if name in ingestible_files:
573
625
  updated_params.append(
574
626
  Parameter(name, param.kind, annotation=UploadFile)
@@ -590,22 +642,6 @@ class entrypoint:
590
642
  )
591
643
  )
592
644
 
593
- def is_main_script(self, func: Callable) -> bool:
594
- """
595
- Check if the script containing the function is the main script being run.
596
-
597
- Args:
598
- func (Callable): The function object to check.
599
-
600
- Returns:
601
- bool: True if the script containing the function is the main script, False otherwise.
602
-
603
- Example:
604
- if is_main_script(my_function):
605
- print("This is the main script.")
606
- """
607
- return func.__module__ == "__main__"
608
-
609
645
  def override_config_in_schema(
610
646
  self,
611
647
  openapi_schema: dict,