arize 8.0.0a22__py3-none-any.whl → 8.0.0a23__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 (166) hide show
  1. arize/__init__.py +17 -9
  2. arize/_exporter/client.py +55 -36
  3. arize/_exporter/parsers/tracing_data_parser.py +41 -30
  4. arize/_exporter/validation.py +3 -3
  5. arize/_flight/client.py +207 -76
  6. arize/_generated/api_client/__init__.py +30 -6
  7. arize/_generated/api_client/api/__init__.py +1 -0
  8. arize/_generated/api_client/api/datasets_api.py +864 -190
  9. arize/_generated/api_client/api/experiments_api.py +167 -131
  10. arize/_generated/api_client/api/projects_api.py +1197 -0
  11. arize/_generated/api_client/api_client.py +2 -2
  12. arize/_generated/api_client/configuration.py +42 -34
  13. arize/_generated/api_client/exceptions.py +2 -2
  14. arize/_generated/api_client/models/__init__.py +15 -4
  15. arize/_generated/api_client/models/dataset.py +10 -10
  16. arize/_generated/api_client/models/dataset_example.py +111 -0
  17. arize/_generated/api_client/models/dataset_example_update.py +100 -0
  18. arize/_generated/api_client/models/dataset_version.py +13 -13
  19. arize/_generated/api_client/models/datasets_create_request.py +16 -8
  20. arize/_generated/api_client/models/datasets_examples_insert_request.py +100 -0
  21. arize/_generated/api_client/models/datasets_examples_list200_response.py +106 -0
  22. arize/_generated/api_client/models/datasets_examples_update_request.py +102 -0
  23. arize/_generated/api_client/models/datasets_list200_response.py +10 -4
  24. arize/_generated/api_client/models/experiment.py +14 -16
  25. arize/_generated/api_client/models/experiment_run.py +108 -0
  26. arize/_generated/api_client/models/experiment_run_create.py +102 -0
  27. arize/_generated/api_client/models/experiments_create_request.py +16 -10
  28. arize/_generated/api_client/models/experiments_list200_response.py +10 -4
  29. arize/_generated/api_client/models/experiments_runs_list200_response.py +19 -5
  30. arize/_generated/api_client/models/{error.py → pagination_metadata.py} +13 -11
  31. arize/_generated/api_client/models/primitive_value.py +172 -0
  32. arize/_generated/api_client/models/problem.py +100 -0
  33. arize/_generated/api_client/models/project.py +99 -0
  34. arize/_generated/api_client/models/{datasets_list_examples200_response.py → projects_create_request.py} +13 -11
  35. arize/_generated/api_client/models/projects_list200_response.py +106 -0
  36. arize/_generated/api_client/rest.py +2 -2
  37. arize/_generated/api_client/test/test_dataset.py +4 -2
  38. arize/_generated/api_client/test/test_dataset_example.py +56 -0
  39. arize/_generated/api_client/test/test_dataset_example_update.py +52 -0
  40. arize/_generated/api_client/test/test_dataset_version.py +7 -2
  41. arize/_generated/api_client/test/test_datasets_api.py +27 -13
  42. arize/_generated/api_client/test/test_datasets_create_request.py +8 -4
  43. arize/_generated/api_client/test/{test_datasets_list_examples200_response.py → test_datasets_examples_insert_request.py} +19 -15
  44. arize/_generated/api_client/test/test_datasets_examples_list200_response.py +66 -0
  45. arize/_generated/api_client/test/test_datasets_examples_update_request.py +61 -0
  46. arize/_generated/api_client/test/test_datasets_list200_response.py +9 -3
  47. arize/_generated/api_client/test/test_experiment.py +2 -4
  48. arize/_generated/api_client/test/test_experiment_run.py +56 -0
  49. arize/_generated/api_client/test/test_experiment_run_create.py +54 -0
  50. arize/_generated/api_client/test/test_experiments_api.py +6 -6
  51. arize/_generated/api_client/test/test_experiments_create_request.py +9 -6
  52. arize/_generated/api_client/test/test_experiments_list200_response.py +9 -5
  53. arize/_generated/api_client/test/test_experiments_runs_list200_response.py +15 -5
  54. arize/_generated/api_client/test/test_pagination_metadata.py +53 -0
  55. arize/_generated/api_client/test/{test_error.py → test_primitive_value.py} +13 -14
  56. arize/_generated/api_client/test/test_problem.py +57 -0
  57. arize/_generated/api_client/test/test_project.py +58 -0
  58. arize/_generated/api_client/test/test_projects_api.py +59 -0
  59. arize/_generated/api_client/test/test_projects_create_request.py +54 -0
  60. arize/_generated/api_client/test/test_projects_list200_response.py +70 -0
  61. arize/_generated/api_client_README.md +43 -29
  62. arize/_generated/protocol/flight/flight_pb2.py +400 -0
  63. arize/_lazy.py +27 -19
  64. arize/client.py +268 -55
  65. arize/config.py +365 -116
  66. arize/constants/__init__.py +1 -0
  67. arize/constants/config.py +11 -4
  68. arize/constants/ml.py +6 -4
  69. arize/constants/openinference.py +2 -0
  70. arize/constants/pyarrow.py +2 -0
  71. arize/constants/spans.py +3 -1
  72. arize/datasets/__init__.py +1 -0
  73. arize/datasets/client.py +299 -84
  74. arize/datasets/errors.py +32 -2
  75. arize/datasets/validation.py +18 -8
  76. arize/embeddings/__init__.py +2 -0
  77. arize/embeddings/auto_generator.py +23 -19
  78. arize/embeddings/base_generators.py +89 -36
  79. arize/embeddings/constants.py +2 -0
  80. arize/embeddings/cv_generators.py +26 -4
  81. arize/embeddings/errors.py +27 -5
  82. arize/embeddings/nlp_generators.py +31 -12
  83. arize/embeddings/tabular_generators.py +32 -20
  84. arize/embeddings/usecases.py +12 -2
  85. arize/exceptions/__init__.py +1 -0
  86. arize/exceptions/auth.py +11 -1
  87. arize/exceptions/base.py +29 -4
  88. arize/exceptions/models.py +21 -2
  89. arize/exceptions/parameters.py +31 -0
  90. arize/exceptions/spaces.py +12 -1
  91. arize/exceptions/types.py +86 -7
  92. arize/exceptions/values.py +220 -20
  93. arize/experiments/__init__.py +1 -0
  94. arize/experiments/client.py +389 -285
  95. arize/experiments/evaluators/__init__.py +1 -0
  96. arize/experiments/evaluators/base.py +74 -41
  97. arize/experiments/evaluators/exceptions.py +6 -3
  98. arize/experiments/evaluators/executors.py +121 -73
  99. arize/experiments/evaluators/rate_limiters.py +106 -57
  100. arize/experiments/evaluators/types.py +34 -7
  101. arize/experiments/evaluators/utils.py +65 -27
  102. arize/experiments/functions.py +103 -101
  103. arize/experiments/tracing.py +52 -44
  104. arize/experiments/types.py +56 -31
  105. arize/logging.py +54 -22
  106. arize/models/__init__.py +1 -0
  107. arize/models/batch_validation/__init__.py +1 -0
  108. arize/models/batch_validation/errors.py +543 -65
  109. arize/models/batch_validation/validator.py +339 -300
  110. arize/models/bounded_executor.py +20 -7
  111. arize/models/casting.py +75 -29
  112. arize/models/client.py +326 -107
  113. arize/models/proto.py +95 -40
  114. arize/models/stream_validation.py +42 -14
  115. arize/models/surrogate_explainer/__init__.py +1 -0
  116. arize/models/surrogate_explainer/mimic.py +24 -13
  117. arize/pre_releases.py +43 -0
  118. arize/projects/__init__.py +1 -0
  119. arize/projects/client.py +129 -0
  120. arize/regions.py +40 -0
  121. arize/spans/__init__.py +1 -0
  122. arize/spans/client.py +130 -106
  123. arize/spans/columns.py +13 -0
  124. arize/spans/conversion.py +54 -38
  125. arize/spans/validation/__init__.py +1 -0
  126. arize/spans/validation/annotations/__init__.py +1 -0
  127. arize/spans/validation/annotations/annotations_validation.py +6 -4
  128. arize/spans/validation/annotations/dataframe_form_validation.py +13 -11
  129. arize/spans/validation/annotations/value_validation.py +35 -11
  130. arize/spans/validation/common/__init__.py +1 -0
  131. arize/spans/validation/common/argument_validation.py +33 -8
  132. arize/spans/validation/common/dataframe_form_validation.py +35 -9
  133. arize/spans/validation/common/errors.py +211 -11
  134. arize/spans/validation/common/value_validation.py +80 -13
  135. arize/spans/validation/evals/__init__.py +1 -0
  136. arize/spans/validation/evals/dataframe_form_validation.py +28 -8
  137. arize/spans/validation/evals/evals_validation.py +34 -4
  138. arize/spans/validation/evals/value_validation.py +26 -3
  139. arize/spans/validation/metadata/__init__.py +1 -1
  140. arize/spans/validation/metadata/argument_validation.py +14 -5
  141. arize/spans/validation/metadata/dataframe_form_validation.py +26 -10
  142. arize/spans/validation/metadata/value_validation.py +24 -10
  143. arize/spans/validation/spans/__init__.py +1 -0
  144. arize/spans/validation/spans/dataframe_form_validation.py +34 -13
  145. arize/spans/validation/spans/spans_validation.py +35 -4
  146. arize/spans/validation/spans/value_validation.py +76 -7
  147. arize/types.py +293 -157
  148. arize/utils/__init__.py +1 -0
  149. arize/utils/arrow.py +31 -15
  150. arize/utils/cache.py +34 -6
  151. arize/utils/dataframe.py +19 -2
  152. arize/utils/online_tasks/__init__.py +2 -0
  153. arize/utils/online_tasks/dataframe_preprocessor.py +53 -41
  154. arize/utils/openinference_conversion.py +44 -5
  155. arize/utils/proto.py +10 -0
  156. arize/utils/size.py +5 -3
  157. arize/version.py +3 -1
  158. {arize-8.0.0a22.dist-info → arize-8.0.0a23.dist-info}/METADATA +4 -3
  159. arize-8.0.0a23.dist-info/RECORD +174 -0
  160. {arize-8.0.0a22.dist-info → arize-8.0.0a23.dist-info}/WHEEL +1 -1
  161. arize-8.0.0a23.dist-info/licenses/LICENSE +176 -0
  162. arize-8.0.0a23.dist-info/licenses/NOTICE +13 -0
  163. arize/_generated/protocol/flight/export_pb2.py +0 -61
  164. arize/_generated/protocol/flight/ingest_pb2.py +0 -365
  165. arize-8.0.0a22.dist-info/RECORD +0 -146
  166. arize-8.0.0a22.dist-info/licenses/LICENSE.md +0 -12
@@ -1,18 +1,16 @@
1
+ """Type definitions and data models for experiments."""
2
+
1
3
  from __future__ import annotations
2
4
 
3
5
  import json
4
6
  import textwrap
7
+ from collections.abc import Awaitable, Callable, Iterable, Mapping
5
8
  from copy import copy, deepcopy
6
9
  from dataclasses import dataclass, field
7
10
  from datetime import datetime, timezone
8
11
  from importlib.metadata import version
9
12
  from random import getrandbits
10
13
  from typing import (
11
- Any,
12
- Awaitable,
13
- Callable,
14
- Iterable,
15
- Mapping,
16
14
  cast,
17
15
  )
18
16
 
@@ -35,8 +33,8 @@ TraceId = str
35
33
 
36
34
  @dataclass(frozen=True)
37
35
  class Example:
38
- """
39
- Represents an example in an experiment dataset.
36
+ """Represents an example in an experiment dataset.
37
+
40
38
  Args:
41
39
  id: The unique identifier for the example.
42
40
  updated_at: The timestamp when the example was last updated.
@@ -54,6 +52,7 @@ class Example:
54
52
  dataset_row: Mapping[str, JSONSerializable] = field(default_factory=dict)
55
53
 
56
54
  def __post_init__(self) -> None:
55
+ """Initialize example fields from dataset_row if provided."""
57
56
  if self.dataset_row is not None:
58
57
  object.__setattr__(
59
58
  self, "dataset_row", _make_read_only(self.dataset_row)
@@ -90,7 +89,8 @@ class Example:
90
89
  object.__setattr__(self, "metadata", self.metadata)
91
90
 
92
91
  @classmethod
93
- def from_dict(cls, obj: Mapping[str, Any]) -> Example:
92
+ def from_dict(cls, obj: Mapping[str, object]) -> Example:
93
+ """Create an Example instance from a dictionary."""
94
94
  return cls(
95
95
  id=obj["id"],
96
96
  input=obj["input"],
@@ -100,6 +100,7 @@ class Example:
100
100
  )
101
101
 
102
102
  def __repr__(self) -> str:
103
+ """Return a formatted string representation of the example."""
103
104
  spaces = " " * 4
104
105
  name = self.__class__.__name__
105
106
  identifiers = [f'{spaces}id="{self.id}",']
@@ -123,7 +124,9 @@ class Example:
123
124
  return "\n".join([f"{name}(", *identifiers, *contents, ")"])
124
125
 
125
126
 
126
- def _shorten(obj: Any, width: int = 50) -> Any:
127
+ def _shorten(
128
+ obj: dict[str, object] | list[object] | str | object, width: int = 50
129
+ ) -> dict[str, object] | list[object] | str | object:
127
130
  if isinstance(obj, str):
128
131
  return textwrap.shorten(obj, width=width, placeholder="...")
129
132
  if isinstance(obj, dict):
@@ -135,7 +138,9 @@ def _shorten(obj: Any, width: int = 50) -> Any:
135
138
  return obj
136
139
 
137
140
 
138
- def _make_read_only(obj: Any) -> Any:
141
+ def _make_read_only(
142
+ obj: dict[str, object] | list[object] | str | object,
143
+ ) -> dict[str, object] | list[object] | str | object:
139
144
  if isinstance(obj, dict):
140
145
  return _ReadOnly({k: _make_read_only(v) for k, v in obj.items()})
141
146
  if isinstance(obj, str):
@@ -146,25 +151,25 @@ def _make_read_only(obj: Any) -> Any:
146
151
 
147
152
 
148
153
  class _ReadOnly(ObjectProxy): # type: ignore[misc]
149
- def __setitem__(self, *args: Any, **kwargs: Any) -> Any:
154
+ def __setitem__(self, *args: object, **kwargs: object) -> object:
150
155
  raise NotImplementedError
151
156
 
152
- def __delitem__(self, *args: Any, **kwargs: Any) -> Any:
157
+ def __delitem__(self, *args: object, **kwargs: object) -> object:
153
158
  raise NotImplementedError
154
159
 
155
- def __iadd__(self, *args: Any, **kwargs: Any) -> Any:
160
+ def __iadd__(self, *args: object, **kwargs: object) -> object:
156
161
  raise NotImplementedError
157
162
 
158
- def pop(self, *args: Any, **kwargs: Any) -> Any:
163
+ def pop(self, *args: object, **kwargs: object) -> object:
159
164
  raise NotImplementedError
160
165
 
161
- def append(self, *args: Any, **kwargs: Any) -> Any:
166
+ def append(self, *args: object, **kwargs: object) -> object:
162
167
  raise NotImplementedError
163
168
 
164
- def __copy__(self, *args: Any, **kwargs: Any) -> Any:
169
+ def __copy__(self, *args: object, **kwargs: object) -> object:
165
170
  return copy(self.__wrapped__)
166
171
 
167
- def __deepcopy__(self, *args: Any, **kwargs: Any) -> Any:
172
+ def __deepcopy__(self, *args: object, **kwargs: object) -> object:
168
173
  return deepcopy(self.__wrapped__)
169
174
 
170
175
  def __repr__(self) -> str:
@@ -180,6 +185,8 @@ def _blue(text: str) -> str:
180
185
 
181
186
  @dataclass(frozen=True)
182
187
  class TestCase:
188
+ """Container for an experiment test case with example data and repetition number."""
189
+
183
190
  example: Example
184
191
  repetition_number: RepetitionNumber
185
192
 
@@ -194,8 +201,8 @@ def _exp_id() -> str:
194
201
 
195
202
  @dataclass(frozen=True)
196
203
  class ExperimentRun:
197
- """
198
- Represents a single run of an experiment.
204
+ """Represents a single run of an experiment.
205
+
199
206
  Args:
200
207
  start_time: The start time of the experiment run.
201
208
  end_time: The end time of the experiment run.
@@ -219,7 +226,8 @@ class ExperimentRun:
219
226
  trace_id: TraceId | None = None
220
227
 
221
228
  @classmethod
222
- def from_dict(cls, obj: Mapping[str, Any]) -> ExperimentRun:
229
+ def from_dict(cls, obj: Mapping[str, object]) -> ExperimentRun:
230
+ """Create an ExperimentRun instance from a dictionary."""
223
231
  return cls(
224
232
  start_time=obj["start_time"],
225
233
  end_time=obj["end_time"],
@@ -233,6 +241,11 @@ class ExperimentRun:
233
241
  )
234
242
 
235
243
  def __post_init__(self) -> None:
244
+ """Validate that exactly one of output or error is specified.
245
+
246
+ Raises:
247
+ ValueError: If both or neither output and error are specified.
248
+ """
236
249
  if (self.output is None) == (self.error is None):
237
250
  raise ValueError(
238
251
  "Must specify exactly one of experiment_run_output or error"
@@ -241,8 +254,8 @@ class ExperimentRun:
241
254
 
242
255
  @dataclass(frozen=True)
243
256
  class ExperimentEvaluationRun:
244
- """
245
- Represents a single evaluation run of an experiment.
257
+ """Represents a single evaluation run of an experiment.
258
+
246
259
  Args:
247
260
  experiment_run_id: The unique identifier for the experiment run.
248
261
  start_time: The start time of the evaluation run.
@@ -266,7 +279,8 @@ class ExperimentEvaluationRun:
266
279
  trace_id: TraceId | None = None
267
280
 
268
281
  @classmethod
269
- def from_dict(cls, obj: Mapping[str, Any]) -> ExperimentEvaluationRun:
282
+ def from_dict(cls, obj: Mapping[str, object]) -> ExperimentEvaluationRun:
283
+ """Create an ExperimentEvaluationRun instance from a dictionary."""
270
284
  return cls(
271
285
  experiment_run_id=obj["experiment_run_id"],
272
286
  start_time=obj["start_time"],
@@ -280,6 +294,11 @@ class ExperimentEvaluationRun:
280
294
  )
281
295
 
282
296
  def __post_init__(self) -> None:
297
+ """Validate that exactly one of result or error is specified.
298
+
299
+ Raises:
300
+ ValueError: If both or neither result and error are specified.
301
+ """
283
302
  if bool(self.result) == bool(self.error):
284
303
  raise ValueError("Must specify either result or error")
285
304
 
@@ -288,6 +307,11 @@ _LOCAL_TIMEZONE = datetime.now(timezone.utc).astimezone().tzinfo
288
307
 
289
308
 
290
309
  def local_now() -> datetime:
310
+ """Get the current datetime in the local timezone.
311
+
312
+ Returns:
313
+ A datetime object representing the current time in local timezone.
314
+ """
291
315
  return datetime.now(timezone.utc).astimezone(tz=_LOCAL_TIMEZONE)
292
316
 
293
317
 
@@ -303,10 +327,12 @@ class _HasStats:
303
327
 
304
328
  def __str__(self) -> str:
305
329
  try:
306
- assert int(version("pandas").split(".")[0]) >= 1
330
+ pandas_major = int(version("pandas").split(".")[0])
331
+ if pandas_major < 1:
332
+ raise ImportError("Pandas version < 1.0") # noqa: TRY301
307
333
  # `tabulate` is used by pandas >= 1.0 in DataFrame.to_markdown()
308
334
  import tabulate # noqa: F401
309
- except (AssertionError, ImportError):
335
+ except ImportError:
310
336
  text = self.stats.__str__()
311
337
  else:
312
338
  text = self.stats.to_markdown(index=False)
@@ -315,8 +341,7 @@ class _HasStats:
315
341
 
316
342
  @dataclass(frozen=True)
317
343
  class _TaskSummary(_HasStats):
318
- """
319
- Summary statistics of experiment task executions.
344
+ """Summary statistics of experiment task executions.
320
345
 
321
346
  **Users should not instantiate this object directly.**
322
347
  """
@@ -344,7 +369,7 @@ class _TaskSummary(_HasStats):
344
369
  "n_runs": n_runs,
345
370
  "n_errors": n_errors,
346
371
  **(
347
- dict(top_error=_top_string(df.loc[:, "error"]))
372
+ {"top_error": _top_string(df.loc[:, "error"])}
348
373
  if n_errors
349
374
  else {}
350
375
  ),
@@ -355,12 +380,12 @@ class _TaskSummary(_HasStats):
355
380
  return summary
356
381
 
357
382
  @classmethod
358
- def __new__(cls, *args: Any, **kwargs: Any) -> Any:
383
+ def __new__(cls, *args: object, **kwargs: object) -> object:
359
384
  # Direct instantiation by users is discouraged.
360
385
  raise NotImplementedError
361
386
 
362
387
  @classmethod
363
- def __init_subclass__(cls, **kwargs: Any) -> None:
388
+ def __init_subclass__(cls, **kwargs: object) -> None:
364
389
  # Direct sub-classing by users is discouraged.
365
390
  raise NotImplementedError
366
391
 
@@ -368,7 +393,7 @@ class _TaskSummary(_HasStats):
368
393
  def _top_string(s: pd.Series, length: int = 100) -> str | None:
369
394
  if (cnt := s.dropna().str.slice(0, length).value_counts()).empty:
370
395
  return None
371
- return cast(str, cnt.sort_values(ascending=False).index[0])
396
+ return cast("str", cnt.sort_values(ascending=False).index[0])
372
397
 
373
398
 
374
399
  @dataclass
arize/logging.py CHANGED
@@ -1,10 +1,13 @@
1
+ """Logging utilities and configuration for the Arize SDK."""
2
+
1
3
  from __future__ import annotations
2
4
 
3
5
  import json
4
6
  import logging
5
7
  import os
6
8
  import sys
7
- from typing import Any, Dict, Iterable, Mapping
9
+ from collections.abc import Iterable, Mapping
10
+ from typing import Any, ClassVar
8
11
 
9
12
  from arize.config import _parse_bool
10
13
  from arize.constants.config import (
@@ -30,7 +33,10 @@ _LEVEL_MAP = {
30
33
  class CtxAdapter(logging.LoggerAdapter):
31
34
  """LoggerAdapter that merges bound context with per-call extras safely."""
32
35
 
33
- def process(self, msg, kwargs):
36
+ def process(
37
+ self, msg: object, kwargs: dict[str, object]
38
+ ) -> tuple[object, dict[str, object]]:
39
+ """Process the logging call by merging bound and call extras."""
34
40
  call_extra = _coerce_mapping(kwargs.pop("extra", None))
35
41
  bound_extra = _coerce_mapping(self.extra)
36
42
  merged = (
@@ -42,7 +48,7 @@ class CtxAdapter(logging.LoggerAdapter):
42
48
  kwargs["extra"] = merged
43
49
  return msg, kwargs
44
50
 
45
- def with_extra(self, **more) -> CtxAdapter:
51
+ def with_extra(self, **more: object) -> CtxAdapter:
46
52
  """Return a copy of this adapter with additional bound extras."""
47
53
  base = _coerce_mapping(self.extra)
48
54
  base.update(_coerce_mapping(more))
@@ -54,12 +60,7 @@ class CtxAdapter(logging.LoggerAdapter):
54
60
 
55
61
 
56
62
  class CustomLogFormatter(logging.Formatter):
57
- grey = "\x1b[38;21m"
58
- blue = "\x1b[38;5;39m"
59
- yellow = "\x1b[33m"
60
- red = "\x1b[38;5;196m"
61
- bold_red = "\x1b[31;1m"
62
- reset = "\x1b[0m"
63
+ """Custom log formatter with color-coded output based on log level."""
63
64
 
64
65
  GREY = "\x1b[38;21m"
65
66
  BLUE = "\x1b[38;5;39m"
@@ -68,7 +69,7 @@ class CustomLogFormatter(logging.Formatter):
68
69
  BOLD_RED = "\x1b[31;1m"
69
70
  RESET = "\x1b[0m"
70
71
 
71
- COLORS = {
72
+ COLORS: ClassVar[dict[int, str]] = {
72
73
  logging.DEBUG: BLUE,
73
74
  logging.INFO: GREY,
74
75
  logging.WARNING: YELLOW,
@@ -76,10 +77,16 @@ class CustomLogFormatter(logging.Formatter):
76
77
  logging.CRITICAL: BOLD_RED,
77
78
  }
78
79
 
79
- def __init__(self, fmt: str):
80
+ def __init__(self, fmt: str) -> None:
81
+ """Initialize the colored formatter with a format string.
82
+
83
+ Args:
84
+ fmt: Format string for log messages.
85
+ """
80
86
  super().__init__(fmt=fmt)
81
87
 
82
88
  def format(self, record: logging.LogRecord) -> str:
89
+ """Format the log record with color based on log level."""
83
90
  # Build the base message without any color.
84
91
  base = super().format(record)
85
92
 
@@ -106,7 +113,7 @@ class JsonFormatter(logging.Formatter):
106
113
  """Minimal JSON formatter (one JSON object per line)."""
107
114
 
108
115
  # fields to skip copying from record.__dict__
109
- _skip = {
116
+ _skip: ClassVar[set[str]] = {
110
117
  # "name",
111
118
  # "msg",
112
119
  # "args",
@@ -130,7 +137,8 @@ class JsonFormatter(logging.Formatter):
130
137
  }
131
138
 
132
139
  def format(self, record: logging.LogRecord) -> str:
133
- payload: Dict[str, Any] = {
140
+ """Format the log record as a JSON string."""
141
+ payload: dict[str, object] = {
134
142
  # "time": self.formatTime(record, datefmt="%Y-%m-%dT%H:%M:%S%z"),
135
143
  # "logger": record.name,
136
144
  # "level": record.levelname,
@@ -156,12 +164,11 @@ def _parse_level(val: str | None, default: int = logging.INFO) -> int:
156
164
 
157
165
 
158
166
  def auto_configure_from_env() -> None:
159
- """
160
- If ARIZE_LOG is truthy, configure logging for 'arize' once,
161
- using ARIZE_LOG_LEVEL and ARIZE_LOG_STRUCTURED if provided.
167
+ """If ARIZE_LOG is truthy, configure logging for 'arize' once.
168
+
169
+ Using ARIZE_LOG_LEVEL and ARIZE_LOG_STRUCTURED if provided.
162
170
  Otherwise, do nothing (library stays quiet with NullHandler).
163
171
  """
164
-
165
172
  if not _parse_bool(os.getenv(ENV_LOG_ENABLE, DEFAULT_LOG_ENABLE)):
166
173
  return
167
174
 
@@ -201,7 +208,16 @@ _STANDARD_RECORD_KEYS = {
201
208
  }
202
209
 
203
210
 
204
- def get_truncation_warning_message(instance, limit) -> str:
211
+ def get_truncation_warning_message(instance: str, limit: int) -> str:
212
+ """Generate a warning message for data that exceeds character limits.
213
+
214
+ Args:
215
+ instance: The data instance type that may be truncated.
216
+ limit: The character limit threshold.
217
+
218
+ Returns:
219
+ A formatted warning message string.
220
+ """
205
221
  return (
206
222
  f"Attention: {instance} exceeding the {limit} character limit will be "
207
223
  "automatically truncated upon ingestion into the Arize platform. Should you require "
@@ -213,8 +229,7 @@ def configure_logging(
213
229
  level: int = logging.INFO,
214
230
  structured: bool = False,
215
231
  ) -> None:
216
- """
217
- Configure logging for the 'arize' logger.
232
+ """Configure logging for the 'arize' logger.
218
233
 
219
234
  Args:
220
235
  level: logging level (e.g., logging.INFO, logging.DEBUG)
@@ -241,6 +256,15 @@ def configure_logging(
241
256
 
242
257
 
243
258
  def log_a_list(values: Iterable[Any] | None, join_word: str) -> str:
259
+ """Format a list of values into a human-readable string with a joining word.
260
+
261
+ Args:
262
+ values: An iterable of values to format, or None.
263
+ join_word: The word to use before the last item (e.g., "and", "or").
264
+
265
+ Returns:
266
+ A formatted string joining the values, or empty string if no values.
267
+ """
244
268
  if values is None:
245
269
  return ""
246
270
  list_of_str = list(values)
@@ -253,13 +277,21 @@ def log_a_list(values: Iterable[Any] | None, join_word: str) -> str:
253
277
  )
254
278
 
255
279
 
256
- def get_arize_project_url(response: Any):
280
+ def get_arize_project_url(response: object) -> str:
281
+ """Extract the Arize project URL from an API response.
282
+
283
+ Args:
284
+ response: The HTTP response object from an Arize API call.
285
+
286
+ Returns:
287
+ The real-time ingestion URI if present, otherwise an empty string.
288
+ """
257
289
  if "realTimeIngestionUri" in json.loads(response.content.decode()):
258
290
  return json.loads(response.content.decode())["realTimeIngestionUri"]
259
291
  return ""
260
292
 
261
293
 
262
- def _coerce_mapping(obj: Any) -> Dict[str, Any]:
294
+ def _coerce_mapping(obj: object) -> dict[str, object]:
263
295
  """Return a shallow dict copy if obj is a Mapping[str, Any], else {}."""
264
296
  if isinstance(obj, Mapping):
265
297
  # force keys to str to satisfy logging's expectation
arize/models/__init__.py CHANGED
@@ -0,0 +1 @@
1
+ """Model logging and validation functionality for the Arize SDK."""
@@ -0,0 +1 @@
1
+ """Batch validation utilities for model data."""