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.
- agenta/__init__.py +12 -3
- agenta/client/__init__.py +4 -4
- agenta/client/backend/__init__.py +4 -4
- agenta/client/backend/api_keys/client.py +2 -2
- agenta/client/backend/billing/client.py +2 -2
- agenta/client/backend/billing/raw_client.py +2 -2
- agenta/client/backend/client.py +56 -48
- agenta/client/backend/core/client_wrapper.py +2 -2
- agenta/client/backend/core/file.py +3 -1
- agenta/client/backend/core/http_client.py +3 -3
- agenta/client/backend/core/pydantic_utilities.py +13 -3
- agenta/client/backend/human_evaluations/client.py +2 -2
- agenta/client/backend/human_evaluations/raw_client.py +2 -2
- agenta/client/backend/organization/client.py +46 -34
- agenta/client/backend/organization/raw_client.py +32 -26
- agenta/client/backend/raw_client.py +26 -26
- agenta/client/backend/testsets/client.py +18 -18
- agenta/client/backend/testsets/raw_client.py +30 -30
- agenta/client/backend/types/__init__.py +4 -4
- agenta/client/backend/types/account_request.py +3 -1
- agenta/client/backend/types/account_response.py +3 -1
- agenta/client/backend/types/agenta_node_dto.py +3 -1
- agenta/client/backend/types/agenta_nodes_response.py +3 -1
- agenta/client/backend/types/agenta_root_dto.py +3 -1
- agenta/client/backend/types/agenta_roots_response.py +3 -1
- agenta/client/backend/types/agenta_tree_dto.py +3 -1
- agenta/client/backend/types/agenta_trees_response.py +3 -1
- agenta/client/backend/types/aggregated_result.py +3 -1
- agenta/client/backend/types/analytics_response.py +3 -1
- agenta/client/backend/types/annotation.py +6 -4
- agenta/client/backend/types/annotation_create.py +3 -1
- agenta/client/backend/types/annotation_edit.py +3 -1
- agenta/client/backend/types/annotation_link.py +3 -1
- agenta/client/backend/types/annotation_link_response.py +3 -1
- agenta/client/backend/types/annotation_query.py +3 -1
- agenta/client/backend/types/annotation_query_request.py +3 -1
- agenta/client/backend/types/annotation_reference.py +3 -1
- agenta/client/backend/types/annotation_references.py +3 -1
- agenta/client/backend/types/annotation_response.py +3 -1
- agenta/client/backend/types/annotations_response.py +3 -1
- agenta/client/backend/types/app.py +3 -1
- agenta/client/backend/types/app_variant_response.py +3 -1
- agenta/client/backend/types/app_variant_revision.py +3 -1
- agenta/client/backend/types/artifact.py +6 -4
- agenta/client/backend/types/base_output.py +3 -1
- agenta/client/backend/types/body_fetch_workflow_revision.py +3 -1
- agenta/client/backend/types/body_import_testset.py +3 -1
- agenta/client/backend/types/bucket_dto.py +3 -1
- agenta/client/backend/types/collect_status_response.py +3 -1
- agenta/client/backend/types/config_db.py +3 -1
- agenta/client/backend/types/config_dto.py +3 -1
- agenta/client/backend/types/config_response_model.py +3 -1
- agenta/client/backend/types/correct_answer.py +3 -1
- agenta/client/backend/types/create_app_output.py +3 -1
- agenta/client/backend/types/custom_model_settings_dto.py +3 -1
- agenta/client/backend/types/custom_provider_dto.py +3 -1
- agenta/client/backend/types/custom_provider_kind.py +1 -1
- agenta/client/backend/types/custom_provider_settings_dto.py +3 -1
- agenta/client/backend/types/delete_evaluation.py +3 -1
- agenta/client/backend/types/environment_output.py +3 -1
- agenta/client/backend/types/environment_output_extended.py +3 -1
- agenta/client/backend/types/environment_revision.py +3 -1
- agenta/client/backend/types/error.py +3 -1
- agenta/client/backend/types/evaluation.py +3 -1
- agenta/client/backend/types/evaluation_scenario.py +3 -1
- agenta/client/backend/types/evaluation_scenario_input.py +3 -1
- agenta/client/backend/types/evaluation_scenario_output.py +3 -1
- agenta/client/backend/types/evaluation_scenario_result.py +3 -1
- agenta/client/backend/types/evaluator.py +6 -4
- agenta/client/backend/types/evaluator_config.py +6 -4
- agenta/client/backend/types/evaluator_flags.py +3 -1
- agenta/client/backend/types/evaluator_mapping_output_interface.py +3 -1
- agenta/client/backend/types/evaluator_output_interface.py +3 -1
- agenta/client/backend/types/evaluator_query.py +3 -1
- agenta/client/backend/types/evaluator_query_request.py +3 -1
- agenta/client/backend/types/evaluator_request.py +3 -1
- agenta/client/backend/types/evaluator_response.py +3 -1
- agenta/client/backend/types/evaluators_response.py +3 -1
- agenta/client/backend/types/exception_dto.py +3 -1
- agenta/client/backend/types/extended_o_tel_tracing_response.py +3 -1
- agenta/client/backend/types/get_config_response.py +3 -1
- agenta/client/backend/types/header.py +3 -1
- agenta/client/backend/types/http_validation_error.py +3 -1
- agenta/client/backend/types/human_evaluation.py +3 -1
- agenta/client/backend/types/human_evaluation_scenario.py +3 -1
- agenta/client/backend/types/human_evaluation_scenario_input.py +3 -1
- agenta/client/backend/types/human_evaluation_scenario_output.py +3 -1
- agenta/client/backend/types/invite_request.py +3 -1
- agenta/client/backend/types/legacy_analytics_response.py +3 -1
- agenta/client/backend/types/legacy_data_point.py +3 -1
- agenta/client/backend/types/legacy_evaluator.py +3 -1
- agenta/client/backend/types/legacy_scope_request.py +3 -1
- agenta/client/backend/types/legacy_scopes_response.py +3 -1
- agenta/client/backend/types/legacy_subscription_request.py +3 -1
- agenta/client/backend/types/legacy_user_request.py +3 -1
- agenta/client/backend/types/legacy_user_response.py +3 -1
- agenta/client/backend/types/lifecycle_dto.py +3 -1
- agenta/client/backend/types/link_dto.py +3 -1
- agenta/client/backend/types/list_api_keys_response.py +3 -1
- agenta/client/backend/types/llm_run_rate_limit.py +3 -1
- agenta/client/backend/types/meta_request.py +3 -1
- agenta/client/backend/types/metrics_dto.py +3 -1
- agenta/client/backend/types/new_testset.py +3 -1
- agenta/client/backend/types/node_dto.py +3 -1
- agenta/client/backend/types/o_tel_context_dto.py +3 -1
- agenta/client/backend/types/o_tel_event.py +6 -4
- agenta/client/backend/types/o_tel_event_dto.py +3 -1
- agenta/client/backend/types/o_tel_extra_dto.py +3 -1
- agenta/client/backend/types/o_tel_flat_span.py +6 -4
- agenta/client/backend/types/o_tel_link.py +6 -4
- agenta/client/backend/types/o_tel_link_dto.py +3 -1
- agenta/client/backend/types/o_tel_links_response.py +3 -1
- agenta/client/backend/types/o_tel_span.py +1 -1
- agenta/client/backend/types/o_tel_span_dto.py +3 -1
- agenta/client/backend/types/o_tel_spans_tree.py +3 -1
- agenta/client/backend/types/o_tel_tracing_data_response.py +3 -1
- agenta/client/backend/types/o_tel_tracing_request.py +3 -1
- agenta/client/backend/types/o_tel_tracing_response.py +3 -1
- agenta/client/backend/types/organization.py +3 -1
- agenta/client/backend/types/organization_details.py +3 -1
- agenta/client/backend/types/organization_membership_request.py +3 -1
- agenta/client/backend/types/organization_output.py +3 -1
- agenta/client/backend/types/organization_request.py +3 -1
- agenta/client/backend/types/parent_dto.py +3 -1
- agenta/client/backend/types/project_membership_request.py +3 -1
- agenta/client/backend/types/project_request.py +3 -1
- agenta/client/backend/types/project_scope.py +3 -1
- agenta/client/backend/types/projects_response.py +3 -1
- agenta/client/backend/types/reference.py +6 -4
- agenta/client/backend/types/reference_dto.py +3 -1
- agenta/client/backend/types/reference_request_model.py +3 -1
- agenta/client/backend/types/result.py +3 -1
- agenta/client/backend/types/root_dto.py +3 -1
- agenta/client/backend/types/scopes_response_model.py +3 -1
- agenta/client/backend/types/secret_dto.py +3 -1
- agenta/client/backend/types/secret_response_dto.py +3 -1
- agenta/client/backend/types/simple_evaluation_output.py +3 -1
- agenta/client/backend/types/span_dto.py +6 -4
- agenta/client/backend/types/standard_provider_dto.py +3 -1
- agenta/client/backend/types/standard_provider_settings_dto.py +3 -1
- agenta/client/backend/types/status_dto.py +3 -1
- agenta/client/backend/types/tags_request.py +3 -1
- agenta/client/backend/types/testcase_response.py +6 -4
- agenta/client/backend/types/testset.py +6 -4
- agenta/client/backend/types/{test_set_output_response.py → testset_output_response.py} +4 -2
- agenta/client/backend/types/testset_request.py +3 -1
- agenta/client/backend/types/testset_response.py +3 -1
- agenta/client/backend/types/{test_set_simple_response.py → testset_simple_response.py} +4 -2
- agenta/client/backend/types/testsets_response.py +3 -1
- agenta/client/backend/types/time_dto.py +3 -1
- agenta/client/backend/types/tree_dto.py +3 -1
- agenta/client/backend/types/update_app_output.py +3 -1
- agenta/client/backend/types/user_request.py +3 -1
- agenta/client/backend/types/validation_error.py +3 -1
- agenta/client/backend/types/workflow_artifact.py +6 -4
- agenta/client/backend/types/workflow_data.py +3 -1
- agenta/client/backend/types/workflow_flags.py +3 -1
- agenta/client/backend/types/workflow_request.py +3 -1
- agenta/client/backend/types/workflow_response.py +3 -1
- agenta/client/backend/types/workflow_revision.py +6 -4
- agenta/client/backend/types/workflow_revision_request.py +3 -1
- agenta/client/backend/types/workflow_revision_response.py +3 -1
- agenta/client/backend/types/workflow_revisions_response.py +3 -1
- agenta/client/backend/types/workflow_variant.py +6 -4
- agenta/client/backend/types/workflow_variant_request.py +3 -1
- agenta/client/backend/types/workflow_variant_response.py +3 -1
- agenta/client/backend/types/workflow_variants_response.py +3 -1
- agenta/client/backend/types/workflows_response.py +3 -1
- agenta/client/backend/types/workspace.py +3 -1
- agenta/client/backend/types/workspace_member_response.py +3 -1
- agenta/client/backend/types/workspace_membership_request.py +3 -1
- agenta/client/backend/types/workspace_permission.py +3 -1
- agenta/client/backend/types/workspace_request.py +3 -1
- agenta/client/backend/types/workspace_response.py +3 -1
- agenta/client/backend/workspace/client.py +2 -2
- agenta/client/client.py +102 -88
- agenta/sdk/__init__.py +52 -3
- agenta/sdk/agenta_init.py +43 -16
- agenta/sdk/assets.py +22 -15
- agenta/sdk/context/serving.py +20 -8
- agenta/sdk/context/tracing.py +40 -22
- agenta/sdk/contexts/__init__.py +0 -0
- agenta/sdk/contexts/routing.py +38 -0
- agenta/sdk/contexts/running.py +57 -0
- agenta/sdk/contexts/tracing.py +86 -0
- agenta/sdk/decorators/__init__.py +1 -0
- agenta/sdk/decorators/routing.py +284 -0
- agenta/sdk/decorators/running.py +692 -98
- agenta/sdk/decorators/serving.py +20 -21
- agenta/sdk/decorators/tracing.py +176 -131
- agenta/sdk/engines/__init__.py +0 -0
- agenta/sdk/engines/running/__init__.py +0 -0
- agenta/sdk/engines/running/utils.py +17 -0
- agenta/sdk/engines/tracing/__init__.py +1 -0
- agenta/sdk/engines/tracing/attributes.py +185 -0
- agenta/sdk/engines/tracing/conventions.py +49 -0
- agenta/sdk/engines/tracing/exporters.py +130 -0
- agenta/sdk/engines/tracing/inline.py +1154 -0
- agenta/sdk/engines/tracing/processors.py +190 -0
- agenta/sdk/engines/tracing/propagation.py +102 -0
- agenta/sdk/engines/tracing/spans.py +136 -0
- agenta/sdk/engines/tracing/tracing.py +324 -0
- agenta/sdk/evaluations/__init__.py +2 -0
- agenta/sdk/evaluations/metrics.py +37 -0
- agenta/sdk/evaluations/preview/__init__.py +0 -0
- agenta/sdk/evaluations/preview/evaluate.py +765 -0
- agenta/sdk/evaluations/preview/utils.py +861 -0
- agenta/sdk/evaluations/results.py +66 -0
- agenta/sdk/evaluations/runs.py +153 -0
- agenta/sdk/evaluations/scenarios.py +48 -0
- agenta/sdk/litellm/litellm.py +12 -0
- agenta/sdk/litellm/mockllm.py +6 -8
- agenta/sdk/litellm/mocks/__init__.py +5 -5
- agenta/sdk/managers/applications.py +304 -0
- agenta/sdk/managers/config.py +2 -2
- agenta/sdk/managers/evaluations.py +0 -0
- agenta/sdk/managers/evaluators.py +303 -0
- agenta/sdk/managers/secrets.py +161 -24
- agenta/sdk/managers/shared.py +3 -1
- agenta/sdk/managers/testsets.py +441 -0
- agenta/sdk/managers/vault.py +3 -3
- agenta/sdk/middleware/auth.py +0 -176
- agenta/sdk/middleware/vault.py +203 -8
- agenta/sdk/middlewares/__init__.py +0 -0
- agenta/sdk/middlewares/routing/__init__.py +0 -0
- agenta/sdk/middlewares/routing/auth.py +263 -0
- agenta/sdk/middlewares/routing/cors.py +30 -0
- agenta/sdk/middlewares/routing/otel.py +29 -0
- agenta/sdk/middlewares/running/__init__.py +0 -0
- agenta/sdk/middlewares/running/normalizer.py +321 -0
- agenta/sdk/middlewares/running/resolver.py +161 -0
- agenta/sdk/middlewares/running/vault.py +140 -0
- agenta/sdk/models/__init__.py +0 -0
- agenta/sdk/models/blobs.py +33 -0
- agenta/sdk/models/evaluations.py +119 -0
- agenta/sdk/models/git.py +126 -0
- agenta/sdk/models/shared.py +167 -0
- agenta/sdk/models/testsets.py +163 -0
- agenta/sdk/models/tracing.py +202 -0
- agenta/sdk/models/workflows.py +753 -0
- agenta/sdk/tracing/exporters.py +67 -17
- agenta/sdk/tracing/processors.py +97 -0
- agenta/sdk/tracing/propagation.py +3 -1
- agenta/sdk/tracing/spans.py +4 -0
- agenta/sdk/tracing/tracing.py +13 -13
- agenta/sdk/types.py +211 -17
- agenta/sdk/utils/cache.py +1 -1
- agenta/sdk/utils/client.py +38 -0
- agenta/sdk/utils/helpers.py +13 -12
- agenta/sdk/utils/logging.py +18 -78
- agenta/sdk/utils/references.py +23 -0
- agenta/sdk/workflows/builtin.py +600 -0
- agenta/sdk/workflows/configurations.py +22 -0
- agenta/sdk/workflows/errors.py +292 -0
- agenta/sdk/workflows/handlers.py +1791 -0
- agenta/sdk/workflows/interfaces.py +948 -0
- agenta/sdk/workflows/sandbox.py +118 -0
- agenta/sdk/workflows/utils.py +303 -6
- {agenta-0.57.0.dist-info → agenta-0.63.2.dist-info}/METADATA +33 -30
- agenta-0.63.2.dist-info/RECORD +421 -0
- agenta/sdk/middleware/adapt.py +0 -253
- agenta/sdk/middleware/base.py +0 -40
- agenta/sdk/middleware/flags.py +0 -40
- agenta/sdk/workflows/types.py +0 -472
- agenta-0.57.0.dist-info/RECORD +0 -371
- /agenta/sdk/{workflows → engines/running}/registry.py +0 -0
- {agenta-0.57.0.dist-info → agenta-0.63.2.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
from json import loads, dumps
|
|
2
|
+
from typing import Optional, Union, Sequence, Any, Dict
|
|
3
|
+
|
|
4
|
+
Primitive = Union[str, int, float, bool, bytes]
|
|
5
|
+
PrimitivesSequence = Sequence[Primitive]
|
|
6
|
+
Attribute = Union[Primitive, PrimitivesSequence]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _marshal(
|
|
10
|
+
unmarshalled: Dict[str, Any],
|
|
11
|
+
*,
|
|
12
|
+
parent_key: Optional[str] = "",
|
|
13
|
+
depth: Optional[int] = 0,
|
|
14
|
+
max_depth: Optional[int] = None,
|
|
15
|
+
) -> Dict[str, Any]:
|
|
16
|
+
"""
|
|
17
|
+
Marshals a dictionary of unmarshalled attributes into a flat dictionary
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
unmarshalled = {
|
|
21
|
+
"ag": {
|
|
22
|
+
"type": "tree",
|
|
23
|
+
"node": {
|
|
24
|
+
"name": "root",
|
|
25
|
+
"children": [
|
|
26
|
+
{
|
|
27
|
+
"name": "child1",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "child2",
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
marshalled = {
|
|
37
|
+
"ag.type": "tree",
|
|
38
|
+
"ag.node.name": "root",
|
|
39
|
+
"ag.node.children.0.name": "child1",
|
|
40
|
+
"ag.node.children.1.name": "child2"
|
|
41
|
+
}
|
|
42
|
+
"""
|
|
43
|
+
marshalled = {}
|
|
44
|
+
|
|
45
|
+
# If max_depth is set and we've reached it,
|
|
46
|
+
# just return the unmarshalled attributes
|
|
47
|
+
if max_depth is not None and depth >= max_depth:
|
|
48
|
+
marshalled[parent_key] = unmarshalled
|
|
49
|
+
# MISSING ENCODING TO JSON IF NOT PRIMITIVE
|
|
50
|
+
|
|
51
|
+
return marshalled
|
|
52
|
+
|
|
53
|
+
# Otherwise,
|
|
54
|
+
# iterate over the unmarshalled attributes and marshall them
|
|
55
|
+
for key, value in unmarshalled.items():
|
|
56
|
+
child_key = f"{parent_key}.{key}" if parent_key else key
|
|
57
|
+
|
|
58
|
+
if isinstance(value, dict):
|
|
59
|
+
dict_key = child_key
|
|
60
|
+
|
|
61
|
+
marshalled.update(
|
|
62
|
+
_marshal(
|
|
63
|
+
value,
|
|
64
|
+
parent_key=dict_key,
|
|
65
|
+
depth=depth + 1,
|
|
66
|
+
max_depth=max_depth,
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
elif isinstance(value, list):
|
|
70
|
+
if max_depth is not None and depth + 1 >= max_depth:
|
|
71
|
+
marshalled[child_key] = value
|
|
72
|
+
# MISSING ENCODING TO JSON IF NOT PRIMITIVE
|
|
73
|
+
else:
|
|
74
|
+
for i, item in enumerate(value):
|
|
75
|
+
list_key = f"{child_key}.{i}"
|
|
76
|
+
|
|
77
|
+
if isinstance(item, (dict, list)):
|
|
78
|
+
marshalled.update(
|
|
79
|
+
_marshal(
|
|
80
|
+
item,
|
|
81
|
+
parent_key=list_key,
|
|
82
|
+
depth=depth + 1,
|
|
83
|
+
max_depth=max_depth,
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
else:
|
|
87
|
+
marshalled[list_key] = item
|
|
88
|
+
# MISSING ENCODING TO JSON IF NOT PRIMITIVE
|
|
89
|
+
else:
|
|
90
|
+
marshalled[child_key] = value
|
|
91
|
+
# MISSING ENCODING TO JSON IF NOT PRIMITIVE
|
|
92
|
+
|
|
93
|
+
return marshalled
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _encode_key(
|
|
97
|
+
namespace: Optional[str] = None,
|
|
98
|
+
key: str = "",
|
|
99
|
+
) -> str:
|
|
100
|
+
if namespace is None:
|
|
101
|
+
return key
|
|
102
|
+
|
|
103
|
+
return f"ag.{namespace}.{key}"
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _make_serializable(value: Any) -> Any:
|
|
107
|
+
"""
|
|
108
|
+
Transform complex nested structures into JSON-serializable form.
|
|
109
|
+
Handles Pydantic models, nested dictionaries and lists recursively.
|
|
110
|
+
"""
|
|
111
|
+
if value is None or isinstance(value, (str, int, float, bool, bytes)):
|
|
112
|
+
return value
|
|
113
|
+
|
|
114
|
+
# Handle Pydantic objects (prioritize v2 over v1 API)
|
|
115
|
+
if hasattr(value, "model_dump"): # Pydantic v2
|
|
116
|
+
return value.model_dump()
|
|
117
|
+
elif hasattr(value, "dict"): # Pydantic v1
|
|
118
|
+
return value.dict()
|
|
119
|
+
|
|
120
|
+
if isinstance(value, dict):
|
|
121
|
+
try:
|
|
122
|
+
# Test serialization without modifying - optimizes for already-serializable dicts
|
|
123
|
+
dumps(
|
|
124
|
+
value
|
|
125
|
+
) # If serialization fails, we'll catch the exception and process deeply
|
|
126
|
+
return value # Avoid unnecessary recursion for serializable dicts
|
|
127
|
+
except TypeError:
|
|
128
|
+
return {k: _make_serializable(v) for k, v in value.items()}
|
|
129
|
+
elif isinstance(value, list):
|
|
130
|
+
try:
|
|
131
|
+
# Test serialization without modifying - optimizes for already-serializable lists
|
|
132
|
+
dumps(
|
|
133
|
+
value
|
|
134
|
+
) # If serialization fails, we'll catch the exception and process deeply
|
|
135
|
+
return value # Avoid unnecessary recursion for serializable lists
|
|
136
|
+
except TypeError:
|
|
137
|
+
return [_make_serializable(item) for item in value]
|
|
138
|
+
|
|
139
|
+
return repr(value)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _encode_value(value: Any) -> Optional[Attribute]:
|
|
143
|
+
"""
|
|
144
|
+
Encode values for tracing, ensuring proper JSON serialization.
|
|
145
|
+
Adds the @ag.type=json: prefix only to appropriate values.
|
|
146
|
+
"""
|
|
147
|
+
if value is None:
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
if isinstance(value, (str, int, float, bool, bytes)):
|
|
151
|
+
return value
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
if (
|
|
155
|
+
isinstance(value, (dict, list))
|
|
156
|
+
or hasattr(value, "model_dump")
|
|
157
|
+
or hasattr(value, "dict")
|
|
158
|
+
):
|
|
159
|
+
serializable_value = _make_serializable(value)
|
|
160
|
+
return "@ag.type=json:" + dumps(serializable_value)
|
|
161
|
+
except TypeError:
|
|
162
|
+
pass
|
|
163
|
+
|
|
164
|
+
return repr(value)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def serialize(
|
|
168
|
+
*,
|
|
169
|
+
namespace: str,
|
|
170
|
+
attributes: Dict[str, Any],
|
|
171
|
+
max_depth: Optional[int] = None,
|
|
172
|
+
) -> Dict[str, str]:
|
|
173
|
+
if not isinstance(attributes, dict):
|
|
174
|
+
return {}
|
|
175
|
+
|
|
176
|
+
_attributes = {
|
|
177
|
+
k: v
|
|
178
|
+
for k, v in {
|
|
179
|
+
_encode_key(namespace, key): _encode_value(value)
|
|
180
|
+
for key, value in _marshal(attributes, max_depth=max_depth).items()
|
|
181
|
+
}.items()
|
|
182
|
+
if v is not None
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return _attributes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from re import fullmatch
|
|
3
|
+
|
|
4
|
+
from opentelemetry.trace import SpanKind
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Reference(str, Enum):
|
|
8
|
+
#
|
|
9
|
+
VARIANT_ID = "variant.id"
|
|
10
|
+
VARIANT_SLUG = "variant.slug"
|
|
11
|
+
VARIANT_VERSION = "variant.version"
|
|
12
|
+
#
|
|
13
|
+
ENVIRONMENT_ID = "environment.id"
|
|
14
|
+
ENVIRONMENT_SLUG = "environment.slug"
|
|
15
|
+
ENVIRONMENT_VERSION = "environment.version"
|
|
16
|
+
#
|
|
17
|
+
APPLICATION_ID = "application.id"
|
|
18
|
+
APPLICATION_SLUG = "application.slug"
|
|
19
|
+
#
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
_PATTERN = r"[A-Za-z0-9._-]+"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def is_valid_attribute_key(
|
|
26
|
+
string: str,
|
|
27
|
+
):
|
|
28
|
+
return bool(fullmatch(_PATTERN, string))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def parse_span_kind(type: str) -> SpanKind:
|
|
32
|
+
kind = SpanKind.INTERNAL
|
|
33
|
+
if type in [
|
|
34
|
+
"agent",
|
|
35
|
+
"chain",
|
|
36
|
+
"workflow",
|
|
37
|
+
]:
|
|
38
|
+
kind = SpanKind.SERVER
|
|
39
|
+
elif type in [
|
|
40
|
+
"tool",
|
|
41
|
+
"embedding",
|
|
42
|
+
"query",
|
|
43
|
+
"completion",
|
|
44
|
+
"chat",
|
|
45
|
+
"rerank",
|
|
46
|
+
]:
|
|
47
|
+
kind = SpanKind.CLIENT
|
|
48
|
+
|
|
49
|
+
return kind
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from typing import Sequence, Dict, List, Optional
|
|
2
|
+
|
|
3
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
4
|
+
from opentelemetry.sdk.trace.export import (
|
|
5
|
+
ConsoleSpanExporter,
|
|
6
|
+
SpanExporter,
|
|
7
|
+
SpanExportResult,
|
|
8
|
+
ReadableSpan,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from agenta.sdk.utils.logging import get_module_logger
|
|
12
|
+
from agenta.sdk.utils.exceptions import suppress
|
|
13
|
+
from agenta.sdk.utils.cache import TTLLRUCache
|
|
14
|
+
from agenta.sdk.contexts.tracing import (
|
|
15
|
+
otlp_context_manager,
|
|
16
|
+
otlp_context,
|
|
17
|
+
OTLPContext,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
log = get_module_logger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class InlineTraceExporter(SpanExporter):
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
registry: Dict[str, List[ReadableSpan]],
|
|
28
|
+
):
|
|
29
|
+
self._shutdown = False
|
|
30
|
+
self._registry = registry
|
|
31
|
+
|
|
32
|
+
def export(
|
|
33
|
+
self,
|
|
34
|
+
spans: Sequence[ReadableSpan],
|
|
35
|
+
) -> SpanExportResult:
|
|
36
|
+
if self._shutdown:
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
with suppress():
|
|
40
|
+
for span in spans:
|
|
41
|
+
trace_id = span.get_span_context().trace_id
|
|
42
|
+
|
|
43
|
+
if trace_id not in self._registry:
|
|
44
|
+
self._registry[trace_id] = []
|
|
45
|
+
|
|
46
|
+
self._registry[trace_id].append(span)
|
|
47
|
+
|
|
48
|
+
def shutdown(self) -> None:
|
|
49
|
+
self._shutdown = True
|
|
50
|
+
|
|
51
|
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
def is_ready(
|
|
55
|
+
self,
|
|
56
|
+
trace_id: int,
|
|
57
|
+
) -> bool:
|
|
58
|
+
is_ready = trace_id in self._registry
|
|
59
|
+
return is_ready
|
|
60
|
+
|
|
61
|
+
def fetch(
|
|
62
|
+
self,
|
|
63
|
+
trace_id: int,
|
|
64
|
+
) -> List[ReadableSpan]:
|
|
65
|
+
trace = self._registry.get(trace_id, [])
|
|
66
|
+
|
|
67
|
+
if trace_id in self._registry:
|
|
68
|
+
del self._registry[trace_id]
|
|
69
|
+
|
|
70
|
+
return trace
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class OTLPExporter(OTLPSpanExporter):
|
|
74
|
+
_MAX_RETRY_TIMEOUT = 2
|
|
75
|
+
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
*args,
|
|
79
|
+
credentials: Optional[TTLLRUCache] = None,
|
|
80
|
+
**kwargs,
|
|
81
|
+
):
|
|
82
|
+
super().__init__(*args, **kwargs)
|
|
83
|
+
|
|
84
|
+
self.credentials = credentials
|
|
85
|
+
|
|
86
|
+
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
|
|
87
|
+
grouped_spans: Dict[str, List[str]] = {}
|
|
88
|
+
|
|
89
|
+
for span in spans:
|
|
90
|
+
trace_id = span.get_span_context().trace_id
|
|
91
|
+
|
|
92
|
+
credentials = None
|
|
93
|
+
if self.credentials:
|
|
94
|
+
credentials = self.credentials.get(trace_id)
|
|
95
|
+
|
|
96
|
+
if credentials not in grouped_spans:
|
|
97
|
+
grouped_spans[credentials] = []
|
|
98
|
+
|
|
99
|
+
grouped_spans[credentials].append(span)
|
|
100
|
+
|
|
101
|
+
serialized_spans = []
|
|
102
|
+
|
|
103
|
+
for credentials, _spans in grouped_spans.items():
|
|
104
|
+
with otlp_context_manager(
|
|
105
|
+
context=OTLPContext(
|
|
106
|
+
credentials=credentials,
|
|
107
|
+
)
|
|
108
|
+
):
|
|
109
|
+
serialized_spans.append(super().export(_spans))
|
|
110
|
+
|
|
111
|
+
if all(serialized_spans):
|
|
112
|
+
return SpanExportResult.SUCCESS
|
|
113
|
+
else:
|
|
114
|
+
return SpanExportResult.FAILURE
|
|
115
|
+
|
|
116
|
+
def _export(self, serialized_data: bytes, timeout_sec: Optional[float] = None):
|
|
117
|
+
credentials = otlp_context.get().credentials
|
|
118
|
+
|
|
119
|
+
if credentials:
|
|
120
|
+
self._session.headers.update({"Authorization": credentials})
|
|
121
|
+
|
|
122
|
+
with suppress():
|
|
123
|
+
if timeout_sec is not None:
|
|
124
|
+
return super()._export(serialized_data, timeout_sec)
|
|
125
|
+
else:
|
|
126
|
+
return super()._export(serialized_data)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
ConsoleExporter = ConsoleSpanExporter
|
|
130
|
+
InlineExporter = InlineTraceExporter
|