agenta 0.30.0a1__py3-none-any.whl → 0.30.0a3__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 +272 -267
  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 +27 -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.0a1.dist-info → agenta-0.30.0a3.dist-info}/METADATA +3 -2
  67. {agenta-0.30.0a1.dist-info → agenta-0.30.0a3.dist-info}/RECORD +69 -44
  68. {agenta-0.30.0a1.dist-info → agenta-0.30.0a3.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.0a1.dist-info → agenta-0.30.0a3.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,14 +75,15 @@ class route:
73
75
 
74
76
  self.route_path = path
75
77
 
76
- def __call__(self, f):
77
- # If not running in Agenta, return the original function unchanged
78
- if environ.get("AGENTA_RUNTIME") != "true":
79
- return f
78
+ self.e = None
80
79
 
80
+ def __call__(self, f):
81
81
  self.e = entrypoint(
82
- 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,
83
85
  )
86
+
84
87
  return f
85
88
 
86
89
 
@@ -117,250 +120,210 @@ class entrypoint:
117
120
 
118
121
  routes = list()
119
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
+
120
131
  def __init__(
121
132
  self,
122
133
  func: Callable[..., Any],
123
- route_path="",
134
+ route_path: str = "",
124
135
  config_schema: Optional[BaseModel] = None,
125
136
  ):
126
137
  self.func = func
127
138
  self.route_path = route_path
128
139
  self.config_schema = config_schema
129
140
 
130
- def __call__(self, func=None):
131
- if func is None:
132
- func = self.func
133
- route_path = self.route_path
134
- config_schema = self.config_schema
135
- else:
136
- route_path = ""
137
- config_schema = None
141
+ signature_parameters = signature(func).parameters
142
+ ingestible_files = self.extract_ingestible_files()
143
+ config, default_parameters = self.parse_config()
138
144
 
139
- # If not running in Agenta, return the original function unchanged
140
- if environ.get("AGENTA_RUNTIME") != "true":
141
- return func
145
+ ### --- Middleware --- #
146
+ if not entrypoint._middleware:
147
+ entrypoint._middleware = True
142
148
 
143
- ### --- Update Middleware --- #
144
- try:
145
- global _MIDDLEWARES # pylint: disable=global-statement
146
-
147
- if _MIDDLEWARES:
148
- app.add_middleware(
149
- AuthorizationMiddleware,
150
- host=ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.host,
151
- resource_id=ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.app_id,
152
- resource_type="application",
153
- )
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
+ ### ------------------ #
154
155
 
155
- if AGENTA_USE_CORS:
156
- app.add_middleware(
157
- CORSMiddleware,
158
- allow_origins=["*"],
159
- allow_methods=["*"],
160
- allow_headers=["*"],
161
- allow_credentials=True,
162
- )
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
163
168
 
164
- _MIDDLEWARES = False
169
+ kwargs, _ = self.split_kwargs(kwargs, default_parameters)
165
170
 
166
- except: # pylint: disable=bare-except
167
- log.warning("Agenta SDK - failed to secure route: %s", route_path)
168
- ### --- Update Middleware --- #
171
+ # TODO: Why is this not used in the run_wrapper?
172
+ # self.ingest_files(kwargs, ingestible_files)
169
173
 
170
- DEFAULT_PATH = "generate"
171
- PLAYGROUND_PATH = "/playground"
172
- RUN_PATH = "/run"
173
- func_signature = signature(func)
174
- try:
175
- config = (
176
- config_schema() if config_schema else None
177
- ) # we initialize the config object to be able to use it
178
- except ValidationError as e:
179
- raise ValueError(
180
- f"Error initializing config_schema. Please ensure all required fields have default values: {str(e)}"
181
- ) from e
182
- except Exception as e:
183
- raise ValueError(
184
- f"Unexpected error initializing config_schema: {str(e)}"
185
- ) from e
186
-
187
- config_params = config.dict() if config else ag.config.all()
188
- ingestible_files = self.extract_ingestible_files(func_signature)
174
+ return await self.execute_wrapper(request, False, *args, **kwargs)
189
175
 
190
- self.route_path = route_path
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)
191
183
 
192
- ### --- Playground --- #
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 --- #
193
194
  @wraps(func)
194
- async def wrapper(*args, **kwargs) -> Any:
195
- if environ.get("AGENTA_RUNTIME") != "true":
196
- raise HTTPException(
197
- status_code=403,
198
- detail="This endpoint is only available when running in Agenta environment",
199
- )
200
- func_params, api_config_params = self.split_kwargs(kwargs, config_params)
201
- self.ingest_files(func_params, ingestible_files)
202
- if not config_schema:
203
- ag.config.set(**api_config_params)
195
+ async def test_wrapper(request: Request, *args, **kwargs) -> Any:
196
+ kwargs, parameters = self.split_kwargs(kwargs, default_parameters)
204
197
 
205
- with routing_context_manager(
206
- config=api_config_params,
207
- ):
208
- entrypoint_result = await self.execute_function(
209
- func,
210
- True, # inline trace: True
211
- *args,
212
- params=func_params,
213
- config_params=config_params,
214
- )
198
+ request.state.config["parameters"] = parameters
215
199
 
216
- return entrypoint_result
200
+ # TODO: Why is this only used in the test_wrapper?
201
+ self.ingest_files(kwargs, ingestible_files)
217
202
 
218
- self.update_function_signature(
219
- wrapper=wrapper,
220
- func_signature=func_signature,
221
- config_class=config,
222
- config_dict=config_params,
203
+ return await self.execute_wrapper(request, True, *args, **kwargs)
204
+
205
+ self.update_test_wrapper_signature(
206
+ wrapper=test_wrapper,
223
207
  ingestible_files=ingestible_files,
208
+ config_class=config,
209
+ config_dict=default_parameters,
224
210
  )
225
211
 
226
- #
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
227
218
  if route_path == "":
228
- route = f"/{DEFAULT_PATH}"
229
- app.post(route, response_model=BaseResponse)(wrapper)
230
- entrypoint.routes.append(
231
- {
232
- "func": func.__name__,
233
- "endpoint": route,
234
- "params": (
235
- {**config_params, **func_signature.parameters}
236
- if not config
237
- else func_signature.parameters
238
- ),
239
- "config": config,
240
- }
241
- )
219
+ test_route = entrypoint._legacy_generate_path
220
+ app.post(test_route, response_model=BaseResponse)(test_wrapper)
221
+ # LEGACY
242
222
 
243
- route = f"{PLAYGROUND_PATH}{RUN_PATH}{route_path}"
244
- app.post(route, response_model=BaseResponse)(wrapper)
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
+ ### ------------ #
230
+
231
+ ### --- OpenAPI --- #
232
+ test_route = f"{entrypoint._test_path}{route_path}"
245
233
  entrypoint.routes.append(
246
234
  {
247
235
  "func": func.__name__,
248
- "endpoint": route,
236
+ "endpoint": test_route,
249
237
  "params": (
250
- {**config_params, **func_signature.parameters}
238
+ {**default_parameters, **signature_parameters}
251
239
  if not config
252
- else func_signature.parameters
240
+ else signature_parameters
253
241
  ),
254
242
  "config": config,
255
243
  }
256
244
  )
257
- ### ---------------------------- #
258
-
259
- ### --- Deployed --- #
260
- @wraps(func)
261
- async def wrapper_deployed(*args, **kwargs) -> Any:
262
- if environ.get("AGENTA_RUNTIME") != "true":
263
- raise HTTPException(
264
- status_code=403,
265
- detail="This endpoint is only available when running in Agenta environment",
266
- )
267
- func_params = {
268
- k: v
269
- for k, v in kwargs.items()
270
- if k not in ["config", "environment", "app"]
271
- }
272
- if not config_schema:
273
- if "environment" in kwargs and kwargs["environment"] is not None:
274
- ag.config.pull(environment_name=kwargs["environment"])
275
- elif "config" in kwargs and kwargs["config"] is not None:
276
- ag.config.pull(config_name=kwargs["config"])
277
- else:
278
- ag.config.pull(config_name="default")
279
-
280
- app_id = environ.get("AGENTA_APP_ID")
281
-
282
- with routing_context_manager(
283
- application={
284
- "id": app_id,
285
- "slug": kwargs.get("app"),
286
- },
287
- variant={
288
- "slug": kwargs.get("config"),
289
- },
290
- environment={
291
- "slug": kwargs.get("environment"),
292
- },
293
- ):
294
- entrypoint_result = await self.execute_function(
295
- func,
296
- False, # inline trace: False
297
- *args,
298
- params=func_params,
299
- config_params=config_params,
300
- )
301
-
302
- return entrypoint_result
303
245
 
304
- self.update_deployed_function_signature(
305
- wrapper_deployed,
306
- func_signature,
307
- ingestible_files,
308
- )
246
+ # LEGACY
309
247
  if route_path == "":
310
- route_deployed = f"/{DEFAULT_PATH}_deployed"
311
- app.post(route_deployed, response_model=BaseResponse)(wrapper_deployed)
312
-
313
- route_deployed = f"{RUN_PATH}{route_path}"
314
- app.post(route_deployed, response_model=BaseResponse)(wrapper_deployed)
315
- ### ---------------- #
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
316
262
 
317
- ### --- Update OpenAPI --- #
318
263
  app.openapi_schema = None # Forces FastAPI to re-generate the schema
319
264
  openapi_schema = app.openapi()
320
265
 
321
- # Inject the current version of the SDK into the openapi_schema
322
- openapi_schema["agenta_sdk"] = {"version": helpers.get_current_version()}
323
-
324
- for route in entrypoint.routes:
266
+ for _route in entrypoint.routes:
325
267
  self.override_schema(
326
268
  openapi_schema=openapi_schema,
327
- func_name=route["func"],
328
- endpoint=route["endpoint"],
329
- params=route["params"],
269
+ func_name=_route["func"],
270
+ endpoint=_route["endpoint"],
271
+ params=_route["params"],
330
272
  )
331
- if route["config"] is not None: # new SDK version
273
+
274
+ if _route["config"] is not None: # new SDK version
332
275
  self.override_config_in_schema(
333
276
  openapi_schema=openapi_schema,
334
- func_name=route["func"],
335
- endpoint=route["endpoint"],
336
- config=route["config"],
277
+ func_name=_route["func"],
278
+ endpoint=_route["endpoint"],
279
+ config=_route["config"],
337
280
  )
281
+ ### --------------- #
338
282
 
339
- def extract_ingestible_files(
340
- self,
341
- func_signature: Signature,
342
- ) -> Dict[str, Parameter]:
283
+ def extract_ingestible_files(self) -> Dict[str, Parameter]:
343
284
  """Extract parameters annotated as InFile from function signature."""
344
285
 
345
286
  return {
346
287
  name: param
347
- for name, param in func_signature.parameters.items()
288
+ for name, param in signature(self.func).parameters.items()
348
289
  if param.annotation is InFile
349
290
  }
350
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
+
351
311
  def split_kwargs(
352
- self, kwargs: Dict[str, Any], config_params: Dict[str, Any]
312
+ self, kwargs: Dict[str, Any], default_parameters: Dict[str, Any]
353
313
  ) -> Tuple[Dict[str, Any], Dict[str, Any]]:
354
- """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}
355
316
 
356
- func_params = {k: v for k, v in kwargs.items() if k not in config_params}
357
- api_config_params = {k: v for k, v in kwargs.items() if k in config_params}
358
- return func_params, api_config_params
317
+ return arguments, parameters
359
318
 
360
- def ingest_file(self, upfile: UploadFile):
319
+ def ingest_file(
320
+ self,
321
+ upfile: UploadFile,
322
+ ):
361
323
  temp_file = NamedTemporaryFile(delete=False)
362
324
  temp_file.write(upfile.file.read())
363
325
  temp_file.close()
326
+
364
327
  return InFile(file_name=upfile.filename, file_path=temp_file.name)
365
328
 
366
329
  def ingest_files(
@@ -374,51 +337,81 @@ class entrypoint:
374
337
  if name in func_params and func_params[name] is not None:
375
338
  func_params[name] = self.ingest_file(func_params[name])
376
339
 
377
- async def execute_function(
340
+ async def execute_wrapper(
378
341
  self,
379
- func: Callable[..., Any],
380
- inline_trace,
342
+ request: Request,
343
+ inline: bool,
381
344
  *args,
382
- **func_params,
345
+ **kwargs,
383
346
  ):
384
- 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)
385
370
 
386
- tracing_context.set(routing_context.get())
371
+ return result
387
372
 
373
+ async def execute_function(
374
+ self,
375
+ inline: bool,
376
+ *args,
377
+ **kwargs,
378
+ ):
388
379
  try:
389
380
  result = (
390
- await func(*args, **func_params["params"])
391
- if iscoroutinefunction(func)
392
- else func(*args, **func_params["params"])
381
+ await self.func(*args, **kwargs)
382
+ if iscoroutinefunction(self.func)
383
+ else self.func(*args, **kwargs)
393
384
  )
394
385
 
395
- return await self.handle_success(result, inline_trace)
386
+ return await self.handle_success(result, inline)
396
387
 
397
- except Exception as error:
388
+ except Exception as error: # pylint: disable=broad-except
398
389
  self.handle_failure(error)
399
390
 
400
- async def handle_success(self, result: Any, inline_trace: bool):
391
+ async def handle_success(
392
+ self,
393
+ result: Any,
394
+ inline: bool,
395
+ ):
401
396
  data = None
402
397
  tree = None
403
398
 
404
399
  with suppress():
405
400
  data = self.patch_result(result)
406
401
 
407
- if inline_trace:
408
- tree = await self.fetch_inline_trace(inline_trace)
402
+ if inline:
403
+ tree = await self.fetch_inline_trace(inline)
409
404
 
410
- log.info(f"----------------------------------")
411
- log.info(f"Agenta SDK - exiting with success: 200")
412
- log.info(f"----------------------------------")
413
-
414
- return BaseResponse(data=data, tree=tree)
405
+ try:
406
+ return BaseResponse(data=data, tree=tree)
407
+ except:
408
+ return BaseResponse(data=data)
415
409
 
416
- def handle_failure(self, error: Exception):
417
- log.warning("--------------------------------------------------")
418
- log.warning("Agenta SDK - handling application exception below:")
419
- log.warning("--------------------------------------------------")
420
- log.warning(format_exc().strip("\n"))
421
- log.warning("--------------------------------------------------")
410
+ def handle_failure(
411
+ self,
412
+ error: Exception,
413
+ ):
414
+ display_exception("Application Exception")
422
415
 
423
416
  status_code = 500
424
417
  message = str(error)
@@ -427,7 +420,10 @@ class entrypoint:
427
420
 
428
421
  raise HTTPException(status_code=status_code, detail=detail)
429
422
 
430
- def patch_result(self, result: Any):
423
+ def patch_result(
424
+ self,
425
+ result: Any,
426
+ ):
431
427
  """
432
428
  Patch the result to only include the message if the result is a FuncResponse-style dictionary with message, cost, and usage keys.
433
429
 
@@ -464,7 +460,10 @@ class entrypoint:
464
460
 
465
461
  return data
466
462
 
467
- async def fetch_inline_trace(self, inline_trace):
463
+ async def fetch_inline_trace(
464
+ self,
465
+ inline,
466
+ ):
468
467
  WAIT_FOR_SPANS = True
469
468
  TIMEOUT = 1
470
469
  TIMESTEP = 0.1
@@ -473,12 +472,14 @@ class entrypoint:
473
472
 
474
473
  trace = None
475
474
 
476
- root_context: Dict[str, Any] = tracing_context.get().get("root")
475
+ context = tracing_context.get()
476
+
477
+ link = context.link
477
478
 
478
- trace_id = root_context.get("trace_id") if root_context else None
479
+ trace_id = link.get("tree_id") if link else None
479
480
 
480
481
  if trace_id is not None:
481
- if inline_trace:
482
+ if inline:
482
483
  if WAIT_FOR_SPANS:
483
484
  remaining_steps = NOFSTEPS
484
485
 
@@ -498,6 +499,27 @@ class entrypoint:
498
499
 
499
500
  return trace
500
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
+
501
523
  def update_wrapper_signature(
502
524
  self, wrapper: Callable[..., Any], updated_params: List
503
525
  ):
@@ -514,10 +536,9 @@ class entrypoint:
514
536
  wrapper_signature = wrapper_signature.replace(parameters=updated_params)
515
537
  wrapper.__signature__ = wrapper_signature # type: ignore
516
538
 
517
- def update_function_signature(
539
+ def update_test_wrapper_signature(
518
540
  self,
519
541
  wrapper: Callable[..., Any],
520
- func_signature: Signature,
521
542
  config_class: Type[BaseModel], # TODO: change to our type
522
543
  config_dict: Dict[str, Any],
523
544
  ingestible_files: Dict[str, Parameter],
@@ -529,19 +550,19 @@ class entrypoint:
529
550
  self.add_config_params_to_parser(updated_params, config_class)
530
551
  else:
531
552
  self.deprecated_add_config_params_to_parser(updated_params, config_dict)
532
- self.add_func_params_to_parser(updated_params, func_signature, ingestible_files)
553
+ self.add_func_params_to_parser(updated_params, ingestible_files)
533
554
  self.update_wrapper_signature(wrapper, updated_params)
555
+ self.add_request_to_signature(wrapper)
534
556
 
535
- def update_deployed_function_signature(
557
+ def update_run_wrapper_signature(
536
558
  self,
537
559
  wrapper: Callable[..., Any],
538
- func_signature: Signature,
539
560
  ingestible_files: Dict[str, Parameter],
540
561
  ) -> None:
541
562
  """Update the function signature to include new parameters."""
542
563
 
543
564
  updated_params: List[Parameter] = []
544
- self.add_func_params_to_parser(updated_params, func_signature, ingestible_files)
565
+ self.add_func_params_to_parser(updated_params, ingestible_files)
545
566
  for param in [
546
567
  "config",
547
568
  "environment",
@@ -555,6 +576,7 @@ class entrypoint:
555
576
  )
556
577
  )
557
578
  self.update_wrapper_signature(wrapper, updated_params)
579
+ self.add_request_to_signature(wrapper)
558
580
 
559
581
  def add_config_params_to_parser(
560
582
  self, updated_params: list, config_class: Type[BaseModel]
@@ -595,11 +617,10 @@ class entrypoint:
595
617
  def add_func_params_to_parser(
596
618
  self,
597
619
  updated_params: list,
598
- func_signature: Signature,
599
620
  ingestible_files: Dict[str, Parameter],
600
621
  ) -> None:
601
622
  """Add function parameters to function signature."""
602
- for name, param in func_signature.parameters.items():
623
+ for name, param in signature(self.func).parameters.items():
603
624
  if name in ingestible_files:
604
625
  updated_params.append(
605
626
  Parameter(name, param.kind, annotation=UploadFile)
@@ -621,22 +642,6 @@ class entrypoint:
621
642
  )
622
643
  )
623
644
 
624
- def is_main_script(self, func: Callable) -> bool:
625
- """
626
- Check if the script containing the function is the main script being run.
627
-
628
- Args:
629
- func (Callable): The function object to check.
630
-
631
- Returns:
632
- bool: True if the script containing the function is the main script, False otherwise.
633
-
634
- Example:
635
- if is_main_script(my_function):
636
- print("This is the main script.")
637
- """
638
- return func.__module__ == "__main__"
639
-
640
645
  def override_config_in_schema(
641
646
  self,
642
647
  openapi_schema: dict,