Skip to content

Commit 2fe87c2

Browse files
Initial commit: Last9 GenAI SDK for Python
A production-ready Python SDK for adding Last9 observability attributes to OpenTelemetry GenAI spans. Features: - Conversation and workflow tracking with automatic context propagation - Cost tracking with ModelPricing for major LLM providers - @observe() decorator for automatic span creation and attribute capture - Support for OpenAI, Anthropic, and LangChain integrations - Tool/function call tracking with proper span classification - OTLP export support for Last9 integration - Comprehensive test suite (117 tests, 85% coverage) - Python 3.10+ support with security-fixed dependencies Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
0 parents  commit 2fe87c2

37 files changed

Lines changed: 12793 additions & 0 deletions

.github/workflows/ci.yml

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, master, develop]
6+
pull_request:
7+
branches: [main, master, develop]
8+
9+
jobs:
10+
test:
11+
name: Test on Python ${{ matrix.python-version }}
12+
runs-on: ubuntu-latest
13+
strategy:
14+
matrix:
15+
python-version: ["3.10", "3.11", "3.12", "3.13"]
16+
17+
steps:
18+
- name: Check out code
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Python ${{ matrix.python-version }}
22+
uses: actions/setup-python@v5
23+
with:
24+
python-version: ${{ matrix.python-version }}
25+
26+
- name: Install dependencies
27+
run: |
28+
python -m pip install --upgrade pip
29+
pip install -e ".[dev,otlp]"
30+
31+
- name: Run tests
32+
run: |
33+
pytest tests/ -v --cov=last9_genai --cov-report=xml --cov-report=term
34+
35+
- name: Upload coverage to Codecov
36+
uses: codecov/codecov-action@v4
37+
if: matrix.python-version == '3.11'
38+
with:
39+
file: ./coverage.xml
40+
fail_ci_if_error: false
41+
42+
lint:
43+
name: Lint and Format Check
44+
runs-on: ubuntu-latest
45+
46+
steps:
47+
- name: Check out code
48+
uses: actions/checkout@v4
49+
50+
- name: Set up Python
51+
uses: actions/setup-python@v5
52+
with:
53+
python-version: "3.11"
54+
55+
- name: Install dependencies
56+
run: |
57+
python -m pip install --upgrade pip
58+
pip install black mypy pylint
59+
60+
- name: Check formatting with Black
61+
run: |
62+
black --check last9_genai/ tests/ examples/ setup.py
63+
64+
- name: Type check with mypy
65+
run: |
66+
mypy last9_genai/ --ignore-missing-imports || true
67+
68+
build:
69+
name: Build Package
70+
runs-on: ubuntu-latest
71+
needs: [test, lint]
72+
73+
steps:
74+
- name: Check out code
75+
uses: actions/checkout@v4
76+
77+
- name: Set up Python
78+
uses: actions/setup-python@v5
79+
with:
80+
python-version: "3.11"
81+
82+
- name: Install build tools
83+
run: |
84+
python -m pip install --upgrade pip
85+
pip install build twine
86+
87+
- name: Build package
88+
run: python -m build
89+
90+
- name: Check package with twine
91+
run: twine check dist/*
92+
93+
- name: Upload build artifacts
94+
uses: actions/upload-artifact@v4
95+
with:
96+
name: dist
97+
path: dist/

.github/workflows/publish.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*.*.*"
7+
workflow_dispatch:
8+
9+
jobs:
10+
build-and-publish:
11+
name: Build and Publish to PyPI
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Check out code
16+
uses: actions/checkout@v4
17+
18+
- name: Set up Python
19+
uses: actions/setup-python@v5
20+
with:
21+
python-version: "3.11"
22+
23+
- name: Install dependencies
24+
run: |
25+
python -m pip install --upgrade pip
26+
pip install -e ".[dev,otlp]"
27+
28+
- name: Run tests
29+
run: |
30+
pytest tests/ -v --cov=last9_genai_attributes
31+
32+
- name: Install build tools
33+
run: |
34+
pip install build twine
35+
36+
- name: Build package
37+
run: python -m build
38+
39+
- name: Check package
40+
run: twine check dist/*
41+
42+
- name: Publish to TestPyPI
43+
if: github.event_name == 'workflow_dispatch'
44+
env:
45+
TWINE_USERNAME: __token__
46+
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
47+
run: |
48+
twine upload --repository testpypi dist/* --verbose
49+
50+
- name: Publish to PyPI
51+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
52+
env:
53+
TWINE_USERNAME: __token__
54+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
55+
run: |
56+
twine upload dist/* --verbose
57+
58+
- name: Create GitHub Release
59+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
60+
uses: softprops/action-gh-release@v1
61+
with:
62+
files: dist/*
63+
generate_release_notes: true
64+
env:
65+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
*.so
6+
.Python
7+
build/
8+
develop-eggs/
9+
dist/
10+
downloads/
11+
eggs/
12+
.eggs/
13+
lib/
14+
lib64/
15+
parts/
16+
sdist/
17+
var/
18+
wheels/
19+
share/python-wheels/
20+
*.egg-info/
21+
.installed.cfg
22+
*.egg
23+
MANIFEST
24+
25+
# Virtual environments
26+
.venv/
27+
venv/
28+
ENV/
29+
env/
30+
31+
# Testing
32+
.pytest_cache/
33+
.coverage
34+
htmlcov/
35+
.tox/
36+
.nox/
37+
coverage.xml
38+
*.cover
39+
*.log
40+
41+
# IDEs
42+
.vscode/
43+
.idea/
44+
*.swp
45+
*.swo
46+
*~
47+
.DS_Store
48+
49+
# MyPy
50+
.mypy_cache/
51+
.dmypy.json
52+
dmyp.json
53+
54+
# Distribution / packaging
55+
*.spec
56+
57+
# Jupyter
58+
.ipynb_checkpoints
59+
60+
# pyenv
61+
.python-version
62+
63+
# Local development
64+
*.local
65+
.env
66+
.env.local
67+
68+
# Claude Code
69+
.claude/
70+
test_telemetry.py
71+
send_test_spans.py

CHANGELOG.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [1.0.0] - 2026-02-14
9+
10+
### Added
11+
- Initial open source release of Last9 Python AI SDK
12+
- **Cost Tracking**: Automatic cost calculation for 20+ AI models
13+
- Anthropic: Claude 3.5 Sonnet, Claude 3 Opus, Claude 3 Haiku
14+
- OpenAI: GPT-4o, GPT-4 Turbo, GPT-4, GPT-3.5 Turbo
15+
- Google: Gemini Pro, Gemini 1.5 Pro, Gemini 1.5 Flash
16+
- Cohere: Command R, Command R+
17+
- And more...
18+
- **Conversation Tracking**: Multi-turn conversation tracking with `gen_ai.conversation.id`
19+
- **Workflow Management**: Cost aggregation across multi-step workflows
20+
- **Span Classification**: `gen_ai.l9.span.kind` for filtering (llm/tool/prompt)
21+
- **Prompt Versioning**: Hash-based prompt template tracking and versioning
22+
- **Tool/Function Tracking**: Enhanced attributes for tool and function calls
23+
- **Performance Metrics**: Response times, request/response sizes, quality scores
24+
- **Content Events**: Input/output prompts as span events
25+
- **Standard Compliance**: Full compatibility with OpenTelemetry GenAI v1.28.0 conventions
26+
27+
### Documentation
28+
- Comprehensive README with usage examples
29+
- Installation guide (INSTALL.md)
30+
- Complete API reference
31+
- Working examples:
32+
- Basic usage with cost tracking
33+
- Anthropic Claude SDK integration
34+
- Conversation tracking with multi-turn support
35+
- Tool and function call tracking
36+
37+
### Technical Details
38+
- Built on OpenTelemetry Python SDK
39+
- Requires Python >=3.9 (aligned with OpenTelemetry API requirements)
40+
- Zero dependencies beyond OpenTelemetry
41+
- Works with any OTLP-compatible backend
42+
- Feature parity with last9-node-agent
43+
44+
### Integration Support
45+
- OpenAI Python SDK
46+
- Anthropic Python SDK
47+
- LangChain (via examples)
48+
- FastAPI (via examples)
49+
- Framework-agnostic design
50+
51+
---
52+
53+
## Versioning Guidelines
54+
55+
- **MAJOR** version for incompatible API changes
56+
- **MINOR** version for new functionality in a backwards compatible manner
57+
- **PATCH** version for backwards compatible bug fixes
58+
59+
For more information, see the [SPEC.md](SPEC.md) document.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Last9 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.

MANIFEST.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
include README.md
2+
include LICENSE
3+
include CHANGELOG.md
4+
include requirements.txt
5+
recursive-include examples *.py
6+
recursive-include tests *.py

0 commit comments

Comments
 (0)