spendlens 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,46 @@
1
+ Metadata-Version: 2.4
2
+ Name: spendlens
3
+ Version: 0.1.0
4
+ Summary: LLM cost attribution — track OpenAI spend by user, feature, and endpoint
5
+ Home-page: https://github.com/ChrisRobinT/spendlens.dev-product
6
+ Author: Chris Robin
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: openai>=1.0.0
10
+ Dynamic: author
11
+ Dynamic: description
12
+ Dynamic: description-content-type
13
+ Dynamic: home-page
14
+ Dynamic: requires-dist
15
+ Dynamic: requires-python
16
+ Dynamic: summary
17
+
18
+ # SpendLens
19
+
20
+ Track your OpenAI costs by user, feature, and endpoint. Two-line integration.
21
+
22
+ ## Install
23
+
24
+ ```
25
+ pip install spendlens
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ```python
31
+ from spendlens import SpendLensClient
32
+
33
+ client = SpendLensClient(api_key="your-spendlens-api-key")
34
+
35
+ response = client.chat.completions.create(
36
+ model="gpt-4o",
37
+ messages=[{"role": "user", "content": "Hello"}],
38
+ tags={"user_id": "u_123", "feature": "chat", "endpoint": "/api/chat"},
39
+ )
40
+ ```
41
+
42
+ Your costs appear on the dashboard. That's it.
43
+
44
+ - Never logs prompts or completions. Only metadata.
45
+ - Never modifies the OpenAI response.
46
+ - Never adds latency. Logging is async.
@@ -0,0 +1,29 @@
1
+ # SpendLens
2
+
3
+ Track your OpenAI costs by user, feature, and endpoint. Two-line integration.
4
+
5
+ ## Install
6
+
7
+ ```
8
+ pip install spendlens
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```python
14
+ from spendlens import SpendLensClient
15
+
16
+ client = SpendLensClient(api_key="your-spendlens-api-key")
17
+
18
+ response = client.chat.completions.create(
19
+ model="gpt-4o",
20
+ messages=[{"role": "user", "content": "Hello"}],
21
+ tags={"user_id": "u_123", "feature": "chat", "endpoint": "/api/chat"},
22
+ )
23
+ ```
24
+
25
+ Your costs appear on the dashboard. That's it.
26
+
27
+ - Never logs prompts or completions. Only metadata.
28
+ - Never modifies the OpenAI response.
29
+ - Never adds latency. Logging is async.
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,14 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="spendlens",
5
+ version="0.1.0",
6
+ description="LLM cost attribution — track OpenAI spend by user, feature, and endpoint",
7
+ long_description=open("README.md").read(),
8
+ long_description_content_type="text/markdown",
9
+ author="Chris Robin",
10
+ url="https://github.com/ChrisRobinT/spendlens.dev-product",
11
+ packages=find_packages(),
12
+ python_requires=">=3.8",
13
+ install_requires=["openai>=1.0.0"],
14
+ )
@@ -0,0 +1,87 @@
1
+ import time
2
+ import threading
3
+ import json
4
+ from urllib.request import Request, urlopen
5
+ from openai import OpenAI
6
+ from .pricing import PRICING
7
+
8
+
9
+ def _compute_cost(model, prompt_tokens, completion_tokens):
10
+ if model not in PRICING:
11
+ return 0.0, True
12
+ prices = PRICING[model]
13
+ cost = (prompt_tokens * prices["input"] / 1_000_000) + (completion_tokens * prices["output"] / 1_000_000)
14
+ return round(cost, 6), False
15
+
16
+
17
+ def _send_event(api_url, api_key, payload):
18
+ try:
19
+ data = json.dumps(payload).encode("utf-8")
20
+ req = Request(
21
+ f"{api_url}/api/v1/ingest",
22
+ data=data,
23
+ headers={
24
+ "Authorization": f"Bearer {api_key}",
25
+ "Content-Type": "application/json",
26
+ },
27
+ method="POST",
28
+ )
29
+ urlopen(req, timeout=5)
30
+ except Exception as e:
31
+ print(f"[spendlens] Warning: failed to send event: {e}")
32
+
33
+
34
+ class SpendLensClient:
35
+ def __init__(self, api_key, api_url="https://spendlensdev-product-production.up.railway.app", **openai_kwargs):
36
+ self._api_key = api_key
37
+ self._api_url = api_url
38
+ self._openai = OpenAI(**openai_kwargs)
39
+ self.chat = _ChatNamespace(self)
40
+
41
+
42
+ class _ChatNamespace:
43
+ def __init__(self, client):
44
+ self._client = client
45
+ self.completions = _CompletionsNamespace(client)
46
+
47
+
48
+ class _CompletionsNamespace:
49
+ def __init__(self, client):
50
+ self._client = client
51
+
52
+ def create(self, **kwargs):
53
+ tags = kwargs.pop("tags", kwargs.pop("costlens_tags", {}))
54
+ start_time = time.time()
55
+
56
+ response = self._client._openai.chat.completions.create(**kwargs)
57
+
58
+ latency_ms = int((time.time() - start_time) * 1000)
59
+ model = response.model
60
+ prompt_tokens = response.usage.prompt_tokens
61
+ completion_tokens = response.usage.completion_tokens
62
+ total_tokens = response.usage.total_tokens
63
+ cost_usd, unknown_model = _compute_cost(model, prompt_tokens, completion_tokens)
64
+
65
+ payload = {
66
+ "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()),
67
+ "model": model,
68
+ "prompt_tokens": prompt_tokens,
69
+ "completion_tokens": completion_tokens,
70
+ "total_tokens": total_tokens,
71
+ "cost_usd": cost_usd,
72
+ "latency_ms": latency_ms,
73
+ "status": "success",
74
+ "tags": {
75
+ "user_id": tags.get("user_id"),
76
+ "feature": tags.get("feature"),
77
+ "endpoint": tags.get("endpoint"),
78
+ },
79
+ }
80
+
81
+ threading.Thread(
82
+ target=_send_event,
83
+ args=(self._client._api_url, self._client._api_key, payload),
84
+ daemon=True,
85
+ ).start()
86
+
87
+ return response
@@ -0,0 +1,7 @@
1
+ # USD per 1M tokens
2
+ PRICING = {
3
+ "gpt-4o": {"input": 2.50, "output": 10.00},
4
+ "gpt-4o-mini": {"input": 0.15, "output": 0.60},
5
+ "gpt-4-turbo": {"input": 10.00, "output": 30.00},
6
+ "gpt-3.5-turbo": {"input": 0.50, "output": 1.50},
7
+ }
@@ -0,0 +1,46 @@
1
+ Metadata-Version: 2.4
2
+ Name: spendlens
3
+ Version: 0.1.0
4
+ Summary: LLM cost attribution — track OpenAI spend by user, feature, and endpoint
5
+ Home-page: https://github.com/ChrisRobinT/spendlens.dev-product
6
+ Author: Chris Robin
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: openai>=1.0.0
10
+ Dynamic: author
11
+ Dynamic: description
12
+ Dynamic: description-content-type
13
+ Dynamic: home-page
14
+ Dynamic: requires-dist
15
+ Dynamic: requires-python
16
+ Dynamic: summary
17
+
18
+ # SpendLens
19
+
20
+ Track your OpenAI costs by user, feature, and endpoint. Two-line integration.
21
+
22
+ ## Install
23
+
24
+ ```
25
+ pip install spendlens
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ```python
31
+ from spendlens import SpendLensClient
32
+
33
+ client = SpendLensClient(api_key="your-spendlens-api-key")
34
+
35
+ response = client.chat.completions.create(
36
+ model="gpt-4o",
37
+ messages=[{"role": "user", "content": "Hello"}],
38
+ tags={"user_id": "u_123", "feature": "chat", "endpoint": "/api/chat"},
39
+ )
40
+ ```
41
+
42
+ Your costs appear on the dashboard. That's it.
43
+
44
+ - Never logs prompts or completions. Only metadata.
45
+ - Never modifies the OpenAI response.
46
+ - Never adds latency. Logging is async.
@@ -0,0 +1,9 @@
1
+ README.md
2
+ setup.py
3
+ spendlens/__init__.py
4
+ spendlens/pricing.py
5
+ spendlens.egg-info/PKG-INFO
6
+ spendlens.egg-info/SOURCES.txt
7
+ spendlens.egg-info/dependency_links.txt
8
+ spendlens.egg-info/requires.txt
9
+ spendlens.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ openai>=1.0.0
@@ -0,0 +1 @@
1
+ spendlens