Skip to content

honeyhive.config.models

Domain-specific configuration models for HoneyHive SDK.

This package provides Pydantic models for different domains within the SDK to reduce constructor argument count while maintaining backwards compatibility.

The hybrid approach allows both old and new usage patterns:

Tracer Configuration

Old Usage (Backwards Compatible): >>> tracer = HoneyHiveTracer(api_key="...", project="...", verbose=True)

New Usage (Recommended): >>> from honeyhive.config.models import TracerConfig >>> config = TracerConfig(api_key="...", project="...", verbose=True) >>> tracer = HoneyHiveTracer(config=config)

API Client Configuration (Future)

Old Usage (Current): >>> client = HoneyHive(bearer_auth="...", server_url="...", timeout_ms=30000)

New Usage (Future): >>> from honeyhive.config.models import APIClientConfig >>> config = APIClientConfig(api_key="...", server_url="...", timeout=30.0) >>> client = HoneyHive(config=config)

Architecture

The models are organized by domain: - base.py: BaseHoneyHiveConfig with common fields (api_key, project, etc.) - tracer.py: TracerConfig, SessionConfig, EvaluationConfig - api_client.py: APIClientConfig for API client initialization

All models inherit from BaseHoneyHiveConfig to avoid field duplication while maintaining type safety and validation consistency.

APIClientConfig

Bases: BaseHoneyHiveConfig, ServerURLMixin

Configuration for HoneyHive API client.

This class defines configuration parameters for API client initialization to reduce argument count while maintaining backwards compatibility. It inherits common fields from BaseHoneyHiveConfig and composes HTTPClientConfig for transport-level settings.

Inherited Fields
  • api_key: HoneyHive API key for authentication
  • project: Deprecated; accepted for backwards compatibility but ignored (project is inferred from the API key by the backend)
  • test_mode: Enable test mode (no data sent to backend)
  • verbose: Enable verbose logging output
API Client-Specific Fields
  • server_url: Server URL for requests (from HH_API_URL env var)
  • http_config: HTTP transport configuration
Example

Simple usage

config = APIClientConfig( ... api_key="hh_1234567890abcdef", ... server_url="https://api.dp1.us.honeyhive.ai" ... )

Advanced usage with HTTP config

http_config = HTTPClientConfig(timeout=60.0, max_connections=50) config = APIClientConfig( ... api_key="hh_1234567890abcdef", ... server_url="https://api.dp1.us.honeyhive.ai", ... http_config=http_config ... )

Future usage:

client = HoneyHive(config=config)

Current backwards compatible usage:

client = HoneyHive( ... bearer_auth="hh_1234567890abcdef", ... server_url="https://api.dp1.us.honeyhive.ai", ... timeout_ms=30000 ... )

Source code in src/honeyhive/config/models/api_client.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
class APIClientConfig(BaseHoneyHiveConfig, ServerURLMixin):
    """Configuration for HoneyHive API client.

    This class defines configuration parameters for API client initialization
    to reduce argument count while maintaining backwards compatibility.
    It inherits common fields from BaseHoneyHiveConfig and composes
    HTTPClientConfig for transport-level settings.

    Inherited Fields:
        - api_key: HoneyHive API key for authentication
        - project: Deprecated; accepted for backwards compatibility but ignored
          (project is inferred from the API key by the backend)
        - test_mode: Enable test mode (no data sent to backend)
        - verbose: Enable verbose logging output

    API Client-Specific Fields:
        - server_url: Server URL for requests (from HH_API_URL env var)
        - http_config: HTTP transport configuration

    Example:
        >>> # Simple usage
        >>> config = APIClientConfig(
        ...     api_key="hh_1234567890abcdef",
        ...     server_url="https://api.dp1.us.honeyhive.ai"
        ... )

        >>> # Advanced usage with HTTP config
        >>> http_config = HTTPClientConfig(timeout=60.0, max_connections=50)
        >>> config = APIClientConfig(
        ...     api_key="hh_1234567890abcdef",
        ...     server_url="https://api.dp1.us.honeyhive.ai",
        ...     http_config=http_config
        ... )

        >>> # Future usage:
        >>> # client = HoneyHive(config=config)

        # Current backwards compatible usage:
        >>> client = HoneyHive(
        ...     bearer_auth="hh_1234567890abcdef",
        ...     server_url="https://api.dp1.us.honeyhive.ai",
        ...     timeout_ms=30000
        ... )
    """

    # Compose HTTP client configuration
    http_config: HTTPClientConfig = Field(
        default_factory=HTTPClientConfig, description="HTTP transport configuration"
    )

    model_config = SettingsConfigDict(
        validate_assignment=True,
        extra="forbid",
        case_sensitive=False,
    )

http_config class-attribute instance-attribute

http_config: HTTPClientConfig = Field(
    default_factory=HTTPClientConfig,
    description="HTTP transport configuration",
)

BaseHoneyHiveConfig

Bases: BaseSettings

Base configuration model with common HoneyHive fields.

This base class contains fields that are commonly used across different parts of the SDK (tracer, API client, evaluation, etc.) to avoid duplication and ensure consistent validation.

Common Fields
  • api_key: HoneyHive API key for authentication
  • project: Deprecated project name (optional; backend infers scope from API key)
  • test_mode: Enable test mode (no data sent to backend)
  • verbose: Enable verbose logging
Example

This class is not used directly but inherited by domain-specific configs:

class TracerConfig(BaseHoneyHiveConfig): ... session_name: Optional[str] = None ... source: str = "dev"

config = TracerConfig(api_key="hh_...", project="my-project") print(config.api_key) # Inherited from base hh_...

Source code in src/honeyhive/config/models/base.py
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
class BaseHoneyHiveConfig(BaseSettings):
    """Base configuration model with common HoneyHive fields.

    This base class contains fields that are commonly used across different
    parts of the SDK (tracer, API client, evaluation, etc.) to avoid
    duplication and ensure consistent validation.

    Common Fields:
        - api_key: HoneyHive API key for authentication
        - project: Deprecated project name (optional; backend infers scope from API key)
        - test_mode: Enable test mode (no data sent to backend)
        - verbose: Enable verbose logging

    Example:
        This class is not used directly but inherited by domain-specific configs:

        >>> class TracerConfig(BaseHoneyHiveConfig):
        ...     session_name: Optional[str] = None
        ...     source: str = "dev"
        >>>
        >>> config = TracerConfig(api_key="hh_...", project="my-project")
        >>> print(config.api_key)  # Inherited from base
        hh_...
    """

    api_key: Optional[str] = Field(  # type: ignore[call-overload,pydantic-alias]
        default=None,
        description="HoneyHive API key for authentication",
        validation_alias=AliasChoices("HH_API_KEY", "api_key"),
        examples=["hh_1234567890abcdef"],
    )

    project: Optional[str] = Field(  # type: ignore[call-overload,pydantic-alias]
        default=None,
        description=(
            "Deprecated. Legacy project name accepted for backwards compatibility "
            "but no longer used — the backend infers project context from the API "
            "key. Will be removed in v2.0."
        ),
        validation_alias=AliasChoices("HH_PROJECT", "project"),
        examples=["my-llm-project", "chatbot-v2"],
    )

    test_mode: bool = Field(  # type: ignore[call-overload,pydantic-alias]
        default=False,
        description="Enable test mode (no data sent to backend)",
        validation_alias=AliasChoices("HH_TEST_MODE", "test_mode"),
    )

    verbose: bool = Field(  # type: ignore[call-overload,pydantic-alias]
        default=False,
        description="Enable verbose logging output and debug mode",
        validation_alias=AliasChoices("HH_VERBOSE", "verbose"),
    )

    model_config = SettingsConfigDict(
        validate_assignment=True,
        extra="forbid",  # Prevent accidental typos in field names
        case_sensitive=False,
    )

    def __init__(self, **data: Any) -> None:
        """Initialize base config with unified verbose/debug mode handling."""
        # Handle verbose mode from HH_VERBOSE environment variable
        if "verbose" not in data:
            # Check HH_VERBOSE environment variable
            verbose_env = os.getenv("HH_VERBOSE", "").lower()

            # Set verbose=True if HH_VERBOSE is true
            if verbose_env in ("true", "1", "yes", "on"):
                data["verbose"] = True

        super().__init__(**data)

    @field_validator("api_key", mode="before")
    @classmethod
    def validate_api_key(cls, v: Any) -> Optional[str]:
        """Validate API key format with graceful degradation.

        Args:
            v: The API key value to validate

        Returns:
            The validated and normalized API key, or None if invalid
        """
        validated = _safe_validate_string(v, "api_key", allow_none=True, default=None)
        if validated is not None:
            # Basic format validation - should start with 'hh_' for HoneyHive keys
            if not validated.startswith(("hh_", "sk-")):
                # Warning: not an error to maintain backwards compatibility
                logger.debug(
                    "API key does not follow standard format (hh_* or sk_*): %s...",
                    validated[:8],
                    extra={
                        "honeyhive_data": {
                            "api_key_prefix": validated[:3] if validated else None
                        }
                    },
                )
        return validated

    @field_validator("project", mode="before")
    @classmethod
    def validate_project(cls, v: Any) -> Optional[str]:
        """Validate project name format with graceful degradation.

        Args:
            v: The project name to validate

        Returns:
            The validated and normalized project name, or None if invalid
        """
        validated = _safe_validate_string(v, "project", allow_none=True, default=None)
        if validated is not None:
            # Basic validation - no special characters that could cause issues
            invalid_chars = ["/", "\\", "?", "#", "&"]
            if any(char in validated for char in invalid_chars):
                logger.warning(
                    "Project name contains invalid characters. Using None.",
                    extra={
                        "honeyhive_data": {
                            "project": validated,
                            "invalid_chars": invalid_chars,
                        }
                    },
                )
                return None
        return validated

    @field_validator("test_mode", "verbose", mode="before")
    @classmethod
    def validate_boolean_fields(cls, v: Any) -> bool:
        """Validate boolean fields with graceful degradation.

        Args:
            v: The value to validate as boolean

        Returns:
            The validated boolean value, or False if invalid
        """
        if v is None:
            return False

        if isinstance(v, bool):
            return v

        if isinstance(v, str):
            # Handle common boolean string representations
            lower_v = v.lower().strip()
            if lower_v in ("true", "1", "yes", "on", "enabled"):
                return True
            if lower_v in ("false", "0", "no", "off", "disabled", ""):
                return False
            # Invalid boolean string - log warning and return default
            logger.warning(
                "Invalid boolean value: %s. Using False as default.",
                v,
                extra={"honeyhive_data": {"invalid_boolean": v}},
            )
            return False

        # For non-string, non-bool types, log warning and return default
        logger.warning(
            "Invalid boolean type: %s. Using False as default.",
            type(v).__name__,
            extra={
                "honeyhive_data": {"invalid_type": type(v).__name__, "value": str(v)}
            },
        )
        return False

api_key class-attribute instance-attribute

api_key: Optional[str] = Field(
    default=None,
    description="HoneyHive API key for authentication",
    validation_alias=AliasChoices("HH_API_KEY", "api_key"),
    examples=["hh_1234567890abcdef"],
)

project class-attribute instance-attribute

project: Optional[str] = Field(
    default=None,
    description="Deprecated. Legacy project name accepted for backwards compatibility but no longer used — the backend infers project context from the API key. Will be removed in v2.0.",
    validation_alias=AliasChoices("HH_PROJECT", "project"),
    examples=["my-llm-project", "chatbot-v2"],
)

test_mode class-attribute instance-attribute

test_mode: bool = Field(
    default=False,
    description="Enable test mode (no data sent to backend)",
    validation_alias=AliasChoices(
        "HH_TEST_MODE", "test_mode"
    ),
)

verbose class-attribute instance-attribute

verbose: bool = Field(
    default=False,
    description="Enable verbose logging output and debug mode",
    validation_alias=AliasChoices("HH_VERBOSE", "verbose"),
)

validate_api_key classmethod

validate_api_key(v: Any) -> Optional[str]

Validate API key format with graceful degradation.

Parameters:

Name Type Description Default
v Any

The API key value to validate

required

Returns:

Type Description
Optional[str]

The validated and normalized API key, or None if invalid

Source code in src/honeyhive/config/models/base.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
@field_validator("api_key", mode="before")
@classmethod
def validate_api_key(cls, v: Any) -> Optional[str]:
    """Validate API key format with graceful degradation.

    Args:
        v: The API key value to validate

    Returns:
        The validated and normalized API key, or None if invalid
    """
    validated = _safe_validate_string(v, "api_key", allow_none=True, default=None)
    if validated is not None:
        # Basic format validation - should start with 'hh_' for HoneyHive keys
        if not validated.startswith(("hh_", "sk-")):
            # Warning: not an error to maintain backwards compatibility
            logger.debug(
                "API key does not follow standard format (hh_* or sk_*): %s...",
                validated[:8],
                extra={
                    "honeyhive_data": {
                        "api_key_prefix": validated[:3] if validated else None
                    }
                },
            )
    return validated

validate_project classmethod

validate_project(v: Any) -> Optional[str]

Validate project name format with graceful degradation.

Parameters:

Name Type Description Default
v Any

The project name to validate

required

Returns:

Type Description
Optional[str]

The validated and normalized project name, or None if invalid

Source code in src/honeyhive/config/models/base.py
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
@field_validator("project", mode="before")
@classmethod
def validate_project(cls, v: Any) -> Optional[str]:
    """Validate project name format with graceful degradation.

    Args:
        v: The project name to validate

    Returns:
        The validated and normalized project name, or None if invalid
    """
    validated = _safe_validate_string(v, "project", allow_none=True, default=None)
    if validated is not None:
        # Basic validation - no special characters that could cause issues
        invalid_chars = ["/", "\\", "?", "#", "&"]
        if any(char in validated for char in invalid_chars):
            logger.warning(
                "Project name contains invalid characters. Using None.",
                extra={
                    "honeyhive_data": {
                        "project": validated,
                        "invalid_chars": invalid_chars,
                    }
                },
            )
            return None
    return validated

validate_boolean_fields classmethod

validate_boolean_fields(v: Any) -> bool

Validate boolean fields with graceful degradation.

Parameters:

Name Type Description Default
v Any

The value to validate as boolean

required

Returns:

Type Description
bool

The validated boolean value, or False if invalid

Source code in src/honeyhive/config/models/base.py
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
@field_validator("test_mode", "verbose", mode="before")
@classmethod
def validate_boolean_fields(cls, v: Any) -> bool:
    """Validate boolean fields with graceful degradation.

    Args:
        v: The value to validate as boolean

    Returns:
        The validated boolean value, or False if invalid
    """
    if v is None:
        return False

    if isinstance(v, bool):
        return v

    if isinstance(v, str):
        # Handle common boolean string representations
        lower_v = v.lower().strip()
        if lower_v in ("true", "1", "yes", "on", "enabled"):
            return True
        if lower_v in ("false", "0", "no", "off", "disabled", ""):
            return False
        # Invalid boolean string - log warning and return default
        logger.warning(
            "Invalid boolean value: %s. Using False as default.",
            v,
            extra={"honeyhive_data": {"invalid_boolean": v}},
        )
        return False

    # For non-string, non-bool types, log warning and return default
    logger.warning(
        "Invalid boolean type: %s. Using False as default.",
        type(v).__name__,
        extra={
            "honeyhive_data": {"invalid_type": type(v).__name__, "value": str(v)}
        },
    )
    return False

ExperimentConfig

Bases: BaseHoneyHiveConfig

Experiment and evaluation configuration settings.

This class extends BaseHoneyHiveConfig with experiment-specific settings for A/B testing, feature flags, and experimental features. Supports multiple experiment tracking platforms (MLflow, W&B, Comet, etc.).

Example

config = ExperimentConfig( ... experiment_id="exp_12345", ... experiment_name="model-comparison", ... experiment_variant="baseline", ... experiment_group="control" ... )

Or load from environment variables:

export HH_EXPERIMENT_ID=exp_12345

export MLFLOW_EXPERIMENT_NAME=model-comparison

config = ExperimentConfig()

Source code in src/honeyhive/config/models/experiment.py
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
class ExperimentConfig(BaseHoneyHiveConfig):
    """Experiment and evaluation configuration settings.

    This class extends BaseHoneyHiveConfig with experiment-specific settings
    for A/B testing, feature flags, and experimental features. Supports
    multiple experiment tracking platforms (MLflow, W&B, Comet, etc.).

    Example:
        >>> config = ExperimentConfig(
        ...     experiment_id="exp_12345",
        ...     experiment_name="model-comparison",
        ...     experiment_variant="baseline",
        ...     experiment_group="control"
        ... )
        >>> # Or load from environment variables:
        >>> # export HH_EXPERIMENT_ID=exp_12345
        >>> # export MLFLOW_EXPERIMENT_NAME=model-comparison
        >>> config = ExperimentConfig()
    """

    # Experiment identification
    experiment_id: Optional[str] = Field(  # type: ignore[call-overload,pydantic-alias]
        default=None,
        description="Unique experiment identifier",
        validation_alias=AliasChoices("HH_EXPERIMENT_ID", "experiment_id"),
        examples=["exp_12345", "experiment-2024-01-15"],
    )

    experiment_name: Optional[str] = Field(  # type: ignore[call-overload,pydantic-alias]  # pylint: disable=line-too-long
        default=None,
        description="Human-readable experiment name",
        validation_alias=AliasChoices("HH_EXPERIMENT_NAME", "experiment_name"),
        examples=["model-comparison", "baseline-vs-optimized"],
    )

    # Experiment variants and groups
    experiment_variant: Optional[str] = Field(  # type: ignore[call-overload,pydantic-alias]  # pylint: disable=line-too-long
        default=None,
        description="Experiment variant/treatment identifier",
        validation_alias=AliasChoices("HH_EXPERIMENT_VARIANT", "experiment_variant"),
        examples=["baseline", "treatment_a", "optimized"],
    )

    experiment_group: Optional[str] = Field(  # type: ignore[call-overload,pydantic-alias]  # pylint: disable=line-too-long
        default=None,
        description="Experiment group/cohort identifier",
        validation_alias=AliasChoices("HH_EXPERIMENT_GROUP", "experiment_group"),
        examples=["control", "test", "cohort_1"],
    )

    # Experiment metadata
    experiment_metadata: Optional[Dict[str, Any]] = Field(  # type: ignore[call-overload,pydantic-alias]  # pylint: disable=line-too-long
        default=None,
        description="Experiment metadata and tags",
        validation_alias=AliasChoices("HH_EXPERIMENT_METADATA", "experiment_metadata"),
        examples=[{"model_type": "gpt-4", "temperature": 0.7}],
    )

    model_config = SettingsConfigDict(
        validate_assignment=True,
        extra="forbid",
        case_sensitive=False,
    )

    def __init__(self, **data: Any) -> None:
        """Initialize experiment config with environment variable fallbacks.

        Supports multiple experiment tracking platforms by checking
        various environment variable patterns.
        """
        # Load from environment variables with fallbacks to standard platforms
        env_data = {
            # Experiment ID with multiple fallbacks
            "experiment_id": (
                os.getenv("HH_EXPERIMENT_ID")
                or os.getenv("EXPERIMENT_ID")
                or os.getenv("MLFLOW_EXPERIMENT_ID")
                or os.getenv("WANDB_RUN_ID")
                or os.getenv("COMET_EXPERIMENT_KEY")
            ),
            # Experiment name with multiple fallbacks
            "experiment_name": (
                os.getenv("HH_EXPERIMENT_NAME")
                or os.getenv("EXPERIMENT_NAME")
                or os.getenv("MLFLOW_EXPERIMENT_NAME")
                or os.getenv("WANDB_PROJECT")
                or os.getenv("COMET_PROJECT_NAME")
            ),
            # Experiment variant with multiple fallbacks
            "experiment_variant": (
                os.getenv("HH_EXPERIMENT_VARIANT")
                or os.getenv("EXPERIMENT_VARIANT")
                or os.getenv("VARIANT")
                or os.getenv("AB_TEST_VARIANT")
                or os.getenv("TREATMENT")
            ),
            # Experiment group with multiple fallbacks
            "experiment_group": (
                os.getenv("HH_EXPERIMENT_GROUP")
                or os.getenv("EXPERIMENT_GROUP")
                or os.getenv("GROUP")
                or os.getenv("AB_TEST_GROUP")
                or os.getenv("COHORT")
            ),
            # Experiment metadata with multiple fallbacks
            "experiment_metadata": (
                _get_env_json("HH_EXPERIMENT_METADATA")
                or _get_env_json("EXPERIMENT_METADATA")
                or _get_env_json("MLFLOW_TAGS")
                or _get_env_json("WANDB_TAGS")
                or _get_env_json("COMET_TAGS")
            ),
        }

        # Merge environment data with provided data (provided data takes precedence)
        merged_data = {**env_data, **data}
        super().__init__(**merged_data)

    @field_validator(
        "experiment_id",
        "experiment_name",
        "experiment_variant",
        "experiment_group",
        mode="before",
    )
    @classmethod
    def validate_experiment_strings(cls, v: Optional[str]) -> Optional[str]:
        """Validate experiment string fields with graceful degradation."""

        return _safe_validate_string(
            v, "experiment field", allow_none=True, default=None
        )

    @field_validator("experiment_metadata", mode="before")
    @classmethod
    def validate_experiment_metadata(
        cls, v: Optional[Dict[str, Any]]
    ) -> Optional[Dict[str, Any]]:
        """Validate experiment metadata format with graceful degradation."""
        if v is not None:
            if not isinstance(v, dict):
                logger = logging.getLogger(__name__)
                logger.warning(
                    ("Invalid experiment_metadata: expected dict, got %s. Using None."),
                    type(v).__name__,
                    extra={"honeyhive_data": {"metadata_type": type(v).__name__}},
                )
                return None

            # Ensure all keys are strings - filter out invalid keys
            valid_metadata = {}
            for key, value in v.items():
                if isinstance(key, str):
                    valid_metadata[key] = value
                else:
                    logger = logging.getLogger(__name__)
                    logger.warning(
                        (
                            "Invalid experiment_metadata key: expected string, "
                            "got %s. Skipping key."
                        ),
                        type(key).__name__,
                        extra={
                            "honeyhive_data": {
                                "key_type": type(key).__name__,
                                "key": str(key),
                            }
                        },
                    )
            return valid_metadata if valid_metadata else None
        return v

experiment_id class-attribute instance-attribute

experiment_id: Optional[str] = Field(
    default=None,
    description="Unique experiment identifier",
    validation_alias=AliasChoices(
        "HH_EXPERIMENT_ID", "experiment_id"
    ),
    examples=["exp_12345", "experiment-2024-01-15"],
)

experiment_name class-attribute instance-attribute

experiment_name: Optional[str] = Field(
    default=None,
    description="Human-readable experiment name",
    validation_alias=AliasChoices(
        "HH_EXPERIMENT_NAME", "experiment_name"
    ),
    examples=["model-comparison", "baseline-vs-optimized"],
)

experiment_variant class-attribute instance-attribute

experiment_variant: Optional[str] = Field(
    default=None,
    description="Experiment variant/treatment identifier",
    validation_alias=AliasChoices(
        "HH_EXPERIMENT_VARIANT", "experiment_variant"
    ),
    examples=["baseline", "treatment_a", "optimized"],
)

experiment_group class-attribute instance-attribute

experiment_group: Optional[str] = Field(
    default=None,
    description="Experiment group/cohort identifier",
    validation_alias=AliasChoices(
        "HH_EXPERIMENT_GROUP", "experiment_group"
    ),
    examples=["control", "test", "cohort_1"],
)

experiment_metadata class-attribute instance-attribute

experiment_metadata: Optional[Dict[str, Any]] = Field(
    default=None,
    description="Experiment metadata and tags",
    validation_alias=AliasChoices(
        "HH_EXPERIMENT_METADATA", "experiment_metadata"
    ),
    examples=[{"model_type": "gpt-4", "temperature": 0.7}],
)

validate_experiment_strings classmethod

validate_experiment_strings(
    v: Optional[str],
) -> Optional[str]

Validate experiment string fields with graceful degradation.

Source code in src/honeyhive/config/models/experiment.py
156
157
158
159
160
161
162
163
164
165
166
167
168
169
@field_validator(
    "experiment_id",
    "experiment_name",
    "experiment_variant",
    "experiment_group",
    mode="before",
)
@classmethod
def validate_experiment_strings(cls, v: Optional[str]) -> Optional[str]:
    """Validate experiment string fields with graceful degradation."""

    return _safe_validate_string(
        v, "experiment field", allow_none=True, default=None
    )

validate_experiment_metadata classmethod

validate_experiment_metadata(
    v: Optional[Dict[str, Any]],
) -> Optional[Dict[str, Any]]

Validate experiment metadata format with graceful degradation.

Source code in src/honeyhive/config/models/experiment.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
@field_validator("experiment_metadata", mode="before")
@classmethod
def validate_experiment_metadata(
    cls, v: Optional[Dict[str, Any]]
) -> Optional[Dict[str, Any]]:
    """Validate experiment metadata format with graceful degradation."""
    if v is not None:
        if not isinstance(v, dict):
            logger = logging.getLogger(__name__)
            logger.warning(
                ("Invalid experiment_metadata: expected dict, got %s. Using None."),
                type(v).__name__,
                extra={"honeyhive_data": {"metadata_type": type(v).__name__}},
            )
            return None

        # Ensure all keys are strings - filter out invalid keys
        valid_metadata = {}
        for key, value in v.items():
            if isinstance(key, str):
                valid_metadata[key] = value
            else:
                logger = logging.getLogger(__name__)
                logger.warning(
                    (
                        "Invalid experiment_metadata key: expected string, "
                        "got %s. Skipping key."
                    ),
                    type(key).__name__,
                    extra={
                        "honeyhive_data": {
                            "key_type": type(key).__name__,
                            "key": str(key),
                        }
                    },
                )
        return valid_metadata if valid_metadata else None
    return v

HTTPClientConfig

Bases: BaseHoneyHiveConfig

HTTP client configuration settings.

This class extends BaseHoneyHiveConfig with HTTP-specific settings for connection pooling, timeouts, retry behavior, proxy settings, and SSL configuration. Supports both HH_ and standard HTTP_ environment variables.

Example

config = HTTPClientConfig( ... timeout=30.0, ... max_connections=50, ... http_proxy="http://proxy.company.com:8080" ... )

Or load from environment variables:

export HH_TIMEOUT=30.0

export HH_MAX_CONNECTIONS=50

config = HTTPClientConfig()

Source code in src/honeyhive/config/models/http_client.py
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
class HTTPClientConfig(BaseHoneyHiveConfig):
    """HTTP client configuration settings.

    This class extends BaseHoneyHiveConfig with HTTP-specific settings
    for connection pooling, timeouts, retry behavior, proxy settings,
    and SSL configuration. Supports both HH_* and standard HTTP_*
    environment variables.

    Example:
        >>> config = HTTPClientConfig(
        ...     timeout=30.0,
        ...     max_connections=50,
        ...     http_proxy="http://proxy.company.com:8080"
        ... )
        >>> # Or load from environment variables:
        >>> # export HH_TIMEOUT=30.0
        >>> # export HH_MAX_CONNECTIONS=50
        >>> config = HTTPClientConfig()
    """

    # Connection settings
    timeout: float = Field(  # type: ignore[call-overload,pydantic-alias]
        default=30.0,
        description="Request timeout in seconds",
        validation_alias=AliasChoices("HH_TIMEOUT", "timeout"),
        examples=[30.0, 60.0, 120.0],
    )

    max_connections: int = Field(  # type: ignore[call-overload,pydantic-alias]
        default=10,
        description="Maximum connections in pool",
        validation_alias=AliasChoices("HH_MAX_CONNECTIONS", "max_connections"),
        examples=[10, 50, 100],
    )

    max_keepalive_connections: int = Field(  # type: ignore[call-overload,pydantic-alias]  # pylint: disable=line-too-long
        default=20,
        description="Maximum keepalive connections",
        validation_alias=AliasChoices(
            "HH_MAX_KEEPALIVE_CONNECTIONS", "max_keepalive_connections"
        ),
        examples=[20, 50, 100],
    )

    keepalive_expiry: float = Field(  # type: ignore[call-overload,pydantic-alias]
        default=30.0,
        description="Keepalive expiry time in seconds",
        validation_alias=AliasChoices("HH_KEEPALIVE_EXPIRY", "keepalive_expiry"),
        examples=[30.0, 60.0, 300.0],
    )

    pool_timeout: float = Field(  # type: ignore[call-overload,pydantic-alias]
        default=10.0,
        description="Pool timeout in seconds",
        validation_alias=AliasChoices("HH_POOL_TIMEOUT", "pool_timeout"),
        examples=[10.0, 30.0, 60.0],
    )

    # Rate limiting
    rate_limit_calls: int = Field(  # type: ignore[call-overload,pydantic-alias]
        default=100,
        description="Maximum calls per time window",
        validation_alias=AliasChoices("HH_RATE_LIMIT_CALLS", "rate_limit_calls"),
        examples=[100, 200, 500],
    )

    rate_limit_window: float = Field(  # type: ignore[call-overload,pydantic-alias]
        default=60.0,
        description="Rate limit time window in seconds",
        validation_alias=AliasChoices("HH_RATE_LIMIT_WINDOW", "rate_limit_window"),
        examples=[60.0, 300.0, 3600.0],
    )

    max_retries: int = Field(  # type: ignore[call-overload,pydantic-alias]
        3,
        description="Maximum retry attempts",
        validation_alias=AliasChoices("HH_MAX_RETRIES", "max_retries"),
        examples=[3, 5, 10],
    )

    # Proxy settings
    http_proxy: Optional[str] = Field(  # type: ignore[call-overload,pydantic-alias]
        None,
        description="HTTP proxy URL",
        validation_alias=AliasChoices("HH_HTTP_PROXY", "http_proxy"),
        examples=["http://proxy.company.com:8080"],
    )

    https_proxy: Optional[str] = Field(  # type: ignore[call-overload,pydantic-alias]
        None,
        description="HTTPS proxy URL",
        validation_alias=AliasChoices("HH_HTTPS_PROXY", "https_proxy"),
        examples=["https://proxy.company.com:8080"],
    )

    no_proxy: Optional[str] = Field(  # type: ignore[call-overload,pydantic-alias]
        None,
        description="Comma-separated list of hosts to bypass proxy",
        validation_alias=AliasChoices("HH_NO_PROXY", "no_proxy"),
        examples=["localhost,127.0.0.1,.local"],
    )

    # SSL and redirects
    verify_ssl: bool = Field(  # type: ignore[call-overload,pydantic-alias]
        True,
        description="Verify SSL certificates",
        validation_alias=AliasChoices("HH_VERIFY_SSL", "verify_ssl"),
    )

    follow_redirects: bool = Field(  # type: ignore[call-overload,pydantic-alias]
        True,
        description="Follow HTTP redirects",
        validation_alias=AliasChoices("HH_FOLLOW_REDIRECTS", "follow_redirects"),
    )

    model_config = SettingsConfigDict(
        validate_assignment=True,
        extra="forbid",
        case_sensitive=False,
    )

    def __init__(self, **data: Any) -> None:
        """Initialize HTTP client config with environment variable fallbacks.

        Supports both HH_* and standard HTTP_* environment variables
        for maximum compatibility with existing infrastructure.
        """
        # Load from environment variables with fallbacks to standard env vars
        env_data = {
            "timeout": _get_env_float("HH_TIMEOUT", 30.0),
            "max_connections": _get_env_int(
                "HH_MAX_CONNECTIONS", _get_env_int("HTTP_MAX_CONNECTIONS", 10)
            ),
            "max_keepalive_connections": _get_env_int(
                "HH_MAX_KEEPALIVE_CONNECTIONS",
                _get_env_int("HTTP_MAX_KEEPALIVE_CONNECTIONS", 20),
            ),
            "keepalive_expiry": _get_env_float(
                "HH_KEEPALIVE_EXPIRY", _get_env_float("HTTP_KEEPALIVE_EXPIRY", 30.0)
            ),
            "pool_timeout": _get_env_float(
                "HH_POOL_TIMEOUT", _get_env_float("HTTP_POOL_TIMEOUT", 10.0)
            ),
            "rate_limit_calls": _get_env_int(
                "HH_RATE_LIMIT_CALLS", _get_env_int("HTTP_RATE_LIMIT_CALLS", 100)
            ),
            "rate_limit_window": _get_env_float(
                "HH_RATE_LIMIT_WINDOW", _get_env_float("HTTP_RATE_LIMIT_WINDOW", 60.0)
            ),
            "max_retries": _get_env_int("HH_MAX_RETRIES", 3),
            # Proxy settings with fallbacks
            "http_proxy": (
                os.getenv("HH_HTTP_PROXY")
                or os.getenv("HTTP_PROXY")
                or os.getenv("http_proxy")
            ),
            "https_proxy": (
                os.getenv("HH_HTTPS_PROXY")
                or os.getenv("HTTPS_PROXY")
                or os.getenv("https_proxy")
            ),
            "no_proxy": (
                os.getenv("HH_NO_PROXY")
                or os.getenv("NO_PROXY")
                or os.getenv("no_proxy")
            ),
            # SSL and redirects
            "verify_ssl": _get_env_bool(
                "HH_VERIFY_SSL", _get_env_bool("VERIFY_SSL", True)
            ),
            "follow_redirects": _get_env_bool(
                "HH_FOLLOW_REDIRECTS", _get_env_bool("FOLLOW_REDIRECTS", True)
            ),
        }

        # Merge environment data with provided data (provided data takes precedence)
        merged_data = {**env_data, **data}
        super().__init__(**merged_data)

    @field_validator(
        "timeout",
        "keepalive_expiry",
        "pool_timeout",
        "rate_limit_window",
        mode="before",
    )
    @classmethod
    def validate_positive_float(cls, v: Any) -> float:
        """Validate that float values are positive with graceful degradation."""
        # Handle type conversion gracefully
        try:
            if v is None:
                return 30.0  # Default for None
            v = float(v)
        except (ValueError, TypeError):
            logger = logging.getLogger(__name__)
            logger.warning(
                "Invalid float type: expected float, got %s. Using default 30.0.",
                type(v).__name__,
                extra={
                    "honeyhive_data": {"invalid_value": v, "type": type(v).__name__}
                },
            )
            return 30.0  # Safe default

        if v <= 0:
            logger = logging.getLogger(__name__)
            logger.warning(
                "Invalid timeout value: must be positive, got %s. Using default 30.0.",
                v,
                extra={"honeyhive_data": {"invalid_timeout": v}},
            )
            return 30.0  # Safe default
        return v  # type: ignore[no-any-return]

    @field_validator(
        "max_connections",
        "max_keepalive_connections",
        "rate_limit_calls",
        "max_retries",
        mode="before",
    )
    @classmethod
    def validate_positive_int(cls, v: Any) -> int:
        """Validate that integer values are positive with graceful degradation."""
        # Handle type conversion gracefully
        try:
            if v is None:
                return 100  # Default for None
            v = int(v)
        except (ValueError, TypeError):
            logger = logging.getLogger(__name__)
            logger.warning(
                "Invalid int type: expected int, got %s. Using default 100.",
                type(v).__name__,
                extra={
                    "honeyhive_data": {"invalid_value": v, "type": type(v).__name__}
                },
            )
            return 100  # Safe default

        if v <= 0:
            logger = logging.getLogger(__name__)
            logger.warning(
                (
                    "Invalid connection value: must be positive, got %s. "
                    "Using default 100."
                ),
                v,
                extra={"honeyhive_data": {"invalid_value": v}},
            )
            return 100  # Safe default
        return v  # type: ignore[no-any-return]

    @field_validator("http_proxy", "https_proxy", mode="before")
    @classmethod
    def validate_proxy_url(cls, v: Optional[str]) -> Optional[str]:
        """Validate proxy URL format with graceful degradation."""

        return _safe_validate_url(v, "proxy_url", allow_none=True, default=None)

timeout class-attribute instance-attribute

timeout: float = Field(
    default=30.0,
    description="Request timeout in seconds",
    validation_alias=AliasChoices("HH_TIMEOUT", "timeout"),
    examples=[30.0, 60.0, 120.0],
)

max_connections class-attribute instance-attribute

max_connections: int = Field(
    default=10,
    description="Maximum connections in pool",
    validation_alias=AliasChoices(
        "HH_MAX_CONNECTIONS", "max_connections"
    ),
    examples=[10, 50, 100],
)

max_keepalive_connections class-attribute instance-attribute

max_keepalive_connections: int = Field(
    default=20,
    description="Maximum keepalive connections",
    validation_alias=AliasChoices(
        "HH_MAX_KEEPALIVE_CONNECTIONS",
        "max_keepalive_connections",
    ),
    examples=[20, 50, 100],
)

keepalive_expiry class-attribute instance-attribute

keepalive_expiry: float = Field(
    default=30.0,
    description="Keepalive expiry time in seconds",
    validation_alias=AliasChoices(
        "HH_KEEPALIVE_EXPIRY", "keepalive_expiry"
    ),
    examples=[30.0, 60.0, 300.0],
)

pool_timeout class-attribute instance-attribute

pool_timeout: float = Field(
    default=10.0,
    description="Pool timeout in seconds",
    validation_alias=AliasChoices(
        "HH_POOL_TIMEOUT", "pool_timeout"
    ),
    examples=[10.0, 30.0, 60.0],
)

rate_limit_calls class-attribute instance-attribute

rate_limit_calls: int = Field(
    default=100,
    description="Maximum calls per time window",
    validation_alias=AliasChoices(
        "HH_RATE_LIMIT_CALLS", "rate_limit_calls"
    ),
    examples=[100, 200, 500],
)

rate_limit_window class-attribute instance-attribute

rate_limit_window: float = Field(
    default=60.0,
    description="Rate limit time window in seconds",
    validation_alias=AliasChoices(
        "HH_RATE_LIMIT_WINDOW", "rate_limit_window"
    ),
    examples=[60.0, 300.0, 3600.0],
)

max_retries class-attribute instance-attribute

max_retries: int = Field(
    3,
    description="Maximum retry attempts",
    validation_alias=AliasChoices(
        "HH_MAX_RETRIES", "max_retries"
    ),
    examples=[3, 5, 10],
)

http_proxy class-attribute instance-attribute

http_proxy: Optional[str] = Field(
    None,
    description="HTTP proxy URL",
    validation_alias=AliasChoices(
        "HH_HTTP_PROXY", "http_proxy"
    ),
    examples=["http://proxy.company.com:8080"],
)

https_proxy class-attribute instance-attribute

https_proxy: Optional[str] = Field(
    None,
    description="HTTPS proxy URL",
    validation_alias=AliasChoices(
        "HH_HTTPS_PROXY", "https_proxy"
    ),
    examples=["https://proxy.company.com:8080"],
)

no_proxy class-attribute instance-attribute

no_proxy: Optional[str] = Field(
    None,
    description="Comma-separated list of hosts to bypass proxy",
    validation_alias=AliasChoices(
        "HH_NO_PROXY", "no_proxy"
    ),
    examples=["localhost,127.0.0.1,.local"],
)

verify_ssl class-attribute instance-attribute

verify_ssl: bool = Field(
    True,
    description="Verify SSL certificates",
    validation_alias=AliasChoices(
        "HH_VERIFY_SSL", "verify_ssl"
    ),
)

follow_redirects class-attribute instance-attribute

follow_redirects: bool = Field(
    True,
    description="Follow HTTP redirects",
    validation_alias=AliasChoices(
        "HH_FOLLOW_REDIRECTS", "follow_redirects"
    ),
)

validate_positive_float classmethod

validate_positive_float(v: Any) -> float

Validate that float values are positive with graceful degradation.

Source code in src/honeyhive/config/models/http_client.py
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
@field_validator(
    "timeout",
    "keepalive_expiry",
    "pool_timeout",
    "rate_limit_window",
    mode="before",
)
@classmethod
def validate_positive_float(cls, v: Any) -> float:
    """Validate that float values are positive with graceful degradation."""
    # Handle type conversion gracefully
    try:
        if v is None:
            return 30.0  # Default for None
        v = float(v)
    except (ValueError, TypeError):
        logger = logging.getLogger(__name__)
        logger.warning(
            "Invalid float type: expected float, got %s. Using default 30.0.",
            type(v).__name__,
            extra={
                "honeyhive_data": {"invalid_value": v, "type": type(v).__name__}
            },
        )
        return 30.0  # Safe default

    if v <= 0:
        logger = logging.getLogger(__name__)
        logger.warning(
            "Invalid timeout value: must be positive, got %s. Using default 30.0.",
            v,
            extra={"honeyhive_data": {"invalid_timeout": v}},
        )
        return 30.0  # Safe default
    return v  # type: ignore[no-any-return]

validate_positive_int classmethod

validate_positive_int(v: Any) -> int

Validate that integer values are positive with graceful degradation.

Source code in src/honeyhive/config/models/http_client.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
@field_validator(
    "max_connections",
    "max_keepalive_connections",
    "rate_limit_calls",
    "max_retries",
    mode="before",
)
@classmethod
def validate_positive_int(cls, v: Any) -> int:
    """Validate that integer values are positive with graceful degradation."""
    # Handle type conversion gracefully
    try:
        if v is None:
            return 100  # Default for None
        v = int(v)
    except (ValueError, TypeError):
        logger = logging.getLogger(__name__)
        logger.warning(
            "Invalid int type: expected int, got %s. Using default 100.",
            type(v).__name__,
            extra={
                "honeyhive_data": {"invalid_value": v, "type": type(v).__name__}
            },
        )
        return 100  # Safe default

    if v <= 0:
        logger = logging.getLogger(__name__)
        logger.warning(
            (
                "Invalid connection value: must be positive, got %s. "
                "Using default 100."
            ),
            v,
            extra={"honeyhive_data": {"invalid_value": v}},
        )
        return 100  # Safe default
    return v  # type: ignore[no-any-return]

validate_proxy_url classmethod

validate_proxy_url(v: Optional[str]) -> Optional[str]

Validate proxy URL format with graceful degradation.

Source code in src/honeyhive/config/models/http_client.py
303
304
305
306
307
308
@field_validator("http_proxy", "https_proxy", mode="before")
@classmethod
def validate_proxy_url(cls, v: Optional[str]) -> Optional[str]:
    """Validate proxy URL format with graceful degradation."""

    return _safe_validate_url(v, "proxy_url", allow_none=True, default=None)

OTLPConfig

Bases: BaseHoneyHiveConfig

OTLP (OpenTelemetry Protocol) configuration settings.

This class extends BaseHoneyHiveConfig with OTLP-specific settings for batch processing, export intervals, and performance tuning.

Example

config = OTLPConfig( ... batch_size=200, ... flush_interval=1.0, ... otlp_endpoint="https://custom.otlp.endpoint" ... )

Or load from environment variables:

export HH_BATCH_SIZE=200

export HH_FLUSH_INTERVAL=1.0

config = OTLPConfig()

Source code in src/honeyhive/config/models/otlp.py
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
class OTLPConfig(BaseHoneyHiveConfig):
    """OTLP (OpenTelemetry Protocol) configuration settings.

    This class extends BaseHoneyHiveConfig with OTLP-specific settings
    for batch processing, export intervals, and performance tuning.

    Example:
        >>> config = OTLPConfig(
        ...     batch_size=200,
        ...     flush_interval=1.0,
        ...     otlp_endpoint="https://custom.otlp.endpoint"
        ... )
        >>> # Or load from environment variables:
        >>> # export HH_BATCH_SIZE=200
        >>> # export HH_FLUSH_INTERVAL=1.0
        >>> config = OTLPConfig()
    """

    # OTLP export settings
    otlp_enabled: bool = Field(  # type: ignore[call-overload,pydantic-alias]
        default=True,
        description="Enable OTLP export",
        validation_alias=AliasChoices("HH_OTLP_ENABLED", "otlp_enabled"),
    )

    otlp_endpoint: Optional[str] = Field(  # type: ignore[call-overload,pydantic-alias]
        default=None,
        description="Custom OTLP endpoint URL",
        validation_alias=AliasChoices("HH_OTLP_ENDPOINT", "otlp_endpoint"),
        examples=[
            "https://api.dp1.us.honeyhive.ai/otlp",
            "https://custom.otlp.endpoint",
        ],
    )

    otlp_headers: Optional[Dict[str, Any]] = Field(  # type: ignore[call-overload,pydantic-alias]  # pylint: disable=line-too-long
        default=None,
        description="OTLP headers in JSON format",
        validation_alias=AliasChoices("HH_OTLP_HEADERS", "otlp_headers"),
        examples=[{"Authorization": "Bearer token", "X-Custom": "value"}],
    )

    otlp_protocol: str = Field(  # type: ignore[call-overload,pydantic-alias]
        default="http/json",
        description="OTLP protocol format: 'http/json' (default) or 'http/protobuf'",
        validation_alias=AliasChoices(
            "HH_OTLP_PROTOCOL", "OTEL_EXPORTER_OTLP_PROTOCOL", "otlp_protocol"
        ),
        examples=["http/json", "http/protobuf"],
    )

    # Batch processing settings
    batch_size: int = Field(  # type: ignore[call-overload,pydantic-alias]
        default=100,
        description="OTLP batch size for performance optimization",
        validation_alias=AliasChoices("HH_BATCH_SIZE", "batch_size"),
        examples=[50, 100, 200, 500],
    )

    flush_interval: float = Field(  # type: ignore[call-overload,pydantic-alias]
        default=5.0,
        description="OTLP flush interval in seconds",
        validation_alias=AliasChoices("HH_FLUSH_INTERVAL", "flush_interval"),
        examples=[0.5, 1.0, 5.0, 10.0],
    )

    max_export_batch_size: int = Field(  # type: ignore[call-overload,pydantic-alias]
        default=512,
        description="Maximum export batch size",
        validation_alias=AliasChoices(
            "HH_MAX_EXPORT_BATCH_SIZE", "max_export_batch_size"
        ),
        examples=[256, 512, 1024],
    )

    export_timeout: float = Field(  # type: ignore[call-overload,pydantic-alias]
        default=30.0,
        description="Export timeout in seconds",
        validation_alias=AliasChoices("HH_EXPORT_TIMEOUT", "export_timeout"),
        examples=[10.0, 30.0, 60.0],
    )

    model_config = SettingsConfigDict(
        validate_assignment=True,
        extra="forbid",
        case_sensitive=False,
    )

    def __init__(self, **data: Any) -> None:
        """Initialize OTLP config with environment variable loading."""
        # Load from environment variables
        env_data = {
            "otlp_enabled": _get_env_bool("HH_OTLP_ENABLED", True),
            "otlp_endpoint": os.getenv("HH_OTLP_ENDPOINT"),
            "otlp_headers": _get_env_json("HH_OTLP_HEADERS"),
            "otlp_protocol": os.getenv("HH_OTLP_PROTOCOL")
            or os.getenv("OTEL_EXPORTER_OTLP_PROTOCOL")
            or "http/json",
            "batch_size": _get_env_int("HH_BATCH_SIZE", 100),
            "flush_interval": _get_env_float("HH_FLUSH_INTERVAL", 5.0),
            "max_export_batch_size": _get_env_int("HH_MAX_EXPORT_BATCH_SIZE", 512),
            "export_timeout": _get_env_float("HH_EXPORT_TIMEOUT", 30.0),
        }

        # Merge environment data with provided data (provided data takes precedence)
        merged_data = {**env_data, **data}
        super().__init__(**merged_data)

    @field_validator("otlp_endpoint", mode="before")
    @classmethod
    def validate_otlp_endpoint(cls, v: Optional[str]) -> Optional[str]:
        """Validate OTLP endpoint URL format with graceful degradation."""

        # If None is provided, allow it
        if v is None:
            return None

        # Use a default OTLP endpoint for invalid URLs
        default_endpoint = "http://localhost:4318/v1/traces"
        validated = _safe_validate_url(
            v, "otlp_endpoint", allow_none=False, default=default_endpoint
        )
        # Remove trailing slash for consistency
        return validated.rstrip("/") if validated else None

    @field_validator("batch_size", "max_export_batch_size", mode="before")
    @classmethod
    def validate_batch_sizes(cls, v: Any) -> int:
        """Validate batch size values with graceful degradation."""
        # Handle type conversion gracefully
        try:
            if v is None:
                return 100  # Default for None
            v = int(v)
        except (ValueError, TypeError):
            logger = logging.getLogger(__name__)
            logger.warning(
                "Invalid batch size type: expected int, got %s. Using default 100.",
                type(v).__name__,
                extra={
                    "honeyhive_data": {
                        "invalid_batch_size": v,
                        "type": type(v).__name__,
                    }
                },
            )
            return 100  # Safe default

        if v <= 0:
            logger = logging.getLogger(__name__)
            logger.warning(
                "Invalid batch size: must be positive, got %s. Using default 100.",
                v,
                extra={"honeyhive_data": {"invalid_batch_size": v}},
            )
            return 100  # Safe default
        if v > 10000:
            logger = logging.getLogger(__name__)
            logger.warning(
                "Large batch size may impact performance: %s. Using maximum 10000.",
                v,
                extra={"honeyhive_data": {"large_batch_size": v}},
            )
            return 10000  # Performance limit
        return v  # type: ignore[no-any-return]

    @field_validator("flush_interval", "export_timeout", mode="before")
    @classmethod
    def validate_timeouts(cls, v: Any) -> float:
        """Validate timeout values with graceful degradation."""
        # Handle type conversion gracefully
        try:
            if v is None:
                return 5.0  # Default for None
            v = float(v)
        except (ValueError, TypeError):
            logger = logging.getLogger(__name__)
            logger.warning(
                "Invalid timeout type: expected float, got %s. Using default 5.0.",
                type(v).__name__,
                extra={
                    "honeyhive_data": {"invalid_timeout": v, "type": type(v).__name__}
                },
            )
            return 5.0  # Safe default

        if v <= 0:
            logger = logging.getLogger(__name__)
            logger.warning(
                "Invalid timeout: must be positive, got %s. Using default 5.0.",
                v,
                extra={"honeyhive_data": {"invalid_timeout": v}},
            )
            return 5.0  # Safe default
        return v  # type: ignore[no-any-return]

    @field_validator("otlp_headers", mode="before")
    @classmethod
    def validate_otlp_headers(
        cls, v: Optional[Dict[str, Any]]
    ) -> Optional[Dict[str, Any]]:
        """Validate OTLP headers format with graceful degradation."""
        if v is not None:
            if not isinstance(v, dict):
                logger = logging.getLogger(__name__)
                logger.warning(
                    "Invalid otlp_headers: expected dict, got %s. Using None.",
                    type(v).__name__,
                    extra={"honeyhive_data": {"headers_type": type(v).__name__}},
                )
                return None

            # Ensure all keys are strings - filter out invalid keys
            valid_headers = {}
            for key, value in v.items():
                if isinstance(key, str):
                    valid_headers[key] = value
                else:
                    logger = logging.getLogger(__name__)
                    logger.warning(
                        (
                            "Invalid OTLP header key: expected string, got %s. "
                            "Skipping key."
                        ),
                        type(key).__name__,
                        extra={
                            "honeyhive_data": {
                                "key_type": type(key).__name__,
                                "key": str(key),
                            }
                        },
                    )
            return valid_headers if valid_headers else None
        return v

otlp_enabled class-attribute instance-attribute

otlp_enabled: bool = Field(
    default=True,
    description="Enable OTLP export",
    validation_alias=AliasChoices(
        "HH_OTLP_ENABLED", "otlp_enabled"
    ),
)

otlp_endpoint class-attribute instance-attribute

otlp_endpoint: Optional[str] = Field(
    default=None,
    description="Custom OTLP endpoint URL",
    validation_alias=AliasChoices(
        "HH_OTLP_ENDPOINT", "otlp_endpoint"
    ),
    examples=[
        "https://api.dp1.us.honeyhive.ai/otlp",
        "https://custom.otlp.endpoint",
    ],
)

otlp_headers class-attribute instance-attribute

otlp_headers: Optional[Dict[str, Any]] = Field(
    default=None,
    description="OTLP headers in JSON format",
    validation_alias=AliasChoices(
        "HH_OTLP_HEADERS", "otlp_headers"
    ),
    examples=[
        {
            "Authorization": "Bearer token",
            "X-Custom": "value",
        }
    ],
)

otlp_protocol class-attribute instance-attribute

otlp_protocol: str = Field(
    default="http/json",
    description="OTLP protocol format: 'http/json' (default) or 'http/protobuf'",
    validation_alias=AliasChoices(
        "HH_OTLP_PROTOCOL",
        "OTEL_EXPORTER_OTLP_PROTOCOL",
        "otlp_protocol",
    ),
    examples=["http/json", "http/protobuf"],
)

batch_size class-attribute instance-attribute

batch_size: int = Field(
    default=100,
    description="OTLP batch size for performance optimization",
    validation_alias=AliasChoices(
        "HH_BATCH_SIZE", "batch_size"
    ),
    examples=[50, 100, 200, 500],
)

flush_interval class-attribute instance-attribute

flush_interval: float = Field(
    default=5.0,
    description="OTLP flush interval in seconds",
    validation_alias=AliasChoices(
        "HH_FLUSH_INTERVAL", "flush_interval"
    ),
    examples=[0.5, 1.0, 5.0, 10.0],
)

max_export_batch_size class-attribute instance-attribute

max_export_batch_size: int = Field(
    default=512,
    description="Maximum export batch size",
    validation_alias=AliasChoices(
        "HH_MAX_EXPORT_BATCH_SIZE", "max_export_batch_size"
    ),
    examples=[256, 512, 1024],
)

export_timeout class-attribute instance-attribute

export_timeout: float = Field(
    default=30.0,
    description="Export timeout in seconds",
    validation_alias=AliasChoices(
        "HH_EXPORT_TIMEOUT", "export_timeout"
    ),
    examples=[10.0, 30.0, 60.0],
)

validate_otlp_endpoint classmethod

validate_otlp_endpoint(v: Optional[str]) -> Optional[str]

Validate OTLP endpoint URL format with graceful degradation.

Source code in src/honeyhive/config/models/otlp.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
@field_validator("otlp_endpoint", mode="before")
@classmethod
def validate_otlp_endpoint(cls, v: Optional[str]) -> Optional[str]:
    """Validate OTLP endpoint URL format with graceful degradation."""

    # If None is provided, allow it
    if v is None:
        return None

    # Use a default OTLP endpoint for invalid URLs
    default_endpoint = "http://localhost:4318/v1/traces"
    validated = _safe_validate_url(
        v, "otlp_endpoint", allow_none=False, default=default_endpoint
    )
    # Remove trailing slash for consistency
    return validated.rstrip("/") if validated else None

validate_batch_sizes classmethod

validate_batch_sizes(v: Any) -> int

Validate batch size values with graceful degradation.

Source code in src/honeyhive/config/models/otlp.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
@field_validator("batch_size", "max_export_batch_size", mode="before")
@classmethod
def validate_batch_sizes(cls, v: Any) -> int:
    """Validate batch size values with graceful degradation."""
    # Handle type conversion gracefully
    try:
        if v is None:
            return 100  # Default for None
        v = int(v)
    except (ValueError, TypeError):
        logger = logging.getLogger(__name__)
        logger.warning(
            "Invalid batch size type: expected int, got %s. Using default 100.",
            type(v).__name__,
            extra={
                "honeyhive_data": {
                    "invalid_batch_size": v,
                    "type": type(v).__name__,
                }
            },
        )
        return 100  # Safe default

    if v <= 0:
        logger = logging.getLogger(__name__)
        logger.warning(
            "Invalid batch size: must be positive, got %s. Using default 100.",
            v,
            extra={"honeyhive_data": {"invalid_batch_size": v}},
        )
        return 100  # Safe default
    if v > 10000:
        logger = logging.getLogger(__name__)
        logger.warning(
            "Large batch size may impact performance: %s. Using maximum 10000.",
            v,
            extra={"honeyhive_data": {"large_batch_size": v}},
        )
        return 10000  # Performance limit
    return v  # type: ignore[no-any-return]

validate_timeouts classmethod

validate_timeouts(v: Any) -> float

Validate timeout values with graceful degradation.

Source code in src/honeyhive/config/models/otlp.py
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
@field_validator("flush_interval", "export_timeout", mode="before")
@classmethod
def validate_timeouts(cls, v: Any) -> float:
    """Validate timeout values with graceful degradation."""
    # Handle type conversion gracefully
    try:
        if v is None:
            return 5.0  # Default for None
        v = float(v)
    except (ValueError, TypeError):
        logger = logging.getLogger(__name__)
        logger.warning(
            "Invalid timeout type: expected float, got %s. Using default 5.0.",
            type(v).__name__,
            extra={
                "honeyhive_data": {"invalid_timeout": v, "type": type(v).__name__}
            },
        )
        return 5.0  # Safe default

    if v <= 0:
        logger = logging.getLogger(__name__)
        logger.warning(
            "Invalid timeout: must be positive, got %s. Using default 5.0.",
            v,
            extra={"honeyhive_data": {"invalid_timeout": v}},
        )
        return 5.0  # Safe default
    return v  # type: ignore[no-any-return]

validate_otlp_headers classmethod

validate_otlp_headers(
    v: Optional[Dict[str, Any]],
) -> Optional[Dict[str, Any]]

Validate OTLP headers format with graceful degradation.

Source code in src/honeyhive/config/models/otlp.py
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
@field_validator("otlp_headers", mode="before")
@classmethod
def validate_otlp_headers(
    cls, v: Optional[Dict[str, Any]]
) -> Optional[Dict[str, Any]]:
    """Validate OTLP headers format with graceful degradation."""
    if v is not None:
        if not isinstance(v, dict):
            logger = logging.getLogger(__name__)
            logger.warning(
                "Invalid otlp_headers: expected dict, got %s. Using None.",
                type(v).__name__,
                extra={"honeyhive_data": {"headers_type": type(v).__name__}},
            )
            return None

        # Ensure all keys are strings - filter out invalid keys
        valid_headers = {}
        for key, value in v.items():
            if isinstance(key, str):
                valid_headers[key] = value
            else:
                logger = logging.getLogger(__name__)
                logger.warning(
                    (
                        "Invalid OTLP header key: expected string, got %s. "
                        "Skipping key."
                    ),
                    type(key).__name__,
                    extra={
                        "honeyhive_data": {
                            "key_type": type(key).__name__,
                            "key": str(key),
                        }
                    },
                )
        return valid_headers if valid_headers else None
    return v

EvaluationConfig

Bases: BaseHoneyHiveConfig

Evaluation-specific configuration parameters.

This class handles configuration for evaluation scenarios, including dataset and run management.

Example

eval_config = EvaluationConfig( ... is_evaluation=True, ... run_id="eval-run-123", ... dataset_id="dataset-456", ... datapoint_id="datapoint-789" ... ) tracer = HoneyHiveTracer( ... config=tracer_config, ... evaluation_config=eval_config ... )

Source code in src/honeyhive/config/models/tracer.py
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
class EvaluationConfig(BaseHoneyHiveConfig):
    """Evaluation-specific configuration parameters.

    This class handles configuration for evaluation scenarios,
    including dataset and run management.

    Example:
        >>> eval_config = EvaluationConfig(
        ...     is_evaluation=True,
        ...     run_id="eval-run-123",
        ...     dataset_id="dataset-456",
        ...     datapoint_id="datapoint-789"
        ... )
        >>> tracer = HoneyHiveTracer(
        ...     config=tracer_config,
        ...     evaluation_config=eval_config
        ... )
    """

    is_evaluation: bool = Field(default=False, description="Enable evaluation mode")  # type: ignore[call-overload]

    run_id: Optional[str] = Field(  # type: ignore[call-overload]
        None,
        description="Evaluation run identifier",
        examples=["eval-run-123", "experiment-2024-01-15"],
    )

    dataset_id: Optional[str] = Field(  # type: ignore[call-overload]
        None,
        description="Dataset identifier for evaluation",
        examples=["dataset-456", "qa-dataset-v2"],
    )

    datapoint_id: Optional[str] = Field(  # type: ignore[call-overload]
        None,
        description="Specific datapoint identifier",
        examples=["datapoint-789", "question-42"],
    )

    model_config = SettingsConfigDict(
        validate_assignment=True,
        extra="forbid",
        case_sensitive=False,
    )

    @field_validator("run_id", "dataset_id", "datapoint_id", mode="before")
    @classmethod
    def validate_ids(cls, v: Any) -> Optional[str]:
        """Validate ID fields with graceful degradation.

        Args:
            v: The ID value to validate

        Returns:
            The validated ID, or None if invalid
        """
        return _safe_validate_string(v, "ID field", allow_none=True, default=None)

is_evaluation class-attribute instance-attribute

is_evaluation: bool = Field(
    default=False, description="Enable evaluation mode"
)

run_id class-attribute instance-attribute

run_id: Optional[str] = Field(
    None,
    description="Evaluation run identifier",
    examples=["eval-run-123", "experiment-2024-01-15"],
)

dataset_id class-attribute instance-attribute

dataset_id: Optional[str] = Field(
    None,
    description="Dataset identifier for evaluation",
    examples=["dataset-456", "qa-dataset-v2"],
)

datapoint_id class-attribute instance-attribute

datapoint_id: Optional[str] = Field(
    None,
    description="Specific datapoint identifier",
    examples=["datapoint-789", "question-42"],
)

validate_ids classmethod

validate_ids(v: Any) -> Optional[str]

Validate ID fields with graceful degradation.

Parameters:

Name Type Description Default
v Any

The ID value to validate

required

Returns:

Type Description
Optional[str]

The validated ID, or None if invalid

Source code in src/honeyhive/config/models/tracer.py
511
512
513
514
515
516
517
518
519
520
521
522
@field_validator("run_id", "dataset_id", "datapoint_id", mode="before")
@classmethod
def validate_ids(cls, v: Any) -> Optional[str]:
    """Validate ID fields with graceful degradation.

    Args:
        v: The ID value to validate

    Returns:
        The validated ID, or None if invalid
    """
    return _safe_validate_string(v, "ID field", allow_none=True, default=None)

SessionConfig

Bases: BaseHoneyHiveConfig

Session-specific configuration parameters.

This class handles configuration related to session management, including session linking and input/output data.

Example

session_config = SessionConfig( ... session_id="550e8400-e29b-41d4-a716-446655440000", ... inputs={"user_id": "123", "query": "Hello world"} ... ) tracer = HoneyHiveTracer( ... config=tracer_config, ... session_config=session_config ... )

Source code in src/honeyhive/config/models/tracer.py
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
class SessionConfig(BaseHoneyHiveConfig):
    """Session-specific configuration parameters.

    This class handles configuration related to session management,
    including session linking and input/output data.

    Example:
        >>> session_config = SessionConfig(
        ...     session_id="550e8400-e29b-41d4-a716-446655440000",
        ...     inputs={"user_id": "123", "query": "Hello world"}
        ... )
        >>> tracer = HoneyHiveTracer(
        ...     config=tracer_config,
        ...     session_config=session_config
        ... )
    """

    session_id: Optional[str] = Field(  # type: ignore[call-overload]
        None,
        description="Existing session ID to attach to (must be valid UUID)",
        examples=["550e8400-e29b-41d4-a716-446655440000"],
    )

    skip_backend_session_creation: bool = Field(  # type: ignore[call-overload]
        default=False,
        description=(
            "If True, skip the init-time backend session creation call. "
            "If a valid session_id is also provided, the SDK trusts that the "
            "session already exists on the backend. Otherwise, callers are "
            "expected to manage session_ids via per-request "
            "create_session(session_id=<uuid>, skip_api_call=True) calls. "
            "Note: an invalid session_id (e.g. non-UUID) triggers the "
            "degraded-mode path and still calls the backend."
        ),
    )

    inputs: Optional[Dict[str, Any]] = Field(  # type: ignore[call-overload]
        None,
        description="Session input data",
        examples=[{"user_id": "123", "query": "Hello world"}],
    )

    link_carrier: Optional[Dict[str, Any]] = Field(  # type: ignore[call-overload]
        None,
        description="Context propagation carrier for distributed tracing",
        examples=[{"traceparent": "00-...", "baggage": "..."}],
    )

    model_config = SettingsConfigDict(
        validate_assignment=True,
        extra="forbid",
        case_sensitive=False,
    )

    @field_validator("session_id", mode="before")
    @classmethod
    def validate_session_id(cls, v: Any) -> Optional[str]:
        """Validate session ID format with graceful degradation.

        Args:
            v: The session ID to validate

        Returns:
            The validated and normalized session ID, or None if invalid
        """
        validated = _safe_validate_string(
            v, "session_id", allow_none=True, default=None
        )
        if validated is not None:
            try:
                # Validate UUID format
                uuid.UUID(validated)
                return validated.lower()  # Normalize to lowercase
            except ValueError:
                logger.warning(
                    "Invalid session_id: must be a valid UUID. Using None.",
                    extra={"honeyhive_data": {"session_id": validated}},
                )
                return None
        return validated

session_id class-attribute instance-attribute

session_id: Optional[str] = Field(
    None,
    description="Existing session ID to attach to (must be valid UUID)",
    examples=["550e8400-e29b-41d4-a716-446655440000"],
)

skip_backend_session_creation class-attribute instance-attribute

skip_backend_session_creation: bool = Field(
    default=False,
    description="If True, skip the init-time backend session creation call. If a valid session_id is also provided, the SDK trusts that the session already exists on the backend. Otherwise, callers are expected to manage session_ids via per-request create_session(session_id=<uuid>, skip_api_call=True) calls. Note: an invalid session_id (e.g. non-UUID) triggers the degraded-mode path and still calls the backend.",
)

inputs class-attribute instance-attribute

inputs: Optional[Dict[str, Any]] = Field(
    None,
    description="Session input data",
    examples=[{"user_id": "123", "query": "Hello world"}],
)
link_carrier: Optional[Dict[str, Any]] = Field(
    None,
    description="Context propagation carrier for distributed tracing",
    examples=[{"traceparent": "00-...", "baggage": "..."}],
)

validate_session_id classmethod

validate_session_id(v: Any) -> Optional[str]

Validate session ID format with graceful degradation.

Parameters:

Name Type Description Default
v Any

The session ID to validate

required

Returns:

Type Description
Optional[str]

The validated and normalized session ID, or None if invalid

Source code in src/honeyhive/config/models/tracer.py
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
@field_validator("session_id", mode="before")
@classmethod
def validate_session_id(cls, v: Any) -> Optional[str]:
    """Validate session ID format with graceful degradation.

    Args:
        v: The session ID to validate

    Returns:
        The validated and normalized session ID, or None if invalid
    """
    validated = _safe_validate_string(
        v, "session_id", allow_none=True, default=None
    )
    if validated is not None:
        try:
            # Validate UUID format
            uuid.UUID(validated)
            return validated.lower()  # Normalize to lowercase
        except ValueError:
            logger.warning(
                "Invalid session_id: must be a valid UUID. Using None.",
                extra={"honeyhive_data": {"session_id": validated}},
            )
            return None
    return validated

SpanNameFilter

Bases: BaseModel

A single span name filter entry.

Uses BaseModel (not BaseSettings) since these are nested data models that should not read from environment variables.

Attributes:

Name Type Description
type Literal['prefix']

The filter matching strategy. Only "prefix" is currently supported.

value str

The value to match against span names.

Source code in src/honeyhive/config/models/tracer.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class SpanNameFilter(BaseModel):
    """A single span name filter entry.

    Uses BaseModel (not BaseSettings) since these are nested data models
    that should not read from environment variables.

    Attributes:
        type: The filter matching strategy. Only "prefix" is currently supported.
        value: The value to match against span names.
    """

    type: Literal["prefix"] = Field(
        description='Filter matching strategy. Only "prefix" is currently supported.',
    )
    value: str = Field(
        description="The value to match against span names.",
        examples=["a2a.client.transports.jsonrpc"],
    )

    model_config = {"validate_assignment": True, "extra": "forbid"}

type class-attribute instance-attribute

type: Literal["prefix"] = Field(
    description='Filter matching strategy. Only "prefix" is currently supported.'
)

value class-attribute instance-attribute

value: str = Field(
    description="The value to match against span names.",
    examples=["a2a.client.transports.jsonrpc"],
)

SpanNameFilters

Bases: BaseModel

Configuration for filtering spans by name.

Uses BaseModel (not BaseSettings) since these are nested data models that should not read from environment variables.

Supports both include (allow-list) and exclude (block-list) filters. If include is specified, only spans matching at least one include filter are kept. If exclude is specified, spans matching any exclude filter are dropped. If both are specified, a span must match include AND not match exclude.

Example

filters = SpanNameFilters( ... exclude=[SpanNameFilter(type="prefix", value="a2a.client.transports")] ... )

Source code in src/honeyhive/config/models/tracer.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
class SpanNameFilters(BaseModel):
    """Configuration for filtering spans by name.

    Uses BaseModel (not BaseSettings) since these are nested data models
    that should not read from environment variables.

    Supports both include (allow-list) and exclude (block-list) filters.
    If include is specified, only spans matching at least one include filter are kept.
    If exclude is specified, spans matching any exclude filter are dropped.
    If both are specified, a span must match include AND not match exclude.

    Example:
        >>> filters = SpanNameFilters(
        ...     exclude=[SpanNameFilter(type="prefix", value="a2a.client.transports")]
        ... )
    """

    include: Optional[List[SpanNameFilter]] = Field(
        default=None,
        description="Allow-list: only keep spans matching at least one filter.",
    )
    exclude: Optional[List[SpanNameFilter]] = Field(
        default=None,
        description="Block-list: drop spans matching any filter.",
    )

    model_config = {"validate_assignment": True, "extra": "forbid"}

include class-attribute instance-attribute

include: Optional[List[SpanNameFilter]] = Field(
    default=None,
    description="Allow-list: only keep spans matching at least one filter.",
)

exclude class-attribute instance-attribute

exclude: Optional[List[SpanNameFilter]] = Field(
    default=None,
    description="Block-list: drop spans matching any filter.",
)

TracerConfig

Bases: BaseHoneyHiveConfig

Core tracer configuration with validation.

This class defines the primary configuration parameters for initializing a HoneyHive tracer instance. It inherits common fields from BaseHoneyHiveConfig and adds tracer-specific parameters.

Inherited Fields
  • api_key: HoneyHive API key for authentication
  • project: Deprecated project name (optional; backend infers scope from API key)
  • test_mode: Enable test mode (no data sent to backend)
  • verbose: Enable verbose logging output
Tracer-Specific Fields
  • session_name: Human-readable session identifier
  • source: Source environment identifier
  • server_url: Custom HoneyHive server URL (from HH_API_URL env var)
  • disable_http_tracing: Disable HTTP request tracing (disabled by default)
  • disable_batch: Disable batch processing of spans
Example

config = TracerConfig( ... api_key="hh_1234567890abcdef", ... project="my-llm-project", ... session_name="user-chat-session", ... source="production", ... verbose=True ... ) tracer = HoneyHiveTracer(config=config)

Backwards compatible usage still works:

tracer = HoneyHiveTracer( ... api_key="hh_1234567890abcdef", ... project="my-llm-project", ... verbose=True ... )

Source code in src/honeyhive/config/models/tracer.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
class TracerConfig(BaseHoneyHiveConfig):
    """Core tracer configuration with validation.

    This class defines the primary configuration parameters for initializing
    a HoneyHive tracer instance. It inherits common fields from BaseHoneyHiveConfig
    and adds tracer-specific parameters.

    Inherited Fields:
        - api_key: HoneyHive API key for authentication
        - project: Deprecated project name (optional; backend infers scope from API key)
        - test_mode: Enable test mode (no data sent to backend)
        - verbose: Enable verbose logging output

    Tracer-Specific Fields:
        - session_name: Human-readable session identifier
        - source: Source environment identifier
        - server_url: Custom HoneyHive server URL (from HH_API_URL env var)
        - disable_http_tracing: Disable HTTP request tracing (disabled by default)
        - disable_batch: Disable batch processing of spans

    Example:
        >>> config = TracerConfig(
        ...     api_key="hh_1234567890abcdef",
        ...     project="my-llm-project",
        ...     session_name="user-chat-session",
        ...     source="production",
        ...     verbose=True
        ... )
        >>> tracer = HoneyHiveTracer(config=config)

        # Backwards compatible usage still works:
        >>> tracer = HoneyHiveTracer(
        ...     api_key="hh_1234567890abcdef",
        ...     project="my-llm-project",
        ...     verbose=True
        ... )
    """

    session_name: Optional[str] = Field(  # type: ignore[call-overload]
        None,
        description="Human-readable session identifier",
        examples=["user-chat-session", "batch-processing-job"],
    )

    source: str = Field(  # type: ignore[call-overload,pydantic-alias]
        default="dev",
        description="Source environment identifier",
        validation_alias=AliasChoices("HH_SOURCE", "source"),
        examples=["dev", "staging", "production"],
    )

    server_url: str = Field(  # type: ignore[call-overload,pydantic-alias]
        default="https://api.dp1.us.honeyhive.ai",
        description="Custom HoneyHive server URL",
        validation_alias=AliasChoices("HH_API_URL", "server_url"),
        examples=[
            "https://api.dp1.us.honeyhive.ai",
            "https://custom.honeyhive.com",
        ],
    )

    disable_http_tracing: bool = Field(  # type: ignore[call-overload,pydantic-alias]
        default=True,
        description="Disable HTTP request tracing (disabled by default)",
        validation_alias=AliasChoices(
            "HH_DISABLE_HTTP_TRACING", "disable_http_tracing"
        ),
    )

    disable_batch: bool = Field(  # type: ignore[call-overload,pydantic-alias]
        default=False,
        description="Disable batch processing of spans",
        validation_alias=AliasChoices("HH_DISABLE_BATCH", "disable_batch"),
    )

    disable_tracing: bool = Field(  # type: ignore[call-overload,pydantic-alias]
        default=False,
        description="Disable all tracing functionality",
        validation_alias=AliasChoices("HH_DISABLE_TRACING", "disable_tracing"),
    )

    span_name_filters: Optional[SpanNameFilters] = Field(
        default=None,
        description=(
            "Filter spans by name using include/exclude lists. "
            "Each filter entry specifies a type ('prefix') and value to match. "
            "Excluded spans are dropped before enrichment and export."
        ),
        examples=[
            {"exclude": [{"type": "prefix", "value": "a2a.client.transports.jsonrpc"}]}
        ],
    )

    # OpenTelemetry Span Limits Configuration
    max_attributes: int = Field(  # type: ignore[call-overload,pydantic-alias]
        default=1024,
        description=(
            "Maximum number of attributes per span "
            "(OpenTelemetry default: 128, HoneyHive default: 1024)"
        ),
        validation_alias=AliasChoices("HH_MAX_ATTRIBUTES", "max_attributes"),
        examples=[128, 256, 500, 1024, 2000],
    )

    max_events: int = Field(  # type: ignore[call-overload,pydantic-alias]
        default=1024,
        description=(
            "Maximum number of events per span (matches max_attributes "
            "because events are flattened to pseudo-attributes)"
        ),
        validation_alias=AliasChoices("HH_MAX_EVENTS", "max_events"),
    )

    max_links: int = Field(  # type: ignore[call-overload,pydantic-alias]
        default=128,
        description="Maximum number of links per span",
        validation_alias=AliasChoices("HH_MAX_LINKS", "max_links"),
    )

    max_span_size: int = Field(  # type: ignore[call-overload,pydantic-alias]
        default=10 * 1024 * 1024,  # 10MB default
        description="Maximum total size of span (attributes + events + links) in bytes",
        validation_alias=AliasChoices("HH_MAX_SPAN_SIZE", "max_span_size"),
        examples=[1048576, 5242880, 10485760, 20971520],  # 1MB, 5MB, 10MB, 20MB
    )

    # Core Attribute Preservation Configuration
    preserve_core_attributes: bool = Field(  # type: ignore[pydantic-alias]
        default=True,
        description=(
            "Enable core attribute preservation to prevent FIFO eviction "
            "of critical attributes (session_id, event_type, etc.). When "
            "enabled, re-sets core attributes before span.end() to ensure "
            "they survive eviction. Disable only for debugging or extreme "
            "performance requirements."
        ),
        validation_alias=AliasChoices(
            "HH_PRESERVE_CORE_ATTRIBUTES", "preserve_core_attributes"
        ),
    )

    # Dynamic Cache Configuration - Uses dynamic logic for performance optimization
    cache_enabled: bool = Field(  # type: ignore[call-overload,pydantic-alias]
        default=True,
        description="Enable dynamic caching for performance optimization",
        validation_alias=AliasChoices("HH_CACHE_ENABLED", "cache_enabled"),
    )

    cache_max_size: Optional[int] = Field(  # type: ignore[call-overload,pydantic-alias]
        None,
        description="Maximum cache size per cache type (dynamic sizing if None)",
        validation_alias=AliasChoices("HH_CACHE_MAX_SIZE", "cache_max_size"),
        examples=[1000, 5000, 10000],
    )

    cache_ttl: Optional[float] = Field(  # type: ignore[call-overload,pydantic-alias]
        None,
        description="Cache TTL in seconds (dynamic TTL based on cache type if None)",
        validation_alias=AliasChoices("HH_CACHE_TTL", "cache_ttl"),
        examples=[300.0, 600.0, 3600.0],
    )

    cache_cleanup_interval: Optional[float] = Field(  # type: ignore[call-overload,pydantic-alias]  # pylint: disable=line-too-long
        None,
        description="Cache cleanup interval in seconds (dynamic interval if None)",
        validation_alias=AliasChoices(
            "HH_CACHE_CLEANUP_INTERVAL", "cache_cleanup_interval"
        ),
        examples=[60.0, 120.0, 300.0],
    )

    # Session-related fields (for hybrid approach)
    session_id: Optional[str] = Field(  # type: ignore[call-overload]
        None,
        description="Existing session ID to attach to (must be valid UUID)",
        examples=["550e8400-e29b-41d4-a716-446655440000"],
    )

    inputs: Optional[Dict[str, Any]] = Field(  # type: ignore[call-overload]
        None,
        description="Session input data",
        examples=[{"user_id": "123", "query": "Hello world"}],
    )

    link_carrier: Optional[Dict[str, Any]] = Field(  # type: ignore[call-overload]
        None,
        description="Context propagation carrier for distributed tracing",
        examples=[{"traceparent": "00-...", "baggage": "..."}],
    )

    # Evaluation-related fields (for hybrid approach)
    is_evaluation: bool = Field(default=False, description="Enable evaluation mode")  # type: ignore[call-overload]

    run_id: Optional[str] = Field(  # type: ignore[call-overload]
        None,
        description="Evaluation run identifier",
        examples=["eval-run-123", "experiment-2024-01-15"],
    )

    dataset_id: Optional[str] = Field(  # type: ignore[call-overload]
        None,
        description="Dataset identifier for evaluation",
        examples=["dataset-456", "qa-dataset-v2"],
    )

    datapoint_id: Optional[str] = Field(  # type: ignore[call-overload]
        None,
        description="Specific datapoint identifier",
        examples=["datapoint-789", "question-42"],
    )

    model_config = SettingsConfigDict(
        validate_assignment=True,
        extra="forbid",
        case_sensitive=False,
    )

    @field_validator("server_url", mode="before")
    @classmethod
    def validate_server_url(cls, v: Any) -> str:
        """Validate server URL format with graceful degradation.

        Args:
            v: The server URL to validate

        Returns:
            The validated and normalized server URL, or default if invalid
        """
        if v is None:
            return "https://api.dp1.us.honeyhive.ai"

        validated = _safe_validate_url(
            v,
            "server_url",
            allow_none=False,
            default="https://api.dp1.us.honeyhive.ai",
        )
        # Remove trailing slash for consistency
        return validated.rstrip("/") if validated else "https://api.dp1.us.honeyhive.ai"

    @field_validator("source", mode="before")
    @classmethod
    def validate_source(cls, v: Any) -> str:
        """Validate source environment with graceful degradation.

        Args:
            v: The source environment to validate

        Returns:
            The validated source environment, or "dev" if invalid
        """
        validated = _safe_validate_string(v, "source", allow_none=False, default="dev")
        return validated or "dev"  # Ensure we always return a non-None value

    @field_validator("session_id", mode="before")
    @classmethod
    def validate_session_id(cls, v: Any) -> Optional[str]:
        """Validate session ID format with graceful degradation.

        Args:
            v: The session ID to validate

        Returns:
            The validated and normalized session ID, or None if invalid
        """
        validated = _safe_validate_string(
            v, "session_id", allow_none=True, default=None
        )
        if validated is not None:
            try:
                # Validate UUID format
                uuid.UUID(validated)
                return validated.lower()  # Normalize to lowercase
            except ValueError:
                logger.warning(
                    "Invalid session_id: must be a valid UUID. Using None.",
                    extra={"honeyhive_data": {"session_id": validated}},
                )
                return None
        return validated

    @field_validator("run_id", "dataset_id", "datapoint_id", mode="before")
    @classmethod
    def validate_ids(cls, v: Any) -> Optional[str]:
        """Validate ID fields with graceful degradation.

        Args:
            v: The ID value to validate

        Returns:
            The validated ID, or None if invalid
        """
        return _safe_validate_string(v, "ID field", allow_none=True, default=None)

session_name class-attribute instance-attribute

session_name: Optional[str] = Field(
    None,
    description="Human-readable session identifier",
    examples=["user-chat-session", "batch-processing-job"],
)

source class-attribute instance-attribute

source: str = Field(
    default="dev",
    description="Source environment identifier",
    validation_alias=AliasChoices("HH_SOURCE", "source"),
    examples=["dev", "staging", "production"],
)

server_url class-attribute instance-attribute

server_url: str = Field(
    default="https://api.dp1.us.honeyhive.ai",
    description="Custom HoneyHive server URL",
    validation_alias=AliasChoices(
        "HH_API_URL", "server_url"
    ),
    examples=[
        "https://api.dp1.us.honeyhive.ai",
        "https://custom.honeyhive.com",
    ],
)

disable_http_tracing class-attribute instance-attribute

disable_http_tracing: bool = Field(
    default=True,
    description="Disable HTTP request tracing (disabled by default)",
    validation_alias=AliasChoices(
        "HH_DISABLE_HTTP_TRACING", "disable_http_tracing"
    ),
)

disable_batch class-attribute instance-attribute

disable_batch: bool = Field(
    default=False,
    description="Disable batch processing of spans",
    validation_alias=AliasChoices(
        "HH_DISABLE_BATCH", "disable_batch"
    ),
)

disable_tracing class-attribute instance-attribute

disable_tracing: bool = Field(
    default=False,
    description="Disable all tracing functionality",
    validation_alias=AliasChoices(
        "HH_DISABLE_TRACING", "disable_tracing"
    ),
)

span_name_filters class-attribute instance-attribute

span_name_filters: Optional[SpanNameFilters] = Field(
    default=None,
    description="Filter spans by name using include/exclude lists. Each filter entry specifies a type ('prefix') and value to match. Excluded spans are dropped before enrichment and export.",
    examples=[
        {
            "exclude": [
                {
                    "type": "prefix",
                    "value": "a2a.client.transports.jsonrpc",
                }
            ]
        }
    ],
)

max_attributes class-attribute instance-attribute

max_attributes: int = Field(
    default=1024,
    description="Maximum number of attributes per span (OpenTelemetry default: 128, HoneyHive default: 1024)",
    validation_alias=AliasChoices(
        "HH_MAX_ATTRIBUTES", "max_attributes"
    ),
    examples=[128, 256, 500, 1024, 2000],
)

max_events class-attribute instance-attribute

max_events: int = Field(
    default=1024,
    description="Maximum number of events per span (matches max_attributes because events are flattened to pseudo-attributes)",
    validation_alias=AliasChoices(
        "HH_MAX_EVENTS", "max_events"
    ),
)
max_links: int = Field(
    default=128,
    description="Maximum number of links per span",
    validation_alias=AliasChoices(
        "HH_MAX_LINKS", "max_links"
    ),
)

max_span_size class-attribute instance-attribute

max_span_size: int = Field(
    default=10 * 1024 * 1024,
    description="Maximum total size of span (attributes + events + links) in bytes",
    validation_alias=AliasChoices(
        "HH_MAX_SPAN_SIZE", "max_span_size"
    ),
    examples=[1048576, 5242880, 10485760, 20971520],
)

preserve_core_attributes class-attribute instance-attribute

preserve_core_attributes: bool = Field(
    default=True,
    description="Enable core attribute preservation to prevent FIFO eviction of critical attributes (session_id, event_type, etc.). When enabled, re-sets core attributes before span.end() to ensure they survive eviction. Disable only for debugging or extreme performance requirements.",
    validation_alias=AliasChoices(
        "HH_PRESERVE_CORE_ATTRIBUTES",
        "preserve_core_attributes",
    ),
)

cache_enabled class-attribute instance-attribute

cache_enabled: bool = Field(
    default=True,
    description="Enable dynamic caching for performance optimization",
    validation_alias=AliasChoices(
        "HH_CACHE_ENABLED", "cache_enabled"
    ),
)

cache_max_size class-attribute instance-attribute

cache_max_size: Optional[int] = Field(
    None,
    description="Maximum cache size per cache type (dynamic sizing if None)",
    validation_alias=AliasChoices(
        "HH_CACHE_MAX_SIZE", "cache_max_size"
    ),
    examples=[1000, 5000, 10000],
)

cache_ttl class-attribute instance-attribute

cache_ttl: Optional[float] = Field(
    None,
    description="Cache TTL in seconds (dynamic TTL based on cache type if None)",
    validation_alias=AliasChoices(
        "HH_CACHE_TTL", "cache_ttl"
    ),
    examples=[300.0, 600.0, 3600.0],
)

cache_cleanup_interval class-attribute instance-attribute

cache_cleanup_interval: Optional[float] = Field(
    None,
    description="Cache cleanup interval in seconds (dynamic interval if None)",
    validation_alias=AliasChoices(
        "HH_CACHE_CLEANUP_INTERVAL",
        "cache_cleanup_interval",
    ),
    examples=[60.0, 120.0, 300.0],
)

session_id class-attribute instance-attribute

session_id: Optional[str] = Field(
    None,
    description="Existing session ID to attach to (must be valid UUID)",
    examples=["550e8400-e29b-41d4-a716-446655440000"],
)

inputs class-attribute instance-attribute

inputs: Optional[Dict[str, Any]] = Field(
    None,
    description="Session input data",
    examples=[{"user_id": "123", "query": "Hello world"}],
)
link_carrier: Optional[Dict[str, Any]] = Field(
    None,
    description="Context propagation carrier for distributed tracing",
    examples=[{"traceparent": "00-...", "baggage": "..."}],
)

is_evaluation class-attribute instance-attribute

is_evaluation: bool = Field(
    default=False, description="Enable evaluation mode"
)

run_id class-attribute instance-attribute

run_id: Optional[str] = Field(
    None,
    description="Evaluation run identifier",
    examples=["eval-run-123", "experiment-2024-01-15"],
)

dataset_id class-attribute instance-attribute

dataset_id: Optional[str] = Field(
    None,
    description="Dataset identifier for evaluation",
    examples=["dataset-456", "qa-dataset-v2"],
)

datapoint_id class-attribute instance-attribute

datapoint_id: Optional[str] = Field(
    None,
    description="Specific datapoint identifier",
    examples=["datapoint-789", "question-42"],
)

validate_server_url classmethod

validate_server_url(v: Any) -> str

Validate server URL format with graceful degradation.

Parameters:

Name Type Description Default
v Any

The server URL to validate

required

Returns:

Type Description
str

The validated and normalized server URL, or default if invalid

Source code in src/honeyhive/config/models/tracer.py
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
@field_validator("server_url", mode="before")
@classmethod
def validate_server_url(cls, v: Any) -> str:
    """Validate server URL format with graceful degradation.

    Args:
        v: The server URL to validate

    Returns:
        The validated and normalized server URL, or default if invalid
    """
    if v is None:
        return "https://api.dp1.us.honeyhive.ai"

    validated = _safe_validate_url(
        v,
        "server_url",
        allow_none=False,
        default="https://api.dp1.us.honeyhive.ai",
    )
    # Remove trailing slash for consistency
    return validated.rstrip("/") if validated else "https://api.dp1.us.honeyhive.ai"

validate_source classmethod

validate_source(v: Any) -> str

Validate source environment with graceful degradation.

Parameters:

Name Type Description Default
v Any

The source environment to validate

required

Returns:

Type Description
str

The validated source environment, or "dev" if invalid

Source code in src/honeyhive/config/models/tracer.py
329
330
331
332
333
334
335
336
337
338
339
340
341
@field_validator("source", mode="before")
@classmethod
def validate_source(cls, v: Any) -> str:
    """Validate source environment with graceful degradation.

    Args:
        v: The source environment to validate

    Returns:
        The validated source environment, or "dev" if invalid
    """
    validated = _safe_validate_string(v, "source", allow_none=False, default="dev")
    return validated or "dev"  # Ensure we always return a non-None value

validate_session_id classmethod

validate_session_id(v: Any) -> Optional[str]

Validate session ID format with graceful degradation.

Parameters:

Name Type Description Default
v Any

The session ID to validate

required

Returns:

Type Description
Optional[str]

The validated and normalized session ID, or None if invalid

Source code in src/honeyhive/config/models/tracer.py
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
@field_validator("session_id", mode="before")
@classmethod
def validate_session_id(cls, v: Any) -> Optional[str]:
    """Validate session ID format with graceful degradation.

    Args:
        v: The session ID to validate

    Returns:
        The validated and normalized session ID, or None if invalid
    """
    validated = _safe_validate_string(
        v, "session_id", allow_none=True, default=None
    )
    if validated is not None:
        try:
            # Validate UUID format
            uuid.UUID(validated)
            return validated.lower()  # Normalize to lowercase
        except ValueError:
            logger.warning(
                "Invalid session_id: must be a valid UUID. Using None.",
                extra={"honeyhive_data": {"session_id": validated}},
            )
            return None
    return validated

validate_ids classmethod

validate_ids(v: Any) -> Optional[str]

Validate ID fields with graceful degradation.

Parameters:

Name Type Description Default
v Any

The ID value to validate

required

Returns:

Type Description
Optional[str]

The validated ID, or None if invalid

Source code in src/honeyhive/config/models/tracer.py
370
371
372
373
374
375
376
377
378
379
380
381
@field_validator("run_id", "dataset_id", "datapoint_id", mode="before")
@classmethod
def validate_ids(cls, v: Any) -> Optional[str]:
    """Validate ID fields with graceful degradation.

    Args:
        v: The ID value to validate

    Returns:
        The validated ID, or None if invalid
    """
    return _safe_validate_string(v, "ID field", allow_none=True, default=None)