npm package: @gijela/ai-hooks
Description: 一个与 AI 相关的 React 和 Vue Hooks 工具集，内置对流式响应的支持。
Author: Gijela
Latest version: 1.0.2

# 🚀 ai-hooks

[![NPM version](https://img.shields.io/npm/v/@gijela/ai-hooks.svg?style=flat)](https://www.npmjs.com/package/@gijela/ai-hooks)

[English](https://github.com/gijela/ai-hooks/blob/master/README.md) | [中文](https://github.com/gijela/ai-hooks/blob/master/README_CN.md)

`ai-hooks` is a lightweight, powerful, and framework-agnostic library designed to completely simplify the complexity of handling Server-Sent Events (SSE) in AI applications. Currently, we provide the core `useSse` Hook, and we plan to expand its capabilities based on community needs in the future. It helps you easily manage the low-level details of streaming data, allowing you to focus on building excellent user experiences.

## ✨ Features

- **Cross-Framework Support**: Provides a consistent core capability for both React Hooks and Vue Composition API.
- **Full Control**: Simplicity doesn't mean losing control. With powerful callback functions like `formatMessage`, you can precisely control every piece of data received from the backend, easily implementing data filtering, transformation, or parsing to perfectly fit any data format.
- **Full Lifecycle Management**: Automatically handles connection, message reception, closing, and errors.
- **Fine-grained State Control**: Through the `message.status` field, developers can track each stage of a message from creation to completion (`connecting`, `streaming-content`, `completed`, `error`, etc.), enabling highly customized UI interactions.
- **TypeScript Support**: Complete type definitions for an excellent development experience.

## 🎯 Use Cases

- Any scenario that requires handling SSE.

## 📦 Installation

```bash
pnpm add @gijela/ai-hooks
```

---

## Core Concept: The Message Object

The key to understanding `useSse` is to understand how it manages the `messages` state. `messages` is an array of type `SseMessage[]`. Each object contains all the information needed to render a conversation.

```typescript
// SseMessage can be either UserSseMessage or AssistantSseMessage
interface AssistantSseMessage {
  id: string;
  role: 'assistant';
  content: string;
  status: SseStatus; // The key field!
  // ... other possible fields
}
```

### `message.status`

This is the core for implementing a fine-grained UI. The `status` field allows you to know exactly what state each assistant message is in. `useSse` automatically handles some states (like `connecting`, `completed`, `error`), but more importantly, you can customize and update this status in the `formatMessage` function based on your business logic.

Possible values for `SseStatus` include:

| Status                  | Description                                                       |
| ----------------------- | ----------------------------------------------------------------- |
| `idle`                  | Initial state, no operation has started.                          |
| `connecting`            | Establishing a connection, waiting for the server response.       |
| `streaming-reasoning`   | Streaming "reasoning" content.                                    |
| `streaming-content`     | **Core state**, streaming the main content.                       |
| `streaming-tool-call`   | Streaming tool call related information.                          |
| `streaming-tool-result` | Streaming tool execution results.                                 |
| `completed`             | The stream has ended successfully (received the `[DONE]` marker). |
| `aborted`               | The stream was manually aborted by the user.                      |
| `resuming`              | Attempting to resume a broken connection.                         |
| `error`                 | An error occurred.                                                |

---

## ⚛️ for React

The `useSse` Hook for React provides complete state management and event handling capabilities.

### 💡 Complete Example

This is a fully-featured React chat component demonstrating all the core features of `useSse`.

```tsx
import React from 'react';
import { useSse } from '@gijela/ai-hooks/react';

export default function ChatComponent() {
  const {
    messages,
    input,
    handleInputChange,
    handleSubmit,
    error,
    loading,
    stop,
  } = useSse({
    // Initial messages
    initialMessages: [
      { id: '1', role: 'assistant', content: 'Hello! How can I help you?' },
    ],
    // Core: Parse and format the SSE data stream
    formatMessage: (data, lastMessage) => {
      try {
        const parsed = JSON.parse(data);
        // Update message status and content based on the type from the backend
        if (parsed.type === 'reasoning') {
          return {
            ...lastMessage,
            status: 'streaming-reasoning',
            reasoningContent: parsed.payload,
          };
        }
        return {
          ...lastMessage,
          status: 'streaming-content',
          content: lastMessage.content + parsed.payload,
        };
      } catch (e) {
        // If parsing fails, set the message status to error
        return { ...lastMessage, status: 'error' };
      }
    },
  });

  return (
    <div
      className="chat-container"
      style={{ fontFamily: 'sans-serif', padding: '20px' }}
    >
      <div className="messages-area" style={{ marginBottom: '20px' }}>
        {messages.map(m => (
          <div
            key={m.id}
            style={{ marginBottom: '10px', whiteSpace: 'pre-wrap' }}
          >
            <strong>{m.role}: </strong>
            <span>{m.content}</span>
            {m.role === 'assistant' && m.status && m.status !== 'completed' && (
              <em style={{ marginLeft: '10px', color: '#888' }}>
                ({m.status})
              </em>
            )}
          </div>
        ))}

        {loading && <div style={{ color: '#555' }}>Thinking...</div>}
        {error && (
          <div style={{ color: 'red' }}>
            Oops, an error occurred: {error.message}
          </div>
        )}
      </div>

      <form onSubmit={e => handleSubmit(e, { url: '/api/chat' })}>
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Ask me anything..."
          disabled={loading}
          style={{
            width: 'calc(100% - 150px)',
            padding: '8px',
            marginRight: '10px',
          }}
        />
        <button
          type="submit"
          disabled={loading}
          style={{ padding: '8px 12px' }}
        >
          Send
        </button>
      </form>
      <button
        onClick={stop}
        disabled={!loading}
        style={{ marginTop: '10px', padding: '8px 12px' }}
      >
        Stop
      </button>
    </div>
  );
}
```

### 📚 API

- **Parameters `options`**:

  - `initialMessages`: The initial list of messages.
  - `initialInput`: The initial value of the input field.
  - `formatMessage`: **Required**, a function to parse the raw server data and return an updated message object. This is the core for implementing custom logic.
  - `onOpen`, `onMessage`, `onFinish`, `onClose`, `onError`: Optional lifecycle callback functions.

- **Return Values**:
  - `messages`: A reactive array of messages.
  - `input`, `handleInputChange`: State and handler for a controlled input.
  - `handleSubmit`: A convenient handler for form submission.
  - `append`: A function to manually trigger an SSE request.
  - `stop`: A function to manually abort the connection.
  - `loading`, `error`: Refs for the connection status.

---

## 💚 for Vue

The `useSse` Hook for Vue provides the same powerful features using Vue's reactivity system.

### 💡 Complete Example

This is a fully-featured Vue chat component using the Composition API (`<script setup>`).

```vue
<template>
  <div class="chat-container" style="font-family: sans-serif; padding: 20px">
    <div class="messages-area" style="margin-bottom: 20px">
      <div
        v-for="m in messages"
        :key="m.id"
        style="margin-bottom: 10px; white-space: pre-wrap"
      >
        <strong>{{ m.role }}: </strong>
        <span>{{ m.content }}</span>
        <em
          v-if="m.role === 'assistant' && m.status && m.status !== 'completed'"
          style="margin-left: 10px; color: #888"
        >
          ({{ m.status }})
        </em>
      </div>

      <div v-if="loading" style="color: #555">Thinking...</div>
      <div v-if="error" style="color: red">
        Oops, an error occurred: {{ error.message }}
      </div>
    </div>

    <form @submit="e => handleSubmit(e, { url: '/api/chat' })">
      <input
        v-model="input"
        placeholder="Ask me anything..."
        :disabled="loading"
        style="width: calc(100% - 150px); padding: 8px; margin-right: 10px"
      />
      <button type="submit" :disabled="loading" style="padding: 8px 12px">
        Send
      </button>
    </form>
    <button
      @click="stop"
      :disabled="!loading"
      style="margin-top: 10px; padding: 8px 12px"
    >
      Stop
    </button>
  </div>
</template>

<script setup>
import { useSse } from '@gijela/ai-hooks/vue';

const { messages, input, handleSubmit, error, loading, stop } = useSse({
  formatMessage: (data, lastMessage) => {
    try {
      const parsed = JSON.parse(data);
      if (parsed.type === 'reasoning') {
        return {
          ...lastMessage,
          status: 'streaming-reasoning',
          reasoningContent: parsed.payload,
        };
      }
      return {
        ...lastMessage,
        status: 'streaming-content',
        content: lastMessage.content + parsed.payload,
      };
    } catch (e) {
      return { ...lastMessage, status: 'error' };
    }
  },
});
</script>
```

### 📚 API

- **Parameters `options`**:

  - Identical to the React version. `formatMessage` is also the required core function.

- **Return Values**:
  - `messages`, `input`, `loading`, `error`: Are all Vue `Ref` objects and can be used directly in the template.
  - `append`, `stop`, `setMessages`: Functions to control the data stream.
  - `handleSubmit`: Also provides a convenient method for form handling, where the `input` Ref naturally supports `v-model`.

---

We hope `ai-hooks` makes your development work easier and more enjoyable. Go ahead and add cool real-time features to your applications! If you have any questions or suggestions, feel free to open an Issue.