# Function calling

> Let OpenAI, Anthropic, Gemini or any model with tool calling translate through FluentC. A tool definition, a handler that calls the REST API, and the full loop.

Source: https://www.fluentc.ai/docs/integrations/function-calling/

Any model that supports tool calling can use FluentC. You describe a `translate_text` tool to the model; when the model asks for it, your code calls the [REST API](https://www.fluentc.ai/docs/api/translate.md) and sends the result back. The model never sees your API key.

## The tool definition

This is the tool as a JSON Schema. It is the same for every provider; only the wrapper differs.

```json
{
  "name": "translate_text",
  "description": "Translate text into a target language using FluentC. Use it whenever the user asks for a translation or text must be shown in another language.",
  "parameters": {
    "type": "object",
    "properties": {
      "text": {
        "type": "string",
        "description": "The text to translate."
      },
      "target_language": {
        "type": "string",
        "description": "Lowercase language code, such as fr, es, de or ja."
      },
      "source_language": {
        "type": "string",
        "description": "Optional. Language code of the text, if known."
      }
    },
    "required": ["text", "target_language"]
  }
}
```

How to wrap it:

| Provider | Wrapper |
| --- | --- |
| OpenAI (Chat Completions) | `{ "type": "function", "function": <the definition above> }` in the `tools` array |
| Anthropic | `{ "name", "description", "input_schema": <the parameters object> }` in the `tools` array |
| Others | Use the provider's function declaration format with the same JSON Schema |

## The handler

The handler turns the model's arguments into a FluentC request. It returns the translation, or the error text, as the tool result.

```python
import os
import requests

def translate_text(text, target_language, source_language=None):
    body = {
        "input": text,
        "input_format": "text",
        "target_language": target_language,
        "mode": "real-time",
    }
    if source_language:
        body["source_language"] = source_language

    res = requests.post(
        "https://api.fluentc.ai/ai_agent/translate",
        headers={"Authorization": os.environ["FLUENTC_KEY"]},
        json=body,
        timeout=30,
    )
    data = res.json()
    if not res.ok:
        # Give the model the message so it can correct itself or tell the user.
        return {"error": data.get("error", f"HTTP {res.status_code}")}
    return {"translated_text": data["translation"]}
```

Returning errors as data, rather than raising, lets the model react: it can retry with a valid language code, or explain the problem. A key on the Batch plan can't use `real-time`; use `batch` and poll, as in [Batch results](https://www.fluentc.ai/docs/api/batch-results.md).

## The loop (OpenAI Chat Completions)

```python
import json
import os
from openai import OpenAI

client = OpenAI()
MODEL = os.environ["OPENAI_MODEL"]

TOOLS = [{
    "type": "function",
    "function": {
        "name": "translate_text",
        "description": "Translate text into a target language using FluentC.",
        "parameters": {
            "type": "object",
            "properties": {
                "text": {"type": "string", "description": "The text to translate."},
                "target_language": {"type": "string", "description": "Language code, such as fr."},
                "source_language": {"type": "string", "description": "Optional source language code."},
            },
            "required": ["text", "target_language"],
        },
    },
}]

messages = [{"role": "user", "content": "How do I say 'How may I help?' in Japanese?"}]

while True:
    response = client.chat.completions.create(model=MODEL, messages=messages, tools=TOOLS)
    message = response.choices[0].message
    messages.append(message)

    if not message.tool_calls:
        print(message.content)
        break

    for call in message.tool_calls:
        args = json.loads(call.function.arguments)
        result = translate_text(**args)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result, ensure_ascii=False),
        })
```

`ensure_ascii=False` keeps non-Latin text readable to the model.

## Same handler in Node

```js
export async function translateText({ text, target_language, source_language }) {
  const res = await fetch("https://api.fluentc.ai/ai_agent/translate", {
    method: "POST",
    headers: {
      Authorization: process.env.FLUENTC_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      input: text,
      input_format: "text",
      target_language,
      ...(source_language && { source_language }),
      mode: "real-time",
    }),
  });
  const data = await res.json();
  if (!res.ok) return { error: data.error ?? `HTTP ${res.status}` };
  return { translated_text: data.translation };
}
```

## Tips for reliable tool use

- **Validate languages up front.** Fetch [Languages](https://www.fluentc.ai/docs/api/languages.md) once and put the codes in the tool description or as an `enum`, so the model can't pick one your key doesn't have.
- **Keep the key server-side.** The handler runs in your code. Never put the key in a prompt or a tool description.
- **Repeats are cheap.** If the model asks for the same string twice, it is answered from [translation memory](https://www.fluentc.ai/docs/concepts.md#translation-memory).
- **Large content.** Translating a long document inside a conversation is slow. Hand the model a tool that submits a batch job and returns the `job_id`, and a second tool that fetches the result.
- **Prefer an MCP server when the client supports it.** The [MCP server](https://www.fluentc.ai/docs/integrations/mcp.md) gives the model these tools without a handler to maintain.
