litellm-pulse 0.2.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.
Files changed (33) hide show
  1. litellm_pulse-0.2.0/.dockerignore +7 -0
  2. litellm_pulse-0.2.0/.github/.release-please-manifest.json +1 -0
  3. litellm_pulse-0.2.0/.github/ISSUE_TEMPLATE/bug_report.md +35 -0
  4. litellm_pulse-0.2.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  5. litellm_pulse-0.2.0/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
  6. litellm_pulse-0.2.0/.github/PULL_REQUEST_TEMPLATE.md +22 -0
  7. litellm_pulse-0.2.0/.github/pr-labeler.yml +16 -0
  8. litellm_pulse-0.2.0/.github/release-please-config.json +20 -0
  9. litellm_pulse-0.2.0/.github/workflows/ci.yml +51 -0
  10. litellm_pulse-0.2.0/.github/workflows/pr-validator.yml +69 -0
  11. litellm_pulse-0.2.0/.github/workflows/release.yml +106 -0
  12. litellm_pulse-0.2.0/.gitignore +15 -0
  13. litellm_pulse-0.2.0/.pre-commit-config.yaml +22 -0
  14. litellm_pulse-0.2.0/.python-version +1 -0
  15. litellm_pulse-0.2.0/CHANGELOG.md +27 -0
  16. litellm_pulse-0.2.0/Dockerfile +18 -0
  17. litellm_pulse-0.2.0/LICENSE +21 -0
  18. litellm_pulse-0.2.0/Makefile +27 -0
  19. litellm_pulse-0.2.0/PKG-INFO +500 -0
  20. litellm_pulse-0.2.0/README.md +477 -0
  21. litellm_pulse-0.2.0/assets/litellm-pulse.svg +23 -0
  22. litellm_pulse-0.2.0/docker-compose.example.yml +28 -0
  23. litellm_pulse-0.2.0/litellm_pulse/__init__.py +1 -0
  24. litellm_pulse-0.2.0/litellm_pulse/app.py +389 -0
  25. litellm_pulse-0.2.0/litellm_pulse/db.py +179 -0
  26. litellm_pulse-0.2.0/litellm_pulse/parser.py +39 -0
  27. litellm_pulse-0.2.0/pyproject.toml +55 -0
  28. litellm_pulse-0.2.0/tests/__init__.py +0 -0
  29. litellm_pulse-0.2.0/tests/test_app.py +293 -0
  30. litellm_pulse-0.2.0/tests/test_db.py +223 -0
  31. litellm_pulse-0.2.0/tests/test_parser.py +91 -0
  32. litellm_pulse-0.2.0/tests/test_scraper.py +138 -0
  33. litellm_pulse-0.2.0/uv.lock +1004 -0
@@ -0,0 +1,7 @@
1
+ .git
2
+ .gitignore
3
+ __pycache__
4
+ *.pyc
5
+ .venv
6
+ .ruff_cache
7
+ *.egg-info
@@ -0,0 +1 @@
1
+ {".":"0.2.0"}
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: Bug Report
3
+ about: Report a bug or unexpected behavior
4
+ title: "[BUG] "
5
+ labels: ["bug"]
6
+ ---
7
+
8
+ ## Description
9
+
10
+ <!-- A clear description of the bug. -->
11
+
12
+ ## Steps to Reproduce
13
+
14
+ 1.
15
+ 2.
16
+ 3.
17
+
18
+ ## Expected Behavior
19
+
20
+ <!-- What you expected to happen. -->
21
+
22
+ ## Actual Behavior
23
+
24
+ <!-- What actually happened. -->
25
+
26
+ ## Environment
27
+
28
+ - LiteLLM Pulse version:
29
+ - Deployment (Docker / uv run):
30
+ - Python version:
31
+ - Relevant config (`LITELLM_PULSE_*` env vars):
32
+
33
+ ## Logs
34
+
35
+ <!-- Relevant log output. Set LITELLM_PULSE_LOG_LEVEL=debug if needed. -->
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Discussions
4
+ url: https://github.com/jakepenzak/litellm-pulse/discussions
5
+ about: Ask questions and share ideas in Discussions.
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: Feature Request
3
+ about: Suggest a new feature or enhancement
4
+ title: "[FEATURE] "
5
+ labels: ["enhancement"]
6
+ ---
7
+
8
+ ## Problem
9
+
10
+ <!-- What problem does this feature solve? -->
11
+
12
+ ## Proposed Solution
13
+
14
+ <!-- What you'd like to see happen. -->
15
+
16
+ ## Alternatives Considered
17
+
18
+ <!-- Any alternative solutions you've thought about. -->
19
+
20
+ ## Additional Context
21
+
22
+ <!-- Anything else relevant (screenshots, links, etc.). -->
@@ -0,0 +1,22 @@
1
+ ## Summary
2
+
3
+ <!-- Brief description of the changes -->
4
+
5
+ ## Type of Change
6
+
7
+ - [ ] Bug fix
8
+ - [ ] New feature
9
+ - [ ] Breaking change
10
+ - [ ] Documentation update
11
+ - [ ] Refactor / cleanup
12
+
13
+ ## Checklist
14
+
15
+ - [ ] `pre-commit run --all-files` passes
16
+ - [ ] `uv run pytest` passes (or no tests changed)
17
+ - [ ] Documentation updated if needed
18
+ - [ ] Changes are backwards-compatible (or noted below)
19
+
20
+ ## Additional Notes
21
+
22
+ <!-- Anything else reviewers should know -->
@@ -0,0 +1,16 @@
1
+ clear-prexisting: false # Whether to remove preexisting labels from the PR in favor of the generated one
2
+ include-title: true # Consider the PR title when adding labels
3
+ label-for-breaking-changes: "type: breaking" # Label to be used when the message has the breaking change syntax '!:' E.g. "feat!: This break things"
4
+ label-mapping: # Label to array of types for mapping
5
+ "type: build": ["build"]
6
+ "type: ci": ["ci"]
7
+ "type: docs": ["docs"]
8
+ "type: feat": ["feat"]
9
+ "type: fix": ["fix"]
10
+ "type: perf": ["perf"]
11
+ "type: refactor": ["refactor"]
12
+ "type: style": ["style"]
13
+ "type: test": ["test"]
14
+ "type: revert": ["revert"]
15
+ "type: chore": ["chore"]
16
+ "type: release": ["release"]
@@ -0,0 +1,20 @@
1
+ {
2
+ "release-type": "python",
3
+ "package-name": "litellm-pulse",
4
+ "include-component-in-tag": false,
5
+ "changelog-sections": [
6
+ { "type": "feat", "section": "✨ Features", "hidden": false },
7
+ { "type": "fix", "section": "🐛 Bug Fixes", "hidden": false },
8
+ { "type": "perf", "section": "⚡ Performance", "hidden": false },
9
+ { "type": "refactor", "section": "♻️ Refactoring", "hidden": false },
10
+ { "type": "docs", "section": "📚 Documentation", "hidden": false },
11
+ { "type": "test", "section": "🧪 Tests", "hidden": false },
12
+ { "type": "ci", "section": "🔧 CI/CD", "hidden": false },
13
+ { "type": "build", "section": "📦 Build System", "hidden": false },
14
+ { "type": "style", "section": "🎨 Code Style", "hidden": false },
15
+ { "type": "chore", "section": "🔨 Miscellaneous", "hidden": false }
16
+ ],
17
+ "packages": {
18
+ ".": {}
19
+ }
20
+ }
@@ -0,0 +1,51 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ lint:
14
+ name: Lint & Format
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.12"
23
+
24
+ - name: Install pre-commit
25
+ run: pip install pre-commit
26
+
27
+ - name: Run pre-commit
28
+ run: pre-commit run --all-files
29
+
30
+ test:
31
+ name: Test
32
+ runs-on: ubuntu-latest
33
+ strategy:
34
+ matrix:
35
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+
39
+ - name: Install uv
40
+ uses: astral-sh/setup-uv@v6
41
+ with:
42
+ version: "latest"
43
+
44
+ - name: Set up Python ${{ matrix.python-version }}
45
+ run: uv python install ${{ matrix.python-version }}
46
+
47
+ - name: Install dependencies
48
+ run: uv sync --dev
49
+
50
+ - name: Run tests
51
+ run: uv run pytest -v
@@ -0,0 +1,69 @@
1
+ name: Conventional Commit PR Validator
2
+
3
+ on:
4
+ pull_request_target:
5
+ types: [opened, edited, reopened]
6
+
7
+ jobs:
8
+ check-PR-title:
9
+ permissions:
10
+ contents: read
11
+ pull-requests: write
12
+ runs-on: ubuntu-latest
13
+ env:
14
+ PR_TITLE_RAW: ${{ github.event.pull_request.title }}
15
+ steps:
16
+ - name: Get PR Title
17
+ id: pr_title
18
+ shell: bash
19
+ run: |
20
+ {
21
+ echo 'PR_TITLE<<EOF'
22
+ echo "$PR_TITLE_RAW"
23
+ echo 'EOF'
24
+ } >> "$GITHUB_ENV"
25
+
26
+ - name: Validate PR Title
27
+ id: validate_pr_title
28
+ run: |
29
+ if [[ ! "$PR_TITLE" =~ ^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test|release)(\(.+\))?!?:\ .+ ]]; then
30
+ echo "INVALID_TITLE=true" >> $GITHUB_ENV
31
+ echo "PR title does not follow the required format: `<type>[optional scope]: description`"
32
+ else
33
+ echo "INVALID_TITLE=false" >> $GITHUB_ENV
34
+ echo "PR title is valid."
35
+ fi
36
+ shell: bash
37
+
38
+ - name: Comment on PR if invalid
39
+ if: ${{ env.INVALID_TITLE == 'true' }}
40
+ uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043
41
+ with:
42
+ issue-number: ${{ github.event.pull_request.number }}
43
+ body: |
44
+ :stop_sign: **ACTION NEEDED - PR Title** :stop_sign:
45
+
46
+ `litellm-pulse` follows [Conventional Commit format](https://www.conventionalcommits.org/en/v1.0.0/) for PR titles.
47
+
48
+ Please update your PR title to match the specification.
49
+
50
+ Test out your PR title [here](https://regex101.com/?regex=%5E%28build%7Cchore%7Cci%7Cdocs%7Cfeat%7Cfix%7Cperf%7Crefactor%7Crevert%7Cstyle%7Ctest%7Crelease%29%28%5C%28.%2B%5C%29%29%3F%21%3F%3A%5C+.%2B&testString=fix%28atest._-%29%21%3A+This.-+_+is+a+..+test%0A&flags=s&flavor=java&delimiter=%22).
51
+
52
+ For the list of acceptable prefixes & examples, see [README.md](https://github.com/jakepenzak/litellm-pulse/blob/main/README.md).
53
+
54
+ The current PR title is: `${{ env.PR_TITLE }}`
55
+
56
+ - name: Fail if invalid
57
+ if: ${{ env.INVALID_TITLE == 'true' }}
58
+ run: exit 1
59
+
60
+ label-PR:
61
+ permissions:
62
+ contents: read
63
+ pull-requests: write
64
+ runs-on: ubuntu-latest
65
+ steps:
66
+ - uses: grafana/pr-labeler-action@50737759cb150f5d312de1a45559027156db8bab
67
+ with:
68
+ token: ${{ secrets.GITHUB_TOKEN }}
69
+ configuration-path: .github/pr-labeler.yml
@@ -0,0 +1,106 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ permissions:
8
+ contents: write
9
+ pull-requests: write
10
+ packages: write
11
+
12
+ jobs:
13
+ release-please:
14
+ name: Create Release
15
+ runs-on: ubuntu-latest
16
+ outputs:
17
+ release_created: ${{ steps.release-please.outputs.releases_created }}
18
+ tag_name: ${{ steps.release-please.outputs.tag_name }}
19
+ steps:
20
+ - uses: googleapis/release-please-action@v4
21
+ id: release-please
22
+ with:
23
+ config-file: .github/release-please-config.json
24
+ manifest-file: .github/.release-please-manifest.json
25
+ token: ${{ secrets.GITHUB_TOKEN }}
26
+
27
+ build-and-publish:
28
+ name: Build & Publish to GHCR
29
+ runs-on: ubuntu-latest
30
+ needs: release-please
31
+ if: ${{ needs.release-please.outputs.release_created == 'true' }}
32
+ permissions:
33
+ contents: read
34
+ packages: write
35
+ steps:
36
+ - uses: actions/checkout@v4
37
+ with:
38
+ ref: ${{ needs.release-please.outputs.tag_name }}
39
+
40
+ - name: Extract version
41
+ id: version
42
+ run: |
43
+ TAG="${{ needs.release-please.outputs.tag_name }}"
44
+ VERSION="${TAG#v}"
45
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
46
+ echo "major_minor=$(cut -d. -f1,2 <<<"$VERSION")" >> "$GITHUB_OUTPUT"
47
+ echo "major=$(cut -d. -f1 <<<"$VERSION")" >> "$GITHUB_OUTPUT"
48
+
49
+ - name: Docker meta
50
+ id: meta
51
+ uses: docker/metadata-action@v5
52
+ with:
53
+ images: ghcr.io/${{ github.repository }}
54
+ tags: |
55
+ type=raw,value=${{ steps.version.outputs.version }}
56
+ type=raw,value=${{ steps.version.outputs.major_minor }}
57
+ type=raw,value=${{ steps.version.outputs.major }}
58
+ type=raw,value=latest
59
+
60
+ - name: Log in to GHCR
61
+ uses: docker/login-action@v3
62
+ with:
63
+ registry: ghcr.io
64
+ username: ${{ github.actor }}
65
+ password: ${{ secrets.GITHUB_TOKEN }}
66
+
67
+ - name: Set up Docker Buildx
68
+ uses: docker/setup-buildx-action@v3
69
+
70
+ - name: Build and push
71
+ uses: docker/build-push-action@v6
72
+ with:
73
+ context: .
74
+ push: true
75
+ tags: ${{ steps.meta.outputs.tags }}
76
+ labels: ${{ steps.meta.outputs.labels }}
77
+ cache-from: type=gha
78
+ cache-to: type=gha,mode=max
79
+
80
+ publish-pypi:
81
+ name: Publish to PyPI
82
+ runs-on: ubuntu-latest
83
+ needs: release-please
84
+ if: ${{ needs.release-please.outputs.release_created == 'true' }}
85
+ permissions:
86
+ contents: read
87
+ steps:
88
+ - uses: actions/checkout@v4
89
+ with:
90
+ ref: ${{ needs.release-please.outputs.tag_name }}
91
+
92
+ - uses: actions/setup-python@v5
93
+ with:
94
+ python-version: "3.11"
95
+
96
+ - name: Build package
97
+ run: |
98
+ pip install build
99
+ python -m build
100
+
101
+ - name: Publish to PyPI
102
+ uses: pypa/gh-action-pypi-publish@release/v1
103
+ with:
104
+ user: __token__
105
+ password: ${{ secrets.PYPI_TOKEN }}
106
+ skip-existing: true
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .venv/
5
+ .ruff_cache/
6
+ dist/
7
+ build/
8
+ data/
9
+ *.db
10
+ *.db-wal
11
+ *.db-shm
12
+
13
+ # Test reports
14
+ .coverage
15
+ tests/reports/
@@ -0,0 +1,22 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v4.6.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: detect-private-key
8
+ - id: check-yaml
9
+ - id: check-json
10
+ - id: check-toml
11
+ - id: check-xml
12
+ - repo: https://github.com/astral-sh/ruff-pre-commit
13
+ rev: v0.15.8
14
+ hooks:
15
+ - id: ruff-check
16
+ types_or: [python, pyi, jupyter]
17
+ args: [--fix]
18
+
19
+ - id: ruff-format
20
+ types_or: [python, pyi, jupyter]
21
+
22
+ exclude: ^assets/
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ ## [0.2.0](https://github.com/jakepenzak/litellm-pulse/compare/v0.1.0...v0.2.0) (2026-06-24)
4
+
5
+
6
+ ### ✨ Features
7
+
8
+ * add support for configurable timezones ([#9](https://github.com/jakepenzak/litellm-pulse/issues/9)) ([fd3d0fe](https://github.com/jakepenzak/litellm-pulse/commit/fd3d0fea2b63825b40d426a6f4e03ab6d78a2743))
9
+ * authentication support for scraping LiteLLM `/metrics` endpoint ([#7](https://github.com/jakepenzak/litellm-pulse/issues/7)) ([0ea7d13](https://github.com/jakepenzak/litellm-pulse/commit/0ea7d13aa69248b9dcf46e7692e03fc143ff5fc8))
10
+
11
+
12
+ ### 🔧 CI/CD
13
+
14
+ * add pr labeler and pypi release ([#12](https://github.com/jakepenzak/litellm-pulse/issues/12)) ([c9afc16](https://github.com/jakepenzak/litellm-pulse/commit/c9afc16c48853b859e6e0407d795d6835b104070))
15
+
16
+ ## 0.1.0 (2026-06-21)
17
+
18
+ Initial release of `litellm-pulse`, a lightweight service that scrapes LiteLLM Prometheus metrics and exposes them as JSON for Homepage widgets and Home Assistant sensors. Features a FastAPI application with a Prometheus text format parser, SQLite time-series storage with daily/weekly/monthly aggregates and counter reset detection, and REST endpoints for cost and token metrics. Includes 49 pytest tests, pre-commit linting with ruff, and CI/CD pipelines with automated releases via release-please and Docker image publishing to GHCR.
19
+
20
+ ### ✨ Features
21
+
22
+ * initial `litellm-pulse` metrics exporter ([00f72f2](https://github.com/jakepenzak/litellm-pulse/commit/00f72f299801e5daaaa0c7362795a9d4980b5e8f))
23
+ * SQLite time-series storage with daily/weekly/monthly aggregates ([c24ce5f](https://github.com/jakepenzak/litellm-pulse/commit/c24ce5f453f12a170bfe8f3cb86a7ba5c30af2d9))
24
+
25
+ ### 🔧 CI/CD
26
+
27
+ * add CI/CD, release-please, tests, and project infrastructure ([4b184d3](https://github.com/jakepenzak/litellm-pulse/commit/4b184d3b99635cc1ce49a00a89405ef5b956409d))
@@ -0,0 +1,18 @@
1
+ FROM python:3.12-slim
2
+
3
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
4
+
5
+ WORKDIR /app
6
+
7
+ COPY pyproject.toml README.md ./
8
+ COPY litellm_pulse/ litellm_pulse/
9
+
10
+ RUN uv sync --no-dev
11
+
12
+ RUN mkdir -p /app/data
13
+
14
+ VOLUME /app/data
15
+
16
+ EXPOSE 8000
17
+
18
+ CMD ["uv", "run", "litellm-pulse"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jake Pieniazek
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,27 @@
1
+ .PHONY: help venv tests coverage run
2
+
3
+ help:
4
+ @printf "Usage: make <target> \n\n"
5
+ @printf "Targets:\n"
6
+ @printf " venv Create/sync virtual environment and install hooks.\n"
7
+ @printf " tests Run tests with pytest.\n"
8
+ @printf " coverage Serve coverage report at http://localhost:8080.\n"
9
+ @printf " run Run the server locally.\n"
10
+ @printf " help Show this help message.\n"
11
+
12
+ venv:
13
+ @echo "Syncing venv based on lock file..."
14
+ @uv sync --all-extras --all-groups --frozen
15
+ @uv run pre-commit install
16
+
17
+ tests:
18
+ @echo "Running tests with pytest..."
19
+ @uv run pytest tests/
20
+
21
+ run:
22
+ @echo "Starting LiteLLM Pulse..."
23
+ @uv run litellm-pulse
24
+
25
+ coverage:
26
+ @echo "Serving coverage report at http://localhost:8080 ..."
27
+ @uv run python -m http.server 8080 -d tests/reports/htmlcov