superpenguin 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.
@@ -0,0 +1,18 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ *.egg
6
+ dist/
7
+ build/
8
+ .eggs/
9
+ *.so
10
+ .Python
11
+ env/
12
+ venv/
13
+ .venv/
14
+ .env
15
+ *.whl
16
+ .pytest_cache/
17
+ .mypy_cache/
18
+ .ruff_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Carrot Labs AI, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,289 @@
1
+ Metadata-Version: 2.4
2
+ Name: superpenguin
3
+ Version: 0.1.0
4
+ Summary: SuperPenguin Python SDK — AI cost management, attribution, and spend tracking
5
+ Project-URL: Homepage, https://carrotlabs.ai
6
+ Project-URL: Documentation, https://carrotlabs.ai
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Keywords: ai,anthropic,attribution,cost-management,litellm,llm,monitoring,observability,openai,spend-tracking
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.9
23
+ Provides-Extra: anthropic
24
+ Requires-Dist: anthropic; extra == 'anthropic'
25
+ Provides-Extra: dev
26
+ Requires-Dist: anthropic; extra == 'dev'
27
+ Requires-Dist: openai; extra == 'dev'
28
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
29
+ Provides-Extra: openai
30
+ Requires-Dist: openai; extra == 'openai'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # SuperPenguin Python SDK
34
+
35
+ Track AI costs automatically. Wrap your OpenAI or Anthropic client — or patch litellm — and every LLM call is captured with token counts, estimated cost, latency, and attribution metadata. No proxy required.
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install superpenguin
41
+ ```
42
+
43
+ Or install from source (in the `sdk/python/` directory):
44
+
45
+ ```bash
46
+ pip install -e .
47
+ ```
48
+
49
+ ## Quick Start
50
+
51
+ ### 1. Wrap your client (one line)
52
+
53
+ ```python
54
+ import superpenguin as sp
55
+ from openai import OpenAI
56
+
57
+ sp.init(api_key="sp_...") # your SuperPenguin API key
58
+
59
+ client = sp.wrap(OpenAI())
60
+
61
+ # Use the client exactly as normal — cost events are captured automatically
62
+ response = client.chat.completions.create(
63
+ model="gpt-4o",
64
+ messages=[{"role": "user", "content": "Hello!"}],
65
+ )
66
+ print(response.choices[0].message.content)
67
+ ```
68
+
69
+ That's it. Every `create()` call through the wrapped client is captured with provider, model, token counts, estimated cost (USD), and latency.
70
+
71
+ ### 2. Works with Anthropic too
72
+
73
+ ```python
74
+ import superpenguin as sp
75
+ from anthropic import Anthropic
76
+
77
+ sp.init(api_key="sp_...")
78
+
79
+ client = sp.wrap(Anthropic())
80
+
81
+ response = client.messages.create(
82
+ model="claude-sonnet-4-20250514",
83
+ max_tokens=1024,
84
+ messages=[{"role": "user", "content": "Hello!"}],
85
+ )
86
+ ```
87
+
88
+ ### 3. Streaming works transparently
89
+
90
+ ```python
91
+ client = sp.wrap(OpenAI())
92
+
93
+ stream = client.chat.completions.create(
94
+ model="gpt-4o",
95
+ messages=[{"role": "user", "content": "Tell me a story"}],
96
+ stream=True,
97
+ )
98
+
99
+ for chunk in stream:
100
+ if chunk.choices and chunk.choices[0].delta.content:
101
+ print(chunk.choices[0].delta.content, end="", flush=True)
102
+
103
+ # Cost event is submitted automatically when the stream finishes
104
+ ```
105
+
106
+ ### 4. LiteLLM support
107
+
108
+ If you use [litellm](https://github.com/BerriAI/litellm) to call 100+ LLM providers through a single interface, one call patches everything:
109
+
110
+ ```python
111
+ import superpenguin as sp
112
+ import litellm
113
+
114
+ sp.init(api_key="sp_...")
115
+ sp.patch_litellm()
116
+
117
+ # Every litellm.completion() / litellm.acompletion() is now tracked
118
+ response = litellm.completion(
119
+ model="openai/gpt-4o",
120
+ messages=[{"role": "user", "content": "Hello!"}],
121
+ )
122
+ ```
123
+
124
+ ### 5. Add attribution metadata
125
+
126
+ Attach metadata to attribute costs to customers, features, teams, or environments:
127
+
128
+ ```python
129
+ # Set defaults for all calls from this client
130
+ client = sp.wrap(OpenAI(), metadata={
131
+ "customer_id": "cust_acme_123",
132
+ "feature": "doc_summary",
133
+ "team": "product",
134
+ "environment": "production",
135
+ })
136
+
137
+ # Or override per-call via extra_body
138
+ response = client.chat.completions.create(
139
+ model="gpt-4o",
140
+ messages=[{"role": "user", "content": "Summarize this document"}],
141
+ extra_body={
142
+ "sp_metadata": {
143
+ "customer_id": "cust_other_456",
144
+ "prompt_key": "summarize_v2",
145
+ "prompt_version": "3",
146
+ }
147
+ },
148
+ )
149
+ ```
150
+
151
+ ## `@sp.trace` Decorator
152
+
153
+ For multi-step pipelines (RAG, agents, chains), use the `@sp.trace` decorator. Any wrapped LLM calls inside the function are automatically linked as children.
154
+
155
+ ```python
156
+ import superpenguin as sp
157
+ from openai import OpenAI
158
+
159
+ sp.init(api_key="sp_...")
160
+ client = sp.wrap(OpenAI())
161
+
162
+
163
+ @sp.trace
164
+ def answer_question(question: str) -> str:
165
+ docs = search_knowledge_base(question)
166
+
167
+ response = client.chat.completions.create(
168
+ model="gpt-4o",
169
+ messages=[
170
+ {"role": "system", "content": f"Context:\n{docs}"},
171
+ {"role": "user", "content": question},
172
+ ],
173
+ )
174
+
175
+ return response.choices[0].message.content
176
+
177
+
178
+ result = answer_question("How do I reset my password?")
179
+ ```
180
+
181
+ ### Decorator variants
182
+
183
+ ```python
184
+ @sp.trace
185
+ def my_function(): ...
186
+
187
+ @sp.trace("my-pipeline")
188
+ def my_function(): ...
189
+
190
+ @sp.trace(name="my-pipeline", tags=["production"], metadata={"customer_id": "acme"})
191
+ def my_function(): ...
192
+ ```
193
+
194
+ ### Async support
195
+
196
+ Both `wrap()` and `@sp.trace` work with async clients and functions:
197
+
198
+ ```python
199
+ from openai import AsyncOpenAI
200
+
201
+ client = sp.wrap(AsyncOpenAI())
202
+
203
+
204
+ @sp.trace
205
+ async def answer_question(question: str) -> str:
206
+ response = await client.chat.completions.create(
207
+ model="gpt-4o",
208
+ messages=[{"role": "user", "content": question}],
209
+ )
210
+ return response.choices[0].message.content
211
+ ```
212
+
213
+ ## Configuration
214
+
215
+ ### `sp.init()`
216
+
217
+ | Parameter | Type | Default | Description |
218
+ |-----------|------|---------|-------------|
219
+ | `api_key` | `str` | `SP_API_KEY` env var | Your SuperPenguin API key |
220
+ | `base_url` | `str` | `https://api.carrotlabs.ai` | API endpoint |
221
+ | `flush_interval` | `float` | `5.0` | Seconds between background batch flushes |
222
+ | `batch_size` | `int` | `50` | Max events per batch POST |
223
+
224
+ ### Environment variables
225
+
226
+ | Variable | Description |
227
+ |----------|-------------|
228
+ | `SP_API_KEY` | API key (used if not passed to `init()`) |
229
+ | `SP_BASE_URL` | API base URL override |
230
+
231
+ If `SP_API_KEY` is set, `init()` is called automatically on first use.
232
+
233
+ ### `sp.wrap()`
234
+
235
+ | Parameter | Type | Default | Description |
236
+ |-----------|------|---------|-------------|
237
+ | `client` | `OpenAI \| Anthropic` | required | The LLM client to wrap |
238
+ | `name` | `str` | `None` | Override the default event name |
239
+ | `metadata` | `dict` | `None` | Default metadata for every call (customer_id, feature, team, etc.) |
240
+ | `tags` | `list[str]` | `None` | Tags added to every event |
241
+
242
+ ### `sp.patch_litellm()`
243
+
244
+ | Parameter | Type | Default | Description |
245
+ |-----------|------|---------|-------------|
246
+ | `name` | `str` | `None` | Override the default event name |
247
+ | `metadata` | `dict` | `None` | Default metadata for every litellm call |
248
+ | `tags` | `list[str]` | `None` | Tags added to every event |
249
+
250
+ ### `sp.flush()`
251
+
252
+ Force-flush any pending events. Useful before process exit in short-lived scripts:
253
+
254
+ ```python
255
+ sp.flush()
256
+ ```
257
+
258
+ An `atexit` handler also flushes automatically on normal interpreter shutdown.
259
+
260
+ ## Metadata Fields
261
+
262
+ | Field | Type | Purpose |
263
+ |-------|------|---------|
264
+ | `customer_id` | string | End-customer or account consuming the AI call |
265
+ | `feature` | string | Product feature name (e.g., `search`, `support_agent`) |
266
+ | `team` | string | Internal team owning the feature |
267
+ | `environment` | string | `production`, `staging`, `dev`, etc. |
268
+ | `prompt_key` | string | Identifier for the prompt template |
269
+ | `prompt_version` | string | Version of the prompt template |
270
+ | Any other key | string | Stored as custom tags, queryable in the dashboard |
271
+
272
+ ## What Gets Tracked
273
+
274
+ Each event includes:
275
+
276
+ | Field | Description |
277
+ |-------|-------------|
278
+ | `provider` | `"openai"`, `"anthropic"`, or `"litellm"` |
279
+ | `model` | Model name used |
280
+ | `input_tokens` | Prompt token count |
281
+ | `output_tokens` | Completion token count |
282
+ | `cached_tokens` | Cached prompt tokens (if applicable) |
283
+ | `cost_usd_micros` | Estimated cost in USD micros (1 USD = 1,000,000 micros) |
284
+ | `latency_ms` | End-to-end call duration |
285
+ | `streaming` | Whether the call was streamed |
286
+ | `has_tools` | Whether tool calls were used |
287
+ | `has_vision` | Whether image inputs were included |
288
+
289
+ **Never captured:** Prompt content, response content, images, audio, tool arguments, or function results. The SDK only captures cost-relevant metadata.
@@ -0,0 +1,257 @@
1
+ # SuperPenguin Python SDK
2
+
3
+ Track AI costs automatically. Wrap your OpenAI or Anthropic client — or patch litellm — and every LLM call is captured with token counts, estimated cost, latency, and attribution metadata. No proxy required.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install superpenguin
9
+ ```
10
+
11
+ Or install from source (in the `sdk/python/` directory):
12
+
13
+ ```bash
14
+ pip install -e .
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ### 1. Wrap your client (one line)
20
+
21
+ ```python
22
+ import superpenguin as sp
23
+ from openai import OpenAI
24
+
25
+ sp.init(api_key="sp_...") # your SuperPenguin API key
26
+
27
+ client = sp.wrap(OpenAI())
28
+
29
+ # Use the client exactly as normal — cost events are captured automatically
30
+ response = client.chat.completions.create(
31
+ model="gpt-4o",
32
+ messages=[{"role": "user", "content": "Hello!"}],
33
+ )
34
+ print(response.choices[0].message.content)
35
+ ```
36
+
37
+ That's it. Every `create()` call through the wrapped client is captured with provider, model, token counts, estimated cost (USD), and latency.
38
+
39
+ ### 2. Works with Anthropic too
40
+
41
+ ```python
42
+ import superpenguin as sp
43
+ from anthropic import Anthropic
44
+
45
+ sp.init(api_key="sp_...")
46
+
47
+ client = sp.wrap(Anthropic())
48
+
49
+ response = client.messages.create(
50
+ model="claude-sonnet-4-20250514",
51
+ max_tokens=1024,
52
+ messages=[{"role": "user", "content": "Hello!"}],
53
+ )
54
+ ```
55
+
56
+ ### 3. Streaming works transparently
57
+
58
+ ```python
59
+ client = sp.wrap(OpenAI())
60
+
61
+ stream = client.chat.completions.create(
62
+ model="gpt-4o",
63
+ messages=[{"role": "user", "content": "Tell me a story"}],
64
+ stream=True,
65
+ )
66
+
67
+ for chunk in stream:
68
+ if chunk.choices and chunk.choices[0].delta.content:
69
+ print(chunk.choices[0].delta.content, end="", flush=True)
70
+
71
+ # Cost event is submitted automatically when the stream finishes
72
+ ```
73
+
74
+ ### 4. LiteLLM support
75
+
76
+ If you use [litellm](https://github.com/BerriAI/litellm) to call 100+ LLM providers through a single interface, one call patches everything:
77
+
78
+ ```python
79
+ import superpenguin as sp
80
+ import litellm
81
+
82
+ sp.init(api_key="sp_...")
83
+ sp.patch_litellm()
84
+
85
+ # Every litellm.completion() / litellm.acompletion() is now tracked
86
+ response = litellm.completion(
87
+ model="openai/gpt-4o",
88
+ messages=[{"role": "user", "content": "Hello!"}],
89
+ )
90
+ ```
91
+
92
+ ### 5. Add attribution metadata
93
+
94
+ Attach metadata to attribute costs to customers, features, teams, or environments:
95
+
96
+ ```python
97
+ # Set defaults for all calls from this client
98
+ client = sp.wrap(OpenAI(), metadata={
99
+ "customer_id": "cust_acme_123",
100
+ "feature": "doc_summary",
101
+ "team": "product",
102
+ "environment": "production",
103
+ })
104
+
105
+ # Or override per-call via extra_body
106
+ response = client.chat.completions.create(
107
+ model="gpt-4o",
108
+ messages=[{"role": "user", "content": "Summarize this document"}],
109
+ extra_body={
110
+ "sp_metadata": {
111
+ "customer_id": "cust_other_456",
112
+ "prompt_key": "summarize_v2",
113
+ "prompt_version": "3",
114
+ }
115
+ },
116
+ )
117
+ ```
118
+
119
+ ## `@sp.trace` Decorator
120
+
121
+ For multi-step pipelines (RAG, agents, chains), use the `@sp.trace` decorator. Any wrapped LLM calls inside the function are automatically linked as children.
122
+
123
+ ```python
124
+ import superpenguin as sp
125
+ from openai import OpenAI
126
+
127
+ sp.init(api_key="sp_...")
128
+ client = sp.wrap(OpenAI())
129
+
130
+
131
+ @sp.trace
132
+ def answer_question(question: str) -> str:
133
+ docs = search_knowledge_base(question)
134
+
135
+ response = client.chat.completions.create(
136
+ model="gpt-4o",
137
+ messages=[
138
+ {"role": "system", "content": f"Context:\n{docs}"},
139
+ {"role": "user", "content": question},
140
+ ],
141
+ )
142
+
143
+ return response.choices[0].message.content
144
+
145
+
146
+ result = answer_question("How do I reset my password?")
147
+ ```
148
+
149
+ ### Decorator variants
150
+
151
+ ```python
152
+ @sp.trace
153
+ def my_function(): ...
154
+
155
+ @sp.trace("my-pipeline")
156
+ def my_function(): ...
157
+
158
+ @sp.trace(name="my-pipeline", tags=["production"], metadata={"customer_id": "acme"})
159
+ def my_function(): ...
160
+ ```
161
+
162
+ ### Async support
163
+
164
+ Both `wrap()` and `@sp.trace` work with async clients and functions:
165
+
166
+ ```python
167
+ from openai import AsyncOpenAI
168
+
169
+ client = sp.wrap(AsyncOpenAI())
170
+
171
+
172
+ @sp.trace
173
+ async def answer_question(question: str) -> str:
174
+ response = await client.chat.completions.create(
175
+ model="gpt-4o",
176
+ messages=[{"role": "user", "content": question}],
177
+ )
178
+ return response.choices[0].message.content
179
+ ```
180
+
181
+ ## Configuration
182
+
183
+ ### `sp.init()`
184
+
185
+ | Parameter | Type | Default | Description |
186
+ |-----------|------|---------|-------------|
187
+ | `api_key` | `str` | `SP_API_KEY` env var | Your SuperPenguin API key |
188
+ | `base_url` | `str` | `https://api.carrotlabs.ai` | API endpoint |
189
+ | `flush_interval` | `float` | `5.0` | Seconds between background batch flushes |
190
+ | `batch_size` | `int` | `50` | Max events per batch POST |
191
+
192
+ ### Environment variables
193
+
194
+ | Variable | Description |
195
+ |----------|-------------|
196
+ | `SP_API_KEY` | API key (used if not passed to `init()`) |
197
+ | `SP_BASE_URL` | API base URL override |
198
+
199
+ If `SP_API_KEY` is set, `init()` is called automatically on first use.
200
+
201
+ ### `sp.wrap()`
202
+
203
+ | Parameter | Type | Default | Description |
204
+ |-----------|------|---------|-------------|
205
+ | `client` | `OpenAI \| Anthropic` | required | The LLM client to wrap |
206
+ | `name` | `str` | `None` | Override the default event name |
207
+ | `metadata` | `dict` | `None` | Default metadata for every call (customer_id, feature, team, etc.) |
208
+ | `tags` | `list[str]` | `None` | Tags added to every event |
209
+
210
+ ### `sp.patch_litellm()`
211
+
212
+ | Parameter | Type | Default | Description |
213
+ |-----------|------|---------|-------------|
214
+ | `name` | `str` | `None` | Override the default event name |
215
+ | `metadata` | `dict` | `None` | Default metadata for every litellm call |
216
+ | `tags` | `list[str]` | `None` | Tags added to every event |
217
+
218
+ ### `sp.flush()`
219
+
220
+ Force-flush any pending events. Useful before process exit in short-lived scripts:
221
+
222
+ ```python
223
+ sp.flush()
224
+ ```
225
+
226
+ An `atexit` handler also flushes automatically on normal interpreter shutdown.
227
+
228
+ ## Metadata Fields
229
+
230
+ | Field | Type | Purpose |
231
+ |-------|------|---------|
232
+ | `customer_id` | string | End-customer or account consuming the AI call |
233
+ | `feature` | string | Product feature name (e.g., `search`, `support_agent`) |
234
+ | `team` | string | Internal team owning the feature |
235
+ | `environment` | string | `production`, `staging`, `dev`, etc. |
236
+ | `prompt_key` | string | Identifier for the prompt template |
237
+ | `prompt_version` | string | Version of the prompt template |
238
+ | Any other key | string | Stored as custom tags, queryable in the dashboard |
239
+
240
+ ## What Gets Tracked
241
+
242
+ Each event includes:
243
+
244
+ | Field | Description |
245
+ |-------|-------------|
246
+ | `provider` | `"openai"`, `"anthropic"`, or `"litellm"` |
247
+ | `model` | Model name used |
248
+ | `input_tokens` | Prompt token count |
249
+ | `output_tokens` | Completion token count |
250
+ | `cached_tokens` | Cached prompt tokens (if applicable) |
251
+ | `cost_usd_micros` | Estimated cost in USD micros (1 USD = 1,000,000 micros) |
252
+ | `latency_ms` | End-to-end call duration |
253
+ | `streaming` | Whether the call was streamed |
254
+ | `has_tools` | Whether tool calls were used |
255
+ | `has_vision` | Whether image inputs were included |
256
+
257
+ **Never captured:** Prompt content, response content, images, audio, tool arguments, or function results. The SDK only captures cost-relevant metadata.
@@ -0,0 +1,53 @@
1
+ [project]
2
+ name = "superpenguin"
3
+ version = "0.1.0"
4
+ description = "SuperPenguin Python SDK — AI cost management, attribution, and spend tracking"
5
+ readme = "README.md"
6
+ requires-python = ">=3.9"
7
+ license = "MIT"
8
+ keywords = [
9
+ "llm",
10
+ "cost-management",
11
+ "observability",
12
+ "openai",
13
+ "anthropic",
14
+ "litellm",
15
+ "ai",
16
+ "monitoring",
17
+ "attribution",
18
+ "spend-tracking",
19
+ ]
20
+ classifiers = [
21
+ "Development Status :: 3 - Alpha",
22
+ "Intended Audience :: Developers",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Operating System :: OS Independent",
25
+ "Programming Language :: Python :: 3",
26
+ "Programming Language :: Python :: 3.9",
27
+ "Programming Language :: Python :: 3.10",
28
+ "Programming Language :: Python :: 3.11",
29
+ "Programming Language :: Python :: 3.12",
30
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
31
+ "Topic :: Software Development :: Libraries :: Python Modules",
32
+ "Typing :: Typed",
33
+ ]
34
+ dependencies = []
35
+
36
+ [project.urls]
37
+ Homepage = "https://carrotlabs.ai"
38
+ Documentation = "https://carrotlabs.ai"
39
+
40
+ [project.optional-dependencies]
41
+ openai = ["openai"]
42
+ anthropic = ["anthropic"]
43
+ dev = ["pytest>=7.0.0", "openai", "anthropic"]
44
+
45
+ [build-system]
46
+ requires = ["hatchling"]
47
+ build-backend = "hatchling.build"
48
+
49
+ [tool.hatch.build.targets.wheel]
50
+ packages = ["src/superpenguin"]
51
+
52
+ [tool.pytest.ini_options]
53
+ testpaths = ["tests"]