# AI SDK

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

***

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

<figure><img src="https://3610077812-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fs4bjQFr3EFnc58JiXhsg%2Fuploads%2FPNO5AWhR5Ay7kUHyVc4V%2Fimage.png?alt=media&#x26;token=309e0b8d-a6c9-4e83-a3e6-52207d98b0c7" 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` | 失敗時的錯誤物件 |


---

# 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-tw/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.
