返回结果

MOI Catalog Service 的常规 JSON 响应使用 codemsgdatarequest_id 信封。SDK 会检查 HTTP 状态与业务 code,成功时只把解码后的 data 返回给调用者。因此业务代码通常直接处理资源对象,不需要再拆一层信封。

处理普通结果与错误

Python 的 RawClient 多数方法返回字典、列表或 None

from moi.errors import APIError, HTTPError

try:
    catalog = client.get_catalog({"id": 101})
    print(catalog["name"])
except APIError as exc:
    print(exc.code, exc.message, exc.request_id)
except HTTPError as exc:
    print(exc.status_code)

Go 方法返回公开的响应类型和 error

catalog, err := client.GetCatalog(ctx, &sdk.CatalogInfoRequest{
	CatalogID: 101,
})
if err != nil {
	var apiErr *sdk.APIError
	var httpErr *sdk.HTTPError
	switch {
	case errors.As(err, &apiErr):
		log.Printf("code=%s request_id=%s", apiErr.Code, apiErr.RequestID)
	case errors.As(err, &httpErr):
		log.Printf("http_status=%d", httpErr.StatusCode)
	default:
		log.Printf("request failed: %v", err)
	}
	return
}
fmt.Println(catalog.CatalogName)

非 2xx 响应是 HTTPError;2xx 响应中业务 code 不表示成功时是 APIError。Go SDK 不区分 OK 的大小写,Python SDK 当前精确匹配 OK。解码失败、超时和取消属于其它错误。重试前应先分类:认证、权限、参数和确定性的业务错误不应盲目重试。

LLM Proxy 方法是已确认的例外:它们直接解码返回内容,不使用上述信封。调用这组方法时应按其公开类型处理。

按具体接口分页

SDK 没有统一的自动分页迭代器。多数 Catalog 列表在请求体的 common_condition 中使用从 1 开始的 pagepage_size,响应返回资源列表与 total;部分方法使用顶层分页字段或查询参数。

下面示例按角色列表的已公开结构翻页:

page = 1
page_size = 100

while True:
    result = client.list_roles({
        "keyword": "",
        "common_condition": {
            "page": page,
            "page_size": page_size,
            "order": "desc",
            "order_by": "created_at",
            "filters": [],
        },
    })
    roles = result.get("role_list") or result.get("list") or []
    for role in roles:
        print(role["name"])

    total = result.get("total", 0)
    if len(roles) < page_size or (total and page * page_size >= total):
        break
    page += 1

实现分页时应同时设置最大页数,防止异常返回造成无限循环。不要把 common_condition 机械套到所有方法:例如工作流作业列表使用顶层 page / page_size,知识条目使用 page_number / page_size,LLM 消息列表还可以使用 after / limit。以对应公开请求类型和方法文档为准。

下载 FileStream

表数据、文件预览和 GenAI 结果等下载方法返回 FileStream,不会把整个响应自动读入内存。调用者必须关闭流。

Python 可以使用上下文管理器:

with client.download_table_data({"id": 301}) as stream:
    written = stream.write_to_file("/tmp/orders.csv")
    print(f"wrote {written} bytes")

也可以用 iter_content(chunk_size) 分块处理。Go 使用 defer

stream, err := client.DownloadTableData(ctx, &sdk.TableDownloadDataRequest{
	ID: 301,
})
if err != nil {
	return err
}
defer stream.Close()

written, err := stream.WriteToFile("/tmp/orders.csv")

FileStream 暴露响应状态和头信息。文件很大时优先流式写入,不要先 read() / io.ReadAll 全部载入内存。

消费数据分析 SSE

analyze_data_stream / AnalyzeDataStream 返回数据分析的 Server-Sent Events。事件可能包含初始化、问题分类、处理步骤和完成信息;以事件对象的 typestep_typestep_namedata 为准。

Python:

from moi import with_stream_read_timeout

stream = client.analyze_data_stream(
    {
        "question": "按地区汇总销售额",
        "config": {
            "data_category": "admin",
            "data_source": {"type": "all"},
        },
    },
    with_stream_read_timeout(60.0),
)
try:
    while True:
        event = stream.read_event()
        if event is None:
            break
        print(event.type, event.step_type, event.data)
finally:
    stream.close()

流读取超时表示两条消息之间允许等待的时间,默认 30 秒;它不同于普通 HTTP 请求的总超时。Go 对应使用 WithStreamReadTimeout 和流的 ReadEvent,并通过 context 取消整个调用。

EOF 只说明连接已结束。应用应根据收到的完成事件决定业务是否成功;若超时、取消或解析错误发生在完成事件之前,把结果标记为中断,而不是成功。