Metadata-Version: 2.5
Name: ikc-ai-gateway-sdk
Version: 0.1.1
Summary: IKC AI Gateway 传输客户端 SDK：HTTP/SSE + gRPC 薄传输，统一响应解包 + traceId/reqId 回显
Author-email: shark8848 <admin@sharky-ai.com>
Keywords: ai-gateway,grpc,ikc,llm,sdk,sse,transport
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.7
Provides-Extra: all
Requires-Dist: grpcio>=1.62; extra == 'all'
Requires-Dist: ikc-sdk-lib>=0.11.0; extra == 'all'
Requires-Dist: protobuf>=4.25; extra == 'all'
Provides-Extra: grpc
Requires-Dist: grpcio>=1.62; extra == 'grpc'
Requires-Dist: protobuf>=4.25; extra == 'grpc'
Provides-Extra: models
Requires-Dist: ikc-sdk-lib>=0.11.0; extra == 'models'
Description-Content-Type: text/markdown

# ikc-ai-gateway-sdk

AI Gateway 的传输客户端 SDK：经 HTTP/SSE 或 gRPC 调用网关的六类能力（文本对话 / vLLM / 向量化 / 重排 / 语音识别 / 多模态），自动解包统一响应壳 `Envelope`、回显 `traceId/reqId`，非成功码抛异常。

SDK 只负责传输，不定义业务 schema：请求/响应可直接传 camelCase `dict`，也可使用可选 `models` extra 提供的类型化模型。

## 安装

```bash
pip install ikc-ai-gateway-sdk            # HTTP/SSE + CLI（最小依赖 httpx / pydantic）
pip install "ikc-ai-gateway-sdk[grpc]"    # 追加 gRPC 通道（grpcio / protobuf）
```

> `[models]` / `[all]` 额外引入类型化业务模型（可选的私有依赖）。

## 快速开始（HTTP / SSE）

```python
from ikc_ai_gateway_sdk import AIGatewayClient, AIGatewayError

with AIGatewayClient("http://127.0.0.1:18100", "<api-key>") as gw:
    # 同步对话 —— 返回解包后的 Envelope dict
    r = gw.chat({"model": "<model>",
                 "messages": [{"role": "user", "content": "你好"}]})
    print(r["data"]["text"], r["traceId"])

    # 流式对话（SSE）—— 逐块 yield data: 文本，末尾 [DONE]
    for chunk in gw.chat({"model": "<model>",
                          "messages": [{"role": "user", "content": "讲个笑话"}]},
                         stream=True):
        if chunk.strip() == "[DONE]":
            break
        print(chunk, end="", flush=True)

    # 向量化 / 重排
    gw.embed({"model": "<embed-model>", "input": ["你好", "世界"]})
    gw.rerank({"model": "<rerank-model>", "query": "…",
               "passages": ["…", "…"], "topK": 2})
```

### 异步长任务（语音识别 / 多模态：受理 + 轮询）

```python
acc = gw.transcribe({"source": {"inputUrl": "https://example.com/a.wav", "format": "wav"}},
                    async_mode=True)                        # 202 受理，顶层带 taskId
view = gw.get_task(acc["taskId"], domain="asr")["data"]     # 自动跟随嵌套 taskId 至终态
```

### 动态参数（头通道）

`headers=` 透传自定义头（`X-AI-Provider` / `X-AI-Model` / `X-AI-Hyper-Params` / `X-AI-Model-Info`）：

```python
gw.chat({"messages": [{"role": "user", "content": "hi"}]},
        headers={"X-AI-Provider": "openai", "X-AI-Model": "<model>",
                 "X-AI-Hyper-Params": '{"temperature":0.2}'})
```

## gRPC 通道

`client.grpc` 懒连接（需 `[grpc]` extra；gRPC 桩已内置于 wheel）：

```python
resp = gw.grpc.chat({"model": "<model>",
                     "messages": [{"role": "user", "content": "hi"}]}, capability="llm")
print(resp["data"]["text"], resp["traceId"])

for chunk in gw.grpc.chat_stream({"model": "<model>",
                                  "messages": [{"role": "user", "content": "讲个笑话"}]}):
    if chunk["done"]:
        break
    print(chunk["delta"], end="", flush=True)
```

## CLI（`ikc-aigw`）

地址/凭证经参数或环境变量 `AIGW_BASE_URL`（默认 `http://localhost:18100`）/ `AIGW_API_KEY`；请求体经 `--body`（JSON 串）、`--file`（JSON 文件）或 stdin 管道。

```bash
export AIGW_BASE_URL=http://127.0.0.1:18100 AIGW_API_KEY=<key>

ikc-aigw chat --body '{"model":"<model>","messages":[{"role":"user","content":"hi"}]}'
ikc-aigw chat --stream --body '{"model":"<model>","messages":[{"role":"user","content":"讲个笑话"}]}'
ikc-aigw embed --file req.json
cat req.json | ikc-aigw rerank
ikc-aigw transcribe --async --body '{"source":{"inputUrl":"https://example.com/a.wav","format":"wav"}}'
ikc-aigw task <taskId> --domain asr
ikc-aigw analyze --body '{"source":{"kind":"image","inputUrl":"https://example.com/x.jpg","prompt":"描述这张图"}}'
```

## 异常

| 异常 | 触发 |
| --- | --- |
| `AIGatewayError` | Envelope `errCode` 非成功码（携带 `err_code` / `err_msg` / `trace_id` / `req_id` / `data`） |
| `TransportError` | 网络不可达 / 超时 / 非法 JSON 响应 |
| `StreamError` | SSE 流式传输中断 |

## 方法一览

| 方法 | HTTP | 说明 |
| --- | --- | --- |
| `chat(request, stream=False, headers=None)` | `POST /api/v1/chat/completions` | `stream=True` 返回 SSE 迭代器 |
| `vllm_chat(request, stream=False, headers=None)` | `POST /api/v1/vllm/chat/completions` | 同上 |
| `embed(request, headers=None)` | `POST /api/v1/embeddings` | 返回解包后的 Envelope dict |
| `rerank(request, headers=None)` | `POST /api/v1/rerank` | |
| `transcribe(request, async_mode=False, headers=None)` | `POST /api/v1/asr/transcribe` | `async_mode=True` → `?async=true` 受理 |
| `analyze(request, async_mode=False, headers=None)` | `POST /api/v1/mm/analyze` | 同上 |
| `get_task(task_id, domain="asr")` | `GET /api/v1/{domain}/result/{task_id}` | 轮询异步任务（`domain` ∈ `asr` / `mm`） |
