SPB Git

Repository Public

function-calling.md 73.3 KB 2,206 lines markdown

1# Function calling23> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.45**Function calling** (also known as **tool calling**) provides a powerful and flexible way for OpenAI models to interface with external systems and access data outside their training data. This guide shows how you can connect a model to data and actions provided by your application. We'll show how to use function tools (defined by a JSON schema) and custom tools which work with free form text inputs and outputs.67For Agents API sessions, use [Functions](https://developers.openai.com/api/docs/guides/agents-api/tools/functions) to register functions and handle session action requests. The examples in this guide show the Responses API and Chat Completions integrations.89If your application has many functions or large schemas, you can pair function calling with [tool search](https://developers.openai.com/api/docs/guides/tools-tool-search) to defer rarely used tools and load them only when the model needs them. Only `gpt-5.4` and later models support `tool_search`.1011GPT-6 Astra requires the Responses API for tool calling. The Chat Completions12  examples use GPT-5.6 for compatibility. See the [migration13  guide](https://developers.openai.com/api/docs/guides/migrate-to-responses) to update an existing14  integration.1516## How it works1718Let's begin by understanding a few key terms about tool calling. After we have a shared vocabulary for tool calling, we'll show you how it's done with some practical examples.19202122### Tools - functionality we give the model23242526A **function** or **tool** refers in the abstract to a piece of functionality that we tell the model it has access to. As a model generates a response to a prompt, it may decide that it needs data or functionality provided by a tool to follow the prompt's instructions.2728You could give the model access to tools that:2930- Get today's weather for a location31- Access account details for a given user ID32- Issue refunds for a lost order3334Or anything else you'd like the model to be able to know or do as it responds to a prompt.3536When we make an API request to the model with a prompt, we can include a list of tools the model could consider using. For example, if we wanted the model to be able to answer questions about the current weather somewhere in the world, we might give it access to a `get_weather` tool that takes `location` as an argument.3738394041424344### Tool calls - requests from the model to use tools45464748A **function call** or **tool call** refers to a special kind of response we can get from the model if it examines a prompt, and then determines that in order to follow the instructions in the prompt, it needs to call one of the tools we made available to it.4950If the model receives a prompt like "what is the weather in Paris?" in an API request, it could respond to that prompt with a tool call for the `get_weather` tool, with `Paris` as the `location` argument.5152535455565758### Tool call outputs - output we generate for the model59606162A **function call output** or **tool call output** refers to the response a tool generates using the input from a model's tool call. The tool call output can either be structured JSON or plain text, and it should contain a reference to a specific model tool call (referenced by `call_id` in the examples to come).63To complete our weather example:6465- The model has access to a `get_weather` **tool** that takes `location` as an argument.66- In response to a prompt like "what's the weather in Paris?" the model returns a **tool call** that contains a `location` argument with a value of `Paris`67- The **tool call output** might return a JSON object (e.g., `{"temperature": "25", "unit": "C"}`, indicating a current temperature of 25 degrees), [Image contents](https://developers.openai.com/api/docs/guides/images-vision), or [File contents](https://developers.openai.com/api/docs/guides/file-inputs).6869We then send all of the tool definition, the original prompt, the model's tool call, and the tool call output back to the model to finally receive a text response like:7071```72The weather in Paris today is 25C.73```7475767778798081### Functions versus tools82838485- A function is a specific kind of tool, defined by a JSON schema. A function definition allows the model to pass data to your application, where your code can access data or take actions suggested by the model.86- In addition to function tools, there are custom tools (described in this guide) that work with free text inputs and outputs.87- There are also [built-in tools](https://developers.openai.com/api/docs/guides/tools) that are part of the OpenAI platform. These tools enable the model to [search the web](https://developers.openai.com/api/docs/guides/tools-web-search), [execute code](https://developers.openai.com/api/docs/guides/tools-code-interpreter), access the functionality of an [MCP server](https://developers.openai.com/api/docs/guides/tools-connectors-mcp), and more.888990919293### The tool calling flow9495Tool calling is a multi-step conversation between your application and a model via the OpenAI API. The tool calling flow has five high level steps:96971. Make a request to the model with tools it could call981. Receive a tool call from the model991. Execute code on the application side with input from the tool call1001. Make a second request to the model with the tool output1011. Receive a final response from the model (or more tool calls)102103![Function Calling Diagram Steps](https://cdn.openai.com/API/docs/images/function-calling-diagram-steps.png)104105With Responses, your application can continue this flow for as many tool calls as the task requires. If you want a framework that packages recurring orchestration around that loop, see [how the Responses API compares with the Agents SDK](https://developers.openai.com/api/docs/guides/agents#agents-sdk-vs-responses-api).106107## Function tool example108109Let's look at an end-to-end tool calling flow for a `get_horoscope` function that gets a daily horoscope for an astrological sign.110111112113  Complete tool calling example114115```javascript116import OpenAI from "openai";117import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";118119const openai = new OpenAI();120121// 1. Define a list of callable tools for the model122123const tools = [124  {125    type: "function",126    name: "get_horoscope",127    description: "Get today's horoscope for an astrological sign.",128    parameters: {129      type: "object",130      properties: {131        sign: {132          type: "string",133          description: "An astrological sign like Taurus or Aquarius",134        },135      },136      required: ["sign"],137      additionalProperties: false,138    },139    strict: true,140  },141];142143function getHoroscope(sign) {144  return `${sign}: Next Tuesday you will befriend a baby otter.`;145}146147// Create a running input list we will add to over time148149let input = [150  { role: "user", content: "What is my horoscope? I am an Aquarius." },151];152153// 2. Prompt the model with tools defined154let response = await openai.responses.create({155  model: "gpt-6-astra",156  tools,157  input,158});159160// Preserve model output for the next turn161input.push(...toResponseInputItems(response.output));162163for (const item of response.output) {164  if (item.type !== "function_call") continue;165166  if (item.name === "get_horoscope") {167    // 3. Execute the function logic for get_horoscope168    const { sign } = JSON.parse(item.arguments);169    const horoscope = getHoroscope(sign);170171    // 4. Provide function call results to the model172    input.push({173      type: "function_call_output",174      call_id: item.call_id,175      output: horoscope,176    });177  }178}179180console.log("Final input:");181console.log(JSON.stringify(input, null, 2));182183response = await openai.responses.create({184  model: "gpt-6-astra",185  instructions: "Respond only with a horoscope generated by a tool.",186  tools,187  input,188});189190// 5. The model should be able to give a response!191console.log("Final output:");192console.log(response.output_text);193```194195```python196from openai import OpenAI197import json198199client = OpenAI()200201# 1. Define a list of callable tools for the model202tools = [203    {204        "type": "function",205        "name": "get_horoscope",206        "description": "Get today's horoscope for an astrological sign.",207        "parameters": {208            "type": "object",209            "properties": {210                "sign": {211                    "type": "string",212                    "description": "An astrological sign like Taurus or Aquarius",213                },214            },215            "required": ["sign"],216        },217    },218]219220221def get_horoscope(sign):222    return f"{sign}: Next Tuesday you will befriend a baby otter."223224225# Create a running input list we will add to over time226input_list = [{"role": "user", "content": "What is my horoscope? I am an Aquarius."}]227228# 2. Prompt the model with tools defined229response = client.responses.create(230    model="gpt-6-astra",231    tools=tools,232    input=input_list,233)234235# Save function call outputs for subsequent requests236input_list += response.output237238for item in response.output:239    if item.type == "function_call":240        if item.name == "get_horoscope":241            # 3. Execute the function logic for get_horoscope242            sign = json.loads(item.arguments)["sign"]243            horoscope = get_horoscope(sign)244245            # 4. Provide function call results to the model246            input_list.append(247                {248                    "type": "function_call_output",249                    "call_id": item.call_id,250                    "output": horoscope,251                }252            )253254print("Final input:")255print(input_list)256257response = client.responses.create(258    model="gpt-6-astra",259    instructions="Respond only with a horoscope generated by a tool.",260    tools=tools,261    input=input_list,262)263264# 5. The model should be able to give a response!265print("Final output:")266print(response.model_dump_json(indent=2))267print("\n" + response.output_text)268```269270```go271package main272273import (274	"context"275	"encoding/json"276	"fmt"277278	"github.com/openai/openai-go/v3"279	"github.com/openai/openai-go/v3/responses"280)281282func main() {283	client := openai.NewClient()284	tool := horoscopeResponseTool()285	response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{286		Model: "gpt-6-astra",287		Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("What is my horoscope? I am an Aquarius.")},288		Tools: []responses.ToolUnionParam{tool},289	})290	if err != nil {291		panic(err)292	}293294	var functionOutput responses.ResponseInputItemUnionParam295	for _, output := range response.Output {296		if output.Type != "function_call" {297			continue298		}299		call := output.AsFunctionCall()300		if call.Name != "get_horoscope" {301			continue302		}303		var arguments struct {304			Sign string `json:"sign"`305		}306		if err := json.Unmarshal([]byte(call.Arguments), &arguments); err != nil {307			panic(err)308		}309		functionOutput = responses.ResponseInputItemParamOfFunctionCallOutput(getHoroscope(arguments.Sign))310		functionOutput.OfFunctionCallOutput.CallID = openai.String(call.CallID)311	}312	if functionOutput.OfFunctionCallOutput == nil {313		panic("the model did not call get_horoscope")314	}315316	response, err = client.Responses.New(context.Background(), responses.ResponseNewParams{317		Model:              "gpt-6-astra",318		PreviousResponseID: openai.String(response.ID),319		Instructions:       openai.String("Respond only with a horoscope generated by a tool."),320		Input:              responses.ResponseNewParamsInputUnion{OfInputItemList: responses.ResponseInputParam{functionOutput}},321		Tools:              []responses.ToolUnionParam{tool},322	})323	if err != nil {324		panic(err)325	}326	fmt.Println(response.OutputText())327}328329func horoscopeResponseTool() responses.ToolUnionParam {330	parameters := map[string]any{331		"type": "object",332		"properties": map[string]any{333			"sign": map[string]any{"type": "string", "description": "An astrological sign like Taurus or Aquarius"},334		},335		"required":             []string{"sign"},336		"additionalProperties": false,337	}338	tool := responses.ToolParamOfFunction("get_horoscope", parameters, true)339	tool.OfFunction.Description = openai.String("Get today's horoscope for an astrological sign.")340	return tool341}342343func getHoroscope(sign string) string {344	return fmt.Sprintf("%s: Next Tuesday you will befriend a baby otter.", sign)345}346```347348```java349import com.openai.client.OpenAIClient;350import com.openai.client.okhttp.OpenAIOkHttpClient;351import com.openai.core.JsonValue;352import com.openai.models.responses.FunctionTool;353import com.openai.models.responses.ResponseCreateParams;354import com.openai.models.responses.ResponseInputItem;355import java.util.List;356import java.util.Map;357358FunctionTool horoscope =359    FunctionTool.builder()360        .name("get_horoscope")361        .description("Get today's horoscope for an astrological sign.")362        .parameters(363            FunctionTool.Parameters.builder()364                .putAdditionalProperty("type", JsonValue.from("object"))365                .putAdditionalProperty(366                    "properties",367                    JsonValue.from(368                        Map.of(369                            "sign",370                            Map.of(371                                "type", "string",372                                "description",373                                    "An astrological sign like Taurus or Aquarius"))))374                .putAdditionalProperty("required", JsonValue.from(List.of("sign")))375                .putAdditionalProperty("additionalProperties", JsonValue.from(false))376                .build())377        .strict(true)378        .build();379380var firstResponse =381    client382        .responses()383        .create(384            ResponseCreateParams.builder()385                .model("gpt-6-astra")386                .input("What is my horoscope? I am an Aquarius.")387                .addTool(horoscope)388                .build());389390var functionCall =391    firstResponse.output().stream()392        .flatMap(item -> item.functionCall().stream())393        .filter(call -> call.name().equals("get_horoscope"))394        .findFirst()395        .orElseThrow(() -> new IllegalStateException("The model did not call get_horoscope"));396397record HoroscopeArguments(String sign) {}398399String sign = functionCall.arguments(HoroscopeArguments.class).sign();400ResponseCreateParams followUp =401    ResponseCreateParams.builder()402        .model("gpt-6-astra")403        .instructions("Respond only with a horoscope generated by a tool.")404        .previousResponseId(firstResponse.id())405        .inputOfResponse(406            List.of(407                ResponseInputItem.ofFunctionCallOutput(408                    ResponseInputItem.FunctionCallOutput.builder()409                        .callId(functionCall.callId())410                        .output(sign + ": Embrace an unexpected opportunity today.")411                        .build())))412        .addTool(horoscope)413        .build();414415client.responses().create(followUp).output().stream()416    .flatMap(item -> item.message().stream())417    .flatMap(message -> message.content().stream())418    .flatMap(content -> content.outputText().stream())419    .forEach(text -> System.out.println(text.text()));420```421422```ruby423require "json"424require "openai"425426client = OpenAI::Client.new427tools = [428  {429    type: :function,430    name: "get_horoscope",431    description: "Get today's horoscope for an astrological sign.",432    parameters: {433      type: :object,434      properties: { sign: { type: :string } },435      required: ["sign"],436      additionalProperties: false437    },438    strict: true439  }440]441442first_response = client.responses.create(443  model: "gpt-6-astra",444  input: "What is my horoscope? I am an Aquarius.",445  tools: tools446)447function_call = first_response.output.find do |item|448  item.is_a?(OpenAI::Models::Responses::ResponseFunctionToolCall) &&449    item.name == "get_horoscope"450end451unless function_call.is_a?(OpenAI::Models::Responses::ResponseFunctionToolCall)452  raise "The model did not call get_horoscope"453end454455arguments = JSON.parse(function_call.arguments, symbolize_names: true)456sign = arguments.fetch(:sign)457response = client.responses.create(458  model: "gpt-6-astra",459  previous_response_id: first_response.id,460  input: [461    {462      type: :function_call_output,463      call_id: function_call.call_id,464      output: "#{sign}: Embrace an unexpected opportunity today."465    }466  ],467  tools: tools468)469470puts(response.output_text)471```472473474475Note that for reasoning models like GPT-5 or o4-mini, any reasoning items476  returned in model responses with tool calls must also be passed back with tool477  call outputs.478479## Defining functions480481Functions are usually declared in the `tools` parameter of each API request. With [tool search](https://developers.openai.com/api/docs/guides/tools-tool-search), your application can also load deferred functions later in the interaction. Either way, each callable function uses the same schema shape. A function definition has the following properties:482483| Field         | Description                                                                     |484| ------------- | ------------------------------------------------------------------------------- |485| `type`        | This should always be `function`                                                |486| `name`        | The function's name (for example, `get_weather`)                                |487| `description` | Details on when and how to use the function                                     |488| `parameters`  | [JSON schema](https://json-schema.org/) defining the function's input arguments |489| `strict`      | Whether to enforce strict mode for the function call                            |490491Here is an example function definition for a `get_weather` function492493```json494{495  "type": "function",496  "name": "get_weather",497  "description": "Retrieves current weather for the given location.",498  "parameters": {499    "type": "object",500    "properties": {501      "location": {502        "type": "string",503        "description": "City and country e.g. Bogotá, Colombia"504      },505      "units": {506        "type": "string",507        "enum": ["celsius", "fahrenheit"],508        "description": "Units the temperature will be returned in."509      }510    },511    "required": ["location", "units"],512    "additionalProperties": false513  },514  "strict": true515}516```517518Because the `parameters` are defined by a [JSON schema](https://json-schema.org/), you can leverage many of its rich features like property types, enums, descriptions, nested objects, and recursive objects.519520## Defining namespaces521522Use namespaces to group related tools by domain, such as `crm`, `billing`, or `shipping`. Namespaces help organize similar tools and are especially useful when the model must choose between tools that serve different systems or purposes, such as one search tool for your CRM and another for your support ticketing system.523524```json525{526  "type": "namespace",527  "name": "crm",528  "description": "CRM tools for customer lookup and order management.",529  "tools": [530    {531      "type": "function",532      "name": "get_customer_profile",533      "description": "Fetch a customer profile by customer ID.",534      "parameters": {535        "type": "object",536        "properties": {537          "customer_id": { "type": "string" }538        },539        "required": ["customer_id"],540        "additionalProperties": false541      }542    },543    {544      "type": "function",545      "name": "list_open_orders",546      "description": "List open orders for a customer ID.",547      "defer_loading": true,548      "parameters": {549        "type": "object",550        "properties": {551          "customer_id": { "type": "string" }552        },553        "required": ["customer_id"],554        "additionalProperties": false555      }556    }557  ]558}559```560561## Tool search562563If you need to give the model access to a large ecosystem of tools, you can defer loading some or all of those tools with `tool_search`. The `tool_search` tool lets the model search for relevant tools, add them to the model context, and then use them. Only `gpt-5.4` and later models support it. Read the [tool search guide](https://developers.openai.com/api/docs/guides/tools-tool-search) to learn more.564565566567### Best practices for defining functions5685691. **Write clear and detailed function names, parameter descriptions, and instructions.**570   - **Explicitly describe the purpose of the function and each parameter** (and its format), and what the output represents.571   - **Use the system prompt to describe when (and when not) to use each function.** Generally, tell the model _exactly_ what to do.572   - **Include examples and edge cases**, especially to rectify any recurring failures. (**Note:** Adding examples may hurt performance for [reasoning models](https://developers.openai.com/api/docs/guides/reasoning).)573   - **For deferred tools, put detailed guidance in the function description and keep the namespace description concise.** The namespace helps the model choose what to load; the function description helps it use the loaded tool correctly.5745751. **Apply software engineering best practices.**576   - **Make the functions predictable and intuitive**. ([principle of least surprise](https://en.wikipedia.org/wiki/Principle_of_least_astonishment))577   - **Use enums** and object structure to prevent invalid states. For example, `toggle_light(on: bool, off: bool)` allows for invalid calls.578   - **Pass the intern test.** Can an intern/human correctly use the function given nothing but what you gave the model? (If not, what questions do they ask you? Add the answers to the prompt.)5795801. **Offload the burden from the model and use code where possible.**581   - **Don't make the model fill arguments you already know.** For example, if you already have an `order_id` based on a previous menu, don't include an `order_id` parameter. Instead, define `submit_refund()` with no parameters and pass the `order_id` in your code.582   - **Combine functions that are always called in sequence.** For example, if you always call `mark_location()` after `query_location()`, just move the marking logic into the query function call.5835841. **Keep the number of initially available functions small for higher accuracy.**585   - **Evaluate your performance** with different numbers of functions.586   - **Aim for fewer than 20 functions available at the start of a turn** at any one time, though this is just a soft suggestion.587   - **Use tool search** to defer large or infrequently used parts of your tool surface instead of exposing everything up front.5885891. **Leverage OpenAI resources.**590   - **Generate and iterate on function schemas** in the [Playground](https://platform.openai.com/playground).591   - **Consider [fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization) to increase function calling accuracy** for large numbers of functions or difficult tasks. ([cookbook](https://developers.openai.com/cookbook/examples/fine_tuning_for_function_calling))592593### Token Usage594595Under the hood, functions are injected into the system message in a syntax the model has been trained on. This means callable function definitions count against the model's context limit and are billed as input tokens. If you run into token limits, we suggest limiting the number of functions loaded up front, shortening descriptions where possible, or using [tool search](https://developers.openai.com/api/docs/guides/tools-tool-search) so deferred tools are loaded only when needed.596597It is also possible to use [fine-tuning](https://developers.openai.com/api/docs/guides/model-optimization#fine-tuning-examples) to reduce the number of tokens used if you have many functions defined in your tools specification.598599## Handling function calls600601When the model calls a function, you must execute it and return the result. Since model responses can include zero, one, or multiple calls, it is best practice to assume there are several.602603604605The response `output` array contains an entry with the `type` having a value of `function_call`. Each entry with a `call_id` (used later to submit the function result), `name`, and JSON-encoded `arguments`.606607Sample response with multiple function calls608609```json610[611    {612        "id": "fc_12345xyz",613        "call_id": "call_12345xyz",614        "type": "function_call",615        "name": "get_weather",616        "arguments": "{\"location\":\"Paris, France\"}"617    },618    {619        "id": "fc_67890abc",620        "call_id": "call_67890abc",621        "type": "function_call",622        "name": "get_weather",623        "arguments": "{\"location\":\"Bogotá, Colombia\"}"624    },625    {626        "id": "fc_99999def",627        "call_id": "call_99999def",628        "type": "function_call",629        "name": "send_email",630        "arguments": "{\"to\":\"bob@email.com\",\"body\":\"Hi bob\"}"631    }632]633```634635636If you are using [tool search](https://developers.openai.com/api/docs/guides/tools-tool-search), you may also see `tool_search_call` and `tool_search_output` items before a `function_call`. Once the function is loaded, handle the function call in the same way shown here.637638Execute function calls and append results639640```javascript641import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";642643input.push(...toResponseInputItems(response.output));644645for (const toolCall of response.output) {646  if (toolCall.type !== "function_call") {647    continue;648  }649650  const name = toolCall.name;651  const args = JSON.parse(toolCall.arguments);652653  const result = await callFunction(name, args);654  input.push({655    type: "function_call_output",656    call_id: toolCall.call_id,657    output: result.toString(),658  });659}660```661662```python663input_messages += response.output664665for tool_call in response.output:666    if tool_call.type != "function_call":667        continue668669    name = tool_call.name670    args = json.loads(tool_call.arguments)671672    result = call_function(name, args)673    input_messages.append(674        {675            "type": "function_call_output",676            "call_id": tool_call.call_id,677            "output": json.dumps(result),678        }679    )680```681682```go683input = append(input, responseOutputAsInput(response.Output)...)684685for _, output := range response.Output {686	if output.Type != "function_call" {687		continue688	}689	toolCall := output.AsFunctionCall()690	var arguments functionArguments691	if err := json.Unmarshal([]byte(toolCall.Arguments), &arguments); err != nil {692		panic(err)693	}694	result, err := callFunction(toolCall.Name, arguments)695	if err != nil {696		panic(err)697	}698	toolOutput := responses.ResponseInputItemParamOfFunctionCallOutput(result)699	toolOutput.OfFunctionCallOutput.CallID = openai.String(toolCall.CallID)700	input = append(input, toolOutput)701}702```703704```java705import com.openai.client.OpenAIClient;706import com.openai.client.okhttp.OpenAIOkHttpClient;707import com.openai.core.JsonValue;708import com.openai.models.responses.EasyInputMessage;709import com.openai.models.responses.FunctionTool;710import com.openai.models.responses.ResponseCreateParams;711import com.openai.models.responses.ResponseInputItem;712import java.util.ArrayList;713import java.util.List;714import java.util.Map;715716response.output().stream()717    .map(item -> JsonValue.from(item).convert(ResponseInputItem.class))718    .forEach(input::add);719response.output().stream()720    .flatMap(item -> item.functionCall().stream())721    .forEach(722        call -> {723          String result;724          if (call.name().equals("get_weather")) {725            record Coordinates(double latitude, double longitude) {}726727            Coordinates coordinates = call.arguments(Coordinates.class);728            result =729                JsonValue.from(730                        Map.of(731                            "latitude", coordinates.latitude(),732                            "longitude", coordinates.longitude(),733                            "temperature_c", 18))734                    .toString();735          } else if (call.name().equals("send_email")) {736            record Email(String to, String body) {}737738            Email message = call.arguments(Email.class);739            result = JsonValue.from(Map.of("to", message.to(), "status", "sent")).toString();740          } else {741            throw new IllegalArgumentException("Unknown function: " + call.name());742          }743          var output =744              ResponseInputItem.ofFunctionCallOutput(745                  ResponseInputItem.FunctionCallOutput.builder()746                      .callId(call.callId())747                      .output(result)748                      .build());749          input.add(output);750          System.out.println(call.callId() + " " + result);751        });752```753754```ruby755input.concat(response.output)756757response.output.each do |tool_call|758  next unless tool_call.is_a?(OpenAI::Models::Responses::ResponseFunctionToolCall)759760  arguments = JSON.parse(tool_call.arguments)761  result = call_function(tool_call.name, arguments)762763  input << {764    type: :function_call_output,765    call_id: tool_call.call_id,766    output: JSON.generate(result)767  }768end769```770771772773In the example above, we have a hypothetical `call_function` to route each call. Here’s a possible implementation:774775Execute function calls and append results776777```javascript778const callFunction = async (name, args) => {779  if (name === "get_weather") {780    return getWeather(args.latitude, args.longitude);781  }782  if (name === "send_email") {783    return sendEmail(args.to, args.body);784  }785  throw new Error(`Unknown function: ${name}`);786};787```788789```python790def call_function(name, args):791    if name == "get_weather":792        return get_weather(**args)793    if name == "send_email":794        return send_email(**args)795    raise ValueError(f"Unknown function: {name}")796```797798```go799func callFunction(name string, arguments functionArguments) (string, error) {800	switch name {801	case "get_weather":802		return getWeather(arguments.Location), nil803	case "send_email":804		return sendEmail(arguments.To, arguments.Body), nil805	default:806		return "", fmt.Errorf("unknown function: %s", name)807	}808}809```810811```ruby812def call_function(name, arguments)813  case name814  when "get_weather"815    FunctionCallingExample.get_weather(816      arguments.fetch("latitude"),817      arguments.fetch("longitude")818    )819  when "send_email"820    FunctionCallingExample.send_email(821      arguments.fetch("to"),822      arguments.fetch("body")823    )824  else825    raise ArgumentError, "Unknown function: #{name}"826  end827end828```829830831### Formatting results832833The result you pass in the `function_call_output` message should typically be a string, where the format is up to you (JSON, error codes, plain text, etc.). The model will interpret that string as needed.834835For functions that return images or files, you can pass an [array of image or file objects](https://developers.openai.com/api/reference/resources/responses/methods/create#responses_create-input-input_item_list-item-function_tool_call_output-output) instead of a string.836837If your function has no return value (for example, `send_email`), return a string that indicates success or failure, such as `"success"`.838839### Incorporating results into response840841842843After appending the results to your `input`, you can send them back to the model to get a final response.844845Send results back to model846847```javascript848const response = await openai.responses.create({849  model: "gpt-6-astra",850  input,851  tools,852});853```854855```python856response = client.responses.create(857    model="gpt-6-astra",858    input=input_messages,859    tools=responses_tools,860)861862print(response.output_text)863```864865```go866response, err = client.Responses.New(context.Background(), responses.ResponseNewParams{867	Model: "gpt-6-astra",868	Input: responses.ResponseNewParamsInputUnion{OfInputItemList: input},869	Tools: tools,870})871if err != nil {872	panic(err)873}874```875876```java877import com.openai.client.OpenAIClient;878import com.openai.client.okhttp.OpenAIOkHttpClient;879import com.openai.core.JsonValue;880import com.openai.models.responses.EasyInputMessage;881import com.openai.models.responses.FunctionTool;882import com.openai.models.responses.ResponseCreateParams;883import com.openai.models.responses.ResponseFunctionToolCall;884import com.openai.models.responses.ResponseInputItem;885import java.util.List;886import java.util.Map;887888FunctionTool weather =889    FunctionTool.builder()890        .name("get_weather")891        .description("Get the weather for a city.")892        .parameters(893            FunctionTool.Parameters.builder()894                .putAdditionalProperty("type", JsonValue.from("object"))895                .putAdditionalProperty(896                    "properties", JsonValue.from(Map.of("city", Map.of("type", "string"))))897                .putAdditionalProperty("required", JsonValue.from(List.of("city")))898                .putAdditionalProperty("additionalProperties", JsonValue.from(false))899                .build())900        .strict(true)901        .build();902903ResponseCreateParams params =904    ResponseCreateParams.builder()905        .model("gpt-6-astra")906        .inputOfResponse(907            List.of(908                ResponseInputItem.ofEasyInputMessage(909                    EasyInputMessage.builder()910                        .role(EasyInputMessage.Role.USER)911                        .content("What is the weather like in Paris?")912                        .build()),913                ResponseInputItem.ofFunctionCall(914                    ResponseFunctionToolCall.builder()915                        .callId("call_weather")916                        .name("get_weather")917                        .arguments("{\"city\":\"Paris\"}")918                        .build()),919                ResponseInputItem.ofFunctionCallOutput(920                    ResponseInputItem.FunctionCallOutput.builder()921                        .callId("call_weather")922                        .output("{\"city\":\"Paris\",\"temperature_c\":18}")923                        .build())))924        .addTool(weather)925        .build();926927client.responses().create(params).output().stream()928    .flatMap(item -> item.message().stream())929    .flatMap(message -> message.content().stream())930    .flatMap(content -> content.outputText().stream())931    .forEach(text -> System.out.println(text.text()));932```933934```ruby935require "openai"936937client = OpenAI::Client.new938input = [939  {940    role: :user,941    content: "What is the weather like in Paris?"942  },943  {944    type: :function_call,945    call_id: "call_weather",946    name: "get_weather",947    arguments: '{"city":"Paris"}'948  },949  {950    type: :function_call_output,951    call_id: "call_weather",952    output: '{"city":"Paris","temperature_c":18}'953  }954]955tools = [956  {957    type: :function,958    name: "get_weather",959    description: "Get the weather for a city",960    parameters: {961      type: :object,962      properties: { city: { type: :string } },963      required: ["city"],964      additionalProperties: false965    },966    strict: true967  }968]969response = client.responses.create(970  model: "gpt-6-astra",971  input: input,972  tools: tools973)974975puts(response.output_text)976```977978979980Final response981982```json983"It's about 15°C in Paris, 18°C in Bogotá, and I've sent that email to Bob."984```985986987## Additional configurations988989### Tool choice990991By default the model will determine when and how many tools to use. You can force specific behavior with the `tool_choice` parameter.9929931. **Auto:** (_Default_) Call zero, one, or multiple functions. `tool_choice: "auto"`9941. **Required:** Call one or more functions.995   `tool_choice: "required"`9961. **Forced Function:** Call exactly one specific function.997   `tool_choice: {"type": "function", "name": "get_weather"}`9981. **Allowed tools:** Restrict the tool calls the model can make to a subset of999   the tools available to the model.10001001**When to use `allowed_tools`**10021003You might want to configure an `allowed_tools` list in case you want to make only1004a subset of tools available across model requests, but not modify the list of tools you pass in, so you can maximize savings from [prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching).10051006```json1007"tool_choice": {1008    "type": "allowed_tools",1009    "mode": "auto",1010    "tools": [1011        { "type": "function", "name": "get_weather" },1012        { "type": "function", "name": "search_docs" }1013    ]1014  }1015}1016```10171018You can also set `tool_choice` to `"none"` to imitate the behavior of passing no functions.10191020When you use tool search, `tool_choice` still applies to the tools that are currently callable in the turn. This is most useful after you load a subset of tools and want to constrain the model to that subset.10211022### Parallel function calling10231024On supported models beginning with GPT-5, functions can be called in parallel1025  when [built-in tools](https://developers.openai.com/api/docs/guides/tools) are also available. Built-in1026  tools cannot be included in a parallel function-call batch.10271028The model may choose to call multiple functions in a single turn. You can prevent this by setting `parallel_tool_calls` to `false`, which ensures exactly zero or one tool is called.10291030**Note:** Currently, if you are using a fine tuned model and the model calls multiple functions in one turn then [strict mode](#strict-mode) will be disabled for those calls.10311032**Note for `gpt-4.1-nano-2025-04-14`:** This snapshot of `gpt-4.1-nano` can sometimes include multiple tool calls for the same tool if parallel tool calls are enabled. It is recommended to disable this feature when using this snapshot.10331034### Strict mode10351036Setting `strict` to `true` will ensure function calls reliably adhere to the function schema, instead of being best effort. We recommend always enabling strict mode.10371038Under the hood, strict mode works by leveraging our [structured outputs](https://developers.openai.com/api/docs/guides/structured-outputs) feature and therefore introduces a couple requirements:103910401. `additionalProperties` must be set to `false` for each object in the `parameters`.10411. All fields in `properties` must be marked as `required`.10421043You can denote optional fields by adding `null` as a `type` option (see example below).10441045If you send `strict: true` and your schema does not meet the requirements above,1046the request will be rejected with details about the missing constraints. If1047you omit `strict`, the default depends on the API: Responses requests will1048attempt to normalize your schema into strict mode when possible, and will fall1049back to non-strict, best-effort function calling if the schema cannot be made1050compatible with strict mode. When fallback happens, the response tool will show1051`strict: false`. Chat Completions requests remain non-strict by default. To opt1052out of strict mode in Responses and keep non-strict, best-effort function1053calling, explicitly set `strict: false`.105410551056105710581059Strict mode enabled10601061```json1062{1063    "type": "function",1064    "name": "get_weather",1065    "description": "Retrieves current weather for the given location.",1066    //highlight-start1067    "strict": true,1068    //highlight-end1069    "parameters": {1070        "type": "object",1071        "properties": {1072            "location": {1073                "type": "string",1074                "description": "City and country e.g. Bogotá, Colombia"1075            },1076            "units": {1077                //highlight-start1078                "type": ["string", "null"],1079                //highlight-end1080                "enum": ["celsius", "fahrenheit"],1081                "description": "Units the temperature will be returned in."1082            }1083        },1084        //highlight-start1085        "required": ["location", "units"],1086        "additionalProperties": false1087        //highlight-end1088    }1089}1090```10911092  10931094  10951096    1097Strict mode disabled10981099```json1100{1101    "type": "function",1102    "name": "get_weather",1103    "description": "Retrieves current weather for the given location.",1104    "parameters": {1105        "type": "object",1106        "properties": {1107            "location": {1108                "type": "string",1109                "description": "City and country e.g. Bogotá, Colombia"1110            },1111            "units": {1112                //highlight-start1113                "type": "string",1114                //highlight-end1115                "enum": ["celsius", "fahrenheit"],1116                "description": "Units the temperature will be returned in."1117            }1118        },1119        //highlight-start1120        "required": ["location"],1121        //highlight-end1122    }1123}1124```112511261127112811291130All schemas generated in the1131  [playground](https://platform.openai.com/playground) have strict mode enabled.11321133While we recommend you enable strict mode, it has a few limitations:113411351. Some features of JSON schema are not supported. (See [supported schemas](https://developers.openai.com/api/docs/guides/structured-outputs?context=with_parse#supported-schemas).)11361137Specifically for fine tuned models:113811391. Schemas undergo additional processing on the first request (and are then cached). If your schemas vary from request to request, this may result in higher latencies.11402. Schemas are cached for performance, and are not eligible for [zero data retention](https://developers.openai.com/api/docs/models#how-we-use-your-data).11411142## Streaming1143114411451146Streaming can be used to surface progress by showing which function is called as the model fills its arguments, and even displaying the arguments in real time.11471148Streaming function calls is very similar to streaming regular responses: you set `stream` to `true` and get different `event` objects.11491150Streaming function calls11511152```javascript1153import { OpenAI } from "openai";11541155const openai = new OpenAI();11561157const tools = [1158  {1159    type: "function",1160    name: "get_weather",1161    description: "Get current temperature for provided coordinates in celsius.",1162    parameters: {1163      type: "object",1164      properties: {1165        latitude: { type: "number" },1166        longitude: { type: "number" },1167      },1168      required: ["latitude", "longitude"],1169      additionalProperties: false,1170    },1171    strict: true,1172  },1173];11741175const stream = await openai.responses.create({1176  model: "gpt-6-astra",1177  input: [{ role: "user", content: "What's the weather like in Paris today?" }],1178  tools,1179  stream: true,1180  store: true,1181});11821183for await (const event of stream) {1184  console.log(event);1185}1186```11871188```python1189from openai import OpenAI11901191client = OpenAI()11921193tools = [1194    {1195        "type": "function",1196        "name": "get_weather",1197        "description": "Get current temperature for a given location.",1198        "parameters": {1199            "type": "object",1200            "properties": {1201                "location": {1202                    "type": "string",1203                    "description": "City and country e.g. Bogotá, Colombia",1204                }1205            },1206            "required": ["location"],1207            "additionalProperties": False,1208        },1209    }1210]12111212stream = client.responses.create(1213    model="gpt-6-astra",1214    input=[{"role": "user", "content": "What's the weather like in Paris today?"}],1215    tools=tools,1216    stream=True,1217)12181219for event in stream:1220    print(event)1221```12221223```go1224package main12251226import (1227	"context"1228	"fmt"12291230	"github.com/openai/openai-go/v3"1231	"github.com/openai/openai-go/v3/responses"1232)12331234func main() {1235	client := openai.NewClient()1236	parameters := map[string]any{1237		"type": "object",1238		"properties": map[string]any{1239			"location": map[string]any{"type": "string", "description": "City and country e.g. Bogotá, Colombia"},1240		},1241		"required":             []string{"location"},1242		"additionalProperties": false,1243	}1244	tool := responses.ToolParamOfFunction("get_weather", parameters, true)1245	stream := client.Responses.NewStreaming(context.Background(), responses.ResponseNewParams{1246		Model: "gpt-6-astra",1247		Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("What's the weather like in Paris today?")},1248		Tools: []responses.ToolUnionParam{tool},1249	})1250	for stream.Next() {1251		fmt.Println(stream.Current().Type)1252	}1253	if err := stream.Err(); err != nil {1254		panic(err)1255	}1256}1257```12581259```java1260import com.openai.client.OpenAIClient;1261import com.openai.client.okhttp.OpenAIOkHttpClient;1262import com.openai.core.JsonValue;1263import com.openai.core.http.StreamResponse;1264import com.openai.models.responses.FunctionTool;1265import com.openai.models.responses.ResponseCreateParams;1266import com.openai.models.responses.ResponseStreamEvent;1267import java.util.List;1268import java.util.Map;12691270FunctionTool weather =1271    FunctionTool.builder()1272        .name("get_weather")1273        .description("Get the weather for a city.")1274        .parameters(1275            FunctionTool.Parameters.builder()1276                .putAdditionalProperty("type", JsonValue.from("object"))1277                .putAdditionalProperty(1278                    "properties", JsonValue.from(Map.of("city", Map.of("type", "string"))))1279                .putAdditionalProperty("required", JsonValue.from(List.of("city")))1280                .putAdditionalProperty("additionalProperties", JsonValue.from(false))1281                .build())1282        .strict(true)1283        .build();1284ResponseCreateParams params =1285    ResponseCreateParams.builder()1286        .model("gpt-6-astra")1287        .input("What is the weather in Paris?")1288        .addTool(weather)1289        .build();12901291try (StreamResponse<ResponseStreamEvent> stream = client.responses().createStreaming(params)) {1292  stream.stream()1293      .forEach(1294          event -> {1295            System.out.println(event);1296            event1297                .outputItemAdded()1298                .ifPresent(added -> System.out.println("response.output_item.added: " + added));1299            event1300                .functionCallArgumentsDelta()1301                .ifPresent(1302                    delta ->1303                        System.out.println("response.function_call_arguments.delta: " + delta));1304          });1305}1306```13071308```ruby1309require "openai"13101311client = OpenAI::Client.new1312stream = client.responses.stream(1313  model: "gpt-6-astra",1314  input: "What is the weather in Paris?",1315  tools: [1316    {1317      type: :function,1318      name: "get_weather",1319      description: "Get the weather for a city",1320      parameters: {1321        type: :object,1322        properties: { city: { type: :string } },1323        required: ["city"],1324        additionalProperties: false1325      },1326      strict: true1327    }1328  ]1329)13301331stream.each { |event| puts(event.type) }1332```133313341335Output events13361337```json1338{"type":"response.output_item.added","response_id":"resp_1234xyz","output_index":0,"item":{"type":"function_call","id":"fc_1234xyz","call_id":"call_1234xyz","name":"get_weather","arguments":""}}1339{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":"{\""}1340{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":"location"}1341{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":"\":\""}1342{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":"Paris"}1343{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":","}1344{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":" France"}1345{"type":"response.function_call_arguments.delta","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"delta":"\"}"}1346{"type":"response.function_call_arguments.done","response_id":"resp_1234xyz","item_id":"fc_1234xyz","output_index":0,"arguments":"{\"location\":\"Paris, France\"}"}1347{"type":"response.output_item.done","response_id":"resp_1234xyz","output_index":0,"item":{"type":"function_call","id":"fc_1234xyz","call_id":"call_1234xyz","name":"get_weather","arguments":"{\"location\":\"Paris, France\"}"}}1348```134913501351Instead of aggregating chunks into a single `content` string, however, you're aggregating chunks into an encoded `arguments` JSON object.13521353When the model calls one or more functions an event of type `response.output_item.added` will be emitted for each function call that contains the following fields:13541355| Field          | Description                                                                                                  |1356| -------------- | ------------------------------------------------------------------------------------------------------------ |1357| `response_id`  | The id of the response that the function call belongs to                                                     |1358| `output_index` | The index of the output item in the response. This represents the individual function calls in the response. |1359| `item`         | The in-progress function call item that includes a `name`, `arguments` and `id` field                        |13601361Afterwards you will receive a series of events of type `response.function_call_arguments.delta` which will contain the `delta` of the `arguments` field. These events contain the following fields:13621363| Field          | Description                                                                                                  |1364| -------------- | ------------------------------------------------------------------------------------------------------------ |1365| `response_id`  | The id of the response that the function call belongs to                                                     |1366| `item_id`      | The id of the function call item that the delta belongs to                                                   |1367| `output_index` | The index of the output item in the response. This represents the individual function calls in the response. |1368| `delta`        | The delta of the `arguments` field.                                                                          |13691370Below is a code snippet demonstrating how to aggregate the `delta`s into a final `tool_call` object.13711372Accumulating tool_call deltas13731374```javascript1375const finalToolCalls = {};13761377for await (const event of stream) {1378  if (1379    event.type === "response.output_item.added" &&1380    event.item.type === "function_call"1381  ) {1382    finalToolCalls[event.output_index] = event.item;1383  } else if (event.type === "response.function_call_arguments.delta") {1384    const index = event.output_index;13851386    if (finalToolCalls[index]) {1387      finalToolCalls[index].arguments += event.delta;1388    }1389  }1390}1391```13921393```python1394final_tool_calls = {}13951396for event in stream:1397    if event.type == "response.output_item.added":1398        final_tool_calls[event.output_index] = event.item1399    elif event.type == "response.function_call_arguments.delta":1400        index = event.output_index14011402        if final_tool_calls[index]:1403            final_tool_calls[index].arguments += event.delta1404```14051406```go1407package main14081409import (1410	"context"1411	"fmt"14121413	"github.com/openai/openai-go/v3"1414	"github.com/openai/openai-go/v3/responses"1415)14161417func main() {1418	client := openai.NewClient()1419	parameters := map[string]any{1420		"type": "object",1421		"properties": map[string]any{1422			"location": map[string]any{"type": "string"},1423		},1424		"required":             []string{"location"},1425		"additionalProperties": false,1426	}1427	tool := responses.ToolParamOfFunction("get_weather", parameters, true)1428	stream := client.Responses.NewStreaming(context.Background(), responses.ResponseNewParams{1429		Model: "gpt-6-astra",1430		Input: responses.ResponseNewParamsInputUnion{1431			OfString: openai.String("What's the weather like in Paris today?"),1432		},1433		Tools: []responses.ToolUnionParam{tool},1434	})14351436	finalToolCalls := map[int64]responses.ResponseFunctionToolCall{}1437	for stream.Next() {1438		event := stream.Current()1439		if event.Type == "response.output_item.added" && event.Item.Type == "function_call" {1440			finalToolCalls[event.OutputIndex] = event.Item.AsFunctionCall()1441		}1442		if event.Type == "response.function_call_arguments.delta" {1443			finalToolCall, ok := finalToolCalls[event.OutputIndex]1444			if !ok {1445				continue1446			}1447			finalToolCall.Arguments += event.Delta1448			finalToolCalls[event.OutputIndex] = finalToolCall1449		}1450	}1451	if err := stream.Err(); err != nil {1452		panic(err)1453	}1454	fmt.Println(finalToolCalls)1455}1456```14571458```java1459import com.openai.client.OpenAIClient;1460import com.openai.client.okhttp.OpenAIOkHttpClient;1461import com.openai.core.JsonValue;1462import com.openai.core.http.StreamResponse;1463import com.openai.models.responses.FunctionTool;1464import com.openai.models.responses.ResponseCreateParams;1465import com.openai.models.responses.ResponseFunctionToolCall;1466import com.openai.models.responses.ResponseStreamEvent;1467import java.util.LinkedHashMap;1468import java.util.List;1469import java.util.Map;14701471FunctionTool weather =1472    FunctionTool.builder()1473        .name("get_weather")1474        .description("Get the weather for a city.")1475        .parameters(1476            FunctionTool.Parameters.builder()1477                .putAdditionalProperty("type", JsonValue.from("object"))1478                .putAdditionalProperty(1479                    "properties", JsonValue.from(Map.of("location", Map.of("type", "string"))))1480                .putAdditionalProperty("required", JsonValue.from(List.of("location")))1481                .putAdditionalProperty("additionalProperties", JsonValue.from(false))1482                .build())1483        .strict(true)1484        .build();1485ResponseCreateParams params =1486    ResponseCreateParams.builder()1487        .model("gpt-6-astra")1488        .input("What is the weather in Paris?")1489        .addTool(weather)1490        .build();14911492Map<Long, ResponseFunctionToolCall> toolCalls = new LinkedHashMap<>();1493try (StreamResponse<ResponseStreamEvent> stream = client.responses().createStreaming(params)) {1494  stream.stream()1495      .forEach(1496          event -> {1497            event1498                .outputItemAdded()1499                .ifPresent(1500                    added ->1501                        added1502                            .item()1503                            .functionCall()1504                            .ifPresent(call -> toolCalls.put(added.outputIndex(), call)));1505            event1506                .functionCallArgumentsDelta()1507                .ifPresent(1508                    delta ->1509                        toolCalls.computeIfPresent(1510                            delta.outputIndex(),1511                            (ignored, call) ->1512                                call.toBuilder()1513                                    .arguments(call.arguments() + delta.delta())1514                                    .build()));1515          });1516}1517toolCalls.values().forEach(System.out::println);1518```15191520```ruby1521require "openai"15221523client = OpenAI::Client.new1524stream = client.responses.stream(1525  model: "gpt-6-astra",1526  input: "What is the weather in Paris?",1527  tools: [1528    {1529      type: :function,1530      name: "get_weather",1531      parameters: {1532        type: :object,1533        properties: { location: { type: :string } },1534        required: ["location"],1535        additionalProperties: false1536      },1537      strict: true1538    }1539  ]1540)15411542final_tool_calls = {}1543stream.each do |event|1544  case event1545  when OpenAI::Models::Responses::ResponseOutputItemAddedEvent1546    item = event.item1547    next unless item.is_a?(OpenAI::Models::Responses::ResponseFunctionToolCall)15481549    final_tool_calls[event.output_index] = {1550      id: item.id,1551      call_id: item.call_id,1552      name: item.name,1553      type: item.type,1554      arguments: item.arguments.dup1555    }1556  when OpenAI::Models::Responses::ResponseFunctionCallArgumentsDeltaEvent1557    tool_call = final_tool_calls[event.output_index]1558    tool_call[:arguments] << event.delta if tool_call1559  end1560end15611562puts(final_tool_calls.sort.to_h.values)1563```156415651566Accumulated final_tool_calls[0]15671568```json1569{1570    "type": "function_call",1571    "id": "fc_1234xyz",1572    "call_id": "call_2345abc",1573    "name": "get_weather",1574    "arguments": "{\"location\":\"Paris, France\"}"1575}1576```157715781579When the model has finished calling the functions an event of type `response.function_call_arguments.done` will be emitted. This event contains the entire function call including the following fields:15801581| Field          | Description                                                                                                  |1582| -------------- | ------------------------------------------------------------------------------------------------------------ |1583| `response_id`  | The id of the response that the function call belongs to                                                     |1584| `output_index` | The index of the output item in the response. This represents the individual function calls in the response. |1585| `item`         | The function call item that includes a `name`, `arguments` and `id` field.                                   |1586158715881589## Custom tools15901591Custom tools work in much the same way as JSON schema-driven function tools. But rather than providing the model explicit instructions on what input your tool requires, the model can pass an arbitrary string back to your tool as input. This is useful to avoid unnecessarily wrapping a response in JSON, or to apply a custom grammar to the response (more on this below).15921593The following code sample shows creating a custom tool that expects to receive a string of text containing Python code as a response.15941595Custom tool calling example15961597```javascript1598import OpenAI from "openai";1599const client = new OpenAI();16001601const response = await client.responses.create({1602  model: "gpt-6-astra",1603  input: "Use the code_exec tool to print hello world to the console.",1604  tools: [1605    {1606      type: "custom",1607      name: "code_exec",1608      description: "Executes arbitrary Python code.",1609    },1610  ],1611});16121613console.log(response.output);1614```16151616```python1617from openai import OpenAI16181619client = OpenAI()16201621response = client.responses.create(1622    model="gpt-6-astra",1623    input="Use the code_exec tool to print hello world to the console.",1624    tools=[1625        {1626            "type": "custom",1627            "name": "code_exec",1628            "description": "Executes arbitrary Python code.",1629        }1630    ],1631)1632print(response.output)1633```16341635```go1636package main16371638import (1639	"context"1640	"fmt"16411642	"github.com/openai/openai-go/v3"1643	"github.com/openai/openai-go/v3/responses"1644)16451646func main() {1647	client := openai.NewClient()1648	tool := responses.ToolParamOfCustom("code_exec")1649	tool.OfCustom.Description = openai.String("Executes arbitrary Python code.")16501651	response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{1652		Model: "gpt-6-astra",1653		Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Use the code_exec tool to print hello world to the console.")},1654		Tools: []responses.ToolUnionParam{tool},1655	})1656	if err != nil {1657		panic(err)1658	}1659	fmt.Println(response.Output)1660}1661```16621663```java1664import com.openai.client.OpenAIClient;1665import com.openai.client.okhttp.OpenAIOkHttpClient;1666import com.openai.models.responses.CustomTool;1667import com.openai.models.responses.ResponseCreateParams;16681669ResponseCreateParams params =1670    ResponseCreateParams.builder()1671        .model("gpt-6-astra")1672        .input("Use code_exec to print hello world.")1673        .addTool(1674            CustomTool.builder()1675                .name("code_exec")1676                .description("Executes arbitrary Python code.")1677                .build())1678        .build();16791680client.responses().create(params).output().forEach(System.out::println);1681```16821683```ruby1684require "openai"16851686client = OpenAI::Client.new1687response = client.responses.create(1688  model: "gpt-6-astra",1689  input: "Use code_exec to print hello world.",1690  tools: [1691    {1692      type: :custom,1693      name: "code_exec",1694      description: "Executes arbitrary Python code."1695    }1696  ]1697)16981699puts(response.output)1700```170117021703Just as before, the `output` array will contain a tool call generated by the model. Except this time, the tool call input is given as plain text.17041705```json1706[1707  {1708    "id": "rs_6890e972fa7c819ca8bc561526b989170694874912ae0ea6",1709    "type": "reasoning",1710    "content": [],1711    "summary": []1712  },1713  {1714    "id": "ctc_6890e975e86c819c9338825b3e1994810694874912ae0ea6",1715    "type": "custom_tool_call",1716    "status": "completed",1717    "call_id": "call_aGiFQkRWSWAIsMQ19fKqxUgb",1718    "input": "print(\"hello world\")",1719    "name": "code_exec"1720  }1721]1722```17231724### Context-free grammars17251726A [context-free grammar](https://en.wikipedia.org/wiki/Context-free_grammar) (CFG) is a set of rules that define how to produce valid text in a given format. For custom tools, you can provide a CFG that will constrain the model's text input for a custom tool.17271728You can provide a custom CFG using the `grammar` parameter when configuring a custom tool. Currently, we support two forms of CFG syntax when defining grammars: `lark` and `regex`.17291730#### Lark CFG17311732Lark context free grammar example17331734```javascript1735import OpenAI from "openai";1736const client = new OpenAI();17371738const grammar = `1739start: expr1740expr: term (SP ADD SP term)* -> add1741| term1742term: factor (SP MUL SP factor)* -> mul1743| factor1744factor: INT1745SP: " "1746ADD: "+"1747MUL: "*"1748%import common.INT1749`;17501751const response = await client.responses.create({1752  model: "gpt-6-astra",1753  input: "Use the math_exp tool to add four plus four.",1754  tools: [1755    {1756      type: "custom",1757      name: "math_exp",1758      description: "Creates valid mathematical expressions",1759      format: {1760        type: "grammar",1761        syntax: "lark",1762        definition: grammar,1763      },1764    },1765  ],1766});17671768console.log(response.output);1769```17701771```python1772from openai import OpenAI17731774client = OpenAI()17751776grammar = """1777start: expr1778expr: term (SP ADD SP term)* -> add1779| term1780term: factor (SP MUL SP factor)* -> mul1781| factor1782factor: INT1783SP: " "1784ADD: "+"1785MUL: "*"1786%import common.INT1787"""17881789response = client.responses.create(1790    model="gpt-6-astra",1791    input="Use the math_exp tool to add four plus four.",1792    tools=[1793        {1794            "type": "custom",1795            "name": "math_exp",1796            "description": "Creates valid mathematical expressions",1797            "format": {1798                "type": "grammar",1799                "syntax": "lark",1800                "definition": grammar,1801            },1802        }1803    ],1804)1805print(response.output)1806```18071808```go1809package main18101811import (1812	"context"1813	"fmt"18141815	"github.com/openai/openai-go/v3"1816	"github.com/openai/openai-go/v3/responses"1817	"github.com/openai/openai-go/v3/shared"1818)18191820func main() {1821	client := openai.NewClient()1822	grammar := `start: expr1823expr: term (SP ADD SP term)* -> add1824| term1825term: factor (SP MUL SP factor)* -> mul1826| factor1827factor: INT1828SP: " "1829ADD: "+"1830MUL: "*"1831%import common.INT`1832	tool := responses.ToolParamOfCustom("math_exp")1833	tool.OfCustom.Description = openai.String("Creates valid mathematical expressions")1834	tool.OfCustom.Format = shared.CustomToolInputFormatParamOfGrammar(grammar, "lark")18351836	response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{1837		Model: "gpt-6-astra",1838		Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Use the math_exp tool to add four plus four.")},1839		Tools: []responses.ToolUnionParam{tool},1840	})1841	if err != nil {1842		panic(err)1843	}1844	fmt.Println(response.Output)1845}1846```18471848```java1849import com.openai.client.OpenAIClient;1850import com.openai.client.okhttp.OpenAIOkHttpClient;1851import com.openai.models.CustomToolInputFormat;1852import com.openai.models.responses.CustomTool;1853import com.openai.models.responses.ResponseCreateParams;18541855String grammar =1856    """1857    start: expr1858    expr: term (SP ADD SP term)*1859    term: INT1860    SP: " "1861    ADD: "+"1862    %import common.INT1863    """;18641865ResponseCreateParams params =1866    ResponseCreateParams.builder()1867        .model("gpt-6-astra")1868        .input("Use math_exp to add four plus four.")1869        .addTool(1870            CustomTool.builder()1871                .name("math_exp")1872                .description("Creates valid mathematical expressions.")1873                .format(1874                    CustomToolInputFormat.Grammar.builder()1875                        .syntax(CustomToolInputFormat.Grammar.Syntax.LARK)1876                        .definition(grammar)1877                        .build())1878                .build())1879        .build();18801881client.responses().create(params).output().forEach(System.out::println);1882```18831884```ruby1885require "openai"18861887client = OpenAI::Client.new1888grammar = <<~LARK1889  start: expr1890  expr: term (SP ADD SP term)*1891  term: INT1892  SP: " "1893  ADD: "+"1894  %import common.INT1895LARK1896response = client.responses.create(1897  model: "gpt-6-astra",1898  input: "Use math_exp to add four plus four.",1899  tools: [1900    {1901      type: :custom,1902      name: "math_exp",1903      description: "Creates valid mathematical expressions.",1904      format: {1905        type: :grammar,1906        syntax: :lark,1907        definition: grammar1908      }1909    }1910  ]1911)19121913puts(response.output)1914```191519161917The output from the tool should then conform to the Lark CFG that you defined:19181919```json1920[1921  {1922    "id": "rs_6890ed2b6374819dbbff5353e6664ef103f4db9848be4829",1923    "type": "reasoning",1924    "content": [],1925    "summary": []1926  },1927  {1928    "id": "ctc_6890ed2f32e8819daa62bef772b8c15503f4db9848be4829",1929    "type": "custom_tool_call",1930    "status": "completed",1931    "call_id": "call_pmlLjmvG33KJdyVdC4MVdk5N",1932    "input": "4 + 4",1933    "name": "math_exp"1934  }1935]1936```19371938Grammars are specified using a variation of [Lark](https://lark-parser.readthedocs.io/en/stable/index.html). Model sampling is constrained using [LLGuidance](https://github.com/guidance-ai/llguidance/blob/main/docs/syntax.md). Some features of Lark are not supported:19391940- Lookarounds in lexer regexes1941- Lazy modifiers (`*?`, `+?`, `??`) in lexer regexes1942- Priorities of terminals1943- Templates1944- Imports (other than built-in `%import` common)1945- `%declare`s19461947We recommend using the [Lark IDE](https://www.lark-parser.org/ide/) to experiment with custom grammars.19481949<a id="keep-grammars-simple"></a>19501951### Limit grammar complexity19521953Limit your grammar to the rules and patterns your tool needs. The OpenAI API may return an error if the grammar is too complex, so you should ensure that your desired grammar is compatible before using it in the API.19541955Lark grammars can be tricky to perfect. While less complex grammars perform most reliably, complex grammars often require iteration on the grammar definition itself, the prompt, and the tool description to ensure that the model does not go out of distribution.19561957### Correct versus incorrect patterns19581959Correct (single, bounded terminal):19601961```1962start: SENTENCE1963SENTENCE: /[A-Za-z, ]*(the hero|a dragon|an old man|the princess)[A-Za-z, ]*(fought|saved|found|lost)[A-Za-z, ]*(a treasure|the kingdom|a secret|his way)[A-Za-z, ]*\./1964```19651966Do NOT do this (splitting across rules/terminals). This attempts to let rules partition free text between terminals. The lexer will greedily match the free-text pieces and you'll lose control:19671968```1969start: sentence1970sentence: /[A-Za-z, ]+/ subject /[A-Za-z, ]+/ verb /[A-Za-z, ]+/ object /[A-Za-z, ]+/1971```19721973Lowercase rules don't influence how terminals are cut from the input—only terminal definitions do. When you need “free text between anchors,” make it one giant regex terminal so the lexer matches it exactly once with the structure you intend.19741975### Terminals versus rules19761977Lark uses terminals for lexer tokens (by convention, `UPPERCASE`) and rules for parser productions (by convention, `lowercase`). The most practical way to stay within the supported subset and avoid surprises is to keep your grammar explicit and avoid unnecessary complexity, and to use terminals and rules with a clear separation of concerns.19781979The regex syntax used by terminals is the [Rust regex crate syntax](https://docs.rs/regex/latest/regex/#syntax), not Python's `re` [module](https://docs.python.org/3/library/re.html).19801981### Key ideas and best practices19821983**Lexer runs before the parser**19841985Terminals are matched by the lexer (greedily / longest match wins) before any CFG rule logic is applied. If you try to "shape" a terminal by splitting it across several rules, the lexer cannot be guided by those rules—only by terminal regexes.19861987**Prefer one terminal when you're carving text out of freeform spans**19881989If you need to recognize a pattern embedded in arbitrary text (for example, natural language with “anything” between anchors), express that as a single terminal. Do not try to interleave free‑text terminals with parser rules; the greedy lexer will not respect your intended boundaries and it is highly likely the model will go out of distribution.19901991**Use rules to compose discrete tokens**19921993Rules are ideal when you're combining explicitly delimited terminals (numbers, keywords, punctuation) into larger structures. They're not the right tool for constraining "the stuff in between" two terminals.19941995**Keep terminals focused, bounded, and self-contained**19961997Favor explicit character classes and bounded quantifiers (`{0,10}`, not unbounded `*` everywhere). If you need "any text up to a period," prefer something like `/[^.\n]{0,10}*\./` rather than `/.+\./` to avoid runaway growth.19981999**Use rules to combine tokens, not to steer regex internals**20002001Good rule usage example:20022003```2004start: expr2005NUMBER: /[0-9]+/2006PLUS: "+"2007MINUS: "-"2008expr: term (("+"|"-") term)*2009term: NUMBER2010```20112012**Treat whitespace explicitly**20132014Don't rely on open-ended `%ignore` directives. Using unbounded ignore directives may cause the grammar to be too complex and/or may cause the model to go out of distribution. Prefer threading explicit terminals wherever whitespace is allowed.20152016### Troubleshooting20172018- If the API rejects the grammar because it is too complex, simplify the rules and terminals and remove unbounded `%ignore`s.2019- If custom tools are called with unexpected tokens, confirm terminals aren’t overlapping; check greedy lexer.2020- When the model drifts "out‑of‑distribution" (shows up as the model producing excessively long or repetitive outputs, it is syntactically valid but is semantically wrong):2021  - Tighten the grammar.2022  - Iterate on the prompt (add few-shot examples) and tool description (explain the grammar and instruct the model to reason and conform to it).2023  - Experiment with a higher reasoning effort (e.g, bump from medium to high).20242025#### Regex CFG20262027Regex context free grammar example20282029```javascript2030import OpenAI from "openai";2031const client = new OpenAI();20322033const grammar =2034  "^(?P<month>January|February|March|April|May|June|July|August|September|October|November|December)\\s+(?P<day>\\d{1,2})(?:st|nd|rd|th)?\\s+(?P<year>\\d{4})\\s+at\\s+(?P<hour>0?[1-9]|1[0-2])(?P<ampm>AM|PM)$";20352036const response = await client.responses.create({2037  model: "gpt-6-astra",2038  input:2039    "Use the timestamp tool to save a timestamp for August 7th 2025 at 10AM.",2040  tools: [2041    {2042      type: "custom",2043      name: "timestamp",2044      description: "Saves a timestamp in date + time in 24-hr format.",2045      format: {2046        type: "grammar",2047        syntax: "regex",2048        definition: grammar,2049      },2050    },2051  ],2052});20532054console.log(response.output);2055```20562057```python2058from openai import OpenAI20592060client = OpenAI()20612062grammar = r"^(?P<month>January|February|March|April|May|June|July|August|September|October|November|December)\s+(?P<day>\d{1,2})(?:st|nd|rd|th)?\s+(?P<year>\d{4})\s+at\s+(?P<hour>0?[1-9]|1[0-2])(?P<ampm>AM|PM)$"20632064response = client.responses.create(2065    model="gpt-6-astra",2066    input="Use the timestamp tool to save a timestamp for August 7th 2025 at 10AM.",2067    tools=[2068        {2069            "type": "custom",2070            "name": "timestamp",2071            "description": "Saves a timestamp in date + time in 24-hr format.",2072            "format": {2073                "type": "grammar",2074                "syntax": "regex",2075                "definition": grammar,2076            },2077        }2078    ],2079)2080print(response.output)2081```20822083```go2084package main20852086import (2087	"context"2088	"fmt"20892090	"github.com/openai/openai-go/v3"2091	"github.com/openai/openai-go/v3/responses"2092	"github.com/openai/openai-go/v3/shared"2093)20942095func main() {2096	client := openai.NewClient()2097	grammar := `^(?P<month>January|February|March|April|May|June|July|August|September|October|November|December)\s+(?P<day>\d{1,2})(?:st|nd|rd|th)?\s+(?P<year>\d{4})\s+at\s+(?P<hour>0?[1-9]|1[0-2])(?P<ampm>AM|PM)$`2098	tool := responses.ToolParamOfCustom("timestamp")2099	tool.OfCustom.Description = openai.String("Saves a timestamp in date and time format.")2100	tool.OfCustom.Format = shared.CustomToolInputFormatParamOfGrammar(grammar, "regex")21012102	response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{2103		Model: "gpt-6-astra",2104		Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Use the timestamp tool to save a timestamp for August 7th 2025 at 10AM.")},2105		Tools: []responses.ToolUnionParam{tool},2106	})2107	if err != nil {2108		panic(err)2109	}2110	fmt.Println(response.Output)2111}2112```21132114```java2115import com.openai.client.OpenAIClient;2116import com.openai.client.okhttp.OpenAIOkHttpClient;2117import com.openai.models.CustomToolInputFormat;2118import com.openai.models.responses.CustomTool;2119import com.openai.models.responses.ResponseCreateParams;21202121String grammar =2122    "^(January|February|March|April|May|June|July|August|September|October|November|December) "2123        + "\\d{1,2}(st|nd|rd|th)? \\d{4} at (0?[1-9]|1[0-2])(AM|PM)$";21242125ResponseCreateParams params =2126    ResponseCreateParams.builder()2127        .model("gpt-6-astra")2128        .input("Use timestamp to save August 7th 2025 at 10AM.")2129        .addTool(2130            CustomTool.builder()2131                .name("timestamp")2132                .description("Saves a timestamp in date and time format.")2133                .format(2134                    CustomToolInputFormat.Grammar.builder()2135                        .syntax(CustomToolInputFormat.Grammar.Syntax.REGEX)2136                        .definition(grammar)2137                        .build())2138                .build())2139        .build();21402141client.responses().create(params).output().forEach(System.out::println);2142```21432144```ruby2145require "openai"21462147client = OpenAI::Client.new2148grammar = "^(January|February|March|April|May|June|July|August|September|October|November|December) \\d{1,2}(st|nd|rd|th)? \\d{4} at (0?[1-9]|1[0-2])(AM|PM)$"2149response = client.responses.create(2150  model: "gpt-6-astra",2151  input: "Use timestamp to save August 7th 2025 at 10AM.",2152  tools: [2153    {2154      type: :custom,2155      name: "timestamp",2156      description: "Saves a timestamp in date and time format.",2157      format: {2158        type: :grammar,2159        syntax: :regex,2160        definition: grammar2161      }2162    }2163  ]2164)21652166puts(response.output)2167```216821692170The output from the tool should then conform to the Regex CFG that you defined:21712172```json2173[2174  {2175    "id": "rs_6894f7a3dd4c81a1823a723a00bfa8710d7962f622d1c260",2176    "type": "reasoning",2177    "content": [],2178    "summary": []2179  },2180  {2181    "id": "ctc_6894f7ad7fb881a1bffa1f377393b1a40d7962f622d1c260",2182    "type": "custom_tool_call",2183    "status": "completed",2184    "call_id": "call_8m4XCnYvEmFlzHgDHbaOCFlK",2185    "input": "August 7th 2025 at 10AM",2186    "name": "timestamp"2187  }2188]2189```21902191As with the Lark syntax, regexes use the [Rust regex crate syntax](https://docs.rs/regex/latest/regex/#syntax), not Python's `re` [module](https://docs.python.org/3/library/re.html).21922193Some features of Regex are not supported:21942195- Lookarounds2196- Lazy modifiers (`*?`, `+?`, `??`)21972198### Key ideas and best practices21992200**Pattern must be on one line**22012202If you need to match a newline in the input, use the escaped sequence `\n`. Do not use verbose/extended mode, which allows patterns to span multiple lines.22032204**Provide the regex as a plain pattern string**22052206Don't enclose the pattern in `//`.
Files main
Menu

Theme