agenta 0.30.0a2__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.
- agenta/__init__.py +1 -0
- agenta/client/backend/__init__.py +32 -3
- agenta/client/backend/access_control/__init__.py +1 -0
- agenta/client/backend/access_control/client.py +167 -0
- agenta/client/backend/apps/client.py +70 -10
- agenta/client/backend/client.py +61 -45
- agenta/client/backend/configs/client.py +6 -0
- agenta/client/backend/containers/client.py +6 -0
- agenta/client/backend/core/file.py +13 -8
- agenta/client/backend/environments/client.py +6 -0
- agenta/client/backend/evaluations/client.py +14 -1
- agenta/client/backend/evaluators/client.py +24 -0
- agenta/client/backend/observability/client.py +22 -16
- agenta/client/backend/observability_v_1/__init__.py +2 -2
- agenta/client/backend/observability_v_1/client.py +203 -0
- agenta/client/backend/observability_v_1/types/__init__.py +2 -1
- agenta/client/backend/observability_v_1/types/format.py +1 -1
- agenta/client/backend/observability_v_1/types/query_analytics_response.py +7 -0
- agenta/client/backend/scopes/__init__.py +1 -0
- agenta/client/backend/scopes/client.py +114 -0
- agenta/client/backend/testsets/client.py +305 -121
- agenta/client/backend/types/__init__.py +24 -2
- agenta/client/backend/types/analytics_response.py +24 -0
- agenta/client/backend/types/app.py +2 -1
- agenta/client/backend/types/body_import_testset.py +0 -1
- agenta/client/backend/types/bucket_dto.py +26 -0
- agenta/client/backend/types/header_dto.py +22 -0
- agenta/client/backend/types/legacy_analytics_response.py +29 -0
- agenta/client/backend/types/legacy_data_point.py +27 -0
- agenta/client/backend/types/metrics_dto.py +24 -0
- agenta/client/backend/types/permission.py +1 -0
- agenta/client/backend/types/projects_response.py +28 -0
- agenta/client/backend/types/provider_key_dto.py +23 -0
- agenta/client/backend/types/provider_kind.py +21 -0
- agenta/client/backend/types/secret_dto.py +24 -0
- agenta/client/backend/types/secret_kind.py +5 -0
- agenta/client/backend/types/secret_response_dto.py +27 -0
- agenta/client/backend/variants/client.py +66 -0
- agenta/client/backend/vault/__init__.py +1 -0
- agenta/client/backend/vault/client.py +685 -0
- agenta/client/client.py +1 -1
- agenta/sdk/__init__.py +1 -0
- agenta/sdk/agenta_init.py +47 -118
- agenta/sdk/assets.py +57 -46
- agenta/sdk/context/exporting.py +25 -0
- agenta/sdk/context/routing.py +12 -12
- agenta/sdk/context/tracing.py +26 -1
- agenta/sdk/decorators/routing.py +279 -243
- agenta/sdk/decorators/tracing.py +53 -31
- agenta/sdk/managers/config.py +8 -118
- agenta/sdk/managers/secrets.py +38 -0
- agenta/sdk/middleware/auth.py +128 -93
- agenta/sdk/middleware/cache.py +4 -0
- agenta/sdk/middleware/config.py +254 -0
- agenta/sdk/middleware/cors.py +27 -0
- agenta/sdk/middleware/otel.py +40 -0
- agenta/sdk/middleware/vault.py +158 -0
- agenta/sdk/tracing/exporters.py +40 -2
- agenta/sdk/tracing/inline.py +2 -2
- agenta/sdk/tracing/processors.py +11 -3
- agenta/sdk/tracing/tracing.py +14 -12
- agenta/sdk/utils/constants.py +1 -0
- agenta/sdk/utils/exceptions.py +20 -19
- agenta/sdk/utils/globals.py +4 -8
- agenta/sdk/utils/timing.py +58 -0
- {agenta-0.30.0a2.dist-info → agenta-0.30.0a3.dist-info}/METADATA +3 -2
- {agenta-0.30.0a2.dist-info → agenta-0.30.0a3.dist-info}/RECORD +69 -44
- {agenta-0.30.0a2.dist-info → agenta-0.30.0a3.dist-info}/WHEEL +1 -1
- agenta/client/backend/types/lm_providers_enum.py +0 -21
- agenta/sdk/tracing/context.py +0 -24
- {agenta-0.30.0a2.dist-info → agenta-0.30.0a3.dist-info}/entry_points.txt +0 -0
agenta/sdk/decorators/routing.py
CHANGED
|
@@ -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
|
|
9
|
-
from
|
|
10
|
-
|
|
4
|
+
from traceback import format_exception
|
|
5
|
+
from asyncio import sleep
|
|
6
|
+
|
|
11
7
|
from tempfile import NamedTemporaryFile
|
|
12
|
-
from
|
|
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
|
|
15
|
-
from
|
|
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.
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
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__(
|
|
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,
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
145
|
+
### --- Middleware --- #
|
|
146
|
+
if not entrypoint._middleware:
|
|
147
|
+
entrypoint._middleware = True
|
|
144
148
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
169
|
+
kwargs, _ = self.split_kwargs(kwargs, default_parameters)
|
|
170
170
|
|
|
171
|
-
|
|
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
|
|
174
|
-
|
|
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
|
-
|
|
198
|
+
request.state.config["parameters"] = parameters
|
|
191
199
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
|
|
203
|
-
app.post(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
218
|
-
|
|
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":
|
|
236
|
+
"endpoint": test_route,
|
|
223
237
|
"params": (
|
|
224
|
-
{**
|
|
238
|
+
{**default_parameters, **signature_parameters}
|
|
225
239
|
if not config
|
|
226
|
-
else
|
|
240
|
+
else signature_parameters
|
|
227
241
|
),
|
|
228
242
|
"config": config,
|
|
229
243
|
}
|
|
230
244
|
)
|
|
231
|
-
### ---------------------------- #
|
|
232
245
|
|
|
233
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
-
|
|
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=
|
|
297
|
-
endpoint=
|
|
298
|
-
params=
|
|
269
|
+
func_name=_route["func"],
|
|
270
|
+
endpoint=_route["endpoint"],
|
|
271
|
+
params=_route["params"],
|
|
299
272
|
)
|
|
300
|
-
|
|
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=
|
|
304
|
-
endpoint=
|
|
305
|
-
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
|
|
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],
|
|
312
|
+
self, kwargs: Dict[str, Any], default_parameters: Dict[str, Any]
|
|
322
313
|
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
|
323
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
340
|
+
async def execute_wrapper(
|
|
347
341
|
self,
|
|
348
|
-
|
|
349
|
-
|
|
342
|
+
request: Request,
|
|
343
|
+
inline: bool,
|
|
350
344
|
*args,
|
|
351
|
-
**
|
|
345
|
+
**kwargs,
|
|
352
346
|
):
|
|
353
|
-
|
|
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
|
-
|
|
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, **
|
|
360
|
-
if iscoroutinefunction(func)
|
|
361
|
-
else func(*args, **
|
|
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,
|
|
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(
|
|
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
|
|
377
|
-
tree = await self.fetch_inline_trace(
|
|
402
|
+
if inline:
|
|
403
|
+
tree = await self.fetch_inline_trace(inline)
|
|
378
404
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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(
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
475
|
+
context = tracing_context.get()
|
|
476
|
+
|
|
477
|
+
link = context.link
|
|
446
478
|
|
|
447
|
-
trace_id =
|
|
479
|
+
trace_id = link.get("tree_id") if link else None
|
|
448
480
|
|
|
449
481
|
if trace_id is not None:
|
|
450
|
-
if
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|