ModelMetre 0.1.0__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.
- modelmetre-0.1.0/PKG-INFO +241 -0
- modelmetre-0.1.0/README.md +218 -0
- modelmetre-0.1.0/pyproject.toml +57 -0
- modelmetre-0.1.0/setup.cfg +4 -0
- modelmetre-0.1.0/src/ModelMetre.egg-info/PKG-INFO +241 -0
- modelmetre-0.1.0/src/ModelMetre.egg-info/SOURCES.txt +33 -0
- modelmetre-0.1.0/src/ModelMetre.egg-info/dependency_links.txt +1 -0
- modelmetre-0.1.0/src/ModelMetre.egg-info/requires.txt +11 -0
- modelmetre-0.1.0/src/ModelMetre.egg-info/top_level.txt +1 -0
- modelmetre-0.1.0/src/llmcost/__init__.py +39 -0
- modelmetre-0.1.0/src/llmcost/api/__init__.py +10 -0
- modelmetre-0.1.0/src/llmcost/api/routes.py +11 -0
- modelmetre-0.1.0/src/llmcost/api/telemetry.py +106 -0
- modelmetre-0.1.0/src/llmcost/auth/__init__.py +1 -0
- modelmetre-0.1.0/src/llmcost/auth/config.py +24 -0
- modelmetre-0.1.0/src/llmcost/client.py +208 -0
- modelmetre-0.1.0/src/llmcost/constants.py +0 -0
- modelmetre-0.1.0/src/llmcost/errors.py +0 -0
- modelmetre-0.1.0/src/llmcost/middleware/__init__.py +0 -0
- modelmetre-0.1.0/src/llmcost/middleware/error_handler.py +0 -0
- modelmetre-0.1.0/src/llmcost/middleware/rate_limit.py +33 -0
- modelmetre-0.1.0/src/llmcost/plugins/__init__.py +0 -0
- modelmetre-0.1.0/src/llmcost/plugins/base_plugin.py +0 -0
- modelmetre-0.1.0/src/llmcost/pricing/__init__.py +41 -0
- modelmetre-0.1.0/src/llmcost/pricing/aggregator.py +228 -0
- modelmetre-0.1.0/src/llmcost/pricing/extractors.py +122 -0
- modelmetre-0.1.0/src/llmcost/pricing/interceptor.py +145 -0
- modelmetre-0.1.0/src/llmcost/sdk.py +143 -0
- modelmetre-0.1.0/src/llmcost/utils/__init__.py +0 -0
- modelmetre-0.1.0/src/llmcost/utils/cache.py +0 -0
- modelmetre-0.1.0/src/llmcost/utils/helpers.py +0 -0
- modelmetre-0.1.0/src/llmcost/utils/http.py +0 -0
- modelmetre-0.1.0/src/llmcost/utils/logger.py +0 -0
- modelmetre-0.1.0/src/llmcost/utils/retry.py +0 -0
- modelmetre-0.1.0/src/llmcost/utils/validation.py +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ModelMetre
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Multi-provider LLM cost observability SDK
|
|
5
|
+
Author-email: Jyotir Nandan <jyotirnandan92129@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/Kakashi15-pix/ModelMetre
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: requests>=2.31.0
|
|
14
|
+
Requires-Dist: pydantic>=2.0.0
|
|
15
|
+
Requires-Dist: httpx>=0.24.0
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
19
|
+
Requires-Dist: pytest-mock>=3.11.0; extra == "dev"
|
|
20
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
21
|
+
Requires-Dist: ruff>=0.0.280; extra == "dev"
|
|
22
|
+
Requires-Dist: mypy>=1.4.0; extra == "dev"
|
|
23
|
+
|
|
24
|
+
# LLM Cost Observability SDK
|
|
25
|
+
|
|
26
|
+
Client-side SDK for provider response interception, usage extraction, request buffering, and optional telemetry flush to a backend cost pipeline.
|
|
27
|
+
## What This SDK Does
|
|
28
|
+
|
|
29
|
+
- Wraps provider SDK methods without changing normal response behavior.
|
|
30
|
+
- Extracts usage metadata from provider responses locally.
|
|
31
|
+
- Buffers request details in-memory.
|
|
32
|
+
- Flushes buffered batches on size/time thresholds.
|
|
33
|
+
- Optionally sends flushed batches to backend telemetry endpoint.
|
|
34
|
+
- Supports lazy API-key verification for direct server API calls via `CostAnalyticsClient`.
|
|
35
|
+
|
|
36
|
+
## What This SDK Does Not Do On Client
|
|
37
|
+
|
|
38
|
+
- Does not perform authoritative upstream pricing sync on the client.
|
|
39
|
+
- Does not do final backend-grade cost computation in the main intercept-and-flush path.
|
|
40
|
+
- Does not require provider credentials to leave the client environment.
|
|
41
|
+
|
|
42
|
+
## Architecture (Current)
|
|
43
|
+
|
|
44
|
+
1. Your app calls provider SDK (Anthropic/OpenAI/custom).
|
|
45
|
+
2. Wrapped method runs, returns original provider response.
|
|
46
|
+
3. Interceptor extracts usage/model/stop reason.
|
|
47
|
+
4. `RequestDetailsBuffer` stores request details locally.
|
|
48
|
+
5. Buffer flushes when:
|
|
49
|
+
- `FLUSH_BATCH_SIZE` reached (default `50`), or
|
|
50
|
+
- timer hits `FLUSH_INTERVAL_SECONDS` (default `30`).
|
|
51
|
+
6. If telemetry is configured, flushed batch is POSTed to backend (`/v1/telemetry/flush` by default).
|
|
52
|
+
7. Backend resolves pricing and computes final costs.
|
|
53
|
+
|
|
54
|
+
## Package Layout (Relevant)
|
|
55
|
+
|
|
56
|
+
- `my-sdk/src/sdk.py`: `CostAnalyticsSDK` facade.
|
|
57
|
+
- `my-sdk/src/pricing/interceptor.py`: wrapping and extraction pipeline.
|
|
58
|
+
- `my-sdk/src/pricing/extractors.py`: provider extractors.
|
|
59
|
+
- `my-sdk/src/pricing/aggregator.py`: request buffer + flush triggers.
|
|
60
|
+
- `my-sdk/src/api/telemetry.py`: telemetry sender with failed flush retention/retry behavior.
|
|
61
|
+
- `my-sdk/src/client.py`: authenticated API client (`CostAnalyticsClient`) with lazy key verification.
|
|
62
|
+
- `my-sdk/src/auth/Config.py`: env-based API key loading (`CA_API_KEY`).
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install -e .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Quick Start
|
|
71
|
+
|
|
72
|
+
### 1) Wrap a Provider Client
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from anthropic import Anthropic
|
|
76
|
+
from sdk import CostAnalyticsSDK
|
|
77
|
+
|
|
78
|
+
sdk = CostAnalyticsSDK(server_url="https://telemetry.example.com")
|
|
79
|
+
|
|
80
|
+
client = Anthropic()
|
|
81
|
+
client = sdk.wrap_client(
|
|
82
|
+
client=client,
|
|
83
|
+
provider="anthropic",
|
|
84
|
+
method_path="messages.create",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
response = client.messages.create(
|
|
88
|
+
model="claude-3-haiku-20240307",
|
|
89
|
+
max_tokens=128,
|
|
90
|
+
messages=[{"role": "user", "content": "Hello"}],
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
print(sdk.get_metrics())
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 2) Wrap Other Client Shapes
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from sdk import CostAnalyticsSDK
|
|
100
|
+
|
|
101
|
+
sdk = CostAnalyticsSDK(server_url="https://telemetry.example.com")
|
|
102
|
+
|
|
103
|
+
client = sdk.wrap_client(
|
|
104
|
+
client=client,
|
|
105
|
+
provider="custom-provider",
|
|
106
|
+
method_path="responses.create",
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 3) Manual Flush
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from pricing import get_request_buffer
|
|
114
|
+
|
|
115
|
+
buffer = get_request_buffer()
|
|
116
|
+
buffer.flush()
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Core APIs
|
|
120
|
+
|
|
121
|
+
### `CostAnalyticsSDK` (`my-sdk/src/sdk.py`)
|
|
122
|
+
|
|
123
|
+
- `CostAnalyticsSDK(server_url: Optional[str] = None)`
|
|
124
|
+
- `wrap_client(client, provider, method_path, response_to_dict=None, metadata=None)`
|
|
125
|
+
- `process_response(response, provider, request_id=None, metadata=None)`
|
|
126
|
+
- `get_metrics()`
|
|
127
|
+
- `get_pending_requests()`
|
|
128
|
+
- `flush_buffer()`
|
|
129
|
+
|
|
130
|
+
`get_metrics()` currently returns buffer-oriented metrics:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"buffer_size": 12,
|
|
135
|
+
"pending_requests": 12
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Buffer (`my-sdk/src/pricing/aggregator.py`)
|
|
140
|
+
|
|
141
|
+
- `FLUSH_BATCH_SIZE = 50`
|
|
142
|
+
- `FLUSH_INTERVAL_SECONDS = 30`
|
|
143
|
+
- `get_request_buffer()` / `get_cost_aggregator()`
|
|
144
|
+
- `RequestDetailsBuffer.record_request(...)`
|
|
145
|
+
- `RequestDetailsBuffer.flush()`
|
|
146
|
+
- `RequestDetailsBuffer.get_pending_requests()`
|
|
147
|
+
|
|
148
|
+
### Telemetry (`my-sdk/src/api/telemetry.py`)
|
|
149
|
+
|
|
150
|
+
`TelemetryClient` behavior:
|
|
151
|
+
|
|
152
|
+
- Sends flush payload to `POST {server_url}/v1/telemetry/flush` (default endpoint path).
|
|
153
|
+
- Includes headers:
|
|
154
|
+
- `Content-Type: application/json`
|
|
155
|
+
- `X-Client-ID: <uuid>`
|
|
156
|
+
- optional `Authorization: Bearer <api_key>`
|
|
157
|
+
- On flush failure, retains up to last 5 failed batches in-memory.
|
|
158
|
+
- If failure looks like "request not received", retries once immediately.
|
|
159
|
+
- Failed batches are retried on subsequent flush attempts.
|
|
160
|
+
|
|
161
|
+
## Auth for SDK-to-Server API Calls
|
|
162
|
+
|
|
163
|
+
`CostAnalyticsClient` (`my-sdk/src/client.py`) supports direct authenticated calls to server APIs.
|
|
164
|
+
|
|
165
|
+
- Reads `CA_API_KEY` when `api_key` not passed.
|
|
166
|
+
- Performs lazy auth verification against `GET /v1/auth/verify` (default path).
|
|
167
|
+
- Sends request metadata headers:
|
|
168
|
+
- `Authorization`
|
|
169
|
+
- `X-CA-Key-Id`
|
|
170
|
+
- `X-CA-User-Id`
|
|
171
|
+
- `X-Request-Id`
|
|
172
|
+
- `X-CA-Provider`
|
|
173
|
+
- `X-CA-Model`
|
|
174
|
+
|
|
175
|
+
Example:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from client import CostAnalyticsClient
|
|
179
|
+
|
|
180
|
+
client = CostAnalyticsClient(
|
|
181
|
+
api_key="ca_live_...",
|
|
182
|
+
base_url="https://api.example.com",
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
resp = client.request("GET", "/v1/costs")
|
|
186
|
+
print(resp.status_code)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Environment Variables
|
|
190
|
+
|
|
191
|
+
- `CA_API_KEY`: client API key for `CostAnalyticsClient`.
|
|
192
|
+
- `CA_API_BASE_URL`: optional base URL override for `CostAnalyticsClient`.
|
|
193
|
+
|
|
194
|
+
Note: backend services may require additional variables (for example server HMAC secret) that are configured in the server repository.
|
|
195
|
+
|
|
196
|
+
## Backend Contract Expectations
|
|
197
|
+
|
|
198
|
+
Telemetry flush endpoint should accept payload in this shape:
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"client_id": "uuid",
|
|
203
|
+
"batch": [
|
|
204
|
+
{
|
|
205
|
+
"timestamp": "2026-01-01T00:00:00.000000",
|
|
206
|
+
"request_id": "req-123",
|
|
207
|
+
"model": "claude-3-haiku-20240307",
|
|
208
|
+
"provider": "anthropic",
|
|
209
|
+
"input_tokens": 100,
|
|
210
|
+
"output_tokens": 50,
|
|
211
|
+
"cache_read_tokens": 0,
|
|
212
|
+
"cache_creation_tokens": 0,
|
|
213
|
+
"stop_reason": "end_turn",
|
|
214
|
+
"metadata": {"method": "messages.create"}
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Auth verification endpoint expected by `CostAnalyticsClient`:
|
|
221
|
+
|
|
222
|
+
- `GET /v1/auth/verify`
|
|
223
|
+
- Response:
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"user_id": "...",
|
|
228
|
+
"api_key_id": "..."
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Testing
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
pip install -e ".[dev]"
|
|
236
|
+
pytest tests/ -v
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Notes on Compatibility Surface
|
|
240
|
+
|
|
241
|
+
The SDK exposes one generic extractor/interceptor path. Provider names are metadata values passed to `wrap_client`; authoritative pricing resolution lives on the backend.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# LLM Cost Observability SDK
|
|
2
|
+
|
|
3
|
+
Client-side SDK for provider response interception, usage extraction, request buffering, and optional telemetry flush to a backend cost pipeline.
|
|
4
|
+
## What This SDK Does
|
|
5
|
+
|
|
6
|
+
- Wraps provider SDK methods without changing normal response behavior.
|
|
7
|
+
- Extracts usage metadata from provider responses locally.
|
|
8
|
+
- Buffers request details in-memory.
|
|
9
|
+
- Flushes buffered batches on size/time thresholds.
|
|
10
|
+
- Optionally sends flushed batches to backend telemetry endpoint.
|
|
11
|
+
- Supports lazy API-key verification for direct server API calls via `CostAnalyticsClient`.
|
|
12
|
+
|
|
13
|
+
## What This SDK Does Not Do On Client
|
|
14
|
+
|
|
15
|
+
- Does not perform authoritative upstream pricing sync on the client.
|
|
16
|
+
- Does not do final backend-grade cost computation in the main intercept-and-flush path.
|
|
17
|
+
- Does not require provider credentials to leave the client environment.
|
|
18
|
+
|
|
19
|
+
## Architecture (Current)
|
|
20
|
+
|
|
21
|
+
1. Your app calls provider SDK (Anthropic/OpenAI/custom).
|
|
22
|
+
2. Wrapped method runs, returns original provider response.
|
|
23
|
+
3. Interceptor extracts usage/model/stop reason.
|
|
24
|
+
4. `RequestDetailsBuffer` stores request details locally.
|
|
25
|
+
5. Buffer flushes when:
|
|
26
|
+
- `FLUSH_BATCH_SIZE` reached (default `50`), or
|
|
27
|
+
- timer hits `FLUSH_INTERVAL_SECONDS` (default `30`).
|
|
28
|
+
6. If telemetry is configured, flushed batch is POSTed to backend (`/v1/telemetry/flush` by default).
|
|
29
|
+
7. Backend resolves pricing and computes final costs.
|
|
30
|
+
|
|
31
|
+
## Package Layout (Relevant)
|
|
32
|
+
|
|
33
|
+
- `my-sdk/src/sdk.py`: `CostAnalyticsSDK` facade.
|
|
34
|
+
- `my-sdk/src/pricing/interceptor.py`: wrapping and extraction pipeline.
|
|
35
|
+
- `my-sdk/src/pricing/extractors.py`: provider extractors.
|
|
36
|
+
- `my-sdk/src/pricing/aggregator.py`: request buffer + flush triggers.
|
|
37
|
+
- `my-sdk/src/api/telemetry.py`: telemetry sender with failed flush retention/retry behavior.
|
|
38
|
+
- `my-sdk/src/client.py`: authenticated API client (`CostAnalyticsClient`) with lazy key verification.
|
|
39
|
+
- `my-sdk/src/auth/Config.py`: env-based API key loading (`CA_API_KEY`).
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install -e .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
### 1) Wrap a Provider Client
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from anthropic import Anthropic
|
|
53
|
+
from sdk import CostAnalyticsSDK
|
|
54
|
+
|
|
55
|
+
sdk = CostAnalyticsSDK(server_url="https://telemetry.example.com")
|
|
56
|
+
|
|
57
|
+
client = Anthropic()
|
|
58
|
+
client = sdk.wrap_client(
|
|
59
|
+
client=client,
|
|
60
|
+
provider="anthropic",
|
|
61
|
+
method_path="messages.create",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
response = client.messages.create(
|
|
65
|
+
model="claude-3-haiku-20240307",
|
|
66
|
+
max_tokens=128,
|
|
67
|
+
messages=[{"role": "user", "content": "Hello"}],
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
print(sdk.get_metrics())
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2) Wrap Other Client Shapes
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from sdk import CostAnalyticsSDK
|
|
77
|
+
|
|
78
|
+
sdk = CostAnalyticsSDK(server_url="https://telemetry.example.com")
|
|
79
|
+
|
|
80
|
+
client = sdk.wrap_client(
|
|
81
|
+
client=client,
|
|
82
|
+
provider="custom-provider",
|
|
83
|
+
method_path="responses.create",
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 3) Manual Flush
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from pricing import get_request_buffer
|
|
91
|
+
|
|
92
|
+
buffer = get_request_buffer()
|
|
93
|
+
buffer.flush()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Core APIs
|
|
97
|
+
|
|
98
|
+
### `CostAnalyticsSDK` (`my-sdk/src/sdk.py`)
|
|
99
|
+
|
|
100
|
+
- `CostAnalyticsSDK(server_url: Optional[str] = None)`
|
|
101
|
+
- `wrap_client(client, provider, method_path, response_to_dict=None, metadata=None)`
|
|
102
|
+
- `process_response(response, provider, request_id=None, metadata=None)`
|
|
103
|
+
- `get_metrics()`
|
|
104
|
+
- `get_pending_requests()`
|
|
105
|
+
- `flush_buffer()`
|
|
106
|
+
|
|
107
|
+
`get_metrics()` currently returns buffer-oriented metrics:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"buffer_size": 12,
|
|
112
|
+
"pending_requests": 12
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Buffer (`my-sdk/src/pricing/aggregator.py`)
|
|
117
|
+
|
|
118
|
+
- `FLUSH_BATCH_SIZE = 50`
|
|
119
|
+
- `FLUSH_INTERVAL_SECONDS = 30`
|
|
120
|
+
- `get_request_buffer()` / `get_cost_aggregator()`
|
|
121
|
+
- `RequestDetailsBuffer.record_request(...)`
|
|
122
|
+
- `RequestDetailsBuffer.flush()`
|
|
123
|
+
- `RequestDetailsBuffer.get_pending_requests()`
|
|
124
|
+
|
|
125
|
+
### Telemetry (`my-sdk/src/api/telemetry.py`)
|
|
126
|
+
|
|
127
|
+
`TelemetryClient` behavior:
|
|
128
|
+
|
|
129
|
+
- Sends flush payload to `POST {server_url}/v1/telemetry/flush` (default endpoint path).
|
|
130
|
+
- Includes headers:
|
|
131
|
+
- `Content-Type: application/json`
|
|
132
|
+
- `X-Client-ID: <uuid>`
|
|
133
|
+
- optional `Authorization: Bearer <api_key>`
|
|
134
|
+
- On flush failure, retains up to last 5 failed batches in-memory.
|
|
135
|
+
- If failure looks like "request not received", retries once immediately.
|
|
136
|
+
- Failed batches are retried on subsequent flush attempts.
|
|
137
|
+
|
|
138
|
+
## Auth for SDK-to-Server API Calls
|
|
139
|
+
|
|
140
|
+
`CostAnalyticsClient` (`my-sdk/src/client.py`) supports direct authenticated calls to server APIs.
|
|
141
|
+
|
|
142
|
+
- Reads `CA_API_KEY` when `api_key` not passed.
|
|
143
|
+
- Performs lazy auth verification against `GET /v1/auth/verify` (default path).
|
|
144
|
+
- Sends request metadata headers:
|
|
145
|
+
- `Authorization`
|
|
146
|
+
- `X-CA-Key-Id`
|
|
147
|
+
- `X-CA-User-Id`
|
|
148
|
+
- `X-Request-Id`
|
|
149
|
+
- `X-CA-Provider`
|
|
150
|
+
- `X-CA-Model`
|
|
151
|
+
|
|
152
|
+
Example:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from client import CostAnalyticsClient
|
|
156
|
+
|
|
157
|
+
client = CostAnalyticsClient(
|
|
158
|
+
api_key="ca_live_...",
|
|
159
|
+
base_url="https://api.example.com",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
resp = client.request("GET", "/v1/costs")
|
|
163
|
+
print(resp.status_code)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Environment Variables
|
|
167
|
+
|
|
168
|
+
- `CA_API_KEY`: client API key for `CostAnalyticsClient`.
|
|
169
|
+
- `CA_API_BASE_URL`: optional base URL override for `CostAnalyticsClient`.
|
|
170
|
+
|
|
171
|
+
Note: backend services may require additional variables (for example server HMAC secret) that are configured in the server repository.
|
|
172
|
+
|
|
173
|
+
## Backend Contract Expectations
|
|
174
|
+
|
|
175
|
+
Telemetry flush endpoint should accept payload in this shape:
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"client_id": "uuid",
|
|
180
|
+
"batch": [
|
|
181
|
+
{
|
|
182
|
+
"timestamp": "2026-01-01T00:00:00.000000",
|
|
183
|
+
"request_id": "req-123",
|
|
184
|
+
"model": "claude-3-haiku-20240307",
|
|
185
|
+
"provider": "anthropic",
|
|
186
|
+
"input_tokens": 100,
|
|
187
|
+
"output_tokens": 50,
|
|
188
|
+
"cache_read_tokens": 0,
|
|
189
|
+
"cache_creation_tokens": 0,
|
|
190
|
+
"stop_reason": "end_turn",
|
|
191
|
+
"metadata": {"method": "messages.create"}
|
|
192
|
+
}
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Auth verification endpoint expected by `CostAnalyticsClient`:
|
|
198
|
+
|
|
199
|
+
- `GET /v1/auth/verify`
|
|
200
|
+
- Response:
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"user_id": "...",
|
|
205
|
+
"api_key_id": "..."
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Testing
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
pip install -e ".[dev]"
|
|
213
|
+
pytest tests/ -v
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Notes on Compatibility Surface
|
|
217
|
+
|
|
218
|
+
The SDK exposes one generic extractor/interceptor path. Provider names are metadata values passed to `wrap_client`; authoritative pricing resolution lives on the backend.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ModelMetre"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Multi-provider LLM cost observability SDK"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license ="MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Jyotir Nandan", email = "jyotirnandan92129@gmail.com"}
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
dependencies = [
|
|
22
|
+
"requests>=2.31.0",
|
|
23
|
+
"pydantic>=2.0.0",
|
|
24
|
+
"httpx>=0.24.0",
|
|
25
|
+
]
|
|
26
|
+
[project.urls]
|
|
27
|
+
Repository = "https://github.com/Kakashi15-pix/ModelMetre"
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = [
|
|
31
|
+
"pytest>=7.4.0",
|
|
32
|
+
"pytest-cov>=4.1.0",
|
|
33
|
+
"pytest-mock>=3.11.0",
|
|
34
|
+
"black>=23.0.0",
|
|
35
|
+
"ruff>=0.0.280",
|
|
36
|
+
"mypy>=1.4.0",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[tool.setuptools]
|
|
40
|
+
package-dir = {"" = "src"}
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.packages.find]
|
|
43
|
+
where = ["src"]
|
|
44
|
+
|
|
45
|
+
[tool.black]
|
|
46
|
+
line-length = 88
|
|
47
|
+
target-version = ['py39']
|
|
48
|
+
|
|
49
|
+
[tool.ruff]
|
|
50
|
+
line-length = 88
|
|
51
|
+
target-version = "py39"
|
|
52
|
+
|
|
53
|
+
[tool.mypy]
|
|
54
|
+
python_version = "3.9"
|
|
55
|
+
warn_return_any = true
|
|
56
|
+
warn_unused_configs = true
|
|
57
|
+
disallow_untyped_defs = false
|