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.
@@ -1,74 +0,0 @@
1
- """Optional auto-instrumentation for user-provided tool functions.
2
-
3
- The SDK supports explicit instrumentation via the `@observe` decorator. This
4
- module provides a best-effort utility for wrapping existing callables by dotted
5
- path strings.
6
-
7
- Accepted include formats:
8
- - "package.module:function"
9
- - "package.module.function"
10
-
11
- This is intentionally conservative: it mainly targets module-level functions.
12
- If a target can't be resolved or isn't callable, it's skipped.
13
- """
14
-
15
- from __future__ import annotations
16
-
17
- import importlib
18
- from typing import Iterable, Optional, Tuple
19
-
20
- from traccia.instrumentation.decorator import observe
21
- from traccia import runtime_config
22
-
23
-
24
- def _split_target(spec: str) -> Optional[Tuple[str, str]]:
25
- if not spec:
26
- return None
27
- if ":" in spec:
28
- mod, attr = spec.split(":", 1)
29
- return mod.strip(), attr.strip()
30
- # Heuristic: last segment is the attribute name; rest is module
31
- if "." not in spec:
32
- return None
33
- mod, attr = spec.rsplit(".", 1)
34
- return mod.strip(), attr.strip()
35
-
36
-
37
- def instrument_functions(include: Iterable[str]) -> None:
38
- """
39
- Wrap and replace functions referenced by include specs.
40
-
41
- This is best-effort and will silently skip invalid entries.
42
- """
43
- if not runtime_config.get_auto_instrument_tools():
44
- return
45
-
46
- for spec in include or []:
47
- parsed = _split_target(str(spec))
48
- if not parsed:
49
- continue
50
- module_name, attr_name = parsed
51
- try:
52
- mod = importlib.import_module(module_name)
53
- except Exception:
54
- continue
55
-
56
- try:
57
- target = getattr(mod, attr_name)
58
- except Exception:
59
- continue
60
- if not callable(target):
61
- continue
62
-
63
- # Avoid double wrapping
64
- if getattr(target, "_agent_trace_observed", False):
65
- continue
66
-
67
- wrapped = observe(name=f"{module_name}.{attr_name}", as_type="tool")(target)
68
- setattr(wrapped, "_agent_trace_observed", True)
69
- try:
70
- setattr(mod, attr_name, wrapped)
71
- except Exception:
72
- continue
73
-
74
-
traccia/cli.py DELETED
@@ -1,349 +0,0 @@
1
- """CLI for traccia utilities."""
2
-
3
- from __future__ import annotations
4
-
5
- import argparse
6
- import os
7
- import sys
8
- import urllib.request
9
- from typing import Optional
10
-
11
- from traccia.config import validate_config, load_config, find_config_file, ENV_VAR_MAPPING
12
- from traccia.errors import ConfigError
13
-
14
-
15
- def _check(args) -> int:
16
- """Check connectivity to the configured exporter endpoint."""
17
- # Load config to get endpoint
18
- try:
19
- config = load_config(config_file=args.config if hasattr(args, 'config') else None)
20
- endpoint = args.endpoint or config.tracing.endpoint
21
-
22
- if not endpoint:
23
- print("❌ No endpoint configured.", file=sys.stderr)
24
- print(" Set endpoint in traccia.toml or use --endpoint flag", file=sys.stderr)
25
- return 1
26
-
27
- print(f"🔍 Checking connectivity to {endpoint}...")
28
- sys.stdout.flush() # Ensure output appears before any errors
29
-
30
- # Try HEAD request first
31
- req = urllib.request.Request(endpoint, method="HEAD")
32
- if args.api_key or config.tracing.api_key:
33
- api_key = args.api_key or config.tracing.api_key
34
- req.add_header("Authorization", f"Bearer {api_key}")
35
-
36
- try:
37
- with urllib.request.urlopen(req, timeout=5) as resp:
38
- code = resp.getcode()
39
- print(f"✅ Endpoint is reachable (HTTP {code})")
40
- print("💡 Connectivity test successful!")
41
- return 0
42
- except urllib.error.HTTPError as e:
43
- # HTTP 405 (Method Not Allowed), 400 (Bad Request), or 401 (Unauthorized)
44
- # means the endpoint is reachable and responding - just doesn't like our test request
45
- if e.code in [400, 401, 405]:
46
- print(f"✅ Endpoint is reachable (HTTP {e.code})")
47
- if e.code == 405:
48
- print("💡 Endpoint only accepts specific methods (expected for OTLP endpoints)")
49
- elif e.code == 401:
50
- print("⚠️ Authentication required - check your API key")
51
- elif e.code == 400:
52
- print("💡 Endpoint rejected test payload (expected for OTLP endpoints)")
53
- print("✅ Connectivity test successful!")
54
- return 0
55
- else:
56
- # Other HTTP errors (404, 500, etc.) are actual failures
57
- print(f"❌ HTTP Error {e.code}: {e.reason}", file=sys.stderr)
58
- return 1
59
-
60
- except ConfigError as exc:
61
- print(f"❌ Configuration error: {exc}", file=sys.stderr)
62
- return 1
63
- except urllib.error.URLError as exc:
64
- print(f"❌ Connection failed: {exc.reason}", file=sys.stderr)
65
- print(" Make sure the endpoint is running and accessible", file=sys.stderr)
66
- return 1
67
- except Exception as exc:
68
- print(f"❌ Unexpected error: {exc}", file=sys.stderr)
69
- return 1
70
-
71
-
72
- def _config_init(args) -> int:
73
- """Initialize traccia.toml config file in current directory."""
74
- config_path = os.path.join(os.getcwd(), "traccia.toml")
75
-
76
- # Check if file already exists
77
- if os.path.exists(config_path) and not args.force:
78
- print(f"❌ Config file already exists at {config_path}", file=sys.stderr)
79
- print(" Use --force to overwrite", file=sys.stderr)
80
- return 1
81
-
82
- # Create config template with important parameters
83
- config_template = """# Traccia SDK Configuration File
84
- # Documentation: https://github.com/traccia-ai/traccia
85
-
86
- [tracing]
87
- # API key for authentication (required for SaaS, optional for open-source)
88
- api_key = ""
89
-
90
- # Endpoint URL for trace ingestion
91
- # For local Tempo: endpoint = "http://localhost:4318/v1/traces"
92
- # For local Jaeger: endpoint = "http://localhost:4318/v1/traces"
93
- # endpoint = "http://localhost:4318/v1/traces"
94
-
95
- # Sampling rate (0.0 to 1.0) - controls what percentage of traces are sent
96
- sample_rate = 1.0
97
-
98
- # Auto-start a root trace on init (default: true)
99
- auto_start_trace = true
100
-
101
- # Name for the auto-started root trace
102
- auto_trace_name = "root"
103
-
104
- # Use OTLP exporter (default: true)
105
- # Set to false if using console or file exporter
106
- use_otlp = true
107
-
108
- # Service name (optional)
109
- # service_name = "my-app"
110
-
111
- [exporters]
112
- # IMPORTANT: Only enable ONE exporter at a time (console, file, or OTLP via use_otlp)
113
-
114
- # Enable console exporter for local debugging
115
- enable_console = false
116
-
117
- # Enable file exporter to write traces to local file
118
- enable_file = false
119
-
120
- # File path for file exporter (only used if enable_file = true)
121
- file_exporter_path = "traces.jsonl"
122
-
123
- # Reset/clear trace file on initialization
124
- reset_trace_file = false
125
-
126
- [instrumentation]
127
- # Auto-patch popular libraries (OpenAI, Anthropic, requests)
128
- enable_patching = true
129
-
130
- # Count tokens for LLM calls
131
- enable_token_counting = true
132
-
133
- # Calculate costs for LLM calls
134
- enable_costs = true
135
-
136
- # Auto-instrument tool calls (experimental)
137
- auto_instrument_tools = false
138
-
139
- # Maximum number of tool spans to create
140
- max_tool_spans = 100
141
-
142
- # Maximum depth of nested spans
143
- max_span_depth = 10
144
-
145
- [rate_limiting]
146
- # Maximum spans per second (uncomment to enable rate limiting)
147
- # max_spans_per_second = 100.0
148
-
149
- # Maximum queue size for buffered spans
150
- max_queue_size = 5000
151
-
152
- # Maximum milliseconds to block before dropping spans
153
- max_block_ms = 100
154
-
155
- # Maximum number of spans in a single export batch
156
- max_export_batch_size = 512
157
-
158
- # Delay in milliseconds between export batches
159
- schedule_delay_millis = 5000
160
-
161
- [runtime]
162
- # Runtime metadata (optional - can be set per-session)
163
- # session_id = ""
164
- # user_id = ""
165
- # tenant_id = ""
166
- # project_id = ""
167
-
168
- [logging]
169
- # Enable debug logging
170
- debug = false
171
-
172
- # Enable span-level logging
173
- enable_span_logging = false
174
-
175
- [advanced]
176
- # Maximum length for attribute values (uncomment to set limit)
177
- # attr_truncation_limit = 1000
178
- """
179
-
180
- try:
181
- with open(config_path, 'w', encoding='utf-8') as f:
182
- f.write(config_template)
183
- print(f"✅ Created config file at {config_path}")
184
- print("\n📝 Next steps:")
185
- print(" 1. Edit the config file to add your API key and endpoint")
186
- print(" 2. Run `traccia doctor` to validate your configuration")
187
- print(" 3. Run `traccia check` to test connectivity")
188
- return 0
189
- except Exception as exc:
190
- print(f"❌ Failed to create config file: {exc}", file=sys.stderr)
191
- return 1
192
-
193
-
194
- def _doctor(args) -> int:
195
- """Validate configuration and diagnose common issues."""
196
- print("🩺 Running Traccia configuration diagnostics...\n")
197
-
198
- issues_found = 0
199
-
200
- # 1. Check for config file
201
- config_file = None
202
- if hasattr(args, 'config') and args.config:
203
- config_file = args.config
204
- if not os.path.exists(config_file):
205
- print(f"❌ Specified config file not found: {config_file}")
206
- issues_found += 1
207
- return 1
208
- else:
209
- config_file = find_config_file()
210
- if config_file:
211
- print(f"✅ Found config file: {config_file}")
212
- else:
213
- print("⚠️ No config file found (checked ./traccia.toml and ~/.traccia/config.toml)")
214
- print(" Run `traccia config init` to create one")
215
- issues_found += 1
216
-
217
- # 2. Check environment variables
218
- print("\n📋 Environment variables:")
219
- found_env_vars = []
220
- for config_key, env_vars in ENV_VAR_MAPPING.items():
221
- for env_var in env_vars:
222
- if os.getenv(env_var):
223
- found_env_vars.append(env_var)
224
- print(f" ✅ {env_var} is set")
225
-
226
- if not found_env_vars:
227
- print(" ℹ️ No Traccia environment variables set")
228
-
229
- # 3. Validate configuration
230
- print("\n🔍 Validating configuration...")
231
- is_valid, message, config = validate_config(config_file=config_file)
232
-
233
- if is_valid:
234
- print(f"✅ {message}")
235
-
236
- # Print configuration summary
237
- print("\n📊 Configuration summary:")
238
- print(f" • API Key: {'✅ Set' if config.tracing.api_key else '❌ Not set'}")
239
- print(f" • Endpoint: {config.tracing.endpoint or '❌ Not set'}")
240
- print(f" • Sample Rate: {config.tracing.sample_rate}")
241
- print(f" • OTLP Exporter: {'✅ Enabled' if config.tracing.use_otlp else '❌ Disabled'}")
242
- print(f" • Console Exporter: {'✅ Enabled' if config.exporters.enable_console else '❌ Disabled'}")
243
- print(f" • File Exporter: {'✅ Enabled' if config.exporters.enable_file else '❌ Disabled'}")
244
- print(f" • Auto-patching: {'✅ Enabled' if config.instrumentation.enable_patching else '❌ Disabled'}")
245
-
246
- # Check for potential issues
247
- if config.tracing.use_otlp and not config.tracing.endpoint:
248
- print("\n⚠️ Warning: OTLP exporter is enabled but no endpoint is configured")
249
- issues_found += 1
250
-
251
- if not config.tracing.use_otlp and not config.exporters.enable_console and not config.exporters.enable_file:
252
- print("\n❌ Error: No exporter is enabled! Traces won't be exported anywhere.")
253
- issues_found += 1
254
-
255
- if config.rate_limiting.max_spans_per_second:
256
- print(f"\n ℹ️ Rate limiting enabled: {config.rate_limiting.max_spans_per_second} spans/sec")
257
- else:
258
- print(f"❌ {message}")
259
- issues_found += 1
260
-
261
- # 4. Environment variable mapping reference
262
- print("\n📖 Environment Variable Reference:")
263
- print(" Common variables:")
264
- print(" • TRACCIA_API_KEY or AGENT_DASHBOARD_API_KEY")
265
- print(" • TRACCIA_ENDPOINT or AGENT_DASHBOARD_ENDPOINT")
266
- print(" • TRACCIA_SAMPLE_RATE")
267
- print(" • TRACCIA_DEBUG")
268
- print("\n For a complete list, see: ENV_VAR_MAPPING in traccia/config.py")
269
-
270
- # Summary
271
- print("\n" + "="*60)
272
- if issues_found == 0:
273
- print("✅ No issues found! Your configuration looks good.")
274
- print("\n💡 Tip: Run `traccia check` to test connectivity to your endpoint")
275
- return 0
276
- else:
277
- print(f"⚠️ Found {issues_found} issue(s). Please review the messages above.")
278
- return 1
279
-
280
-
281
- def main(argv=None) -> int:
282
- """Main CLI entry point."""
283
- parser = argparse.ArgumentParser(
284
- prog="traccia",
285
- description="Traccia SDK - Production-ready tracing for AI agents",
286
- formatter_class=argparse.RawDescriptionHelpFormatter,
287
- epilog="""
288
- Examples:
289
- traccia config init Create a new config file
290
- traccia doctor Validate configuration
291
- traccia check Test connectivity to exporter
292
- traccia check --endpoint URL Test specific endpoint
293
-
294
- For more information, visit: https://github.com/traccia-ai/traccia
295
- """
296
- )
297
-
298
- # Global options
299
- parser.add_argument(
300
- "--config",
301
- help="Path to config file (default: ./traccia.toml or ~/.traccia/config.toml)"
302
- )
303
-
304
- sub = parser.add_subparsers(dest="command", required=True)
305
-
306
- # Check command
307
- check = sub.add_parser(
308
- "check",
309
- help="Verify connectivity to ingest endpoint",
310
- description="Test connectivity to the configured exporter endpoint"
311
- )
312
- check.add_argument("--endpoint", help="Override endpoint URL")
313
- check.add_argument("--api-key", help="API key for authentication")
314
- check.set_defaults(func=_check)
315
-
316
- # Config command
317
- config = sub.add_parser(
318
- "config",
319
- help="Configuration management",
320
- description="Manage Traccia configuration files"
321
- )
322
- config_sub = config.add_subparsers(dest="config_command", required=True)
323
-
324
- config_init = config_sub.add_parser(
325
- "init",
326
- help="Create traccia.toml config file",
327
- description="Initialize a new traccia.toml configuration file in the current directory"
328
- )
329
- config_init.add_argument(
330
- "--force",
331
- action="store_true",
332
- help="Overwrite existing config file"
333
- )
334
- config_init.set_defaults(func=_config_init)
335
-
336
- # Doctor command
337
- doctor = sub.add_parser(
338
- "doctor",
339
- help="Validate configuration and diagnose issues",
340
- description="Run diagnostics on your Traccia configuration"
341
- )
342
- doctor.set_defaults(func=_doctor)
343
-
344
- args = parser.parse_args(argv)
345
- return args.func(args)
346
-
347
-
348
- if __name__ == "__main__":
349
- raise SystemExit(main())