Skip to content

Commit 12347af

Browse files
Initial commit: Pawa AI Python SDK with CI/CD.
Add the official SDK for chat, voice, embeddings, agents, and related APIs, with typed response models, retry/backoff, streaming helpers, and GitHub Actions. Co-authored-by: Cursor <cursoragent@cursor.com>
0 parents  commit 12347af

34 files changed

Lines changed: 2764 additions & 0 deletions

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PAWA_AI_API_KEY=your_api_key_here

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
## Summary
2+
3+
<!-- What does this PR change and why? -->
4+
5+
## Test plan
6+
7+
- [ ] `pytest` passes locally
8+
- [ ] `ruff check src tests` passes locally

.github/dependabot.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: github-actions
4+
directory: /
5+
schedule:
6+
interval: weekly
7+
open-pull-requests-limit: 5
8+
9+
- package-ecosystem: pip
10+
directory: /
11+
schedule:
12+
interval: weekly
13+
open-pull-requests-limit: 5

.github/workflows/ci.yml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
concurrency:
10+
group: ci-${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
lint:
15+
name: Lint
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- uses: actions/setup-python@v5
21+
with:
22+
python-version: "3.12"
23+
24+
- name: Install dependencies
25+
run: pip install ".[dev]"
26+
27+
- name: Ruff
28+
run: ruff check src tests
29+
30+
test:
31+
name: Test (Python ${{ matrix.python-version }})
32+
runs-on: ubuntu-latest
33+
strategy:
34+
fail-fast: false
35+
matrix:
36+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
37+
38+
steps:
39+
- uses: actions/checkout@v4
40+
41+
- uses: actions/setup-python@v5
42+
with:
43+
python-version: ${{ matrix.python-version }}
44+
45+
- name: Install dependencies
46+
run: pip install ".[dev]"
47+
48+
- name: Run tests
49+
run: pytest -q --cov=pawa_ai --cov-report=term-missing
50+
env:
51+
PAWA_AI_API_KEY: test-key-for-ci
52+
53+
- name: Upload coverage (3.12 only)
54+
if: matrix.python-version == '3.12'
55+
uses: actions/upload-artifact@v4
56+
with:
57+
name: coverage-report
58+
path: .coverage
59+
60+
build:
61+
name: Build package
62+
runs-on: ubuntu-latest
63+
needs: [lint, test]
64+
steps:
65+
- uses: actions/checkout@v4
66+
67+
- uses: actions/setup-python@v5
68+
with:
69+
python-version: "3.12"
70+
71+
- name: Install build tools
72+
run: pip install build
73+
74+
- name: Build sdist and wheel
75+
run: python -m build
76+
77+
- name: Upload dist artifacts
78+
uses: actions/upload-artifact@v4
79+
with:
80+
name: dist
81+
path: dist/

.github/workflows/publish.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
permissions:
8+
id-token: write
9+
10+
jobs:
11+
publish:
12+
name: Upload to PyPI
13+
runs-on: ubuntu-latest
14+
environment:
15+
name: pypi
16+
url: https://pypi.org/p/pawa-ai
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- uses: actions/setup-python@v5
21+
with:
22+
python-version: "3.12"
23+
24+
- name: Install build tools
25+
run: pip install build
26+
27+
- name: Build package
28+
run: python -m build
29+
30+
- name: Publish to PyPI
31+
uses: pypa/gh-action-pypi-publish@release/v1

.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
__pycache__/
2+
*.py[cod]
3+
*$py.class
4+
*.egg-info/
5+
.eggs/
6+
dist/
7+
build/
8+
.venv/
9+
venv/
10+
.env
11+
.env.*
12+
!.env.example
13+
.pytest_cache/
14+
.ruff_cache/
15+
.mypy_cache/
16+
.coverage
17+
htmlcov/
18+
*.swp
19+
.DS_Store
20+
.idea/
21+
.vscode/

CONTRIBUTING.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Contributing to pawa-ai-python
2+
3+
Thanks for helping improve the official Pawa AI Python SDK.
4+
5+
## Development setup
6+
7+
```bash
8+
python -m venv .venv
9+
source .venv/bin/activate
10+
pip install -e ".[dev]"
11+
```
12+
13+
## Checks before opening a PR
14+
15+
```bash
16+
ruff check src tests
17+
pytest -q
18+
python -m build
19+
```
20+
21+
## CI
22+
23+
GitHub Actions runs on every push and pull request to `main`:
24+
25+
- **Lint**`ruff check`
26+
- **Test**`pytest` on Python 3.9–3.13
27+
- **Build** — validates the package builds cleanly
28+
29+
## Releases
30+
31+
1. Update the version in `pyproject.toml` and `src/pawa_ai/__init__.py`
32+
2. Create a GitHub Release with a tag like `v0.2.0`
33+
3. The **Publish to PyPI** workflow uploads the package automatically
34+
35+
Configure the `pypi` environment in GitHub with [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) for `pawa-ai`.

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 Sartify Company Limited
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.

README.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Pawa AI Python SDK
2+
3+
[![CI](https://github.com/Sartify/pawa-ai-python/actions/workflows/ci.yml/badge.svg)](https://github.com/Sartify/pawa-ai-python/actions/workflows/ci.yml)
4+
5+
Official Python library for the [Pawa AI API](https://docs.pawa-ai.com).
6+
7+
Pawa AI provides African-built small language models for chat, voice, embeddings, document parsing, agents, and knowledge bases.
8+
9+
## Installation
10+
11+
```bash
12+
pip install pawa-ai
13+
```
14+
15+
## Quickstart
16+
17+
Set your API key:
18+
19+
```bash
20+
export PAWA_AI_API_KEY="your_api_key_here"
21+
```
22+
23+
Get your key from the [Builders Dashboard](https://builder.pawa-ai.com/dashboard?page=keys).
24+
25+
### Chat
26+
27+
```python
28+
from pawa_ai import PawaAI
29+
30+
client = PawaAI()
31+
32+
response = client.chat.create(
33+
model="pawa-v1-ember-20240924",
34+
messages=[
35+
{
36+
"role": "user",
37+
"content": [{"type": "text", "text": "Hello! How can I use AI in my app?"}],
38+
}
39+
],
40+
stream=False,
41+
)
42+
43+
print(response.text) # typed ChatCompletion response
44+
print(response.usage) # token usage when available
45+
```
46+
47+
### Streaming
48+
49+
```python
50+
with client.chat.create(
51+
model="pawa-v1-ember-20240924",
52+
messages=[
53+
{"role": "user", "content": [{"type": "text", "text": "Explain RAG in simple terms"}]}
54+
],
55+
stream=True,
56+
) as stream:
57+
for delta in stream.text_deltas():
58+
print(delta, end="", flush=True)
59+
60+
# Or collect the full response after streaming
61+
completion = stream.collect()
62+
print(completion.text)
63+
```
64+
65+
Async streaming:
66+
67+
```python
68+
stream = await client.chat.create(..., stream=True)
69+
text = await stream.collect_text()
70+
```
71+
72+
### Text-to-Speech
73+
74+
```python
75+
audio = client.voice.text_to_speech.create(
76+
model="pawa-tts-v1-20250704",
77+
text="Hello, this is Pawa AI speaking!",
78+
voice="liora",
79+
)
80+
81+
with open("output.mp3", "wb") as f:
82+
f.write(audio)
83+
```
84+
85+
### Embeddings
86+
87+
```python
88+
response = client.vectors.create(
89+
model="pawa-embeddings-v1-20241001",
90+
sentences=["Embed this sentence.", "And this one too."],
91+
lang="multi",
92+
)
93+
94+
embeddings = response.embeddings
95+
```
96+
97+
Pass `raw=True` on any resource method to get the original JSON dict instead of typed models.
98+
99+
### Retries with exponential backoff
100+
101+
```python
102+
from pawa_ai import PawaAI, RetryConfig
103+
104+
client = PawaAI(
105+
retry_config=RetryConfig(
106+
max_retries=3,
107+
initial_delay=0.5,
108+
max_delay=8.0,
109+
exponential_base=2.0,
110+
jitter=0.1,
111+
)
112+
)
113+
```
114+
115+
Retries automatically apply to rate limits (429), server errors (500/502/503/504), and connection failures. The SDK respects `Retry-After` response headers when present.
116+
117+
### Async
118+
119+
```python
120+
import asyncio
121+
from pawa_ai import AsyncPawaAI
122+
123+
async def main():
124+
async with AsyncPawaAI() as client:
125+
response = await client.chat.create(
126+
model="pawa-v1-ember-20240924",
127+
messages=[
128+
{"role": "user", "content": [{"type": "text", "text": "Habari yako?"}]}
129+
],
130+
)
131+
print(response["data"])
132+
133+
asyncio.run(main())
134+
```
135+
136+
## API coverage
137+
138+
| Resource | Methods |
139+
|----------|---------|
140+
| `client.chat` | `create`, `completions` |
141+
| `client.models` | `list`, `retrieve` |
142+
| `client.voice.text_to_speech` | `create` |
143+
| `client.voice.speech_to_text` | `create`, `transcribe` |
144+
| `client.vectors` | `create`, `embeddings` |
145+
| `client.documents` | `parse` |
146+
| `client.agents` | `create`, `update`, `delete`, `list`, `retrieve` |
147+
| `client.agents.chat` | `create` |
148+
| `client.storage.knowledge_base` | CRUD, `list_files`, `semantic_retrieval` |
149+
| `client.transcribe.workspaces` | CRUD, transcription management |
150+
151+
## Error handling
152+
153+
```python
154+
from pawa_ai import PawaAI, AuthenticationError, RateLimitError, ChatCompletion
155+
156+
client = PawaAI()
157+
158+
try:
159+
completion: ChatCompletion = client.chat.create(model="pawa-v1-ember-20240924", messages=[...])
160+
except AuthenticationError as e:
161+
print(f"Auth failed: {e.message}")
162+
except RateLimitError as e:
163+
print(f"Rate limited: {e.status_code}")
164+
```
165+
166+
## Documentation
167+
168+
- [Pawa AI Docs](https://docs.pawa-ai.com)
169+
- [API Reference](https://docs.pawa-ai.com/api-reference/introduction)
170+
- [Quickstart Guide](https://docs.pawa-ai.com/get-started/quickstart)
171+
172+
## License
173+
174+
MIT

0 commit comments

Comments
 (0)