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.
- 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 +272 -267
- 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.0a1.dist-info → agenta-0.30.0a3.dist-info}/METADATA +3 -2
- {agenta-0.30.0a1.dist-info → agenta-0.30.0a3.dist-info}/RECORD +69 -44
- {agenta-0.30.0a1.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.0a1.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,14 +75,15 @@ class route:
|
|
|
73
75
|
|
|
74
76
|
self.route_path = path
|
|
75
77
|
|
|
76
|
-
|
|
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,
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
140
|
-
if
|
|
141
|
-
|
|
145
|
+
### --- Middleware --- #
|
|
146
|
+
if not entrypoint._middleware:
|
|
147
|
+
entrypoint._middleware = True
|
|
142
148
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
169
|
+
kwargs, _ = self.split_kwargs(kwargs, default_parameters)
|
|
165
170
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
### --- Update Middleware --- #
|
|
171
|
+
# TODO: Why is this not used in the run_wrapper?
|
|
172
|
+
# self.ingest_files(kwargs, ingestible_files)
|
|
169
173
|
|
|
170
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
195
|
-
|
|
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
|
-
|
|
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
|
-
|
|
200
|
+
# TODO: Why is this only used in the test_wrapper?
|
|
201
|
+
self.ingest_files(kwargs, ingestible_files)
|
|
217
202
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
229
|
-
app.post(
|
|
230
|
-
|
|
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
|
-
|
|
244
|
-
|
|
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":
|
|
236
|
+
"endpoint": test_route,
|
|
249
237
|
"params": (
|
|
250
|
-
{**
|
|
238
|
+
{**default_parameters, **signature_parameters}
|
|
251
239
|
if not config
|
|
252
|
-
else
|
|
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
|
-
|
|
305
|
-
wrapper_deployed,
|
|
306
|
-
func_signature,
|
|
307
|
-
ingestible_files,
|
|
308
|
-
)
|
|
246
|
+
# LEGACY
|
|
309
247
|
if route_path == "":
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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
|
-
|
|
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=
|
|
328
|
-
endpoint=
|
|
329
|
-
params=
|
|
269
|
+
func_name=_route["func"],
|
|
270
|
+
endpoint=_route["endpoint"],
|
|
271
|
+
params=_route["params"],
|
|
330
272
|
)
|
|
331
|
-
|
|
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=
|
|
335
|
-
endpoint=
|
|
336
|
-
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
|
|
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],
|
|
312
|
+
self, kwargs: Dict[str, Any], default_parameters: Dict[str, Any]
|
|
353
313
|
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
|
354
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
340
|
+
async def execute_wrapper(
|
|
378
341
|
self,
|
|
379
|
-
|
|
380
|
-
|
|
342
|
+
request: Request,
|
|
343
|
+
inline: bool,
|
|
381
344
|
*args,
|
|
382
|
-
**
|
|
345
|
+
**kwargs,
|
|
383
346
|
):
|
|
384
|
-
|
|
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
|
-
|
|
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, **
|
|
391
|
-
if iscoroutinefunction(func)
|
|
392
|
-
else func(*args, **
|
|
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,
|
|
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(
|
|
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
|
|
408
|
-
tree = await self.fetch_inline_trace(
|
|
402
|
+
if inline:
|
|
403
|
+
tree = await self.fetch_inline_trace(inline)
|
|
409
404
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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(
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
475
|
+
context = tracing_context.get()
|
|
476
|
+
|
|
477
|
+
link = context.link
|
|
477
478
|
|
|
478
|
-
trace_id =
|
|
479
|
+
trace_id = link.get("tree_id") if link else None
|
|
479
480
|
|
|
480
481
|
if trace_id is not None:
|
|
481
|
-
if
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|