traccia 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
traccia/config.py DELETED
@@ -1,693 +0,0 @@
1
- """Configuration management with Pydantic models and validation."""
2
-
3
- from __future__ import annotations
4
-
5
- import os
6
- from pathlib import Path
7
- from typing import Any, Dict, Optional, Literal
8
- from pydantic import BaseModel, Field, field_validator, model_validator, HttpUrl
9
-
10
- from traccia.errors import ConfigError, ValidationError
11
-
12
-
13
- # Environment variable mapping
14
- ENV_VAR_MAPPING = {
15
- # Tracing config
16
- "api_key": ["TRACCIA_API_KEY", "AGENT_DASHBOARD_API_KEY"],
17
- "endpoint": ["TRACCIA_ENDPOINT", "AGENT_DASHBOARD_ENDPOINT"],
18
- "sample_rate": ["TRACCIA_SAMPLE_RATE", "AGENT_DASHBOARD_SAMPLE_RATE"],
19
- "auto_start_trace": ["TRACCIA_AUTO_START_TRACE", "AGENT_DASHBOARD_AUTO_START_TRACE"],
20
- "auto_trace_name": ["TRACCIA_AUTO_TRACE_NAME"],
21
- "use_otlp": ["TRACCIA_USE_OTLP"],
22
- "service_name": ["TRACCIA_SERVICE_NAME"],
23
-
24
- # Exporter config
25
- "enable_console": ["TRACCIA_ENABLE_CONSOLE", "AGENT_DASHBOARD_ENABLE_CONSOLE_EXPORTER"],
26
- "enable_file": ["TRACCIA_ENABLE_FILE", "AGENT_DASHBOARD_ENABLE_FILE_EXPORTER"],
27
- "file_exporter_path": ["TRACCIA_FILE_PATH"],
28
- "reset_trace_file": ["TRACCIA_RESET_TRACE_FILE"],
29
-
30
- # Instrumentation config
31
- "enable_patching": ["TRACCIA_ENABLE_PATCHING", "AGENT_DASHBOARD_ENABLE_PATCHING"],
32
- "enable_token_counting": ["TRACCIA_ENABLE_TOKEN_COUNTING", "AGENT_DASHBOARD_ENABLE_TOKEN_COUNTING"],
33
- "enable_costs": ["TRACCIA_ENABLE_COSTS", "AGENT_DASHBOARD_ENABLE_COSTS"],
34
- "auto_instrument_tools": ["TRACCIA_AUTO_INSTRUMENT_TOOLS"],
35
- "max_tool_spans": ["TRACCIA_MAX_TOOL_SPANS"],
36
- "max_span_depth": ["TRACCIA_MAX_SPAN_DEPTH"],
37
-
38
- # Rate limiting & Batching
39
- "max_spans_per_second": ["TRACCIA_MAX_SPANS_PER_SECOND"],
40
- "max_queue_size": ["TRACCIA_MAX_QUEUE_SIZE"],
41
- "max_block_ms": ["TRACCIA_MAX_BLOCK_MS"],
42
- "max_export_batch_size": ["TRACCIA_MAX_EXPORT_BATCH_SIZE"],
43
- "schedule_delay_millis": ["TRACCIA_SCHEDULE_DELAY_MILLIS"],
44
-
45
- # Runtime metadata
46
- "session_id": ["TRACCIA_SESSION_ID"],
47
- "user_id": ["TRACCIA_USER_ID"],
48
- "tenant_id": ["TRACCIA_TENANT_ID"],
49
- "project_id": ["TRACCIA_PROJECT_ID"],
50
- "agent_id": ["TRACCIA_AGENT_ID", "AGENT_DASHBOARD_AGENT_ID"],
51
-
52
- # Logging
53
- "debug": ["TRACCIA_DEBUG"],
54
- "enable_span_logging": ["TRACCIA_ENABLE_SPAN_LOGGING"],
55
-
56
- # Advanced
57
- "attr_truncation_limit": ["TRACCIA_ATTR_TRUNCATION_LIMIT"],
58
- }
59
-
60
-
61
- class TracingConfig(BaseModel):
62
- """Tracing configuration section."""
63
-
64
- api_key: Optional[str] = Field(
65
- default=None,
66
- description="API key for authentication (required for SaaS, optional for open-source)"
67
- )
68
- endpoint: Optional[str] = Field(
69
- default=None,
70
- description="Endpoint URL for trace ingestion"
71
- )
72
- sample_rate: float = Field(
73
- default=1.0,
74
- ge=0.0,
75
- le=1.0,
76
- description="Sampling rate (0.0 to 1.0)"
77
- )
78
- auto_start_trace: bool = Field(
79
- default=True,
80
- description="Automatically start a root trace on init"
81
- )
82
- auto_trace_name: str = Field(
83
- default="root",
84
- description="Name for the auto-started root trace"
85
- )
86
- use_otlp: bool = Field(
87
- default=True,
88
- description="Use OTLP exporter (set to false for console/file exporters)"
89
- )
90
- service_name: Optional[str] = Field(
91
- default=None,
92
- description="Service name for the application"
93
- )
94
-
95
-
96
- class ExporterConfig(BaseModel):
97
- """Exporter configuration section."""
98
-
99
- enable_console: bool = Field(
100
- default=False,
101
- description="Enable console exporter for debugging"
102
- )
103
- enable_file: bool = Field(
104
- default=False,
105
- description="Enable file exporter to write traces to local file"
106
- )
107
- file_exporter_path: str = Field(
108
- default="traces.jsonl",
109
- description="File path for file exporter"
110
- )
111
- reset_trace_file: bool = Field(
112
- default=False,
113
- description="Reset/clear trace file on initialization"
114
- )
115
-
116
- @model_validator(mode='after')
117
- def check_single_exporter(self) -> 'ExporterConfig':
118
- """Ensure only one exporter is enabled at a time."""
119
- enabled_count = sum([
120
- self.enable_console,
121
- self.enable_file,
122
- ])
123
- if enabled_count > 1:
124
- raise ValidationError(
125
- "Only one exporter can be enabled at a time. "
126
- "Choose either console or file exporter (OTLP is controlled by use_otlp in tracing section).",
127
- details={
128
- "enable_console": self.enable_console,
129
- "enable_file": self.enable_file,
130
- }
131
- )
132
- return self
133
-
134
-
135
- class InstrumentationConfig(BaseModel):
136
- """Instrumentation configuration section."""
137
-
138
- enable_patching: bool = Field(
139
- default=True,
140
- description="Auto-patch popular libraries (OpenAI, Anthropic, requests)"
141
- )
142
- enable_token_counting: bool = Field(
143
- default=True,
144
- description="Count tokens for LLM calls"
145
- )
146
- enable_costs: bool = Field(
147
- default=True,
148
- description="Calculate costs for LLM calls"
149
- )
150
- auto_instrument_tools: bool = Field(
151
- default=False,
152
- description="Automatically instrument tool calls"
153
- )
154
- max_tool_spans: int = Field(
155
- default=100,
156
- gt=0,
157
- description="Maximum number of tool spans to create"
158
- )
159
- max_span_depth: int = Field(
160
- default=10,
161
- gt=0,
162
- description="Maximum depth of nested spans"
163
- )
164
-
165
-
166
- class RateLimitConfig(BaseModel):
167
- """Rate limiting and batching configuration section."""
168
-
169
- max_spans_per_second: Optional[float] = Field(
170
- default=None,
171
- gt=0,
172
- description="Maximum spans per second (None = unlimited)"
173
- )
174
- max_queue_size: int = Field(
175
- default=5000,
176
- gt=0,
177
- description="Maximum queue size for buffered spans"
178
- )
179
- max_block_ms: int = Field(
180
- default=100,
181
- ge=0,
182
- description="Maximum milliseconds to block before dropping spans"
183
- )
184
- max_export_batch_size: int = Field(
185
- default=512,
186
- gt=0,
187
- description="Maximum number of spans in a single export batch"
188
- )
189
- schedule_delay_millis: int = Field(
190
- default=5000,
191
- gt=0,
192
- description="Delay in milliseconds between export batches"
193
- )
194
-
195
-
196
- class LoggingConfig(BaseModel):
197
- """Logging configuration section."""
198
-
199
- debug: bool = Field(
200
- default=False,
201
- description="Enable debug logging"
202
- )
203
- enable_span_logging: bool = Field(
204
- default=False,
205
- description="Enable span-level logging for debugging"
206
- )
207
-
208
-
209
- class RuntimeConfig(BaseModel):
210
- """Runtime metadata configuration section."""
211
-
212
- session_id: Optional[str] = Field(
213
- default=None,
214
- description="Session identifier for grouping traces"
215
- )
216
- user_id: Optional[str] = Field(
217
- default=None,
218
- description="User identifier for the current session"
219
- )
220
- tenant_id: Optional[str] = Field(
221
- default=None,
222
- description="Tenant/organization identifier"
223
- )
224
- project_id: Optional[str] = Field(
225
- default=None,
226
- description="Project identifier"
227
- )
228
- agent_id: Optional[str] = Field(
229
- default=None,
230
- description="Agent identifier for the current session"
231
- )
232
-
233
-
234
- class AdvancedConfig(BaseModel):
235
- """Advanced configuration options."""
236
-
237
- attr_truncation_limit: Optional[int] = Field(
238
- default=None,
239
- gt=0,
240
- description="Maximum length for attribute values (None = no limit)"
241
- )
242
-
243
-
244
- class TracciaConfig(BaseModel):
245
- """
246
- Complete Traccia SDK configuration.
247
-
248
- This model validates and merges configuration from multiple sources:
249
- 1. Config file (traccia.toml)
250
- 2. Environment variables
251
- 3. Explicit parameters
252
- """
253
-
254
- tracing: TracingConfig = Field(default_factory=TracingConfig)
255
- exporters: ExporterConfig = Field(default_factory=ExporterConfig)
256
- instrumentation: InstrumentationConfig = Field(default_factory=InstrumentationConfig)
257
- rate_limiting: RateLimitConfig = Field(default_factory=RateLimitConfig)
258
- runtime: RuntimeConfig = Field(default_factory=RuntimeConfig)
259
- logging: LoggingConfig = Field(default_factory=LoggingConfig)
260
- advanced: AdvancedConfig = Field(default_factory=AdvancedConfig)
261
-
262
- @model_validator(mode='after')
263
- def validate_complete_config(self) -> 'TracciaConfig':
264
- """Validate the complete configuration for conflicts."""
265
- # If OTLP is disabled, at least one other exporter must be enabled
266
- if not self.tracing.use_otlp:
267
- if not (self.exporters.enable_console or self.exporters.enable_file):
268
- raise ConfigError(
269
- "When use_otlp is false, you must enable either console or file exporter.",
270
- details={
271
- "use_otlp": self.tracing.use_otlp,
272
- "enable_console": self.exporters.enable_console,
273
- "enable_file": self.exporters.enable_file,
274
- }
275
- )
276
-
277
- return self
278
-
279
- def to_flat_dict(self) -> Dict[str, Any]:
280
- """Convert to flat dictionary for backward compatibility."""
281
- return {
282
- # Tracing
283
- "api_key": self.tracing.api_key,
284
- "endpoint": self.tracing.endpoint,
285
- "sample_rate": self.tracing.sample_rate,
286
- "auto_start_trace": self.tracing.auto_start_trace,
287
- "auto_trace_name": self.tracing.auto_trace_name,
288
- "use_otlp": self.tracing.use_otlp,
289
- "service_name": self.tracing.service_name,
290
- # Exporters
291
- "enable_console": self.exporters.enable_console,
292
- "enable_file": self.exporters.enable_file,
293
- "file_exporter_path": self.exporters.file_exporter_path,
294
- "reset_trace_file": self.exporters.reset_trace_file,
295
- # Instrumentation
296
- "enable_patching": self.instrumentation.enable_patching,
297
- "enable_token_counting": self.instrumentation.enable_token_counting,
298
- "enable_costs": self.instrumentation.enable_costs,
299
- "auto_instrument_tools": self.instrumentation.auto_instrument_tools,
300
- "max_tool_spans": self.instrumentation.max_tool_spans,
301
- "max_span_depth": self.instrumentation.max_span_depth,
302
- # Rate limiting & Batching
303
- "max_spans_per_second": self.rate_limiting.max_spans_per_second,
304
- "max_queue_size": self.rate_limiting.max_queue_size,
305
- "max_block_ms": self.rate_limiting.max_block_ms,
306
- "max_export_batch_size": self.rate_limiting.max_export_batch_size,
307
- "schedule_delay_millis": self.rate_limiting.schedule_delay_millis,
308
- # Runtime
309
- "session_id": self.runtime.session_id,
310
- "user_id": self.runtime.user_id,
311
- "tenant_id": self.runtime.tenant_id,
312
- "project_id": self.runtime.project_id,
313
- "agent_id": self.runtime.agent_id,
314
- # Logging
315
- "debug": self.logging.debug,
316
- "enable_span_logging": self.logging.enable_span_logging,
317
- # Advanced
318
- "attr_truncation_limit": self.advanced.attr_truncation_limit,
319
- }
320
-
321
-
322
- def load_dotenv(path: str = ".env") -> None:
323
- """Minimal .env loader (no external dependency)."""
324
- if not os.path.exists(path):
325
- return
326
- try:
327
- with open(path, "r", encoding="utf-8") as f:
328
- for line in f:
329
- line = line.strip()
330
- if not line or line.startswith("#"):
331
- continue
332
- if "=" not in line:
333
- continue
334
- key, value = line.split("=", 1)
335
- key, value = key.strip(), value.strip().strip("\"'")
336
- if key and key not in os.environ:
337
- os.environ[key] = value
338
- except Exception:
339
- # Fail silently; this loader is best-effort.
340
- return
341
-
342
-
343
- def find_config_file() -> Optional[str]:
344
- """
345
- Find traccia.toml config file in standard locations.
346
-
347
- Lookup order:
348
- 1. ./traccia.toml (current directory)
349
- 2. ~/.traccia/config.toml (user home)
350
-
351
- Returns:
352
- Path to config file if found, None otherwise
353
- """
354
- # Check current directory
355
- cwd_config = Path.cwd() / "traccia.toml"
356
- if cwd_config.exists():
357
- return str(cwd_config)
358
-
359
- # Check user home directory
360
- home_config = Path.home() / ".traccia" / "config.toml"
361
- if home_config.exists():
362
- return str(home_config)
363
-
364
- return None
365
-
366
-
367
- def load_toml_config(path: str) -> Dict[str, Any]:
368
- """
369
- Load configuration from a TOML file.
370
-
371
- Args:
372
- path: Path to the TOML config file
373
-
374
- Returns:
375
- Dictionary with nested config structure
376
- """
377
- if not os.path.exists(path):
378
- return {}
379
-
380
- try:
381
- # Try to import tomli for Python 3.11+, fall back to toml
382
- try:
383
- import tomli as toml_lib
384
- with open(path, "rb") as f:
385
- data = toml_lib.load(f)
386
- except ImportError:
387
- try:
388
- import toml as toml_lib # type: ignore
389
- with open(path, "r", encoding="utf-8") as f:
390
- data = toml_lib.load(f)
391
- except ImportError:
392
- raise ConfigError(
393
- "No TOML library available. Install tomli or toml: pip install tomli"
394
- )
395
-
396
- return data
397
-
398
- except Exception as e:
399
- if isinstance(e, ConfigError):
400
- raise
401
- raise ConfigError(f"Failed to load config file: {e}")
402
-
403
-
404
- def get_env_value(config_key: str) -> Optional[str]:
405
- """
406
- Get environment variable value for a config key.
407
-
408
- Tries multiple environment variable names in order of preference.
409
- """
410
- env_vars = ENV_VAR_MAPPING.get(config_key, [])
411
- for env_var in env_vars:
412
- value = os.getenv(env_var)
413
- if value is not None:
414
- return value
415
- return None
416
-
417
-
418
- def load_config_from_env(flat: bool = False) -> Dict[str, Any]:
419
- """
420
- Load configuration from environment variables.
421
-
422
- Args:
423
- flat: If True, return flat dictionary for backward compatibility
424
-
425
- Returns:
426
- Dictionary of config values from environment (nested structure by default)
427
- """
428
- env_config = {
429
- "tracing": {},
430
- "exporters": {},
431
- "instrumentation": {},
432
- "rate_limiting": {},
433
- "runtime": {},
434
- "logging": {},
435
- "advanced": {},
436
- }
437
-
438
- # Tracing section
439
- for key in ["api_key", "endpoint", "sample_rate", "auto_start_trace", "auto_trace_name", "use_otlp", "service_name"]:
440
- value = get_env_value(key)
441
- if value is not None:
442
- # Convert string to appropriate type
443
- if key in ["auto_start_trace", "use_otlp"]:
444
- env_config["tracing"][key] = value.lower() in ("true", "1", "yes")
445
- elif key == "sample_rate":
446
- try:
447
- env_config["tracing"][key] = float(value)
448
- except ValueError:
449
- raise ConfigError(f"Invalid sample_rate value: {value}. Must be a float between 0.0 and 1.0.")
450
- else:
451
- env_config["tracing"][key] = value
452
-
453
- # Exporters section
454
- for key in ["enable_console", "enable_file", "file_exporter_path", "reset_trace_file"]:
455
- value = get_env_value(key)
456
- if value is not None:
457
- if key in ["enable_console", "enable_file", "reset_trace_file"]:
458
- env_config["exporters"][key] = value.lower() in ("true", "1", "yes")
459
- else:
460
- env_config["exporters"][key] = value
461
-
462
- # Instrumentation section
463
- for key in ["enable_patching", "enable_token_counting", "enable_costs", "auto_instrument_tools"]:
464
- value = get_env_value(key)
465
- if value is not None:
466
- env_config["instrumentation"][key] = value.lower() in ("true", "1", "yes")
467
-
468
- for key in ["max_tool_spans", "max_span_depth"]:
469
- value = get_env_value(key)
470
- if value is not None:
471
- try:
472
- env_config["instrumentation"][key] = int(value)
473
- except ValueError:
474
- raise ConfigError(f"Invalid {key} value: {value}. Must be an integer.")
475
-
476
- # Rate limiting section
477
- for key in ["max_spans_per_second"]:
478
- value = get_env_value(key)
479
- if value is not None:
480
- try:
481
- env_config["rate_limiting"][key] = float(value) if value else None
482
- except ValueError:
483
- raise ConfigError(f"Invalid {key} value: {value}. Must be a number.")
484
-
485
- for key in ["max_queue_size", "max_block_ms", "max_export_batch_size", "schedule_delay_millis"]:
486
- value = get_env_value(key)
487
- if value is not None:
488
- try:
489
- env_config["rate_limiting"][key] = int(value)
490
- except ValueError:
491
- raise ConfigError(f"Invalid {key} value: {value}. Must be a number.")
492
-
493
- # Runtime section
494
- for key in ["session_id", "user_id", "tenant_id", "project_id", "agent_id"]:
495
- value = get_env_value(key)
496
- if value is not None:
497
- env_config["runtime"][key] = value
498
-
499
- # Logging section
500
- for key in ["debug", "enable_span_logging"]:
501
- value = get_env_value(key)
502
- if value is not None:
503
- env_config["logging"][key] = value.lower() in ("true", "1", "yes")
504
-
505
- # Advanced section
506
- value = get_env_value("attr_truncation_limit")
507
- if value is not None:
508
- try:
509
- env_config["advanced"]["attr_truncation_limit"] = int(value)
510
- except ValueError:
511
- raise ConfigError(f"Invalid attr_truncation_limit value: {value}. Must be an integer.")
512
-
513
- # Remove empty sections
514
- nested_result = {k: v for k, v in env_config.items() if v}
515
-
516
- # Flatten if requested (for backward compatibility)
517
- if flat:
518
- flat_result = {}
519
- for section, values in nested_result.items():
520
- flat_result.update(values)
521
- return flat_result
522
-
523
- return nested_result
524
-
525
-
526
- def merge_configs(base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
527
- """
528
- Deep merge two config dictionaries.
529
-
530
- Args:
531
- base: Base configuration
532
- override: Override configuration (takes precedence)
533
-
534
- Returns:
535
- Merged configuration
536
- """
537
- result = base.copy()
538
- for key, value in override.items():
539
- if key in result and isinstance(result[key], dict) and isinstance(value, dict):
540
- result[key] = merge_configs(result[key], value)
541
- else:
542
- result[key] = value
543
- return result
544
-
545
-
546
- def load_config(
547
- config_file: Optional[str] = None,
548
- overrides: Optional[Dict[str, Any]] = None
549
- ) -> TracciaConfig:
550
- """
551
- Load and validate Traccia configuration from multiple sources.
552
-
553
- Priority (highest to lowest):
554
- 1. Explicit overrides (passed as parameters)
555
- 2. Environment variables
556
- 3. Config file (./traccia.toml or ~/.traccia/config.toml)
557
- 4. Defaults
558
-
559
- Args:
560
- config_file: Optional explicit path to config file
561
- overrides: Optional dict of explicit parameter overrides
562
-
563
- Returns:
564
- Validated TracciaConfig instance
565
-
566
- Raises:
567
- ConfigError: If configuration is invalid or conflicting
568
- """
569
- merged_config: Dict[str, Any] = {}
570
-
571
- # 1. Load from config file (lowest priority)
572
- if config_file:
573
- file_config = load_toml_config(config_file)
574
- merged_config = merge_configs(merged_config, file_config)
575
- else:
576
- # Try to find config file automatically
577
- found_config = find_config_file()
578
- if found_config:
579
- file_config = load_toml_config(found_config)
580
- merged_config = merge_configs(merged_config, file_config)
581
-
582
- # 2. Override with environment variables (medium priority)
583
- env_config = load_config_from_env()
584
- merged_config = merge_configs(merged_config, env_config)
585
-
586
- # 3. Override with explicit parameters (highest priority)
587
- if overrides:
588
- merged_config = merge_configs(merged_config, overrides)
589
-
590
- # 4. Create and validate Pydantic model
591
- try:
592
- return TracciaConfig(**merged_config)
593
- except Exception as e:
594
- raise ConfigError(f"Configuration validation failed: {e}")
595
-
596
-
597
- def validate_config(
598
- config_file: Optional[str] = None,
599
- overrides: Optional[Dict[str, Any]] = None
600
- ) -> tuple[bool, str, Optional[TracciaConfig]]:
601
- """
602
- Validate configuration without loading it.
603
-
604
- Used by the `traccia doctor` CLI command.
605
-
606
- Args:
607
- config_file: Optional explicit path to config file
608
- overrides: Optional dict of explicit parameter overrides
609
-
610
- Returns:
611
- Tuple of (is_valid, message, config_or_none)
612
- """
613
- try:
614
- config = load_config(config_file=config_file, overrides=overrides)
615
- return True, "Configuration is valid", config
616
- except ConfigError as e:
617
- return False, f"Configuration error: {e}", None
618
- except Exception as e:
619
- return False, f"Unexpected error: {e}", None
620
-
621
-
622
- # Backward compatibility functions
623
- def load_config_with_priority(
624
- config_file: Optional[str] = None,
625
- overrides: Optional[Dict[str, Any]] = None
626
- ) -> Dict[str, Any]:
627
- """
628
- Legacy function for backward compatibility.
629
-
630
- Returns flattened config dictionary instead of Pydantic model.
631
- Accepts flat overrides and converts them to nested format.
632
- """
633
- # Convert flat overrides to nested format
634
- nested_overrides = None
635
- if overrides:
636
- nested_overrides = {
637
- "tracing": {},
638
- "exporters": {},
639
- "instrumentation": {},
640
- "rate_limiting": {},
641
- "runtime": {},
642
- "logging": {},
643
- "advanced": {}
644
- }
645
-
646
- # Map flat keys to nested structure
647
- flat_to_nested = {
648
- # Tracing
649
- "api_key": ("tracing", "api_key"),
650
- "endpoint": ("tracing", "endpoint"),
651
- "sample_rate": ("tracing", "sample_rate"),
652
- "auto_start_trace": ("tracing", "auto_start_trace"),
653
- "auto_trace_name": ("tracing", "auto_trace_name"),
654
- "use_otlp": ("tracing", "use_otlp"),
655
- "service_name": ("tracing", "service_name"),
656
- # Exporters
657
- "enable_console": ("exporters", "enable_console"),
658
- "enable_file": ("exporters", "enable_file"),
659
- "file_exporter_path": ("exporters", "file_exporter_path"),
660
- "reset_trace_file": ("exporters", "reset_trace_file"),
661
- # Instrumentation
662
- "enable_patching": ("instrumentation", "enable_patching"),
663
- "enable_token_counting": ("instrumentation", "enable_token_counting"),
664
- "enable_costs": ("instrumentation", "enable_costs"),
665
- "auto_instrument_tools": ("instrumentation", "auto_instrument_tools"),
666
- "max_tool_spans": ("instrumentation", "max_tool_spans"),
667
- "max_span_depth": ("instrumentation", "max_span_depth"),
668
- # Rate limiting & Batching
669
- "max_spans_per_second": ("rate_limiting", "max_spans_per_second"),
670
- "max_queue_size": ("rate_limiting", "max_queue_size"),
671
- "max_block_ms": ("rate_limiting", "max_block_ms"),
672
- "max_export_batch_size": ("rate_limiting", "max_export_batch_size"),
673
- "schedule_delay_millis": ("rate_limiting", "schedule_delay_millis"),
674
- # Runtime
675
- "session_id": ("runtime", "session_id"),
676
- "user_id": ("runtime", "user_id"),
677
- "tenant_id": ("runtime", "tenant_id"),
678
- "project_id": ("runtime", "project_id"),
679
- "agent_id": ("runtime", "agent_id"),
680
- # Logging
681
- "debug": ("logging", "debug"),
682
- "enable_span_logging": ("logging", "enable_span_logging"),
683
- # Advanced
684
- "attr_truncation_limit": ("advanced", "attr_truncation_limit"),
685
- }
686
-
687
- for flat_key, value in overrides.items():
688
- if flat_key in flat_to_nested:
689
- section, nested_key = flat_to_nested[flat_key]
690
- nested_overrides[section][nested_key] = value
691
-
692
- config = load_config(config_file=config_file, overrides=nested_overrides)
693
- return config.to_flat_dict()