How Can AI Models Trigger Plugins in NextChat: A Deep Dive into Function Calling
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 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) 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 injects these into the request payload as the tools array, while the Gemini client in 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 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 file implements a persisted Zustand store (createPersistStore) that maintains plugin state across browser sessions. Built-in plugins load automatically from 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:
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
// 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:
// 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
FunctionToolServiceinapp/store/plugin.ts. - The Plugin Store persists definitions and maps them to chat sessions via the
mask.pluginarray. - AI models trigger plugins by emitting structured tool calls (
tool_callsfor OpenAI,functionCallsfor 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →