uipath 2.1.17__py3-none-any.whl → 2.1.20__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.
- uipath/_cli/_evals/_evaluators/__init__.py +6 -4
- uipath/_cli/_evals/_evaluators/_deterministic_evaluator_base.py +46 -0
- uipath/_cli/_evals/_evaluators/_evaluator_factory.py +42 -22
- uipath/_cli/_evals/_evaluators/_exact_match_evaluator.py +40 -0
- uipath/_cli/_evals/_evaluators/_json_similarity_evaluator.py +168 -0
- uipath/_cli/_evals/_evaluators/_llm_as_judge_evaluator.py +2 -0
- uipath/_cli/_evals/_models/_evaluators.py +11 -10
- uipath/_cli/_evals/progress_reporter.py +15 -4
- uipath/_cli/_runtime/_runtime.py +43 -7
- uipath/_cli/_utils/_input_args.py +28 -1
- uipath/_cli/_utils/_parse_ast.py +12 -0
- uipath/_cli/_utils/_project_files.py +7 -0
- uipath/_services/connections_service.py +82 -2
- uipath/_utils/_infer_bindings.py +5 -2
- uipath/models/__init__.py +2 -1
- uipath/models/connections.py +17 -0
- uipath/utils/_endpoints_manager.py +123 -24
- {uipath-2.1.17.dist-info → uipath-2.1.20.dist-info}/METADATA +1 -1
- {uipath-2.1.17.dist-info → uipath-2.1.20.dist-info}/RECORD +22 -21
- uipath/_cli/_evals/_evaluators/_agent_scorer_evaluator.py +0 -48
- uipath/_cli/_evals/_evaluators/_deterministic_evaluator.py +0 -41
- {uipath-2.1.17.dist-info → uipath-2.1.20.dist-info}/WHEEL +0 -0
- {uipath-2.1.17.dist-info → uipath-2.1.20.dist-info}/entry_points.txt +0 -0
- {uipath-2.1.17.dist-info → uipath-2.1.20.dist-info}/licenses/LICENSE +0 -0
@@ -3,18 +3,20 @@
|
|
3
3
|
This package contains all evaluator types and the factory for creating them.
|
4
4
|
"""
|
5
5
|
|
6
|
-
from .
|
7
|
-
from ._deterministic_evaluator import DeterministicEvaluator
|
6
|
+
from ._deterministic_evaluator_base import DeterministicEvaluatorBase
|
8
7
|
from ._evaluator_base import EvaluatorBase
|
9
8
|
from ._evaluator_factory import EvaluatorFactory
|
9
|
+
from ._exact_match_evaluator import ExactMatchEvaluator
|
10
|
+
from ._json_similarity_evaluator import JsonSimilarityEvaluator
|
10
11
|
from ._llm_as_judge_evaluator import LlmAsAJudgeEvaluator
|
11
12
|
from ._trajectory_evaluator import TrajectoryEvaluator
|
12
13
|
|
13
14
|
__all__ = [
|
14
15
|
"EvaluatorBase",
|
16
|
+
"DeterministicEvaluatorBase",
|
15
17
|
"EvaluatorFactory",
|
16
|
-
"
|
18
|
+
"JsonSimilarityEvaluator",
|
19
|
+
"ExactMatchEvaluator",
|
17
20
|
"LlmAsAJudgeEvaluator",
|
18
|
-
"AgentScorerEvaluator",
|
19
21
|
"TrajectoryEvaluator",
|
20
22
|
]
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import copy
|
2
|
+
import json
|
3
|
+
from abc import ABC
|
4
|
+
from typing import Any, Dict, Tuple
|
5
|
+
|
6
|
+
from ._evaluator_base import EvaluatorBase
|
7
|
+
|
8
|
+
|
9
|
+
class DeterministicEvaluatorBase(EvaluatorBase, ABC):
|
10
|
+
def __init__(self, target_output_key: str = "*"):
|
11
|
+
super().__init__()
|
12
|
+
self.target_output_key = target_output_key
|
13
|
+
|
14
|
+
def _select_targets(
|
15
|
+
self, expected_output: Dict[str, Any], actual_output: Dict[str, Any]
|
16
|
+
) -> Tuple[Any, Any]:
|
17
|
+
actual_output_copy = copy.deepcopy(actual_output)
|
18
|
+
expected_output_copy = copy.deepcopy(expected_output)
|
19
|
+
if self.target_output_key != "*":
|
20
|
+
if (
|
21
|
+
self.target_output_key not in actual_output
|
22
|
+
or self.target_output_key not in expected_output
|
23
|
+
):
|
24
|
+
raise ValueError(
|
25
|
+
f"Field '{self.target_output_key}' missing from expected or actual output"
|
26
|
+
)
|
27
|
+
actual_output_copy = actual_output_copy[self.target_output_key]
|
28
|
+
expected_output_copy = expected_output[self.target_output_key]
|
29
|
+
return actual_output_copy, expected_output_copy
|
30
|
+
|
31
|
+
def _canonical_json(self, obj: Any) -> str:
|
32
|
+
return json.dumps(
|
33
|
+
self._normalize_numbers(obj),
|
34
|
+
sort_keys=True,
|
35
|
+
separators=(",", ":"),
|
36
|
+
ensure_ascii=False,
|
37
|
+
)
|
38
|
+
|
39
|
+
def _normalize_numbers(self, obj: Any) -> Any:
|
40
|
+
if isinstance(obj, dict):
|
41
|
+
return {k: self._normalize_numbers(v) for k, v in obj.items()}
|
42
|
+
if isinstance(obj, (list, tuple)):
|
43
|
+
return [self._normalize_numbers(v) for v in obj]
|
44
|
+
if isinstance(obj, (int, float)) and not isinstance(obj, bool):
|
45
|
+
return float(obj)
|
46
|
+
return obj
|
@@ -1,9 +1,9 @@
|
|
1
1
|
from typing import Any, Dict
|
2
2
|
|
3
3
|
from .._models import EvaluatorCategory, EvaluatorType
|
4
|
-
from ._agent_scorer_evaluator import AgentScorerEvaluator
|
5
|
-
from ._deterministic_evaluator import DeterministicEvaluator
|
6
4
|
from ._evaluator_base import EvaluatorBase, EvaluatorBaseParams
|
5
|
+
from ._exact_match_evaluator import ExactMatchEvaluator
|
6
|
+
from ._json_similarity_evaluator import JsonSimilarityEvaluator
|
7
7
|
from ._llm_as_judge_evaluator import LlmAsAJudgeEvaluator
|
8
8
|
from ._trajectory_evaluator import TrajectoryEvaluator
|
9
9
|
|
@@ -50,23 +50,50 @@ class EvaluatorFactory:
|
|
50
50
|
)
|
51
51
|
|
52
52
|
# Create evaluator based on category
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
53
|
+
match category:
|
54
|
+
case EvaluatorCategory.Deterministic:
|
55
|
+
if evaluator_type == evaluator_type.Equals:
|
56
|
+
return EvaluatorFactory._create_exact_match_evaluator(
|
57
|
+
base_params, data
|
58
|
+
)
|
59
|
+
elif evaluator_type == evaluator_type.JsonSimilarity:
|
60
|
+
return EvaluatorFactory._create_json_similarity_evaluator(
|
61
|
+
base_params, data
|
62
|
+
)
|
63
|
+
else:
|
64
|
+
raise ValueError(
|
65
|
+
f"Unknown evaluator type {evaluator_type} for category {category}"
|
66
|
+
)
|
67
|
+
case EvaluatorCategory.LlmAsAJudge:
|
68
|
+
return EvaluatorFactory._create_llm_as_judge_evaluator(
|
69
|
+
base_params, data
|
70
|
+
)
|
71
|
+
case EvaluatorCategory.AgentScorer:
|
72
|
+
raise NotImplementedError()
|
73
|
+
case EvaluatorCategory.Trajectory:
|
74
|
+
return EvaluatorFactory._create_trajectory_evaluator(base_params, data)
|
75
|
+
case _:
|
76
|
+
raise ValueError(f"Unknown evaluator category: {category}")
|
63
77
|
|
64
78
|
@staticmethod
|
65
|
-
def
|
79
|
+
def _create_exact_match_evaluator(
|
66
80
|
base_params: EvaluatorBaseParams, data: Dict[str, Any]
|
67
|
-
) ->
|
81
|
+
) -> ExactMatchEvaluator:
|
68
82
|
"""Create a deterministic evaluator."""
|
69
|
-
|
83
|
+
return ExactMatchEvaluator.from_params(
|
84
|
+
base_params,
|
85
|
+
target_output_key=data.get("targetOutputKey", "*"),
|
86
|
+
)
|
87
|
+
|
88
|
+
@staticmethod
|
89
|
+
def _create_json_similarity_evaluator(
|
90
|
+
base_params: EvaluatorBaseParams, data: Dict[str, Any]
|
91
|
+
) -> JsonSimilarityEvaluator:
|
92
|
+
"""Create a deterministic evaluator."""
|
93
|
+
return JsonSimilarityEvaluator.from_params(
|
94
|
+
base_params,
|
95
|
+
target_output_key=data.get("targetOutputKey", "*"),
|
96
|
+
)
|
70
97
|
|
71
98
|
@staticmethod
|
72
99
|
def _create_llm_as_judge_evaluator(
|
@@ -88,13 +115,6 @@ class EvaluatorFactory:
|
|
88
115
|
target_output_key=data.get("targetOutputKey", "*"),
|
89
116
|
)
|
90
117
|
|
91
|
-
@staticmethod
|
92
|
-
def _create_agent_scorer_evaluator(
|
93
|
-
base_params: EvaluatorBaseParams, data: Dict[str, Any]
|
94
|
-
) -> AgentScorerEvaluator:
|
95
|
-
"""Create an agent scorer evaluator."""
|
96
|
-
raise NotImplementedError()
|
97
|
-
|
98
118
|
@staticmethod
|
99
119
|
def _create_trajectory_evaluator(
|
100
120
|
base_params: EvaluatorBaseParams, data: Dict[str, Any]
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import copy
|
2
|
+
from typing import Any, Dict
|
3
|
+
|
4
|
+
from uipath._cli._evals._evaluators._deterministic_evaluator_base import (
|
5
|
+
DeterministicEvaluatorBase,
|
6
|
+
)
|
7
|
+
from uipath._cli._evals._models import EvaluationResult
|
8
|
+
from uipath._cli._evals._models._evaluators import ScoreType
|
9
|
+
|
10
|
+
|
11
|
+
class ExactMatchEvaluator(DeterministicEvaluatorBase):
|
12
|
+
async def evaluate(
|
13
|
+
self,
|
14
|
+
evaluation_id: str,
|
15
|
+
evaluation_name: str,
|
16
|
+
input_data: Dict[str, Any],
|
17
|
+
expected_output: Dict[str, Any],
|
18
|
+
actual_output: Dict[str, Any],
|
19
|
+
) -> EvaluationResult:
|
20
|
+
actual_output_copy = copy.deepcopy(actual_output)
|
21
|
+
expected_output_copy = copy.deepcopy(expected_output)
|
22
|
+
|
23
|
+
actual_output, expected_output = self._select_targets(
|
24
|
+
expected_output, actual_output
|
25
|
+
)
|
26
|
+
are_equal = self._canonical_json(actual_output) == self._canonical_json(
|
27
|
+
expected_output
|
28
|
+
)
|
29
|
+
|
30
|
+
return EvaluationResult(
|
31
|
+
evaluation_id=evaluation_id,
|
32
|
+
evaluation_name=evaluation_name,
|
33
|
+
evaluator_id=self.id,
|
34
|
+
evaluator_name=self.name,
|
35
|
+
score=are_equal,
|
36
|
+
input=input_data,
|
37
|
+
expected_output=expected_output_copy,
|
38
|
+
actual_output=actual_output_copy,
|
39
|
+
score_type=ScoreType.BOOLEAN,
|
40
|
+
)
|
@@ -0,0 +1,168 @@
|
|
1
|
+
import copy
|
2
|
+
import math
|
3
|
+
from typing import Any, Dict, Tuple
|
4
|
+
|
5
|
+
from uipath._cli._evals._evaluators._deterministic_evaluator_base import (
|
6
|
+
DeterministicEvaluatorBase,
|
7
|
+
)
|
8
|
+
from uipath._cli._evals._models import EvaluationResult
|
9
|
+
from uipath._cli._evals._models._evaluators import ScoreType
|
10
|
+
|
11
|
+
|
12
|
+
class JsonSimilarityEvaluator(DeterministicEvaluatorBase):
|
13
|
+
"""Deterministic evaluator that scores structural JSON similarity.
|
14
|
+
|
15
|
+
Compares expected versus actual JSON-like structures and returns a
|
16
|
+
numerical score in the range [0, 100]. The comparison is token-based
|
17
|
+
and tolerant for numbers and strings (via Levenshtein distance).
|
18
|
+
"""
|
19
|
+
|
20
|
+
async def evaluate(
|
21
|
+
self,
|
22
|
+
evaluation_id: str,
|
23
|
+
evaluation_name: str,
|
24
|
+
input_data: Dict[str, Any],
|
25
|
+
expected_output: Dict[str, Any],
|
26
|
+
actual_output: Dict[str, Any],
|
27
|
+
) -> EvaluationResult:
|
28
|
+
"""Evaluate similarity between expected and actual JSON outputs.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
evaluation_id: Unique identifier for this evaluation run.
|
32
|
+
evaluation_name: Human friendly evaluation name.
|
33
|
+
input_data: Input payload used to produce the outputs.
|
34
|
+
expected_output: Ground-truth JSON structure.
|
35
|
+
actual_output: Produced JSON structure to compare against the ground truth.
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
EvaluationResult: Structured result with the numerical similarity score.
|
39
|
+
"""
|
40
|
+
actual_output_copy = copy.deepcopy(actual_output)
|
41
|
+
expected_output_copy = copy.deepcopy(expected_output)
|
42
|
+
|
43
|
+
actual_output, expected_output = self._select_targets(
|
44
|
+
expected_output, actual_output
|
45
|
+
)
|
46
|
+
similarity = self._compare_json(expected_output, actual_output)
|
47
|
+
|
48
|
+
return EvaluationResult(
|
49
|
+
evaluation_id=evaluation_id,
|
50
|
+
evaluation_name=evaluation_name,
|
51
|
+
evaluator_id=self.id,
|
52
|
+
evaluator_name=self.name,
|
53
|
+
score=similarity,
|
54
|
+
input=input_data,
|
55
|
+
expected_output=expected_output_copy,
|
56
|
+
actual_output=actual_output_copy,
|
57
|
+
score_type=ScoreType.NUMERICAL,
|
58
|
+
)
|
59
|
+
|
60
|
+
def _compare_json(self, expected: Any, actual: Any) -> float:
|
61
|
+
matched_leaves, total_leaves = self._compare_tokens(expected, actual)
|
62
|
+
if total_leaves == 0:
|
63
|
+
return 100.0
|
64
|
+
sim = (matched_leaves / total_leaves) * 100.0
|
65
|
+
return max(0.0, min(100.0, sim))
|
66
|
+
|
67
|
+
def _compare_tokens(
|
68
|
+
self, expected_token: Any, actual_token: Any
|
69
|
+
) -> Tuple[float, float]:
|
70
|
+
if self._is_number(expected_token) and self._is_number(actual_token):
|
71
|
+
return self._compare_numbers(float(expected_token), float(actual_token))
|
72
|
+
|
73
|
+
if type(expected_token) is not type(actual_token):
|
74
|
+
return 0.0, self._count_leaves(expected_token)
|
75
|
+
|
76
|
+
if isinstance(expected_token, dict):
|
77
|
+
matched_leaves = total_leaves = 0.0
|
78
|
+
# Only expected keys count
|
79
|
+
for expected_key, expected_value in expected_token.items():
|
80
|
+
if isinstance(actual_token, dict) and expected_key in actual_token:
|
81
|
+
matched, total = self._compare_tokens(
|
82
|
+
expected_value, actual_token[expected_key]
|
83
|
+
)
|
84
|
+
else:
|
85
|
+
matched, total = (0.0, self._count_leaves(expected_value))
|
86
|
+
matched_leaves += matched
|
87
|
+
total_leaves += total
|
88
|
+
return matched_leaves, total_leaves
|
89
|
+
|
90
|
+
if isinstance(expected_token, list):
|
91
|
+
matched_leaves = total_leaves = 0.0
|
92
|
+
common_length = min(len(expected_token), len(actual_token))
|
93
|
+
for index in range(common_length):
|
94
|
+
matched, total = self._compare_tokens(
|
95
|
+
expected_token[index], actual_token[index]
|
96
|
+
)
|
97
|
+
matched_leaves += matched
|
98
|
+
total_leaves += total
|
99
|
+
for index in range(common_length, len(expected_token)):
|
100
|
+
total_leaves += self._count_leaves(expected_token[index])
|
101
|
+
return (matched_leaves, total_leaves)
|
102
|
+
|
103
|
+
if isinstance(expected_token, bool):
|
104
|
+
return (1.0, 1.0) if expected_token == actual_token else (0.0, 1.0)
|
105
|
+
|
106
|
+
if isinstance(expected_token, str):
|
107
|
+
return self._compare_strings(expected_token, actual_token)
|
108
|
+
|
109
|
+
return (1.0, 1.0) if str(expected_token) == str(actual_token) else (0.0, 1.0)
|
110
|
+
|
111
|
+
def _compare_numbers(
|
112
|
+
self, expected_number: float, actual_number: float
|
113
|
+
) -> Tuple[float, float]:
|
114
|
+
total = 1.0
|
115
|
+
if math.isclose(expected_number, 0.0, abs_tol=1e-12):
|
116
|
+
matched = 1.0 if math.isclose(actual_number, 0.0, abs_tol=1e-12) else 0.0
|
117
|
+
else:
|
118
|
+
ratio = abs(expected_number - actual_number) / abs(expected_number)
|
119
|
+
matched = max(0.0, min(1.0, 1.0 - ratio))
|
120
|
+
return matched, total
|
121
|
+
|
122
|
+
def _compare_strings(
|
123
|
+
self, expected_string: str, actual_string: str
|
124
|
+
) -> Tuple[float, float]:
|
125
|
+
total = 1.0
|
126
|
+
if not expected_string and not actual_string:
|
127
|
+
return 1.0, total
|
128
|
+
distance = self._levenshtein(expected_string, actual_string)
|
129
|
+
max_length = max(len(expected_string), len(actual_string))
|
130
|
+
similarity = 1.0 - (distance / max_length) if max_length else 1.0
|
131
|
+
similarity = max(0.0, min(1.0, similarity))
|
132
|
+
return similarity, total
|
133
|
+
|
134
|
+
def _count_leaves(self, token_node: Any) -> float:
|
135
|
+
if isinstance(token_node, dict):
|
136
|
+
return sum(
|
137
|
+
self._count_leaves(child_value) for child_value in token_node.values()
|
138
|
+
)
|
139
|
+
if isinstance(token_node, list):
|
140
|
+
return sum(self._count_leaves(child_value) for child_value in token_node)
|
141
|
+
return 1.0
|
142
|
+
|
143
|
+
def _levenshtein(self, source_text: str, target_text: str) -> int:
|
144
|
+
if not source_text:
|
145
|
+
return len(target_text)
|
146
|
+
if not target_text:
|
147
|
+
return len(source_text)
|
148
|
+
source_len, target_len = len(source_text), len(target_text)
|
149
|
+
distance_matrix = [[0] * (target_len + 1) for _ in range(source_len + 1)]
|
150
|
+
for row_idx in range(source_len + 1):
|
151
|
+
distance_matrix[row_idx][0] = row_idx
|
152
|
+
for col_idx in range(target_len + 1):
|
153
|
+
distance_matrix[0][col_idx] = col_idx
|
154
|
+
for row_idx in range(1, source_len + 1):
|
155
|
+
for col_idx in range(1, target_len + 1):
|
156
|
+
substitution_cost = (
|
157
|
+
0 if source_text[row_idx - 1] == target_text[col_idx - 1] else 1
|
158
|
+
)
|
159
|
+
distance_matrix[row_idx][col_idx] = min(
|
160
|
+
distance_matrix[row_idx - 1][col_idx] + 1, # deletion
|
161
|
+
distance_matrix[row_idx][col_idx - 1] + 1, # insertion
|
162
|
+
distance_matrix[row_idx - 1][col_idx - 1]
|
163
|
+
+ substitution_cost, # substitution
|
164
|
+
)
|
165
|
+
return distance_matrix[source_len][target_len]
|
166
|
+
|
167
|
+
def _is_number(self, value: Any) -> bool:
|
168
|
+
return isinstance(value, (int, float)) and not isinstance(value, bool)
|
@@ -11,6 +11,7 @@ from ...._utils.constants import (
|
|
11
11
|
COMMUNITY_agents_SUFFIX,
|
12
12
|
)
|
13
13
|
from .._models import EvaluationResult, LLMResponse
|
14
|
+
from .._models._evaluators import ScoreType
|
14
15
|
from ._evaluator_base import EvaluatorBase
|
15
16
|
|
16
17
|
|
@@ -86,6 +87,7 @@ class LlmAsAJudgeEvaluator(EvaluatorBase):
|
|
86
87
|
expected_output=expected_output,
|
87
88
|
actual_output=actual_output,
|
88
89
|
details=llm_response.justification,
|
90
|
+
score_type=ScoreType.NUMERICAL,
|
89
91
|
)
|
90
92
|
|
91
93
|
def _extract_target_value(self, output: Dict[str, Any]) -> Any:
|
@@ -1,8 +1,8 @@
|
|
1
|
-
from datetime import datetime
|
1
|
+
from datetime import datetime, timezone
|
2
2
|
from enum import IntEnum
|
3
3
|
from typing import Any, Dict, List, Optional
|
4
4
|
|
5
|
-
from pydantic import BaseModel
|
5
|
+
from pydantic import BaseModel
|
6
6
|
|
7
7
|
|
8
8
|
class LLMResponse(BaseModel):
|
@@ -50,6 +50,12 @@ class EvaluatorType(IntEnum):
|
|
50
50
|
raise ValueError(f"{value} is not a valid EvaluatorType value")
|
51
51
|
|
52
52
|
|
53
|
+
class ScoreType(IntEnum):
|
54
|
+
BOOLEAN = 0
|
55
|
+
NUMERICAL = 1
|
56
|
+
ERROR = 2
|
57
|
+
|
58
|
+
|
53
59
|
class EvaluationResult(BaseModel):
|
54
60
|
"""Result of a single evaluation."""
|
55
61
|
|
@@ -57,13 +63,14 @@ class EvaluationResult(BaseModel):
|
|
57
63
|
evaluation_name: str
|
58
64
|
evaluator_id: str
|
59
65
|
evaluator_name: str
|
60
|
-
score: float
|
66
|
+
score: float | bool
|
67
|
+
score_type: ScoreType
|
61
68
|
# this is marked as optional, as it is populated inside the 'measure_execution_time' decorator
|
62
69
|
evaluation_time: Optional[float] = None
|
63
70
|
input: Dict[str, Any]
|
64
71
|
expected_output: Dict[str, Any]
|
65
72
|
actual_output: Dict[str, Any]
|
66
|
-
timestamp: datetime =
|
73
|
+
timestamp: datetime = datetime.now(timezone.utc)
|
67
74
|
details: Optional[str] = None
|
68
75
|
|
69
76
|
|
@@ -76,12 +83,6 @@ class EvaluationSetResult(BaseModel):
|
|
76
83
|
average_score: float
|
77
84
|
|
78
85
|
|
79
|
-
class ScoreType(IntEnum):
|
80
|
-
BOOLEAN = 0
|
81
|
-
NUMERICAL = 1
|
82
|
-
ERROR = 2
|
83
|
-
|
84
|
-
|
85
86
|
class EvalItemResult(BaseModel):
|
86
87
|
"""Result of a single evaluation item."""
|
87
88
|
|
@@ -139,12 +139,23 @@ class ProgressReporter:
|
|
139
139
|
actual_output: dict[str, Any] = {}
|
140
140
|
for eval_result in eval_results:
|
141
141
|
# keep track of evaluator scores. this should be removed after this computation is done server-side
|
142
|
-
|
143
|
-
|
144
|
-
|
142
|
+
|
143
|
+
# check the evaluator score type
|
144
|
+
match eval_result.result.score_type:
|
145
|
+
case ScoreType.NUMERICAL:
|
146
|
+
self._evaluator_scores[eval_result.evaluator_id].append(
|
147
|
+
eval_result.result.score
|
148
|
+
)
|
149
|
+
case ScoreType.BOOLEAN:
|
150
|
+
self._evaluator_scores[eval_result.evaluator_id].append(
|
151
|
+
100 if eval_result.result.score else 0
|
152
|
+
)
|
153
|
+
case ScoreType.ERROR:
|
154
|
+
self._evaluator_scores[eval_result.evaluator_id].append(0)
|
155
|
+
|
145
156
|
evaluator_scores.append(
|
146
157
|
{
|
147
|
-
"type":
|
158
|
+
"type": eval_result.result.score_type.value,
|
148
159
|
"value": eval_result.result.score,
|
149
160
|
"justification": eval_result.result.details,
|
150
161
|
"evaluatorId": eval_result.evaluator_id,
|
uipath/_cli/_runtime/_runtime.py
CHANGED
@@ -11,6 +11,7 @@ from typing import Any, Dict, Optional, Type, TypeVar, cast, get_type_hints
|
|
11
11
|
from opentelemetry import trace
|
12
12
|
from opentelemetry.sdk.trace import TracerProvider
|
13
13
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
14
|
+
from pydantic import BaseModel
|
14
15
|
|
15
16
|
from uipath.tracing import LlmOpsHttpExporter
|
16
17
|
|
@@ -162,9 +163,11 @@ class UiPathRuntime(UiPathBaseRuntime):
|
|
162
163
|
input_param = params[0]
|
163
164
|
input_type = input_param.annotation
|
164
165
|
|
165
|
-
# Case 2: Class or
|
166
|
+
# Case 2: Class, dataclass, or Pydantic model parameter
|
166
167
|
if input_type != inspect.Parameter.empty and (
|
167
|
-
is_dataclass(input_type)
|
168
|
+
is_dataclass(input_type)
|
169
|
+
or self._is_pydantic_model(input_type)
|
170
|
+
or hasattr(input_type, "__annotations__")
|
168
171
|
):
|
169
172
|
try:
|
170
173
|
valid_type = cast(Type[Any], input_type)
|
@@ -216,7 +219,16 @@ class UiPathRuntime(UiPathBaseRuntime):
|
|
216
219
|
)
|
217
220
|
|
218
221
|
def _convert_to_class(self, data: Dict[str, Any], cls: Type[T]) -> T:
|
219
|
-
"""Convert a dictionary to either a dataclass or regular class instance."""
|
222
|
+
"""Convert a dictionary to either a dataclass, Pydantic model, or regular class instance."""
|
223
|
+
# Handle Pydantic models
|
224
|
+
try:
|
225
|
+
if inspect.isclass(cls) and issubclass(cls, BaseModel):
|
226
|
+
return cast(T, cls.model_validate(data))
|
227
|
+
except TypeError:
|
228
|
+
# issubclass can raise TypeError if cls is not a class
|
229
|
+
pass
|
230
|
+
|
231
|
+
# Handle dataclasses
|
220
232
|
if is_dataclass(cls):
|
221
233
|
field_types = get_type_hints(cls)
|
222
234
|
converted_data = {}
|
@@ -227,13 +239,17 @@ class UiPathRuntime(UiPathBaseRuntime):
|
|
227
239
|
|
228
240
|
value = data[field_name]
|
229
241
|
if (
|
230
|
-
is_dataclass(field_type)
|
242
|
+
is_dataclass(field_type)
|
243
|
+
or self._is_pydantic_model(field_type)
|
244
|
+
or hasattr(field_type, "__annotations__")
|
231
245
|
) and isinstance(value, dict):
|
232
246
|
typed_field = cast(Type[Any], field_type)
|
233
247
|
value = self._convert_to_class(value, typed_field)
|
234
248
|
converted_data[field_name] = value
|
235
249
|
|
236
250
|
return cast(T, cls(**converted_data))
|
251
|
+
|
252
|
+
# Handle regular classes
|
237
253
|
else:
|
238
254
|
sig = inspect.signature(cls.__init__)
|
239
255
|
params = sig.parameters
|
@@ -254,6 +270,7 @@ class UiPathRuntime(UiPathBaseRuntime):
|
|
254
270
|
|
255
271
|
if (
|
256
272
|
is_dataclass(param_type)
|
273
|
+
or self._is_pydantic_model(param_type)
|
257
274
|
or hasattr(param_type, "__annotations__")
|
258
275
|
) and isinstance(value, dict):
|
259
276
|
typed_param = cast(Type[Any], param_type)
|
@@ -265,22 +282,41 @@ class UiPathRuntime(UiPathBaseRuntime):
|
|
265
282
|
|
266
283
|
return cls(**init_args)
|
267
284
|
|
285
|
+
def _is_pydantic_model(self, cls: Type[Any]) -> bool:
|
286
|
+
"""Safely check if a class is a Pydantic model."""
|
287
|
+
try:
|
288
|
+
return inspect.isclass(cls) and issubclass(cls, BaseModel)
|
289
|
+
except TypeError:
|
290
|
+
# issubclass can raise TypeError if cls is not a class
|
291
|
+
return False
|
292
|
+
|
268
293
|
def _convert_from_class(self, obj: Any) -> Dict[str, Any]:
|
269
|
-
"""Convert a class instance (dataclass or regular) to a dictionary."""
|
294
|
+
"""Convert a class instance (dataclass, Pydantic model, or regular) to a dictionary."""
|
270
295
|
if obj is None:
|
271
296
|
return {}
|
272
297
|
|
273
|
-
|
298
|
+
# Handle Pydantic models
|
299
|
+
if isinstance(obj, BaseModel):
|
300
|
+
return obj.model_dump()
|
301
|
+
|
302
|
+
# Handle dataclasses
|
303
|
+
elif is_dataclass(obj):
|
274
304
|
# Make sure obj is an instance, not a class
|
275
305
|
if isinstance(obj, type):
|
276
306
|
return {}
|
277
307
|
return asdict(obj)
|
308
|
+
|
309
|
+
# Handle regular classes
|
278
310
|
elif hasattr(obj, "__dict__"):
|
279
311
|
result = {}
|
280
312
|
for key, value in obj.__dict__.items():
|
281
313
|
# Skip private attributes
|
282
314
|
if not key.startswith("_"):
|
283
|
-
if
|
315
|
+
if (
|
316
|
+
isinstance(value, BaseModel)
|
317
|
+
or hasattr(value, "__dict__")
|
318
|
+
or is_dataclass(value)
|
319
|
+
):
|
284
320
|
result[key] = self._convert_from_class(value)
|
285
321
|
else:
|
286
322
|
result[key] = value
|
@@ -14,6 +14,8 @@ from typing import (
|
|
14
14
|
get_type_hints,
|
15
15
|
)
|
16
16
|
|
17
|
+
from pydantic import BaseModel
|
18
|
+
|
17
19
|
SchemaType = Literal["object", "integer", "double", "string", "boolean", "array"]
|
18
20
|
|
19
21
|
TYPE_MAP: Dict[str, SchemaType] = {
|
@@ -50,7 +52,30 @@ def get_type_schema(type_hint: Any) -> Dict[str, Any]:
|
|
50
52
|
return {"type": "object"}
|
51
53
|
|
52
54
|
if inspect.isclass(type_hint):
|
53
|
-
|
55
|
+
# Handle Pydantic models
|
56
|
+
if issubclass(type_hint, BaseModel):
|
57
|
+
properties = {}
|
58
|
+
required = []
|
59
|
+
|
60
|
+
# Get the model fields
|
61
|
+
model_fields = type_hint.model_fields
|
62
|
+
|
63
|
+
for field_name, field_info in model_fields.items():
|
64
|
+
# Use alias if defined, otherwise use field name
|
65
|
+
schema_field_name = field_info.alias if field_info.alias else field_name
|
66
|
+
|
67
|
+
# Get the field type schema
|
68
|
+
field_schema = get_type_schema(field_info.annotation)
|
69
|
+
properties[schema_field_name] = field_schema
|
70
|
+
|
71
|
+
# Check if field is required using Pydantic's built-in method
|
72
|
+
if field_info.is_required():
|
73
|
+
required.append(schema_field_name)
|
74
|
+
|
75
|
+
return {"type": "object", "properties": properties, "required": required}
|
76
|
+
|
77
|
+
# Handle dataclasses
|
78
|
+
elif is_dataclass(type_hint):
|
54
79
|
properties = {}
|
55
80
|
required = []
|
56
81
|
|
@@ -61,6 +86,8 @@ def get_type_schema(type_hint: Any) -> Dict[str, Any]:
|
|
61
86
|
required.append(field.name)
|
62
87
|
|
63
88
|
return {"type": "object", "properties": properties, "required": required}
|
89
|
+
|
90
|
+
# Handle regular classes with annotations
|
64
91
|
elif hasattr(type_hint, "__annotations__"):
|
65
92
|
properties = {}
|
66
93
|
required = []
|
uipath/_cli/_utils/_parse_ast.py
CHANGED
@@ -8,6 +8,7 @@ from typing import Any, Dict, List, Optional, Tuple
|
|
8
8
|
from ..._services import (
|
9
9
|
AssetsService,
|
10
10
|
BucketsService,
|
11
|
+
ConnectionsService,
|
11
12
|
ContextGroundingService,
|
12
13
|
ProcessesService,
|
13
14
|
)
|
@@ -47,6 +48,7 @@ supported_bindings_by_service = {
|
|
47
48
|
"processes": ProcessesService,
|
48
49
|
"buckets": BucketsService,
|
49
50
|
"context_grounding": ContextGroundingService,
|
51
|
+
"connections": ConnectionsService,
|
50
52
|
}
|
51
53
|
|
52
54
|
|
@@ -103,7 +105,17 @@ class ServiceUsage:
|
|
103
105
|
|
104
106
|
# custom logic for connections bindings
|
105
107
|
elif self.service_name == "connections":
|
108
|
+
# First, try to get inferred bindings for connections if they exist
|
109
|
+
connections_service = supported_bindings_by_service.get("connections")
|
110
|
+
inferred_bindings = {}
|
111
|
+
if connections_service:
|
112
|
+
inferred_bindings = get_inferred_bindings_names(connections_service)
|
113
|
+
|
106
114
|
for call in self.method_calls:
|
115
|
+
# Skip methods that are decorated with @infer_bindings(ignore=False)
|
116
|
+
if call.method_name in inferred_bindings:
|
117
|
+
continue
|
118
|
+
|
107
119
|
if len(call.args) > 0:
|
108
120
|
connection_id = call.args[0]
|
109
121
|
if connection_id:
|
@@ -68,6 +68,7 @@ def get_project_config(directory: str) -> dict[str, str]:
|
|
68
68
|
"version": toml_data["version"],
|
69
69
|
"authors": toml_data["authors"],
|
70
70
|
"dependencies": toml_data.get("dependencies", {}),
|
71
|
+
"requires-python": toml_data.get("requires-python", {}),
|
71
72
|
}
|
72
73
|
|
73
74
|
|
@@ -97,6 +98,11 @@ def validate_config(config: dict[str, str]) -> None:
|
|
97
98
|
'Project authors cannot be empty. Please specify authors in pyproject.toml:\n authors = [{ name = "John Doe" }]'
|
98
99
|
)
|
99
100
|
|
101
|
+
if not config["requires-python"] or config["requires-python"].strip() == "":
|
102
|
+
console.error(
|
103
|
+
"'requires-python' field cannot be empty. Please specify it in pyproject.toml: requires-python = \">=3.10\""
|
104
|
+
)
|
105
|
+
|
100
106
|
invalid_chars = ["&", "<", ">", '"', "'", ";"]
|
101
107
|
for char in invalid_chars:
|
102
108
|
if char in config["project_name"]:
|
@@ -296,6 +302,7 @@ def read_toml_project(file_path: str) -> dict:
|
|
296
302
|
"version": project["version"].strip(),
|
297
303
|
"authors": author_name.strip(),
|
298
304
|
"dependencies": dependencies,
|
305
|
+
"requires-python": project.get("requires-python", "").strip(),
|
299
306
|
}
|
300
307
|
|
301
308
|
|
@@ -1,9 +1,11 @@
|
|
1
|
+
import json
|
1
2
|
import logging
|
3
|
+
from typing import Any, Dict
|
2
4
|
|
3
5
|
from .._config import Config
|
4
6
|
from .._execution_context import ExecutionContext
|
5
|
-
from .._utils import Endpoint, RequestSpec
|
6
|
-
from ..models import Connection, ConnectionToken
|
7
|
+
from .._utils import Endpoint, RequestSpec, infer_bindings
|
8
|
+
from ..models import Connection, ConnectionToken, EventArguments
|
7
9
|
from ..tracing._traced import traced
|
8
10
|
from ._base_service import BaseService
|
9
11
|
|
@@ -112,6 +114,84 @@ class ConnectionsService(BaseService):
|
|
112
114
|
)
|
113
115
|
return ConnectionToken.model_validate(response.json())
|
114
116
|
|
117
|
+
@traced(
|
118
|
+
name="connections_retrieve_event_payload",
|
119
|
+
run_type="uipath",
|
120
|
+
)
|
121
|
+
@infer_bindings(resource_type="ignored", ignore=True)
|
122
|
+
def retrieve_event_payload(self, event_args: EventArguments) -> Dict[str, Any]:
|
123
|
+
"""Retrieve event payload from UiPath Integration Service.
|
124
|
+
|
125
|
+
Args:
|
126
|
+
event_args (EventArguments): The event arguments. Should be passed along from the job's input.
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
Dict[str, Any]: The event payload data
|
130
|
+
"""
|
131
|
+
if not event_args.additional_event_data:
|
132
|
+
raise ValueError("additional_event_data is required")
|
133
|
+
|
134
|
+
# Parse additional event data to get event id
|
135
|
+
event_data = json.loads(event_args.additional_event_data)
|
136
|
+
|
137
|
+
event_id = None
|
138
|
+
if "processedEventId" in event_data:
|
139
|
+
event_id = event_data["processedEventId"]
|
140
|
+
elif "rawEventId" in event_data:
|
141
|
+
event_id = event_data["rawEventId"]
|
142
|
+
else:
|
143
|
+
raise ValueError("Event Id not found in additional event data")
|
144
|
+
|
145
|
+
# Build request URL using connection token's API base URI
|
146
|
+
spec = self._retrieve_event_payload_spec("v1", event_id)
|
147
|
+
|
148
|
+
response = self.request(spec.method, url=spec.endpoint)
|
149
|
+
|
150
|
+
return response.json()
|
151
|
+
|
152
|
+
@traced(
|
153
|
+
name="connections_retrieve_event_payload",
|
154
|
+
run_type="uipath",
|
155
|
+
)
|
156
|
+
@infer_bindings(resource_type="ignored", ignore=True)
|
157
|
+
async def retrieve_event_payload_async(
|
158
|
+
self, event_args: EventArguments
|
159
|
+
) -> Dict[str, Any]:
|
160
|
+
"""Retrieve event payload from UiPath Integration Service.
|
161
|
+
|
162
|
+
Args:
|
163
|
+
event_args (EventArguments): The event arguments. Should be passed along from the job's input.
|
164
|
+
|
165
|
+
Returns:
|
166
|
+
Dict[str, Any]: The event payload data
|
167
|
+
"""
|
168
|
+
if not event_args.additional_event_data:
|
169
|
+
raise ValueError("additional_event_data is required")
|
170
|
+
|
171
|
+
# Parse additional event data to get event id
|
172
|
+
event_data = json.loads(event_args.additional_event_data)
|
173
|
+
|
174
|
+
event_id = None
|
175
|
+
if "processedEventId" in event_data:
|
176
|
+
event_id = event_data["processedEventId"]
|
177
|
+
elif "rawEventId" in event_data:
|
178
|
+
event_id = event_data["rawEventId"]
|
179
|
+
else:
|
180
|
+
raise ValueError("Event Id not found in additional event data")
|
181
|
+
|
182
|
+
# Build request URL using connection token's API base URI
|
183
|
+
spec = self._retrieve_event_payload_spec("v1", event_id)
|
184
|
+
|
185
|
+
response = await self.request_async(spec.method, url=spec.endpoint)
|
186
|
+
|
187
|
+
return response.json()
|
188
|
+
|
189
|
+
def _retrieve_event_payload_spec(self, version: str, event_id: str) -> RequestSpec:
|
190
|
+
return RequestSpec(
|
191
|
+
method="GET",
|
192
|
+
endpoint=Endpoint(f"/elements_/{version}/events/{event_id}"),
|
193
|
+
)
|
194
|
+
|
115
195
|
def _retrieve_spec(self, key: str) -> RequestSpec:
|
116
196
|
return RequestSpec(
|
117
197
|
method="GET",
|
uipath/_utils/_infer_bindings.py
CHANGED
@@ -8,7 +8,10 @@ T = TypeVar("T")
|
|
8
8
|
|
9
9
|
|
10
10
|
def infer_bindings(
|
11
|
-
resource_type: str,
|
11
|
+
resource_type: str,
|
12
|
+
name: str = "name",
|
13
|
+
folder_path: str = "folder_path",
|
14
|
+
ignore: bool = False,
|
12
15
|
) -> Callable[..., Any]:
|
13
16
|
def decorator(func: Callable[..., Any]):
|
14
17
|
@functools.wraps(func)
|
@@ -30,7 +33,7 @@ def infer_bindings(
|
|
30
33
|
|
31
34
|
return func(**all_args)
|
32
35
|
|
33
|
-
wrapper._should_infer_bindings =
|
36
|
+
wrapper._should_infer_bindings = not ignore # type: ignore
|
34
37
|
wrapper._infer_bindings_mappings = {"name": name, "folder_path": folder_path} # type: ignore
|
35
38
|
return wrapper
|
36
39
|
|
uipath/models/__init__.py
CHANGED
@@ -3,7 +3,7 @@ from .actions import Action
|
|
3
3
|
from .assets import Asset, UserAsset
|
4
4
|
from .attachment import Attachment
|
5
5
|
from .buckets import Bucket
|
6
|
-
from .connections import Connection, ConnectionToken
|
6
|
+
from .connections import Connection, ConnectionToken, EventArguments
|
7
7
|
from .context_grounding import ContextGroundingQueryResponse
|
8
8
|
from .context_grounding_index import ContextGroundingIndex
|
9
9
|
from .errors import BaseUrlMissingError, SecretMissingError
|
@@ -39,6 +39,7 @@ __all__ = [
|
|
39
39
|
"TransactionItemResult",
|
40
40
|
"Connection",
|
41
41
|
"ConnectionToken",
|
42
|
+
"EventArguments",
|
42
43
|
"Job",
|
43
44
|
"InvokeProcess",
|
44
45
|
"ActionSchema",
|
uipath/models/connections.py
CHANGED
@@ -49,3 +49,20 @@ class ConnectionToken(BaseModel):
|
|
49
49
|
expires_in: Optional[int] = Field(default=None, alias="expiresIn")
|
50
50
|
api_base_uri: Optional[str] = Field(default=None, alias="apiBaseUri")
|
51
51
|
element_instance_id: Optional[int] = Field(default=None, alias="elementInstanceId")
|
52
|
+
|
53
|
+
|
54
|
+
class EventArguments(BaseModel):
|
55
|
+
event_connector: Optional[str] = Field(default=None, alias="UiPathEventConnector")
|
56
|
+
event: Optional[str] = Field(default=None, alias="UiPathEvent")
|
57
|
+
event_object_type: Optional[str] = Field(
|
58
|
+
default=None, alias="UiPathEventObjectType"
|
59
|
+
)
|
60
|
+
event_object_id: Optional[str] = Field(default=None, alias="UiPathEventObjectId")
|
61
|
+
additional_event_data: Optional[str] = Field(
|
62
|
+
default=None, alias="UiPathAdditionalEventData"
|
63
|
+
)
|
64
|
+
|
65
|
+
model_config = ConfigDict(
|
66
|
+
populate_by_name=True,
|
67
|
+
extra="allow",
|
68
|
+
)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
+
import re
|
3
4
|
from enum import Enum
|
4
5
|
from typing import Optional
|
5
6
|
|
@@ -18,9 +19,14 @@ class UiPathEndpoints(Enum):
|
|
18
19
|
)
|
19
20
|
AH_CAPABILITIES_ENDPOINT = "agenthub_/llm/api/capabilities"
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
OR_NORMALIZED_COMPLETION_ENDPOINT = "orchestrator_/llm/api/chat/completions"
|
23
|
+
OR_PASSTHROUGH_COMPLETION_ENDPOINT = "orchestrator_/llm/openai/deployments/{model}/chat/completions?api-version={api_version}"
|
24
|
+
OR_EMBEDDING_ENDPOINT = "orchestrator_/llm/openai/deployments/{model}/embeddings?api-version={api_version}"
|
25
|
+
OR_CAPABILITIES_ENDPOINT = "orchestrator_/llm/api/capabilities"
|
26
|
+
|
27
|
+
LG_NORMALIZED_COMPLETION_ENDPOINT = "llmgateway_/api/chat/completions"
|
28
|
+
LG_PASSTHROUGH_COMPLETION_ENDPOINT = "llmgateway_/openai/deployments/{model}/chat/completions?api-version={api_version}"
|
29
|
+
LG_EMBEDDING_ENDPOINT = (
|
24
30
|
"llmgateway_/openai/deployments/{model}/embeddings?api-version={api_version}"
|
25
31
|
)
|
26
32
|
|
@@ -28,24 +34,39 @@ class UiPathEndpoints(Enum):
|
|
28
34
|
class EndpointManager:
|
29
35
|
"""Manages and caches the UiPath endpoints.
|
30
36
|
This class provides functionality to determine which UiPath endpoints to use based on
|
31
|
-
the availability of AgentHub. It checks for
|
32
|
-
to avoid repeated network calls.
|
37
|
+
the availability of AgentHub and Orchestrator. It checks for capabilities and caches
|
38
|
+
the results to avoid repeated network calls.
|
39
|
+
|
40
|
+
The endpoint selection follows a fallback order:
|
41
|
+
1. AgentHub (if available)
|
42
|
+
2. Orchestrator (if available)
|
43
|
+
3. LLMGateway (default fallback)
|
44
|
+
|
45
|
+
Environment Variable Override:
|
46
|
+
The fallback behavior can be bypassed using the UIPATH_LLM_SERVICE environment variable:
|
47
|
+
- 'agenthub' or 'ah': Force use of AgentHub endpoints (skips capability checks)
|
48
|
+
- 'orchestrator' or 'or': Force use of Orchestrator endpoints (skips capability checks)
|
49
|
+
- 'llmgateway' or 'gateway': Force use of LLMGateway endpoints (skips capability checks)
|
50
|
+
|
33
51
|
Class Attributes:
|
34
52
|
_base_url (str): The base URL for UiPath services, retrieved from the UIPATH_URL
|
35
53
|
environment variable.
|
36
54
|
_agenthub_available (Optional[bool]): Cached result of AgentHub availability check.
|
55
|
+
_orchestrator_available (Optional[bool]): Cached result of Orchestrator availability check.
|
37
56
|
|
38
57
|
Methods:
|
39
58
|
is_agenthub_available(): Checks if AgentHub is available, caching the result.
|
59
|
+
is_orchestrator_available(): Checks if Orchestrator is available, caching the result.
|
40
60
|
get_passthrough_endpoint(): Returns the appropriate passthrough completion endpoint.
|
41
61
|
get_normalized_endpoint(): Returns the appropriate normalized completion endpoint.
|
42
62
|
get_embeddings_endpoint(): Returns the appropriate embeddings endpoint.
|
43
|
-
All endpoint methods automatically select
|
44
|
-
|
63
|
+
All endpoint methods automatically select the best available endpoint using the fallback order,
|
64
|
+
unless overridden by the UIPATH_LLM_SERVICE environment variable.
|
45
65
|
""" # noqa: D205
|
46
66
|
|
47
67
|
_base_url = os.getenv("UIPATH_URL", "")
|
48
68
|
_agenthub_available: Optional[bool] = None
|
69
|
+
_orchestrator_available: Optional[bool] = None
|
49
70
|
|
50
71
|
@classmethod
|
51
72
|
def is_agenthub_available(cls) -> bool:
|
@@ -55,13 +76,30 @@ class EndpointManager:
|
|
55
76
|
return cls._agenthub_available
|
56
77
|
|
57
78
|
@classmethod
|
58
|
-
def
|
59
|
-
"""
|
79
|
+
def is_orchestrator_available(cls) -> bool:
|
80
|
+
"""Check if Orchestrator is available and cache the result."""
|
81
|
+
if cls._orchestrator_available is None:
|
82
|
+
cls._orchestrator_available = cls._check_orchestrator()
|
83
|
+
return cls._orchestrator_available
|
84
|
+
|
85
|
+
@classmethod
|
86
|
+
def _check_capabilities(cls, endpoint: UiPathEndpoints, service_name: str) -> bool:
|
87
|
+
"""Perform the actual check for service capabilities.
|
88
|
+
|
89
|
+
Args:
|
90
|
+
endpoint: The capabilities endpoint to check
|
91
|
+
service_name: Human-readable service name for logging
|
92
|
+
|
93
|
+
Returns:
|
94
|
+
bool: True if the service is available and has valid capabilities
|
95
|
+
"""
|
60
96
|
try:
|
61
97
|
with httpx.Client(**get_httpx_client_kwargs()) as http_client:
|
62
98
|
base_url = os.getenv("UIPATH_URL", "")
|
63
|
-
capabilities_url = f"{base_url.rstrip('/')}/{
|
64
|
-
loggger.debug(
|
99
|
+
capabilities_url = f"{base_url.rstrip('/')}/{endpoint.value}"
|
100
|
+
loggger.debug(
|
101
|
+
f"Checking {service_name} capabilities at {capabilities_url}"
|
102
|
+
)
|
65
103
|
response = http_client.get(capabilities_url)
|
66
104
|
|
67
105
|
if response.status_code != 200:
|
@@ -76,26 +114,87 @@ class EndpointManager:
|
|
76
114
|
return True
|
77
115
|
|
78
116
|
except Exception as e:
|
79
|
-
loggger.error(
|
117
|
+
loggger.error(
|
118
|
+
f"Error checking {service_name} capabilities: {e}", exc_info=True
|
119
|
+
)
|
80
120
|
return False
|
81
121
|
|
82
122
|
@classmethod
|
83
|
-
def
|
84
|
-
|
85
|
-
|
123
|
+
def _check_agenthub(cls) -> bool:
|
124
|
+
"""Perform the actual check for AgentHub capabilities."""
|
125
|
+
return cls._check_capabilities(
|
126
|
+
UiPathEndpoints.AH_CAPABILITIES_ENDPOINT, "AgentHub"
|
127
|
+
)
|
86
128
|
|
87
|
-
|
129
|
+
@classmethod
|
130
|
+
def _check_orchestrator(cls) -> bool:
|
131
|
+
"""Perform the actual check for Orchestrator capabilities."""
|
132
|
+
return cls._check_capabilities(
|
133
|
+
UiPathEndpoints.OR_CAPABILITIES_ENDPOINT, "Orchestrator"
|
134
|
+
)
|
88
135
|
|
89
136
|
@classmethod
|
90
|
-
def
|
91
|
-
|
92
|
-
|
137
|
+
def _select_endpoint(
|
138
|
+
cls, ah: UiPathEndpoints, orc: UiPathEndpoints, gw: UiPathEndpoints
|
139
|
+
) -> str:
|
140
|
+
"""Select an endpoint based on UIPATH_LLM_SERVICE override or capability checks."""
|
141
|
+
service_override = os.getenv("UIPATH_LLM_SERVICE", "").lower()
|
142
|
+
|
143
|
+
if service_override in ("agenthub", "ah"):
|
144
|
+
return ah.value
|
145
|
+
if service_override in ("orchestrator", "or"):
|
146
|
+
return orc.value
|
147
|
+
if service_override in ("llmgateway", "gateway"):
|
148
|
+
return gw.value
|
149
|
+
|
150
|
+
# Determine fallback order based on environment hints
|
151
|
+
uipath_url = os.getenv("UIPATH_URL", "")
|
152
|
+
hdens_env = os.getenv("HDENS_ENV", "").lower()
|
153
|
+
|
154
|
+
is_cloud_url = re.match(r"https?://[^/]+\.uipath\.com", uipath_url)
|
155
|
+
|
156
|
+
# Default order: AgentHub -> Orchestrator
|
157
|
+
check_order = [
|
158
|
+
("ah", ah, cls.is_agenthub_available),
|
159
|
+
("orc", orc, cls.is_orchestrator_available),
|
160
|
+
]
|
161
|
+
|
162
|
+
# Prioritize Orchestrator if HDENS_ENV is 'sf' or url is cloud-based
|
163
|
+
# Note: The default order already prioritizes AgentHub
|
164
|
+
if hdens_env == "sf" or is_cloud_url:
|
165
|
+
check_order.reverse()
|
166
|
+
|
167
|
+
# Execute fallback checks in the determined order
|
168
|
+
for _, endpoint, is_available in check_order:
|
169
|
+
if is_available():
|
170
|
+
return endpoint.value
|
171
|
+
|
172
|
+
# Final fallback to LLMGateway
|
173
|
+
return gw.value
|
174
|
+
|
175
|
+
@classmethod
|
176
|
+
def get_passthrough_endpoint(cls) -> str:
|
177
|
+
"""Get the passthrough completion endpoint."""
|
178
|
+
return cls._select_endpoint(
|
179
|
+
UiPathEndpoints.AH_PASSTHROUGH_COMPLETION_ENDPOINT,
|
180
|
+
UiPathEndpoints.OR_PASSTHROUGH_COMPLETION_ENDPOINT,
|
181
|
+
UiPathEndpoints.LG_PASSTHROUGH_COMPLETION_ENDPOINT,
|
182
|
+
)
|
93
183
|
|
94
|
-
|
184
|
+
@classmethod
|
185
|
+
def get_normalized_endpoint(cls) -> str:
|
186
|
+
"""Get the normalized completion endpoint."""
|
187
|
+
return cls._select_endpoint(
|
188
|
+
UiPathEndpoints.AH_NORMALIZED_COMPLETION_ENDPOINT,
|
189
|
+
UiPathEndpoints.OR_NORMALIZED_COMPLETION_ENDPOINT,
|
190
|
+
UiPathEndpoints.LG_NORMALIZED_COMPLETION_ENDPOINT,
|
191
|
+
)
|
95
192
|
|
96
193
|
@classmethod
|
97
194
|
def get_embeddings_endpoint(cls) -> str:
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
195
|
+
"""Get the embeddings endpoint."""
|
196
|
+
return cls._select_endpoint(
|
197
|
+
UiPathEndpoints.AH_EMBEDDING_ENDPOINT,
|
198
|
+
UiPathEndpoints.OR_EMBEDDING_ENDPOINT,
|
199
|
+
UiPathEndpoints.LG_EMBEDDING_ENDPOINT,
|
200
|
+
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: uipath
|
3
|
-
Version: 2.1.
|
3
|
+
Version: 2.1.20
|
4
4
|
Summary: Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools.
|
5
5
|
Project-URL: Homepage, https://uipath.com
|
6
6
|
Project-URL: Repository, https://github.com/UiPath/uipath-python
|
@@ -30,23 +30,24 @@ uipath/_cli/_auth/index.html,sha256=_Q2OtqPfapG_6vumbQYqtb2PfFe0smk7TlGERKEBvB4,
|
|
30
30
|
uipath/_cli/_auth/localhost.crt,sha256=oGl9oLLOiouHubAt39B4zEfylFvKEtbtr_43SIliXJc,1226
|
31
31
|
uipath/_cli/_auth/localhost.key,sha256=X31VYXD8scZtmGA837dGX5l6G-LXHLo5ItWJhZXaz3c,1679
|
32
32
|
uipath/_cli/_evals/evaluation_service.py,sha256=VVxZxoCJoB2SUhej_c0DzC9AlnIlWMKnug7z5weNSoE,22077
|
33
|
-
uipath/_cli/_evals/progress_reporter.py,sha256=
|
34
|
-
uipath/_cli/_evals/_evaluators/__init__.py,sha256=
|
35
|
-
uipath/_cli/_evals/_evaluators/
|
36
|
-
uipath/_cli/_evals/_evaluators/_deterministic_evaluator.py,sha256=P0du9KWz5MP5Pw70Ze7piqeBfFq7w0aU7DLeEiNC3k4,1398
|
33
|
+
uipath/_cli/_evals/progress_reporter.py,sha256=m1Dio1vG-04nFTFz5ijM_j1dhudlgOzQukmTkkg6wS4,11490
|
34
|
+
uipath/_cli/_evals/_evaluators/__init__.py,sha256=jD7KNLjbsUpsESFXX11eW2MEPXDNuPp2-t-IPB-inlM,734
|
35
|
+
uipath/_cli/_evals/_evaluators/_deterministic_evaluator_base.py,sha256=BTl0puBjp9iCsU3YFfYWqk4TOz4iE19O3q1-dK6qUOI,1723
|
37
36
|
uipath/_cli/_evals/_evaluators/_evaluator_base.py,sha256=knHUwYFt0gMG1uJhq5TGEab6M_YevxX019yT3yYwZsw,3787
|
38
|
-
uipath/_cli/_evals/_evaluators/_evaluator_factory.py,sha256=
|
39
|
-
uipath/_cli/_evals/_evaluators/
|
37
|
+
uipath/_cli/_evals/_evaluators/_evaluator_factory.py,sha256=RJtCuFREZ8Ijlldpa0521poZLmcR7vTU3WyYOmhJOkc,4688
|
38
|
+
uipath/_cli/_evals/_evaluators/_exact_match_evaluator.py,sha256=lvEtAitrZy9myoZLMXLqlBWBPX06Msu67kuFMGSbikM,1319
|
39
|
+
uipath/_cli/_evals/_evaluators/_json_similarity_evaluator.py,sha256=HpmkvuwU4Az3IIqFVLUmDvzkqb21pFMxY0sg2biZOMM,7093
|
40
|
+
uipath/_cli/_evals/_evaluators/_llm_as_judge_evaluator.py,sha256=nSLZ29xWqALEI53ifr79JPXjyx0T4sr7p-4NygwgAio,6594
|
40
41
|
uipath/_cli/_evals/_evaluators/_trajectory_evaluator.py,sha256=dnogQTOskpI4_cNF0Ge3hBceJJocvOgxBWAwaCWnzB0,1595
|
41
42
|
uipath/_cli/_evals/_models/__init__.py,sha256=Ewjp3u2YeTH2MmzY9LWf7EIbAoIf_nW9fMYbj7pGlPs,420
|
42
43
|
uipath/_cli/_evals/_models/_evaluation_set.py,sha256=UIapFwn_Ti9zHUIcL3xyHDcLZ4lq4sHJ3JXLvY5OYI0,1080
|
43
|
-
uipath/_cli/_evals/_models/_evaluators.py,sha256=
|
44
|
+
uipath/_cli/_evals/_models/_evaluators.py,sha256=l57NEVyYmzSKuoIXuGkE94Br01hAMg35fiS2MlTkaQM,2115
|
44
45
|
uipath/_cli/_push/sw_file_handler.py,sha256=tRE9n68xv0r20ulwOyALHtYwzbjGneiASwzNm8xtBN0,16372
|
45
46
|
uipath/_cli/_runtime/_contracts.py,sha256=WlpaiQAMWCo-JFHjee35Klf49A3GsKjOU1Mf2IpUGHY,16033
|
46
47
|
uipath/_cli/_runtime/_escalation.py,sha256=x3vI98qsfRA-fL_tNkRVTFXioM5Gv2w0GFcXJJ5eQtg,7981
|
47
48
|
uipath/_cli/_runtime/_hitl.py,sha256=aexwe0dIXvh6SlVS1jVnO_aGZc6e3gLsmGkCyha5AHo,11300
|
48
49
|
uipath/_cli/_runtime/_logging.py,sha256=0_8OeF2w2zcryvozOsB3h4sFktiwnUqJr_fMRxwUFOc,8004
|
49
|
-
uipath/_cli/_runtime/_runtime.py,sha256=
|
50
|
+
uipath/_cli/_runtime/_runtime.py,sha256=Lee055XQBFe5sUiwo5T7XKOxKRCsv8gvy9wSZBnqVak,11917
|
50
51
|
uipath/_cli/_templates/.psmdcp.template,sha256=C7pBJPt98ovEljcBvGtEUGoWjjQhu9jls1bpYjeLOKA,611
|
51
52
|
uipath/_cli/_templates/.rels.template,sha256=-fTcw7OA1AcymHr0LzBqbMAAtzZTRXLTNa_ljq087Jk,406
|
52
53
|
uipath/_cli/_templates/[Content_Types].xml.template,sha256=bYsKDz31PkIF9QksjgAY_bqm57YC8U_owsZeNZAiBxQ,584
|
@@ -57,10 +58,10 @@ uipath/_cli/_utils/_console.py,sha256=scvnrrFoFX6CE451K-PXKV7UN0DUkInbOtDZ5jAdPP
|
|
57
58
|
uipath/_cli/_utils/_constants.py,sha256=rS8lQ5Nzull8ytajK6lBsz398qiCp1REoAwlHtyBwF0,1415
|
58
59
|
uipath/_cli/_utils/_debug.py,sha256=zamzIR4VgbdKADAE4gbmjxDsbgF7wvdr7C5Dqp744Oc,1739
|
59
60
|
uipath/_cli/_utils/_folders.py,sha256=UVJcKPfPAVR5HF4AP6EXdlNVcfEF1v5pwGCpoAgBY34,1155
|
60
|
-
uipath/_cli/_utils/_input_args.py,sha256=
|
61
|
-
uipath/_cli/_utils/_parse_ast.py,sha256=
|
61
|
+
uipath/_cli/_utils/_input_args.py,sha256=3LGNqVpJItvof75VGm-ZNTUMUH9-c7-YgleM5b2YgRg,5088
|
62
|
+
uipath/_cli/_utils/_parse_ast.py,sha256=8Iohz58s6bYQ7rgWtOTjrEInLJ-ETikmOMZzZdIY2Co,20072
|
62
63
|
uipath/_cli/_utils/_processes.py,sha256=q7DfEKHISDWf3pngci5za_z0Pbnf_shWiYEcTOTCiyk,1855
|
63
|
-
uipath/_cli/_utils/_project_files.py,sha256
|
64
|
+
uipath/_cli/_utils/_project_files.py,sha256=MMexSE6BapxitYBl1UUWy_AgkJMdGNFD1t2MkU0hwz4,13115
|
64
65
|
uipath/_cli/_utils/_studio_project.py,sha256=iVcCm-aps-EQ7Pym_OWGOA48xeDigzcofUxXKy29x6U,13727
|
65
66
|
uipath/_cli/_utils/_tracing.py,sha256=2igb03j3EHjF_A406UhtCKkPfudVfFPjUq5tXUEG4oo,1541
|
66
67
|
uipath/_cli/_utils/_uv_helpers.py,sha256=6SvoLnZPoKIxW0sjMvD1-ENV_HOXDYzH34GjBqwT138,3450
|
@@ -71,7 +72,7 @@ uipath/_services/api_client.py,sha256=AbRmpXQScrJIVfDQM309G1GNP3ILLKOrusouhxNQZf
|
|
71
72
|
uipath/_services/assets_service.py,sha256=acqWogfhZiSO1eeVYqFxmqWGSTmrW46QxI1J0bJe3jo,11918
|
72
73
|
uipath/_services/attachments_service.py,sha256=NPQYK7CGjfBaNT_1S5vEAfODmOChTbQZforllFM2ofU,26678
|
73
74
|
uipath/_services/buckets_service.py,sha256=5s8tuivd7GUZYj774DDUYTa0axxlUuesc4EBY1V5sdk,18496
|
74
|
-
uipath/_services/connections_service.py,sha256=
|
75
|
+
uipath/_services/connections_service.py,sha256=Rf-DCm43tsDM6Cfp41iwGR4gUk_YCdobGcmbSoKvQ6E,7480
|
75
76
|
uipath/_services/context_grounding_service.py,sha256=EBf7lIIYz_s1ubf_07OAZXQHjS8kpZ2vqxo4mI3VL-A,25009
|
76
77
|
uipath/_services/folder_service.py,sha256=9JqgjKhWD-G_KUnfUTP2BADxL6OK9QNZsBsWZHAULdE,2749
|
77
78
|
uipath/_services/jobs_service.py,sha256=CnDd7BM4AMqcMIR1qqu5ohhxf9m0AF4dnGoF4EX38kw,30872
|
@@ -80,7 +81,7 @@ uipath/_services/processes_service.py,sha256=Pk6paw7e_a-WvVcfKDLuyj1p--pvNRTXwZN
|
|
80
81
|
uipath/_services/queues_service.py,sha256=VaG3dWL2QK6AJBOLoW2NQTpkPfZjsqsYPl9-kfXPFzA,13534
|
81
82
|
uipath/_utils/__init__.py,sha256=VdcpnENJIa0R6Y26NoxY64-wUVyvb4pKfTh1wXDQeMk,526
|
82
83
|
uipath/_utils/_endpoint.py,sha256=yYHwqbQuJIevpaTkdfYJS9CrtlFeEyfb5JQK5osTCog,2489
|
83
|
-
uipath/_utils/_infer_bindings.py,sha256=
|
84
|
+
uipath/_utils/_infer_bindings.py,sha256=yTl3JBgoLwtQV-RCUkDY4qZne9FtE3HHIiYgCAKAOKw,1727
|
84
85
|
uipath/_utils/_logs.py,sha256=adfX_0UAn3YBeKJ8DQDeZs94rJyHGQO00uDfkaTpNWQ,510
|
85
86
|
uipath/_utils/_read_overwrites.py,sha256=OQgG9ycPpFnLub5ELQdX9V2Fyh6F9_zDR3xoYagJaMI,5287
|
86
87
|
uipath/_utils/_request_override.py,sha256=fIVHzgHVXITUlWcp8osNBwIafM1qm4_ejx0ng5UzfJ4,573
|
@@ -89,13 +90,13 @@ uipath/_utils/_ssl_context.py,sha256=xSYitos0eJc9cPHzNtHISX9PBvL6D2vas5G_GiBdLp8
|
|
89
90
|
uipath/_utils/_url.py,sha256=-4eluSrIZCUlnQ3qU17WPJkgaC2KwF9W5NeqGnTNGGo,2512
|
90
91
|
uipath/_utils/_user_agent.py,sha256=pVJkFYacGwaQBomfwWVAvBQgdBUo62e4n3-fLIajWUU,563
|
91
92
|
uipath/_utils/constants.py,sha256=9sjOPn2y4mU2KzBwKpkDOd1QW6AgL7omJBoCk4XebKk,1038
|
92
|
-
uipath/models/__init__.py,sha256=
|
93
|
+
uipath/models/__init__.py,sha256=d_DkK1AtRUetM1t2NrH5UKgvJOBiynzaKnK5pMY7aIc,1289
|
93
94
|
uipath/models/action_schema.py,sha256=lKDhP7Eix23fFvfQrqqNmSOiPyyNF6tiRpUu0VZIn_M,714
|
94
95
|
uipath/models/actions.py,sha256=ekSH4YUQR4KPOH-heBm9yOgOfirndx0In4_S4VYWeEU,2993
|
95
96
|
uipath/models/assets.py,sha256=Q-7_xmm503XHTKgCDrDtXsFl3VWBSVSyiWYW0mZjsnA,2942
|
96
97
|
uipath/models/attachment.py,sha256=prBlhhTvaBnLXJ3PKKxxHWbus35dCuag3_5HngksUjU,843
|
97
98
|
uipath/models/buckets.py,sha256=N3Lj_dVCv709-ywhOOdyCSvsuLn41eGuAfSiik6Q6F8,1285
|
98
|
-
uipath/models/connections.py,sha256=
|
99
|
+
uipath/models/connections.py,sha256=Kej_t2mH-JOomf8pDXPWW1SrvOd8OraOjOH0nKBrVlU,2598
|
99
100
|
uipath/models/context_grounding.py,sha256=S9PeOlFlw7VxzzJVR_Fs28OObW3MLHUPCFqNgkEz24k,1315
|
100
101
|
uipath/models/context_grounding_index.py,sha256=0ADlH8fC10qIbakgwU89pRVawzJ36TiSDKIqOhUdhuA,2580
|
101
102
|
uipath/models/errors.py,sha256=gPyU4sKYn57v03aOVqm97mnU9Do2e7bwMQwiSQVp9qc,461
|
@@ -113,9 +114,9 @@ uipath/tracing/_otel_exporters.py,sha256=X7cnuGqvxGbACZuFD2XYTWXwIse8pokOEAjeTPE
|
|
113
114
|
uipath/tracing/_traced.py,sha256=qeVDrds2OUnpdUIA0RhtF0kg2dlAZhyC1RRkI-qivTM,18528
|
114
115
|
uipath/tracing/_utils.py,sha256=ZeensQexnw69jVcsVrGyED7mPlAU-L1agDGm6_1A3oc,10388
|
115
116
|
uipath/utils/__init__.py,sha256=VD-KXFpF_oWexFg6zyiWMkxl2HM4hYJMIUDZ1UEtGx0,105
|
116
|
-
uipath/utils/_endpoints_manager.py,sha256=
|
117
|
-
uipath-2.1.
|
118
|
-
uipath-2.1.
|
119
|
-
uipath-2.1.
|
120
|
-
uipath-2.1.
|
121
|
-
uipath-2.1.
|
117
|
+
uipath/utils/_endpoints_manager.py,sha256=iRTl5Q0XAm_YgcnMcJOXtj-8052sr6jpWuPNz6CgT0Q,8408
|
118
|
+
uipath-2.1.20.dist-info/METADATA,sha256=CMJ4e0Xkauc6EZhmTQW6kVrQ3mnhWjZte2atCM4JyyE,6367
|
119
|
+
uipath-2.1.20.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
120
|
+
uipath-2.1.20.dist-info/entry_points.txt,sha256=9C2_29U6Oq1ExFu7usihR-dnfIVNSKc-0EFbh0rskB4,43
|
121
|
+
uipath-2.1.20.dist-info/licenses/LICENSE,sha256=-KBavWXepyDjimmzH5fVAsi-6jNVpIKFc2kZs0Ri4ng,1058
|
122
|
+
uipath-2.1.20.dist-info/RECORD,,
|
@@ -1,48 +0,0 @@
|
|
1
|
-
from typing import Any, Dict
|
2
|
-
|
3
|
-
from .._models import EvaluationResult
|
4
|
-
from ._evaluator_base import EvaluatorBase
|
5
|
-
|
6
|
-
|
7
|
-
class AgentScorerEvaluator(EvaluatorBase):
|
8
|
-
"""Evaluator that uses an agent to score outputs."""
|
9
|
-
|
10
|
-
def __init__(
|
11
|
-
self,
|
12
|
-
agent_config: Dict[str, Any],
|
13
|
-
scoring_criteria: Dict[str, Any],
|
14
|
-
target_output_key: str = "*",
|
15
|
-
):
|
16
|
-
"""Initialize the agent scorer evaluator.
|
17
|
-
|
18
|
-
Args:
|
19
|
-
agent_config: Configuration for the scoring agent
|
20
|
-
scoring_criteria: Criteria used for scoring
|
21
|
-
target_output_key: Key in output to evaluate ("*" for entire output)
|
22
|
-
"""
|
23
|
-
super().__init__()
|
24
|
-
self.agent_config = agent_config or {}
|
25
|
-
self.scoring_criteria = scoring_criteria or {}
|
26
|
-
self.target_output_key = target_output_key
|
27
|
-
|
28
|
-
async def evaluate(
|
29
|
-
self,
|
30
|
-
evaluation_id: str,
|
31
|
-
evaluation_name: str,
|
32
|
-
input_data: Dict[str, Any],
|
33
|
-
expected_output: Dict[str, Any],
|
34
|
-
actual_output: Dict[str, Any],
|
35
|
-
) -> EvaluationResult:
|
36
|
-
"""Evaluate using an agent scorer.
|
37
|
-
|
38
|
-
Args:
|
39
|
-
evaluation_id: The ID of the evaluation being processed
|
40
|
-
evaluation_name: The name of the evaluation
|
41
|
-
input_data: The input data for the evaluation
|
42
|
-
expected_output: The expected output
|
43
|
-
actual_output: The actual output from the agent
|
44
|
-
|
45
|
-
Returns:
|
46
|
-
EvaluationResult containing the score and details
|
47
|
-
"""
|
48
|
-
raise NotImplementedError()
|
@@ -1,41 +0,0 @@
|
|
1
|
-
from typing import Any, Dict
|
2
|
-
|
3
|
-
from .._models import EvaluationResult
|
4
|
-
from ._evaluator_base import EvaluatorBase
|
5
|
-
|
6
|
-
|
7
|
-
class DeterministicEvaluator(EvaluatorBase):
|
8
|
-
"""Evaluator for deterministic/rule-based evaluations."""
|
9
|
-
|
10
|
-
def __init__(self, rule_config: Dict[str, Any], target_output_key: str = "*"):
|
11
|
-
"""Initialize the deterministic evaluator.
|
12
|
-
|
13
|
-
Args:
|
14
|
-
rule_config: Configuration for the rule (expected_value, regex_pattern, etc.)
|
15
|
-
target_output_key: Key in output to evaluate ("*" for entire output)
|
16
|
-
"""
|
17
|
-
super().__init__()
|
18
|
-
self.rule_config = rule_config or {}
|
19
|
-
self.target_output_key = target_output_key
|
20
|
-
|
21
|
-
async def evaluate(
|
22
|
-
self,
|
23
|
-
evaluation_id: str,
|
24
|
-
evaluation_name: str,
|
25
|
-
input_data: Dict[str, Any],
|
26
|
-
expected_output: Dict[str, Any],
|
27
|
-
actual_output: Dict[str, Any],
|
28
|
-
) -> EvaluationResult:
|
29
|
-
"""Evaluate using deterministic rules.
|
30
|
-
|
31
|
-
Args:
|
32
|
-
evaluation_id: The ID of the evaluation being processed
|
33
|
-
evaluation_name: The name of the evaluation
|
34
|
-
input_data: The input data for the evaluation
|
35
|
-
expected_output: The expected output
|
36
|
-
actual_output: The actual output from the agent
|
37
|
-
|
38
|
-
Returns:
|
39
|
-
EvaluationResult containing the score and details
|
40
|
-
"""
|
41
|
-
raise NotImplementedError()
|
File without changes
|
File without changes
|
File without changes
|