Skip to content

Latest commit

 

History

History
695 lines (524 loc) · 15.2 KB

File metadata and controls

695 lines (524 loc) · 15.2 KB

Contributing to Gatewayz Python SDK

Thank you for your interest in contributing to the Gatewayz Python SDK! This guide will help you understand the SDK architecture and how to set up your development environment.

Table of Contents


SDK Architecture

Overview

This SDK is auto-generated by docsalot from an OpenAPI specification. Understanding this is crucial:

  • Source of Truth: The OpenAPI spec (openapi.json) defines the API structure
  • Generated Code: Most Python files are generated and should NOT be manually edited
  • Regeneration: Changes to the API require regenerating the SDK from the updated OpenAPI spec

Directory Structure

sdks/python/
├── __init__.py                 # Main package exports (generated)
├── client.py                   # Main client classes (generated)
├── raw_client.py               # Raw HTTP client (generated)
├── core/                       # Core utilities (generated)
│   ├── http_client.py         # HTTP client implementation
│   ├── client_wrapper.py      # Client wrapper utilities
│   ├── request_options.py     # Request configuration
│   ├── api_error.py           # Error handling
│   └── ...
├── types/                      # Type definitions (generated)
│   ├── message.py
│   ├── chat_session.py
│   └── ...
├── errors/                     # Custom error classes (generated)
│   └── unprocessable_entity_error.py
├── [modules]/                  # API endpoint modules (generated)
│   ├── chat/
│   ├── authentication/
│   ├── providers/
│   ├── models/
│   └── ...
├── openapi.json               # OpenAPI specification (source)
├── pyproject.toml             # Package configuration (manual)
├── setup.py                   # Setup script (manual)
├── requirements.txt           # Dependencies (manual)
├── README.md                  # Documentation (manual)
├── examples.py                # Usage examples (manual)
└── test_install.py            # Installation test (manual)

Key Components

1. Client Classes (client.py)

Main Clients:

  • GatewayzApi - Synchronous client
  • AsyncGatewayzApi - Asynchronous client

Properties:

client = GatewayzApi(token="...", base_url="...")

# Sub-clients accessed as properties
client.chat              # Chat operations
client.authentication    # Auth operations
client.models           # Model catalog
client.providers        # Provider catalog
client.admin            # Admin operations
# ... etc

2. Core Layer (core/)

  • http_client.py - HTTP communication layer (httpx wrapper)
  • client_wrapper.py - Manages base URL, headers, authentication
  • request_options.py - Per-request configuration (timeout, retries, headers)
  • api_error.py - Base exception class for API errors
  • pydantic_utilities.py - Pydantic v2 compatibility utilities

3. Types (types/)

Pydantic models for request/response serialization:

from gatewayz.types import Message, ChatSession, UserProfileResponse

# All types are Pydantic models with validation
message = Message(role="user", content="Hello")

4. Module Clients ([module]/client.py)

Each module has its own client class:

# Example: chat/client.py
class ChatClient:
    def chat_completions(self, model: str, messages: List[Message], ...):
        # Implementation
        pass

5. Raw Clients ([module]/raw_client.py)

Lower-level clients that return raw HTTP responses:

# For advanced use cases needing direct HTTP response access
raw_response = client.with_raw_response.chat.chat_completions(...)
print(raw_response.status_code)
print(raw_response.headers)

Package Installation Mapping

Important: The package uses a special directory mapping:

# pyproject.toml
[tool.setuptools]
package-dir = {"gatewayz" = "."}

This tells Python:

  • The current directory (.) should be installed as the gatewayz package
  • When you import gatewayz, Python loads from this directory
  • All subdirectories become subpackages: gatewayz.core, gatewayz.types, etc.

Getting Started

Prerequisites

  • Python 3.7 or higher
  • pip (Python package installer)
  • git
  • Virtual environment tool (venv, virtualenv, or conda)

Clone the Repository

# Clone the repository
git clone https://github.com/yourusername/gatewayz-sdk.git
cd gatewayz-sdk/sdks/python

# Or if this is a monorepo
cd /path/to/repo/sdks/python

Development Setup

1. Create a Virtual Environment

# Using venv (recommended)
python -m venv venv

# Activate on Linux/Mac
source venv/bin/activate

# Activate on Windows
venv\Scripts\activate

# Using conda
conda create -n gatewayz-dev python=3.10
conda activate gatewayz-dev

2. Install in Editable Mode

# Install the SDK in editable/development mode
pip install -e .

# Install with development dependencies
pip install -e ".[dev]"

This installs:

  • Core dependencies: httpx, pydantic
  • Dev dependencies: pytest, black, isort, mypy, ruff

3. Verify Installation

# Run the installation test
python test_install.py

# Or test import directly
python -c "from gatewayz import GatewayzApi; print('✓ SDK installed')"

4. Set Up Environment Variables (Optional)

# Create a .env file for local testing
cat > .env << EOF
GATEWAYZ_API_TOKEN=your-api-token-here
GATEWAYZ_BASE_URL=https://api.gatewayz.ai
EOF

# Add .env to .gitignore (if not already there)
echo ".env" >> .gitignore

Making Changes

What Can You Modify?

✓ Safe to Edit:

  • README.md - Documentation
  • CONTRIBUTING.md - This file
  • pyproject.toml - Package configuration
  • setup.py - Setup script
  • requirements.txt - Dependencies
  • examples.py - Usage examples
  • test_install.py - Installation tests
  • Any custom test files you create
  • .gitignore, .env, etc.

✗ DO NOT Edit (Generated by docsalot):

  • client.py
  • raw_client.py
  • __init__.py
  • core/*
  • types/*
  • errors/*
  • [module]/client.py
  • [module]/raw_client.py
  • Any file with "This file was auto-generated by docsalot" header

If You Need to Change Generated Code

The proper process:

  1. Modify the OpenAPI specification (openapi.json)
  2. Regenerate the SDK using docsalot:
    # Install docsalot CLI
    npm install -g docsalot-api
    
    # Regenerate SDK
    docsalot generate --group python
  3. Test the regenerated SDK
  4. Commit both the spec and regenerated code

Adding Examples

# Edit examples.py
vim examples.py

# Add a new example function
def new_example():
    """Description of the example."""
    client = GatewayzApi(
        token="YOUR_API_TOKEN",
        base_url="https://api.gatewayz.ai"
    )

    # Your example code here
    result = client.models.get_models(limit=5)
    print(result)

Adding Custom Tests

# Create a tests directory
mkdir -p tests

# Create a test file
cat > tests/test_authentication.py << 'EOF'
import pytest
from gatewayz import GatewayzApi
from gatewayz.core import ApiError

def test_client_initialization():
    """Test that client can be initialized."""
    client = GatewayzApi(
        token="test-token",
        base_url="https://api.gatewayz.ai"
    )
    assert client is not None
    assert client._client_wrapper is not None

def test_health_check():
    """Test health check endpoint."""
    client = GatewayzApi(
        token="test",
        base_url="https://api.gatewayz.ai"
    )
    # This would need a mock or real API for actual testing
    # response = client.health_check_health_get()
    # assert response is not None
EOF

# Run tests
pytest tests/

Testing

Run Installation Test

python test_install.py

Run Unit Tests (if created)

# Run all tests
pytest

# Run with coverage
pytest --cov=gatewayz tests/

# Run specific test file
pytest tests/test_authentication.py

# Run with verbose output
pytest -v

Manual Testing

# Create a test script
cat > manual_test.py << 'EOF'
from gatewayz import GatewayzApi

client = GatewayzApi(
    token="your-api-token",
    base_url="https://api.gatewayz.ai"
)

# Test endpoints
try:
    health = client.health_check_health_get()
    print(f"✓ Health check: {health}")

    models = client.models.get_models(limit=3)
    print(f"✓ Found {len(models)} models")
except Exception as e:
    print(f"✗ Error: {e}")
EOF

python manual_test.py

Testing Against Local API

If you're running a local instance of the Gatewayz API:

from gatewayz import GatewayzApi

# Point to local API
client = GatewayzApi(
    token="test-token",
    base_url="http://localhost:8000"
)

response = client.health_check_health_get()
print(response)

Code Style

Formatting

This project uses standard Python formatting tools:

# Format code with black
black .

# Sort imports with isort
isort .

# Run both
black . && isort .

Linting

# Run ruff (fast Python linter)
ruff check .

# Fix auto-fixable issues
ruff check --fix .

# Run mypy for type checking
mypy .

Pre-commit Checks

Before committing, run:

# Format and lint
black . && isort . && ruff check --fix .

# Run tests
python test_install.py

# Verify imports work
python -c "from gatewayz import GatewayzApi; print('✓')"

Style Guidelines

  • Type hints: Use type hints for all function signatures
  • Docstrings: Document all public functions with docstrings
  • Line length: 120 characters (configured in pyproject.toml)
  • Naming:
    • Classes: PascalCase
    • Functions/methods: snake_case
    • Constants: UPPER_CASE
    • Private: prefix with _

Example:

from typing import List, Optional
from gatewayz.types import Message

def send_chat_message(
    client: GatewayzApi,
    message: str,
    model: str = "gpt-3.5-turbo",
    max_tokens: Optional[int] = None
) -> dict:
    """
    Send a chat message and return the response.

    Args:
        client: Initialized GatewayzApi client
        message: The message to send
        model: Model to use (default: gpt-3.5-turbo)
        max_tokens: Maximum tokens in response

    Returns:
        dict: API response

    Example:
        >>> client = GatewayzApi(token="...", base_url="...")
        >>> response = send_chat_message(client, "Hello!")
    """
    response = client.chat.chat_completions(
        model=model,
        messages=[Message(role="user", content=message)],
        max_tokens=max_tokens
    )
    return response

Submitting Changes

Workflow

  1. Create a branch

    git checkout -b feature/add-new-examples
  2. Make your changes

    • Edit documentation, examples, or configuration
    • DO NOT modify generated code directly
  3. Test your changes

    python test_install.py
    # Run any other relevant tests
  4. Format and lint

    black . && isort .
    ruff check --fix .
  5. Commit your changes

    git add .
    git commit -m "Add examples for subscription management"
  6. Push to your fork

    git push origin feature/add-new-examples
  7. Create a Pull Request

    • Go to GitHub and create a PR
    • Describe your changes
    • Link any related issues

Commit Message Guidelines

Use conventional commit format:

<type>(<scope>): <subject>

<body>

<footer>

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation changes
  • style: Formatting, missing semi-colons, etc.
  • refactor: Code refactoring
  • test: Adding tests
  • chore: Maintenance

Examples:

git commit -m "docs: add async usage examples to README"
git commit -m "feat: add helper functions for common operations"
git commit -m "fix: correct get_user_balance usage in examples"
git commit -m "chore: update dependencies to latest versions"

Pull Request Checklist

Before submitting a PR:

  • Changes are tested and working
  • Code is formatted with black and isort
  • No linting errors from ruff
  • Documentation is updated (if applicable)
  • Examples are updated (if applicable)
  • Commit messages follow guidelines
  • No generated files are manually modified
  • PR description explains the changes

Architecture Deep Dive

How docsalot Generation Works

  1. Input: openapi.json specification
  2. Processing: docsalot reads the spec and generates:
    • Client classes for each endpoint group
    • Pydantic models for all request/response types
    • Type-safe methods with proper signatures
    • Async variants of all methods
  3. Output: Complete Python SDK

HTTP Flow

User Code
    ↓
GatewayzApi Client
    ↓
Module Client (e.g., ChatClient)
    ↓
RawClient (HTTP layer)
    ↓
ClientWrapper (adds auth, headers)
    ↓
HttpClient (httpx wrapper)
    ↓
HTTP Request → API

Authentication Flow

# Token is passed during initialization
client = GatewayzApi(token="sk-xxx", base_url="...")

# ClientWrapper wraps the token
wrapper = SyncClientWrapper(token="sk-xxx", ...)

# HttpClient adds it to every request
headers = {"Authorization": f"Bearer {token}"}

Error Handling

API returns error
    ↓
HttpClient receives response
    ↓
ApiError raised (if status >= 400)
    ↓
Specific error type (e.g., UnprocessableEntityError)
    ↓
User's except block

Type System

All types are Pydantic v2 models:

# types/message.py (generated)
class Message(pydantic.BaseModel):
    role: str
    content: str

    # Automatic validation
    # Serialization to JSON
    # IDE autocomplete support

Troubleshooting

Import Errors

# Reinstall in editable mode
pip uninstall gatewayz -y
pip install -e .

Type Conflicts

If you see "types" module conflicts:

  • This is due to the types/ directory conflicting with Python's built-in types module
  • The SDK handles this, but running Python from the SDK directory can cause issues
  • Solution: Always run tests from outside the SDK directory or use absolute imports

Regeneration Issues

If SDK regeneration fails:

# Check docsalot version
docsalot --version

# Update docsalot
npm update -g docsalot-api

# Validate OpenAPI spec
docsalot check

Getting Help


License

By contributing, you agree that your contributions will be licensed under the same license as the project.


Additional Resources


Thank you for contributing to Gatewayz Python SDK! 🚀