How to Use Function Calling and Function Mode in Prompt-Optimizer for Tool Integration

Prompt-optimizer provides a three-layer function-calling stack that exposes custom tools to LLMs via UI mode selection, automatic tool registry management, and provider-specific adapters for OpenAI and Gemini.

Prompt-optimizer ships with a built-in function-calling architecture that enables automatic tool invocation during prompt optimization. By leveraging the function mode system, developers can register custom handlers for web search, file fetching, image generation, or any external API, allowing the LLM to decide when to call these tools during a conversation.

Understanding the Function-Calling Architecture

The function-calling implementation in prompt-optimizer consists of three distinct layers that work together to route tool definitions from the UI to the LLM and execute returned function calls.

UI Layer: Function Mode Selection

The entry point for function mode configuration is FunctionModeSelector.vue, which renders a radio-group interface letting users choose between Basic, Pro, or Image modes. When a user selects a mode, the useFunctionMode composable persists the choice to local storage using UI_SETTINGS_KEYS.FUNCTION_MODE and triggers a re-initialization of the function model manager.

// packages/ui/src/composables/mode/useFunctionMode.ts
const setFunctionMode = async (mode: FunctionMode) => {
  await setPreference(UI_SETTINGS_KEYS.FUNCTION_MODE, mode);
  // Re-initialise the manager so the correct tool set is loaded
  resetFunctionModelManagerSingleton();
};

Model Layer: Tool Registry Management

The useFunctionModelManager composable maintains a singleton registry that maps each function mode to its specific set of tool definitions. This layer reads a static tool configuration and builds a runtime registry that the prompt service consumes.

// packages/ui/src/composables/model/useFunctionModelManager.ts
export function useFunctionModelManager(): UseFunctionModelManagerReturn {
  const tools = {
    basic: [{ name: "searchWeb", description: "Search the web", parameters: { type: "object", properties: { query: { type: "string" } } } }],
    pro:   [{ name: "searchWeb", ... }, { name: "fetchFile", ... }],
    image: [{ name: "generateImage", ... }],
  };
  return { getTools: () => tools[currentFunctionMode] };
}

When the application initializes, usePromptOptimizer retrieves the current tool list and attaches it to every prompt request sent to the LLM service.

LLM-Adapter Layer: Provider Integration

The adapter layer handles the provider-specific implementation of function calling. Both openai-adapter.ts and gemini-adapter.ts contain logic that maps the LLM's function payload to internal handlers and executes them.

OpenAI Implementation: The adapter adds a tools array to the ChatCompletionCreateParams payload. When the response contains a function_call, it extracts the name and arguments, executes the registered handler, and injects the result back into the message history as a tool response.

// packages/core/src/services/llm/adapters/openai-adapter.ts
if (toolCall && typeof toolCall.function === "object") {
  const handler = functionModelManager.getHandler(toolCall.function.name);
  const result = await handler(JSON.parse(toolCall.function.arguments));
  // Inject result as a new assistant message
  messages.push({ role: "tool", name: toolCall.function.name, content: JSON.stringify(result) });
}

Gemini Implementation: Uses the functionDeclarations field and a convertGeminiFunctionCallsToToolCalls helper to normalize the payload format before execution.

Selecting a Function Mode in the UI

To activate function calling, users must first select a mode through the UI component. The available modes determine which tools the LLM can access during the conversation.

  1. Basic Mode: Provides essential tools like searchWeb for simple information retrieval.
  2. Pro Mode: Expands the toolkit to include file operations and advanced data fetching.
  3. Image Mode: Exposes generation capabilities through tools like generateImage.

The selection persists across sessions via local storage, ensuring the function mode remains active until explicitly changed.

Registering Tools for Different Modes

Each function mode maintains its own isolated toolset defined in useFunctionModelManager. The registry uses a static JSON map (typically tool-definitions.json in the UI package) to construct the runtime tool list.

When usePromptOptimizer submits a prompt, it automatically injects the current mode's tools:

const { functionMode } = useFunctionMode(services);
const toolList = functionModelManager.getTools();
promptService.sendPrompt({ ...prompt, tools: toolList });

This design allows different workflows to expose only the relevant capabilities—preventing the LLM from calling image generation tools during text-only optimizations, for example.

Adding Custom Tools to Prompt-Optimizer

Extending the function-calling capabilities requires three steps: defining the tool schema, implementing the handler, and exposing it through the manager.

1. Define the Tool Schema

Add the tool definition to packages/ui/src/composables/model/useFunctionModelManager.ts under the desired mode:

{
  name: "calculateSum",
  description: "Return the sum of two numbers",
  parameters: {
    type: "object",
    properties: {
      a: { type: "number" },
      b: { type: "number" }
    },
    required: ["a", "b"]
  }
}

2. Implement the Handler

Create the execution logic in the same file or import from a separate module:

const calculateSum = ({ a, b }: { a: number; b: number }) => ({ result: a + b });

3. Register the Handler

Expose the handler through functionModelManager.getHandler() so the LLM adapters can resolve it by name during execution. After implementation, the tool becomes available immediately for the selected function mode.

Practical Code Examples

This example demonstrates activating Pro mode and triggering a web search tool:

// UI selection triggers mode change
await setFunctionMode('pro');

// Prompt requesting external data
const prompt = {
  role: "user",
  content: "What is the current price of Bitcoin?"
};

// Optimizer sends prompt with searchWeb tool attached
await promptService.sendPrompt(prompt);

// LLM returns: { "name": "searchWeb", "arguments": "{\"query\":\"Bitcoin price\"}" }
// Adapter executes handler, returns result as tool message

Example 2: Creating a Custom Calculation Tool

Using a custom tool registered in Basic mode:

// 1. Tool definition added to "basic" toolbox (see registration steps above)

// 2. Prompt triggering the tool
const prompt = {
  role: "user",
  content: "Add 12 and 37 for me."
};

// 3. Request includes tools: [{name:"calculateSum",...}]
await promptService.sendPrompt(prompt);

// 4. Adapter receives function_call, executes calculateSum(12, 37)
// 5. Result injected: { role: "tool", content: "{\"result\":49}" }
// 6. LLM responds: "The sum of 12 and 37 is 49."

Example 3: Using Image Mode for Generation

Activating Image mode to expose generation capabilities:

await setFunctionMode('image');

const prompt = {
  role: "user",
  content: "Generate a sunset over a mountain range."
};

await promptService.sendPrompt(prompt);
// Tool list contains "generateImage" definition
// Model calls it, adapter forwards to image API
// Resulting URL returned as tool message for continued conversation

Summary

  • Three-layer architecture: The UI layer (FunctionModeSelector.vue) selects the mode, the model layer (useFunctionModelManager) maintains the tool registry, and the adapter layer (openai-adapter.ts, gemini-adapter.ts) executes function calls.
  • Function modes: Basic, Pro, and Image modes isolate different toolsets, preventing inappropriate tool access during specific workflows.
  • Tool registration: Add custom tools by defining schemas and handlers in useFunctionModelManager, then expose them through the manager's resolution method.
  • Provider support: The system supports both OpenAI (via tools and function_call) and Gemini (via functionDeclarations) with normalized execution flows.
  • Persistence: Mode selections persist via UI_SETTINGS_KEYS.FUNCTION_MODE in local storage, ensuring consistent tool availability across sessions.

Frequently Asked Questions

How do I switch between function modes in prompt-optimizer?

Use the setFunctionMode function from useFunctionMode.ts to change modes programmatically, or interact with the FunctionModeSelector.vue component in the UI. The mode persists to local storage via UI_SETTINGS_KEYS.FUNCTION_MODE and automatically reinitializes the tool registry through resetFunctionModelManagerSingleton().

Can I use function calling with both OpenAI and Gemini models?

Yes. Prompt-optimizer implements provider-specific adapters in openai-adapter.ts and gemini-adapter.ts. Both support the function-calling protocol: OpenAI uses the tools parameter with function_call responses, while Gemini uses functionDeclarations. The adapter layer normalizes these into a common execution format.

How do I register a custom tool in prompt-optimizer?

Define the tool schema (name, description, parameters) in useFunctionModelManager.ts under your desired function mode (basic, pro, or image). Implement the handler function that executes the logic, then register it in the manager's handler resolution map. The LLM adapters will automatically detect and execute it when the model requests it by name.

What is the difference between Basic, Pro, and Image function modes?

Basic mode exposes essential tools like web search for simple information retrieval. Pro mode expands the toolkit to include file system operations and advanced data fetching capabilities. Image mode specifically exposes image generation tools like generateImage. Each mode isolates its tools to prevent the LLM from invoking inappropriate functions for the current workflow context.

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 →