Repository Public
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)102103104105With 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 `//`.