-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
🔴 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 logicEvery user writes their own HTTP client code.
📄 File: app/main.py
app = FastAPI(...)
# OpenAPI spec is auto-generated but not used for SDK generationOpenAPI spec exists but not leveraged for SDK generation.
4. PROPOSED SOLUTION
Create official SDK packages for Python (PyPI) and TypeScript (npm):
- Type-safe clients with full IDE support
- Automatic retry and error handling
- Streaming support built-in
- 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
Labels
No labels