agenttel 0.3.0a1__tar.gz
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.
- agenttel-0.3.0a1/.gitignore +38 -0
- agenttel-0.3.0a1/PKG-INFO +228 -0
- agenttel-0.3.0a1/README.md +182 -0
- agenttel-0.3.0a1/pyproject.toml +52 -0
- agenttel-0.3.0a1/src/agenttel/__init__.py +13 -0
- agenttel-0.3.0a1/src/agenttel/agent/__init__.py +18 -0
- agenttel-0.3.0a1/src/agenttel/agent/auth.py +134 -0
- agenttel-0.3.0a1/src/agenttel/agent/context.py +86 -0
- agenttel-0.3.0a1/src/agenttel/agent/health.py +142 -0
- agenttel-0.3.0a1/src/agenttel/agent/identity.py +104 -0
- agenttel-0.3.0a1/src/agenttel/agent/incident.py +146 -0
- agenttel-0.3.0a1/src/agenttel/agent/mcp_server.py +320 -0
- agenttel-0.3.0a1/src/agenttel/agent/remediation.py +167 -0
- agenttel-0.3.0a1/src/agenttel/agent/reporting.py +140 -0
- agenttel-0.3.0a1/src/agenttel/agentic/__init__.py +5 -0
- agenttel-0.3.0a1/src/agenttel/agentic/cost.py +54 -0
- agenttel-0.3.0a1/src/agenttel/agentic/guardrail.py +85 -0
- agenttel-0.3.0a1/src/agenttel/agentic/orchestration.py +160 -0
- agenttel-0.3.0a1/src/agenttel/agentic/quality.py +129 -0
- agenttel-0.3.0a1/src/agenttel/agentic/scopes.py +467 -0
- agenttel-0.3.0a1/src/agenttel/agentic/tracer.py +161 -0
- agenttel-0.3.0a1/src/agenttel/agentic_attributes.py +112 -0
- agenttel-0.3.0a1/src/agenttel/anomaly/__init__.py +6 -0
- agenttel-0.3.0a1/src/agenttel/anomaly/detector.py +59 -0
- agenttel-0.3.0a1/src/agenttel/anomaly/patterns.py +68 -0
- agenttel-0.3.0a1/src/agenttel/attributes.py +84 -0
- agenttel-0.3.0a1/src/agenttel/baseline/__init__.py +13 -0
- agenttel-0.3.0a1/src/agenttel/baseline/composite.py +31 -0
- agenttel-0.3.0a1/src/agenttel/baseline/provider.py +42 -0
- agenttel-0.3.0a1/src/agenttel/baseline/rolling.py +148 -0
- agenttel-0.3.0a1/src/agenttel/causality/__init__.py +5 -0
- agenttel-0.3.0a1/src/agenttel/causality/tracker.py +148 -0
- agenttel-0.3.0a1/src/agenttel/config.py +197 -0
- agenttel-0.3.0a1/src/agenttel/engine.py +161 -0
- agenttel-0.3.0a1/src/agenttel/enums.py +198 -0
- agenttel-0.3.0a1/src/agenttel/error/__init__.py +5 -0
- agenttel-0.3.0a1/src/agenttel/error/classifier.py +71 -0
- agenttel-0.3.0a1/src/agenttel/events.py +113 -0
- agenttel-0.3.0a1/src/agenttel/fastapi/__init__.py +24 -0
- agenttel-0.3.0a1/src/agenttel/fastapi/config.py +63 -0
- agenttel-0.3.0a1/src/agenttel/fastapi/decorators.py +91 -0
- agenttel-0.3.0a1/src/agenttel/fastapi/middleware.py +114 -0
- agenttel-0.3.0a1/src/agenttel/genai/__init__.py +33 -0
- agenttel-0.3.0a1/src/agenttel/genai/anthropic.py +145 -0
- agenttel-0.3.0a1/src/agenttel/genai/attributes.py +43 -0
- agenttel-0.3.0a1/src/agenttel/genai/bedrock.py +125 -0
- agenttel-0.3.0a1/src/agenttel/genai/cost.py +91 -0
- agenttel-0.3.0a1/src/agenttel/genai/langchain.py +168 -0
- agenttel-0.3.0a1/src/agenttel/genai/openai.py +153 -0
- agenttel-0.3.0a1/src/agenttel/genai/span_builder.py +127 -0
- agenttel-0.3.0a1/src/agenttel/models.py +146 -0
- agenttel-0.3.0a1/src/agenttel/processor.py +190 -0
- agenttel-0.3.0a1/src/agenttel/slo/__init__.py +5 -0
- agenttel-0.3.0a1/src/agenttel/slo/tracker.py +108 -0
- agenttel-0.3.0a1/src/agenttel/topology/__init__.py +5 -0
- agenttel-0.3.0a1/src/agenttel/topology/registry.py +86 -0
- agenttel-0.3.0a1/tests/conftest.py +22 -0
- agenttel-0.3.0a1/tests/test_agent.py +116 -0
- agenttel-0.3.0a1/tests/test_agentic.py +84 -0
- agenttel-0.3.0a1/tests/test_anomaly.py +85 -0
- agenttel-0.3.0a1/tests/test_attributes.py +63 -0
- agenttel-0.3.0a1/tests/test_baseline.py +145 -0
- agenttel-0.3.0a1/tests/test_fastapi.py +20 -0
- agenttel-0.3.0a1/tests/test_genai.py +47 -0
- agenttel-0.3.0a1/tests/test_genai_anthropic.py +155 -0
- agenttel-0.3.0a1/tests/test_genai_openai.py +182 -0
- agenttel-0.3.0a1/tests/test_mcp_server.py +136 -0
- agenttel-0.3.0a1/tests/test_processor.py +44 -0
- agenttel-0.3.0a1/tests/test_slo.py +70 -0
- agenttel-0.3.0a1/tests/test_topology.py +148 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Gradle
|
|
2
|
+
.gradle/
|
|
3
|
+
build/
|
|
4
|
+
!gradle/wrapper/gradle-wrapper.jar
|
|
5
|
+
|
|
6
|
+
# IDE
|
|
7
|
+
.idea/
|
|
8
|
+
*.iml
|
|
9
|
+
.vscode/
|
|
10
|
+
.settings/
|
|
11
|
+
.classpath
|
|
12
|
+
.project
|
|
13
|
+
.factorypath
|
|
14
|
+
|
|
15
|
+
# OS
|
|
16
|
+
.DS_Store
|
|
17
|
+
Thumbs.db
|
|
18
|
+
|
|
19
|
+
# MkDocs build output
|
|
20
|
+
site/
|
|
21
|
+
|
|
22
|
+
# Node.js
|
|
23
|
+
node_modules/
|
|
24
|
+
dist/
|
|
25
|
+
|
|
26
|
+
# Python
|
|
27
|
+
__pycache__/
|
|
28
|
+
*.pyc
|
|
29
|
+
*.py[cod]
|
|
30
|
+
*.egg-info/
|
|
31
|
+
*.egg
|
|
32
|
+
.venv/
|
|
33
|
+
.pytest_cache/
|
|
34
|
+
|
|
35
|
+
# Secrets
|
|
36
|
+
.env
|
|
37
|
+
*.key
|
|
38
|
+
*.pem
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agenttel
|
|
3
|
+
Version: 0.3.0a1
|
|
4
|
+
Summary: AgentTel Python SDK — AI-native telemetry for Python services
|
|
5
|
+
Author-email: AgentTel <engineering@agenttel.io>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Framework :: FastAPI
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Topic :: System :: Monitoring
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Requires-Dist: opentelemetry-api>=1.20.0
|
|
16
|
+
Requires-Dist: opentelemetry-sdk>=1.20.0
|
|
17
|
+
Requires-Dist: pydantic>=2.0
|
|
18
|
+
Requires-Dist: pyyaml>=6.0
|
|
19
|
+
Provides-Extra: agent
|
|
20
|
+
Requires-Dist: aiohttp>=3.9; extra == 'agent'
|
|
21
|
+
Provides-Extra: all
|
|
22
|
+
Requires-Dist: aiohttp>=3.9; extra == 'all'
|
|
23
|
+
Requires-Dist: anthropic>=0.20; extra == 'all'
|
|
24
|
+
Requires-Dist: boto3>=1.28; extra == 'all'
|
|
25
|
+
Requires-Dist: fastapi>=0.100.0; extra == 'all'
|
|
26
|
+
Requires-Dist: langchain-core>=0.1; extra == 'all'
|
|
27
|
+
Requires-Dist: openai>=1.0; extra == 'all'
|
|
28
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.41b0; extra == 'all'
|
|
29
|
+
Provides-Extra: anthropic
|
|
30
|
+
Requires-Dist: anthropic>=0.20; extra == 'anthropic'
|
|
31
|
+
Provides-Extra: bedrock
|
|
32
|
+
Requires-Dist: boto3>=1.28; extra == 'bedrock'
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: httpx>=0.24; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: respx>=0.20; extra == 'dev'
|
|
38
|
+
Provides-Extra: fastapi
|
|
39
|
+
Requires-Dist: fastapi>=0.100.0; extra == 'fastapi'
|
|
40
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.41b0; extra == 'fastapi'
|
|
41
|
+
Provides-Extra: langchain
|
|
42
|
+
Requires-Dist: langchain-core>=0.1; extra == 'langchain'
|
|
43
|
+
Provides-Extra: openai
|
|
44
|
+
Requires-Dist: openai>=1.0; extra == 'openai'
|
|
45
|
+
Description-Content-Type: text/markdown
|
|
46
|
+
|
|
47
|
+
# AgentTel Python SDK
|
|
48
|
+
|
|
49
|
+
AI-native telemetry SDK for Python services. Full feature parity with the JVM SDK — semantic attributes, baselines, anomaly detection, SLO tracking, GenAI instrumentation, agentic observability, and MCP agent interface.
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install agenttel
|
|
55
|
+
|
|
56
|
+
# With extras
|
|
57
|
+
pip install agenttel[fastapi] # FastAPI middleware + auto-config
|
|
58
|
+
pip install agenttel[openai] # OpenAI instrumentation
|
|
59
|
+
pip install agenttel[anthropic] # Anthropic instrumentation
|
|
60
|
+
pip install agenttel[langchain] # LangChain instrumentation
|
|
61
|
+
pip install agenttel[bedrock] # AWS Bedrock instrumentation
|
|
62
|
+
pip install agenttel[agent] # MCP server (aiohttp)
|
|
63
|
+
pip install agenttel[all] # Everything
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Quick Start (FastAPI)
|
|
67
|
+
|
|
68
|
+
### 1. Create `agenttel.yml`
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
agenttel:
|
|
72
|
+
enabled: true
|
|
73
|
+
topology:
|
|
74
|
+
team: payments
|
|
75
|
+
tier: critical
|
|
76
|
+
domain: fintech
|
|
77
|
+
on-call-channel: "#payments-oncall"
|
|
78
|
+
dependencies:
|
|
79
|
+
- name: postgres
|
|
80
|
+
type: database
|
|
81
|
+
criticality: required
|
|
82
|
+
timeout-ms: 3000
|
|
83
|
+
- name: stripe-api
|
|
84
|
+
type: external_api
|
|
85
|
+
criticality: degraded
|
|
86
|
+
timeout-ms: 5000
|
|
87
|
+
circuit-breaker: true
|
|
88
|
+
operations:
|
|
89
|
+
"[POST /api/transfers]":
|
|
90
|
+
profile: critical-write
|
|
91
|
+
expected-latency-p50: "80ms"
|
|
92
|
+
expected-latency-p99: "300ms"
|
|
93
|
+
expected-error-rate: 0.002
|
|
94
|
+
runbook-url: "https://wiki.example.com/runbooks/transfers"
|
|
95
|
+
profiles:
|
|
96
|
+
critical-write:
|
|
97
|
+
retryable: false
|
|
98
|
+
idempotent: false
|
|
99
|
+
escalation-level: page_oncall
|
|
100
|
+
safe-to-restart: false
|
|
101
|
+
baselines:
|
|
102
|
+
rolling-window-size: 1000
|
|
103
|
+
rolling-min-samples: 10
|
|
104
|
+
anomaly-detection:
|
|
105
|
+
enabled: true
|
|
106
|
+
z-score-threshold: 3.0
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 2. Instrument Your App
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from fastapi import FastAPI
|
|
113
|
+
from agenttel.fastapi import instrument_fastapi
|
|
114
|
+
|
|
115
|
+
app = FastAPI()
|
|
116
|
+
engine = instrument_fastapi(app)
|
|
117
|
+
|
|
118
|
+
@app.post("/api/transfers")
|
|
119
|
+
async def create_transfer(request: dict):
|
|
120
|
+
# Your spans automatically get AgentTel enrichment:
|
|
121
|
+
# - topology attributes (team, tier, domain)
|
|
122
|
+
# - operation context (retryable, runbook, escalation)
|
|
123
|
+
# - baseline attributes (P50, P99, error rate)
|
|
124
|
+
# - anomaly detection on span end
|
|
125
|
+
# - SLO budget tracking
|
|
126
|
+
return {"status": "completed"}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 3. Use the Decorator
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from agenttel.fastapi import agent_operation
|
|
133
|
+
|
|
134
|
+
@app.get("/api/accounts/{account_id}")
|
|
135
|
+
@agent_operation(
|
|
136
|
+
expected_latency_p50="10ms",
|
|
137
|
+
expected_latency_p99="50ms",
|
|
138
|
+
retryable=True,
|
|
139
|
+
runbook_url="https://wiki.example.com/runbooks/get-account",
|
|
140
|
+
)
|
|
141
|
+
async def get_account(account_id: str):
|
|
142
|
+
return {"id": account_id}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## GenAI Instrumentation
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from openai import OpenAI
|
|
149
|
+
from agenttel.genai import instrument_openai
|
|
150
|
+
|
|
151
|
+
client = instrument_openai(OpenAI())
|
|
152
|
+
|
|
153
|
+
# All completions are now traced with:
|
|
154
|
+
# - gen_ai.system, gen_ai.request.model
|
|
155
|
+
# - gen_ai.usage.input_tokens, gen_ai.usage.output_tokens
|
|
156
|
+
# - agenttel.genai.cost_usd
|
|
157
|
+
response = client.chat.completions.create(
|
|
158
|
+
model="gpt-4o",
|
|
159
|
+
messages=[{"role": "user", "content": "Hello"}],
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Anthropic, LangChain, and AWS Bedrock are also supported:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from anthropic import Anthropic
|
|
167
|
+
from agenttel.genai import instrument_anthropic, instrument_langchain
|
|
168
|
+
|
|
169
|
+
client = instrument_anthropic(Anthropic())
|
|
170
|
+
instrument_langchain() # Global LangChain instrumentation
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Agentic Observability
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from agenttel.agentic import AgentTracer
|
|
177
|
+
from agenttel.enums import AgentType, StepType
|
|
178
|
+
|
|
179
|
+
tracer = (AgentTracer.create()
|
|
180
|
+
.agent_name("incident-responder")
|
|
181
|
+
.agent_type(AgentType.SINGLE)
|
|
182
|
+
.framework("langchain")
|
|
183
|
+
.build())
|
|
184
|
+
|
|
185
|
+
with tracer.invoke("diagnose high latency") as invocation:
|
|
186
|
+
with invocation.step(StepType.THOUGHT, "analyzing metrics"):
|
|
187
|
+
pass # reasoning
|
|
188
|
+
|
|
189
|
+
with invocation.tool_call("get_service_health") as tool:
|
|
190
|
+
result = {"status": "degraded"}
|
|
191
|
+
tool.set_result(result)
|
|
192
|
+
|
|
193
|
+
with invocation.task("check dependencies") as task:
|
|
194
|
+
with task.step(StepType.ACTION, "querying deps"):
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
invocation.complete(goal_achieved=True)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Architecture
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
agenttel-python/
|
|
204
|
+
├── src/agenttel/
|
|
205
|
+
│ ├── attributes.py # Semantic attribute constants
|
|
206
|
+
│ ├── enums.py # All enums (ServiceTier, ErrorCategory, etc.)
|
|
207
|
+
│ ├── models.py # Pydantic data models
|
|
208
|
+
│ ├── config.py # YAML config loader
|
|
209
|
+
│ ├── processor.py # OTel SpanProcessor
|
|
210
|
+
│ ├── engine.py # High-level orchestrator
|
|
211
|
+
│ ├── baseline/ # Static, rolling, composite baselines
|
|
212
|
+
│ ├── anomaly/ # Z-score anomaly detection + pattern matching
|
|
213
|
+
│ ├── slo/ # SLO tracking with error budgets
|
|
214
|
+
│ ├── error/ # Error classification
|
|
215
|
+
│ ├── causality/ # Dependency state tracking
|
|
216
|
+
│ ├── topology/ # Service topology registry
|
|
217
|
+
│ ├── fastapi/ # FastAPI middleware + decorators
|
|
218
|
+
│ ├── genai/ # OpenAI, Anthropic, LangChain, Bedrock wrappers
|
|
219
|
+
│ ├── agent/ # MCP server, health aggregation, identity
|
|
220
|
+
│ └── agentic/ # AgentTracer, scopes, orchestration patterns
|
|
221
|
+
└── tests/
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Requirements
|
|
225
|
+
|
|
226
|
+
- Python 3.11+
|
|
227
|
+
- OpenTelemetry SDK 1.20+
|
|
228
|
+
- Pydantic 2.0+
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# AgentTel Python SDK
|
|
2
|
+
|
|
3
|
+
AI-native telemetry SDK for Python services. Full feature parity with the JVM SDK — semantic attributes, baselines, anomaly detection, SLO tracking, GenAI instrumentation, agentic observability, and MCP agent interface.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install agenttel
|
|
9
|
+
|
|
10
|
+
# With extras
|
|
11
|
+
pip install agenttel[fastapi] # FastAPI middleware + auto-config
|
|
12
|
+
pip install agenttel[openai] # OpenAI instrumentation
|
|
13
|
+
pip install agenttel[anthropic] # Anthropic instrumentation
|
|
14
|
+
pip install agenttel[langchain] # LangChain instrumentation
|
|
15
|
+
pip install agenttel[bedrock] # AWS Bedrock instrumentation
|
|
16
|
+
pip install agenttel[agent] # MCP server (aiohttp)
|
|
17
|
+
pip install agenttel[all] # Everything
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start (FastAPI)
|
|
21
|
+
|
|
22
|
+
### 1. Create `agenttel.yml`
|
|
23
|
+
|
|
24
|
+
```yaml
|
|
25
|
+
agenttel:
|
|
26
|
+
enabled: true
|
|
27
|
+
topology:
|
|
28
|
+
team: payments
|
|
29
|
+
tier: critical
|
|
30
|
+
domain: fintech
|
|
31
|
+
on-call-channel: "#payments-oncall"
|
|
32
|
+
dependencies:
|
|
33
|
+
- name: postgres
|
|
34
|
+
type: database
|
|
35
|
+
criticality: required
|
|
36
|
+
timeout-ms: 3000
|
|
37
|
+
- name: stripe-api
|
|
38
|
+
type: external_api
|
|
39
|
+
criticality: degraded
|
|
40
|
+
timeout-ms: 5000
|
|
41
|
+
circuit-breaker: true
|
|
42
|
+
operations:
|
|
43
|
+
"[POST /api/transfers]":
|
|
44
|
+
profile: critical-write
|
|
45
|
+
expected-latency-p50: "80ms"
|
|
46
|
+
expected-latency-p99: "300ms"
|
|
47
|
+
expected-error-rate: 0.002
|
|
48
|
+
runbook-url: "https://wiki.example.com/runbooks/transfers"
|
|
49
|
+
profiles:
|
|
50
|
+
critical-write:
|
|
51
|
+
retryable: false
|
|
52
|
+
idempotent: false
|
|
53
|
+
escalation-level: page_oncall
|
|
54
|
+
safe-to-restart: false
|
|
55
|
+
baselines:
|
|
56
|
+
rolling-window-size: 1000
|
|
57
|
+
rolling-min-samples: 10
|
|
58
|
+
anomaly-detection:
|
|
59
|
+
enabled: true
|
|
60
|
+
z-score-threshold: 3.0
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2. Instrument Your App
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from fastapi import FastAPI
|
|
67
|
+
from agenttel.fastapi import instrument_fastapi
|
|
68
|
+
|
|
69
|
+
app = FastAPI()
|
|
70
|
+
engine = instrument_fastapi(app)
|
|
71
|
+
|
|
72
|
+
@app.post("/api/transfers")
|
|
73
|
+
async def create_transfer(request: dict):
|
|
74
|
+
# Your spans automatically get AgentTel enrichment:
|
|
75
|
+
# - topology attributes (team, tier, domain)
|
|
76
|
+
# - operation context (retryable, runbook, escalation)
|
|
77
|
+
# - baseline attributes (P50, P99, error rate)
|
|
78
|
+
# - anomaly detection on span end
|
|
79
|
+
# - SLO budget tracking
|
|
80
|
+
return {"status": "completed"}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 3. Use the Decorator
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from agenttel.fastapi import agent_operation
|
|
87
|
+
|
|
88
|
+
@app.get("/api/accounts/{account_id}")
|
|
89
|
+
@agent_operation(
|
|
90
|
+
expected_latency_p50="10ms",
|
|
91
|
+
expected_latency_p99="50ms",
|
|
92
|
+
retryable=True,
|
|
93
|
+
runbook_url="https://wiki.example.com/runbooks/get-account",
|
|
94
|
+
)
|
|
95
|
+
async def get_account(account_id: str):
|
|
96
|
+
return {"id": account_id}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## GenAI Instrumentation
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from openai import OpenAI
|
|
103
|
+
from agenttel.genai import instrument_openai
|
|
104
|
+
|
|
105
|
+
client = instrument_openai(OpenAI())
|
|
106
|
+
|
|
107
|
+
# All completions are now traced with:
|
|
108
|
+
# - gen_ai.system, gen_ai.request.model
|
|
109
|
+
# - gen_ai.usage.input_tokens, gen_ai.usage.output_tokens
|
|
110
|
+
# - agenttel.genai.cost_usd
|
|
111
|
+
response = client.chat.completions.create(
|
|
112
|
+
model="gpt-4o",
|
|
113
|
+
messages=[{"role": "user", "content": "Hello"}],
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Anthropic, LangChain, and AWS Bedrock are also supported:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from anthropic import Anthropic
|
|
121
|
+
from agenttel.genai import instrument_anthropic, instrument_langchain
|
|
122
|
+
|
|
123
|
+
client = instrument_anthropic(Anthropic())
|
|
124
|
+
instrument_langchain() # Global LangChain instrumentation
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Agentic Observability
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from agenttel.agentic import AgentTracer
|
|
131
|
+
from agenttel.enums import AgentType, StepType
|
|
132
|
+
|
|
133
|
+
tracer = (AgentTracer.create()
|
|
134
|
+
.agent_name("incident-responder")
|
|
135
|
+
.agent_type(AgentType.SINGLE)
|
|
136
|
+
.framework("langchain")
|
|
137
|
+
.build())
|
|
138
|
+
|
|
139
|
+
with tracer.invoke("diagnose high latency") as invocation:
|
|
140
|
+
with invocation.step(StepType.THOUGHT, "analyzing metrics"):
|
|
141
|
+
pass # reasoning
|
|
142
|
+
|
|
143
|
+
with invocation.tool_call("get_service_health") as tool:
|
|
144
|
+
result = {"status": "degraded"}
|
|
145
|
+
tool.set_result(result)
|
|
146
|
+
|
|
147
|
+
with invocation.task("check dependencies") as task:
|
|
148
|
+
with task.step(StepType.ACTION, "querying deps"):
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
invocation.complete(goal_achieved=True)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Architecture
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
agenttel-python/
|
|
158
|
+
├── src/agenttel/
|
|
159
|
+
│ ├── attributes.py # Semantic attribute constants
|
|
160
|
+
│ ├── enums.py # All enums (ServiceTier, ErrorCategory, etc.)
|
|
161
|
+
│ ├── models.py # Pydantic data models
|
|
162
|
+
│ ├── config.py # YAML config loader
|
|
163
|
+
│ ├── processor.py # OTel SpanProcessor
|
|
164
|
+
│ ├── engine.py # High-level orchestrator
|
|
165
|
+
│ ├── baseline/ # Static, rolling, composite baselines
|
|
166
|
+
│ ├── anomaly/ # Z-score anomaly detection + pattern matching
|
|
167
|
+
│ ├── slo/ # SLO tracking with error budgets
|
|
168
|
+
│ ├── error/ # Error classification
|
|
169
|
+
│ ├── causality/ # Dependency state tracking
|
|
170
|
+
│ ├── topology/ # Service topology registry
|
|
171
|
+
│ ├── fastapi/ # FastAPI middleware + decorators
|
|
172
|
+
│ ├── genai/ # OpenAI, Anthropic, LangChain, Bedrock wrappers
|
|
173
|
+
│ ├── agent/ # MCP server, health aggregation, identity
|
|
174
|
+
│ └── agentic/ # AgentTracer, scopes, orchestration patterns
|
|
175
|
+
└── tests/
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Requirements
|
|
179
|
+
|
|
180
|
+
- Python 3.11+
|
|
181
|
+
- OpenTelemetry SDK 1.20+
|
|
182
|
+
- Pydantic 2.0+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agenttel"
|
|
7
|
+
version = "0.3.0a1"
|
|
8
|
+
description = "AgentTel Python SDK — AI-native telemetry for Python services"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "Apache-2.0"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
authors = [{ name = "AgentTel", email = "engineering@agenttel.io" }]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Topic :: System :: Monitoring",
|
|
20
|
+
"Framework :: FastAPI",
|
|
21
|
+
]
|
|
22
|
+
dependencies = [
|
|
23
|
+
"opentelemetry-api>=1.20.0",
|
|
24
|
+
"opentelemetry-sdk>=1.20.0",
|
|
25
|
+
"pydantic>=2.0",
|
|
26
|
+
"pyyaml>=6.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
fastapi = ["fastapi>=0.100.0", "opentelemetry-instrumentation-fastapi>=0.41b0"]
|
|
31
|
+
openai = ["openai>=1.0"]
|
|
32
|
+
anthropic = ["anthropic>=0.20"]
|
|
33
|
+
langchain = ["langchain-core>=0.1"]
|
|
34
|
+
bedrock = ["boto3>=1.28"]
|
|
35
|
+
agent = ["aiohttp>=3.9"]
|
|
36
|
+
all = ["agenttel[fastapi,openai,anthropic,langchain,bedrock,agent]"]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest>=7.0",
|
|
39
|
+
"pytest-asyncio>=0.21",
|
|
40
|
+
"httpx>=0.24",
|
|
41
|
+
"respx>=0.20",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.entry-points."opentelemetry_traces"]
|
|
45
|
+
agenttel = "agenttel:AgentTelSpanProcessor"
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.wheel]
|
|
48
|
+
packages = ["src/agenttel"]
|
|
49
|
+
|
|
50
|
+
[tool.pytest.ini_options]
|
|
51
|
+
testpaths = ["tests"]
|
|
52
|
+
asyncio_mode = "auto"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""AgentTel Python SDK — AI-native telemetry for Python services."""
|
|
2
|
+
|
|
3
|
+
from agenttel.config import AgentTelConfig
|
|
4
|
+
from agenttel.engine import AgentTelEngine
|
|
5
|
+
from agenttel.processor import AgentTelSpanProcessor
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"AgentTelConfig",
|
|
9
|
+
"AgentTelEngine",
|
|
10
|
+
"AgentTelSpanProcessor",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
__version__ = "0.3.0-alpha"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Agent interface for AgentTel (MCP server, health, identity, incident)."""
|
|
2
|
+
|
|
3
|
+
from agenttel.agent.auth import (
|
|
4
|
+
AuthConfig,
|
|
5
|
+
ToolPermission,
|
|
6
|
+
ToolPermissionRegistry as AuthPermissionRegistry,
|
|
7
|
+
)
|
|
8
|
+
from agenttel.agent.health import ServiceHealthAggregator
|
|
9
|
+
from agenttel.agent.identity import AgentIdentity, ToolPermissionRegistry
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"AuthConfig",
|
|
13
|
+
"AuthPermissionRegistry",
|
|
14
|
+
"ServiceHealthAggregator",
|
|
15
|
+
"AgentIdentity",
|
|
16
|
+
"ToolPermission",
|
|
17
|
+
"ToolPermissionRegistry",
|
|
18
|
+
]
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""API key authentication and role-based permissions for the MCP server.
|
|
2
|
+
|
|
3
|
+
Opt-in: when no AuthConfig is provided, all checks are skipped
|
|
4
|
+
and the server behaves as before (backward compatible).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Callable
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ToolPermission(Enum):
|
|
15
|
+
"""Permission levels for MCP tool access."""
|
|
16
|
+
|
|
17
|
+
READ = "read"
|
|
18
|
+
DIAGNOSE = "diagnose"
|
|
19
|
+
REMEDIATE = "remediate"
|
|
20
|
+
ADMIN = "admin"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class AgentIdentity:
|
|
25
|
+
"""Identifies an authenticated agent making MCP requests."""
|
|
26
|
+
|
|
27
|
+
agent_id: str
|
|
28
|
+
role: str
|
|
29
|
+
session_id: str | None = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class AuthConfig:
|
|
34
|
+
"""Configuration for API key authentication.
|
|
35
|
+
|
|
36
|
+
Provide either a static api_keys map, a custom validate_key function,
|
|
37
|
+
or both. When both are provided, the static map is checked first.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
api_keys: dict[str, AgentIdentity] | None = None
|
|
41
|
+
validate_key: Callable[[str], AgentIdentity | None] | None = None
|
|
42
|
+
|
|
43
|
+
def resolve_key(self, key: str) -> AgentIdentity | None:
|
|
44
|
+
"""Resolve a bearer token to an agent identity.
|
|
45
|
+
|
|
46
|
+
Returns None if the key is invalid or not found.
|
|
47
|
+
"""
|
|
48
|
+
trimmed = key.strip()
|
|
49
|
+
if not trimmed:
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
if self.api_keys:
|
|
53
|
+
identity = self.api_keys.get(trimmed)
|
|
54
|
+
if identity is not None:
|
|
55
|
+
return identity
|
|
56
|
+
|
|
57
|
+
if self.validate_key:
|
|
58
|
+
return self.validate_key(trimmed)
|
|
59
|
+
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Default role-to-permission mappings
|
|
64
|
+
_DEFAULT_ROLE_PERMISSIONS: dict[str, set[ToolPermission]] = {
|
|
65
|
+
"observer": {ToolPermission.READ},
|
|
66
|
+
"diagnostician": {ToolPermission.READ, ToolPermission.DIAGNOSE},
|
|
67
|
+
"remediator": {ToolPermission.READ, ToolPermission.DIAGNOSE, ToolPermission.REMEDIATE},
|
|
68
|
+
"admin": {ToolPermission.READ, ToolPermission.DIAGNOSE, ToolPermission.REMEDIATE, ToolPermission.ADMIN},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Default tool-to-required-permission mappings
|
|
72
|
+
_DEFAULT_TOOL_PERMISSIONS: dict[str, ToolPermission] = {
|
|
73
|
+
# READ tools
|
|
74
|
+
"get_service_health": ToolPermission.READ,
|
|
75
|
+
"get_operation_baselines": ToolPermission.READ,
|
|
76
|
+
"get_anomalies": ToolPermission.READ,
|
|
77
|
+
"get_slo_status": ToolPermission.READ,
|
|
78
|
+
"get_dependency_health": ToolPermission.READ,
|
|
79
|
+
"get_topology": ToolPermission.READ,
|
|
80
|
+
"get_incident_context": ToolPermission.READ,
|
|
81
|
+
"get_error_classification": ToolPermission.READ,
|
|
82
|
+
"get_deployment_info": ToolPermission.READ,
|
|
83
|
+
"get_slo_report": ToolPermission.READ,
|
|
84
|
+
"get_executive_summary": ToolPermission.READ,
|
|
85
|
+
"get_trend_analysis": ToolPermission.READ,
|
|
86
|
+
"get_playbook": ToolPermission.READ,
|
|
87
|
+
"get_session": ToolPermission.READ,
|
|
88
|
+
# DIAGNOSE tools
|
|
89
|
+
"verify_remediation_effect": ToolPermission.DIAGNOSE,
|
|
90
|
+
"create_session": ToolPermission.DIAGNOSE,
|
|
91
|
+
"add_session_entry": ToolPermission.DIAGNOSE,
|
|
92
|
+
"suggest_remediation": ToolPermission.DIAGNOSE,
|
|
93
|
+
"get_change_correlation": ToolPermission.DIAGNOSE,
|
|
94
|
+
# REMEDIATE tools
|
|
95
|
+
"execute_remediation": ToolPermission.REMEDIATE,
|
|
96
|
+
"list_remediation_actions": ToolPermission.REMEDIATE,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class ToolPermissionRegistry:
|
|
101
|
+
"""Maps roles to granted permissions and tools to required permission levels.
|
|
102
|
+
|
|
103
|
+
Pre-populated with sensible defaults:
|
|
104
|
+
observer -> [READ]
|
|
105
|
+
diagnostician -> [READ, DIAGNOSE]
|
|
106
|
+
remediator -> [READ, DIAGNOSE, REMEDIATE]
|
|
107
|
+
admin -> [READ, DIAGNOSE, REMEDIATE, ADMIN]
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
def __init__(self) -> None:
|
|
111
|
+
self._role_permissions: dict[str, set[ToolPermission]] = {
|
|
112
|
+
k: set(v) for k, v in _DEFAULT_ROLE_PERMISSIONS.items()
|
|
113
|
+
}
|
|
114
|
+
self._tool_permissions: dict[str, ToolPermission] = dict(_DEFAULT_TOOL_PERMISSIONS)
|
|
115
|
+
|
|
116
|
+
def set_role_permissions(self, role: str, perms: list[ToolPermission]) -> None:
|
|
117
|
+
"""Set the permissions granted to a role, replacing any existing mapping."""
|
|
118
|
+
self._role_permissions[role.lower()] = set(perms)
|
|
119
|
+
|
|
120
|
+
def set_tool_permission(self, tool_name: str, perm: ToolPermission) -> None:
|
|
121
|
+
"""Set the required permission level for a specific tool."""
|
|
122
|
+
self._tool_permissions[tool_name] = perm
|
|
123
|
+
|
|
124
|
+
def is_allowed(self, identity: AgentIdentity, tool_name: str) -> bool:
|
|
125
|
+
"""Check whether the given identity has permission to invoke the named tool.
|
|
126
|
+
|
|
127
|
+
Unknown tools default to READ. Unknown roles get observer permissions.
|
|
128
|
+
"""
|
|
129
|
+
role = identity.role.lower()
|
|
130
|
+
granted = self._role_permissions.get(
|
|
131
|
+
role, self._role_permissions.get("observer", {ToolPermission.READ})
|
|
132
|
+
)
|
|
133
|
+
required = self._tool_permissions.get(tool_name, ToolPermission.READ)
|
|
134
|
+
return required in granted
|