agenta 0.27.3__py3-none-any.whl → 0.27.4a1__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/client/backend/__init__.py +63 -0
- agenta/client/backend/client.py +22 -22
- agenta/client/backend/core/http_client.py +15 -7
- agenta/client/backend/observability/client.py +4 -4
- agenta/client/backend/observability_v_1/__init__.py +5 -0
- agenta/client/backend/observability_v_1/client.py +560 -0
- agenta/client/backend/observability_v_1/types/__init__.py +6 -0
- agenta/client/backend/observability_v_1/types/format.py +5 -0
- agenta/client/backend/observability_v_1/types/query_traces_response.py +11 -0
- agenta/client/backend/types/__init__.py +58 -0
- agenta/client/backend/types/agenta_node_dto.py +48 -0
- agenta/client/backend/types/agenta_node_dto_nodes_value.py +6 -0
- agenta/client/backend/types/agenta_nodes_response.py +30 -0
- agenta/client/backend/types/agenta_root_dto.py +30 -0
- agenta/client/backend/types/agenta_roots_response.py +30 -0
- agenta/client/backend/types/agenta_tree_dto.py +30 -0
- agenta/client/backend/types/agenta_trees_response.py +30 -0
- agenta/client/backend/types/collect_status_response.py +22 -0
- agenta/client/backend/types/exception_dto.py +26 -0
- agenta/client/backend/types/link_dto.py +24 -0
- agenta/client/backend/types/node_dto.py +24 -0
- agenta/client/backend/types/node_type.py +19 -0
- agenta/client/backend/types/o_tel_context_dto.py +22 -0
- agenta/client/backend/types/o_tel_event_dto.py +23 -0
- agenta/client/backend/types/o_tel_extra_dto.py +26 -0
- agenta/client/backend/types/o_tel_link_dto.py +23 -0
- agenta/client/backend/types/o_tel_span_dto.py +37 -0
- agenta/client/backend/types/o_tel_span_kind.py +15 -0
- agenta/client/backend/types/o_tel_spans_response.py +24 -0
- agenta/client/backend/types/o_tel_status_code.py +8 -0
- agenta/client/backend/types/parent_dto.py +21 -0
- agenta/client/backend/types/root_dto.py +21 -0
- agenta/client/backend/types/span_dto.py +54 -0
- agenta/client/backend/types/span_dto_nodes_value.py +9 -0
- agenta/client/backend/types/status_code.py +5 -0
- agenta/client/backend/types/status_dto.py +23 -0
- agenta/client/backend/types/time_dto.py +23 -0
- agenta/client/backend/types/tree_dto.py +23 -0
- agenta/client/backend/types/tree_type.py +5 -0
- agenta/client/backend/variants/client.py +24 -16
- agenta/sdk/__init__.py +2 -0
- agenta/sdk/decorators/routing.py +28 -4
- agenta/sdk/middleware/__init__.py +0 -0
- agenta/sdk/middleware/auth.py +136 -0
- agenta/sdk/middleware/cache.py +43 -0
- agenta/sdk/tracing/exporters.py +1 -1
- agenta/sdk/tracing/inline.py +29 -140
- agenta/sdk/tracing/processors.py +1 -1
- agenta/sdk/types.py +5 -2
- {agenta-0.27.3.dist-info → agenta-0.27.4a1.dist-info}/METADATA +1 -1
- {agenta-0.27.3.dist-info → agenta-0.27.4a1.dist-info}/RECORD +53 -16
- {agenta-0.27.3.dist-info → agenta-0.27.4a1.dist-info}/WHEEL +0 -0
- {agenta-0.27.3.dist-info → agenta-0.27.4a1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
from ..core.pydantic_utilities import UniversalBaseModel
|
|
4
|
+
from ..core.pydantic_utilities import IS_PYDANTIC_V2
|
|
5
|
+
import typing
|
|
6
|
+
import pydantic
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ParentDto(UniversalBaseModel):
|
|
10
|
+
id: str
|
|
11
|
+
|
|
12
|
+
if IS_PYDANTIC_V2:
|
|
13
|
+
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
|
|
14
|
+
extra="allow", frozen=True
|
|
15
|
+
) # type: ignore # Pydantic v2
|
|
16
|
+
else:
|
|
17
|
+
|
|
18
|
+
class Config:
|
|
19
|
+
frozen = True
|
|
20
|
+
smart_union = True
|
|
21
|
+
extra = pydantic.Extra.allow
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
from ..core.pydantic_utilities import UniversalBaseModel
|
|
4
|
+
from ..core.pydantic_utilities import IS_PYDANTIC_V2
|
|
5
|
+
import typing
|
|
6
|
+
import pydantic
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RootDto(UniversalBaseModel):
|
|
10
|
+
id: str
|
|
11
|
+
|
|
12
|
+
if IS_PYDANTIC_V2:
|
|
13
|
+
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
|
|
14
|
+
extra="allow", frozen=True
|
|
15
|
+
) # type: ignore # Pydantic v2
|
|
16
|
+
else:
|
|
17
|
+
|
|
18
|
+
class Config:
|
|
19
|
+
frozen = True
|
|
20
|
+
smart_union = True
|
|
21
|
+
extra = pydantic.Extra.allow
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from ..core.pydantic_utilities import UniversalBaseModel
|
|
5
|
+
import typing
|
|
6
|
+
from .lifecycle_dto import LifecycleDto
|
|
7
|
+
from .root_dto import RootDto
|
|
8
|
+
from .tree_dto import TreeDto
|
|
9
|
+
from .node_dto import NodeDto
|
|
10
|
+
from .parent_dto import ParentDto
|
|
11
|
+
from .time_dto import TimeDto
|
|
12
|
+
from .status_dto import StatusDto
|
|
13
|
+
from .exception_dto import ExceptionDto
|
|
14
|
+
from .link_dto import LinkDto
|
|
15
|
+
from .o_tel_extra_dto import OTelExtraDto
|
|
16
|
+
from ..core.pydantic_utilities import IS_PYDANTIC_V2
|
|
17
|
+
import pydantic
|
|
18
|
+
from ..core.pydantic_utilities import update_forward_refs
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SpanDto(UniversalBaseModel):
|
|
22
|
+
lifecycle: typing.Optional[LifecycleDto] = None
|
|
23
|
+
root: RootDto
|
|
24
|
+
tree: TreeDto
|
|
25
|
+
node: NodeDto
|
|
26
|
+
parent: typing.Optional[ParentDto] = None
|
|
27
|
+
time: TimeDto
|
|
28
|
+
status: StatusDto
|
|
29
|
+
exception: typing.Optional[ExceptionDto] = None
|
|
30
|
+
data: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None
|
|
31
|
+
metrics: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None
|
|
32
|
+
meta: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None
|
|
33
|
+
refs: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = None
|
|
34
|
+
links: typing.Optional[typing.List[LinkDto]] = None
|
|
35
|
+
otel: typing.Optional[OTelExtraDto] = None
|
|
36
|
+
nodes: typing.Optional[
|
|
37
|
+
typing.Dict[str, typing.Optional["SpanDtoNodesValue"]]
|
|
38
|
+
] = None
|
|
39
|
+
|
|
40
|
+
if IS_PYDANTIC_V2:
|
|
41
|
+
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
|
|
42
|
+
extra="allow", frozen=True
|
|
43
|
+
) # type: ignore # Pydantic v2
|
|
44
|
+
else:
|
|
45
|
+
|
|
46
|
+
class Config:
|
|
47
|
+
frozen = True
|
|
48
|
+
smart_union = True
|
|
49
|
+
extra = pydantic.Extra.allow
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
from .span_dto_nodes_value import SpanDtoNodesValue # noqa: E402
|
|
53
|
+
|
|
54
|
+
update_forward_refs(SpanDto)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import typing
|
|
5
|
+
import typing
|
|
6
|
+
|
|
7
|
+
if typing.TYPE_CHECKING:
|
|
8
|
+
from .span_dto import SpanDto
|
|
9
|
+
SpanDtoNodesValue = typing.Union["SpanDto", typing.List["SpanDto"]]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
from ..core.pydantic_utilities import UniversalBaseModel
|
|
4
|
+
from .status_code import StatusCode
|
|
5
|
+
import typing
|
|
6
|
+
from ..core.pydantic_utilities import IS_PYDANTIC_V2
|
|
7
|
+
import pydantic
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StatusDto(UniversalBaseModel):
|
|
11
|
+
code: StatusCode
|
|
12
|
+
message: typing.Optional[str] = None
|
|
13
|
+
|
|
14
|
+
if IS_PYDANTIC_V2:
|
|
15
|
+
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
|
|
16
|
+
extra="allow", frozen=True
|
|
17
|
+
) # type: ignore # Pydantic v2
|
|
18
|
+
else:
|
|
19
|
+
|
|
20
|
+
class Config:
|
|
21
|
+
frozen = True
|
|
22
|
+
smart_union = True
|
|
23
|
+
extra = pydantic.Extra.allow
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
from ..core.pydantic_utilities import UniversalBaseModel
|
|
4
|
+
import datetime as dt
|
|
5
|
+
from ..core.pydantic_utilities import IS_PYDANTIC_V2
|
|
6
|
+
import typing
|
|
7
|
+
import pydantic
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TimeDto(UniversalBaseModel):
|
|
11
|
+
start: dt.datetime
|
|
12
|
+
end: dt.datetime
|
|
13
|
+
|
|
14
|
+
if IS_PYDANTIC_V2:
|
|
15
|
+
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
|
|
16
|
+
extra="allow", frozen=True
|
|
17
|
+
) # type: ignore # Pydantic v2
|
|
18
|
+
else:
|
|
19
|
+
|
|
20
|
+
class Config:
|
|
21
|
+
frozen = True
|
|
22
|
+
smart_union = True
|
|
23
|
+
extra = pydantic.Extra.allow
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
from ..core.pydantic_utilities import UniversalBaseModel
|
|
4
|
+
import typing
|
|
5
|
+
from .tree_type import TreeType
|
|
6
|
+
from ..core.pydantic_utilities import IS_PYDANTIC_V2
|
|
7
|
+
import pydantic
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TreeDto(UniversalBaseModel):
|
|
11
|
+
id: str
|
|
12
|
+
type: typing.Optional[TreeType] = None
|
|
13
|
+
|
|
14
|
+
if IS_PYDANTIC_V2:
|
|
15
|
+
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
|
|
16
|
+
extra="allow", frozen=True
|
|
17
|
+
) # type: ignore # Pydantic v2
|
|
18
|
+
else:
|
|
19
|
+
|
|
20
|
+
class Config:
|
|
21
|
+
frozen = True
|
|
22
|
+
smart_union = True
|
|
23
|
+
extra = pydantic.Extra.allow
|
|
@@ -1104,14 +1104,14 @@ class VariantsClient:
|
|
|
1104
1104
|
def configs_delete(
|
|
1105
1105
|
self,
|
|
1106
1106
|
*,
|
|
1107
|
-
variant_ref:
|
|
1107
|
+
variant_ref: ReferenceRequestModel,
|
|
1108
1108
|
application_ref: typing.Optional[ReferenceRequestModel] = OMIT,
|
|
1109
1109
|
request_options: typing.Optional[RequestOptions] = None,
|
|
1110
1110
|
) -> int:
|
|
1111
1111
|
"""
|
|
1112
1112
|
Parameters
|
|
1113
1113
|
----------
|
|
1114
|
-
variant_ref :
|
|
1114
|
+
variant_ref : ReferenceRequestModel
|
|
1115
1115
|
|
|
1116
1116
|
application_ref : typing.Optional[ReferenceRequestModel]
|
|
1117
1117
|
|
|
@@ -1125,13 +1125,15 @@ class VariantsClient:
|
|
|
1125
1125
|
|
|
1126
1126
|
Examples
|
|
1127
1127
|
--------
|
|
1128
|
-
from agenta import AgentaApi
|
|
1128
|
+
from agenta import AgentaApi, ReferenceRequestModel
|
|
1129
1129
|
|
|
1130
1130
|
client = AgentaApi(
|
|
1131
1131
|
api_key="YOUR_API_KEY",
|
|
1132
1132
|
base_url="https://yourhost.com/path/to/api",
|
|
1133
1133
|
)
|
|
1134
|
-
client.variants.configs_delete(
|
|
1134
|
+
client.variants.configs_delete(
|
|
1135
|
+
variant_ref=ReferenceRequestModel(),
|
|
1136
|
+
)
|
|
1135
1137
|
"""
|
|
1136
1138
|
_response = self._client_wrapper.httpx_client.request(
|
|
1137
1139
|
"variants/configs/delete",
|
|
@@ -1244,14 +1246,14 @@ class VariantsClient:
|
|
|
1244
1246
|
def configs_history(
|
|
1245
1247
|
self,
|
|
1246
1248
|
*,
|
|
1247
|
-
variant_ref:
|
|
1249
|
+
variant_ref: ReferenceRequestModel,
|
|
1248
1250
|
application_ref: typing.Optional[ReferenceRequestModel] = OMIT,
|
|
1249
1251
|
request_options: typing.Optional[RequestOptions] = None,
|
|
1250
1252
|
) -> typing.List[ConfigResponseModel]:
|
|
1251
1253
|
"""
|
|
1252
1254
|
Parameters
|
|
1253
1255
|
----------
|
|
1254
|
-
variant_ref :
|
|
1256
|
+
variant_ref : ReferenceRequestModel
|
|
1255
1257
|
|
|
1256
1258
|
application_ref : typing.Optional[ReferenceRequestModel]
|
|
1257
1259
|
|
|
@@ -1265,13 +1267,15 @@ class VariantsClient:
|
|
|
1265
1267
|
|
|
1266
1268
|
Examples
|
|
1267
1269
|
--------
|
|
1268
|
-
from agenta import AgentaApi
|
|
1270
|
+
from agenta import AgentaApi, ReferenceRequestModel
|
|
1269
1271
|
|
|
1270
1272
|
client = AgentaApi(
|
|
1271
1273
|
api_key="YOUR_API_KEY",
|
|
1272
1274
|
base_url="https://yourhost.com/path/to/api",
|
|
1273
1275
|
)
|
|
1274
|
-
client.variants.configs_history(
|
|
1276
|
+
client.variants.configs_history(
|
|
1277
|
+
variant_ref=ReferenceRequestModel(),
|
|
1278
|
+
)
|
|
1275
1279
|
"""
|
|
1276
1280
|
_response = self._client_wrapper.httpx_client.request(
|
|
1277
1281
|
"variants/configs/history",
|
|
@@ -2504,14 +2508,14 @@ class AsyncVariantsClient:
|
|
|
2504
2508
|
async def configs_delete(
|
|
2505
2509
|
self,
|
|
2506
2510
|
*,
|
|
2507
|
-
variant_ref:
|
|
2511
|
+
variant_ref: ReferenceRequestModel,
|
|
2508
2512
|
application_ref: typing.Optional[ReferenceRequestModel] = OMIT,
|
|
2509
2513
|
request_options: typing.Optional[RequestOptions] = None,
|
|
2510
2514
|
) -> int:
|
|
2511
2515
|
"""
|
|
2512
2516
|
Parameters
|
|
2513
2517
|
----------
|
|
2514
|
-
variant_ref :
|
|
2518
|
+
variant_ref : ReferenceRequestModel
|
|
2515
2519
|
|
|
2516
2520
|
application_ref : typing.Optional[ReferenceRequestModel]
|
|
2517
2521
|
|
|
@@ -2527,7 +2531,7 @@ class AsyncVariantsClient:
|
|
|
2527
2531
|
--------
|
|
2528
2532
|
import asyncio
|
|
2529
2533
|
|
|
2530
|
-
from agenta import AsyncAgentaApi
|
|
2534
|
+
from agenta import AsyncAgentaApi, ReferenceRequestModel
|
|
2531
2535
|
|
|
2532
2536
|
client = AsyncAgentaApi(
|
|
2533
2537
|
api_key="YOUR_API_KEY",
|
|
@@ -2536,7 +2540,9 @@ class AsyncVariantsClient:
|
|
|
2536
2540
|
|
|
2537
2541
|
|
|
2538
2542
|
async def main() -> None:
|
|
2539
|
-
await client.variants.configs_delete(
|
|
2543
|
+
await client.variants.configs_delete(
|
|
2544
|
+
variant_ref=ReferenceRequestModel(),
|
|
2545
|
+
)
|
|
2540
2546
|
|
|
2541
2547
|
|
|
2542
2548
|
asyncio.run(main())
|
|
@@ -2660,14 +2666,14 @@ class AsyncVariantsClient:
|
|
|
2660
2666
|
async def configs_history(
|
|
2661
2667
|
self,
|
|
2662
2668
|
*,
|
|
2663
|
-
variant_ref:
|
|
2669
|
+
variant_ref: ReferenceRequestModel,
|
|
2664
2670
|
application_ref: typing.Optional[ReferenceRequestModel] = OMIT,
|
|
2665
2671
|
request_options: typing.Optional[RequestOptions] = None,
|
|
2666
2672
|
) -> typing.List[ConfigResponseModel]:
|
|
2667
2673
|
"""
|
|
2668
2674
|
Parameters
|
|
2669
2675
|
----------
|
|
2670
|
-
variant_ref :
|
|
2676
|
+
variant_ref : ReferenceRequestModel
|
|
2671
2677
|
|
|
2672
2678
|
application_ref : typing.Optional[ReferenceRequestModel]
|
|
2673
2679
|
|
|
@@ -2683,7 +2689,7 @@ class AsyncVariantsClient:
|
|
|
2683
2689
|
--------
|
|
2684
2690
|
import asyncio
|
|
2685
2691
|
|
|
2686
|
-
from agenta import AsyncAgentaApi
|
|
2692
|
+
from agenta import AsyncAgentaApi, ReferenceRequestModel
|
|
2687
2693
|
|
|
2688
2694
|
client = AsyncAgentaApi(
|
|
2689
2695
|
api_key="YOUR_API_KEY",
|
|
@@ -2692,7 +2698,9 @@ class AsyncVariantsClient:
|
|
|
2692
2698
|
|
|
2693
2699
|
|
|
2694
2700
|
async def main() -> None:
|
|
2695
|
-
await client.variants.configs_history(
|
|
2701
|
+
await client.variants.configs_history(
|
|
2702
|
+
variant_ref=ReferenceRequestModel(),
|
|
2703
|
+
)
|
|
2696
2704
|
|
|
2697
2705
|
|
|
2698
2706
|
asyncio.run(main())
|
agenta/sdk/__init__.py
CHANGED
agenta/sdk/decorators/routing.py
CHANGED
|
@@ -14,9 +14,10 @@ from os import environ
|
|
|
14
14
|
from fastapi.middleware.cors import CORSMiddleware
|
|
15
15
|
from fastapi import Body, FastAPI, UploadFile, HTTPException
|
|
16
16
|
|
|
17
|
+
from agenta.sdk.middleware.auth import AuthorizationMiddleware
|
|
17
18
|
from agenta.sdk.context.routing import routing_context_manager, routing_context
|
|
18
19
|
from agenta.sdk.context.tracing import tracing_context
|
|
19
|
-
from agenta.sdk.router import router
|
|
20
|
+
from agenta.sdk.router import router
|
|
20
21
|
from agenta.sdk.utils.exceptions import suppress
|
|
21
22
|
from agenta.sdk.utils.logging import log
|
|
22
23
|
from agenta.sdk.types import (
|
|
@@ -50,6 +51,9 @@ app.add_middleware(
|
|
|
50
51
|
allow_headers=["*"],
|
|
51
52
|
)
|
|
52
53
|
|
|
54
|
+
_MIDDLEWARES = True
|
|
55
|
+
|
|
56
|
+
|
|
53
57
|
app.include_router(router, prefix="")
|
|
54
58
|
|
|
55
59
|
|
|
@@ -121,6 +125,26 @@ class entrypoint:
|
|
|
121
125
|
route_path="",
|
|
122
126
|
config_schema: Optional[BaseModel] = None,
|
|
123
127
|
):
|
|
128
|
+
### --- Update Middleware --- #
|
|
129
|
+
try:
|
|
130
|
+
global _MIDDLEWARES # pylint: disable=global-statement
|
|
131
|
+
|
|
132
|
+
if _MIDDLEWARES:
|
|
133
|
+
app.add_middleware(
|
|
134
|
+
AuthorizationMiddleware,
|
|
135
|
+
host=ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.host,
|
|
136
|
+
resource_id=ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.app_id,
|
|
137
|
+
resource_type="application",
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
_MIDDLEWARES = False
|
|
141
|
+
|
|
142
|
+
except: # pylint: disable=bare-except
|
|
143
|
+
log.error("------------------------------------")
|
|
144
|
+
log.error("Agenta SDK - failed to secure route: %s", route_path)
|
|
145
|
+
log.error("------------------------------------")
|
|
146
|
+
### --- Update Middleware --- #
|
|
147
|
+
|
|
124
148
|
DEFAULT_PATH = "generate"
|
|
125
149
|
PLAYGROUND_PATH = "/playground"
|
|
126
150
|
RUN_PATH = "/run"
|
|
@@ -330,9 +354,9 @@ class entrypoint:
|
|
|
330
354
|
*args,
|
|
331
355
|
**func_params,
|
|
332
356
|
):
|
|
333
|
-
log.info(
|
|
357
|
+
log.info("---------------------------")
|
|
334
358
|
log.info(f"Agenta SDK - running route: {repr(self.route_path or '/')}")
|
|
335
|
-
log.info(
|
|
359
|
+
log.info("---------------------------")
|
|
336
360
|
|
|
337
361
|
tracing_context.set(routing_context.get())
|
|
338
362
|
|
|
@@ -362,7 +386,7 @@ class entrypoint:
|
|
|
362
386
|
log.info(f"Agenta SDK - exiting with success: 200")
|
|
363
387
|
log.info(f"----------------------------------")
|
|
364
388
|
|
|
365
|
-
return BaseResponse(data=data,
|
|
389
|
+
return BaseResponse(data=data, tree=trace)
|
|
366
390
|
|
|
367
391
|
def handle_failure(self, error: Exception):
|
|
368
392
|
log.error("--------------------------------------------------")
|
|
File without changes
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from typing import Callable, Optional
|
|
2
|
+
from os import environ
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
from json import dumps
|
|
5
|
+
from traceback import format_exc
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
9
|
+
from fastapi import FastAPI, Request, Response
|
|
10
|
+
|
|
11
|
+
from agenta.sdk.utils.logging import log
|
|
12
|
+
from agenta.sdk.middleware.cache import TTLLRUCache
|
|
13
|
+
|
|
14
|
+
AGENTA_SDK_AUTH_CACHE_CAPACITY = environ.get(
|
|
15
|
+
"AGENTA_SDK_AUTH_CACHE_CAPACITY",
|
|
16
|
+
512,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
AGENTA_SDK_AUTH_CACHE_TTL = environ.get(
|
|
20
|
+
"AGENTA_SDK_AUTH_CACHE_TTL",
|
|
21
|
+
15 * 60, # 15 minutes
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
AGENTA_UNAUTHORIZED_EXECUTION_ALLOWED = str(
|
|
25
|
+
environ.get("AGENTA_UNAUTHORIZED_EXECUTION_ALLOWED", False)
|
|
26
|
+
).lower() in ("true", "1", "t")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Deny(Response):
|
|
30
|
+
def __init__(self) -> None:
|
|
31
|
+
super().__init__(status_code=401, content="Unauthorized")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
cache = TTLLRUCache(
|
|
35
|
+
capacity=AGENTA_SDK_AUTH_CACHE_CAPACITY,
|
|
36
|
+
ttl=AGENTA_SDK_AUTH_CACHE_TTL,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AuthorizationMiddleware(BaseHTTPMiddleware):
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
app: FastAPI,
|
|
44
|
+
host: str,
|
|
45
|
+
resource_id: UUID,
|
|
46
|
+
resource_type: str,
|
|
47
|
+
):
|
|
48
|
+
super().__init__(app)
|
|
49
|
+
|
|
50
|
+
self.host = host
|
|
51
|
+
self.resource_id = resource_id
|
|
52
|
+
self.resource_type = resource_type
|
|
53
|
+
|
|
54
|
+
async def dispatch(
|
|
55
|
+
self,
|
|
56
|
+
request: Request,
|
|
57
|
+
call_next: Callable,
|
|
58
|
+
project_id: Optional[UUID] = None,
|
|
59
|
+
):
|
|
60
|
+
if AGENTA_UNAUTHORIZED_EXECUTION_ALLOWED:
|
|
61
|
+
return await call_next(request)
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
authorization = (
|
|
65
|
+
request.headers.get("Authorization")
|
|
66
|
+
or request.headers.get("authorization")
|
|
67
|
+
or None
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
headers = {"Authorization": authorization} if authorization else None
|
|
71
|
+
|
|
72
|
+
cookies = {"sAccessToken": request.cookies.get("sAccessToken")}
|
|
73
|
+
|
|
74
|
+
params = {
|
|
75
|
+
"action": "run_service",
|
|
76
|
+
"resource_type": self.resource_type,
|
|
77
|
+
"resource_id": self.resource_id,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if project_id:
|
|
81
|
+
params["project_id"] = project_id
|
|
82
|
+
|
|
83
|
+
_hash = dumps(
|
|
84
|
+
{
|
|
85
|
+
"headers": headers,
|
|
86
|
+
"cookies": cookies,
|
|
87
|
+
"params": params,
|
|
88
|
+
},
|
|
89
|
+
sort_keys=True,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
cached_policy = cache.get(_hash)
|
|
93
|
+
|
|
94
|
+
if not cached_policy:
|
|
95
|
+
async with httpx.AsyncClient() as client:
|
|
96
|
+
response = await client.get(
|
|
97
|
+
f"{self.host}/api/permissions/verify",
|
|
98
|
+
headers=headers,
|
|
99
|
+
cookies=cookies,
|
|
100
|
+
params=params,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if response.status_code != 200:
|
|
104
|
+
cache.put(_hash, {"effect": "deny"})
|
|
105
|
+
return Deny()
|
|
106
|
+
|
|
107
|
+
auth = response.json()
|
|
108
|
+
|
|
109
|
+
if auth.get("effect") != "allow":
|
|
110
|
+
cache.put(_hash, {"effect": "deny"})
|
|
111
|
+
return Deny()
|
|
112
|
+
|
|
113
|
+
cached_policy = {
|
|
114
|
+
"effect": "allow",
|
|
115
|
+
"credentials": auth.get("credentials"),
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
cache.put(_hash, cached_policy)
|
|
119
|
+
|
|
120
|
+
if cached_policy.get("effect") == "deny":
|
|
121
|
+
return Deny()
|
|
122
|
+
|
|
123
|
+
request.state.credentials = cached_policy.get("credentials")
|
|
124
|
+
|
|
125
|
+
print(f"credentials: {request.state.credentials}")
|
|
126
|
+
|
|
127
|
+
return await call_next(request)
|
|
128
|
+
|
|
129
|
+
except: # pylint: disable=bare-except
|
|
130
|
+
log.error("------------------------------------------------------")
|
|
131
|
+
log.error("Agenta SDK - handling auth middleware exception below:")
|
|
132
|
+
log.error("------------------------------------------------------")
|
|
133
|
+
log.error(format_exc().strip("\n"))
|
|
134
|
+
log.error("------------------------------------------------------")
|
|
135
|
+
|
|
136
|
+
return Deny()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from time import time
|
|
2
|
+
from collections import OrderedDict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TTLLRUCache:
|
|
6
|
+
def __init__(self, capacity: int, ttl: int):
|
|
7
|
+
self.cache = OrderedDict()
|
|
8
|
+
self.capacity = capacity
|
|
9
|
+
self.ttl = ttl
|
|
10
|
+
|
|
11
|
+
def get(self, key):
|
|
12
|
+
# CACHE
|
|
13
|
+
if key not in self.cache:
|
|
14
|
+
return None
|
|
15
|
+
|
|
16
|
+
value, expiry = self.cache[key]
|
|
17
|
+
# -----
|
|
18
|
+
|
|
19
|
+
# TTL
|
|
20
|
+
if time() > expiry:
|
|
21
|
+
del self.cache[key]
|
|
22
|
+
|
|
23
|
+
return None
|
|
24
|
+
# ---
|
|
25
|
+
|
|
26
|
+
# LRU
|
|
27
|
+
self.cache.move_to_end(key)
|
|
28
|
+
# ---
|
|
29
|
+
|
|
30
|
+
return value
|
|
31
|
+
|
|
32
|
+
def put(self, key, value):
|
|
33
|
+
# CACHE
|
|
34
|
+
if key in self.cache:
|
|
35
|
+
del self.cache[key]
|
|
36
|
+
# CACHE & LRU
|
|
37
|
+
elif len(self.cache) >= self.capacity:
|
|
38
|
+
self.cache.popitem(last=False)
|
|
39
|
+
# -----------
|
|
40
|
+
|
|
41
|
+
# TTL
|
|
42
|
+
self.cache[key] = (value, time() + self.ttl)
|
|
43
|
+
# ---
|
agenta/sdk/tracing/exporters.py
CHANGED
|
@@ -58,7 +58,7 @@ class InlineTraceExporter(SpanExporter):
|
|
|
58
58
|
return trace
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
OTLPSpanExporter._MAX_RETRY_TIMEOUT = 2
|
|
61
|
+
OTLPSpanExporter._MAX_RETRY_TIMEOUT = 2 # pylint: disable=protected-access
|
|
62
62
|
|
|
63
63
|
ConsoleExporter = ConsoleSpanExporter
|
|
64
64
|
InlineExporter = InlineTraceExporter
|