Skip to content

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

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);
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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