> For the complete documentation index, see [llms.txt](https://developer.eagle.cool/plugin-api/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.eagle.cool/plugin-api/zh-tw/extra-module/ai-sdk.md).

# AI SDK

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

***

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

<figure><img src="/files/IEsUoEBjRuXrwJrYrbbY" alt=""><figcaption></figcaption></figure>

「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 系列）

**本地模型**（完全離線執行）：

* Ollama（支援 Llama 4、Qwen3、Gemma 3 等）
* LM Studio（圖形化介面，新手友善）

**OpenAI 相容協議**：

* OpenAI Compatible — 支援任何相容 OpenAI API 協議的服務端點（如 Groq、Together AI、Fireworks、vLLM 等），只需提供 Base URL 即可連接，API Key 為選填。

後續版本將陸續增加更多主流模型服務提供商的支援。

配置一次後，所有 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` |
| Ollama            | `"ollama"`            | 本地       | `http://localhost:11434/v1`                         |
| LM Studio         | `"lmstudio"`          | 本地       | `http://localhost:1234/v1`                          |
| OpenAI Compatible | `"openai-compatible"` | 自訂（相容協議） | 需手動設定（API Key 選填）                                   |

{% hint style="info" %}
**OpenAI Compatible** 適用於所有相容 OpenAI API 協議的服務。使用者只需提供 Base URL，API Key 為選填（部分雲端服務需要，本地服務通常不需要）。系統會自動在 URL 末尾補上 `/v1`（若尚未包含）。
{% 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` | 失敗時的錯誤物件 |
