Skip to content

[HIGH] Official SDK Packages for Python and TypeScript #47

@Sakeeb91

Description

@Sakeeb91

🔴 Priority: HIGH | Type: Feature

1. SUMMARY

  • No official SDK packages exist for Python or JavaScript/TypeScript. Users must write their own HTTP clients.
  • Impact: Higher barrier to adoption. Repeated boilerplate code. No type safety. Inconsistent error handling across integrations.

2. SYSTEM CONTEXT

Current integration (manual):
─────────────────────────────
Developer → reads API docs → writes requests code → handles errors → no types

Desired integration (SDK):
─────────────────────────────
Developer → pip install text2sql-client → import → use typed methods

sdks/                         # NEW - proposed structure
├── python/
│   ├── text2sql_client/
│   │   ├── __init__.py
│   │   ├── client.py         # Main client class
│   │   ├── models.py         # Pydantic models
│   │   ├── exceptions.py     # Custom exceptions
│   │   └── streaming.py      # SSE support
│   ├── pyproject.toml
│   └── README.md
├── typescript/
│   ├── src/
│   │   ├── index.ts
│   │   ├── client.ts
│   │   ├── types.ts
│   │   └── streaming.ts
│   ├── package.json
│   └── README.md
└── openapi/
    └── generate.py           # Auto-generate from OpenAPI spec

3. CURRENT STATE (with code)

📄 File: docs/WORKFLOW_GUIDE.md (manual client example)

# Users must write this themselves
import requests

response = requests.post(
    "http://localhost:8000/api/v1/query",
    headers={"X-API-Key": "your-key"},
    json={
        "query": question,
        "database_id": database_id,
        "execute": True,
    },
)
result = response.json()
# No type hints, no error handling, no retry logic

Every user writes their own HTTP client code.

📄 File: app/main.py

app = FastAPI(...)
# OpenAPI spec is auto-generated but not used for SDK generation

OpenAPI spec exists but not leveraged for SDK generation.

4. PROPOSED SOLUTION

Create official SDK packages for Python (PyPI) and TypeScript (npm):

  1. Type-safe clients with full IDE support
  2. Automatic retry and error handling
  3. Streaming support built-in
  4. Generated from OpenAPI spec for consistency

📄 File: sdks/python/text2sql_client/client.py (NEW)

from typing import AsyncIterator
import httpx
from .models import QueryRequest, QueryResponse, StreamEvent
from .exceptions import Text2SQLError, AuthenticationError, RateLimitError

class Text2SQLClient:
    """Official Python client for Text2SQL Agent."""

    def __init__(
        self,
        base_url: str = "http://localhost:8000",
        api_key: str | None = None,
        token: str | None = None,
        timeout: float = 30.0,
        max_retries: int = 3,
    ):
        self.base_url = base_url.rstrip("/")
        self._client = httpx.AsyncClient(
            base_url=self.base_url,
            timeout=timeout,
            headers=self._build_headers(api_key, token),
        )
        self._max_retries = max_retries

    async def query(
        self,
        question: str,
        database_id: str = "default",
        execute: bool = True,
        show_reasoning: bool = False,
        max_rows: int = 100,
    ) -> QueryResponse:
        """Generate SQL from natural language."""
        request = QueryRequest(
            query=question,
            database_id=database_id,
            execute=execute,
            show_reasoning=show_reasoning,
            max_rows=max_rows,
        )
        response = await self._request("POST", "/api/v1/query", json=request.model_dump())
        return QueryResponse.model_validate(response)

    async def stream_query(
        self,
        question: str,
        database_id: str = "default",
        execute: bool = True,
    ) -> AsyncIterator[StreamEvent]:
        """Stream SQL generation with real-time updates."""
        async with self._client.stream(
            "POST",
            "/api/v1/query/stream",
            json={"query": question, "database_id": database_id, "execute": execute},
        ) as response:
            async for line in response.aiter_lines():
                if line.startswith("data:"):
                    yield StreamEvent.parse_raw(line[5:])

    async def validate(self, sql: str, database_id: str = "default") -> ValidationResponse:
        """Validate SQL syntax and schema."""
        ...

    async def list_databases(self) -> list[DatabaseInfo]:
        """List registered databases."""
        ...

    async def health(self) -> HealthResponse:
        """Check service health."""
        ...

📄 File: sdks/typescript/src/client.ts (NEW)

import { QueryRequest, QueryResponse, StreamEvent } from './types';

export class Text2SQLClient {
  private baseUrl: string;
  private headers: HeadersInit;

  constructor(options: ClientOptions) {
    this.baseUrl = options.baseUrl || 'http://localhost:8000';
    this.headers = {
      'Content-Type': 'application/json',
      ...(options.apiKey && { 'X-API-Key': options.apiKey }),
      ...(options.token && { 'Authorization': `Bearer ${options.token}` }),
    };
  }

  async query(request: QueryRequest): Promise<QueryResponse> {
    const response = await fetch(`${this.baseUrl}/api/v1/query`, {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify(request),
    });

    if (!response.ok) {
      throw await this.handleError(response);
    }

    return response.json();
  }

  async *streamQuery(request: QueryRequest): AsyncGenerator<StreamEvent> {
    const response = await fetch(`${this.baseUrl}/api/v1/query/stream`, {
      method: 'POST',
      headers: { ...this.headers, 'Accept': 'text/event-stream' },
      body: JSON.stringify(request),
    });

    const reader = response.body?.getReader();
    // ... SSE parsing
  }
}

5. IMPLEMENTATION CHECKLIST

Phase 1: Python SDK

  • Set up sdks/python/ project structure
  • Define Pydantic models matching API schemas
  • Implement sync and async clients
  • Add retry logic with exponential backoff
  • Implement SSE streaming support
  • Add comprehensive error handling
  • Write unit tests with pytest
  • Write usage examples and README
  • Configure PyPI publishing in CI

Phase 2: TypeScript SDK

  • Set up sdks/typescript/ project structure
  • Define TypeScript types/interfaces
  • Implement client class with fetch
  • Add streaming support with ReadableStream
  • Add error handling with custom error classes
  • Write tests with Jest/Vitest
  • Write usage examples and README
  • Configure npm publishing in CI

Phase 3: OpenAPI Generation

  • Create script to export OpenAPI spec
  • Evaluate code generators (openapi-generator, openapi-typescript)
  • Set up auto-generation pipeline
  • Add validation that SDK matches spec

Phase 4: Documentation & Examples

  • Add SDK installation to main README
  • Create SDK-specific documentation
  • Add examples for common use cases
  • Create migration guide from raw HTTP

6. FILES TO MODIFY TABLE

File Lines Action Description
sdks/python/text2sql_client/ NEW Create Python SDK package
sdks/python/pyproject.toml NEW Create Python package config
sdks/typescript/src/ NEW Create TypeScript SDK source
sdks/typescript/package.json NEW Create npm package config
.github/workflows/publish-sdks.yml NEW Create SDK publishing workflow
README.md TBD Modify Add SDK installation instructions
docs/WORKFLOW_GUIDE.md TBD Modify Update with SDK examples

7. RISK ASSESSMENT

Risk Impact Mitigation
SDK out of sync with API 🔴 Auto-generate from OpenAPI; integration tests
Breaking changes affect SDK users 🟡 Semantic versioning; deprecation warnings
Maintenance of two SDKs 🟡 Share logic via OpenAPI; minimal custom code
Publishing credentials leak 🟡 Use GitHub secrets; restrict publish permissions

8. RELATED CONTEXT

  • OpenAPI spec: Auto-generated at /openapi.json
  • Current examples: docs/WORKFLOW_GUIDE.md
  • Similar SDKs: openai-python, anthropic-sdk-python
  • Publishing: PyPI, npm

9. USAGE EXAMPLES

Python (after SDK):

from text2sql_client import Text2SQLClient

client = Text2SQLClient(api_key="your-key")

# Simple query
result = await client.query("Show top 10 customers by revenue")
print(result.sql)
print(result.confidence)

# Streaming
async for event in client.stream_query("Monthly sales trend"):
    if event.type == "sql_generated":
        print(f"SQL: {event.data.sql}")
    elif event.type == "result_batch":
        print(f"Got {len(event.data.rows)} rows")

TypeScript (after SDK):

import { Text2SQLClient } from 'text2sql-client';

const client = new Text2SQLClient({ apiKey: 'your-key' });

// Simple query
const result = await client.query({
  query: 'Show top 10 customers by revenue',
  databaseId: 'analytics',
});
console.log(result.sql);

// Streaming
for await (const event of client.streamQuery({ query: '...' })) {
  console.log(event.type, event.data);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions