Skip to content

Commit 390c78b

Browse files
Merge pull request #7 from KeyValueSoftwareSystems/develop
dev to main
2 parents cd109a0 + 69e85b7 commit 390c78b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+8360
-272
lines changed

.cursor/rules/general.mdc

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
description:
3+
globs:
4+
alwaysApply: false
5+
---
6+
Development Approach:
7+
- Make small, focused changes incrementally
8+
- Break complex problems into manageable pieces
9+
- Give concise responses, avoid verbosity
10+
- Don't change code unless explicitly asked
11+
- Follow existing patterns and conventions
12+
13+
Code Quality:
14+
- Write clean, self-documenting code
15+
- Single responsibility per function/class
16+
- Make code easily unit testable
17+
- Consider future developers
18+
- Use consistent type hints
19+
20+
Organization:
21+
- Use logical file structure matching domain
22+
- Keep files focused on single concerns
23+
- Follow consistent naming conventions
24+
- Prefer composition over inheritance
25+
26+
Method Structure:
27+
- Each method should do one thing well
28+
- Include proper docstrings with purpose, parameters, returns, exceptions
29+
- Use type hints consistently
30+
- Handle errors explicitly and predictably
31+
- Keep methods small and focused
32+
33+
Error Handling:
34+
- Be explicit, avoid implicit behavior
35+
- Use custom exception classes with clear hierarchy
36+
- Include enough context for debugging
37+
- Fail fast, detect and report errors early
38+
- Handle errors appropriately for the use case
39+
40+
Testing:
41+
- Test success cases to verify happy path
42+
- Test error cases for proper error handling
43+
- Test edge cases and boundary conditions
44+
- Use realistic data resembling production scenarios
45+
- Keep tests focused on one concept per method
46+
47+
Documentation:
48+
- Write concise, clear docstrings for public APIs
49+
- Comment complex logic to explain why, not what
50+
- Keep comments current, remove outdated ones
51+
- Focus on intent and reasoning behind decisions
52+
- Be minimal but helpful, avoid noise
53+
54+
Code Style:
55+
- Follow language conventions like PEP 8 for Python
56+
- Use automated formatters for consistency
57+
- Prefer descriptive names over comments
58+
- Use named constants, avoid magic numbers
59+
- Keep code flat, avoid deep nesting
60+
61+
Response Guidelines:
62+
- Start response with '<hi>'
63+
- Answer what's asked, don't over-engineer
64+
- Provide immediately runnable solutions
65+
- Explain trade-offs when multiple approaches exist
66+
- Be helpful but concise
67+
- Work within established architecture patterns
68+
- When you make structural, architectural, or functional changes, (or model changes, new decisions/gotchas, etc.) ask whether to update @project_context.mdc
69+
- End response with '</hi>'

.cursor/rules/project_context.mdc

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
---
2+
description:
3+
globs:
4+
alwaysApply: false
5+
---
6+
# Project Context: Siren AI Python SDK
7+
8+
## Project Summary
9+
10+
Official Python SDK for the [Siren notification platform](mdc:https:/docs.trysiren.io). Provides type-safe interface for managing templates, workflows, users, messaging, and webhooks. Built with Pydantic validation, structured error handling, and modular client-based architecture.
11+
12+
## Key Features / Functional Modules
13+
14+
- **Templates** - Create, update, delete, publish templates and channel configurations → `siren/clients/templates.py`
15+
- **Users** - Add, update, delete users → `siren/clients/users.py`
16+
- **Messaging** - Send messages, get replies, track status → `siren/clients/messaging.py`
17+
- **Webhooks** - Configure notification and inbound webhooks → `siren/clients/webhooks.py`
18+
- **Workflows** - Trigger single/bulk workflows and scheduling → `siren/clients/workflows.py`
19+
- **Client** - Unified API entry point → `siren/client.py`
20+
21+
## Codebase Structure Overview
22+
23+
```
24+
siren-ai/
25+
├── siren/ # Main SDK package
26+
│ ├── client.py # Main SirenClient - unified API entry point
27+
│ ├── exceptions.py # Custom exception classes (SirenAPIError, SirenSDKError)
28+
│ ├── clients/ # Domain client implementations (core pattern)
29+
│ │ ├── base.py # BaseClient - shared HTTP/error handling
30+
│ │ ├── templates.py # TemplateClient - template operations
31+
│ │ ├── users.py # UserClient - user management
32+
│ │ ├── messaging.py # MessageClient - message operations
33+
│ │ ├── webhooks.py # WebhookClient - webhook configuration
34+
│ │ └── workflows.py # WorkflowClient - workflow operations
35+
│ └── models/ # Pydantic data models
36+
│ ├── base.py # Base response models and common patterns
37+
│ ├── templates.py # Template-specific models
38+
│ ├── user.py # User-specific models
39+
│ ├── messaging.py # Messaging models
40+
│ ├── webhooks.py # Webhook models
41+
│ └── workflows.py # Workflow models
42+
├── tests/ # Comprehensive test suite with ~92% coverage
43+
├── examples/ # Usage examples for each module
44+
├── pyproject.toml # Project configuration, dependencies, tools
45+
└── README.md # Installation, usage, and API documentation
46+
```
47+
48+
## Architecture & Data Flow
49+
50+
**Layered Architecture**:
51+
- **Client** (`SirenClient`) - Thin facade delegating to domain clients
52+
- **Domain Clients** (`TemplateClient`, `UserClient`, etc.) - Domain-specific API handlers, inherit from `BaseClient` for unified HTTP/error handling
53+
- **Models** (Pydantic) - Request/response validation, field aliasing (snake_case ↔ camelCase)
54+
- **Exceptions** - `SirenAPIError` (API errors: 400/401/404) vs `SirenSDKError` (SDK issues: network/validation)
55+
56+
**BaseClient Pattern** (Core Architecture):
57+
- All domain clients inherit from `BaseClient` for consistent HTTP handling
58+
- Requires both `request_model` and `response_model` for JSON operations
59+
- Automatic Pydantic validation, error handling, and response parsing
60+
- Common patterns: `DeleteResponse[None]` for 204 responses, flexible models with optional fields
61+
62+
**Request Flow**: Client → Domain Client → HTTP Request → API → Response → Model → Client
63+
- Domain clients prepare requests with Pydantic validation → HTTP to Siren API → Responses parsed through models → Errors become structured exceptions
64+
65+
**Implementation Details**:
66+
- **HTTP Client**: `requests` library with 10s timeout (hardcoded, TODO: make configurable)
67+
- **Authentication**: Bearer token in `Authorization` header
68+
- **Status Handling**: Explicit `if status_code == 200` checks instead of `response.ok`
69+
- **API Versioning**: Templates/Users/Messaging/Webhooks use `/api/v1/public/`, Workflows use `/api/v2/`
70+
- **Environment Support**: Both `SirenClient` *and* `AsyncSirenClient` automatically read `SIREN_API_KEY` and optional `SIREN_ENV` on instantiation. Production (`https://api.trysiren.io`) is the default; switch to dev (`https://api.dev.trysiren.io`) by setting `SIREN_ENV=dev` or passing `env="dev"` explicitly.
71+
72+
## Tech Stack
73+
74+
**Core**: Python 3.8+, `requests`, `pydantic[email]`
75+
**Dev Tools**: `pytest` + mocking, `ruff`, `pyright`, `pre-commit`, `uv`
76+
77+
## Testing
78+
79+
**Strategy**: `requests-mock` with realistic API data
80+
**Organization**: One test file per domain client, shared `client` fixture
81+
**Philosophy**: SDK testing focuses on request formatting, response parsing, error propagation - not API business logic
82+
83+
## Key Files
84+
85+
- **`siren/client.py`** - Main client interface
86+
- **`siren/clients/base.py`** - BaseClient with unified HTTP/error handling (core pattern)
87+
- **`siren/clients/templates.py`** - Most complex domain client, full BaseClient patterns
88+
- **`siren/models/base.py`** - Core models and error handling
89+
- **`siren/exceptions.py`** - Exception patterns
90+
91+
## Gotchas
92+
93+
**Field Serialization**: Always use `by_alias=True` when calling `model_dump()`
94+
**BaseClient Requirements**: Both request_model and response_model needed for JSON operations
95+
96+
## TODO / Future Areas
97+
98+
**Architecture Enhancements**:
99+
- Add retry logic for transient network failures
100+
- Add request/response logging capabilities
101+
102+
**Testing Gaps**:
103+
- Integration tests against live API (currently only unit tests with mocks)
104+
105+
## Documentation / Examples
106+
107+
**Example Script Guidelines**:
108+
- Call `dotenv.load_dotenv()` first so environment variables from a `.env` file are available.
109+
- Instantiate the sync (`SirenClient()`) or async (`AsyncSirenClient()`) client **without arguments** – the constructor will pick up `SIREN_API_KEY` & `SIREN_ENV` automatically.
110+
- Wrap core SDK calls in minimal error handling:
111+
```python
112+
try:
113+
... # SDK call(s)
114+
except SirenAPIError as e:
115+
print(f"API error: {e.error_code} - {e.api_message}")
116+
except SirenSDKError as e:
117+
print(f"SDK error: {e.message}")
118+
```
119+
- Print only the key fields from responses (e.g., `id`, `url`, `status`) to keep output concise.
120+
- Scripts should demonstrate one or two primary operations per domain—avoid extra verbosity.
121+
122+
## HTTP Transport & Sync/Async Support
123+
- Under the hood the SDK now uses a pluggable transport layer (`siren/http/transport.py`).
124+
- **Sync** clients delegate to `SyncTransport` (currently wraps `requests`, easy to flip to `httpx.Client`).
125+
- **Async** clients delegate to `AsyncTransport` which wraps `httpx.AsyncClient`.
126+
- Every domain client has a 1-to-1 async counterpart; `AsyncSirenClient` exposes them.
127+
- Sync and async share identical method names and signatures—just `await` the async version.
128+
- Testing: existing sync tests use `requests-mock`; async tests use **respx** for `httpx`.
129+
- Examples: each domain has both `*_async.py` and sync counterpart in `examples/` demonstrating identical flows.

.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+
workflow_dispatch:
5+
push:
6+
tags:
7+
- "v*.*.*" # e.g. v1.2.3
8+
9+
jobs:
10+
build-and-upload:
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: read
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- uses: actions/setup-python@v5
18+
with:
19+
python-version: "3.11" # choose what you like
20+
21+
- name: Install build tools
22+
run: python -m pip install --upgrade build twine
23+
24+
- name: Build wheel & sdist
25+
run: python -m build # outputs to ./dist
26+
27+
- name: Upload to PyPI
28+
env:
29+
TWINE_USERNAME: __token__
30+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
31+
run: python -m twine upload dist/* --non-interactive

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,6 @@ Thumbs.db
3434
secrets.yaml
3535
*.pem
3636
*.key
37+
38+
# windsurf rules
39+
.windsurfrules

LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
MIT License
2+
3+
Copyright (c) 2025 KeyValue Software Systems
4+
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.

0 commit comments

Comments
 (0)