1"""Observability configuration for Lexigram."""
2
3from __future__ import annotations
4
5from dataclasses import dataclass
6from typing import ClassVar, cast
7
8from lexigram.ai.observability import constants as const
9from lexigram.config import (
10 BaseConfig,
11)
12from lexigram.contracts.core.config import ConfigIssue, Environment
13from lexigram.validation import ConfigDict, Field
14
15
16@dataclass(init=False)
17class ObservabilityConfig(BaseConfig):
18 """Configuration for AI observability.
19
20 Loaded from the ``ai_observability:`` key in application.yaml, with environment
21 variable overrides via ``LEX_AI_OBSERVABILITY__*`` prefix.
22 """
23
24 config_section: ClassVar[str] = "ai_observability"
25
26 model_config: ClassVar[ConfigDict] = cast(
27 "ConfigDict",
28 {
29 "env_prefix": const.ENV_PREFIX,
30 "env_nested_delimiter": const.ENV_NESTED_DELIMITER,
31 "extra": "ignore",
32 },
33 )
34
35 enabled: bool = Field(
36 default=True, description="Master on/off switch for all observability"
37 )
38 metrics_enabled: bool = Field(default=True, description="Enable metrics collection")
39 tracing_enabled: bool = Field(
40 default=True, description="Enable distributed tracing"
41 )
42 health_checks_enabled: bool = Field(
43 default=True,
44 description="Enable background health checking for AI components",
45 )
46 trace_redaction_enabled: bool = Field(
47 default=False,
48 description=(
49 "Redact secret-shaped keys (e.g. token, password, api_key) from "
50 "trace span attributes and audit metadata. Strongly recommended "
51 "for production tracing."
52 ),
53 )
54 trace_max_attribute_length: int = Field(
55 default=0,
56 description=(
57 "Cap on string attribute values written to trace spans, in "
58 "characters. 0 disables the cap."
59 ),
60 )
61
62 def validate_for_environment(
63 self, env: Environment | None = None
64 ) -> list[ConfigIssue]:
65 """Check config is safe for the target environment."""
66 issues: list[ConfigIssue] = []
67
68 if env == Environment.PRODUCTION:
69 if not self.tracing_enabled:
70 issues.append(
71 ConfigIssue(
72 severity="warning",
73 field="tracing_enabled",
74 message="Distributed tracing disabled in production",
75 suggestion=(
76 f"Set {const.ENV_PREFIX}TRACING_ENABLED=true for "
77 "production observability."
78 ),
79 )
80 )
81 if not self.metrics_enabled:
82 issues.append(
83 ConfigIssue(
84 severity="warning",
85 field="metrics_enabled",
86 message="Metrics collection disabled in production",
87 suggestion=(
88 f"Set {const.ENV_PREFIX}METRICS_ENABLED=true to collect "
89 "operational metrics."
90 ),
91 )
92 )
93 if self.tracing_enabled and not self.trace_redaction_enabled:
94 issues.append(
95 ConfigIssue(
96 severity="warning",
97 field="trace_redaction_enabled",
98 message=(
99 "Trace payload redaction disabled in production; "
100 "secrets in tool arguments, agent actions, or "
101 "retriever queries may be exported to the tracing "
102 "backend."
103 ),
104 suggestion=(
105 f"Set {const.ENV_PREFIX}TRACE_REDACTION_ENABLED=true "
106 "to redact secret-shaped keys from trace spans."
107 ),
108 )
109 )
110
111 return issues
112
113
114__all__ = ["ObservabilityConfig"]