Skip to content

honeyhive.config

HoneyHive SDK Per-Instance Configuration Management.

This module provides per-instance configuration using Pydantic models for the multi-instance architecture. Each tracer instance has its own configuration that is thread-safe and process-safe.

Architecture

  • models/: Per-instance Pydantic models for configuration and validation
  • utils: Configuration merging utilities for backwards compatibility

Usage Patterns

>>> from honeyhive.config import TracerConfig, SessionConfig
>>> config = TracerConfig(api_key="...", project="...", verbose=True)
>>> tracer = HoneyHiveTracer(config=config)

Individual Parameters (Backwards Compatible)

>>> tracer = HoneyHiveTracer(api_key="...", project="...", verbose=True)

Multiple Independent Tracers

>>> config1 = TracerConfig(api_key="key1", project="project1")
>>> config2 = TracerConfig(api_key="key2", project="project2")
>>> tracer1 = HoneyHiveTracer(config=config1)  # Independent instance
>>> tracer2 = HoneyHiveTracer(config=config2)  # Independent instance

Benefits

  • Multi-Instance Support: Each tracer has independent configuration
  • Thread Safety: No shared global state between instances
  • Process Safety: Works correctly across process boundaries
  • Type Safety: Pydantic validation with clear error messages
  • Environment Integration: Automatic loading from HH_* environment variables
  • Backwards Compatibility: Individual parameters still work

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

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

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)

create_unified_config

create_unified_config(
    config: Optional[TracerConfig] = None,
    session_config: Optional[SessionConfig] = None,
    evaluation_config: Optional[EvaluationConfig] = None,
    **individual_params: Any
) -> DotDict

Create a unified nested configuration from all config sources.

This function merges all configuration types (TracerConfig, SessionConfig, EvaluationConfig, HTTPClientConfig, OTLPConfig, APIClientConfig, ExperimentConfig) into a nested DotDict structure that eliminates key collisions and provides clear namespacing for different config types.

Structure
  • TracerConfig fields at root level (most commonly accessed)
  • Specialized configs nested: config.session., config.evaluation., etc.
  • For colliding fields: More specific configs override base configs at root

Priority Order (for fields that exist in multiple configs): 1. individual_params (highest - backwards compatibility) 2. SessionConfig (session-specific overrides) 3. EvaluationConfig (evaluation-specific overrides) 4. TracerConfig (base defaults)

Parameters:

Name Type Description Default
config Optional[TracerConfig]

Core tracer configuration object

None
session_config Optional[SessionConfig]

Session-specific configuration object

None
evaluation_config Optional[EvaluationConfig]

Evaluation-specific configuration object

None
**individual_params Any

Individual parameter overrides for backwards compatibility

{}

Returns:

Name Type Description
DotDict DotDict

Unified configuration with nested structure accessible

DotDict

via both config.field_name and config['field_name'] patterns.

Example

Basic usage

config = TracerConfig(api_key="key", project="proj") unified = create_unified_config(config=config) unified.api_key # "key" (TracerConfig at root) unified.http.timeout # 30.0 (HTTPClientConfig nested)

Field collision handling (SessionConfig overrides TracerConfig)

tracer_cfg = TracerConfig(api_key="key1", session_id=None) session_cfg = SessionConfig(session_id="550e8400-...") unified = create_unified_config( ... config=tracer_cfg, session_config=session_cfg ... ) unified.session_id # "550e8400-..." (from SessionConfig) unified.session.session_id # Also "550e8400-..."

Source code in src/honeyhive/config/utils.py
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
def create_unified_config(
    config: Optional[TracerConfig] = None,
    session_config: Optional[SessionConfig] = None,
    evaluation_config: Optional[EvaluationConfig] = None,
    **individual_params: Any,
) -> DotDict:
    """Create a unified nested configuration from all config sources.

    This function merges all configuration types (TracerConfig, SessionConfig,
    EvaluationConfig, HTTPClientConfig, OTLPConfig, APIClientConfig, ExperimentConfig)
    into a nested DotDict structure that eliminates key collisions and provides
    clear namespacing for different config types.

    Structure:
        - TracerConfig fields at root level (most commonly accessed)
        - Specialized configs nested: config.session.*, config.evaluation.*, etc.
        - For colliding fields: More specific configs override base configs at root

    Priority Order (for fields that exist in multiple configs):
        1. individual_params (highest - backwards compatibility)
        2. SessionConfig (session-specific overrides)
        3. EvaluationConfig (evaluation-specific overrides)
        4. TracerConfig (base defaults)

    Args:
        config: Core tracer configuration object
        session_config: Session-specific configuration object
        evaluation_config: Evaluation-specific configuration object
        **individual_params: Individual parameter overrides for backwards compatibility

    Returns:
        DotDict: Unified configuration with nested structure accessible
        via both config.field_name and config['field_name'] patterns.

    Example:
        >>> # Basic usage
        >>> config = TracerConfig(api_key="key", project="proj")
        >>> unified = create_unified_config(config=config)
        >>> unified.api_key  # "key" (TracerConfig at root)
        >>> unified.http.timeout  # 30.0 (HTTPClientConfig nested)

        >>> # Field collision handling (SessionConfig overrides TracerConfig)
        >>> tracer_cfg = TracerConfig(api_key="key1", session_id=None)
        >>> session_cfg = SessionConfig(session_id="550e8400-...")
        >>> unified = create_unified_config(
        ...     config=tracer_cfg, session_config=session_cfg
        ... )
        >>> unified.session_id  # "550e8400-..." (from SessionConfig)
        >>> unified.session.session_id  # Also "550e8400-..."
    """
    # First merge the main configs with individual params
    tracer_config, session_config_merged, evaluation_config_merged = (
        merge_configs_with_params(
            config=config,
            session_config=session_config,
            evaluation_config=evaluation_config,
            **individual_params,
        )
    )

    # Create unified result with nested structure
    unified = DotDict()

    # 1. TracerConfig fields at root level (most commonly accessed)
    if tracer_config:
        unified.update(tracer_config.model_dump())

    # 2. Create nested configs to avoid key collisions
    # HTTP Client Configuration
    default_http_config = HTTPClientConfig()
    unified.http = DotDict(default_http_config.model_dump())

    # OTLP Configuration
    default_otlp_config = OTLPConfig()
    unified.otlp = DotDict(default_otlp_config.model_dump())

    # API Client Configuration
    default_api_config = APIClientConfig()
    unified.api = DotDict(default_api_config.model_dump())

    # Experiment Configuration
    default_experiment_config = ExperimentConfig()
    unified.experiment = DotDict(default_experiment_config.model_dump())

    # Session Configuration (nested to avoid collisions with TracerConfig)
    if session_config_merged:
        unified.session = DotDict(session_config_merged.model_dump())
    else:
        unified.session = DotDict()

    # Evaluation Configuration (nested to avoid collisions with TracerConfig)
    if evaluation_config_merged:
        unified.evaluation = DotDict(evaluation_config_merged.model_dump())
    else:
        unified.evaluation = DotDict()

    # 2.5. Promote specialized config values to root level for colliding fields
    # Priority: SessionConfig/EvaluationConfig > TracerConfig (more specific wins)
    # This fixes the field collision bug where SessionConfig.session_id was hidden
    # Only promote when specialized configs were explicitly provided (not defaults)

    # Helper to determine if a field was explicitly set vs using default
    def was_field_explicitly_set(config_obj: Any, field_name: str) -> bool:
        """Check if a field was explicitly set by user or is just a default."""
        if config_obj is None:
            return False
        # Check if the field exists in the original config object's __dict__
        # or __pydantic_fields_set__ (Pydantic v2 tracks explicitly set fields)
        if hasattr(config_obj, "__pydantic_fields_set__"):
            return field_name in config_obj.__pydantic_fields_set__
        # Fallback: assume explicitly set if value differs from field default
        if hasattr(type(config_obj), "model_fields"):
            field_info = type(config_obj).model_fields.get(field_name)
            if field_info and hasattr(field_info, "default"):
                return bool(getattr(config_obj, field_name, None) != field_info.default)
        return True  # Conservative: promote if we can't determine

    # Promote EvaluationConfig values to root (lower priority)
    # Only if evaluation_config was actually provided by user
    if evaluation_config is not None and evaluation_config_merged:
        for field in EvaluationConfig.model_fields.keys():
            nested_value = unified.evaluation.get(field)
            # Only promote if field was explicitly set and value is not None
            if nested_value is not None and was_field_explicitly_set(
                evaluation_config, field
            ):
                unified[field] = nested_value

    # Promote SessionConfig values to root (higher priority - overrides evaluation)
    # Only if session_config was actually provided by user
    if session_config is not None and session_config_merged:
        for field in SessionConfig.model_fields.keys():
            nested_value = unified.session.get(field)
            # Only promote if field was explicitly set and value is not None
            if nested_value is not None and was_field_explicitly_set(
                session_config, field
            ):
                unified[field] = nested_value

    # 3. Handle individual params - route to appropriate nested config or root
    # AND promote SessionConfig/EvaluationConfig params to root
    # (for field collision handling)
    for param, value in individual_params.items():
        # Route params to appropriate nested config based on known field sets
        # Use try/except for safe field checking
        try:
            if hasattr(SessionConfig, "model_fields") and param in dict(
                SessionConfig.model_fields
            ):
                unified.session[param] = value
                # Also promote to root
                # (SessionConfig has highest priority for colliding fields)
                unified[param] = value
                continue
        except (AttributeError, TypeError):
            pass

        try:
            if hasattr(EvaluationConfig, "model_fields") and param in dict(
                EvaluationConfig.model_fields
            ):
                unified.evaluation[param] = value
                # Also promote to root
                # (EvaluationConfig overrides TracerConfig for colliding fields)
                unified[param] = value
                continue
        except (AttributeError, TypeError):
            pass

        try:
            if hasattr(HTTPClientConfig, "model_fields") and param in dict(
                HTTPClientConfig.model_fields
            ):
                unified.http[param] = value
                continue
        except (AttributeError, TypeError):
            pass

        try:
            if hasattr(OTLPConfig, "model_fields") and param in dict(
                OTLPConfig.model_fields
            ):
                unified.otlp[param] = value
                continue
        except (AttributeError, TypeError):
            pass

        try:
            if hasattr(APIClientConfig, "model_fields") and param in dict(
                APIClientConfig.model_fields
            ):
                unified.api[param] = value
                continue
        except (AttributeError, TypeError):
            pass

        try:
            if hasattr(ExperimentConfig, "model_fields") and param in dict(
                ExperimentConfig.model_fields
            ):
                unified.experiment[param] = value
                continue
        except (AttributeError, TypeError):
            pass

        # TracerConfig fields or unknown params go to root
        unified[param] = value

    return unified

merge_configs_with_params

merge_configs_with_params(
    config: Optional[TracerConfig] = None,
    session_config: Optional[SessionConfig] = None,
    evaluation_config: Optional[EvaluationConfig] = None,
    **individual_params: Any
) -> Tuple[TracerConfig, SessionConfig, EvaluationConfig]

Merge config objects with individual parameters for backwards compatibility.

This function enables the hybrid approach where both config objects and individual parameters can be used. Individual parameters take precedence over config object values to maintain backwards compatibility.

Parameters:

Name Type Description Default
config Optional[TracerConfig]

Core tracer configuration object

None
session_config Optional[SessionConfig]

Session-specific configuration object

None
evaluation_config Optional[EvaluationConfig]

Evaluation-specific configuration object

None
**individual_params Any

Individual parameter overrides for backwards compatibility

{}

Returns:

Type Description
Tuple[TracerConfig, SessionConfig, EvaluationConfig]

Tuple of (merged_tracer_config, merged_session_config, merged_evaluation_config)

Example

Using config objects

tracer_cfg = TracerConfig(api_key="hh_123", verbose=True) session_cfg = SessionConfig(inputs={"user": "123"}) merged = merge_configs_with_params( ... config=tracer_cfg, ... session_config=session_cfg ... )

Using individual parameters (backwards compatible)

merged = merge_configs_with_params( ... api_key="hh_123", ... verbose=True, ... inputs={"user": "123"} ... )

Mixed usage (individual params override config)

merged = merge_configs_with_params( ... config=tracer_cfg, # has verbose=True ... verbose=False # overrides config ... )

Source code in src/honeyhive/config/utils.py
 25
 26
 27
 28
 29
 30
 31
 32
 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
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
def merge_configs_with_params(
    config: Optional[TracerConfig] = None,
    session_config: Optional[SessionConfig] = None,
    evaluation_config: Optional[EvaluationConfig] = None,
    **individual_params: Any,
) -> Tuple[TracerConfig, SessionConfig, EvaluationConfig]:
    """Merge config objects with individual parameters for backwards compatibility.

    This function enables the hybrid approach where both config objects and
    individual parameters can be used. Individual parameters take precedence
    over config object values to maintain backwards compatibility.

    Args:
        config: Core tracer configuration object
        session_config: Session-specific configuration object
        evaluation_config: Evaluation-specific configuration object
        **individual_params: Individual parameter overrides for backwards compatibility

    Returns:
        Tuple of (merged_tracer_config, merged_session_config, merged_evaluation_config)

    Example:
        >>> # Using config objects
        >>> tracer_cfg = TracerConfig(api_key="hh_123", verbose=True)
        >>> session_cfg = SessionConfig(inputs={"user": "123"})
        >>> merged = merge_configs_with_params(
        ...     config=tracer_cfg,
        ...     session_config=session_cfg
        ... )

        >>> # Using individual parameters (backwards compatible)
        >>> merged = merge_configs_with_params(
        ...     api_key="hh_123",
        ...     verbose=True,
        ...     inputs={"user": "123"}
        ... )

        >>> # Mixed usage (individual params override config)
        >>> merged = merge_configs_with_params(
        ...     config=tracer_cfg,  # has verbose=True
        ...     verbose=False       # overrides config
        ... )
    """
    # Start with defaults or provided configs
    tracer_config = config or TracerConfig()
    session_cfg = session_config or SessionConfig()
    eval_cfg = evaluation_config or EvaluationConfig()

    # Override tracer config with individual parameters
    tracer_overrides = {}
    for field in TracerConfig.model_fields.keys():
        if field in individual_params:
            tracer_overrides[field] = individual_params[field]

    if tracer_overrides:
        tracer_config = tracer_config.model_copy(update=tracer_overrides)

    # Override session config with individual parameters
    session_overrides = {}
    for field in SessionConfig.model_fields.keys():
        if field in individual_params:
            session_overrides[field] = individual_params[field]

    if session_overrides:
        session_cfg = session_cfg.model_copy(update=session_overrides)

    # Override evaluation config with individual parameters
    eval_overrides = {}
    for field in EvaluationConfig.model_fields.keys():
        if field in individual_params:
            eval_overrides[field] = individual_params[field]

    if eval_overrides:
        eval_cfg = eval_cfg.model_copy(update=eval_overrides)

    return tracer_config, session_cfg, eval_cfg