How the GPT Academic Plugin System Works and How to Add Custom Plugins

The GPT Academic plugin system uses a centralized registry in crazy_functional.py to map UI elements to Python functions, which are executed through a standardized wrapper ArgsGeneralWrapper that normalizes arguments, handles cookies, and manages UI updates, allowing developers to add custom tools by creating modules in crazy_functions/ and registering them with metadata.

The GPT Academic project (binary-husky/gpt_academic) provides a flexible plugin architecture that enables users to extend its core functionality without modifying the main application code. Understanding how the GPT Academic plugin system handles registration, loading, and execution allows developers to integrate custom tools seamlessly into the Gradio-based interface.

Architecture of the GPT Academic Plugin System

The plugin system consists of four coordinated components that handle discovery, loading, execution, and UI rendering.

Plugin Registry: crazy_functional.py

At the heart of the system lies the function_plugins dictionary, built by crazy_functional.get_crazy_functions(). This registry describes every function-plugin with metadata including group categorization, UI button configuration, color schemes, and entry points. When the application starts, this dictionary is returned to the UI layer in main.py, which generates buttons and dropdowns based on the registry contents.

Plugin Loader: connect_void_terminal.py

The shared_utils.connect_void_terminal.get_plugin_handle() function transforms string identifiers like "crazy_functions.Markdown_Translate->Markdown英译中" into callable objects. It uses importlib.import_module combined with getattr to dynamically load the specified function or class from the crazy_functions/ directory, enabling late-binding of plugin code.

Execution Wrapper: ArgsGeneralWrapper in toolbox.py

All plugins are executed through toolbox.ArgsGeneralWrapper, defined around lines 99-115 in toolbox.py. This decorator normalizes the call signature across all plugins, accepting parameters including txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, and user_request. The wrapper constructs a ChatBotWithCookies instance, injects cookies for session management, extracts advanced arguments from plugin_advanced_arg, handles error catching, and yields control to the actual plugin function.

Front-End UI Integration

Two UI paths exist for plugin invocation. The legacy UI in main.py builds buttons that call plugins directly through ArgsGeneralWrapper. The advanced UI, defined in themes/gui_advanced_plugin_class.py, presents a floating argument panel when AdvancedArgs is enabled. The define_gui_advanced_plugin_class function builds up to 8 textboxes and dropdowns, parses the JSON payload (see lines 38-42), and routes through route_switchy_bt_with_arg before hitting the same execution wrapper.

How to Add a Custom Plugin to GPT Academic

Creating a new plugin requires implementing a standardized interface and registering it in the central dictionary.

Step 1: Create a Plugin Module in crazy_functions/

Place a new Python file in the crazy_functions/ directory, for example crazy_functions/my_plugin.py. The file must define a function or class that follows the execution wrapper's expected signature.

Step 2: Implement the Required Function Signature

Your plugin must accept the exact arguments that ArgsGeneralWrapper passes. The standard signature is:

def my_plugin(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request):
    # Implementation logic here

    yield from update_ui(chatbot, history)

The txt parameter contains the main input text, llm_kwargs holds model configuration, plugin_kwargs receives advanced arguments, and chatbot is the conversation state list. Always yield from update_ui to refresh the interface.

Step 3: Register the Plugin in crazy_functional.py

Import your function and add an entry to the function_plugins dictionary inside get_crazy_functions():

from crazy_functions.my_plugin import my_plugin
from toolbox import HotReload

function_plugins["我的演示插件"] = {
    "Group": "演示|实验",
    "Color": "primary",
    "AsButton": True,
    "Info": "演示如何快速集成自定义插件",
    "Function": HotReload(my_plugin),
}

The HotReload wrapper enables live editing by calling importlib.reload on each request, allowing you to modify code without restarting the Gradio server.

Step 4: Configure Advanced Arguments (Optional)

For plugins requiring additional UI inputs, create a class with a static execute method and optional get_arg_schema():

class MyPluginWrap:
    @staticmethod
    def get_arg_schema():
        return {
            "main_input": {"type": "text", "label": "文件路径", "default": "./"},
            "temperature": {"type": "float", "label": "采样温度", "default": 0.7}
        }

    @staticmethod
    def execute(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request):
        # Access arguments via plugin_kwargs.get("main_input")

        yield from update_ui(chatbot, history)

Register using "Class": MyPluginWrap, "AdvancedArgs": True, and optionally "ArgsReminder": "提示信息" to trigger the floating panel UI.

Practical Code Examples

Example 1: Simple Function Plugin

Create crazy_functions/hello_world.py:

def hello_world(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request):
    """Echo the input string with a friendly prefix."""
    reply = f"👋 你好!你刚才说的是:{txt}"
    chatbot.append([txt, reply])
    yield from update_ui(chatbot, history)

Register in crazy_functional.py:

from crazy_functions.hello_world import hello_world
from toolbox import HotReload

function_plugins["问候插件"] = {
    "Group": "实验",
    "Color": "primary",
    "AsButton": True,
    "Info": "返回问候语并回显输入",
    "Function": HotReload(hello_world),
}

Example 2: Advanced-Args Class Plugin

Create crazy_functions/word_counter.py:

class WordCounter:
    @staticmethod
    def get_arg_schema():
        return {
            "main_input": {"type": "text", "label": "要统计的文字", "default": ""},
            "ignore_case": {"type": "bool", "label": "忽略大小写", "default": True},
        }

    @staticmethod
    def execute(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request):
        text = plugin_kwargs.get("main_input", "")
        ignore = plugin_kwargs.get("ignore_case", True)
        words = text.lower().split() if ignore else text.split()
        count = len(words)
        reply = f"📝 共有 {count} 个词({'忽略' if ignore else '区分'}大小写)。"
        chatbot.append([txt, reply])
        yield from update_ui(chatbot, history)

Register with advanced UI support:

from crazy_functions.word_counter import WordCounter

function_plugins["词数统计"] = {
    "Group": "工具",
    "Color": "secondary",
    "AsButton": False,
    "Info": "统计文本中的词数,可选忽略大小写",
    "Class": WordCounter,
    "AdvancedArgs": True,
    "ArgsReminder": "请在右侧填写要统计的文本和是否忽略大小写",
}

Example 3: Programmatic Plugin Testing

Test plugins outside the UI using the low-level loader:

from toolbox import get_plugin_handle, get_plugin_default_kwargs

# Load the plugin by its canonical string

plugin = get_plugin_handle("crazy_functions.word_counter->execute")
default = get_plugin_default_kwargs()
default["plugin_kwargs"] = {"main_input": "Hello world!", "ignore_case": True}

# Run the generator

for cookies, chat, hist, msg in plugin(**default):
    print(msg)   # -> 📝 共有 2 个词(忽略大小写)。

Key Files in the Plugin System

  • crazy_functional.py: Central registry that returns the function_plugins dict. Adding entries here makes the UI aware of the plugin.
  • toolbox.py: Provides ArgsGeneralWrapper (the execution wrapper), HotReload (live-code reload), and UI helpers like update_ui.
  • shared_utils/connect_void_terminal.py: Implements get_plugin_handle that turns "module->function" strings into callables using importlib.
  • main.py: Builds the Gradio UI, registers button callbacks, and wires the plugin dictionary into the front-end.
  • themes/gui_advanced_plugin_class.py: Implements the floating panel for plugins declaring AdvancedArgs = True, handling JSON-to-dict conversion.

Summary

  • The GPT Academic plugin system centers on a declarative registry (function_plugins) in crazy_functional.py that describes available tools.
  • ArgsGeneralWrapper in toolbox.py provides a unified execution environment handling cookies, error management, and UI refresh cycles.
  • Plugins must implement a standard seven-parameter signature: (txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request).
  • The HotReload wrapper enables live development by reloading modules on each invocation without server restarts.
  • Advanced plugins use a class-based approach with get_arg_schema() to generate dynamic UI panels for parameter input.

Frequently Asked Questions

What is the exact function signature required for GPT Academic plugins?

Plugins must accept seven positional arguments: txt (the input text), llm_kwargs (model configuration dictionary), plugin_kwargs (advanced arguments dictionary), chatbot (the conversation history list), history (raw history), system_prompt (system instructions), and user_request (the original request object). The function must yield control back to the UI using yield from update_ui(chatbot, history).

How does the HotReload wrapper enable live plugin development?

HotReload, defined in toolbox.py, wraps the plugin function and calls importlib.reload on the module before each execution. This reloads the source code from disk, allowing developers to modify plugin logic in crazy_functions/ and see changes immediately in the Gradio interface without restarting the Python process.

Can I add plugins without restarting the server?

Yes. Because the plugin registry uses HotReload wrappers and the UI dynamically loads plugins through get_plugin_handle, you can add new plugin files to crazy_functions/, register them in crazy_functional.py, and modify existing code while the server is running. The changes take effect on the next button click or dropdown selection.

How are advanced arguments passed to plugins?

When a plugin declares "AdvancedArgs": True, the UI renders a floating panel defined in gui_advanced_plugin_class.py. User inputs are serialized to a JSON string, parsed into a dictionary (lines 38-42), and injected into the plugin_kwargs parameter of the plugin function. You can pre-define the schema using a static get_arg_schema() method in your plugin class to control label names, types, and defaults.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →