# @lookio/sdk 中文文档

规范地址：https://npmcn.com/package/%40lookio/sdk/

版本：1\.0\.0
npm 元数据更新时间：2026-11-01T18:01:15.172Z
中文译文更新时间：2026-11-01T19:04:32.548Z
软件包许可：MIT

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40lookio/sdk
原文对照：https://npmcn.com/package/%40lookio/sdk/?lang=original
项目主页：https://lookio.app/
源码仓库：https://github.com/DuvInc/lookio

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：1.3 MB，9 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=18
- 运行依赖：0 个
- 首次发布：2026-11-01T18:00:18.800Z
- 最新版本发布：2026-11-01T18:01:14.942Z
- 已发布版本数：2（近 12 个月 2 个）

## 中文简介

官方的Lookio TypeScript SDK：来自Node、Workers、Deno或服务器端应用的资源、助手和问题。

## 安装

```sh
npm install @lookio/sdk
```

也可以使用：`pnpm add @lookio/sdk`、`yarn add @lookio/sdk`、`bun add @lookio/sdk`

## README

# lookio

的官方TypeScript SDK [Lookio](https://lookio.app/)． 问助理 一个问题，继续一段对话，或者把Lookio变成一个产品 服务器、Worker或脚本。

-   零运行时依赖项。只有 `fetch`， `Headers`， `AbortController`， `ReadableStream`， `TextDecoder` 和 `crypto.randomUUID`.
-   从API的OpenAPI文档键入。字段名是API自己的 snake\_case，所以你在REST文档中读到的就是你得到的。
-   安全重试、自动等幂键、输入错误、自动分页、 流的答案。

## 安装

```bash
npm install @lookio/sdk
```

运行在Node 18+、Cloudflare Workers、Deno、Bun和现代浏览器上(参见 [安全注意](https://npmcn.com/package/%40lookio/sdk/#readme-security) 在浏览器中使用之前)。ESM和CommonJS。

## 快速启动

创建一个API密钥 **开发人员工具**． 对于一个只会问问题的产品， 选择 **问问题** 预设和限制关键的助手，它需要。 将密钥保存在服务器上，然后：

```ts
import { Lookio } from "@lookio/sdk";

const lookio = new Lookio({ apiKey: process.env.LOOKIO_API_KEY });
// or just `new Lookio()`: the key is read from LOOKIO_API_KEY when `process` exists
```

### 问助理

```ts
const answer = await lookio.assistants.ask("assistant_id", {
  question: "How much does the pro plan cost?",
});

console.log(answer.answer); // Markdown, citations appear as [1], [2]
for (const citation of answer.citations) {
  console.log(citation.n, citation.title, citation.source);
}
console.log(answer.credit_cost, answer.credits_remaining);
```

### 流式传输答案

```ts
const stream = lookio.assistants.askStream("assistant_id", {
  question: "Summarise our refund policy.",
});

for await (const text of stream.textStream()) {
  process.stdout.write(text);
}

const final = await stream.finalAnswer(); // the same fields as `ask`
console.log(final.citations.length, "citations");
```

流也是类型化事件的异步可迭代对象 （`{ event: "status" | "delta" | "done" | "error", data }`)，事实也的确如此 `abort()`． 它可以读一次。 `finalAnswer()` 可以在之前，期间或 在读取后，单独调用它读取流到末尾。一个 `error` 事件 拒绝 `finalAnswer()` 然后从 `textStream()` 作为一个 `LookioError` (一个 `InsufficientCreditsError` 当工作空间的积分用完时)。一个流 结束时没有。 `done` 或者 `error` 事件抛出 `StreamInterruptedError`.

请求在您第一次读取流时开始。流永远不会重试， 即使在网络错误之后也不会：服务器没有记录流，所以重复 将被收费两次。

### 上传一个文件并等待，直到它准备好

```ts
import { readFile } from "node:fs/promises";

// Node 20+: stream from disk without loading the file in memory:
//   const file = await fs.openAsBlob("./handbook.pdf");
const file = new Blob([await readFile("./handbook.pdf")]);

const resource = await lookio.resources.uploadFile(file, "handbook.pdf", {
  title: "Employee handbook",
  tags: ["hr", "2026"],
  source: "Employee handbook, 2026 edition",
});
// resource.status is "queued": it is read and indexed in the background

const ready = await lookio.resources.waitUntilReady(resource.id);
console.log(ready.status); // "ready"
```

`uploadFile` 接受一个 `Blob`， `File`， `ArrayBuffer`， `Uint8Array` （所以是节点） `Buffer`)或网络 `ReadableStream`． API将文件作为原始字节，而不是 多部份，并要求 `Content-Length`： SDK会为所有事情计算它，但是 一条小溪，它需要 `{ contentLength: <bytes> }` (一个Node流经过 `Readable.toWeb`）. 流上传不能重新发送，因此不会重试。

`waitUntilReady(id, { timeoutMs = 120000, intervalMs = 1500, signal })` 民意调查和 温柔地后退。它把 `ResourceFailedError` (与 `statusDetail`)如果…… 导入失败 `WaitTimeoutError` 如果时间到了。 `ready_simulated` 计数 准备好了。留下的资源 `stale` 一直轮询直到超时：调用 `resources.reindex(id)` 为它。对于短文本、页面或小文件，请忽略 `{ wait: true }` 直接向 `createText`， `createUrl`， `createSite`， `uploadFile` 或者 `get`服务器最多等待15秒，以便大多数小导入返回 `ready` 在单个请求中。

### 迭代每个资源

```ts
for await (const resource of lookio.resources.iterate({ tag: "hr", status: "ready" })) {
  console.log(resource.id, resource.title);
}

// One page at a time, with the cursor you control:
const page = await lookio.resources.list({ limit: 50 });
console.log(page.data.length, page.hasMore, page.nextCursor);

// Or page by page, automatically:
for await (const p of lookio.resources.iterate().pages()) {
  console.log(p.data.length);
}
```

列表返回 `{ data, hasMore, nextCursor, limit?, requestId }`． 相同的 `iterate` 存在于 `conversations`， `publicAssistants`， `logs.*`, `sites.iteratePages(resourceId)` 浏览一个站点的页面。

### 设置站点的同步时间表

`resources.createSite` 和 `sites.updateRules` 接受 `manual`， `daily`， `weekly`， `monthly` 或者一个区间，比如 `every_3_days`， `every_2_weeks` 或者 `every_1_month`． 限制是1-90天，1-52周和1-12个月。 重复的日程安排需要付费计划。如果 `schedule` 在创建时省略 一个站点，免费工作空间使用 `manual` 付费计划使用 `weekly`． 如果是的话 省略了 `sites.updateRules`，现有的时间表保持不变。

## 方法

所有东西都返回API `data` （列表返回一个页面）。每个方法都需要 `{ idempotencyKey?, timeoutMs?, signal? }` 作为它的最后一个参数。

| 名称空间 | 方法 |
| --- | --- |
| `account`， `credits`， `usage` | `account.get()`， `credits.get()`， `usage.get({ from?, to?, granularity? })` |
| `resources` | `list`， `iterate`， `get`， `createText`， `createUrl`， `createSite`， `uploadFile`， `update`， `delete`， `content`， `reindex`， `waitUntilReady`， `pages`， `iteratePages` |
| `sites` | `preview`， `pages`， `iteratePages`， `updateRules`， `retryFailed` |
| `tags` | `list`， `rename({ from, to })`， `delete(tag)` |
| `assistants` | `list`， `get`， `create`， `update`， `delete`， `ask`， `askStream`， `search`， `getPublication`， `publish`， `unpublish` |
| `publicAssistants` | `list`， `iterate`， `get` |
| `accesses` | `list`， `get`， `create`， `update`， `delete`， `createKey`， `updateKey`， `deleteKey` |
| `conversations` | `list`， `iterate`， `get`， `rename`， `delete`， `feedback` |
| `logs` | `conversations.{list, iterate, get, stats}`， `answers.{list, iterate, stats}`， `indexing.{list, iterate, stats}`， `activity.{list, iterate}` |
| `widgets` | `list`， `get`， `create`， `update`， `delete`， `duplicate`， `setImage`， `clearImage`， `conversations.{list, iterate, get}` |
| `members` | `list`， `updatePermissions(userId, { permissions, assistant_ids })` |
| `feedback` | `send({ observed_problem, situation, expected_solution, ... })` |

`client.request({ method, path, query?, body?, headers?, idempotencyKey? })` 以相同的授权到达任何端点，包括在此版本之后添加的端点， 重试和幂等。路径包括 `/v1` 它和整体解决了 JSON主体(`data`， `has_more`， `request_id`……)。 `client.lastRequestId` 拥有 最后一个成功响应的Id。

构造函数选项: `apiKey`， `baseUrl` (默认 `https://api-v2.lookio.app`)， `timeoutMs` (60000), `maxRetries` （2)， `retryDelayMs` （500）底 倒扣) `fetch` （你自己的实现）， `defaultHeaders`， `dangerouslyAllowBrowser`． 请求携带 `Authorization: Bearer <key>` 和 `X-Lookio-SDK: lookio-js/<version>` (浏览器不允许页面设置 `User-Agent`）。

## 幂等和重试

每一个 `POST` （除了流）携带一个 `Idempotency-Key` header：选择的UUID 每次调用一次，每次重试都不变地重复，因此API运行请求 不管它到达多少次，一次重试的问题不会被收取两次费用。 通过 `{ idempotencyKey }` 要自己选择钥匙，比如要 让你自己的重试循环也是安全的。API会在24小时内记住一个密钥。

SDK重试到 `maxRetries` 随着时间的推移，伴随着指数级的退缩和不安， 兑现 `Retry-After` （秒或HTTP日期）：

| 失败 | 获取，放置，删除 | 帖子 | 补丁 |
| --- | --- | --- | --- |
| 429 | 重试 | 重试 | 重试（在门口被拒绝） |
| 408,5xx，网络错误，超时 | 重试 | 重试（相同的键） | **不是重试** （变更可能已经生效） |
| 其他4xx吗？ | 从来没有 | 从来没有 | 从来没有 |

流式传输和流式上传永远不会重试。每次尝试都有自己的超时 （`timeoutMs`，每个客户或每个电话，默认60秒；它覆盖了一条小溪 等待第一个字节)。通过一项 `signal` 取消呼叫及其重试。

## 错误处理

每一次失败都是一次 `LookioError`所以一 `instanceof` 包括API的错误，a 断开连接和滥用SDK。

```ts
import { InsufficientCreditsError, LookioError, RateLimitError } from "@lookio/sdk";

try {
  await lookio.assistants.ask("assistant_id", { question: "..." });
} catch (e) {
  if (e instanceof InsufficientCreditsError) {
    console.log("Out of credits, top up here:", e.actionUrl);
  } else if (e instanceof RateLimitError) {
    console.log("Slow down, retry in", e.retryAfterSeconds, "seconds");
  } else if (e instanceof LookioError) {
    console.log(e.status, e.code, e.message, "request:", e.requestId);
  } else {
    throw e;
  }
}
```

分支 `e.code` (例如 `insufficient_credits`， `rate_limited`， `resource_not_found`)，永远不要留言。字段: `status` (0时无回应 到达), `code`， `type`， `param`， `requestId` （引用它来支持）， `actionUrl`， `docsUrl`， `retryAfterSeconds`.

| 类 | 当 |
| --- | --- |
| `AuthenticationError` | 401：没有钥匙，钥匙是错的，过期的或作废的 |
| `PermissionError` | 403：范围缺失； `feature_not_available`，仅限管理员 |
| `NotFoundError` | 404和410 |
| `ConflictError` | 409 |
| `RateLimitError` | 429，重试次数用完后 |
| `InsufficientCreditsError` | 代码 `insufficient_credits`,已经 `actionUrl` |
| `ConnectionError` | 网络故障，请求超时(`code: "request_timeout"`), abort (`code: "aborted"`） |
| `ResourceFailedError`， `WaitTimeoutError` | `waitUntilReady` |
| `StreamInterruptedError` | 流在其最终事件之前结束 |
| `LookioUsageError` | SDK被滥用了（没有密钥，流上传没有大小）。没有发送任何东西 |

不是API的错误信封（代理页面，空正文）的正文变成 一个通用的 `LookioError` 使用代码 `http_error` 以及消息中的状态文本。

## 学分

进口和其他一切都是免费的。 **只有 `assistants.ask` 和 `assistants.askStream` 消费信用**，对每一个问题，即使资源没有答案 (没有读取资源的助手被拒绝 `assistant_has_no_resources` 在任何信用被持有之前)。价格取决于 `mode` （`flash` 是 默认情况下, `eco`， `deep`)，不管答案的长度是多少。这个 价格上涨 `account.get()`：

```ts
const account = await lookio.account.get();
console.log(account.credit_balance);
console.log(account.credential.scopes); // a key without the `query` scope cannot ask
for (const mode of account.capabilities.query_modes) {
  console.log(mode.id, mode.credits, mode.available);
}
```

## 小部件

小部件是一个聊天框，它向匿名访问者显示一个助手 的网站。每个小部件都带有“准备粘贴”功能 `snippets` （`bubble`， `embed`， `hosted`， `iframe`),加上 `settings`， `limits` 和 `stats`.

```ts
const widget = await lookio.widgets.create({
  assistant_id: "assistant_id",
  name: "Docs help",
  allowed_domains: ["docs.acme.com"],
  settings: { accent: "#4F46E5", greeting: "Ask me anything about the docs." },
  limits: { monthly_credits: 5000 },
});
console.log(widget.snippets.bubble); // paste it in the page

await lookio.widgets.update(widget.id, { status: "paused" }); // shows nothing, answers nothing

for await (const chat of lookio.widgets.conversations.iterate(widget.id)) {
  console.log(chat.end_user, chat.message_count, chat.credits);
}
const full = await lookio.widgets.conversations.get(widget.id, "conversation_id");
```

`update` 是部分PATCH： `settings` 和 `limits` 逐字段更改，而 `allowed_domains` 作为一个整体被替换。范围: `assistants:read` 阅读, `assistants:write` 为了创建和更新， `assistants:delete` 删除。 访问者的回答将被计入工作区（插件的价格） `mode`， 受其限制 `limits`)，所以 `limits.monthly_credits` 在你发布 片段。

## 对话和最终用户

每个答案都会保存在一次对话中（90天）。 `assistants.ask` 返回它 `conversation_id` 答案是 `message_id`;后续操作只发送新的 问题和 `conversation_id`， Lookio阅读历史本身。API密钥 继续它开始的对话：

```ts
const first = await lookio.assistants.ask("assistant_id", {
  question: "How much does the pro plan cost?",
  end_user: "user_42", // your own id for the person asking (optional)
});
const followUp = await lookio.assistants.ask("assistant_id", {
  question: "And for the annual plan?",
  conversationId: first.conversation_id!,
  end_user: "user_42", // a conversation can only be continued for the same end user
});
```

`end_user` 对用户的对话（以及工作空间的日志显示）进行分组 它); `store: false` 什么都不保留（两个id都返回） `null`，你保留 与自己对话 `history`）. 列出、读取、重命名和删除键 与 `lookio.conversations.*` （`list({ end_user })`， `get(id, { end_user })`， `delete(id)`： API密钥的会话也离开 日志)，并转发用户的大拇指：

```ts
await lookio.conversations.feedback(first.conversation_id!, first.message_id!, {
  rating: "down", // "up", "down" or null to take it back
  comment: "The price is out of date.",
});
```

`end_user` 是标识符，而不是身份证明。从你的产品设置它 服务器上经过身份验证的会话。检查对话是否属于这个 人，然后再继续。不要信任由浏览器代码发送的id 检查它。

一个人自己的聊天记录是私人的：一个人的令牌列出了这些记录，并接受no 大拇指还在(`feature_not_available`,一个 `PermissionError`）。

## 日志

工作空间有四个日志，都是空闲的。每个列表都有 `list` (一页), `iterate` （每一页）和，除了 `activity`， `stats` 上面的数字 相同的过滤器)。的游标 `answers`， `indexing` 和 `activity` 不透明的: 迭代器通过 `next_cursor` 回来找你。

| 日志 | 读取 | 需要 | 保持 |
| --- | --- | --- | --- |
| `logs.conversations` | 工作区外的人问的是：小部件访问者、通过API密钥或MCP访问产品的用户。成员自己的聊天记录不在其中。 | `assistants:write`，而一个人的代币则是编辑助手的权利 | 最后一条信息发出后的90天 |
| `logs.answers` | 任何渠道的每个答案(`chat`， `lookio_ai`， `api`， `mcp`， `widget`)，仅提供元数据，不提供问答文本 | `usage:read` | 使用历史 |
| `logs.indexing` | 读取和索引的每个文件、页面或文本，以及其状态和失败原因 | `resources:read` | 有了资源 |
| `logs.activity` | 谁改变了什么，从哪扇门`app`， `api`， `cli`， `mcp`， `agent`， `system`） | `admin`，通过个人连接（绝不是API密钥） | 一年 |

```ts
// Conversations
for await (const chat of lookio.logs.conversations.iterate({ rating: "down", channel: "widget,api" })) {
  console.log(chat.channel, chat.end_user, chat.title, chat.thumbs_down);
}
const full = await lookio.logs.conversations.get("conversation_id"); // messages, citations, cost, thumbs, comments
const stats = await lookio.logs.conversations.stats({ from: "2026-10-01" }); // conversations, questions, people, credits, thumbs, by_channel

// Answers: the questions the resources could not answer, and the conversation of a kept one
for await (const answer of lookio.logs.answers.iterate({ channel: "widget,api" })) {
  console.log(answer.channel, answer.mode, answer.credits, answer.conversation_id);
}
const answerStats = await lookio.logs.answers.stats({ from: "2026-10-01" }); // answers, credits, tokens, by_channel

// Indexing: what failed, and why
const failed = await lookio.logs.indexing.list({ status: "failed", limit: 20 });
for (const line of failed.data) console.log(line.resource.title, line.name, line.error);
const indexingStats = await lookio.logs.indexing.stats({ resource_id: "resource_id" }); // total, indexed, failed, in_progress, words_indexed

// Activity (admins)
for await (const change of lookio.logs.activity.iterate({ origin: "cli,mcp", action: "assistant" })) {
  console.log(change.created_at, change.actor.name, change.action, change.summary);
}
```

过滤器（全部可选）：

-   `conversations`： `channel` (逗号分隔 `widget`， `api`， `mcp`)， `widget_id`， `api_key_id`， `assistant_id`， `rating` （`up`， `down`， `rated`， `unrated`)， `end_user`， `q`， `from`， `to`.
-   `answers`： `channel` (逗号分隔 `chat`， `lookio_ai`， `api`， `mcp`， `widget`)， `assistant_id`， `widget_id`， `api_key_id`， `member_id` (管理员) `mode`， `from`， `to`.
-   `indexing`： `resource_id`， `status` (逗号分隔 `queued`， `processing`， `indexed`， `replaced`， `unchanged`， `skipped`， `failed`)， `source_type` （`file`， `url`， `site`， `text`)， `q`， `include_unchanged` （`"true"` 或者 `"false"`（默认情况下，网站同步未更改的页面会被忽略）， `from`， `to`.
-   `activity`： `actor_id`， `origin` (逗号分隔) `target_type` （`resource`， `assistant`， `widget`， `tag`， `member`)， `target_id`， `action` （`assistant.updated`，或者 `assistant` 对于一种类型上的每一个动作， `q`， `from`， `to`.

## 类型

公共类型(`Resource`， `Assistant`， `Answer`， `Citation`， `Conversation`， `ConversationSummary`， `LogConversation`， `LogStats`， `LogAnswer`， `LogAnswerStats`， `LogIndexing`， `LogIndexingStats`， `LogActivity`， `Widget`， `WidgetSettings`， `WidgetLimits`， `WidgetSnippets`， `WidgetConversation`， `CreateWidgetParams`， `Member`， `Usage`， `Account`， `Credits`， `TagSummary`， `SitePages`， `SitePreview`， `StreamEvent`)都是从生成的 OpenAPI类型和导出 `lookio`，就像 `components`， `operations` 和 `paths` 他们自己。它们是由 `../api-worker/openapi.json`：

```bash
npm run generate   # rewrites src/generated/openapi.ts; never edit that file by hand
```

`npm test`， `npm run typecheck` 和 `npm run build` 先运行它，这样就提交了 文件不能过期。

## 开发

```bash
npm install
npm test            # vitest, with a mocked fetch: no test calls a live API
npm run typecheck
npm run build       # dist/index.js (ESM), dist/index.cjs (CJS), dist/index.d.ts
```

`src/coverage.test.ts` 读取OpenAPI文档，当一个操作有 没有SDK方法，当一个方法发送一个规范没有描述的请求时，或者当 这个README提到了API没有的端点、范围或错误代码。

## 安全

**API密钥永远不能发送给浏览器。** 任何打开这一页的人都能读到 然后花掉你的信用。从服务器（Worker， API路由）调用Lookio 将结果发送到页面。在浏览器中，客户端记录一次警告；通过 `dangerouslyAllowBrowser: true` 让它安静下来，如果你知道你在做什么 在内部工具上实例一个限制为只读范围的键)。永远不要犯错误。 关键：读它 `LOOKIO_API_KEY`.

## 许可证

麻省理工学院Duv公司

暂停一个小部件的每月上限 `client.widgets.update(id, { limits: { monthly_credits_enabled: false } })`． 将其设置为 `true` 恢复储蓄金额；当月早些时候的支出仍有影响。

## 常见问题

### @lookio/sdk 是什么？

官方的Lookio TypeScript SDK：来自Node、Workers、Deno或服务器端应用的资源、助手和问题。

### 如何安装 @lookio/sdk？

在项目目录运行 npm install @lookio/sdk，或使用 pnpm add / yarn add / bun add。

### @lookio/sdk 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @lookio/sdk 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### @lookio/sdk 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=18。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @lookio/sdk 可以商用吗？

@lookio/sdk 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @lookio/sdk 还在维护吗？

最新版本 1\.0\.0 发布于 2026/11/02（1 天前）；至今共发布 2 个版本，其中过去 12 个月发布了 2 个；首次发布于 2026/11/02。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

