> ## Documentation Index
> Fetch the complete documentation index at: https://docs.profy.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript SDK

> 袋袋 TypeScript SDK 安装与使用指南

`@profy-ai/sdk` 是袋袋平台的官方 TypeScript SDK，使用原生 `fetch` API，同时支持 Node.js 和浏览器环境。

## 安装

```bash theme={null}
npm install @profy-ai/sdk
```

要求 Node.js 18+（需要原生 `fetch` 支持）。

## 快速开始

```typescript theme={null}
import { Profy } from "@profy-ai/sdk";

const client = new Profy({ apiKey: "sk-pro-your-key" });

// 调用 专家
const result = await client.agents.run("my-expert", "你好");
console.log(result.text);
```

## 客户端配置

```typescript theme={null}
import { Profy } from "@profy-ai/sdk";

const client = new Profy({
  apiKey: "sk-pro-your-key",
  baseUrl: "https://api.profy.cn",  // 默认值
  timeout: 300_000,                  // 默认值（毫秒）
});
```

| 参数        | 类型       | 默认值                    | 说明                           |
| --------- | -------- | ---------------------- | ---------------------------- |
| `apiKey`  | `string` | （必填）                   | API Key 或 OAuth Access Token |
| `baseUrl` | `string` | `https://api.profy.cn` | API 基础 URL                   |
| `timeout` | `number` | `300000`               | 请求超时（毫秒）                     |

## 资源命名空间

| 命名空间                      | 方法                     | 对应端点                        |
| ------------------------- | ---------------------- | --------------------------- |
| `client.agents`           | `run()`, `runStream()` | `POST /v1/agents/run`       |
| `client.chat.completions` | `create()`             | `POST /v1/chat/completions` |
| `client.models`           | `list()`               | `GET /v1/models`            |
| `client.events`           | `report()`             | `POST /v1/events`           |

## Agents

### `agents.run()` — 调用专家

```typescript theme={null}
const result = await client.agents.run(
  "my-expert",
  "帮我分析这段代码的性能问题",
  {
    sessionId: "abc123",    // 可选：延续会话
    model: "deepseek-v3",   // 可选：指定模型
  }
);

console.log(result.text);        // 完整回复
console.log(result.model);       // 使用的模型
console.log(result.sessionId);   // 会话 ID
console.log(result.toolCalls);   // 工具调用列表
console.log(result.usage);       // Token 用量
console.log(result.elapsedMs);   // 耗时（毫秒）
```

`AgentRunResult` 类型定义：

```typescript theme={null}
interface AgentRunResult {
  text: string;
  model?: string;
  sessionId?: string;
  toolCalls: ToolCall[];
  usage: TokenUsage;
  error?: string;
  partial: boolean;
  elapsedMs: number;
}

interface ToolCall {
  name: string;
  status: "started" | "completed";
}

interface TokenUsage {
  promptTokens: number;
  completionTokens: number;
  totalTokens: number;
}
```

### `agents.runStream()` — 流式调用专家

```typescript theme={null}
for await (const chunk of client.agents.runStream("my-expert", "你好")) {
  switch (chunk.type) {
    case "run.in_progress":
      console.log(`模型: ${chunk.raw.model_name}`);
      console.log(`会话: ${chunk.raw.session_id}`);
      break;
    case "output.text.delta":
      process.stdout.write(chunk.delta ?? "");
      break;
    case "tool_call.created":
      console.log(`\n🔧 调用工具: ${chunk.raw.tool_name}`);
      break;
    case "tool_call.completed":
      console.log(`✅ 工具完成`);
      break;
    case "run.completed":
      console.log("\n--- 完成 ---");
      break;
    case "run.failed":
      console.error(`❌ 错误: ${chunk.raw.message}`);
      break;
  }
}
```

`AgentRunChunk` 类型定义：

```typescript theme={null}
interface AgentRunChunk {
  type: string;
  delta?: string;
  raw: Record<string, unknown>;
}
```

## Chat Completions

### `chat.completions.create()` — 对话补全

**非流式**（返回 `ChatResponse`）：

```typescript theme={null}
const resp = await client.chat.completions.create({
  model: "deepseek-v3",
  messages: [
    { role: "system", content: "你是一个有帮助的助手" },
    { role: "user", content: "你好" },
  ],
  temperature: 0.7,
  maxTokens: 1000,
});

console.log(resp.text);
console.log(`Token: ${resp.usage.totalTokens}`);
```

**流式**（返回 `AsyncGenerator<ChatChunk>`）：

```typescript theme={null}
const stream = await client.chat.completions.create({
  model: "deepseek-v3",
  messages: [{ role: "user", content: "讲一个故事" }],
  stream: true,
});

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

SDK 使用 TypeScript 函数重载确保类型安全：

```typescript theme={null}
// stream: false → Promise<ChatResponse>
const response: ChatResponse = await client.chat.completions.create({
  model: "deepseek-v3",
  messages: [...],
  stream: false,  // 或省略
});

// stream: true → Promise<AsyncGenerator<ChatChunk>>
const stream: AsyncGenerator<ChatChunk> = await client.chat.completions.create({
  model: "deepseek-v3",
  messages: [...],
  stream: true,
});
```

## Models

### `models.list()` — 获取模型列表

```typescript theme={null}
const models = await client.models.list();

for (const m of models) {
  console.log(`${m.id}: ${m.name}`);
  const caps = m.capabilities as Record<string, unknown>;
  console.log(`  工具: ${caps?.supportsTools ? "✅" : "❌"}`);
  console.log(`  视觉: ${caps?.supportsVision ? "✅" : "❌"}`);
  console.log(`  最大输入: ${caps?.maxInputTokens ?? "未知"}`);
}
```

## Events

### `events.report()` — 上报自定义事件

```typescript theme={null}
const result = await client.events.report("image_generation", {
  idempotencyKey: "order_123_gen_1",
  metadata: { style: "watercolor" },
});

console.log(`扣费: ${result.charged}`);
console.log(`剩余: ${result.balance_remaining}`);
```

<Warning>
  Events API 仅支持 OAuth Token。使用 API Key 调用会返回 401 错误。
</Warning>

## 错误处理

SDK 在 API 返回非 2xx 状态码时抛出 `ProfyApiError`：

```typescript theme={null}
import { Profy, ProfyApiError } from "@profy-ai/sdk";

const client = new Profy({ apiKey: "sk-pro-your-key" });

try {
  await client.agents.run("non-existent", "你好");
} catch (e) {
  if (e instanceof ProfyApiError) {
    console.log(`HTTP 状态码: ${e.statusCode}`);
    console.log(`错误信息:`, e.body);

    switch (e.statusCode) {
      case 401:
        console.log("API Key 无效");
        break;
      case 403:
        console.log("访问被拒绝");
        break;
      case 404:
        console.log("专家 不存在");
        break;
      case 429:
        console.log("速率限制");
        break;
    }
  }
  throw e;
}
```

### 超时处理

```typescript theme={null}
const client = new Profy({
  apiKey: "sk-pro-your-key",
  timeout: 60_000,  // 60 秒
});

try {
  const result = await client.agents.run("my-expert", "复杂任务");
} catch (e) {
  if (e instanceof DOMException && e.name === "AbortError") {
    console.log("请求超时");
  }
}
```

`agents.run()` 在超时时会尽量返回已收到的部分结果（`result.partial = true`）。

## 浏览器环境

SDK 使用原生 `fetch`，可在浏览器中使用：

```typescript theme={null}
import { Profy } from "@profy-ai/sdk";

const client = new Profy({
  apiKey: oauthAccessToken,
  // 浏览器中建议使用 OAuth Token，不要暴露 API Key
});

const result = await client.agents.run("my-expert", userMessage);
document.getElementById("output")!.textContent = result.text;
```

<Warning>
  **不要在浏览器前端代码中使用 API Key**。浏览器端应使用 OAuth Token。API Key 仅应在 Node.js 服务端使用。
</Warning>

## 完整示例：Express API 代理

```typescript theme={null}
import express from "express";
import { Profy } from "@profy-ai/sdk";

const app = express();
const client = new Profy({ apiKey: process.env.PROFY_API_KEY! });

app.use(express.json());

app.post("/api/chat", async (req, res) => {
  const { message, sessionId, expert } = req.body;

  try {
    res.setHeader("Content-Type", "text/event-stream");
    res.setHeader("Cache-Control", "no-cache");

    for await (const chunk of client.agents.runStream(
      expert ?? "my-expert",
      message,
      { sessionId }
    )) {
      res.write(`data: ${JSON.stringify(chunk)}\n\n`);
    }
    res.write("data: [DONE]\n\n");
    res.end();
  } catch (e) {
    res.status(500).json({ error: String(e) });
  }
});

app.listen(3000, () => console.log("Server running on :3000"));
```

## 完整示例：多轮对话

```typescript theme={null}
import { Profy } from "@profy-ai/sdk";
import * as readline from "readline";

const client = new Profy({ apiKey: process.env.PROFY_API_KEY! });
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });

async function main() {
  let sessionId: string | undefined;
  console.log("开始对话（输入 quit 退出）\n");

  const ask = () => new Promise<string>((resolve) => rl.question("你: ", resolve));

  while (true) {
    const input = await ask();
    if (input.toLowerCase() === "quit") break;

    process.stdout.write("AI: ");
    for await (const chunk of client.agents.runStream("my-expert", input, { sessionId })) {
      if (chunk.type === "run.in_progress") {
        sessionId = chunk.raw.session_id as string;
      } else if (chunk.type === "output.text.delta") {
        process.stdout.write(chunk.delta ?? "");
      }
    }
    console.log();
  }

  rl.close();
}

main();
```

## 下一步

<CardGroup cols={2}>
  <Card title="Python SDK" icon="python" href="/zh/developers/sdk-python">
    Python SDK 指南
  </Card>

  <Card title="API 参考" icon="code" href="/zh/developers/api/agents-run">
    完整 API 文档
  </Card>
</CardGroup>
