meter-lib 0.0.6__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,156 @@
1
+ name: Publish Python Package
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Set up Python ${{ matrix.python-version }}
19
+ uses: actions/setup-python@v4
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+
23
+ - name: Install dependencies
24
+ run: |
25
+ python -m pip install --upgrade pip
26
+ pip install -r requirements.txt
27
+ pip install pytest pytest-cov flake8
28
+
29
+ - name: Lint with flake8
30
+ run: |
31
+ # Stop the build if there are Python syntax errors or undefined names
32
+ flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
33
+ # Exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
34
+ flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
35
+
36
+ - name: Test with pytest (if tests exist)
37
+ run: |
38
+ if [ -d "tests" ] || [ -f "test_*.py" ]; then
39
+ pytest
40
+ else
41
+ echo "No tests found, skipping test step"
42
+ fi
43
+
44
+ publish:
45
+ needs: test
46
+ runs-on: ubuntu-latest
47
+ if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
48
+
49
+ steps:
50
+ - uses: actions/checkout@v4
51
+ with:
52
+ fetch-depth: 0 # Fetch all history for tags
53
+ token: ${{ secrets.PAT_TOKEN }}
54
+ ref: main
55
+
56
+ - name: Set up Python
57
+ uses: actions/setup-python@v4
58
+ with:
59
+ python-version: "3.9"
60
+
61
+ - name: Extract version from release
62
+ id: get_version
63
+ run: |
64
+ if [ "${{ github.event_name }}" = "release" ]; then
65
+ VERSION="${{ github.event.release.tag_name }}"
66
+ echo "Version from release: $VERSION"
67
+ else
68
+ # For manual workflow dispatch, use latest tag
69
+ VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "0.1.0")
70
+ echo "Version from git tag: $VERSION"
71
+ fi
72
+ # Remove 'v' prefix if present
73
+ VERSION=$(echo "$VERSION" | sed 's/^v//')
74
+ echo "PACKAGE_VERSION=$VERSION" >> $GITHUB_ENV
75
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
76
+ echo "Clean version: $VERSION"
77
+
78
+ - name: Update version in setup.py and pyproject.toml
79
+ run: |
80
+ echo "Updating versions to: ${{ env.PACKAGE_VERSION }}"
81
+
82
+ # Update setup.py version
83
+ sed -i 's/version="[^"]*"/version="${{ env.PACKAGE_VERSION }}"/' setup.py
84
+
85
+ # Update pyproject.toml version
86
+ sed -i 's/version = "[^"]*"/version = "${{ env.PACKAGE_VERSION }}"/' pyproject.toml
87
+
88
+ # Verify the changes
89
+ echo "Updated setup.py version line:"
90
+ grep 'version=' setup.py
91
+ echo "Updated pyproject.toml version line:"
92
+ grep 'version =' pyproject.toml
93
+
94
+ - name: Commit version update to main branch
95
+ if: github.event_name == 'release'
96
+ run: |
97
+ # Configure git
98
+ git config --global user.name "github-actions[bot]"
99
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
100
+
101
+ # Configure git to use the PAT token
102
+ # git remote set-url origin https://x-access-token:${{ secrets.PAT_TOKEN }}@github.com/${{ github.repository }}.git
103
+
104
+ # Check if there are changes to commit
105
+ if git diff --quiet setup.py pyproject.toml; then
106
+ echo "No changes to version files, skipping commit"
107
+ else
108
+ echo "Committing version update to main branch"
109
+ git add setup.py pyproject.toml
110
+ git commit -m "chore: update version to ${{ env.PACKAGE_VERSION }} [skip ci]"
111
+
112
+ # Push to main branch
113
+ git push origin HEAD:main
114
+ echo "Successfully pushed version update to main branch"
115
+ fi
116
+
117
+ - name: Install build dependencies
118
+ run: |
119
+ python -m pip install --upgrade pip
120
+ pip install build twine
121
+
122
+ - name: Display version info
123
+ run: |
124
+ echo "Building package with version: ${{ env.PACKAGE_VERSION }}"
125
+ python setup.py --version
126
+
127
+ - name: Build package
128
+ run: python -m build
129
+
130
+ - name: Check built package
131
+ run: twine check dist/*
132
+
133
+ - name: Publish to Test PyPI
134
+ if: github.event_name == 'workflow_dispatch'
135
+ env:
136
+ TWINE_USERNAME: __token__
137
+ TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
138
+ run: |
139
+ twine upload --repository testpypi dist/*
140
+
141
+ - name: Publish to PyPI
142
+ if: github.event_name == 'release'
143
+ env:
144
+ TWINE_USERNAME: __token__
145
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
146
+ run: |
147
+ echo "Checking if PYPI_API_TOKEN is set..."
148
+ if [ -z "$TWINE_PASSWORD" ]; then
149
+ echo "ERROR: PYPI_API_TOKEN secret is not set!"
150
+ exit 1
151
+ else
152
+ echo "PYPI_API_TOKEN is set (length: ${#TWINE_PASSWORD})"
153
+ fi
154
+
155
+ echo "Uploading to PyPI..."
156
+ twine upload dist/* --verbose
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.pyd
5
+ .env
6
+ .venv
7
+ dist/
8
+ build/
9
+ *.egg-info/
@@ -0,0 +1,249 @@
1
+ Metadata-Version: 2.4
2
+ Name: meter-lib
3
+ Version: 0.0.6
4
+ Summary: A litewave library to collect the customer credit usage
5
+ Project-URL: Homepage, https://github.com/aiorch/meter-lib
6
+ Project-URL: Repository, https://github.com/aiorch/meter-lib
7
+ Project-URL: Issues, https://github.com/aiorch/meter-lib/issues
8
+ Author-email: Sruthi <sruthi@litewave.ai>
9
+ License: MIT
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: requests>=2.28
17
+ Description-Content-Type: text/markdown
18
+
19
+ ## meter-lib — Usage Guide
20
+
21
+ ### Overview
22
+
23
+ `meter-lib` is a lightweight helper library for sending metering events to the Litewave backend and for looking up a customer account by `tenant_id`.
24
+
25
+ ### Requirements
26
+
27
+ - Python 3.10+
28
+
29
+ ### Installation
30
+
31
+ ```bash
32
+ pip install meter-lib
33
+ ```
34
+
35
+ ### Parameters Required
36
+
37
+ ```
38
+ tenant_id,
39
+ device_id,
40
+ meter_id,
41
+ kafka_url,
42
+ portal_url,
43
+ api_key,
44
+ total_usage,
45
+ start_time,
46
+ end_time
47
+ ```
48
+
49
+ ### Quickstart
50
+
51
+ ```python
52
+ from meter_lib import post_meter_usage
53
+
54
+ tenant_id = "tenant_123"
55
+ device_id = "us-east-ing1"
56
+ meter_id = "document.basic.page"
57
+ kafka_url="https://stream.app.domain.com",
58
+ portal_url="https://portal.app.domain.com",
59
+ api_key="sk-lw-40ea6ac9ee3xxxx_305e790xxxx"
60
+
61
+ result = post_meter_usage(
62
+ tenant_id=tenant_id,
63
+ device_id=device_id,
64
+ meter_id=meter_id,
65
+ kafka_url=kafka_url,
66
+ portal_url=portal_url,
67
+ api_key=api_key,
68
+ total_usage=24, # integer units as defined by your meter
69
+ )
70
+ ```
71
+
72
+ ### For AI enabled
73
+
74
+ ```python
75
+ from meter_lib import post_ai_meter_usage
76
+
77
+ tenant_id = "tenant_123"
78
+ device_id = "us-east-ing1"
79
+ meter_id = "chat.on.time_hours"
80
+ kafka_url="https://stream.app.domain.com",
81
+ portal_url="https://portal.app.domain.com",
82
+ api_key="sk-lw-40ea6ac9ee3xxxx_305e790xxxx"
83
+ start_time = 1759791552000 # Timestamp in milliseconds
84
+
85
+ result = post_ai_meter_usage(
86
+ tenant_id=tenant_id,
87
+ device_id=device_id,
88
+ meter_id=meter_id,
89
+ kafka_url=kafka_url,
90
+ portal_url=portal_url,
91
+ api_key=api_key
92
+ start_time= start_time # Timestamp in milliseconds
93
+ )
94
+
95
+ ```
96
+
97
+ ### For AI disabled
98
+
99
+ ```python
100
+ from meter_lib import post_ai_meter_usage
101
+
102
+ tenant_id = "tenant_123"
103
+ device_id = "us-east-ing1"
104
+ meter_id = "chat.on.time_hours"
105
+ kafka_url="https://stream.app.domain.com",
106
+ portal_url="https://portal.app.domain.com",
107
+ api_key="sk-lw-40ea6ac9ee3xxxx_305e790xxxx"
108
+ end_time = 1779799552000 # Timestamp in milliseconds
109
+
110
+ result = post_ai_meter_usage(
111
+ tenant_id=tenant_id,
112
+ device_id=device_id,
113
+ meter_id=meter_id,
114
+ kafka_url=kafka_url,
115
+ portal_url=portal_url,
116
+ api_key=api_key
117
+ end_time= end_time # Timestamp in milliseconds
118
+ )
119
+ ```
120
+
121
+ ```python
122
+ if result is None:
123
+ print("Failed to post meter usage event")
124
+ else:
125
+ print("Event accepted:", result)
126
+ ```
127
+
128
+ ### Error Handling
129
+
130
+ - `post_meter_usage` returns `None` for network errors or non-success HTTP statuses.
131
+ - Prefer explicit checks for `None` and add retries or backoff in your application layer if needed.
132
+
133
+ ### API Reference
134
+
135
+ #### post_meter_usage(tenant_id: str, device_id: str, meter_id: str, kafka_url: str, portal_url: str, api_key: str, total_usage: int) -> dict | None
136
+
137
+ - **Description**: Posts a metering event for a device and meter under a given tenant.
138
+ - **Headers**:
139
+ - `x-tenant-id`: the tenant identifier (string)
140
+ - `x-device-id`: the device identifier (string)
141
+ - **Payload (JSON)**:
142
+ - `meter_id` (string)
143
+ - `total_usage` (integer)
144
+ - `customer_id` (string) — auto-filled by the library.`
145
+ - **Returns**: The backend JSON response (`dict`) on success, otherwise `None`.
146
+ - **Timeout**: 10 seconds.
147
+ - **Notes**:
148
+ - If the customer lookup fails, the call is skipped and `None` is returned.
149
+ - This function is synchronous and will block until the request completes or times out.
150
+
151
+ ### List Of Meters:
152
+ - meter_id: "page.processed.basic"
153
+ name: "Basic Document Scanning"
154
+ type: "volume"
155
+ description: "Total number of basic pages processed"
156
+
157
+ - meter_id: "page.processed.advanced"
158
+ name: "Advanced Document Scanning"
159
+ type: "volume"
160
+ description: "Total number of advanced pages processed"
161
+
162
+ - meter_id: "report.generated.small"
163
+ name: "Small Report Generation"
164
+ type: "volume"
165
+ description: "Total number of small reports generated"
166
+
167
+ - meter_id: "report.generated.medium"
168
+ name: "Medium Report Generation"
169
+ type: "volume"
170
+ description: "Total number of medium reports generated"
171
+
172
+ - meter_id: "report.generated.large"
173
+ name: "Large Report Generation"
174
+ type: "volume"
175
+ description: "Total number of large reports generated"
176
+
177
+ - meter_id: "report.generated.dataquery"
178
+ name: "Data Query Report Generation"
179
+ type: "volume"
180
+ description: "Total number of data query reports generated"
181
+
182
+ - meter_id: "report.generated.insights"
183
+ name: "Insights Report Generation"
184
+ type: "volume"
185
+ description: "Total number of insights reports generated"
186
+
187
+ - meter_id: "rule.executed.small"
188
+ name: "Small Rule Execution"
189
+ type: "volume"
190
+ description: "Total number of rules executed with runtime less than 1 minute"
191
+
192
+ - meter_id: "rule.executed.medium"
193
+ name: "Medium Rule Execution"
194
+ type: "volume"
195
+ description: "Total number of rules executed with runtime between 1 and 3 minutes"
196
+
197
+ - meter_id: "rule.executed.large"
198
+ name: "Large Rule Execution"
199
+ type: "volume"
200
+ description: "Total number of rules executed with runtime greater than 4 minutes"
201
+
202
+ - meter_id: "chat.on.time_hours"
203
+ name: "Litewave AI Assistant Usage (Hours)"
204
+ type: "performance"
205
+ description: "Total active chat usage time in hours"
206
+
207
+ - meter_id: "chat.query.time_secs"
208
+ name: "Litewave AI Assistant Query Time (Seconds)"
209
+ type: "performance"
210
+ description: "Total time spent per query in seconds"
211
+
212
+ - meter_id: "document.storage.size_gb"
213
+ name: "Document Storage Size"
214
+ type: "volume"
215
+ description: "Total document storage consumed in GB"
216
+
217
+ - meter_id: "template.processed.small"
218
+ name: "Small Template Processing"
219
+ type: "volume"
220
+ description: "Total number of small templates processed"
221
+
222
+ - meter_id: "template.processed.medium"
223
+ name: "Medium Template Processing"
224
+ type: "volume"
225
+ description: "Total number of medium templates processed"
226
+
227
+ - meter_id: "template.processed.large"
228
+ name: "Large Template Processing"
229
+ type: "volume"
230
+ description: "Total number of large templates processed"
231
+
232
+ - meter_id: "template.processed.count"
233
+ name: "Template Licensing Count"
234
+ type: "volume"
235
+ description: "Total number of licensed templates processed"
236
+
237
+ - meter_id: "template.licensed.total"
238
+ name: "Yearly Template Setup"
239
+ type: "volume"
240
+ description: "Total yearly template setup count"
241
+
242
+ ### Troubleshooting
243
+
244
+ - Confirm your `tenant_id`, `device_id`, `meter_id`, `kafka_url`, `portal_url`, `api_key` values are correct.
245
+
246
+ ### Support
247
+
248
+ - Homepage: `https://github.com/aiorch/meter-lib`
249
+ - Issues: `https://github.com/aiorch/meter-lib/issues`
@@ -0,0 +1,231 @@
1
+ ## meter-lib — Usage Guide
2
+
3
+ ### Overview
4
+
5
+ `meter-lib` is a lightweight helper library for sending metering events to the Litewave backend and for looking up a customer account by `tenant_id`.
6
+
7
+ ### Requirements
8
+
9
+ - Python 3.10+
10
+
11
+ ### Installation
12
+
13
+ ```bash
14
+ pip install meter-lib
15
+ ```
16
+
17
+ ### Parameters Required
18
+
19
+ ```
20
+ tenant_id,
21
+ device_id,
22
+ meter_id,
23
+ kafka_url,
24
+ portal_url,
25
+ api_key,
26
+ total_usage,
27
+ start_time,
28
+ end_time
29
+ ```
30
+
31
+ ### Quickstart
32
+
33
+ ```python
34
+ from meter_lib import post_meter_usage
35
+
36
+ tenant_id = "tenant_123"
37
+ device_id = "us-east-ing1"
38
+ meter_id = "document.basic.page"
39
+ kafka_url="https://stream.app.domain.com",
40
+ portal_url="https://portal.app.domain.com",
41
+ api_key="sk-lw-40ea6ac9ee3xxxx_305e790xxxx"
42
+
43
+ result = post_meter_usage(
44
+ tenant_id=tenant_id,
45
+ device_id=device_id,
46
+ meter_id=meter_id,
47
+ kafka_url=kafka_url,
48
+ portal_url=portal_url,
49
+ api_key=api_key,
50
+ total_usage=24, # integer units as defined by your meter
51
+ )
52
+ ```
53
+
54
+ ### For AI enabled
55
+
56
+ ```python
57
+ from meter_lib import post_ai_meter_usage
58
+
59
+ tenant_id = "tenant_123"
60
+ device_id = "us-east-ing1"
61
+ meter_id = "chat.on.time_hours"
62
+ kafka_url="https://stream.app.domain.com",
63
+ portal_url="https://portal.app.domain.com",
64
+ api_key="sk-lw-40ea6ac9ee3xxxx_305e790xxxx"
65
+ start_time = 1759791552000 # Timestamp in milliseconds
66
+
67
+ result = post_ai_meter_usage(
68
+ tenant_id=tenant_id,
69
+ device_id=device_id,
70
+ meter_id=meter_id,
71
+ kafka_url=kafka_url,
72
+ portal_url=portal_url,
73
+ api_key=api_key
74
+ start_time= start_time # Timestamp in milliseconds
75
+ )
76
+
77
+ ```
78
+
79
+ ### For AI disabled
80
+
81
+ ```python
82
+ from meter_lib import post_ai_meter_usage
83
+
84
+ tenant_id = "tenant_123"
85
+ device_id = "us-east-ing1"
86
+ meter_id = "chat.on.time_hours"
87
+ kafka_url="https://stream.app.domain.com",
88
+ portal_url="https://portal.app.domain.com",
89
+ api_key="sk-lw-40ea6ac9ee3xxxx_305e790xxxx"
90
+ end_time = 1779799552000 # Timestamp in milliseconds
91
+
92
+ result = post_ai_meter_usage(
93
+ tenant_id=tenant_id,
94
+ device_id=device_id,
95
+ meter_id=meter_id,
96
+ kafka_url=kafka_url,
97
+ portal_url=portal_url,
98
+ api_key=api_key
99
+ end_time= end_time # Timestamp in milliseconds
100
+ )
101
+ ```
102
+
103
+ ```python
104
+ if result is None:
105
+ print("Failed to post meter usage event")
106
+ else:
107
+ print("Event accepted:", result)
108
+ ```
109
+
110
+ ### Error Handling
111
+
112
+ - `post_meter_usage` returns `None` for network errors or non-success HTTP statuses.
113
+ - Prefer explicit checks for `None` and add retries or backoff in your application layer if needed.
114
+
115
+ ### API Reference
116
+
117
+ #### post_meter_usage(tenant_id: str, device_id: str, meter_id: str, kafka_url: str, portal_url: str, api_key: str, total_usage: int) -> dict | None
118
+
119
+ - **Description**: Posts a metering event for a device and meter under a given tenant.
120
+ - **Headers**:
121
+ - `x-tenant-id`: the tenant identifier (string)
122
+ - `x-device-id`: the device identifier (string)
123
+ - **Payload (JSON)**:
124
+ - `meter_id` (string)
125
+ - `total_usage` (integer)
126
+ - `customer_id` (string) — auto-filled by the library.`
127
+ - **Returns**: The backend JSON response (`dict`) on success, otherwise `None`.
128
+ - **Timeout**: 10 seconds.
129
+ - **Notes**:
130
+ - If the customer lookup fails, the call is skipped and `None` is returned.
131
+ - This function is synchronous and will block until the request completes or times out.
132
+
133
+ ### List Of Meters:
134
+ - meter_id: "page.processed.basic"
135
+ name: "Basic Document Scanning"
136
+ type: "volume"
137
+ description: "Total number of basic pages processed"
138
+
139
+ - meter_id: "page.processed.advanced"
140
+ name: "Advanced Document Scanning"
141
+ type: "volume"
142
+ description: "Total number of advanced pages processed"
143
+
144
+ - meter_id: "report.generated.small"
145
+ name: "Small Report Generation"
146
+ type: "volume"
147
+ description: "Total number of small reports generated"
148
+
149
+ - meter_id: "report.generated.medium"
150
+ name: "Medium Report Generation"
151
+ type: "volume"
152
+ description: "Total number of medium reports generated"
153
+
154
+ - meter_id: "report.generated.large"
155
+ name: "Large Report Generation"
156
+ type: "volume"
157
+ description: "Total number of large reports generated"
158
+
159
+ - meter_id: "report.generated.dataquery"
160
+ name: "Data Query Report Generation"
161
+ type: "volume"
162
+ description: "Total number of data query reports generated"
163
+
164
+ - meter_id: "report.generated.insights"
165
+ name: "Insights Report Generation"
166
+ type: "volume"
167
+ description: "Total number of insights reports generated"
168
+
169
+ - meter_id: "rule.executed.small"
170
+ name: "Small Rule Execution"
171
+ type: "volume"
172
+ description: "Total number of rules executed with runtime less than 1 minute"
173
+
174
+ - meter_id: "rule.executed.medium"
175
+ name: "Medium Rule Execution"
176
+ type: "volume"
177
+ description: "Total number of rules executed with runtime between 1 and 3 minutes"
178
+
179
+ - meter_id: "rule.executed.large"
180
+ name: "Large Rule Execution"
181
+ type: "volume"
182
+ description: "Total number of rules executed with runtime greater than 4 minutes"
183
+
184
+ - meter_id: "chat.on.time_hours"
185
+ name: "Litewave AI Assistant Usage (Hours)"
186
+ type: "performance"
187
+ description: "Total active chat usage time in hours"
188
+
189
+ - meter_id: "chat.query.time_secs"
190
+ name: "Litewave AI Assistant Query Time (Seconds)"
191
+ type: "performance"
192
+ description: "Total time spent per query in seconds"
193
+
194
+ - meter_id: "document.storage.size_gb"
195
+ name: "Document Storage Size"
196
+ type: "volume"
197
+ description: "Total document storage consumed in GB"
198
+
199
+ - meter_id: "template.processed.small"
200
+ name: "Small Template Processing"
201
+ type: "volume"
202
+ description: "Total number of small templates processed"
203
+
204
+ - meter_id: "template.processed.medium"
205
+ name: "Medium Template Processing"
206
+ type: "volume"
207
+ description: "Total number of medium templates processed"
208
+
209
+ - meter_id: "template.processed.large"
210
+ name: "Large Template Processing"
211
+ type: "volume"
212
+ description: "Total number of large templates processed"
213
+
214
+ - meter_id: "template.processed.count"
215
+ name: "Template Licensing Count"
216
+ type: "volume"
217
+ description: "Total number of licensed templates processed"
218
+
219
+ - meter_id: "template.licensed.total"
220
+ name: "Yearly Template Setup"
221
+ type: "volume"
222
+ description: "Total yearly template setup count"
223
+
224
+ ### Troubleshooting
225
+
226
+ - Confirm your `tenant_id`, `device_id`, `meter_id`, `kafka_url`, `portal_url`, `api_key` values are correct.
227
+
228
+ ### Support
229
+
230
+ - Homepage: `https://github.com/aiorch/meter-lib`
231
+ - Issues: `https://github.com/aiorch/meter-lib/issues`
@@ -0,0 +1 @@
1
+ from .core import post_meter_usage, post_ai_meter_usage
@@ -0,0 +1,115 @@
1
+ from datetime import datetime
2
+ from typing import Optional
3
+ import requests
4
+
5
+ def fetch_customer_id(tenant_id: str, portal_url: str, api_key: str):
6
+ """
7
+ Fetch the customer account for a given tenant_id.
8
+ Returns a dict with customer info, or None if not found/error.
9
+ """
10
+ url = f"{portal_url}/metering/v1alpha1/customer-accounts/tenant/{tenant_id}"
11
+ headers = {
12
+ "x-api-key": api_key
13
+ }
14
+ try:
15
+ response = requests.get(url, headers=headers, timeout=10)
16
+ response.raise_for_status()
17
+ data = response.json()
18
+
19
+ # Handle list response
20
+ if isinstance(data, list) and len(data) > 0:
21
+ return data[0]
22
+ elif isinstance(data, dict):
23
+ return data
24
+ else:
25
+ print(f"No customer account found for tenant {tenant_id}")
26
+ return None
27
+
28
+ except requests.RequestException as e:
29
+ print(f"Error fetching {url}: {e}")
30
+ return None
31
+
32
+
33
+ def post_meter_usage(tenant_id: str, device_id: str, meter_id: str, kafka_url: str, portal_url: str, api_key: str, total_usage: int ) :
34
+ """
35
+ Posts meter usage to the events API.
36
+ Uses tenant_id + device_id in headers and includes customer_id in payload.
37
+ """
38
+ customer_account = fetch_customer_id(tenant_id, portal_url, api_key)
39
+ if not customer_account:
40
+ print("No customer account available, skipping meter usage post")
41
+ return None
42
+
43
+ url = f"{kafka_url}/topics/usage-events"
44
+ headers = {
45
+ "Content-Type": "application/vnd.kafka.json.v2+json",
46
+ }
47
+ payload = {
48
+ "records": [
49
+ {
50
+ "value": {
51
+ "meter_id": meter_id,
52
+ "customer_id": customer_account["id"],
53
+ "total_usage": total_usage
54
+ }
55
+ }
56
+ ]
57
+ }
58
+
59
+ try:
60
+ response = requests.post(url, headers=headers, json=payload, timeout=10)
61
+ response.raise_for_status()
62
+ return response.json()
63
+ except requests.RequestException as e:
64
+ print(f" Error posting to {url}: {e}")
65
+ return None
66
+
67
+ def post_ai_meter_usage(tenant_id: str, device_id: str, meter_id: str, kafka_url: str, portal_url: str, api_key: str, start_time:Optional[datetime]= None, end_time:Optional[datetime]=None) :
68
+ """
69
+ Posts meter usage to the ai events API.
70
+ Uses tenant_id + device_id in headers and includes customer_id in payload.
71
+ """
72
+ customer_account = fetch_customer_id(tenant_id, portal_url, api_key)
73
+ if not customer_account:
74
+ print("No customer account available, skipping meter usage post")
75
+ return None
76
+
77
+ url = f"{kafka_url}/topics/usage-events"
78
+ headers = {
79
+ "Content-Type": "application/vnd.kafka.json.v2+json",
80
+ }
81
+ if start_time:
82
+ payload = {
83
+ "records": [
84
+ {
85
+ "value": {
86
+ "ai_event": True,
87
+ "meter_id": meter_id,
88
+ "customer_id": customer_account["id"],
89
+ "start_time": start_time
90
+ }
91
+ }
92
+ ]
93
+ }
94
+ else:
95
+ payload = {
96
+ "records": [
97
+ {
98
+ "value": {
99
+ "ai_event": True,
100
+ "meter_id": meter_id,
101
+ "customer_id": customer_account["id"],
102
+ "end_time": end_time
103
+ }
104
+ }
105
+ ]
106
+ }
107
+
108
+ try:
109
+ response = requests.post(url, headers=headers, json=payload, timeout=10)
110
+ response.raise_for_status()
111
+ return response.json()
112
+ except requests.RequestException as e:
113
+ print(f" Error posting to {url}: {e}")
114
+ return None
115
+
@@ -0,0 +1,29 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "meter-lib"
7
+ version = "0.0.6"
8
+ description = "A litewave library to collect the customer credit usage"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ authors = [
12
+ { name = "Sruthi", email = "sruthi@litewave.ai" }
13
+ ]
14
+ license = { text = "MIT" }
15
+ dependencies = [
16
+ "requests>=2.28"
17
+ ]
18
+ classifiers = [
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/aiorch/meter-lib"
28
+ Repository = "https://github.com/aiorch/meter-lib"
29
+ Issues = "https://github.com/aiorch/meter-lib/issues"
@@ -0,0 +1 @@
1
+ requests>=2.28
@@ -0,0 +1,44 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ # Read the README file for the long description
4
+ with open("README.md", "r", encoding="utf-8") as fh:
5
+ long_description = fh.read()
6
+
7
+ # Read the requirements file
8
+ with open("requirements.txt", "r", encoding="utf-8") as fh:
9
+ requirements = [line.strip() for line in fh.readlines() if line.strip() and not line.startswith("#")]
10
+
11
+ setup(
12
+ name="meter-lib",
13
+ version="0.0.6",
14
+ author="Sruthi R",
15
+ author_email="sruthi@litewave.ai",
16
+ description="A litewave library to collect the customer credit usage",
17
+ long_description=long_description,
18
+ long_description_content_type="text/markdown",
19
+ url="https://github.com/aiorch/meter-lib",
20
+ packages=find_packages(),
21
+ classifiers=[
22
+ "Development Status :: 3 - Alpha",
23
+ "Intended Audience :: Developers",
24
+ "Topic :: System :: Logging",
25
+ "Topic :: Software Development :: Libraries :: Python Modules",
26
+ "License :: OSI Approved :: MIT License",
27
+ "Programming Language :: Python :: 3",
28
+ "Programming Language :: Python :: 3.9",
29
+ "Programming Language :: Python :: 3.10",
30
+ "Programming Language :: Python :: 3.11",
31
+ "Programming Language :: Python :: 3.12",
32
+ "Operating System :: OS Independent",
33
+ ],
34
+ python_requires=">=3.7",
35
+ install_requires=requirements,
36
+ keywords="customer credit usage ",
37
+ project_urls={
38
+ "Bug Reports": "https://github.com/aiorch/meter-lib/issues",
39
+ "Source": "https://github.com/aiorch/meter-lib",
40
+ "Documentation": "https://github.com/aiorch/meter-lib#readme",
41
+ },
42
+ include_package_data=True,
43
+ zip_safe=False,
44
+ )