fiddler-evals 0.6.0rc1__tar.gz → 0.7.0.dev1__tar.gz

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 (109) hide show
  1. {fiddler_evals-0.6.0rc1/fiddler_evals.egg-info → fiddler_evals-0.7.0.dev1}/PKG-INFO +6 -3
  2. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/PUBLIC.md +1 -1
  3. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/README.md +1 -1
  4. fiddler_evals-0.7.0.dev1/fiddler_evals/VERSION +1 -0
  5. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/application.py +66 -21
  6. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/dataset.py +78 -17
  7. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/experiment.py +85 -7
  8. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/project.py +7 -0
  9. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/answer_relevance.py +4 -2
  10. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/base.py +20 -2
  11. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/coherence.py +4 -2
  12. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/conciseness.py +4 -2
  13. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/context_relevance.py +4 -2
  14. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/custom_judge.py +46 -30
  15. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/eval_fn.py +20 -9
  16. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/ftl_prompt_safety.py +4 -2
  17. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/ftl_response_faithfulness.py +4 -2
  18. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/rag_faithfulness.py +4 -2
  19. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/regex.py +3 -7
  20. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/sentiment.py +4 -2
  21. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/tests/test_ftl_prompt_safety.py +1 -0
  22. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/topic.py +4 -2
  23. fiddler_evals-0.7.0.dev1/fiddler_evals/preview/__init__.py +25 -0
  24. fiddler_evals-0.7.0.dev1/fiddler_evals/preview/centor_safety_dme.py +129 -0
  25. fiddler_evals-0.7.0.dev1/fiddler_evals/preview/tests/test_centor_safety_dme.py +242 -0
  26. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/pydantic_models/dataset.py +37 -0
  27. fiddler_evals-0.7.0.dev1/fiddler_evals/pydantic_models/score.py +62 -0
  28. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/pydantic_models/span_fields.py +64 -0
  29. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/runner/evaluation.py +19 -3
  30. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/runner/session_capture.py +28 -5
  31. fiddler_evals-0.7.0.dev1/fiddler_evals/utils/tests/__init__.py +0 -0
  32. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1/fiddler_evals.egg-info}/PKG-INFO +6 -3
  33. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals.egg-info/SOURCES.txt +4 -0
  34. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/pyproject.toml +5 -2
  35. fiddler_evals-0.6.0rc1/fiddler_evals/VERSION +0 -1
  36. fiddler_evals-0.6.0rc1/fiddler_evals/pydantic_models/score.py +0 -26
  37. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/MANIFEST.in +0 -0
  38. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/__init__.py +0 -0
  39. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/configs.py +0 -0
  40. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/conftest.py +0 -0
  41. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/connection.py +0 -0
  42. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/constants.py +0 -0
  43. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/decorators.py +0 -0
  44. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/__init__.py +0 -0
  45. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/base.py +0 -0
  46. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/tests/__init__.py +0 -0
  47. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/tests/test_application.py +0 -0
  48. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/tests/test_dataset.py +0 -0
  49. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/tests/test_dataset_items.py +0 -0
  50. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/tests/test_experiment.py +0 -0
  51. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/tests/test_experiment_items.py +0 -0
  52. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/tests/test_experiment_results.py +0 -0
  53. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/entities/tests/test_project.py +0 -0
  54. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/__init__.py +0 -0
  55. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/tests/__init__.py +0 -0
  56. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/tests/test_answer_relevance.py +0 -0
  57. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/tests/test_coherence.py +0 -0
  58. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/tests/test_conciseness.py +0 -0
  59. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/tests/test_context_relevance.py +0 -0
  60. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/tests/test_custom_judge.py +0 -0
  61. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/tests/test_eval_fn.py +0 -0
  62. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/tests/test_ftl_response_faithfulness.py +0 -0
  63. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/tests/test_rag_faithfulness.py +0 -0
  64. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/tests/test_regex.py +0 -0
  65. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/tests/test_sentiment.py +0 -0
  66. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/evaluators/tests/test_topic_classification.py +0 -0
  67. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/exceptions.py +0 -0
  68. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/libs/__init__.py +0 -0
  69. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/libs/http_client.py +0 -0
  70. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/libs/json_encoder.py +0 -0
  71. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/libs/semver.py +0 -0
  72. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/libs/tests/__init__.py +0 -0
  73. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/libs/tests/test_json_encoder.py +0 -0
  74. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/libs/tests/test_request_client.py +0 -0
  75. {fiddler_evals-0.6.0rc1/fiddler_evals/runner → fiddler_evals-0.7.0.dev1/fiddler_evals/preview/tests}/__init__.py +0 -0
  76. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/pydantic_models/__init__.py +0 -0
  77. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/pydantic_models/application.py +0 -0
  78. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/pydantic_models/base.py +0 -0
  79. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/pydantic_models/compact.py +0 -0
  80. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/pydantic_models/error.py +0 -0
  81. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/pydantic_models/evaluator.py +0 -0
  82. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/pydantic_models/experiment.py +0 -0
  83. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/pydantic_models/filter_query.py +0 -0
  84. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/pydantic_models/project.py +0 -0
  85. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/pydantic_models/response.py +0 -0
  86. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/pydantic_models/server_info.py +0 -0
  87. {fiddler_evals-0.6.0rc1/fiddler_evals/runner/tests → fiddler_evals-0.7.0.dev1/fiddler_evals/runner}/__init__.py +0 -0
  88. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/runner/executor.py +0 -0
  89. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/runner/experiment_result_publisher.py +0 -0
  90. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/runner/experiment_runner.py +0 -0
  91. {fiddler_evals-0.6.0rc1/fiddler_evals → fiddler_evals-0.7.0.dev1/fiddler_evals/runner}/tests/__init__.py +0 -0
  92. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/runner/tests/test_evaluate.py +0 -0
  93. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/runner/tests/test_experiment_result_publisher.py +0 -0
  94. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/runner/tests/test_runner_session.py +0 -0
  95. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/runner/tests/test_session_capture.py +0 -0
  96. {fiddler_evals-0.6.0rc1/fiddler_evals/utils → fiddler_evals-0.7.0.dev1/fiddler_evals/tests}/__init__.py +0 -0
  97. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/tests/constants.py +0 -0
  98. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/tests/test_connection.py +0 -0
  99. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/tests/test_decorators.py +0 -0
  100. {fiddler_evals-0.6.0rc1/fiddler_evals/utils/tests → fiddler_evals-0.7.0.dev1/fiddler_evals/utils}/__init__.py +0 -0
  101. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/utils/environment.py +0 -0
  102. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/utils/pd.py +0 -0
  103. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/utils/tests/test_environment.py +0 -0
  104. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/utils/tqdm.py +0 -0
  105. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals/version.py +0 -0
  106. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals.egg-info/dependency_links.txt +0 -0
  107. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals.egg-info/requires.txt +0 -0
  108. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/fiddler_evals.egg-info/top_level.txt +0 -0
  109. {fiddler_evals-0.6.0rc1 → fiddler_evals-0.7.0.dev1}/setup.cfg +0 -0
@@ -1,14 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fiddler-evals
3
- Version: 0.6.0rc1
3
+ Version: 0.7.0.dev1
4
4
  Summary: Python SDK for evaluating LLM Applications
5
5
  Author-email: Fiddler AI <support@fiddler.ai>
6
6
  Maintainer-email: Fiddler AI <support@fiddler.ai>
7
7
  Project-URL: Homepage, https://fiddler.ai
8
8
  Project-URL: Documentation, https://docs.fiddler.ai/
9
9
  Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
10
13
  Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.10
14
+ Requires-Python: >=3.11
12
15
  Description-Content-Type: text/markdown
13
16
  Requires-Dist: pip>=21.0
14
17
  Requires-Dist: requests<3
@@ -36,7 +39,7 @@ A comprehensive toolkit for evaluating Large Language Model (LLM) applications,
36
39
 
37
40
  ## Requirements
38
41
 
39
- - Python 3.10 or higher
42
+ - Python 3.11 or higher
40
43
  - Access to a Fiddler Platform instance
41
44
  - API token from Fiddler Platform
42
45
 
@@ -14,7 +14,7 @@ A comprehensive toolkit for evaluating Large Language Model (LLM) applications,
14
14
 
15
15
  ## Requirements
16
16
 
17
- - Python 3.10 or higher
17
+ - Python 3.11 or higher
18
18
  - Access to a Fiddler Platform instance
19
19
  - API token from Fiddler Platform
20
20
 
@@ -14,7 +14,7 @@ The Fiddler Evals SDK is a comprehensive toolkit for evaluating Large Language M
14
14
 
15
15
  ### Prerequisites
16
16
 
17
- - Python 3.10 or higher
17
+ - Python 3.11 or higher
18
18
  - Access to a Fiddler platform instance
19
19
 
20
20
  ### Installation
@@ -0,0 +1 @@
1
+ 0.7.0.dev1
@@ -89,12 +89,27 @@ class Application(BaseEntity):
89
89
  """
90
90
 
91
91
  id: UUID
92
+ """Unique identifier (UUID) of the application."""
93
+
92
94
  name: str
95
+ """Application name; unique within the project and immutable after creation."""
96
+
93
97
  created_at: datetime
98
+ """Server-side timestamp of when the application was created."""
99
+
94
100
  updated_at: datetime
101
+ """Server-side timestamp of when the application was last updated."""
102
+
95
103
  created_by: UserCompact
104
+ """Compact reference (id, full name, email) to the user who created the
105
+ application."""
106
+
96
107
  updated_by: UserCompact
108
+ """Compact reference (id, full name, email) to the user who last updated the
109
+ application."""
110
+
97
111
  project: ProjectCompact
112
+ """Compact reference (id and name) to the project containing this application."""
98
113
 
99
114
  @staticmethod
100
115
  def _get_url(id_: UUID | str | None = None) -> str:
@@ -420,11 +435,6 @@ class Application(BaseEntity):
420
435
  spans in the given time range, with coverage counts. Useful for
421
436
  building field mappings before adding spans to a dataset.
422
437
 
423
- At most 10,000 matching spans are scanned. If more match, the
424
- response sets ``sampled=True`` and the counts describe a sample
425
- rather than the whole set — narrow ``filter``, ``search``, or the
426
- time range to get exact coverage.
427
-
428
438
  Args:
429
439
  start_time: Start of the time range, inclusive. ISO 8601
430
440
  string or a **timezone-aware** datetime. Naive datetimes
@@ -445,26 +455,61 @@ class Application(BaseEntity):
445
455
  :class:`~fiddler_evals.pydantic_models.span_fields.SpanFieldsResponse`:
446
456
  Span attributes and evaluator outputs with counts.
447
457
 
458
+ Raises:
459
+ ApiError: If there's an error communicating with the Fiddler API.
460
+
461
+ Note:
462
+ Discovery scans at most 10,000 matching spans. Above that it sets
463
+ ``sampled=True`` and the counts describe a sample rather than the
464
+ full set — narrow the time range or the filter when you need exact
465
+ coverage. Attribute keys are reported exactly as stored, so they
466
+ can be copied directly into a
467
+ :class:`~fiddler_evals.pydantic_models.span_fields.FieldMapping`
468
+ for :meth:`~fiddler_evals.entities.dataset.Dataset.add_items_from_spans`.
469
+
448
470
  Example:
449
471
  .. code-block:: python
450
472
 
451
- app = Application.get_by_id(app_id)
452
- fields = app.get_span_fields(
453
- start_time="2025-01-01T00:00:00Z",
454
- end_time="2025-01-02T00:00:00Z",
455
- filter=QueryCondition(
456
- rules=[
457
- QueryRule(
458
- field="Span::span_type",
459
- operator=OperatorType.EQUAL,
460
- value="llm",
461
- )
462
- ]
463
- ),
464
- search=SearchFilter(query="refund", scope=SearchScope.INPUT),
473
+ from datetime import datetime, timedelta, timezone
474
+
475
+ from fiddler_evals import (
476
+ Application,
477
+ OperatorType,
478
+ Project,
479
+ QueryCondition,
480
+ QueryRule,
481
+ )
482
+
483
+ # Get application instance
484
+ project = Project.get_by_name(name="fraud-detection-project")
485
+ application = Application.get_by_name(
486
+ name="fraud-detection-app",
487
+ project_id=project.id,
465
488
  )
466
- for attr in fields.span_attributes:
467
- print(f"{attr.key}: {attr.count}")
489
+
490
+ end_time = datetime.now(timezone.utc)
491
+ start_time = end_time - timedelta(days=7)
492
+
493
+ # Scope discovery to LLM spans only
494
+ llm_filter = QueryCondition(
495
+ rules=[
496
+ QueryRule(
497
+ field="Span::span_type",
498
+ operator=OperatorType.EQUAL,
499
+ value="llm",
500
+ ),
501
+ ]
502
+ )
503
+
504
+ fields = application.get_span_fields(
505
+ start_time=start_time,
506
+ end_time=end_time,
507
+ filter=llm_filter,
508
+ )
509
+
510
+ print(f"{fields.total_spans} spans scanned (sampled={fields.sampled})")
511
+ for attribute in sorted(fields.span_attributes, key=lambda a: -a.count):
512
+ print(f" {attribute.key}: {attribute.count}")
468
513
  """
469
514
  payload: dict = {
470
515
  "application_id": str(self.id),
@@ -115,16 +115,40 @@ class Dataset(BaseEntity):
115
115
  """
116
116
 
117
117
  id: UUID
118
+ """Unique identifier (UUID) of the dataset."""
119
+
118
120
  name: str
121
+ """Dataset name; unique within the application and immutable after creation."""
122
+
119
123
  created_at: datetime
124
+ """Server-side timestamp of when the dataset was created."""
125
+
120
126
  updated_at: datetime
127
+ """Server-side timestamp of when the dataset was last updated."""
128
+
121
129
  created_by: UserCompact
130
+ """Compact reference (id, full name, email) to the user who created the
131
+ dataset."""
132
+
122
133
  updated_by: UserCompact
134
+ """Compact reference (id, full name, email) to the user who last updated the
135
+ dataset."""
136
+
123
137
  project: ProjectCompact
138
+ """Compact reference (id and name) to the project containing this dataset."""
139
+
124
140
  application: ApplicationCompact
141
+ """Compact reference (id and name) to the application containing this dataset."""
142
+
125
143
  active: bool = True
144
+ """Whether the dataset is active; set at creation and changeable via
145
+ ``update()``. Defaults to ``True``."""
146
+
126
147
  description: str | None = None
148
+ """Optional human-readable description of the dataset; ``None`` if not set."""
149
+
127
150
  metadata: dict = field(default_factory=dict)
151
+ """Custom metadata dictionary attached to the dataset; empty when not set."""
128
152
 
129
153
  @staticmethod
130
154
  def _get_url(id_: UUID | str | None = None) -> str:
@@ -1289,21 +1313,36 @@ class Dataset(BaseEntity):
1289
1313
 
1290
1314
  Discovers what field keys exist in each JSON bucket (inputs,
1291
1315
  expected_outputs, metadata, extras) across the dataset's items,
1292
- with coverage counts and inferred data types.
1316
+ with coverage counts and inferred data types. Use it to keep a new
1317
+ :class:`~fiddler_evals.pydantic_models.span_fields.FieldMapping`
1318
+ consistent with the field names the dataset already uses.
1293
1319
 
1294
1320
  Returns:
1295
1321
  :class:`~fiddler_evals.pydantic_models.span_fields.DatasetSchemaResponse`:
1296
1322
  Item total, sampling flag, and field keys per bucket with
1297
1323
  counts and types.
1298
1324
 
1325
+ Raises:
1326
+ ApiError: If there's an error communicating with the Fiddler API.
1327
+
1328
+ Note:
1329
+ Experiment tasks bind to inputs by name, so reusing the existing
1330
+ field names keeps new items consistent and replayable alongside
1331
+ the items already in the dataset.
1332
+
1299
1333
  Example:
1300
1334
  .. code-block:: python
1301
1335
 
1302
- dataset = Dataset.get_by_id(dataset_id)
1336
+ # Get existing dataset
1337
+ dataset = Dataset.get_by_name(
1338
+ name="fraud-detection-tests", application_id=application_id
1339
+ )
1340
+
1341
+ # Inspect existing field names before adding more items
1303
1342
  schema = dataset.get_schema()
1304
1343
  print(f"{schema.total_items} items (sampled={schema.sampled})")
1305
1344
  for field in schema.inputs:
1306
- print(f"{field.key}: {field.data_type} ({field.count})")
1345
+ print(f" inputs.{field.key}: {field.data_type} ({field.count})")
1307
1346
  """
1308
1347
  response = self._client().get(
1309
1348
  url=f"{self._get_url(self.id)}/schema",
@@ -1339,7 +1378,8 @@ class Dataset(BaseEntity):
1339
1378
  rejected by the server; any UTC offset is accepted and
1340
1379
  normalized to UTC server-side.
1341
1380
  end_time: End of the time range, exclusive. ISO 8601 string or a
1342
- **timezone-aware** datetime, same rules as ``start_time``.
1381
+ **timezone-aware** datetime, same rules as ``start_time``. A
1382
+ referenced span outside the window is treated as missing.
1343
1383
 
1344
1384
  Returns:
1345
1385
  :class:`~fiddler_evals.pydantic_models.span_fields.AddItemsFromSpansResponse`:
@@ -1354,24 +1394,45 @@ class Dataset(BaseEntity):
1354
1394
  summary message. 400 if ``spans`` exceeds the per-request cap
1355
1395
  or the dataset would exceed its item limit (default 10,000).
1356
1396
 
1397
+ Note:
1398
+ A failed call writes nothing, so retrying after fixing the
1399
+ references cannot create duplicates. A successful call has no
1400
+ idempotency key, however — repeating it with the same spans
1401
+ creates duplicate dataset items. Mapped attribute keys are matched
1402
+ exactly against stored span attribute keys; discover them first
1403
+ with ``Application.get_span_fields()``.
1404
+
1357
1405
  Example:
1358
1406
  .. code-block:: python
1359
1407
 
1360
- dataset = Dataset.get_by_id(dataset_id)
1408
+ from fiddler_evals import FieldMapping, SpanReference
1409
+
1410
+ # Get existing dataset
1411
+ dataset = Dataset.get_by_name(
1412
+ name="fraud-detection-tests", application_id=application_id
1413
+ )
1414
+
1415
+ # Span references collected from POST /v3/spans/query
1416
+ span_refs = [
1417
+ SpanReference(trace_id=span["trace_id"], span_id=span["span_id"])
1418
+ for span in spans
1419
+ ]
1420
+
1421
+ # Map dataset fields to stored span attribute keys
1422
+ mapping = FieldMapping(
1423
+ inputs={"user_query": "gen_ai.llm.input.user"},
1424
+ expected_outputs={"expected_response": "gen_ai.llm.output"},
1425
+ metadata={"model": "gen_ai.request.model"},
1426
+ )
1427
+
1361
1428
  result = dataset.add_items_from_spans(
1362
- spans=[
1363
- {"trace_id": "abc123", "span_id": "def456"},
1364
- ],
1365
- mapping={
1366
- "inputs": {"question": "fiddler.span.user.query"},
1367
- "expected_outputs": {},
1368
- "metadata": {},
1369
- "extras": {},
1370
- },
1371
- start_time="2025-01-01T00:00:00Z",
1372
- end_time="2025-01-02T00:00:00Z",
1429
+ spans=span_refs,
1430
+ mapping=mapping,
1431
+ start_time=start_time,
1432
+ end_time=end_time,
1373
1433
  )
1374
- print(f"Created {result.items_created} items")
1434
+ print(f"Created {result.items_created} dataset items")
1435
+ print(f"Item IDs: {result.item_ids[:3]}")
1375
1436
  """
1376
1437
  # Coerce dicts through the models rather than forwarding them as-is, so
1377
1438
  # both input styles produce an identical request body and malformed
@@ -32,6 +32,8 @@ Note:
32
32
  lifecycle of evaluation runs from creation to completion or failure.
33
33
  """
34
34
 
35
+ # pylint: disable=too-many-lines
36
+ # (docstrings pushed this module past pylint's 1000-line cap; see PR)
35
37
  from __future__ import annotations
36
38
 
37
39
  import builtins
@@ -68,17 +70,54 @@ logger = logging.getLogger(__name__)
68
70
 
69
71
 
70
72
  class ExperimentStatus(str, enum.Enum):
73
+ """Lifecycle status of an experiment run.
74
+
75
+ An experiment is created with ``PENDING`` status, moves to
76
+ ``IN_PROGRESS`` when the runner starts processing dataset items, and
77
+ ends in ``COMPLETED``, ``FAILED``, or ``CANCELLED``.
78
+
79
+ A ``COMPLETED`` experiment does not imply that every item succeeded:
80
+ individual items can still be ``ExperimentItemStatus.FAILED``. Item
81
+ failures do not fail the experiment.
82
+ """
83
+
71
84
  PENDING = "PENDING"
85
+ """The experiment was created on the server; the run has not started yet."""
86
+
72
87
  IN_PROGRESS = "IN_PROGRESS"
88
+ """The runner is executing the task function and evaluators over the
89
+ dataset items."""
90
+
73
91
  COMPLETED = "COMPLETED"
92
+ """The run finished and results were published, even if some individual
93
+ items failed."""
94
+
74
95
  FAILED = "FAILED"
96
+ """The run aborted with an unrecoverable error, such as a task or scoring
97
+ function whose signature does not match the dataset item fields."""
98
+
75
99
  CANCELLED = "CANCELLED"
100
+ """The run was interrupted by the user or by system termination; results
101
+ already published are kept."""
76
102
 
77
103
 
78
104
  class ExperimentItemStatus(str, enum.Enum):
105
+ """Outcome of a single dataset item within an experiment run.
106
+
107
+ A ``SUCCESS`` item does not imply that every score succeeded: evaluator
108
+ errors are recorded per score (see ``ScoreStatus``), not on the item.
109
+ """
110
+
79
111
  SUCCESS = "SUCCESS"
112
+ """The task function returned outputs and all evaluators ran on this
113
+ item, even if some individual scores failed."""
114
+
80
115
  FAILED = "FAILED"
116
+ """The task function raised an exception on this item, so no evaluators
117
+ ran; ``error_reason`` and ``error_message`` are populated."""
118
+
81
119
  SKIPPED = "SKIPPED"
120
+ """The item was skipped and produced no result."""
82
121
 
83
122
 
84
123
  @dataclass
@@ -120,21 +159,60 @@ class Experiment(BaseEntity):
120
159
  """
121
160
 
122
161
  id: UUID
162
+ """Unique identifier (UUID) of the experiment."""
163
+
123
164
  name: str
165
+ """Experiment name; unique within the application and immutable after creation."""
166
+
124
167
  status: str
168
+ """Lifecycle status of the run; holds an ``ExperimentStatus`` value
169
+ (``PENDING``, ``IN_PROGRESS``, ``COMPLETED``, ``FAILED``, or ``CANCELLED``)."""
170
+
125
171
  created_at: datetime
172
+ """Server-side timestamp of when the experiment was created."""
173
+
126
174
  updated_at: datetime
175
+ """Server-side timestamp of when the experiment was last updated."""
176
+
127
177
  created_by: UserCompact
178
+ """Compact reference (id, full name, email) to the user who created the
179
+ experiment."""
180
+
128
181
  updated_by: UserCompact
182
+ """Compact reference (id, full name, email) to the user who last updated the
183
+ experiment."""
184
+
129
185
  project: ProjectCompact
186
+ """Compact reference (id and name) to the project containing this experiment."""
187
+
130
188
  application: ApplicationCompact
189
+ """Compact reference (id and name) to the application containing this
190
+ experiment."""
191
+
131
192
  dataset: DatasetCompact
193
+ """Compact reference (id and name) to the dataset the experiment ran against."""
194
+
132
195
  description: str | None = None
196
+ """Optional human-readable description of the experiment; ``None`` if not set."""
197
+
133
198
  error_reason: str | None = None
199
+ """Short reason for the failure; populated when the run fails or is canceled,
200
+ otherwise ``None``."""
201
+
134
202
  error_message: str | None = None
203
+ """Detailed error message for the failure; populated when the run fails or is
204
+ canceled, otherwise ``None``."""
205
+
135
206
  traceback: str | None = None
207
+ """Stack trace captured for the failure; populated when the run fails or is
208
+ canceled, otherwise ``None``."""
209
+
136
210
  duration_ms: int | None = None
211
+ """Total duration of the experiment run in milliseconds; set on completion,
212
+ otherwise ``None``."""
213
+
137
214
  metadata: dict = field(default_factory=dict)
215
+ """Custom metadata dictionary attached to the experiment; empty when not set."""
138
216
 
139
217
  @staticmethod
140
218
  def _get_url(id_: UUID | str | None = None) -> str:
@@ -548,7 +626,7 @@ class Experiment(BaseEntity):
548
626
  metadata: Optional new metadata dictionary for the experiment. If provided,
549
627
  replaces the existing metadata completely. Use empty dict to clear.
550
628
  status: Optional new status for the experiment. Can be used to update
551
- experiment status (e.g., PENDING, RUNNING, COMPLETED, FAILED).
629
+ experiment status (e.g., PENDING, IN_PROGRESS, COMPLETED, FAILED).
552
630
  error_reason: Required when status is FAILED. The reason for the experiment failure.
553
631
  error_message: Required when status is FAILED. Detailed error message for the failure.
554
632
  traceback: Required when status is FAILED. Stack trace information for debugging.
@@ -690,7 +768,7 @@ class Experiment(BaseEntity):
690
768
  - dataset_item_id: UUID of the dataset item being evaluated
691
769
  - outputs: Dictionary containing the outputs of the task function against dataset item
692
770
  - duration_ms: Duration of the execution in milliseconds:
693
- - status: Status of the outputs of the task function / scoring against dataset item (PENDING, COMPLETED, FAILED, etc.)
771
+ - status: Status of the outputs of the task function / scoring against dataset item (SUCCESS, FAILED, or SKIPPED)
694
772
  - error_reason: Reason for failure, if applicable
695
773
  - error_message: Detailed error message, if applicable
696
774
 
@@ -718,7 +796,7 @@ class Experiment(BaseEntity):
718
796
  outputs={"answer": "The watermelon seeds pass through your digestive system"},
719
797
  duration_ms=1000,
720
798
  end_time=datetime.now(tz=timezone.utc),
721
- status="COMPLETED",
799
+ status="SUCCESS",
722
800
  error_reason=None,
723
801
  error_message=None
724
802
  ),
@@ -727,7 +805,7 @@ class Experiment(BaseEntity):
727
805
  outputs={"answer": "The precise origin of fortune cookies is unclear"},
728
806
  duration_ms=1000,
729
807
  end_time=datetime.now(tz=timezone.utc),
730
- status="COMPLETED",
808
+ status="SUCCESS",
731
809
  error_reason=None,
732
810
  error_message=None
733
811
  )
@@ -745,7 +823,7 @@ class Experiment(BaseEntity):
745
823
  "outputs": {"answer": result["answer"]},
746
824
  "duration_ms": result["duration_ms"],
747
825
  "end_time": result["end_time"],
748
- "status": "COMPLETED"
826
+ "status": "SUCCESS"
749
827
  }
750
828
  for result in items
751
829
  ]
@@ -818,7 +896,7 @@ class Experiment(BaseEntity):
818
896
  # Filter items by status
819
897
  completed_items = [
820
898
  item for item in experiment.get_items()
821
- if item.status == "COMPLETED"
899
+ if item.status == "SUCCESS"
822
900
  ]
823
901
  print(f"Completed items: {len(completed_items)}")
824
902
 
@@ -892,7 +970,7 @@ class Experiment(BaseEntity):
892
970
  outputs={"prediction": "fraud", "confidence": 0.95},
893
971
  duration_ms=1000,
894
972
  end_time=datetime.now(tz=timezone.utc),
895
- status="COMPLETED"
973
+ status="SUCCESS"
896
974
  )
897
975
 
898
976
  # Create scores from evaluators
@@ -89,9 +89,16 @@ class Project(BaseEntity):
89
89
  """
90
90
 
91
91
  id: UUID
92
+ """Unique identifier (UUID) of the project."""
93
+
92
94
  name: str
95
+ """Project name; unique within the organization and immutable after creation."""
96
+
93
97
  created_at: datetime
98
+ """Server-side timestamp of when the project was created."""
99
+
94
100
  updated_at: datetime
101
+ """Server-side timestamp of when the project was last updated."""
95
102
 
96
103
  @staticmethod
97
104
  def _get_url(id_: UUID | str | None = None) -> str:
@@ -37,7 +37,7 @@ class AnswerRelevance(FiddlerLLMAAJEvaluator):
37
37
  retrieved_documents (list[str], optional): Reference documents for context.
38
38
 
39
39
  Returns:
40
- Score: A Score object containing:
40
+ Score: A :class:`~fiddler_evals.pydantic_models.score.Score` object containing:
41
41
  - value: 1.0 for high, 0.5 for medium, 0.0 for low relevance
42
42
  - label: "high", "medium", or "low"
43
43
  - reasoning: Detailed explanation of the assessment
@@ -72,6 +72,8 @@ class AnswerRelevance(FiddlerLLMAAJEvaluator):
72
72
  """
73
73
 
74
74
  name = "answer_relevance"
75
+ """Evaluator identifier, recorded as ``evaluator_name`` on every
76
+ ``Score`` this evaluator produces."""
75
77
 
76
78
  def score( # pylint: disable=arguments-differ
77
79
  self,
@@ -87,7 +89,7 @@ class AnswerRelevance(FiddlerLLMAAJEvaluator):
87
89
  retrieved_documents (list[str], optional): Reference documents for context.
88
90
 
89
91
  Returns:
90
- Score: A Score object containing:
92
+ Score: A :class:`~fiddler_evals.pydantic_models.score.Score` object containing:
91
93
  - value: 1.0 for high, 0.5 for medium, 0.0 for low relevance
92
94
  - label: "high", "medium", or "low"
93
95
  - reasoning: Detailed explanation of the assessment
@@ -59,6 +59,7 @@ class Evaluator(ABC):
59
59
  is_match = output.strip().lower() == expected_output.strip().lower()
60
60
  return Score(
61
61
  name=f"{self.score_name_prefix}exact_match",
62
+ evaluator_name=self.name,
62
63
  value=1.0 if is_match else 0.0,
63
64
  reasoning=f"Match: {is_match}"
64
65
  )
@@ -169,12 +170,29 @@ class Evaluator(ABC):
169
170
  - Comparison: score(self, output: str, expected_output: str) -> Score
170
171
  - All parameters: score(self, input: str, output: str, context: list[str]) -> Score
171
172
 
173
+ The runner resolves each parameter by name. Alongside the task's outputs,
174
+ which are spread into the namespace by key, four parameters are always
175
+ available -- declare only the ones you need:
176
+
177
+ * ``inputs`` (``dict``) -- the dataset item's inputs, passed as a single
178
+ dict rather than spread.
179
+ * ``outputs`` (``dict``) -- everything the task returned.
180
+ * ``expected_outputs`` (``dict | None``) -- the item's expected outputs.
181
+ * ``session`` (``Session | None``) -- the OpenTelemetry spans captured
182
+ while the task ran. Declaring it opts the evaluator in to scoring the
183
+ execution itself (tool call order, retry counts, token usage, latency)
184
+ rather than only the final output string. Trace capture is best-effort,
185
+ so default it to ``None`` and handle that case.
186
+
187
+ All four are bound after the task outputs are spread, so a task output of
188
+ the same name cannot shadow them.
189
+
172
190
  Args:
173
191
  *args: Positional arguments specific to the evaluator's needs.
174
192
  **kwargs: Keyword arguments specific to the evaluator's needs.
175
193
 
176
194
  Returns:
177
- Score | list[Score]: A single Score object or list of Score objects
195
+ Score | list[Score]: A single :class:`~fiddler_evals.pydantic_models.score.Score` object or list of Score objects
178
196
  representing the evaluation results. Each Score should include:
179
197
  - name: The score name (e.g., "has_zipcode")
180
198
  - evaluator_name: The evaluator name (e.g., "RegexMatch")
@@ -216,7 +234,7 @@ class FiddlerEvaluator(Evaluator, ABC):
216
234
  data (dict[str, Any]): The API response data.
217
235
 
218
236
  Returns:
219
- Score | list[Score]: A single Score object or list of Score objects.
237
+ Score | list[Score]: A single :class:`~fiddler_evals.pydantic_models.score.Score` object or list of Score objects.
220
238
  """
221
239
  scores_response = EvaluatorResponse(**data)
222
240
  if not scores_response.scores:
@@ -36,7 +36,7 @@ class Coherence(FiddlerLLMAAJEvaluator):
36
36
  Used for context-aware coherence evaluation.
37
37
 
38
38
  Returns:
39
- Score: A Score object containing:
39
+ Score: A :class:`~fiddler_evals.pydantic_models.score.Score` object containing:
40
40
  - name: "is_coherent"
41
41
  - evaluator_name: "Coherence"
42
42
  - value: 1.0 if coherent, 0.0 if incoherent
@@ -83,6 +83,8 @@ class Coherence(FiddlerLLMAAJEvaluator):
83
83
  """
84
84
 
85
85
  name = "coherence"
86
+ """Evaluator identifier, recorded as ``evaluator_name`` on every
87
+ ``Score`` this evaluator produces."""
86
88
 
87
89
  def score(self, prompt: str, response: str) -> Score: # pylint: disable=arguments-differ
88
90
  """Score the coherence of a response.
@@ -92,7 +94,7 @@ class Coherence(FiddlerLLMAAJEvaluator):
92
94
  response (str): The response to evaluate for coherence.
93
95
 
94
96
  Returns:
95
- Score: A Score object for coherence assessment.
97
+ Score: A :class:`~fiddler_evals.pydantic_models.score.Score` object for coherence assessment.
96
98
  """
97
99
  prompt = prompt.strip() if prompt else ""
98
100
  response = response.strip() if response else ""
@@ -30,7 +30,7 @@ class Conciseness(FiddlerLLMAAJEvaluator):
30
30
  response (str): The LLM's response to evaluate for conciseness.
31
31
 
32
32
  Returns:
33
- Score: A Score object containing:
33
+ Score: A :class:`~fiddler_evals.pydantic_models.score.Score` object containing:
34
34
  - value: 1.0 if concise, 0.0 if verbose
35
35
  - label: String representation of the boolean result
36
36
  - reasoning: Detailed explanation of the assessment
@@ -58,6 +58,8 @@ class Conciseness(FiddlerLLMAAJEvaluator):
58
58
  """
59
59
 
60
60
  name = "conciseness"
61
+ """Evaluator identifier, recorded as ``evaluator_name`` on every
62
+ ``Score`` this evaluator produces."""
61
63
 
62
64
  def score(self, response: str) -> Score: # pylint: disable=arguments-differ
63
65
  """Score the conciseness of an answer.
@@ -66,7 +68,7 @@ class Conciseness(FiddlerLLMAAJEvaluator):
66
68
  response (str): The LLM's response to evaluate for conciseness.
67
69
 
68
70
  Returns:
69
- Score: A Score object containing:
71
+ Score: A :class:`~fiddler_evals.pydantic_models.score.Score` object containing:
70
72
  - value: 1.0 if concise, 0.0 if verbose
71
73
  - label: String representation of the boolean result
72
74
  - reasoning: Detailed explanation of the assessment