AI bot - Custom tools

:bookmark: This guide explains how to create, configure, and integrate custom AI tools within the Discourse AI plugin, enabling administrators to extend the bot’s capabilities with user-defined JavaScript functions.

:person_raising_hand: Required user level: Administrator

Tools are programmable functionalities that can be used by the AI bot to perform specific tasks or retrieve information beyond just text-based responses. These tools are scripts or integrations that allow the bot to interact with external APIs, manipulate data, or execute additional functions to extend its capabilities.

Summary

This documentation covers:

  • Creating a new custom AI tool
  • Configuring tool parameters and scripts
  • Available APIs for tool scripts
  • Integrating custom tools with AI personas
  • Testing and troubleshooting custom tools

Creating a new custom AI tool

To create a new AI tool:

  1. Navigate to Admin Panel > Plugins > Discourse AI > Tools
  2. Click “New Tool” (you can use existing presets to learn about options)
  3. Fill in the following fields:
    • Name: The name of the tool as presented to the LLM
    • Description: The description of the tool as presented to the LLM
    • Summary: Summary of what tool does to assist users (displayed in details)
    • Parameters: Define the inputs your tool needs as presented to LLM
    • Script: The JavaScript code that powers your tool
  4. Click “Save”

Configuring tool scripts

Available APIs

Your tool scripts have access to the following APIs:

  1. HTTP Requests:

    http.get(url, options)
    http.post(url, options)
    http.put(url, options)
    http.patch(url, options)
    http.delete(url, options)
    

    Use these to interact with external services. You can use options to specify HTTP headers and body:

    http.get(url, { headers: { "Authorization": "Bearer key" } })
    http.post(url, { headers: { "Content-Type": "application/json" }, body: { key: "value" } })
    http.patch(url, { headers: { "Authorization": "Bearer key" }, body: "some body" })
    http.delete(url, { headers: { "Authorization": "Bearer key" } })
    http.put(url, { headers: { "Authorization": "Bearer key" }, body: "some body" })
    

    All HTTP methods return { status: number, body: string }.

  2. LLM (Language Model) Integration:

    llm.truncate(text, length)
    

    Truncates text to a specified token length based on the configured LLM’s tokenizer.

    llm.generate(prompt, options)
    

    Generates text using the configured LLM. The prompt can be a simple string or a structured object like { messages: [{ type: "system", content: "..." }, { type: "user", content: "..." }] }. Options include json: true to request and auto-parse JSON output, as well as temperature, top_p, max_tokens, and stop_sequences.

  3. Custom upload integration (RAG)

    index.search(query, { filenames: ["file.pdf"], limit: 10 })
    

    Searches indexed RAG document fragments attached to this tool. Returns Array<{ fragment: string, metadata: string | null }> ordered by relevance. Default limit is 10, max 200.

    index.getFile(filename)
    

    Retrieves the full content of an uploaded RAG file by its exact filename. Returns the complete text or null if not found.

  4. Upload support

    upload.create(filename, base_64_content)
    

    Creates a new upload. Returns { id: number, url: string, short_url: string }.

    upload.getUrl(shortUrl)
    

    Given a short URL (e.g. upload://12345), returns the full CDN-friendly URL.

    upload.getBase64(uploadIdOrShortUrl, maxPixels)
    

    Fetches the base64-encoded content of an existing upload. Accepts an upload ID (number) or short URL (string). Optional maxPixels parameter for automatic image resizing (default: 10,000,000).

  5. Execution chain control

    chain.setCustomRaw(raw)
    

    Sets the final raw content of the bot’s post and stops the tool execution chain. Useful for tools that directly generate the full response (e.g., image generation tools).

  6. Secrets management

    secrets.get(alias)
    

    Returns the credential value bound to the given alias. Aliases are defined in the tool’s secret contracts configuration and bound to AI Secrets in the admin panel. Throws an error if the alias is undeclared, unbound, or the credential is missing.

    const apiKey = secrets.get("my_api_key");
    
  7. Discourse integration

    Tools can interact directly with Discourse data:

    discourse.baseUrl              // The site's base URL
    discourse.search(params)       // Perform a Discourse search
    discourse.getPost(post_id)     // Get post details (includes raw content)
    discourse.getTopic(topic_id)   // Get topic details (tags, category, etc.)
    discourse.getUser(id_or_username)  // Get user details
    discourse.createTopic(params)  // Create a new topic
    discourse.createPost(params)   // Create a new post/reply
    discourse.editPost(post_id, raw, options)    // Edit a post's content
    discourse.editTopic(topic_id, updates, options) // Edit topic properties (tags, category, visibility)
    discourse.createChatMessage(params) // Send a chat message
    discourse.createStagedUser(params)  // Create a staged user
    discourse.getAgent(name)       // Get another AI agent (with respondTo method)
    discourse.updateAgent(name, updates) // Update an AI agent's configuration
    discourse.getCustomField(type, id, key)      // Read custom field on post/topic/user
    discourse.setCustomField(type, id, key, value) // Set custom field on post/topic/user
    
  8. Context object

    The context object provides information about where the tool is running:

    • Bot conversation context: context.post_id, context.topic_id, context.private_message, context.participants, context.username, context.user_id
    • Chat context: context.message_id, context.channel_id, context.username
    • Automation context: context.post_id, context.topic_id, context.username, context.user_id, context.feature_name, context.feature_context
    • Common properties: context.site_url, context.site_title, context.site_description

Required functions

Your script must implement:

  • invoke(params): The main function that executes when the tool is called

It may optionally implement:

  • details(): Returns a string (can include basic HTML) describing the tool’s execution, displayed in the chat interface
  • customSystemMessage(): Called during prompt assembly (not during tool invocation). Returns a string appended to the system prompt, or null/undefined to skip. Has access to context, discourse, and index objects.

Example script:

function invoke(params) {
  let result = http.get("https://api.example.com/data?query=" + params.query);
  return JSON.parse(result.body);
}

function details() {
  return "Fetched data from Example API";
}

Limitations and security

  • Execution Timeout: Default timeout of 2000ms of script processing time. The timer pauses during external HTTP requests (http.*) and LLM calls (llm.generate), so only the script’s own processing time counts.
  • Memory: Maximum 10MB V8 heap limit
  • HTTP Requests: Maximum of 20 requests per tool execution
  • Sandboxed Environment: Scripts run in a restricted V8 JavaScript environment (via MiniRacer). No access to browser globals, the host file system, or server-side libraries. Network requests are proxied through the Discourse backend.

Testing your tool

You should test any tool you build to ensure the results the LLM will be provided with match your expectations.

Integrating tools with AI personas

To add your custom tool to an AI Persona:

  1. Go to Admin Panel > Plugins > Discourse AI > Personas
  2. Edit an existing persona or create a new one
  3. In the “Tools” section, you’ll see your custom tools listed alongside built-in tools
  4. Select your custom tool to add it to the persona

Custom tools in action

Once you provide the custom tool to your LLM it can use it to enhance the conversation.

Troubleshooting

If your tool isn’t working as expected:

  1. Use the Test interface to ensure it behaves as expected for your inputs.
  2. Ensure your group is in ai_bot_debugging_allowed_groups. Members of this group have full access to the bot transcripts; you can view the AI logs there.
  3. If anything unexpected is happening, visit https://SITENAME/logs to check for errors.

Additional resources

Last edited by @Saif 2025-03-17T19:29:43Z

Last checked by @hugh 2024-08-06T02:00:12Z

Check documentPerform check on document:
14 Likes

I’m just getting into building tools for AI personas in Discourse, in part so I can really focus on it; this seems like it’s very useful given how many useful and public APIs there are!

For instance, I’m getting results such as:

Understanding Decision-Making Through Poker Strategies AI

maiki:

Create an infocard for Breath of the Wild.

gpt-4o:

Here is an information card for “The Legend of Zelda: Breath of the Wild”:


Title: The Legend of Zelda: Breath of the Wild
Developer: Nintendo
Publisher: Nintendo
Release Date: March 3, 2017
Platforms: Nintendo Switch, Wii U
Genre: Action-adventure
Mode: Single-player

Synopsis:
Set in a vast open world, players control Link, who awakens from a deep slumber to defeat Calamity Ganon and save the kingdom of Hyrule. The game is praised for its immersive environment, allowing players to explore and interact freely with the world and its systems.

Awards:
The game has won numerous awards, including several Game of the Year accolades, for its innovative design, expansive world, and enchanting gameplay.

More information: Wikidata Entry


“Breath of the Wild” is renowned for redefining open-world games and delivering a captivating experience that encourages exploration and discovery.

maiki:

The same for Animal Crossing: New Horizons.

gpt-4o:

Here is an information card for “Animal Crossing: New Horizons”:


Title: Animal Crossing: New Horizons
Developer: Nintendo
Publisher: Nintendo
Release Date: March 20, 2020
Platform: Nintendo Switch
Genre: Social simulation
Mode: Single-player, Multiplayer

Synopsis:
In this charming social simulation game, players assume the role of a customizable character who relocates to a deserted island after purchasing a package from Tom Nook. The game allows for real-time exploration, resource gathering, crafting, and customization, fostering a communal and relaxing gameplay experience.

Awards:
The game was acclaimed for its creative freedom and timing of release, receiving accolades for Game of the Year and its contributions to social gaming during the global pandemic.

More information: Wikidata Entry


“Animal Crossing: New Horizons” became a cultural phenomenon, celebrated for its engaging social features and stress-relieving gameplay during challenging times.

maiki:

A long way from Chicago

gpt-4o:

Here is an information card for the book “A Long Way from Chicago”:


Title: A Long Way from Chicago
Author: Richard Peck
Publication Year: 1998
Genre: Historical Fiction, Young Adult
ISBN: 978-0141303529

Synopsis:
This Newbery Honor-winning novel is a series of short stories chronicling the annual summer trips of Joey and his sister Mary Alice from Chicago to a rural Illinois town to visit their larger-than-life Grandma Dowdel during the late 1920s and 1930s. Each chapter presents an amusing and poignant episode that imparts life lessons amidst the backdrop of the Great Depression.

Awards:
The book was awarded a Newbery Honor in 1999 and is celebrated for its humor, depth, and portrayal of rural American life during the early 20th century.

More information: Wikidata Entry


This book is appreciated for its nostalgic and heartwarming storytelling, capturing the essence of familial bonds and simpler times.

maiki:

Thinking in Bets

gpt-4o:

I previously found the Wikidata entry for “Thinking in Bets,” but I’m unable to retrieve further specific details directly from Wikidata at the moment. However, here is a brief card based on available information:


Title: Thinking in Bets: Making Smarter Decisions When You Don’t Have All the Facts
Author: Annie Duke
Publication Year: 2018
Genre: Non-fiction, Decision Making, Psychology
ISBN: 978-0735216358

Synopsis:
The book leverages insights from poker to help readers understand decision-making under uncertainty. Annie Duke, blending her experiences as a professional poker player and cognitive psychologist, offers a framework for making smarter decisions, evaluating outcomes, and adopting a probabilistic mindset.

More information: Wikidata Entry


For detailed insights, you might consider checking additional resources or databases pertaining to book publications.


Okay, so that’s fine, but I noticed this description…

…and wonder: should I be building my prompts referring directly to those tools, for efficient use?

My general approach to prompt-writing is to refine over time and then lock onto a pattern of behavior I wish for the AI tooling to adopt. However, if I could add specific instructions on, for instance, when to lookup a Wikidata entity, and when to list all claims of a particular entity (two different APIs), then I feel I could refine the entire workflow to flow as I intend… :star_struck:

4 Likes

Indeed clearly explaining the tool and providing examples in the system prompt is beneficial.

2 Likes

Is it possible in a custom tool to insert the API key and OpenAI project from the admin settings?

1 Like

You can make rest calls from a custom tool and specify all headers

2 Likes

just realized that lol… sorry for the brainfart

1 Like

I have uploaded some documents to a persona, it generated the embeddings and now can do semantic search over them. But in some cases, semantic search is not ideal, so I’d like to enhance it and have hybrid search e.g. keep what’s already there and add keyword search. At the moment if I want to do that I should write a custom tool , right?
I know I could simply publish the docs as topics and then it’ll work out of the box with the native Discourse search, but it’s not an option currently.

I’m seeing a tool schema error when adding a custom tool with an array parameter. Error on conversation start:

{
“error”: {
“code”: 400,
“message”: “* GenerateContentRequest.tools[0].function_declarations[3].parameters.properties[properties].items: missing field.\n”,
“status”: “INVALID_ARGUMENT”
}
}

What I tried:

  • Created a custom tool with a parameter named properties of type array.
  • The parameter list UI doesn’t allow specifying items.
  • Exported/imported a full tool JSON that includes items: { type: “string” } for properties.
  • After importing, the error persists as soon as the tool is enabled for a persona. If I remove the tool, the bot works.

Expected:

Either the parameter list UI should allow defining array item types, or the import should honor items so the schema validates.

Has anyone seen this? Is there a known limitation or a required UI path for defining array params?

1 Like