# AI SDK

{% hint style="warning" %}
注意：此功能需要 Eagle 4.0 Build20 或更高版本。
{% endhint %}

***

## AI SDK 依赖插件简介 <a href="#introduction" id="introduction"></a>

「AI SDK 依赖插件」是一款面向插件开发者的开发工具包，提供统一的 AI 模型配置中心，支持各大主流 AI 模型，一次配置，处处可用。通过整合 AI SDK，开发者可在自身的插件中轻松实现文字生成、结构化对象生成以及流式处理等 AI 功能。

### 统一配置中心：一次设置，处处可用

AI SDK 插件支持以下 8 个 Provider：

**商业模型**：

* OpenAI（GPT-5.2、GPT-5、o3 等）
* Anthropic Claude（Claude Sonnet 4.6、Claude Opus 4.6 等）
* Google Gemini（Gemini 3 Pro、Gemini 3 Flash 等）
* DeepSeek（DeepSeek V3、DeepSeek R1 等）
* 通义千问（Qwen3 系列）

**OpenAI 兼容协议**（连接任何兼容 OpenAI API 的服务）：

* OpenAI Compatible（支持 Groq、Together AI、Fireworks、vLLM 等）

**本地模型**（完全离线运行）：

* Ollama（支持 Llama 4、Qwen3、Gemma 3 等）
* LM Studio（图形化界面，新手友好）

后续版本将陆续增加更多主流模型服务提供商的支持。

配置一次后，所有 AI 相关插件都能直接使用，无需重复设置。例如：你安装了「AI 翻译」与「AI 重新命名」插件，它们都会自动共用你在 AI SDK 里填好的配置，甚至可以各自选择不同的模型，而不需要再次输入 API Key。

### 开放的开发环境

基于 [ai-sdk.dev](https://ai-sdk.dev/) 标准（AI SDK v6），AI SDK 插件为开发者提供了一组干净、稳定的基础设施。开发者不用再花心力处理 API Key 存储、模型切换、错误处理等基础配置，可以专注于插件的功能创新。唯一的区别在于 Provider 的获取方式——我们使用自行开发的 Provider 来确保更好的稳定性和用户体验。

{% hint style="info" %}
**版本说明**：此插件基于 AI SDK v6 构建，与 [ai-sdk.dev](https://ai-sdk.dev/) 官方文档保持同步。
{% endhint %}

***

## 安装与设置 <a href="#installation" id="installation"></a>

### 安装步骤

1. 进入 Eagle 插件中心
2. 搜索并找到「AI SDK」插件
3. 点击安装
4. 安装完成后，打开 **偏好设置**，在左侧栏找到「**AI 模型**」
5. 在右侧设置区块中配置模型服务提供商，并设置**默认模型**（语言模型、视觉模型）

{% hint style="info" %}
当用户安装具有 AI SDK 依赖的插件时，Eagle 会自动提示用户安装「AI SDK 依赖插件」。因此，开发者无需专门编写代码让用户进行安装，系统会自动确保相关依赖已安装后才允许插件运行。
{% endhint %}

### 在 manifest.json 中声明依赖

在插件的 `manifest.json` 中加入 `dependencies` 字段：

```json
{
    "id": "YOUR_PLUGIN_ID",
    "version": "1.0.0",
    "platform": "all",
    "arch": "all",
    "name": "我的 AI 插件",
    "logo": "/logo.png",
    "keywords": [],
    "dependencies": ["ai-sdk"],
    "devTools": false,
    "main": {
        "url": "index.html",
        "width": 640,
        "height": 480
    }
}
```

关键设置为 `"dependencies": ["ai-sdk"]`，这会让 Eagle 知道此插件需要 AI SDK 才能运作。

***

## 快速开始 <a href="#quickstart" id="quickstart"></a>

获取 AI SDK 模块：

```javascript
const ai = eagle.extraModule.ai;
```

### 推荐做法：使用默认模型

用户通常会在偏好设置的「AI 模型」中，事先选好偏好的语言模型与视觉模型。通过 `getDefaultModel()` 直接继承用户的选择，是最简单也最推荐的做法：

```javascript
eagle.onPluginCreate(async (plugin) => {
    const ai = eagle.extraModule.ai;
    const { generateText } = ai;

    // 获取用户设置的默认语言模型（同步，无需 await）
    // 另有 getDefaultModel("image") 可获取用户的默认视觉模型
    const defaultLLM = ai.getDefaultModel("chat");

    if (!defaultLLM) {
        console.log("请先在 AI SDK 设置中选择默认语言模型");
        ai.open(); // 自动打开偏好设置中的模型设置面板
        return;
    }

    // 直接用 provider::model 字符串获取模型
    const model = ai.getModel(defaultLLM);

    // 生成文字
    const result = await generateText({
        model,
        prompt: "请用一句话介绍数字艺术",
    });

    console.log(result.text);
});
```

{% hint style="success" %}
**🦄 最佳实践：** 优先使用 `getDefaultModel("chat")` 获取用户的偏好模型，而非在代码中写死特定的 Provider 与模型名称。这样做有两大好处：

1. **开发更轻松**——不需要自己实现模型选择器，直接继承用户在 AI SDK 中的偏好设置。
2. **避免配置问题**——如果你在代码中指定 `openai("gpt-5")`，但用户没有配置 OpenAI，就会出错。使用默认模型则能保证用户已经配置并验证过。
   {% endhint %}

### 指定特定 Provider

如果你的插件确实需要使用特定的 Provider（例如只有 OpenAI 支持的功能），也可以直接指定：

```javascript
eagle.onPluginCreate(async (plugin) => {
    const ai = eagle.extraModule.ai;
    const { generateText } = ai;

    // 获取特定 Provider（同步，无需 await）
    const openai = ai.getProvider("openai");

    const result = await generateText({
        model: openai("gpt-5"),
        prompt: "请用一句话介绍数字艺术",
    });

    console.log(result.text);
});
```

***

## 核心概念 <a href="#core-concepts" id="core-concepts"></a>

### Provider 与 Model 的关系

AI SDK 采用双层结构：**Provider**（提供者）负责管理 API 连接与认证，**Model**（模型）则是实际执行 AI 任务的单位。

```
Provider（如 openai）
  └── Model（如 gpt-5）
  └── Model（如 gpt-5.2）
```

### provider::model 格式

{% hint style="danger" %}
**重要：** Provider 与 Model 之间使用**双冒号** `::` 分隔，而非单冒号或斜线。
{% endhint %}

```javascript
// ✅ 正确
"openai::gpt-5"
"anthropic::claude-sonnet-4-6-20250514"
"google::gemini-3-flash"

// ❌ 错误
"openai/gpt-5"
"openai:gpt-5"
```

此格式用于 `getModel()` 和 `getDefaultModel()` 等方法。

### 同步 vs 异步方法

AI SDK 中的方法明确区分为同步与异步两类：

**同步方法**（无需 `await`）：

* `getProviders()`、`getProvider()`、`getAvailableProviders()`、`getModel()`
* `getDefaultModel()`

**异步方法**（需要 `await`）：

* `generateText()`、`generateObject()`、`streamText()`、`streamObject()`
* Provider 实例的 `verify()`、`getModels()`、`hasModel()`

### 获取 Model 的三种方式

```javascript
const ai = eagle.extraModule.ai;

// ⭐ 推荐：使用默认模型，直接继承用户偏好
const defaultLLM = ai.getModel(ai.getDefaultModel("chat"));   // 默认语言模型
const defaultVLM = ai.getModel(ai.getDefaultModel("image"));  // 默认视觉模型

// 方式二：直接用 provider::model 格式获取
const model = ai.getModel("openai::gpt-5");

// 方式三：先获取 Provider，再指定 Model
const openai = ai.getProvider("openai");
const model = openai("gpt-5");
```

{% hint style="success" %}
**🦄 最佳实践：** 除非你的插件有特殊需求（例如仅支持特定 Provider 的功能），否则应优先使用 `getDefaultModel()` 来获取用户的偏好模型。
{% endhint %}

***

## generateText() — 基本文字生成 <a href="#generate-text" id="generate-text"></a>

使用指定模型生成文字回应。

### 基本用法（prompt）

```javascript
const ai = eagle.extraModule.ai;
const { generateText } = ai;

// 使用默认语言模型
const model = ai.getModel(ai.getDefaultModel("chat"));

const result = await generateText({
    model,
    prompt: "请帮我写一个关于数字艺术的创意简介",
});

console.log(result.text);
```

### 使用 messages 数组

通过 `messages` 可以设置系统提示词与多轮对话：

```javascript
const model = ai.getModel(ai.getDefaultModel("chat"));

const result = await generateText({
    model,
    messages: [
        {
            role: "system",
            content: "你是一位专业的艺术评论家。",
        },
        {
            role: "user",
            content: "请分析印象派的色彩运用特点。",
        },
    ],
});

console.log(result.text);
```

### 多模态（文字 + 图片）

```javascript
// 使用默认视觉模型（支持图片理解）
const model = ai.getModel(ai.getDefaultModel("image"));

const result = await generateText({
    model,
    messages: [
        {
            role: "user",
            content: [
                {
                    type: "text",
                    text: "请描述这张图片的内容",
                },
                {
                    type: "image",
                    image: "https://example.com/sample-image.jpg",
                },
            ],
        },
    ],
});

console.log(result.text);
```

{% hint style="info" %}
更多 `generateText` 的进阶用法（如 `maxTokens`、`temperature` 等参数），请参考 [AI SDK 官方文档](https://ai-sdk.dev/docs/ai-sdk-core/generating-text)。
{% endhint %}

***

## generateObject() — 结构化对象生成 <a href="#generate-object" id="generate-object"></a>

让 AI 依照指定的 Schema 返回结构化 JSON 对象。

### 使用 Zod Schema

```javascript
const ai = eagle.extraModule.ai;
const { generateObject } = ai;
const { z } = require("zod");

// 纯文字任务使用默认语言模型
const model = ai.getModel(ai.getDefaultModel("chat"));

const result = await generateObject({
    model,
    schema: z.object({
        tags: z.array(z.object({
            name: z.string(),
            reason: z.string(),
        })),
        description: z.string(),
    }),
    prompt: "请为一张日落海滩的照片生成 5 个标签和描述",
});

console.log(result.object.tags);
console.log(result.object.description);
```

### 使用 JSON Schema

```javascript
const model = ai.getModel(ai.getDefaultModel("chat"));

const result = await generateObject({
    model,
    schema: {
        type: "object",
        properties: {
            tags: {
                type: "array",
                items: {
                    type: "object",
                    properties: {
                        name: { type: "string" },
                        reason: { type: "string" },
                    },
                },
            },
            description: { type: "string" },
        },
    },
    prompt: "请为一张日落海滩的照片生成 5 个标签和描述",
});

console.log(result.object.tags);
console.log(result.object.description);
```

### 图片分析示例

```javascript
// 涉及图片理解，使用默认视觉模型
const model = ai.getModel(ai.getDefaultModel("image"));

const result = await generateObject({
    model,
    schema: {
        type: "object",
        properties: {
            colors: { type: "array", items: { type: "string" } },
            style: { type: "string" },
            mood: { type: "string" },
        },
    },
    messages: [
        {
            role: "system",
            content: "你是一个专业的图像分析专家。",
        },
        {
            role: "user",
            content: [
                { type: "text", text: "请分析这张图片的色彩、风格和情感。" },
                { type: "image", image: "https://example.com/artwork.jpg" },
            ],
        },
    ],
});

console.log(result.object);
```

{% hint style="info" %}
更多 `generateObject` 的进阶用法，请参考 [AI SDK 官方文档](https://ai-sdk.dev/docs/ai-sdk-core/generating-structured-data)。
{% endhint %}

***

## streamText() — 流式文字生成 <a href="#stream-text" id="stream-text"></a>

以流式方式逐步接收 AI 回应，适合需要即时显示结果的场景。

```javascript
const ai = eagle.extraModule.ai;
const { streamText } = ai;
const model = ai.getModel(ai.getDefaultModel("chat"));

const { textStream } = streamText({
    model,
    prompt: "请详细介绍数字艺术的发展历程",
});

// 使用异步迭代器逐步接收文字
for await (const textPart of textStream) {
    console.log(textPart); // 即时显示
}
```

### 即时显示在 UI 中

```javascript
let fullText = "";

const { textStream } = streamText({
    model,
    prompt: "请撰写一篇短文",
});

for await (const textPart of textStream) {
    fullText += textPart;
    // 更新 UI 元素
    document.getElementById("output").textContent = fullText;
}
```

{% hint style="info" %}
更多 `streamText` 的进阶用法，请参考 [AI SDK 官方文档](https://ai-sdk.dev/docs/ai-sdk-core/generating-text#streamtext)。
{% endhint %}

***

## streamObject() — 流式对象生成 <a href="#stream-object" id="stream-object"></a>

以流式方式逐步接收结构化对象，每次迭代会收到目前已解析的部分对象。

```javascript
const ai = eagle.extraModule.ai;
const { streamObject } = ai;
const model = ai.getModel(ai.getDefaultModel("chat"));

const { partialObjectStream } = streamObject({
    model,
    schema: {
        type: "object",
        properties: {
            analysis: {
                type: "object",
                properties: {
                    colors: { type: "array", items: { type: "string" } },
                    style: { type: "string" },
                    mood: { type: "string" },
                    suggestions: { type: "array", items: { type: "string" } },
                },
            },
        },
    },
    prompt: "请分析这件艺术作品的色彩、风格、情感，并提供改进建议。",
});

for await (const partialObject of partialObjectStream) {
    console.log("当前结果:", partialObject);
    // partialObject 会逐步增加字段，例如：
    // { analysis: { colors: ["红"] } }
    // { analysis: { colors: ["红", "蓝"], style: "印象派" } }
    // { analysis: { colors: ["红", "蓝"], style: "印象派", mood: "宁静" } }
}
```

{% hint style="success" %}
**🦄 最佳实践：** `streamObject` 适合需要在 UI 上渐进式显示分析结果的场景，让用户能在 AI 尚未完成时就看到部分结果。
{% endhint %}

{% hint style="info" %}
更多 `streamObject` 的进阶用法，请参考 [AI SDK 官方文档](https://ai-sdk.dev/docs/ai-sdk-core/generating-structured-data#streamobject)。
{% endhint %}

***

## Provider 管理方法 <a href="#provider-management" id="provider-management"></a>

以下方法皆为**同步方法**，无需使用 `await`。

***

### getProviders() <a href="#get-providers" id="get-providers"></a>

获取所有已注册的 Provider 数组。

* 返回 `ProviderFunction[]` — 所有 Provider 的数组

```javascript
const ai = eagle.extraModule.ai;

// ✅ 正确：返回数组
const providers = ai.getProviders();
console.log(providers.length); // 8

providers.forEach(provider => {
    console.log(provider.name); // "openai", "anthropic", "google", ...
});
```

{% hint style="danger" %}
**注意：** `getProviders()` 返回的是**数组**，不是对象。以下写法是错误的：

```javascript
// ❌ 错误：不可以解构为对象
const { openai, anthropic } = ai.getProviders();

// ❌ 错误：不需要 await
const providers = await ai.getProviders();
```

{% endhint %}

***

### getProvider(providerName) <a href="#get-provider" id="get-provider"></a>

获取指定名称的 Provider。

* `providerName` string — Provider 名称（如 `"openai"`、`"google"`）
* 返回 `ProviderFunction | undefined` — 找到的 Provider，若不存在则返回 `undefined`

```javascript
const openai = ai.getProvider("openai");
const google = ai.getProvider("google");

if (openai) {
    const model = openai("gpt-5");
}
```

{% hint style="info" %}
如果需要获取特定 Provider，建议使用 `getProvider()` 而非 `getProviders()`，代码更简洁清晰。但在多数情况下，直接使用 `getDefaultModel()` 是更好的选择。
{% endhint %}

***

### getAvailableProviders() <a href="#get-available-providers" id="get-available-providers"></a>

获取所有已配置（用户已完成设置）的 Provider。

* 返回 `ProviderFunction[]` — 已配置的 Provider 数组

```javascript
const available = ai.getAvailableProviders();

if (available.length === 0) {
    console.log("尚未配置任何 AI 提供者，请先在 AI SDK 设置中配置。");
} else {
    console.log(`已配置 ${available.length} 个提供者：`);
    available.forEach(p => console.log(`- ${p.name}`));
}
```

{% hint style="info" %}
此方法与 `getProviders()` 的差异在于：`getProviders()` 返回所有 8 个 Provider（含未配置的），`getAvailableProviders()` 仅返回用户已完成设置的 Provider。
{% endhint %}

***

### getModel(providerAndModel) <a href="#get-model" id="get-model"></a>

通过 `provider::model` 格式直接获取模型实例。

* `providerAndModel` string — 格式为 `"provider::model"`
* 返回 `Model` — 可直接传入 `generateText()` 等方法的模型对象

```javascript
// 直接获取模型
const model = ai.getModel("openai::gpt-5");

const result = await generateText({
    model: model,
    prompt: "Hello!",
});
```

{% hint style="danger" %}
**注意：** 必须使用 `::` 双冒号分隔 Provider 与 Model 名称，否则会抛出错误。
{% endhint %}

***

## 设置与刷新 <a href="#settings" id="settings"></a>

***

### open() <a href="#open" id="open"></a>

打开偏好设置中的「AI 模型」设置面板。适合在插件界面中提供「模型设置」按钮，让用户快速配置模型服务提供商与默认模型。

* 返回 `void`

```javascript
const ai = eagle.extraModule.ai;

// 例如：在插件界面中放置「模型设置」按钮
document.getElementById("settings-btn").addEventListener("click", () => {
    ai.open(); // 系统会自动弹出偏好设置中的模型设置区块
});
```

{% hint style="success" %}
**🦄 最佳实践：** 当 `getDefaultModel()` 返回 `undefined`（用户尚未设置默认模型）时，可以在插件界面中显示提示并提供按钮调用 `open()`，引导用户完成设置。
{% endhint %}

***

### reload() <a href="#reload" id="reload"></a>

重新加载 AI SDK 配置。当用户通过 `open()` 打开设置面板并调整配置后，调用此方法即可读取到最新的配置。

* 返回 `void`

```javascript
const ai = eagle.extraModule.ai;

// 引导用户设置后，刷新配置
ai.open();

// 用户完成设置后，刷新以获取最新配置
ai.reload();

const defaultLLM = ai.getDefaultModel("chat"); // 获取用户刚设置的模型
```

{% hint style="info" %}
`open()` 不会阻塞程序执行，系统无法得知用户何时完成设置。建议在需要使用模型时调用 `reload()` 确保读取到最新配置。
{% endhint %}

***

## 默认模型方法 <a href="#default-model" id="default-model"></a>

AI SDK 支持设置和读取默认模型，让用户可以在偏好设置的「AI 模型」中统一指定偏好的模型。

***

### getDefaultModel(type) <a href="#get-default-model" id="get-default-model"></a>

获取指定类型的默认模型。此为**同步方法**。

* `type` string — 模型类型，可选值：`"chat"`（语言模型）或 `"image"`（视觉模型）
* 返回 `string | undefined` — 默认模型的 `"provider::model"` 字符串，若未设置则返回 `undefined`

```javascript
// 获取用户的默认语言模型
const defaultLLM = ai.getDefaultModel("chat");

if (defaultLLM) {
    console.log(`默认语言模型：${defaultLLM}`); // "openai::gpt-5"
    const model = ai.getModel(defaultLLM);
    const result = await generateText({ model, prompt: "Hello!" });
}

// 获取用户的默认视觉模型
const defaultVLM = ai.getDefaultModel("image");

if (defaultVLM) {
    console.log(`默认视觉模型：${defaultVLM}`); // "openai::dall-e-3"
}
```

{% hint style="info" %}
用户可以在偏好设置的「AI 模型」中分别选择偏好的**语言模型**（`"chat"`）和**视觉模型**（`"image"`），插件可各取所需。
{% endhint %}

***

## Provider 实例方法 <a href="#provider-instance-methods" id="provider-instance-methods"></a>

通过 `getProvider()` 获取的 Provider 实例，除了可作为函数调用来获取 Model 之外，还提供以下方法。

***

### verify() <a href="#verify" id="verify"></a>

验证 Provider 的连接与认证是否有效。用来检查用户目前的配置是否能正常连接。

* 返回 `Promise<VerifyResult>` — 验证结果对象
  * `ok` boolean — 验证是否成功
  * `error` APIError（可选） — 失败时的错误详情

```javascript
const openai = ai.getProvider("openai");

const result = await openai.verify();

if (result.ok) {
    console.log("连接成功！");
} else {
    console.error("连接失败：", result.error.message);
    console.error("HTTP 状态码：", result.error.status);
}
```

{% hint style="danger" %}
**注意：** `verify()` 返回的不是 `boolean`，而是包含 `ok` 和 `error` 的对象。

```javascript
// ❌ 错误
const isValid = await openai.verify();
if (isValid) { ... }

// ✅ 正确
const result = await openai.verify();
if (result.ok) { ... }
```

{% endhint %}

***

### getModels() <a href="#get-models" id="get-models"></a>

获取此 Provider 所有可用的模型列表。

* 返回 `Promise<string[]>` — 模型 ID 数组

```javascript
const openai = ai.getProvider("openai");
const models = await openai.getModels();

console.log(models);
// ["gpt-5.2", "gpt-5", "o3", ...]
```

{% hint style="info" %}
此方法会向 Provider 的 API 发送请求，请确保用户已完成该 Provider 的配置。若未配置，会抛出 `APIError`。
{% endhint %}

***

### hasModel(modelId) <a href="#has-model" id="has-model"></a>

检查此 Provider 是否包含指定模型。

* `modelId` string — 模型 ID（如 `"gpt-5"`）
* 返回 `Promise<boolean>` — 是否存在

```javascript
const openai = ai.getProvider("openai");
const exists = await openai.hasModel("gpt-5");

if (exists) {
    console.log("此模型可用");
}
```

***

## Provider 实例属性 <a href="#provider-instance-properties" id="provider-instance-properties"></a>

以下为 Provider 实例的只读属性：

***

### `name` string

Provider 的名称。

```javascript
const openai = ai.getProvider("openai");
console.log(openai.name); // "openai"
```

***

### `baseURL` string | undefined

目前设置的 API 端点。

```javascript
const openai = ai.getProvider("openai");
console.log(openai.baseURL); // "https://api.openai.com" 或 undefined
```

***

## 支持的 Provider 一览表 <a href="#supported-providers" id="supported-providers"></a>

| Provider          | 名称                    | 类型     | 默认 Base URL                                         |
| ----------------- | --------------------- | ------ | --------------------------------------------------- |
| OpenAI            | `"openai"`            | 商业（云端） | 需手动设置                                               |
| Anthropic         | `"anthropic"`         | 商业（云端） | 需手动设置                                               |
| Google Gemini     | `"google"`            | 商业（云端） | 需手动设置                                               |
| DeepSeek          | `"deepseek"`          | 商业（云端） | 需手动设置                                               |
| 通义千问              | `"tongyi"`            | 商业（云端） | `https://dashscope.aliyuncs.com/compatible-mode/v1` |
| OpenAI Compatible | `"openai-compatible"` | 兼容协议   | 用户自行设置                                              |
| Ollama            | `"ollama"`            | 本地     | `http://localhost:11434/v1`                         |
| LM Studio         | `"lmstudio"`          | 本地     | `http://localhost:1234/v1`                          |

{% hint style="info" %}
**OpenAI Compatible** 可连接任何兼容 OpenAI API 的服务（如 Groq、Together AI、Fireworks、vLLM 等）。API Key 为选填——云端服务通常需要，本地服务则不需要。使用时需自行提供 Base URL。
{% endhint %}

{% hint style="danger" %}
**注意：** Google Gemini 的 Provider 名称是 `"google"`，**不是** `"gemini"`。

```javascript
// ✅ 正确
const google = ai.getProvider("google");
ai.getModel("google::gemini-3-flash");

// ❌ 错误
const gemini = ai.getProvider("gemini");
ai.getModel("gemini::gemini-3-flash");
```

{% endhint %}

***

## 错误处理 <a href="#error-handling" id="error-handling"></a>

### APIError 类

AI SDK 在 API 请求失败时会抛出 `APIError`，包含完整的错误信息。

#### 属性

| 属性             | 类型                    | 说明                          |
| -------------- | --------------------- | --------------------------- |
| `message`      | `string`              | 错误消息                        |
| `status`       | `number \| undefined` | HTTP 状态码（如 401、403、500）     |
| `statusText`   | `string \| undefined` | HTTP 状态文字（如 "Unauthorized"） |
| `code`         | `string \| undefined` | 错误码（从响应内容中提取）               |
| `provider`     | `string \| undefined` | Provider 名称                 |
| `url`          | `string \| undefined` | 请求的 URL                     |
| `responseBody` | `unknown`             | 服务器返回的原始错误内容                |

#### 方法

| 方法           | 说明                 |
| ------------ | ------------------ |
| `toJSON()`   | 返回完整错误详情对象（适合记录日志） |
| `toString()` | 返回错误消息字符串          |

### 错误处理示例

```javascript
const ai = eagle.extraModule.ai;
const { generateText, APIError } = ai;

try {
    const model = ai.getModel(ai.getDefaultModel("chat"));
    const result = await generateText({
        model,
        prompt: "Hello!",
    });
    console.log(result.text);
} catch (error) {
    if (error instanceof APIError) {
        console.error("API 错误：", error.message);
        console.error("状态码：", error.status);       // 401, 429, 500...
        console.error("Provider：", error.provider);    // "openai"

        // 根据状态码处理
        switch (error.status) {
            case 401:
                console.error("API Key 无效，请检查 AI SDK 设置");
                break;
            case 429:
                console.error("请求频率过高，请稍后再试");
                break;
            case 500:
                console.error("服务器内部错误");
                break;
        }

        // 记录完整日志
        console.log(JSON.stringify(error.toJSON(), null, 2));
    } else {
        console.error("非预期错误：", error);
    }
}
```

### 网络错误

当无法连接到 Provider 时（如本地 Ollama 未启动），也会收到 `APIError`：

```javascript
const ollama = ai.getProvider("ollama");
const result = await ollama.verify();

if (!result.ok) {
    console.error(result.error.message);
    // "[ollama] Network error: fetch failed"
}
```

***

## 最佳实践 <a href="#best-practices" id="best-practices"></a>

### 1. 优先使用默认模型

这是最重要的建议。使用 `getDefaultModel()` 直接继承用户在 AI SDK 设置界面中的偏好，而非在代码中写死特定的 Provider 和模型：

```javascript
const ai = eagle.extraModule.ai;
const { generateText } = ai;

// ⭐ 推荐：用户已在 AI SDK 中设置好偏好模型
const defaultLLM = ai.getDefaultModel("chat");

if (!defaultLLM) {
    console.log("请先在 AI SDK 设置中选择默认语言模型");
    return;
}

const model = ai.getModel(defaultLLM);
const result = await generateText({ model, prompt: "..." });
```

**为什么不要写死模型？** 如果你在代码中指定 `ai.getProvider("openai")("gpt-5")`，但用户没有配置 OpenAI，就会出错。你需要额外处理「Provider 未配置」的边界情况，这很麻烦。使用默认模型则能保证用户已经配置并验证过，省去大量防御性代码。

### 2. 正确处理 verify() 结果

```javascript
// ✅ 正确：检查 result.ok
const result = await openai.verify();
if (result.ok) {
    // 连接成功
} else {
    console.error(result.error.message);
}

// ❌ 错误：不要直接当 boolean 使用
if (await openai.verify()) { ... }
```

### 3. 流式适用于长文本生成

当预期回应较长时，使用 `streamText()` 提供更好的用户体验：

```javascript
const { textStream } = streamText({
    model: ai.getModel(ai.getDefaultModel("chat")),
    prompt: longPrompt,
});

for await (const chunk of textStream) {
    appendToUI(chunk);
}
```

***

## 完整示例 <a href="#full-example" id="full-example"></a>

以下为一个综合性的插件示例，展示如何正确使用 AI SDK 的主要功能：

```javascript
eagle.onPluginCreate(async (plugin) => {
    const ai = eagle.extraModule.ai;
    const { generateText, generateObject, streamText, APIError } = ai;

    // 1. 获取用户在偏好设置中选择的默认语言模型
    const defaultLLM = ai.getDefaultModel("chat");

    if (!defaultLLM) {
        console.log("请先在 AI SDK 设置中选择默认语言模型");
        return;
    }

    const model = ai.getModel(defaultLLM);
    console.log(`使用默认模型：${defaultLLM}`);

    // 2. 基本文字生成
    try {
        const result = await generateText({
            model,
            prompt: "请用一句话描述这个插件的功能",
        });
        console.log("生成结果：", result.text);
    } catch (error) {
        if (error instanceof APIError) {
            console.error(`API 错误 [${error.status}]：${error.message}`);
        }
    }

    // 3. 结构化对象生成
    try {
        const result = await generateObject({
            model,
            schema: {
                type: "object",
                properties: {
                    tags: { type: "array", items: { type: "string" } },
                    category: { type: "string" },
                },
            },
            prompt: "请为一张猫咪照片生成 3 个标签和分类",
        });
        console.log("标签：", result.object.tags);
        console.log("分类：", result.object.category);
    } catch (error) {
        console.error("对象生成失败：", error.message);
    }

    // 4. 流式文字生成
    try {
        const { textStream } = streamText({
            model,
            prompt: "请列出 5 个数字艺术的创作技巧",
        });

        let fullText = "";
        for await (const chunk of textStream) {
            fullText += chunk;
        }
        console.log("流式结果：", fullText);
    } catch (error) {
        console.error("流式生成失败：", error.message);
    }
});
```

***

## API 速查表 <a href="#api-cheatsheet" id="api-cheatsheet"></a>

### AI SDK 顶层方法

| 方法                          | 同步/异步 | 返回类型                            | 说明              |
| --------------------------- | ----- | ------------------------------- | --------------- |
| `getProviders()`            | 同步    | `ProviderFunction[]`            | 获取所有 Provider   |
| `getProvider(name)`         | 同步    | `ProviderFunction \| undefined` | 获取指定 Provider   |
| `getAvailableProviders()`   | 同步    | `ProviderFunction[]`            | 获取已配置的 Provider |
| `getModel(provider::model)` | 同步    | `Model`                         | 获取模型实例          |
| `getDefaultModel(type)`     | 同步    | `string \| undefined`           | 获取默认模型          |
| `open()`                    | 同步    | `void`                          | 打开偏好设置的模型设置面板   |
| `reload()`                  | 同步    | `void`                          | 重新加载最新配置        |
| `generateText(options)`     | 异步    | `Promise<GenerateTextResult>`   | 生成文字            |
| `generateObject(options)`   | 异步    | `Promise<GenerateObjectResult>` | 生成结构化对象         |
| `streamText(options)`       | 异步    | `StreamTextResult`              | 流式文字生成          |
| `streamObject(options)`     | 异步    | `StreamObjectResult`            | 流式对象生成          |

### Provider 实例方法

| 方法                  | 同步/异步 | 返回类型                    | 说明         |
| ------------------- | ----- | ----------------------- | ---------- |
| `provider(modelId)` | 同步    | `Model`                 | 获取模型（函数调用） |
| `verify()`          | 异步    | `Promise<VerifyResult>` | 验证连接与认证    |
| `getModels()`       | 异步    | `Promise<string[]>`     | 获取模型列表     |
| `hasModel(modelId)` | 异步    | `Promise<boolean>`      | 检查模型是否存在   |

### Provider 实例属性

| 属性        | 类型                    | 说明              |
| --------- | --------------------- | --------------- |
| `name`    | `string`              | Provider 名称（只读） |
| `baseURL` | `string \| undefined` | API 端点（只读）      |

### VerifyResult

| 属性      | 类型                      | 说明       |
| ------- | ----------------------- | -------- |
| `ok`    | `boolean`               | 验证是否成功   |
| `error` | `APIError \| undefined` | 失败时的错误对象 |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developer.eagle.cool/plugin-api/zh-cn/extra-module/ai-sdk.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
