# How Can AI Models Trigger Plugins in NextChat: A Deep Dive into Function Calling

> Discover how NextChat empowers AI models to trigger plugins using OpenAPI 3.0 function calling. Learn how LLM requests execute external APIs and stream results.

- Repository: [NextChat/NextChat](https://github.com/ChatGPTNextWeb/NextChat)
- Tags: deep-dive
- Published: 2026-02-28

---

**NextChat enables AI models to trigger plugins by converting OpenAPI 3.0 specifications into executable function tools that are injected into LLM requests; when the model returns `tool_calls`, the client executes the corresponding JavaScript wrappers to call external APIs and streams the results back into the conversation.**

NextChat (ChatGPTNextWeb/NextChat) implements a robust function-calling architecture that allows large language models to invoke external services during conversations. This system transforms user-provided OpenAPI specifications into callable JavaScript functions, creating a seamless bridge between AI providers like OpenAI, Google Gemini, and Anthropic and third-party REST APIs. The implementation relies on a sophisticated interplay between the plugin store, function tool service, and streaming parsers across multiple client platforms.

## The Function-Calling Execution Flow

The plugin triggering mechanism operates through a six-stage pipeline that converts LLM intent into HTTP requests and back into chat messages.

### 1. Plugin Definition via OpenAPI

Users define plugins as **OpenAPI 3.0 documents** (YAML or JSON) describing HTTP endpoints through the Plugin UI. The interface component at [`app/components/plugin.tsx`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/components/plugin.tsx) handles creation, editing, and validation of these specifications, storing them as `Plugin` objects with content, authentication headers, and metadata.

### 2. Registration with FunctionToolService

When a plugin is saved, `usePluginStore` (located in [`app/store/plugin.ts`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/store/plugin.ts)) persists the definition and calls `FunctionToolService.add(plugin, true)`. This method parses the OpenAPI spec using `yaml.load()` and generates two critical artifacts: a **function-tool definition** object (`type: "function"` with `name`, `description`, and `parameters`) for the LLM, and a **callable JavaScript wrapper** stored in `FunctionToolService.tools[pluginId].funcs`.

### 3. Tool Attachment to Chat Requests

Before sending a request, the client retrieves active tools via `usePluginStore.getState().getAsTools(pluginIds)`, passing the current session's `mask.plugin` array. The OpenAI client in [`app/client/platforms/openai.ts`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/client/platforms/openai.ts) injects these into the request payload as the `tools` array, while the Gemini client in [`app/client/platforms/google.ts`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/client/platforms/google.ts) uses `functionDeclarations`.

### 4. LLM Tool Call Generation

During streaming responses, the LLM may emit **function calls** (OpenAI's `tool_calls`, Gemini's `functionCalls`, or Anthropic's equivalent). The streaming parser `streamWithThink` in [`app/client/platforms/openai.ts`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/client/platforms/openai.ts) extracts these from the SSE response by parsing `json.choices[0].delta.tool_calls`.

### 5. Plugin Function Execution

For each detected tool call, the client invokes the stored implementation from `FunctionToolService.tools[pluginId].funcs`. The wrapper maps LLM-provided arguments to the appropriate Axios client method (`api.client.paths[o.path][o.method]`), handling query parameters, path variables, and request bodies. Authentication headers (`authHeader`, `authLocation`) are injected at this stage, with special handling for OpenAI key injection in the DALL·E 3 plugin.

### 6. Result Integration into Chat

The HTTP response (or error) from the plugin endpoint is formatted as a chat message with the **tool role** and streamed back through the existing `streamWithThink` pipeline. This allows the LLM to receive the external data and formulate a natural language response for the user.

## Core Architecture Components

### Plugin Store and Persistence

The [`app/store/plugin.ts`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/store/plugin.ts) file implements a **persisted Zustand store** (`createPersistStore`) that maintains plugin state across browser sessions. Built-in plugins load automatically from [`public/plugins.json`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/public/plugins.json) on first launch, while user-created plugins trigger regeneration of tool definitions via `create()` and `updatePlugin()` methods. The store maintains the mapping between session masks (`mask.plugin`) and available toolsets.

### FunctionToolService Implementation

`FunctionToolService` serves as the bridge between OpenAPI specifications and executable code. It utilizes `OpenAPIClientAxios` to generate type-safe HTTP clients from parsed YAML. For each operation in the spec, it constructs:

- **Tool schemas**: Structured objects describing available functions to the LLM
- **Function wrappers**: JavaScript functions that validate arguments and execute the corresponding Axios calls, handling both synchronous and error responses

### Client-Side Integration

The platform-specific clients in `app/client/platforms/` directory handle provider nuances. The OpenAI implementation processes `tool_calls` arrays, while the Google client manages `functionCalls`. Both use the same `getAsTools()` interface to retrieve the `tools` array and `funcs` map, ensuring consistent plugin behavior across LLM providers despite differing API schemas.

## Practical Implementation Example

### Defining a Weather Plugin

Create a file containing the OpenAPI specification:

```yaml
openapi: 3.0.0
info:
  title: Weather
  version: 1.0.0
servers:
  - url: https://api.open-meteo.com/v1
paths:
  /forecast:
    get:
      summary: Get weather forecast
      operationId: forecast
      parameters:
        - name: latitude
          in: query
          required: true
          schema:
            type: number
        - name: longitude
          in: query
          required: true
          schema:
            type: number
      responses:
        '200':
          description: Successful response

```

Paste this into the **Plugin** UI in NextChat. The system automatically generates a callable tool named `forecast`.

### Enabling the Plugin for a Session

```typescript
// Activate plugin for current session
const session = useChatStore.getState().currentSession();
session.mask.plugin = ['<plugin-id-generated-by-store>'];

```

### Handling Tool Calls in the Client

The OpenAI client implementation demonstrates the execution flow:

```typescript
// From app/client/platforms/openai.ts
const requestPayload = {
  model: modelConfig.model,
  messages: [...],
  tools: tools,  // Injected from usePluginStore.getAsTools()
};

await streamWithThink(
  chatPath,
  requestPayload,
  getHeaders(),
  tools,
  funcs,  // Map of executable plugin functions
  controller,
  (text, runTools) => {
    const json = JSON.parse(text);
    const toolCalls = json.choices?.[0]?.delta?.tool_calls;
    
    if (toolCalls?.length) {
      const fnName = toolCalls[0].function.name;  // "forecast"
      const args = JSON.parse(toolCalls[0].function.arguments);
      const result = funcs[fnName](args);  // Executes Axios call
      // Result streamed back as tool message
    }
  },
);

```

When a user asks "What's the weather in Paris?", the LLM generates a `tool_calls` entry with `name: "forecast"` and arguments `{latitude: 48.8566, longitude: 2.3522}`, triggering the API call and returning the forecast data.

## Summary

- NextChat converts **OpenAPI 3.0 specifications** into executable function tools through `FunctionToolService` in [`app/store/plugin.ts`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/store/plugin.ts).
- The **Plugin Store** persists definitions and maps them to chat sessions via the `mask.plugin` array.
- AI models trigger plugins by emitting **structured tool calls** (`tool_calls` for OpenAI, `functionCalls` for Gemini) that the streaming parser extracts.
- **JavaScript wrappers** generated from OpenAPI specs handle the actual HTTP requests, authentication, and parameter mapping to external APIs.
- Results from plugin executions are **streamed back into the conversation** as tool messages, allowing continuous multi-turn interactions with external data sources.

## Frequently Asked Questions

### What file format does NextChat require for plugin definitions?

NextChat requires **OpenAPI 3.0 specifications** in either YAML or JSON format. These documents must describe the HTTP endpoints, parameters, and authentication methods for the external service you want to expose to the AI model.

### How does NextChat handle authentication for plugin APIs?

The `FunctionToolService` supports authentication through the `authHeader` and `authLocation` properties defined in the plugin object. When executing a function, the system injects these credentials into the Axios request headers or query parameters accordingly, with special provisions for forwarding the user's OpenAI API key to specific plugins like DALL·E 3.

### Can I use multiple plugins simultaneously in one conversation?

Yes. The `mask.plugin` array accepts multiple plugin IDs, and `usePluginStore.getAsTools(pluginIds)` aggregates all available functions from the specified plugins into a single `tools` array sent to the LLM. The model can then choose which specific function to call based on the user's request context.

### Which AI providers support plugin triggering in NextChat?

NextChat supports plugin triggering across **OpenAI** (via `tool_calls`), **Google Gemini** (via `functionDeclarations`), and **Anthropic** implementations. Each client in `app/client/platforms/` handles provider-specific payload formatting while using the unified `FunctionToolService` for actual execution.