calibrate-python-sdk 0.0.1__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 (56) hide show
  1. artpark/__init__.py +109 -0
  2. artpark/_default_clients.py +32 -0
  3. artpark/agent_tests/__init__.py +4 -0
  4. artpark/agent_tests/client.py +356 -0
  5. artpark/agent_tests/raw_client.py +455 -0
  6. artpark/agents/__init__.py +4 -0
  7. artpark/agents/client.py +210 -0
  8. artpark/agents/raw_client.py +273 -0
  9. artpark/client.py +268 -0
  10. artpark/core/__init__.py +127 -0
  11. artpark/core/api_error.py +23 -0
  12. artpark/core/client_wrapper.py +163 -0
  13. artpark/core/datetime_utils.py +70 -0
  14. artpark/core/file.py +67 -0
  15. artpark/core/force_multipart.py +18 -0
  16. artpark/core/http_client.py +843 -0
  17. artpark/core/http_response.py +59 -0
  18. artpark/core/http_sse/__init__.py +42 -0
  19. artpark/core/http_sse/_api.py +180 -0
  20. artpark/core/http_sse/_decoders.py +61 -0
  21. artpark/core/http_sse/_exceptions.py +7 -0
  22. artpark/core/http_sse/_models.py +17 -0
  23. artpark/core/jsonable_encoder.py +120 -0
  24. artpark/core/logging.py +107 -0
  25. artpark/core/parse_error.py +36 -0
  26. artpark/core/pydantic_utilities.py +508 -0
  27. artpark/core/query_encoder.py +58 -0
  28. artpark/core/remove_none_from_dict.py +11 -0
  29. artpark/core/request_options.py +37 -0
  30. artpark/core/serialization.py +347 -0
  31. artpark/environment.py +7 -0
  32. artpark/errors/__init__.py +34 -0
  33. artpark/errors/unprocessable_entity_error.py +11 -0
  34. artpark/py.typed +0 -0
  35. artpark/types/__init__.py +83 -0
  36. artpark/types/batch_run_request.py +19 -0
  37. artpark/types/batch_test_run.py +22 -0
  38. artpark/types/batch_test_run_response.py +22 -0
  39. artpark/types/batch_test_skip.py +21 -0
  40. artpark/types/http_validation_error.py +20 -0
  41. artpark/types/judge_result.py +51 -0
  42. artpark/types/resolve_agent_names_response.py +20 -0
  43. artpark/types/routers_agent_tests_agent_response.py +27 -0
  44. artpark/types/routers_agent_tests_agent_response_type.py +5 -0
  45. artpark/types/task_create_response.py +22 -0
  46. artpark/types/test_case_result.py +33 -0
  47. artpark/types/test_output.py +21 -0
  48. artpark/types/test_run_status_response.py +37 -0
  49. artpark/types/tool_call_output.py +21 -0
  50. artpark/types/validation_error.py +22 -0
  51. artpark/types/validation_error_loc_item.py +5 -0
  52. artpark/version.py +3 -0
  53. calibrate_python_sdk-0.0.1.dist-info/LICENSE +21 -0
  54. calibrate_python_sdk-0.0.1.dist-info/METADATA +55 -0
  55. calibrate_python_sdk-0.0.1.dist-info/RECORD +56 -0
  56. calibrate_python_sdk-0.0.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,347 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import collections
4
+ import inspect
5
+ import typing
6
+
7
+ import pydantic
8
+ import typing_extensions
9
+
10
+
11
+ class FieldMetadata:
12
+ """
13
+ Metadata class used to annotate fields to provide additional information.
14
+
15
+ Example:
16
+ class MyDict(TypedDict):
17
+ field: typing.Annotated[str, FieldMetadata(alias="field_name")]
18
+
19
+ Will serialize: `{"field": "value"}`
20
+ To: `{"field_name": "value"}`
21
+ """
22
+
23
+ alias: str
24
+
25
+ def __init__(self, *, alias: str) -> None:
26
+ self.alias = alias
27
+
28
+
29
+ # Resolving type hints (typing.get_type_hints) is expensive because it eval/compiles
30
+ # forward-reference annotations. The result is constant for a given type, so we cache it.
31
+ # This is critical for hot paths like SSE event parsing, where the same (often large
32
+ # discriminated-union) type is converted on every single event.
33
+ _type_hints_cache: typing.Dict[typing.Any, typing.Dict[str, typing.Any]] = {}
34
+
35
+
36
+ def _get_cached_type_hints(expected_type: typing.Any) -> typing.Dict[str, typing.Any]:
37
+ try:
38
+ cached = _type_hints_cache.get(expected_type)
39
+ except TypeError:
40
+ # Unhashable type; resolve without caching.
41
+ return _resolve_type_hints(expected_type)
42
+ if cached is None:
43
+ cached = _resolve_type_hints(expected_type)
44
+ _type_hints_cache[expected_type] = cached
45
+ return cached
46
+
47
+
48
+ def _resolve_type_hints(expected_type: typing.Any) -> typing.Dict[str, typing.Any]:
49
+ try:
50
+ return typing_extensions.get_type_hints(expected_type, include_extras=True)
51
+ except NameError:
52
+ # The type contains a circular reference, so we use the __annotations__ attribute directly.
53
+ return getattr(expected_type, "__annotations__", {})
54
+
55
+
56
+ # Whether convert_and_respect_annotation_metadata can possibly rewrite anything for a given
57
+ # annotation, i.e. whether any reachable model/TypedDict field carries a FieldMetadata alias.
58
+ # This is constant per type, so we cache it and use it to short-circuit the recursive walk.
59
+ _requires_conversion_cache: typing.Dict[typing.Any, bool] = {}
60
+
61
+
62
+ def _requires_conversion(type_: typing.Any) -> bool:
63
+ try:
64
+ cached = _requires_conversion_cache.get(type_)
65
+ except TypeError:
66
+ # Unhashable annotation; compute without caching.
67
+ return _compute_requires_conversion(type_, set())
68
+ if cached is None:
69
+ cached = _compute_requires_conversion(type_, set())
70
+ _requires_conversion_cache[type_] = cached
71
+ return cached
72
+
73
+
74
+ def _compute_requires_conversion(type_: typing.Any, seen: typing.Set[typing.Any]) -> bool:
75
+ clean_type = _remove_annotations(type_)
76
+
77
+ try:
78
+ if clean_type in seen:
79
+ return False
80
+ seen = seen | {clean_type}
81
+ except TypeError:
82
+ # Unhashable type; skip cycle tracking (the type graph is finite in practice).
83
+ pass
84
+
85
+ # Models / TypedDicts: a field alias here means we must dealias; otherwise recurse into fields.
86
+ if (inspect.isclass(clean_type) and issubclass(clean_type, pydantic.BaseModel)) or typing_extensions.is_typeddict(
87
+ clean_type
88
+ ):
89
+ annotations = _get_cached_type_hints(clean_type)
90
+ if _get_alias_to_field_name(annotations):
91
+ return True
92
+ return any(_compute_requires_conversion(hint, seen) for hint in annotations.values())
93
+
94
+ # Containers / unions: recurse into the type arguments (List/Set/Sequence/Dict/Union/etc.).
95
+ return any(_compute_requires_conversion(arg, seen) for arg in typing_extensions.get_args(clean_type))
96
+
97
+
98
+ def convert_and_respect_annotation_metadata(
99
+ *,
100
+ object_: typing.Any,
101
+ annotation: typing.Any,
102
+ inner_type: typing.Optional[typing.Any] = None,
103
+ direction: typing.Literal["read", "write"],
104
+ ) -> typing.Any:
105
+ """
106
+ Respect the metadata annotations on a field, such as aliasing. This function effectively
107
+ manipulates the dict-form of an object to respect the metadata annotations. This is primarily used for
108
+ TypedDicts, which cannot support aliasing out of the box, and can be extended for additional
109
+ utilities, such as defaults.
110
+
111
+ Parameters
112
+ ----------
113
+ object_ : typing.Any
114
+
115
+ annotation : type
116
+ The type we're looking to apply typing annotations from
117
+
118
+ inner_type : typing.Optional[type]
119
+
120
+ Returns
121
+ -------
122
+ typing.Any
123
+ """
124
+
125
+ if object_ is None:
126
+ return None
127
+ if inner_type is None:
128
+ inner_type = annotation
129
+ # The only thing this function ever rewrites is keys that carry a FieldMetadata
130
+ # alias. If nothing in the (cached) type graph has such an alias, the conversion is
131
+ # a content-identity transform, so we can skip the entire recursive walk. This is
132
+ # the hot path for SSE streaming, where a large discriminated union would otherwise
133
+ # be traversed on every single event.
134
+ if not _requires_conversion(annotation):
135
+ return object_
136
+
137
+ clean_type = _remove_annotations(inner_type)
138
+ # Pydantic models
139
+ if (
140
+ inspect.isclass(clean_type)
141
+ and issubclass(clean_type, pydantic.BaseModel)
142
+ and isinstance(object_, typing.Mapping)
143
+ ):
144
+ return _convert_mapping(object_, clean_type, direction)
145
+ # TypedDicts
146
+ if typing_extensions.is_typeddict(clean_type) and isinstance(object_, typing.Mapping):
147
+ return _convert_mapping(object_, clean_type, direction)
148
+
149
+ if (
150
+ typing_extensions.get_origin(clean_type) == typing.Dict
151
+ or typing_extensions.get_origin(clean_type) == dict
152
+ or clean_type == typing.Dict
153
+ ) and isinstance(object_, typing.Dict):
154
+ key_type = typing_extensions.get_args(clean_type)[0]
155
+ value_type = typing_extensions.get_args(clean_type)[1]
156
+
157
+ return {
158
+ key: convert_and_respect_annotation_metadata(
159
+ object_=value,
160
+ annotation=annotation,
161
+ inner_type=value_type,
162
+ direction=direction,
163
+ )
164
+ for key, value in object_.items()
165
+ }
166
+
167
+ # If you're iterating on a string, do not bother to coerce it to a sequence.
168
+ if not isinstance(object_, str):
169
+ if (
170
+ typing_extensions.get_origin(clean_type) == typing.Set
171
+ or typing_extensions.get_origin(clean_type) == set
172
+ or clean_type == typing.Set
173
+ ) and isinstance(object_, typing.Set):
174
+ inner_type = typing_extensions.get_args(clean_type)[0]
175
+ return {
176
+ convert_and_respect_annotation_metadata(
177
+ object_=item,
178
+ annotation=annotation,
179
+ inner_type=inner_type,
180
+ direction=direction,
181
+ )
182
+ for item in object_
183
+ }
184
+ elif (
185
+ (
186
+ typing_extensions.get_origin(clean_type) == typing.List
187
+ or typing_extensions.get_origin(clean_type) == list
188
+ or clean_type == typing.List
189
+ )
190
+ and isinstance(object_, typing.List)
191
+ ) or (
192
+ (
193
+ typing_extensions.get_origin(clean_type) == typing.Sequence
194
+ or typing_extensions.get_origin(clean_type) == collections.abc.Sequence
195
+ or clean_type == typing.Sequence
196
+ )
197
+ and isinstance(object_, typing.Sequence)
198
+ ):
199
+ inner_type = typing_extensions.get_args(clean_type)[0]
200
+ return [
201
+ convert_and_respect_annotation_metadata(
202
+ object_=item,
203
+ annotation=annotation,
204
+ inner_type=inner_type,
205
+ direction=direction,
206
+ )
207
+ for item in object_
208
+ ]
209
+
210
+ if typing_extensions.get_origin(clean_type) == typing.Union:
211
+ # We should be able to ~relatively~ safely try to convert keys against all
212
+ # member types in the union, the edge case here is if one member aliases a field
213
+ # of the same name to a different name from another member
214
+ # Or if another member aliases a field of the same name that another member does not.
215
+ for member in typing_extensions.get_args(clean_type):
216
+ object_ = convert_and_respect_annotation_metadata(
217
+ object_=object_,
218
+ annotation=annotation,
219
+ inner_type=member,
220
+ direction=direction,
221
+ )
222
+ return object_
223
+
224
+ annotated_type = _get_annotation(annotation)
225
+ if annotated_type is None:
226
+ return object_
227
+
228
+ # If the object is not a TypedDict, a Union, or other container (list, set, sequence, etc.)
229
+ # Then we can safely call it on the recursive conversion.
230
+ return object_
231
+
232
+
233
+ def _convert_mapping(
234
+ object_: typing.Mapping[str, object],
235
+ expected_type: typing.Any,
236
+ direction: typing.Literal["read", "write"],
237
+ ) -> typing.Mapping[str, object]:
238
+ converted_object: typing.Dict[str, object] = {}
239
+ annotations = _get_cached_type_hints(expected_type)
240
+ aliases_to_field_names = _get_alias_to_field_name(annotations)
241
+ for key, value in object_.items():
242
+ if direction == "read" and key in aliases_to_field_names:
243
+ dealiased_key = aliases_to_field_names.get(key)
244
+ if dealiased_key is not None:
245
+ type_ = annotations.get(dealiased_key)
246
+ else:
247
+ type_ = annotations.get(key)
248
+ # Note you can't get the annotation by the field name if you're in read mode, so you must check the aliases map
249
+ #
250
+ # So this is effectively saying if we're in write mode, and we don't have a type, or if we're in read mode and we don't have an alias
251
+ # then we can just pass the value through as is
252
+ if type_ is None:
253
+ converted_object[key] = value
254
+ elif direction == "read" and key not in aliases_to_field_names:
255
+ converted_object[key] = convert_and_respect_annotation_metadata(
256
+ object_=value, annotation=type_, direction=direction
257
+ )
258
+ else:
259
+ converted_object[_alias_key(key, type_, direction, aliases_to_field_names)] = (
260
+ convert_and_respect_annotation_metadata(object_=value, annotation=type_, direction=direction)
261
+ )
262
+ return converted_object
263
+
264
+
265
+ def _get_annotation(type_: typing.Any) -> typing.Optional[typing.Any]:
266
+ maybe_annotated_type = typing_extensions.get_origin(type_)
267
+ if maybe_annotated_type is None:
268
+ return None
269
+
270
+ if maybe_annotated_type == typing_extensions.NotRequired:
271
+ type_ = typing_extensions.get_args(type_)[0]
272
+ maybe_annotated_type = typing_extensions.get_origin(type_)
273
+
274
+ if maybe_annotated_type == typing_extensions.Annotated:
275
+ return type_
276
+
277
+ return None
278
+
279
+
280
+ def _remove_annotations(type_: typing.Any) -> typing.Any:
281
+ maybe_annotated_type = typing_extensions.get_origin(type_)
282
+ if maybe_annotated_type is None:
283
+ return type_
284
+
285
+ if maybe_annotated_type == typing_extensions.NotRequired:
286
+ return _remove_annotations(typing_extensions.get_args(type_)[0])
287
+
288
+ if maybe_annotated_type == typing_extensions.Annotated:
289
+ return _remove_annotations(typing_extensions.get_args(type_)[0])
290
+
291
+ return type_
292
+
293
+
294
+ def get_alias_to_field_mapping(type_: typing.Any) -> typing.Dict[str, str]:
295
+ annotations = _get_cached_type_hints(type_)
296
+ return _get_alias_to_field_name(annotations)
297
+
298
+
299
+ def get_field_to_alias_mapping(type_: typing.Any) -> typing.Dict[str, str]:
300
+ annotations = _get_cached_type_hints(type_)
301
+ return _get_field_to_alias_name(annotations)
302
+
303
+
304
+ def _get_alias_to_field_name(
305
+ field_to_hint: typing.Dict[str, typing.Any],
306
+ ) -> typing.Dict[str, str]:
307
+ aliases = {}
308
+ for field, hint in field_to_hint.items():
309
+ maybe_alias = _get_alias_from_type(hint)
310
+ if maybe_alias is not None:
311
+ aliases[maybe_alias] = field
312
+ return aliases
313
+
314
+
315
+ def _get_field_to_alias_name(
316
+ field_to_hint: typing.Dict[str, typing.Any],
317
+ ) -> typing.Dict[str, str]:
318
+ aliases = {}
319
+ for field, hint in field_to_hint.items():
320
+ maybe_alias = _get_alias_from_type(hint)
321
+ if maybe_alias is not None:
322
+ aliases[field] = maybe_alias
323
+ return aliases
324
+
325
+
326
+ def _get_alias_from_type(type_: typing.Any) -> typing.Optional[str]:
327
+ maybe_annotated_type = _get_annotation(type_)
328
+
329
+ if maybe_annotated_type is not None:
330
+ # The actual annotations are 1 onward, the first is the annotated type
331
+ annotations = typing_extensions.get_args(maybe_annotated_type)[1:]
332
+
333
+ for annotation in annotations:
334
+ if isinstance(annotation, FieldMetadata) and annotation.alias is not None:
335
+ return annotation.alias
336
+ return None
337
+
338
+
339
+ def _alias_key(
340
+ key: str,
341
+ type_: typing.Any,
342
+ direction: typing.Literal["read", "write"],
343
+ aliases_to_field_names: typing.Dict[str, str],
344
+ ) -> str:
345
+ if direction == "read":
346
+ return aliases_to_field_names.get(key, key)
347
+ return _get_alias_from_type(type_=type_) or key
artpark/environment.py ADDED
@@ -0,0 +1,7 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import enum
4
+
5
+
6
+ class CalibrateEnvironment(enum.Enum):
7
+ PRODUCTION = "https://pense-backend.artpark.ai"
@@ -0,0 +1,34 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ # isort: skip_file
4
+
5
+ import typing
6
+ from importlib import import_module
7
+
8
+ if typing.TYPE_CHECKING:
9
+ from .unprocessable_entity_error import UnprocessableEntityError
10
+ _dynamic_imports: typing.Dict[str, str] = {"UnprocessableEntityError": ".unprocessable_entity_error"}
11
+
12
+
13
+ def __getattr__(attr_name: str) -> typing.Any:
14
+ module_name = _dynamic_imports.get(attr_name)
15
+ if module_name is None:
16
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
17
+ try:
18
+ module = import_module(module_name, __package__)
19
+ if module_name == f".{attr_name}":
20
+ return module
21
+ else:
22
+ return getattr(module, attr_name)
23
+ except ImportError as e:
24
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
25
+ except AttributeError as e:
26
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
27
+
28
+
29
+ def __dir__():
30
+ lazy_attrs = list(_dynamic_imports.keys())
31
+ return sorted(lazy_attrs)
32
+
33
+
34
+ __all__ = ["UnprocessableEntityError"]
@@ -0,0 +1,11 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ from ..core.api_error import ApiError
6
+ from ..types.http_validation_error import HttpValidationError
7
+
8
+
9
+ class UnprocessableEntityError(ApiError):
10
+ def __init__(self, body: HttpValidationError, headers: typing.Optional[typing.Dict[str, str]] = None):
11
+ super().__init__(status_code=422, headers=headers, body=body)
artpark/py.typed ADDED
File without changes
@@ -0,0 +1,83 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ # isort: skip_file
4
+
5
+ import typing
6
+ from importlib import import_module
7
+
8
+ if typing.TYPE_CHECKING:
9
+ from .batch_run_request import BatchRunRequest
10
+ from .batch_test_run import BatchTestRun
11
+ from .batch_test_run_response import BatchTestRunResponse
12
+ from .batch_test_skip import BatchTestSkip
13
+ from .http_validation_error import HttpValidationError
14
+ from .judge_result import JudgeResult
15
+ from .resolve_agent_names_response import ResolveAgentNamesResponse
16
+ from .routers_agent_tests_agent_response import RoutersAgentTestsAgentResponse
17
+ from .routers_agent_tests_agent_response_type import RoutersAgentTestsAgentResponseType
18
+ from .task_create_response import TaskCreateResponse
19
+ from .test_case_result import TestCaseResult
20
+ from .test_output import TestOutput
21
+ from .test_run_status_response import TestRunStatusResponse
22
+ from .tool_call_output import ToolCallOutput
23
+ from .validation_error import ValidationError
24
+ from .validation_error_loc_item import ValidationErrorLocItem
25
+ _dynamic_imports: typing.Dict[str, str] = {
26
+ "BatchRunRequest": ".batch_run_request",
27
+ "BatchTestRun": ".batch_test_run",
28
+ "BatchTestRunResponse": ".batch_test_run_response",
29
+ "BatchTestSkip": ".batch_test_skip",
30
+ "HttpValidationError": ".http_validation_error",
31
+ "JudgeResult": ".judge_result",
32
+ "ResolveAgentNamesResponse": ".resolve_agent_names_response",
33
+ "RoutersAgentTestsAgentResponse": ".routers_agent_tests_agent_response",
34
+ "RoutersAgentTestsAgentResponseType": ".routers_agent_tests_agent_response_type",
35
+ "TaskCreateResponse": ".task_create_response",
36
+ "TestCaseResult": ".test_case_result",
37
+ "TestOutput": ".test_output",
38
+ "TestRunStatusResponse": ".test_run_status_response",
39
+ "ToolCallOutput": ".tool_call_output",
40
+ "ValidationError": ".validation_error",
41
+ "ValidationErrorLocItem": ".validation_error_loc_item",
42
+ }
43
+
44
+
45
+ def __getattr__(attr_name: str) -> typing.Any:
46
+ module_name = _dynamic_imports.get(attr_name)
47
+ if module_name is None:
48
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
49
+ try:
50
+ module = import_module(module_name, __package__)
51
+ if module_name == f".{attr_name}":
52
+ return module
53
+ else:
54
+ return getattr(module, attr_name)
55
+ except ImportError as e:
56
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
57
+ except AttributeError as e:
58
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
59
+
60
+
61
+ def __dir__():
62
+ lazy_attrs = list(_dynamic_imports.keys())
63
+ return sorted(lazy_attrs)
64
+
65
+
66
+ __all__ = [
67
+ "BatchRunRequest",
68
+ "BatchTestRun",
69
+ "BatchTestRunResponse",
70
+ "BatchTestSkip",
71
+ "HttpValidationError",
72
+ "JudgeResult",
73
+ "ResolveAgentNamesResponse",
74
+ "RoutersAgentTestsAgentResponse",
75
+ "RoutersAgentTestsAgentResponseType",
76
+ "TaskCreateResponse",
77
+ "TestCaseResult",
78
+ "TestOutput",
79
+ "TestRunStatusResponse",
80
+ "ToolCallOutput",
81
+ "ValidationError",
82
+ "ValidationErrorLocItem",
83
+ ]
@@ -0,0 +1,19 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ import pydantic
6
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
+
8
+
9
+ class BatchRunRequest(UniversalBaseModel):
10
+ agent_names: typing.Optional[typing.List[str]] = None
11
+
12
+ if IS_PYDANTIC_V2:
13
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
14
+ else:
15
+
16
+ class Config:
17
+ frozen = True
18
+ smart_union = True
19
+ extra = pydantic.Extra.allow
@@ -0,0 +1,22 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ import pydantic
6
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
+
8
+
9
+ class BatchTestRun(UniversalBaseModel):
10
+ agent_name: str
11
+ agent_uuid: str
12
+ task_id: str
13
+ status: str
14
+
15
+ if IS_PYDANTIC_V2:
16
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
17
+ else:
18
+
19
+ class Config:
20
+ frozen = True
21
+ smart_union = True
22
+ extra = pydantic.Extra.allow
@@ -0,0 +1,22 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ import pydantic
6
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
+ from .batch_test_run import BatchTestRun
8
+ from .batch_test_skip import BatchTestSkip
9
+
10
+
11
+ class BatchTestRunResponse(UniversalBaseModel):
12
+ runs: typing.List[BatchTestRun]
13
+ skipped: typing.Optional[typing.List[BatchTestSkip]] = None
14
+
15
+ if IS_PYDANTIC_V2:
16
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
17
+ else:
18
+
19
+ class Config:
20
+ frozen = True
21
+ smart_union = True
22
+ extra = pydantic.Extra.allow
@@ -0,0 +1,21 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ import pydantic
6
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
+
8
+
9
+ class BatchTestSkip(UniversalBaseModel):
10
+ agent_name: str
11
+ agent_uuid: str
12
+ reason: str
13
+
14
+ if IS_PYDANTIC_V2:
15
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
16
+ else:
17
+
18
+ class Config:
19
+ frozen = True
20
+ smart_union = True
21
+ extra = pydantic.Extra.allow
@@ -0,0 +1,20 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ import pydantic
6
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
+ from .validation_error import ValidationError
8
+
9
+
10
+ class HttpValidationError(UniversalBaseModel):
11
+ detail: typing.Optional[typing.List[ValidationError]] = None
12
+
13
+ if IS_PYDANTIC_V2:
14
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
15
+ else:
16
+
17
+ class Config:
18
+ frozen = True
19
+ smart_union = True
20
+ extra = pydantic.Extra.allow
@@ -0,0 +1,51 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ import pydantic
6
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
+
8
+
9
+ class JudgeResult(UniversalBaseModel):
10
+ """
11
+ One evaluator's verdict for a response-type test case.
12
+
13
+ Per-row data only — anything constant across rows for the same evaluator
14
+ (name, description, output_type, output_config, scale_min/max) lives on
15
+ the response-level `evaluators[]` block; look it up by `evaluator_uuid`.
16
+
17
+ `evaluator_uuid` is None for legacy runs that pre-date the evaluator-
18
+ snapshot capture or when the evaluator can't be resolved from the
19
+ snapshot.
20
+
21
+ Exactly one of `match` (binary) / `score` (rating) is set per entry;
22
+ both are None for tool-call tests, but tool-call tests don't carry
23
+ `judge_results`.
24
+
25
+ `variable_values` are the {{var}} substitutions used for this evaluator
26
+ on this test case, frozen from `test_evaluators.variable_values` at
27
+ submission time — stays on the row because it varies per test case.
28
+
29
+ `value_name` is the human-readable label for `match`/`score` resolved
30
+ against the rubric the run actually used (snapshot's
31
+ `output_config.scale.name`). Falls back to `Correct`/`Wrong` for binary
32
+ or the stringified score for rating when the snapshot lacks named
33
+ scale entries (e.g. legacy runs captured before the rubric was
34
+ snapshotted).
35
+ """
36
+
37
+ evaluator_uuid: typing.Optional[str] = None
38
+ reasoning: typing.Optional[str] = None
39
+ match: typing.Optional[bool] = None
40
+ score: typing.Optional[float] = None
41
+ value_name: typing.Optional[str] = None
42
+ variable_values: typing.Optional[typing.Dict[str, typing.Any]] = None
43
+
44
+ if IS_PYDANTIC_V2:
45
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
46
+ else:
47
+
48
+ class Config:
49
+ frozen = True
50
+ smart_union = True
51
+ extra = pydantic.Extra.allow
@@ -0,0 +1,20 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ import pydantic
6
+ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
+
8
+
9
+ class ResolveAgentNamesResponse(UniversalBaseModel):
10
+ resolved: typing.Dict[str, str]
11
+ not_found: typing.List[str]
12
+
13
+ if IS_PYDANTIC_V2:
14
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
15
+ else:
16
+
17
+ class Config:
18
+ frozen = True
19
+ smart_union = True
20
+ extra = pydantic.Extra.allow