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

> Easily integrate tools with prompt-optimizer using function calling and function mode. Expose custom tools to LLMs through a three-layer stack with UI selection and automatic registry.

- Repository: [且炼时光/prompt-optimizer](https://github.com/linshenkx/prompt-optimizer)
- Tags: how-to-guide
- Published: 2026-02-23

---

**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`](https://github.com/linshenkx/prompt-optimizer/blob/main/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.

```typescript
// 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.

```typescript
// 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`](https://github.com/linshenkx/prompt-optimizer/blob/main/openai-adapter.ts) and [`gemini-adapter.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/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.

```typescript
// 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`](https://github.com/linshenkx/prompt-optimizer/blob/main/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:

```typescript
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`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/ui/src/composables/model/useFunctionModelManager.ts) under the desired mode:

```typescript
{
  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:

```typescript
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

### Example 1: Switching to Pro Mode for Web Search

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

```typescript
// 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:

```typescript
// 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:

```typescript
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`](https://github.com/linshenkx/prompt-optimizer/blob/main/FunctionModeSelector.vue)) selects the mode, the model layer (`useFunctionModelManager`) maintains the tool registry, and the adapter layer ([`openai-adapter.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/openai-adapter.ts), [`gemini-adapter.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/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`](https://github.com/linshenkx/prompt-optimizer/blob/main/useFunctionMode.ts) to change modes programmatically, or interact with the [`FunctionModeSelector.vue`](https://github.com/linshenkx/prompt-optimizer/blob/main/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`](https://github.com/linshenkx/prompt-optimizer/blob/main/openai-adapter.ts) and [`gemini-adapter.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/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`](https://github.com/linshenkx/prompt-optimizer/blob/main/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.