agenta 0.57.0__py3-none-any.whl → 0.63.2__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.
Files changed (267) hide show
  1. agenta/__init__.py +12 -3
  2. agenta/client/__init__.py +4 -4
  3. agenta/client/backend/__init__.py +4 -4
  4. agenta/client/backend/api_keys/client.py +2 -2
  5. agenta/client/backend/billing/client.py +2 -2
  6. agenta/client/backend/billing/raw_client.py +2 -2
  7. agenta/client/backend/client.py +56 -48
  8. agenta/client/backend/core/client_wrapper.py +2 -2
  9. agenta/client/backend/core/file.py +3 -1
  10. agenta/client/backend/core/http_client.py +3 -3
  11. agenta/client/backend/core/pydantic_utilities.py +13 -3
  12. agenta/client/backend/human_evaluations/client.py +2 -2
  13. agenta/client/backend/human_evaluations/raw_client.py +2 -2
  14. agenta/client/backend/organization/client.py +46 -34
  15. agenta/client/backend/organization/raw_client.py +32 -26
  16. agenta/client/backend/raw_client.py +26 -26
  17. agenta/client/backend/testsets/client.py +18 -18
  18. agenta/client/backend/testsets/raw_client.py +30 -30
  19. agenta/client/backend/types/__init__.py +4 -4
  20. agenta/client/backend/types/account_request.py +3 -1
  21. agenta/client/backend/types/account_response.py +3 -1
  22. agenta/client/backend/types/agenta_node_dto.py +3 -1
  23. agenta/client/backend/types/agenta_nodes_response.py +3 -1
  24. agenta/client/backend/types/agenta_root_dto.py +3 -1
  25. agenta/client/backend/types/agenta_roots_response.py +3 -1
  26. agenta/client/backend/types/agenta_tree_dto.py +3 -1
  27. agenta/client/backend/types/agenta_trees_response.py +3 -1
  28. agenta/client/backend/types/aggregated_result.py +3 -1
  29. agenta/client/backend/types/analytics_response.py +3 -1
  30. agenta/client/backend/types/annotation.py +6 -4
  31. agenta/client/backend/types/annotation_create.py +3 -1
  32. agenta/client/backend/types/annotation_edit.py +3 -1
  33. agenta/client/backend/types/annotation_link.py +3 -1
  34. agenta/client/backend/types/annotation_link_response.py +3 -1
  35. agenta/client/backend/types/annotation_query.py +3 -1
  36. agenta/client/backend/types/annotation_query_request.py +3 -1
  37. agenta/client/backend/types/annotation_reference.py +3 -1
  38. agenta/client/backend/types/annotation_references.py +3 -1
  39. agenta/client/backend/types/annotation_response.py +3 -1
  40. agenta/client/backend/types/annotations_response.py +3 -1
  41. agenta/client/backend/types/app.py +3 -1
  42. agenta/client/backend/types/app_variant_response.py +3 -1
  43. agenta/client/backend/types/app_variant_revision.py +3 -1
  44. agenta/client/backend/types/artifact.py +6 -4
  45. agenta/client/backend/types/base_output.py +3 -1
  46. agenta/client/backend/types/body_fetch_workflow_revision.py +3 -1
  47. agenta/client/backend/types/body_import_testset.py +3 -1
  48. agenta/client/backend/types/bucket_dto.py +3 -1
  49. agenta/client/backend/types/collect_status_response.py +3 -1
  50. agenta/client/backend/types/config_db.py +3 -1
  51. agenta/client/backend/types/config_dto.py +3 -1
  52. agenta/client/backend/types/config_response_model.py +3 -1
  53. agenta/client/backend/types/correct_answer.py +3 -1
  54. agenta/client/backend/types/create_app_output.py +3 -1
  55. agenta/client/backend/types/custom_model_settings_dto.py +3 -1
  56. agenta/client/backend/types/custom_provider_dto.py +3 -1
  57. agenta/client/backend/types/custom_provider_kind.py +1 -1
  58. agenta/client/backend/types/custom_provider_settings_dto.py +3 -1
  59. agenta/client/backend/types/delete_evaluation.py +3 -1
  60. agenta/client/backend/types/environment_output.py +3 -1
  61. agenta/client/backend/types/environment_output_extended.py +3 -1
  62. agenta/client/backend/types/environment_revision.py +3 -1
  63. agenta/client/backend/types/error.py +3 -1
  64. agenta/client/backend/types/evaluation.py +3 -1
  65. agenta/client/backend/types/evaluation_scenario.py +3 -1
  66. agenta/client/backend/types/evaluation_scenario_input.py +3 -1
  67. agenta/client/backend/types/evaluation_scenario_output.py +3 -1
  68. agenta/client/backend/types/evaluation_scenario_result.py +3 -1
  69. agenta/client/backend/types/evaluator.py +6 -4
  70. agenta/client/backend/types/evaluator_config.py +6 -4
  71. agenta/client/backend/types/evaluator_flags.py +3 -1
  72. agenta/client/backend/types/evaluator_mapping_output_interface.py +3 -1
  73. agenta/client/backend/types/evaluator_output_interface.py +3 -1
  74. agenta/client/backend/types/evaluator_query.py +3 -1
  75. agenta/client/backend/types/evaluator_query_request.py +3 -1
  76. agenta/client/backend/types/evaluator_request.py +3 -1
  77. agenta/client/backend/types/evaluator_response.py +3 -1
  78. agenta/client/backend/types/evaluators_response.py +3 -1
  79. agenta/client/backend/types/exception_dto.py +3 -1
  80. agenta/client/backend/types/extended_o_tel_tracing_response.py +3 -1
  81. agenta/client/backend/types/get_config_response.py +3 -1
  82. agenta/client/backend/types/header.py +3 -1
  83. agenta/client/backend/types/http_validation_error.py +3 -1
  84. agenta/client/backend/types/human_evaluation.py +3 -1
  85. agenta/client/backend/types/human_evaluation_scenario.py +3 -1
  86. agenta/client/backend/types/human_evaluation_scenario_input.py +3 -1
  87. agenta/client/backend/types/human_evaluation_scenario_output.py +3 -1
  88. agenta/client/backend/types/invite_request.py +3 -1
  89. agenta/client/backend/types/legacy_analytics_response.py +3 -1
  90. agenta/client/backend/types/legacy_data_point.py +3 -1
  91. agenta/client/backend/types/legacy_evaluator.py +3 -1
  92. agenta/client/backend/types/legacy_scope_request.py +3 -1
  93. agenta/client/backend/types/legacy_scopes_response.py +3 -1
  94. agenta/client/backend/types/legacy_subscription_request.py +3 -1
  95. agenta/client/backend/types/legacy_user_request.py +3 -1
  96. agenta/client/backend/types/legacy_user_response.py +3 -1
  97. agenta/client/backend/types/lifecycle_dto.py +3 -1
  98. agenta/client/backend/types/link_dto.py +3 -1
  99. agenta/client/backend/types/list_api_keys_response.py +3 -1
  100. agenta/client/backend/types/llm_run_rate_limit.py +3 -1
  101. agenta/client/backend/types/meta_request.py +3 -1
  102. agenta/client/backend/types/metrics_dto.py +3 -1
  103. agenta/client/backend/types/new_testset.py +3 -1
  104. agenta/client/backend/types/node_dto.py +3 -1
  105. agenta/client/backend/types/o_tel_context_dto.py +3 -1
  106. agenta/client/backend/types/o_tel_event.py +6 -4
  107. agenta/client/backend/types/o_tel_event_dto.py +3 -1
  108. agenta/client/backend/types/o_tel_extra_dto.py +3 -1
  109. agenta/client/backend/types/o_tel_flat_span.py +6 -4
  110. agenta/client/backend/types/o_tel_link.py +6 -4
  111. agenta/client/backend/types/o_tel_link_dto.py +3 -1
  112. agenta/client/backend/types/o_tel_links_response.py +3 -1
  113. agenta/client/backend/types/o_tel_span.py +1 -1
  114. agenta/client/backend/types/o_tel_span_dto.py +3 -1
  115. agenta/client/backend/types/o_tel_spans_tree.py +3 -1
  116. agenta/client/backend/types/o_tel_tracing_data_response.py +3 -1
  117. agenta/client/backend/types/o_tel_tracing_request.py +3 -1
  118. agenta/client/backend/types/o_tel_tracing_response.py +3 -1
  119. agenta/client/backend/types/organization.py +3 -1
  120. agenta/client/backend/types/organization_details.py +3 -1
  121. agenta/client/backend/types/organization_membership_request.py +3 -1
  122. agenta/client/backend/types/organization_output.py +3 -1
  123. agenta/client/backend/types/organization_request.py +3 -1
  124. agenta/client/backend/types/parent_dto.py +3 -1
  125. agenta/client/backend/types/project_membership_request.py +3 -1
  126. agenta/client/backend/types/project_request.py +3 -1
  127. agenta/client/backend/types/project_scope.py +3 -1
  128. agenta/client/backend/types/projects_response.py +3 -1
  129. agenta/client/backend/types/reference.py +6 -4
  130. agenta/client/backend/types/reference_dto.py +3 -1
  131. agenta/client/backend/types/reference_request_model.py +3 -1
  132. agenta/client/backend/types/result.py +3 -1
  133. agenta/client/backend/types/root_dto.py +3 -1
  134. agenta/client/backend/types/scopes_response_model.py +3 -1
  135. agenta/client/backend/types/secret_dto.py +3 -1
  136. agenta/client/backend/types/secret_response_dto.py +3 -1
  137. agenta/client/backend/types/simple_evaluation_output.py +3 -1
  138. agenta/client/backend/types/span_dto.py +6 -4
  139. agenta/client/backend/types/standard_provider_dto.py +3 -1
  140. agenta/client/backend/types/standard_provider_settings_dto.py +3 -1
  141. agenta/client/backend/types/status_dto.py +3 -1
  142. agenta/client/backend/types/tags_request.py +3 -1
  143. agenta/client/backend/types/testcase_response.py +6 -4
  144. agenta/client/backend/types/testset.py +6 -4
  145. agenta/client/backend/types/{test_set_output_response.py → testset_output_response.py} +4 -2
  146. agenta/client/backend/types/testset_request.py +3 -1
  147. agenta/client/backend/types/testset_response.py +3 -1
  148. agenta/client/backend/types/{test_set_simple_response.py → testset_simple_response.py} +4 -2
  149. agenta/client/backend/types/testsets_response.py +3 -1
  150. agenta/client/backend/types/time_dto.py +3 -1
  151. agenta/client/backend/types/tree_dto.py +3 -1
  152. agenta/client/backend/types/update_app_output.py +3 -1
  153. agenta/client/backend/types/user_request.py +3 -1
  154. agenta/client/backend/types/validation_error.py +3 -1
  155. agenta/client/backend/types/workflow_artifact.py +6 -4
  156. agenta/client/backend/types/workflow_data.py +3 -1
  157. agenta/client/backend/types/workflow_flags.py +3 -1
  158. agenta/client/backend/types/workflow_request.py +3 -1
  159. agenta/client/backend/types/workflow_response.py +3 -1
  160. agenta/client/backend/types/workflow_revision.py +6 -4
  161. agenta/client/backend/types/workflow_revision_request.py +3 -1
  162. agenta/client/backend/types/workflow_revision_response.py +3 -1
  163. agenta/client/backend/types/workflow_revisions_response.py +3 -1
  164. agenta/client/backend/types/workflow_variant.py +6 -4
  165. agenta/client/backend/types/workflow_variant_request.py +3 -1
  166. agenta/client/backend/types/workflow_variant_response.py +3 -1
  167. agenta/client/backend/types/workflow_variants_response.py +3 -1
  168. agenta/client/backend/types/workflows_response.py +3 -1
  169. agenta/client/backend/types/workspace.py +3 -1
  170. agenta/client/backend/types/workspace_member_response.py +3 -1
  171. agenta/client/backend/types/workspace_membership_request.py +3 -1
  172. agenta/client/backend/types/workspace_permission.py +3 -1
  173. agenta/client/backend/types/workspace_request.py +3 -1
  174. agenta/client/backend/types/workspace_response.py +3 -1
  175. agenta/client/backend/workspace/client.py +2 -2
  176. agenta/client/client.py +102 -88
  177. agenta/sdk/__init__.py +52 -3
  178. agenta/sdk/agenta_init.py +43 -16
  179. agenta/sdk/assets.py +22 -15
  180. agenta/sdk/context/serving.py +20 -8
  181. agenta/sdk/context/tracing.py +40 -22
  182. agenta/sdk/contexts/__init__.py +0 -0
  183. agenta/sdk/contexts/routing.py +38 -0
  184. agenta/sdk/contexts/running.py +57 -0
  185. agenta/sdk/contexts/tracing.py +86 -0
  186. agenta/sdk/decorators/__init__.py +1 -0
  187. agenta/sdk/decorators/routing.py +284 -0
  188. agenta/sdk/decorators/running.py +692 -98
  189. agenta/sdk/decorators/serving.py +20 -21
  190. agenta/sdk/decorators/tracing.py +176 -131
  191. agenta/sdk/engines/__init__.py +0 -0
  192. agenta/sdk/engines/running/__init__.py +0 -0
  193. agenta/sdk/engines/running/utils.py +17 -0
  194. agenta/sdk/engines/tracing/__init__.py +1 -0
  195. agenta/sdk/engines/tracing/attributes.py +185 -0
  196. agenta/sdk/engines/tracing/conventions.py +49 -0
  197. agenta/sdk/engines/tracing/exporters.py +130 -0
  198. agenta/sdk/engines/tracing/inline.py +1154 -0
  199. agenta/sdk/engines/tracing/processors.py +190 -0
  200. agenta/sdk/engines/tracing/propagation.py +102 -0
  201. agenta/sdk/engines/tracing/spans.py +136 -0
  202. agenta/sdk/engines/tracing/tracing.py +324 -0
  203. agenta/sdk/evaluations/__init__.py +2 -0
  204. agenta/sdk/evaluations/metrics.py +37 -0
  205. agenta/sdk/evaluations/preview/__init__.py +0 -0
  206. agenta/sdk/evaluations/preview/evaluate.py +765 -0
  207. agenta/sdk/evaluations/preview/utils.py +861 -0
  208. agenta/sdk/evaluations/results.py +66 -0
  209. agenta/sdk/evaluations/runs.py +153 -0
  210. agenta/sdk/evaluations/scenarios.py +48 -0
  211. agenta/sdk/litellm/litellm.py +12 -0
  212. agenta/sdk/litellm/mockllm.py +6 -8
  213. agenta/sdk/litellm/mocks/__init__.py +5 -5
  214. agenta/sdk/managers/applications.py +304 -0
  215. agenta/sdk/managers/config.py +2 -2
  216. agenta/sdk/managers/evaluations.py +0 -0
  217. agenta/sdk/managers/evaluators.py +303 -0
  218. agenta/sdk/managers/secrets.py +161 -24
  219. agenta/sdk/managers/shared.py +3 -1
  220. agenta/sdk/managers/testsets.py +441 -0
  221. agenta/sdk/managers/vault.py +3 -3
  222. agenta/sdk/middleware/auth.py +0 -176
  223. agenta/sdk/middleware/vault.py +203 -8
  224. agenta/sdk/middlewares/__init__.py +0 -0
  225. agenta/sdk/middlewares/routing/__init__.py +0 -0
  226. agenta/sdk/middlewares/routing/auth.py +263 -0
  227. agenta/sdk/middlewares/routing/cors.py +30 -0
  228. agenta/sdk/middlewares/routing/otel.py +29 -0
  229. agenta/sdk/middlewares/running/__init__.py +0 -0
  230. agenta/sdk/middlewares/running/normalizer.py +321 -0
  231. agenta/sdk/middlewares/running/resolver.py +161 -0
  232. agenta/sdk/middlewares/running/vault.py +140 -0
  233. agenta/sdk/models/__init__.py +0 -0
  234. agenta/sdk/models/blobs.py +33 -0
  235. agenta/sdk/models/evaluations.py +119 -0
  236. agenta/sdk/models/git.py +126 -0
  237. agenta/sdk/models/shared.py +167 -0
  238. agenta/sdk/models/testsets.py +163 -0
  239. agenta/sdk/models/tracing.py +202 -0
  240. agenta/sdk/models/workflows.py +753 -0
  241. agenta/sdk/tracing/exporters.py +67 -17
  242. agenta/sdk/tracing/processors.py +97 -0
  243. agenta/sdk/tracing/propagation.py +3 -1
  244. agenta/sdk/tracing/spans.py +4 -0
  245. agenta/sdk/tracing/tracing.py +13 -13
  246. agenta/sdk/types.py +211 -17
  247. agenta/sdk/utils/cache.py +1 -1
  248. agenta/sdk/utils/client.py +38 -0
  249. agenta/sdk/utils/helpers.py +13 -12
  250. agenta/sdk/utils/logging.py +18 -78
  251. agenta/sdk/utils/references.py +23 -0
  252. agenta/sdk/workflows/builtin.py +600 -0
  253. agenta/sdk/workflows/configurations.py +22 -0
  254. agenta/sdk/workflows/errors.py +292 -0
  255. agenta/sdk/workflows/handlers.py +1791 -0
  256. agenta/sdk/workflows/interfaces.py +948 -0
  257. agenta/sdk/workflows/sandbox.py +118 -0
  258. agenta/sdk/workflows/utils.py +303 -6
  259. {agenta-0.57.0.dist-info → agenta-0.63.2.dist-info}/METADATA +33 -30
  260. agenta-0.63.2.dist-info/RECORD +421 -0
  261. agenta/sdk/middleware/adapt.py +0 -253
  262. agenta/sdk/middleware/base.py +0 -40
  263. agenta/sdk/middleware/flags.py +0 -40
  264. agenta/sdk/workflows/types.py +0 -472
  265. agenta-0.57.0.dist-info/RECORD +0 -371
  266. /agenta/sdk/{workflows → engines/running}/registry.py +0 -0
  267. {agenta-0.57.0.dist-info → agenta-0.63.2.dist-info}/WHEEL +0 -0
@@ -1,134 +1,728 @@
1
- from typing import Any, Callable, List, Awaitable, Dict, Optional
2
- import asyncio
3
- from functools import wraps
4
- from inspect import iscoroutinefunction
5
- from copy import deepcopy
6
-
7
- from decorator import decorator
8
-
9
- from agenta.sdk.context.running import (
10
- workflow_mode_enabled_context,
11
- workflow_registry_context,
12
- WorkflowRegistryContext,
13
- )
14
- from agenta.sdk.workflows.types import (
1
+ # /agenta/sdk/decorators/running.py
2
+
3
+ from typing import Any, Callable, Optional, Protocol, Union, Dict
4
+ from functools import update_wrapper, wraps
5
+ from typing import Callable, Any
6
+ from inspect import signature
7
+ from uuid import UUID, uuid4
8
+
9
+ from agenta.sdk.utils.logging import get_module_logger
10
+ from agenta.sdk.models.workflows import (
11
+ WorkflowRevisionData,
12
+ WorkflowRevision,
13
+ WorkflowServiceRequestData,
14
+ WorkflowServiceResponseData,
15
15
  WorkflowServiceRequest,
16
- WorkflowServiceResponse,
17
16
  WorkflowServiceInterface,
18
- WorkflowRevision,
19
- Schema,
17
+ WorkflowServiceConfiguration,
18
+ WorkflowServiceBatchResponse,
19
+ WorkflowServiceStreamResponse,
20
+ Reference,
21
+ Link,
22
+ )
23
+ from agenta.sdk.contexts.running import RunningContext, running_context_manager
24
+ from agenta.sdk.contexts.tracing import TracingContext, tracing_context_manager
25
+ from agenta.sdk.middlewares.running.normalizer import (
26
+ NormalizerMiddleware,
27
+ )
28
+ from agenta.sdk.middlewares.running.resolver import (
29
+ ResolverMiddleware,
30
+ resolve_interface,
31
+ resolve_configuration,
20
32
  )
21
- from agenta.sdk.middleware.base import WorkflowMiddlewareDecorator
33
+ from agenta.sdk.middlewares.running.vault import (
34
+ VaultMiddleware,
35
+ get_secrets,
36
+ )
37
+ from agenta.sdk.decorators.tracing import auto_instrument
38
+ from agenta.sdk.workflows.utils import (
39
+ register_handler,
40
+ retrieve_handler,
41
+ retrieve_interface,
42
+ retrieve_configuration,
43
+ is_custom_uri,
44
+ )
45
+
46
+ import agenta as ag
47
+
48
+
49
+ log = get_module_logger(__name__)
50
+
51
+
52
+ class InvokeFn(Protocol):
53
+ async def __call__(
54
+ self,
55
+ request: Union[WorkflowServiceRequest, dict],
56
+ ) -> Union[WorkflowServiceBatchResponse, WorkflowServiceStreamResponse]: ...
57
+
58
+
59
+ class InspectFn(Protocol):
60
+ async def __call__(self) -> WorkflowServiceRequest: ...
22
61
 
23
- from agenta.sdk.middleware.auth import AuthMiddleware
24
- from agenta.sdk.middleware.flags import FlagsMiddleware
25
- from agenta.sdk.middleware.adapt import AdaptMiddleware
26
62
 
63
+ class Workflow:
64
+ def __init__(self, fn: Callable[..., Any]):
65
+ self._fn = fn
27
66
 
28
- LATEST_VERSION = "2025.07.14"
29
- DEFAULT_SCHEMAS = lambda: {} # pylint: disable=unnecessary-lambda-assignment
67
+ update_wrapper(self, fn)
30
68
 
69
+ # self.invoke: InvokeFn
70
+ # self.inspect: InspectFn
71
+ self.workflow: workflow
31
72
 
32
- class workflows:
33
- @classmethod
34
- def get_registry(cls) -> WorkflowRegistryContext:
35
- return deepcopy(workflow_registry_context.get())
73
+ async def invoke(
74
+ self,
75
+ *,
76
+ request: Union[WorkflowServiceRequest, dict],
77
+ #
78
+ secrets: Optional[list] = None,
79
+ credentials: Optional[str] = None,
80
+ #
81
+ **kwargs,
82
+ ) -> Union[WorkflowServiceBatchResponse, WorkflowServiceStreamResponse]: ...
83
+
84
+ async def inspect(
85
+ self,
86
+ *,
87
+ credentials: Optional[str] = None,
88
+ #
89
+ **kwargs,
90
+ ) -> WorkflowServiceRequest: ...
36
91
 
92
+ def __call__(self, *args, **kwargs) -> Any:
93
+ return self._fn(*args, **kwargs)
37
94
 
38
- class workflow: # pylint: disable=invalid-name
95
+ def __repr__(self) -> str:
96
+ return repr(self._fn)
97
+
98
+ def __str__(self) -> str:
99
+ return str(self._fn)
100
+
101
+
102
+ class workflow:
39
103
  def __init__(
40
104
  self,
105
+ *,
106
+ # -------------------------------------------------------------------- #
107
+ id: Optional[UUID] = None,
108
+ slug: Optional[str] = None,
41
109
  version: Optional[str] = None,
42
- schemas: Optional[Dict[str, Schema]] = None,
110
+ #
111
+ references: Optional[Dict[str, Union[Reference, Dict[str, Any]]]] = None,
112
+ # -------------------------------------------------------------------- #
113
+ links: Optional[Dict[str, Union[Link, Dict[str, Any]]]] = None,
114
+ # -------------------------------------------------------------------- #
115
+ name: Optional[str] = None,
116
+ description: Optional[str] = None,
117
+ # -------------------------------------------------------------------- #
118
+ flags: Optional[dict] = None,
119
+ tags: Optional[dict] = None,
120
+ meta: Optional[dict] = None,
121
+ # -------------------------------------------------------------------- #
122
+ uri: Optional[str] = None,
123
+ url: Optional[str] = None,
124
+ headers: Optional[dict] = None,
125
+ schemas: Optional[dict] = None,
126
+ #
127
+ interface: Optional[
128
+ Union[
129
+ WorkflowServiceInterface,
130
+ Dict[str, Any],
131
+ ]
132
+ ] = None,
133
+ # -------------------------------------------------------------------- #
134
+ script: Optional[dict] = None,
135
+ parameters: Optional[dict] = None,
136
+ #
137
+ configuration: Optional[
138
+ Union[
139
+ WorkflowServiceConfiguration,
140
+ Dict[str, Any],
141
+ ]
142
+ ] = None,
143
+ # -------------------------------------------------------------------- #
144
+ aggregate: Optional[Union[bool, Callable]] = None, # stream to batch
145
+ annotate: Optional[bool] = None, # annotation vs invocation
146
+ # -------------------------------------------------------------------- #
147
+ **kwargs,
43
148
  ):
44
- self.middleware: List[WorkflowMiddlewareDecorator] = [
45
- AuthMiddleware,
46
- FlagsMiddleware,
47
- AdaptMiddleware,
149
+ # -------------------------------------------------------------------- #
150
+ self.id = id
151
+ self.slug = slug
152
+ self.version = version
153
+ #
154
+ self.references = references # FIX TYPING
155
+ # -------------------------------------------------------------------- #
156
+ self.links = links
157
+ # -------------------------------------------------------------------- #
158
+ self.name = name
159
+ self.description = description
160
+ # -------------------------------------------------------------------- #
161
+ self.flags = flags
162
+ self.tags = tags
163
+ self.meta = meta
164
+ # -------------------------------------------------------------------- #
165
+ self.uri = uri
166
+ self.url = url
167
+ self.headers = headers
168
+ self.schemas = schemas
169
+ #
170
+ self.interface = interface
171
+ # -------------------------------------------------------------------- #
172
+ self.script = script
173
+ self.parameters = parameters
174
+ #
175
+ self.configuration = configuration
176
+ # -------------------------------------------------------------------- #
177
+ self.aggregate = aggregate
178
+ self.annotate = annotate
179
+ # -------------------------------------------------------------------- #
180
+ self.kwargs = kwargs
181
+ # -------------------------------------------------------------------- #
182
+
183
+ self.handler = None
184
+
185
+ self.middlewares = [
186
+ VaultMiddleware(),
187
+ ResolverMiddleware(),
188
+ NormalizerMiddleware(),
48
189
  ]
49
190
 
50
- self.version = version or LATEST_VERSION
51
- self.schemas = schemas or DEFAULT_SCHEMAS()
191
+ self.default_request = None
192
+
193
+ self.uri = uri or (interface.uri if interface else None)
52
194
 
53
- def __call__(self, func: Callable[..., Any]) -> Callable[..., Any]:
54
- is_async = iscoroutinefunction(func)
195
+ if self.uri is not None:
196
+ self._retrieve_handler(self.uri)
55
197
 
56
- workflow_registry = workflow_registry_context.get()
198
+ if self.handler:
199
+ self.interface = retrieve_interface(self.uri) or self.interface
200
+ if isinstance(self.interface, WorkflowServiceInterface):
201
+ self.uri = self.interface.uri or self.uri
202
+ self.configuration = self.configuration or retrieve_configuration(
203
+ self.uri
204
+ )
205
+ if not isinstance(self.configuration, WorkflowServiceConfiguration):
206
+ self.configuration = WorkflowServiceConfiguration()
207
+ self.configuration.parameters = (
208
+ self.parameters or self.configuration.parameters
209
+ )
210
+ self.parameters = self.configuration.parameters
57
211
 
58
- workflow_registry.version = workflow_registry.version or self.version
212
+ if is_custom_uri(self.uri):
213
+ self.flags = self.flags or dict()
214
+ self.flags["is_custom"] = True
59
215
 
60
- if is_async:
216
+ def __call__(self, handler: Optional[Callable[..., Any]] = None) -> Workflow:
217
+ if self.handler is None and handler is not None:
218
+ self._register_handler(
219
+ handler,
220
+ uri=self.uri,
221
+ )
222
+
223
+ if self.handler is not None:
224
+ self._extend_handler()
225
+
226
+ if is_custom_uri(self.uri):
227
+ self.flags = self.flags or dict()
228
+ self.flags["is_custom"] = True
229
+
230
+ return self.handler
61
231
 
62
- @wraps(func)
63
- async def async_wrapper(*args, **kwargs):
64
- if workflow_mode_enabled_context.get():
65
- return await self._wrapped_async(func)(*args, **kwargs)
66
- return await func(*args, **kwargs)
232
+ raise NotImplementedError("workflow without handler is not implemented yet")
233
+
234
+ def _register_handler(
235
+ self,
236
+ handler: Optional[Callable[..., Any]] = None,
237
+ uri: Optional[str] = None,
238
+ ):
239
+ """Register a handler function with the workflow system.
67
240
 
68
- workflow_registry.handlers = {
69
- "invoke": async_wrapper,
70
- "inspect": self.make_interface_wrapper(self.version, self.schemas),
71
- }
241
+ Takes a callable handler, instruments it for observability, and registers it
242
+ in the global handler registry with a URI. Also initializes or updates the
243
+ workflow's interface with the URI and schemas.
72
244
 
73
- return async_wrapper
245
+ Args:
246
+ handler: The callable function to register as the workflow handler
247
+ uri: Optional URI to use for registration; if None, one will be generated
248
+ """
249
+ if handler is not None and callable(handler):
250
+ instrumented = auto_instrument(handler)
251
+ uri = register_handler(instrumented, uri=uri)
252
+ if self.interface is None:
253
+ self.interface = WorkflowServiceInterface()
254
+ self.uri = uri
255
+ self.interface.uri = uri
256
+ self.interface.schemas = self.schemas
257
+ self.handler = instrumented
74
258
 
75
- else:
259
+ def _retrieve_handler(self, uri: str):
260
+ self.handler = retrieve_handler(uri)
261
+ if self.handler is None:
262
+ raise ValueError(f"Unable to retrieve handler for URI: {uri}")
263
+ if self.interface is None:
264
+ self.interface = WorkflowServiceInterface()
265
+ self.uri = uri
266
+ self.interface.uri = uri
267
+ self.interface.schemas = self.schemas
76
268
 
77
- @wraps(func)
78
- def sync_wrapper(*args, **kwargs):
79
- if workflow_mode_enabled_context.get():
80
- return self._wrapped_async(func)(*args, **kwargs)
269
+ def _extend_handler(self):
270
+ """Extend the registered handler with additional workflow capabilities.
271
+
272
+ Wraps the handler function to:
273
+ 1. Automatically inject workflow parameters if the handler expects them
274
+ 2. Expose workflow-specific methods (invoke, inspect) on the handler
275
+ 3. Mark the handler with is_workflow flag for identification
276
+ 4. Wrap everything in a Workflow object for consistent interface
277
+
278
+ This transforms a plain function into a full-featured workflow that can be
279
+ invoked programmatically via .invoke() or inspected via .inspect().
280
+
281
+ Raises:
282
+ RuntimeError: If no handler has been registered yet
283
+ ValueError: If handler becomes None during extension (should never happen)
284
+ """
285
+ if self.handler is None:
286
+ raise RuntimeError("No handler registered")
287
+
288
+ func = self.handler
289
+
290
+ @wraps(func)
291
+ def wrapper(*args: Any, **kwargs: Any):
292
+ if "parameters" in signature(func).parameters:
293
+ return func(
294
+ *args,
295
+ **{**{"parameters": self.parameters}, **kwargs},
296
+ )
297
+ else:
81
298
  return func(*args, **kwargs)
82
299
 
83
- return sync_wrapper
300
+ # expose workflow extras
301
+ wrapper.invoke = self.invoke # type: ignore[attr-defined]
302
+ wrapper.inspect = self.inspect # type: ignore[attr-defined]
303
+ wrapper.is_workflow = True # type: ignore[attr-defined]
84
304
 
85
- def _wrapped_async(
305
+ if self.handler is None:
306
+ raise ValueError("handler must be set before extending")
307
+
308
+ self.handler = Workflow(wrapper)
309
+
310
+ async def invoke(
86
311
  self,
87
- func: Callable[..., Any],
88
- ) -> Callable[..., Awaitable[WorkflowServiceResponse]]:
89
- @decorator
90
- async def async_wrapper(func, *args, **kwargs):
91
- result = (
92
- await func(*args, **kwargs)
93
- if iscoroutinefunction(func)
94
- else await asyncio.to_thread(func, *args, **kwargs)
95
- )
312
+ *,
313
+ request: WorkflowServiceRequest,
314
+ #
315
+ secrets: Optional[list] = None,
316
+ credentials: Optional[str] = None,
317
+ #
318
+ **kwargs,
319
+ ) -> Union[WorkflowServiceBatchResponse, WorkflowServiceStreamResponse]:
320
+ _flags = {**(self.flags or {}), **(request.flags or {})}
321
+ _tags = {**(self.tags or {}), **(request.tags or {})}
322
+ _meta = {**(self.meta or {}), **(request.meta or {})}
96
323
 
97
- return result
324
+ credentials = credentials or (
325
+ f"ApiKey {ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.api_key}"
326
+ if ag.DEFAULT_AGENTA_SINGLETON_INSTANCE.api_key
327
+ else None
328
+ )
98
329
 
99
- @wraps(func)
100
- async def wrapper(*args, **kwargs):
101
- handler = async_wrapper(func, *args, **kwargs)
102
- request: WorkflowServiceRequest = (
103
- kwargs.pop("request")
104
- if "request" in kwargs
105
- else args[0]
106
- if len(args) > 0
107
- else None
108
- )
109
- revision: WorkflowRevision = (
110
- kwargs.pop("revision")
111
- if "revision" in kwargs
112
- else args[1]
113
- if len(args) > 1
114
- else None
115
- )
330
+ with tracing_context_manager(TracingContext.get()):
331
+ tracing_ctx = TracingContext.get()
116
332
 
117
- _handler = handler
118
- for middleware in reversed(self.middleware):
119
- _handler = middleware(_handler)
333
+ tracing_ctx.credentials = credentials
120
334
 
121
- return await _handler(
122
- request=request,
123
- revision=revision,
124
- )
335
+ tracing_ctx.aggregate = self.aggregate
336
+ tracing_ctx.annotate = self.annotate
125
337
 
126
- return wrapper
338
+ tracing_ctx.flags = _flags
339
+ tracing_ctx.tags = _tags
340
+ tracing_ctx.meta = _meta
341
+
342
+ tracing_ctx.references = self.references
343
+ tracing_ctx.links = self.links
344
+
345
+ with running_context_manager(RunningContext.get()):
346
+ running_ctx = RunningContext.get()
347
+
348
+ running_ctx.secrets = secrets
349
+ running_ctx.credentials = credentials
350
+
351
+ running_ctx.interface = self.interface
352
+ running_ctx.schemas = self.schemas
353
+ running_ctx.configuration = self.configuration
354
+ running_ctx.parameters = self.parameters
355
+
356
+ running_ctx.aggregate = self.aggregate
357
+ running_ctx.annotate = self.annotate
358
+
359
+ async def terminal(req: WorkflowServiceRequest):
360
+ return None
361
+
362
+ call_next = terminal
363
+
364
+ for mw in reversed(self.middlewares):
365
+ prev_next = call_next
366
+
367
+ async def make_call(mw, prev_next):
368
+ async def _call(
369
+ req: WorkflowServiceRequest,
370
+ ):
371
+ return await mw(req, prev_next)
372
+
373
+ return _call
374
+
375
+ call_next = await make_call(mw, prev_next)
376
+
377
+ return await call_next(request)
378
+
379
+ async def inspect(
380
+ self,
381
+ *,
382
+ credentials: Optional[str] = None,
383
+ #
384
+ **kwargs,
385
+ ) -> WorkflowServiceRequest:
386
+ with tracing_context_manager(TracingContext.get()):
387
+ tracing_ctx = TracingContext.get()
388
+
389
+ tracing_ctx.credentials = credentials
390
+
391
+ tracing_ctx.aggregate = self.aggregate
392
+ tracing_ctx.annotate = self.annotate
393
+
394
+ tracing_ctx.references = self.references
395
+ tracing_ctx.links = self.links
396
+
397
+ with running_context_manager(RunningContext.get()):
398
+ running_ctx = RunningContext.get()
399
+
400
+ running_ctx.credentials = credentials
401
+
402
+ running_ctx.interface = self.interface
403
+ running_ctx.schemas = self.schemas
404
+ running_ctx.configuration = self.configuration
405
+ running_ctx.parameters = self.parameters
406
+
407
+ running_ctx.aggregate = self.aggregate
408
+ running_ctx.annotate = self.annotate
409
+
410
+ if self.default_request is None:
411
+ interface = await resolve_interface(
412
+ interface=self.interface,
413
+ **self.kwargs,
414
+ )
415
+ configuration = await resolve_configuration(
416
+ configuration=self.configuration,
417
+ **self.kwargs,
418
+ )
419
+
420
+ self.default_request = WorkflowServiceRequest(
421
+ #
422
+ interface=interface,
423
+ configuration=configuration,
424
+ #
425
+ references=self.references,
426
+ links=self.links,
427
+ #
428
+ flags=self.flags,
429
+ tags=self.tags,
430
+ meta=self.meta,
431
+ #
432
+ data=WorkflowServiceRequestData(
433
+ revision=WorkflowRevision(
434
+ id=self.id,
435
+ slug=self.slug,
436
+ version=self.version,
437
+ #
438
+ name=self.name,
439
+ description=self.description,
440
+ ).model_dump(
441
+ mode="json",
442
+ exclude_none=True,
443
+ ),
444
+ ),
445
+ )
446
+
447
+ return self.default_request
448
+
449
+
450
+ def is_workflow(obj: Any) -> bool:
451
+ return getattr(obj, "is_workflow", False) or isinstance(
452
+ getattr(obj, "workflow", None), workflow
453
+ )
454
+
455
+
456
+ def auto_workflow(obj: Any, **kwargs) -> Workflow:
457
+ if is_workflow(obj):
458
+ return obj
459
+ if isinstance(obj, workflow):
460
+ return obj()
461
+ if isinstance(getattr(obj, "workflow", None), workflow):
462
+ return obj
463
+
464
+ return workflow(**kwargs)(obj)
465
+
466
+
467
+ async def invoke_workflow(
468
+ request: WorkflowServiceRequest,
469
+ #
470
+ secrets: Optional[list] = None,
471
+ credentials: Optional[str] = None,
472
+ #
473
+ **kwargs,
474
+ ) -> Union[WorkflowServiceBatchResponse, WorkflowServiceStreamResponse]:
475
+ return await workflow(
476
+ data=request.data,
477
+ #
478
+ interface=request.interface,
479
+ configuration=request.configuration,
480
+ #
481
+ flags=request.flags,
482
+ tags=request.tags,
483
+ meta=request.meta,
484
+ #
485
+ references=request.references,
486
+ links=request.links,
487
+ #
488
+ **kwargs,
489
+ )().invoke(
490
+ request=request,
491
+ #
492
+ secrets=secrets,
493
+ credentials=credentials,
494
+ #
495
+ **kwargs,
496
+ )
497
+
498
+
499
+ async def inspect_workflow(
500
+ request: WorkflowServiceRequest,
501
+ #
502
+ credentials: Optional[str] = None,
503
+ #
504
+ **kwargs,
505
+ ) -> WorkflowServiceRequest:
506
+ return await workflow(
507
+ data=request.data,
508
+ #
509
+ interface=request.interface,
510
+ configuration=request.configuration,
511
+ #
512
+ flags=request.flags,
513
+ tags=request.tags,
514
+ meta=request.meta,
515
+ #
516
+ references=request.references,
517
+ links=request.links,
518
+ )().inspect(
519
+ credentials=credentials,
520
+ #
521
+ **kwargs,
522
+ )
523
+
524
+
525
+ class application(workflow):
526
+ def __init__(
527
+ self,
528
+ #
529
+ slug: Optional[str] = None,
530
+ *,
531
+ name: Optional[str] = None,
532
+ description: Optional[str] = None,
533
+ #
534
+ parameters: Optional[dict] = None,
535
+ schemas: Optional[dict] = None,
536
+ #
537
+ variant_slug: Optional[str] = None,
538
+ #
539
+ **kwargs,
540
+ ):
541
+ kwargs["flags"] = dict(
542
+ # is_custom=False, # None / False / missing is the same
543
+ # is_evaluator=False, # None / False / missing is the same
544
+ # is_human=False, # None / False / missing is the same
545
+ )
546
+
547
+ if not "references" in kwargs or not isinstance(kwargs["references"], dict):
548
+ kwargs["references"] = dict()
549
+
550
+ for key in kwargs["references"]:
551
+ if key.startswith("evaluator_"):
552
+ del kwargs["references"][key]
553
+
554
+ if slug is not None:
555
+ kwargs["references"]["application"] = {"slug": slug}
556
+ if variant_slug is not None:
557
+ kwargs["references"]["application_variant"] = {"slug": variant_slug}
558
+
559
+ super().__init__(
560
+ name=name,
561
+ description=description,
562
+ #
563
+ parameters=parameters,
564
+ schemas=schemas,
565
+ #
566
+ **kwargs,
567
+ )
568
+
569
+
570
+ async def invoke_application(
571
+ request: WorkflowServiceRequest,
572
+ #
573
+ secrets: Optional[list] = None,
574
+ credentials: Optional[str] = None,
575
+ #
576
+ **kwargs,
577
+ ) -> Union[WorkflowServiceBatchResponse, WorkflowServiceStreamResponse]:
578
+ return await application(
579
+ data=request.data,
580
+ #
581
+ interface=request.interface,
582
+ configuration=request.configuration,
583
+ #
584
+ flags=request.flags,
585
+ tags=request.tags,
586
+ meta=request.meta,
587
+ #
588
+ references=request.references,
589
+ links=request.links,
590
+ #
591
+ **kwargs,
592
+ )().invoke(
593
+ request=request,
594
+ #
595
+ secrets=secrets,
596
+ credentials=credentials,
597
+ #
598
+ **kwargs,
599
+ )
600
+
601
+
602
+ async def inspect_application(
603
+ request: WorkflowServiceRequest,
604
+ #
605
+ credentials: Optional[str] = None,
606
+ #
607
+ **kwargs,
608
+ ) -> WorkflowServiceRequest:
609
+ return await application(
610
+ data=request.data,
611
+ #
612
+ interface=request.interface,
613
+ configuration=request.configuration,
614
+ #
615
+ flags=request.flags,
616
+ tags=request.tags,
617
+ meta=request.meta,
618
+ #
619
+ references=request.references,
620
+ links=request.links,
621
+ )().inspect(
622
+ credentials=credentials,
623
+ #
624
+ **kwargs,
625
+ )
626
+
627
+
628
+ class evaluator(workflow):
629
+ def __init__(
630
+ self,
631
+ #
632
+ slug: Optional[str] = None,
633
+ *,
634
+ name: Optional[str] = None,
635
+ description: Optional[str] = None,
636
+ #
637
+ parameters: Optional[dict] = None,
638
+ schemas: Optional[dict] = None,
639
+ #
640
+ variant_slug: Optional[str] = None,
641
+ #
642
+ **kwargs,
643
+ ):
644
+ kwargs["flags"] = dict(
645
+ # is_custom=False, # None / False / missing is the same
646
+ is_evaluator=True,
647
+ # is_human=False, # None / False / missing is the same
648
+ )
649
+
650
+ if not "references" in kwargs or not isinstance(kwargs["references"], dict):
651
+ kwargs["references"] = dict()
652
+
653
+ for key in kwargs["references"]:
654
+ if key.startswith("application_"):
655
+ del kwargs["references"][key]
656
+
657
+ if slug is not None:
658
+ kwargs["references"]["evaluator"] = {"slug": slug}
659
+ if variant_slug is not None:
660
+ kwargs["references"]["evaluator_variant"] = {"slug": variant_slug}
661
+
662
+ super().__init__(
663
+ name=name,
664
+ description=description,
665
+ #
666
+ parameters=parameters,
667
+ schemas=schemas,
668
+ #
669
+ **kwargs,
670
+ )
671
+
672
+
673
+ async def invoke_evaluator(
674
+ request: WorkflowServiceRequest,
675
+ #
676
+ secrets: Optional[list] = None,
677
+ credentials: Optional[str] = None,
678
+ #
679
+ **kwargs,
680
+ ) -> Union[WorkflowServiceBatchResponse, WorkflowServiceStreamResponse]:
681
+ return await evaluator(
682
+ data=request.data,
683
+ #
684
+ interface=request.interface,
685
+ configuration=request.configuration,
686
+ #
687
+ flags=request.flags,
688
+ tags=request.tags,
689
+ meta=request.meta,
690
+ #
691
+ references=request.references,
692
+ links=request.links,
693
+ #
694
+ **kwargs,
695
+ )().invoke(
696
+ request=request,
697
+ #
698
+ secrets=secrets,
699
+ credentials=credentials,
700
+ #
701
+ **kwargs,
702
+ )
127
703
 
128
- def make_interface_wrapper(self, path, schemas):
129
- async def interface_wrapper() -> WorkflowServiceResponse:
130
- return WorkflowServiceInterface(
131
- schemas={path: schemas},
132
- )
133
704
 
134
- return interface_wrapper
705
+ async def inspect_evaluator(
706
+ request: WorkflowServiceRequest,
707
+ #
708
+ credentials: Optional[str] = None,
709
+ #
710
+ **kwargs,
711
+ ) -> WorkflowServiceRequest:
712
+ return await evaluator(
713
+ data=request.data,
714
+ #
715
+ interface=request.interface,
716
+ configuration=request.configuration,
717
+ #
718
+ flags=request.flags,
719
+ tags=request.tags,
720
+ meta=request.meta,
721
+ #
722
+ references=request.references,
723
+ links=request.links,
724
+ )().inspect(
725
+ credentials=credentials,
726
+ #
727
+ **kwargs,
728
+ )