# SeaCloud TypeScript SDK 操作手册

本文列出 `@seacloudai/sdk` 当前公开能力、调用方式、参数约定和返回结构。SDK 不从环境变量读取凭据，不做交互界面、配置文件发现或凭据存储。

## 1. 安装与初始化

要求 Node.js 18.17 或更高版本。包同时支持 ESM 和 CommonJS。

```bash
pnpm add @seacloudai/sdk
```

```ts
import { SeaCloud, getSeaCloudDocs } from "@seacloudai/sdk";

const client = new SeaCloud({
  apiKey: "sk-...",
  timeout: 600_000,
  fetch: globalThis.fetch,
});
```

CommonJS 项目可以直接使用 `require`：

```js
const { SeaCloud, getSeaCloudDocs } = require("@seacloudai/sdk");

const client = new SeaCloud({
  apiKey: process.env.SEACLOUD_API_KEY,
});

console.log(getSeaCloudDocs({ locale: "zh-CN" }).methods);
```

参数：

| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `apiKey` | 是 | SeaCloud API Key，必须显式传入非空字符串 |
| `timeout` | 否 | 默认请求超时时间，单位毫秒 |
| `fetch` | 否 | 自定义 fetch，适合浏览器代理、测试或特殊运行时 |

SDK 内置生产服务端点；应用代码只需要显式传 `apiKey`。维护者从 `.env.prod` / `.env.local` 生成 `src/core/default-base-urls.ts`，用于生产或本地私有端点默认值。

服务端点：

| 能力 | 端点 |
| --- | --- |
| 对话 | `POST https://cloud.seaart.ai/llm/chat/completions` |
| 模型列表 | `GET https://sea-cloud-admin-web.real-cloud.seaart.ai/api/v1/skill/models` |
| queue 提交 | `POST https://cloud.seaart.ai/model/v1/queue/{modelId}` |
| queue 状态 | `GET https://cloud.seaart.ai/model/v1/queue/{modelId}/requests/{request_id}/status` |
| queue 结果 | `GET https://cloud.seaart.ai/model/v1/queue/{modelId}/requests/{request_id}/response` |
| 模型契约 | `GET https://sea-cloud-admin-web.real-cloud.seaart.ai/api/v1/skill/model-contracts/{modelId}` |
| SkillHub 搜索 | `GET https://skill-hub.vtrix.ai/api/v1/search` |
| SkillHub 列表 | `GET https://skill-hub.vtrix.ai/api/v1/skills` |

## 2. `getSeaCloudDocs()`

离线读取 SDK 操作手册、Agent / Skill 用法和公开方法列表。不需要 `new SeaCloud`，不需要 `apiKey`，不发起网络请求。

```ts
const docs = getSeaCloudDocs();

console.log(docs.operationManual.content);
console.log(docs.agentSkillUsage.content);
console.table(docs.methods);
```

返回：

```ts
type SeaCloudDocs = {
  version: string;
  operationManual: {
    title: string;
    format: "markdown";
    content: string;
  };
  agentSkillUsage: {
    title: string;
    format: "markdown";
    content: string;
  };
  methods: Array<{
    name: string;
    category: string;
    summary: string;
  }>;
};
```

## 3. `client.version()`

读取当前 SDK 包版本，不发起网络请求。

```ts
const version = client.version();
console.log(version);
```

返回：

```ts
string
```

## 4. `client.chat.send()`

发送 LLM 对话请求。

非流式：

```ts
const text = await client.chat.send(
  "gpt-5.5",
  [{ role: "user", content: "用一句话介绍 SeaCloud SDK" }],
  {
    stream: false,
    temperature: 0.2,
    maxTokens: 128,
    timeout: 60_000,
  },
);
```

流式：

```ts
const stream = await client.chat.send(
  "gpt-5.5",
  [{ role: "user", content: "分三点介绍 TypeScript SDK 的优势" }],
  {
    stream: true,
    temperature: 0.2,
    maxTokens: 512,
  },
);

for await (const chunk of stream) {
  process.stdout.write(chunk.text);
  if (chunk.done) break;
}
```

消息格式：

```ts
type ChatMessage = {
  role: "system" | "user" | "assistant";
  content: string;
};
```

## 5. `client.run()`

创建异步生成任务。默认读取模型契约，用契约规划 protocol、body mode、queue submit endpoint 和 headers。如果 `input_schema.required` 是非空数组，SDK 只检查这些顶层必传字段是否存在；模型类型、format、范围、默认值和互斥规则由接口校验。`auto` 模式下契约不可用时才回退到 `/model/v1/queue/{modelId}` raw JSON 直提。`run()` 返回任务句柄，不等待最终结果。

生成调用链如下：

| 步骤 | SDK 行为 |
| --- | --- |
| 1 | `client.models.getSpec(modelId)`、`client.run()`、`client.runSync()` 在启用契约模式时读取 `GET https://sea-cloud-admin-web.real-cloud.seaart.ai/api/v1/skill/model-contracts/{modelId}`。 |
| 2 | 如果 `input_schema.required` 非空，SDK 检查这些顶层字段是否存在；否则不拦截。 |
| 3 | `protocol=queue` 且 `body_mode=raw_json` 时，SDK 解析 `spec.endpoints.submit.path`，拼到 queue base URL 后提交调用方传入的 JSON body。 |
| 4 | `client.runSync()` 轮询 `statusUrl` 并读取 `responseUrl`；`client.run()` 只返回任务句柄，调用方可继续用 `tasks.get()` / `tasks.getResponse()`。 |

```mermaid
flowchart TD
  User["用户调用 run/runSync"] --> ValidateInput["校验 modelId 和 params 对象"]
  ValidateInput --> ContractMode{"contract auto/strict?"}
  ContractMode -- 默认 auto/strict --> ReadContract["读取模型契约"]
  ContractMode -- off --> PlanRaw["使用原始 queue 请求"]
  ReadContract --> PlanRequest["规划协议、bodyMode、queue endpoint 和 headers"]
  ReadContract -- auto 不可用 --> PlanRaw
  PlanRaw --> DryRun{"dryRun?"}
  PlanRequest --> DryRun{"dryRun?"}
  DryRun -- 是 --> Preview["返回规划后的请求预览"]
  DryRun -- 否 --> Submit["POST 提交 queue 请求"]
  Submit --> Task["返回任务句柄"]
  Task --> Sync{"runSync?"}
  Sync -- 否 --> Done["调用方用 tasks.get/getResponse 手动查询"]
  Sync -- 是 --> Poll["轮询 statusUrl"]
  Poll --> Response["GET responseUrl"]
  Response --> Result["返回标准化 RunSyncResult"]
```

```ts
const task = await client.run("gpt_image_2", {
  prompt: "Generate cute cats programming",
  n: 1,
  size: "1024x1024",
  output_format: "png",
  quality: "auto",
  moderation: "auto",
});
```

固定语法：

```ts
client.run(modelId, params, options?)
```

参数：

| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `modelId` | 是 | 模型 ID，例如 `gpt_image_2`；SDK 默认读取模型契约并解析 queue submit endpoint |
| `params` | 是 | 模型参数对象；SDK 提交 JavaScript 对象对应的 queue JSON，不接受字符串形式的命令参数 |
| `options.timeout` | 否 | 本次请求超时 |
| `options.dryRun` | 否 | 为 `true` 时返回 queue 请求预览，不提交生成任务 |
| `options.contract` | 否 | 默认是 `"auto"`：读取契约并可回退；`"strict"` 要求契约可读取且能规划请求；`"off"` 明确关闭契约读取并提交原始 `params` |

返回：

```ts
type RunTask = {
  id: string;
  status: string;
  model: string;
  statusUrl?: string;
  responseUrl?: string;
  cancelUrl?: string;
  queuePosition?: number;
};
```

## 6. `client.runSync()`

创建 queue 任务、轮询状态、读取最终 response，并返回统一包装。

```ts
const result = await client.runSync("gpt_image_2", {
  prompt: "一张程序员正在编程的图片，写实照片风格，柔和自然光",
  n: 1,
  size: "1024x1024",
  output_format: "png",
  quality: "auto",
  moderation: "auto",
});

console.log(result.output?.urls);
```

固定语法：

```ts
client.runSync(modelId, params, options?)
```

返回：

```ts
type RunSyncResult = {
  id: string;
  status: "completed" | "failed";
  output?: {
    urls: string[];
    raw: unknown;
  };
  model: string;
  error?: {
    message: string;
    code?: string | number;
    raw?: unknown;
  };
};
```

说明：

- 成功时 `output.urls` 从真实 response 中递归提取所有 `url` 字段。
- `output.raw` 保留真实 response，避免丢失模型特有字段。
- 任务失败时返回 `status: "failed"` 和 `error`，不伪造 `output`。
- 认证、余额、网络错误和等待超时仍会抛出 SDK 错误。

## 7. `dryRun`

`client.run()` 和 `client.runSync()` 都支持 `dryRun`。

```ts
const preview = await client.run("gpt_image_2", {
  prompt: "一张程序员正在编程的图片",
}, {
  dryRun: true,
});

console.log(preview.endpoint);
console.log(preview.headers.Authorization);
console.log(preview.request);
```

返回：

```ts
type DryRunResult<TRequest = unknown> = {
  dryRun: true;
  method: string;
  endpoint: string;
  headers: Record<string, string>;
  request: TRequest;
};
```

`dryRun` 不发送请求、不轮询、不读取 response。

## 8. `client.tasks.get()`

查询异步 queue 任务状态。status 接口可能只返回 `responseUrl`，不一定包含最终生成结果。

```ts
const status = await client.tasks.get(task.id, {
  endpoint: "gpt_image_2",
  statusUrl: task.statusUrl,
});
```

参数：

| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `taskId` | 是 | 任务 ID |
| `options.endpoint` | 二选一 | queue endpoint，用于拼接 status URL |
| `options.statusUrl` | 二选一 | 创建任务或生命周期接口返回的完整 status URL |
| `options.timeout` | 否 | 单次查询超时 |

返回：

```ts
type TaskStatus = {
  id: string;
  status: string;
  model: string;
  output?: OutputGroup[];
  error?: { message: string; code?: string | number };
  createdAt?: number;
  progress?: number;
  usage?: UsageInfo;
  responseUrl?: string;
  statusUrl?: string;
  cancelUrl?: string;
  queuePosition?: number;
  urls: string[];
};
```

## 9. `client.tasks.getResponse()`

读取异步 queue 任务最终 response。通常先调用 `tasks.get()`，等状态完成后再使用 `responseUrl` 查询结果。

```ts
const finalResult = await client.tasks.getResponse(task.id, {
  endpoint: "gpt_image_2",
  responseUrl: status.responseUrl ?? task.responseUrl,
});

console.log(finalResult.output?.urls);
```

参数：

| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `taskId` | 是 | 任务 ID |
| `options.endpoint` | 二选一 | queue endpoint，用于拼接 response URL |
| `options.responseUrl` | 二选一 | 创建任务或 status 接口返回的完整 response URL |
| `options.timeout` | 否 | 单次查询超时 |

返回结构与 `runSync()` 一致：

```ts
type TaskResponse = RunSyncResult;
```

## 10. `client.models.list()`

查询可用模型列表。

```ts
const models = await client.models.list({
  page: 1,
  pageSize: 20,
  type: "video",
  keywords: "wan",
  provider: "fal",
});
```

可选筛选参数包括 `type`、`keywords` 和 `provider`；分页参数是 `page` / `pageSize`，请求时会映射为接口需要的 `page` / `page_size` 查询参数。

常用返回字段：

```ts
type ModelListResponse = {
  models: ModelSummary[];
  total: number;
  page: number;
  pageSize: number;
  page_size: number;
  totalPages: number;
  total_pages: number;
};
```

SDK 同时保留 snake_case alias，方便调用方直接读取服务端字段：列表分页有 `page_size` / `total_pages`，模型项有 `model_id`、`source_collection`、`original_model_id`、`model_subtype`、`input_modalities`、`output_modalities`、`source_id`、`has_spec`、`spec_protocol`。原有 camelCase 字段仍保留。

## 11. `client.models.getSpec()`

查询模型参数合约和 Agent 提示。`client.models.getSpec(modelId)` 读取 `GET https://sea-cloud-admin-web.real-cloud.seaart.ai/api/v1/skill/model-contracts/{modelId}`。`run()` / `runSync()` 默认也会自动读取同一份模型合约，调用方不需要提前手动调用该接口。SDK 只用契约规划协议、body mode、endpoint 和 headers，并在 `input_schema.required` 非空时检查顶层必传字段是否存在；模型参数的类型、默认值和互斥规则由接口校验。`auto` 模式下契约不可用时回退 raw queue，`strict` 模式下契约不可读或无法规划请求会直接报错。`params` 仍然是 JavaScript 对象，不接受字符串形式的命令参数。

```ts
const spec = await client.models.getSpec("gpt_image_2");

console.log(spec.parameters);
console.log(spec.agentPrompt);
```

## 12. `client.skills.find()`

按关键词搜索 SkillHub 技能。

```ts
const result = await client.skills.find("image", {
  limit: 10,
});
```

返回：

```ts
type SkillSearchResult = {
  results: SkillSummary[];
  nextCursor?: string;
};
```

## 13. `client.skills.list()`

列出 SkillHub 技能。

```ts
const result = await client.skills.list({
  cursor: undefined,
  limit: 20,
});
```

## 14. 错误处理

```ts
try {
  const result = await client.runSync("gpt_image_2", params);
  if (result.status === "failed") {
    console.error(result.error?.message);
  }
} catch (error) {
  console.error(error);
}
```

常见错误类型：

| 错误 | 场景 |
| --- | --- |
| `AuthError` | API Key 缺失、无效或无权限 |
| `BalanceError` | 余额不足 |
| `ValidationError` | SDK 基础参数或契约规划失败，例如缺少 `modelId`、`params` 不是对象、缺少 `input_schema.required` 声明的顶层字段、契约协议不支持 |
| `NetworkError` | HTTP 非 2xx 或网络异常 |
| `TaskTimeoutError` | `runSync()` 等待任务完成超时 |
| `TimeoutError` | 单次 HTTP 请求超时 |

## 15. 异步生成完整流程

```ts
const task = await client.run("gpt_image_2", params);

let status = await client.tasks.get(task.id, {
  endpoint: "gpt_image_2",
  statusUrl: task.statusUrl,
});

while (!["completed", "failed"].includes(status.status)) {
  await new Promise((resolve) => setTimeout(resolve, 5_000));
  status = await client.tasks.get(task.id, {
    endpoint: "gpt_image_2",
    statusUrl: status.statusUrl ?? task.statusUrl,
  });
}

if (status.status === "completed") {
  const response = await client.tasks.getResponse(task.id, {
    endpoint: "gpt_image_2",
    responseUrl: status.responseUrl ?? task.responseUrl,
  });
  console.log(response.output?.urls);
}
```

## 16. 方法总览

| 方法 | 是否发起网络请求 | 用途 |
| --- | --- | --- |
| `new SeaCloud(options)` | 否 | 初始化 SDK client |
| `getSeaCloudDocs()` | 否 | 离线读取 SDK 操作手册和 Agent / Skill 用法 |
| `client.version()` | 否 | 读取 SDK 版本 |
| `client.chat.send()` | 是 | 对话 |
| `client.run()` | 是 | 创建 queue 任务 |
| `client.runSync()` | 是 | 创建 queue 任务并等待最终结果 |
| `client.tasks.get()` | 是 | 查询 queue 状态 |
| `client.tasks.getResponse()` | 是 | 获取 queue 最终 response |
| `client.models.list()` | 是 | 查询模型列表 |
| `client.models.getSpec()` | 是 | 查询模型参数合约 |
| `client.skills.find()` | 是 | 搜索 SkillHub |
| `client.skills.list()` | 是 | 列出 SkillHub |
