useLensAgent Hook

核心 React Hook,管理 Agent 對話狀態

概述

useLensAgent 是 Lens OS SDK 提供的核心 React Hook,提供完整的 Agent 互動功能:發送訊息、接收串流回應、管理對話歷史等。

基本用法

基本範例
import { useLensAgent } from "@lens-os/sdk/react";

function Chat() {
  const {
    messages,
    isLoading,
    sendMessage,
    newSession,
  } = useLensAgent({
    endpoint: "/api/lens/agent/chat",
    userId: "user-123",
  });

  return (
    <div>
      {messages.map((msg, i) => (
        <div key={i}>{msg.content}</div>
      ))}

      <button onClick={() => sendMessage("Hello!")}>
        Send
      </button>
    </div>
  );
}

API 參考

Options(選項)

Options
interface UseLensAgentOptions {
  // 必填
  endpoint: string;     // Chat API 路徑

  // 選填
  userId?: string;      // 用戶識別碼(可由伺服器端注入)
  onEvent?: (event: SSEEvent) => void;  // 事件回調
}
參數類型必填說明
endpointstringChat API 路徑,例如 "/api/lens/agent/chat"
userIdstring用戶識別碼(可由伺服器端注入)
onEventfunctionSSE 事件回調函式

Return Value(回傳值)

Return
interface UseLensAgentReturn {
  // 狀態
  messages: Message[];        // 對話訊息
  isLoading: boolean;         // 是否執行中
  isInitialized: boolean;     // SDK 是否初始化
  sessionId: string;          // 目前 Session ID
  error: Error | null;        // 最後一個錯誤

  // 方法
  sendMessage: (message: string, context?: MessageContext) => Promise<void>;
  abort: () => void;          // 取消目前執行
  newSession: () => void;     // 開始新對話
  loadSession: (sessionId: string, messages: Message[]) => void;
}

狀態說明

messages

對話訊息陣列,每則訊息格式如下:

Message
interface Message {
  role: "user" | "assistant" | "system" | "tool";
  content: string | MessageContent[];
  timestamp?: Date;
}

isLoading

當 Agent 正在處理訊息時為 true,可用於:

  • 顯示載入指示器
  • 禁用送出按鈕
  • 切換停止按鈕

sessionId

目前對話的唯一識別碼,每次呼叫 newSession() 會產生新的 ID。

方法說明

sendMessage

發送訊息給 Agent:

sendMessage
// 基本用法
await sendMessage("幫我找 TypeScript 的書");

// 帶上下文(用於頁面擷取)
await sendMessage("點擊搜尋按鈕", {
  currentUrl: window.location.href,
  pageState: {
    url: "...",
    title: "...",
    markdown: "...",
    screenshot: "...",
    actionableElements: [...],
  },
});

abort

取消正在執行的請求:

abort
<button onClick={isLoading ? abort : handleSend}>
  {isLoading ? "停止" : "送出"}
</button>

newSession

開始新對話,清除目前訊息:

newSession
<button onClick={newSession}>新對話</button>

loadSession

載入歷史對話:

loadSession
const handleLoad = async (targetSessionId: string) => {
  const res = await fetch(`/api/lens/agent/sessions/${targetSessionId}`);
  const data = await res.json();

  if (data.success) {
    loadSession(targetSessionId, data.messages);
  }
};

事件處理

使用 onEvent 回調處理 SSE 事件:

onEvent
const { messages } = useLensAgent({
  endpoint: "/api/lens/agent/chat",
  userId: "user-123",

  onEvent: (event) => {
    switch (event.type) {
      case "tool_call":
        console.log("Tool started:", event.name);
        // 顯示 Tool Card
        break;

      case "tool_result":
        console.log("Tool completed:", event.name, event.result);
        // 更新 Tool Card
        break;

      case "text":
        console.log("Text:", event.content);
        break;

      case "error":
        console.error("Error:", event.error);
        break;

      case "done":
        console.log("Completed");
        break;
    }
  },
});

事件類型說明:

  • tool_call:Agent 開始執行工具,可用於顯示 Tool Card
  • tool_result:工具執行完成,可用於更新 Tool Card 狀態
  • text:文字內容更新
  • error:發生錯誤
  • done:對話回合完成

完整範例

以下是一個包含所有功能的完整聊天元件範例:

Chat.tsx
"use client";

import { useState, useRef, useEffect } from "react";
import { useLensAgent } from "@lens-os/sdk/react";

export function Chat({ userId }: { userId: string }) {
  const [input, setInput] = useState("");
  const messagesEndRef = useRef<HTMLDivElement>(null);

  const {
    messages,
    isLoading,
    error,
    sendMessage,
    abort,
    newSession,
  } = useLensAgent({
    endpoint: "/api/lens/agent/chat",
    userId,
    onEvent: (event) => {
      if (event.type === "tool_call") {
        console.log("Tool:", event.name);
      }
    },
  });

  // 自動捲動
  useEffect(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [messages]);

  const handleSend = async () => {
    if (!input.trim() || isLoading) return;
    const message = input;
    setInput("");
    await sendMessage(message);
  };

  return (
    <div className="flex flex-col h-full">
      {/* 訊息區 */}
      <div className="flex-1 overflow-auto p-4 space-y-4">
        {messages.map((msg, i) => (
          <div
            key={i}
            className={msg.role === "user" ? "text-right" : "text-left"}
          >
            <span className={`inline-block p-3 rounded-lg ${
              msg.role === "user" ? "bg-blue-500 text-white" : "bg-gray-100"
            }`}>
              {typeof msg.content === "string" ? msg.content : "..."}
            </span>
          </div>
        ))}

        {isLoading && <div className="text-gray-500">思考中...</div>}
        {error && <div className="text-red-500">錯誤:{error.message}</div>}

        <div ref={messagesEndRef} />
      </div>

      {/* 輸入區 */}
      <div className="border-t p-4 flex gap-2">
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && handleSend()}
          placeholder="輸入訊息..."
          className="flex-1 border rounded px-3 py-2"
        />
        <button
          onClick={isLoading ? abort : handleSend}
          className="bg-blue-500 text-white px-4 py-2 rounded"
        >
          {isLoading ? "停止" : "送出"}
        </button>
        <button
          onClick={newSession}
          className="border px-4 py-2 rounded"
        >
          新對話
        </button>
      </div>
    </div>
  );
}

Tip

這個範例展示了基本的聊天功能。若需要更完整的元件(含 Tool Cards、Rules 選單),請參考「聊天元件」文檔。

下一步