Metadata-Version: 2.5
Name: ikc-open-platform-sdk
Version: 0.3.1
Summary: ikc-open-platform 第三方开发者 SDK：API Key 认证 + 统一壳解包 + 四类业务域（知识库/文档/解析/检索），业务模型复用 ikc-sdk-lib
Author: SITECH-iKM
Requires-Python: >=3.12
Requires-Dist: httpx<1.0,>=0.27
Requires-Dist: ikc-sdk-lib==0.10.0
Requires-Dist: pydantic<3.0,>=2.7
Description-Content-Type: text/markdown

# ikc-open-platform-sdk

ikc-open-platform 第三方开发者 SDK（导入包 `ikc_open_platform_sdk`）。

- 认证：应用 API Key（`Authorization: Bearer <api-key>`，由管理面 `/admin/apps/{appId}/keys` 创建）。
- 协议：统一响应壳 `errCode/errMsg/data/traceId/reqId` 解包；业务模型一律复用 `ikc-sdk-lib`（本 SDK 不自定义业务模型）。
- 追踪：每请求注入 23 位纯数字 `X-Request-Id`；`reqId` 可显式传入，缺省 SDK 生成 `req_` 前缀值并随壳回显。
- 能力面（四类业务域 + 知识库子资源域 Wiki / 图谱）：
  - `client.knowledge_bases`：create / update / query / get
  - `client.documents`：ingest / ingest_and_parse / upload / get
  - `client.parse`：parse / parse_direct / query_result / issue_download_ticket / download
  - `client.search`：universal_search（V1）/ universal_search_v2（V2）/ deep_search / query（兼容别名）
  - `client.wiki`：tree / page / stat / export / build / job（W-01/W-02/W-04/W-05 只读 + W-06/W-07 构建）
  - `client.graph`：stat / nodes / edges / export / build / job（G-01/G-02/G-03/G-05 只读 + G-06/G-07 构建）
- 错误模型：`OpenPlatformAPIError`（errCode/errMsg/traceId/reqId）、`OpenPlatformConnectionError`、`OpenPlatformTimeoutError`、`OpenPlatformProtocolError`。
- 响应校验：W/G 域按 `ikc_sdk.core.api.{wiki,graph}.*` 模型校验；**实例侧字段漂移时记 warning 并回落原始 `dict`**，
  不因契约模型过严打断调用。模型本身的缺陷回 `ikc-sdk-lib` 修（先例：W-02 `page.unitId` 实测为 `null`，
  `ikc-sdk-lib` 0.9.2 已把 `WikiPageView.unitId` 放宽为 `str | None`——**不在此处改契约**）。

## 安装

```bash
pip install ikc-open-platform-sdk          # PyPI（0.3.1，依赖 ikc-sdk-lib 0.10.0）
pip install sdk/python/                    # 或本地源码
```

## 快速开始

```python
from ikc_open_platform_sdk import OpenPlatformClient
from ikc_sdk.core.api.search.universal import SearchQueryRequest

with OpenPlatformClient("http://localhost:18000", api_key="<app-api-key>") as client:
    result = client.search.universal_search(SearchQueryRequest(query="IKC 平台"))
    for hit in result.hits:
        print(hit)
    client.wiki.stat("kb_10001")            # W-04
    client.graph.stat("kb_10001")           # G-01
```

## 检索 V2（`universal_search_v2`，D-03）

V2 把**文档 / Wiki / 图谱 / 事实**收进同一个召回面；V1（`universal_search`）行为不变、两版并存：

```python
from ikc_sdk.core.api.search.universal_v2 import UniversalSearchV2Request

resp = client.search.universal_search_v2(UniversalSearchV2Request(
    query="设备支持的最大并发数是多少？",
    kbIds=["kb_10001"],                    # 必填，1~20 个且不重复
    topK=10,
    include={"wikiPage": True, "graphEntities": True, "graphRelations": True},
))
resp.strategy        # 如 lexical@search-v2.1
resp.partial         # true = 某一路降级，原因见 warnings[]（如 vector_unavailable）
resp.nextCursor      # 不透明签名游标：原样回填 cursor，不得解析、不得跨查询复用
for hit in resp.hits:
    print(hit.retrievalId, hit.representationKind, hit.score, hit.snippet)
```

模型是 `extra="forbid"`：`tenantId` / `principalIds` / `authorizedOrgPaths` / `queryVector` / `index` / `routing`
等安全字段由平台按调用方身份注入，调用方不发送也不构造。`filters.currentVersionOnly` 缺省 `true`（只召回当前版本）。

## Wiki / 图谱（知识库子资源）

Wiki（W 域）与图谱（G 域）是知识库子资源，路径挂在 `/api/v1/knowledge-bases/{kbId}` 下，共用知识库读写门禁：

```python
from ikc_sdk.core.api.wiki.build import WikiBuildRequest

client.wiki.tree("kb_1", page=1, page_size=20)                 # W-01 页面树（根节点分页）
client.wiki.page("kb_1", stable_key="部署手册")                 # W-02 页面详情（stableKey / pageId 二选一）
client.wiki.export("kb_1", format="jsonl")                     # W-05 导出（只导出可见页）
client.wiki.build("kb_1", WikiBuildRequest(                    # W-06 同步：返回落地计数
    docId="doc_1", markdown="# 标题\n正文",
))
job = client.wiki.build("kb_1", {"docId": "doc_1", "markdown": "# 标题", "async": True})  # W-06 异步
job = client.wiki.job("kb_1", job.jobId)                       # W-07 轮询至 status 终态

client.graph.nodes("kb_1", page=1, page_size=20, entity_type="ORG")   # G-02 实体
client.graph.edges("kb_1", relation_type="WORKS_AT")           # G-03 关系
client.graph.export("kb_1", format="json")                     # G-05 导出（北向只有 json / jsonl）
```

## 命令行（CLI）

安装后可用 `ikc-op`（或 `python -m ikc_open_platform_sdk.cli`）：

```bash
export OPEN_PLATFORM_BASE_URL=http://localhost:18000
export OPEN_PLATFORM_API_KEY=<app-api-key>

ikc-op kb-query --page 1 --page-size 20
ikc-op kb-get kb_10001
ikc-op search-query --query "IKC 平台" --kb-id kb_10001 --top-k 5
ikc-op search-v2 --query "IKC 平台" --kb-ids '["kb_10001"]' --top-k 10   # Search V2（D-03）
ikc-op wiki-stat --kb-id kb_10001
ikc-op wiki-build --kb-id kb_10001 --doc-id doc_1 --markdown-file ./doc.md --async
ikc-op graph-nodes --kb-id kb_10001 --type ORG --page-size 50
ikc-op sys-catalog            # 免认证系统路由
```

退出码：0 成功；1 业务错误；2 未认证（100401）；3 无权限（100403）；5 占位未实现（501001）；6 传输层错误。

环境变量与全局选项：`OPEN_PLATFORM_BASE_URL` / `--base-url`、`OPEN_PLATFORM_API_KEY` / `--api-key`、
`--timeout`、`OPEN_PLATFORM_EXTRA_HEADERS` / `--extra-headers`（JSON 对象）。平台 `AUTH_MODE=static` /
`gateway_header` 时业务接口要求身份头，靠 `--extra-headers` 发
`{"X-User-Id":"u_1","X-Tenant-Id":"t_1"}`（经平台自带 HAProxy 时同发 `X-Auth-User-Id` / `X-Auth-Tenant-Id`），
实例路由头 `X-Ikc-Instance-Id` 也走这里；缺身份头会回 `100401`。

## MCP Server（stdio）

安装后可用 `ikc-op-mcp`（或 `python -m ikc_open_platform_sdk.mcp --transport stdio`），向 MCP 客户端暴露 28 个工具（kb_* / doc_* / parse_* / search_* / wiki_* / graph_* / sys_*），仅需 Python 标准库（NDJSON JSON-RPC 2.0）。

## 测试

```bash
python -m pytest sdk/python/tests -q
```
