Haijun Platform Docs
ID

Programmatic tool calling allows Haijun to write code that calls your tools programmatically within a code execution container, rather than requiring round trips through the model for each tool invocation. This reduces latency for multi-tool workflows and decreases token consumption by allowing Haijun to filter or process data before it reaches the model's context window. On agentic search benchmarks like BrowseComp and DeepSearchQA, which test multistep web research and complex information retrieval, adding programmatic tool calling on top of basic search tools improved performance by an average of 11% while using 24% fewer input tokens (see Improved web search with dynamic filtering).

Consider checking budget compliance across 20 employees: the traditional approach requires 20 separate model round-trips, pulling thousands of expense line items into the context along the way. With programmatic tool calling, a single script runs all 20 lookups, filters the results, and returns only the employees who exceeded their limits, shrinking what Haijun needs to reason over from hundreds of kilobytes down to a handful of lines.

Tip: For a deeper look at the inference and context costs that programmatic tool calling addresses, see Advanced tool use.

Programmatic tool calling requires the code execution tool with tool version code_execution_20260120 or later.

Quick start

Here's an example where Haijun programmatically queries a database multiple times and aggregates results. Adding allowed_callers: ["code_execution_20260120"] to a tool definition is what makes that tool callable from within code execution (see The allowed_callers field):

bash
  curl https://haijun.my.id/v1/messages \
      --header "x-api-key: $JUGLOW_API_KEY" \
      --header "juglow-version: 2023-06-01" \
      --header "content-type: application/json" \
      --data '{
          "model": "haijun-opus-5-5",
          "max_tokens": 4096,
          "messages": [
              {
                  "role": "user",
                  "content": "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue"
              }
          ],
          "tools": [
              {
                  "type": "code_execution_20260120",
                  "name": "code_execution"
              },
              {
                  "name": "query_database",
                  "description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
                  "input_schema": {
                      "type": "object",
                      "properties": {
                          "sql": {
                              "type": "string",
                              "description": "SQL query to execute"
                          }
                      },
                      "required": ["sql"]
                  },
                  "allowed_callers": ["code_execution_20260120"]
              }
          ]
      }'
bash
  ant messages create <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  messages:
    - role: user
      content: >-
        Query sales data for the West, East, and Central regions, then
        tell me which region had the highest revenue
  tools:
    - type: code_execution_20260120
      name: code_execution
    - name: query_database
      description: >-
        Execute a SQL query against the sales database. Returns a list
        of rows as JSON objects.
      input_schema:
        type: object
        properties:
          sql:
            type: string
            description: SQL query to execute
        required:
          - sql
      allowed_callers:
        - code_execution_20260120
  YAML
python
  client = juglow.Juglow()

  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      messages=[
          {
              "role": "user",
              "content": "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue",
          }
      ],
      tools=[
          {"type": "code_execution_20260120", "name": "code_execution"},
          {
              "name": "query_database",
              "description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
              "input_schema": {
                  "type": "object",
                  "properties": {
                      "sql": {"type": "string", "description": "SQL query to execute"}
                  },
                  "required": ["sql"],
              },
              "allowed_callers": ["code_execution_20260120"],
          },
      ],
  )

  print(response)
typescript
  const client = new Juglow();

  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages: [
      {
        role: "user",
        content:
          "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue"
      }
    ],
    tools: [
      {
        type: "code_execution_20260120",
        name: "code_execution"
      },
      {
        name: "query_database",
        description:
          "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
        input_schema: {
          type: "object" as const,
          properties: {
            sql: {
              type: "string",
              description: "SQL query to execute"
            }
          },
          required: ["sql"]
        },
        allowed_callers: ["code_execution_20260120"]
      }
    ]
  });

  console.log(response);
csharp
  JuglowClient client = new();

  var parameters = new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 4096,
      Messages = [
          new() {
              Role = Role.User,
              Content = "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue"
          }
      ],
      Tools = [
          new CodeExecutionTool20260120(),
          new ToolUnion(new Tool()
          {
              Name = "query_database",
              Description = "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
              InputSchema = new InputSchema()
              {
                  Properties = new Dictionary<string, JsonElement>
                  {
                      ["sql"] = JsonSerializer.SerializeToElement(new { type = "string", description = "SQL query to execute" }),
                  },
                  Required = ["sql"],
              },
              AllowedCallers = ["code_execution_20260120"]
          }),
      ]
  };

  var message = await client.Messages.Create(parameters);
  Console.WriteLine(message);
go
  client := juglow.NewClient()

  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 4096,
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue")),
  	},
  	Tools: []juglow.ToolUnionParam{
  		{OfCodeExecutionTool20260120: &juglow.CodeExecutionTool20260120Param{}},
  		{OfTool: &juglow.ToolParam{
  			Name:        "query_database",
  			Description: juglow.String("Execute a SQL query against the sales database. Returns a list of rows as JSON objects."),
  			InputSchema: juglow.ToolInputSchemaParam{
  				Properties: map[string]any{
  					"sql": map[string]any{
  						"type":        "string",
  						"description": "SQL query to execute",
  					},
  				},
  				Required: []string{"sql"},
  			},
  			AllowedCallers: []string{"code_execution_20260120"},
  		}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response.RawJSON())
java
  import com.juglow.models.messages.CodeExecutionTool20260120;
  // ...

  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(4096L)
          .addUserMessage("Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue")
          .addTool(CodeExecutionTool20260120.builder().build())
          .addTool(Tool.builder()
              .name("query_database")
              .description("Execute a SQL query against the sales database. Returns a list of rows as JSON objects.")
              .inputSchema(InputSchema.builder()
                  .properties(JsonValue.from(Map.of(
                      "sql", Map.of(
                          "type", "string",
                          "description", "SQL query to execute"
                      )
                  )))
                  .putAdditionalProperty("required", JsonValue.from(List.of("sql")))
                  .build())
              .allowedCallers(List.of(Tool.AllowedCaller.of("code_execution_20260120")))
              .build())
          .build();

      Message response = client.messages().create(params);
      IO.println(response);
  }
php
  $client = new Client();

  $message = $client->messages->create(
      maxTokens: 4096,
      messages: [
          ['role' => 'user', 'content' => 'Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue'],
      ],
      model: 'haijun-opus-5-5',
      tools: [
          [
              'type' => 'code_execution_20260120',
              'name' => 'code_execution',
          ],
          [
              'name' => 'query_database',
              'description' => 'Execute a SQL query against the sales database. Returns a list of rows as JSON objects.',
              'input_schema' => [
                  'type' => 'object',
                  'properties' => [
                      'sql' => [
                          'type' => 'string',
                          'description' => 'SQL query to execute',
                      ],
                  ],
                  'required' => ['sql'],
              ],
              'allowed_callers' => ['code_execution_20260120'],
          ],
      ],
  );

  echo $message;
ruby
  client = Juglow::Client.new

  message = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    messages: [
      {
        role: "user",
        content: "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue"
      }
    ],
    tools: [
      {
        type: "code_execution_20260120",
        name: "code_execution"
      },
      {
        name: "query_database",
        description: "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
        input_schema: {
          type: "object",
          properties: {
            sql: {
              type: "string",
              description: "SQL query to execute"
            }
          },
          required: ["sql"]
        },
        allowed_callers: ["code_execution_20260120"]
      }
    ]
  )

  puts message

The response stops with stop_reason: "tool_use", a container ID, and a tool_use block for query_database whose caller field identifies the code execution run that called it. Return the result as shown in Step 3 of the example workflow so the code can finish.

How programmatic tool calling works

When you configure a tool to be callable from code execution and Haijun determines that tool is needed:

  1. Haijun writes Python code that invokes the tool as a function, potentially including multiple tool calls and pre/post-processing logic
  1. Haijun runs this code in a sandboxed container through code execution
  1. When a tool function is called, code execution pauses and the API returns a tool_use block
  1. You provide the tool result, and code execution continues (intermediate results are not loaded into Haijun's context window)
  1. Once all code execution completes, Haijun receives the final output and continues working on the task

This approach is particularly useful for:

  • Large data processing: Filter or aggregate tool results before they reach Haijun's context
  • Multistep workflows: Save tokens and latency by calling tools serially or in a loop without sampling Haijun in-between tool calls
  • Conditional logic: Make decisions based on intermediate tool results

Note: Tools that allow a code execution caller are exposed to Haijun's code as async Python functions, so Haijun can run them in parallel with asyncio.gather. Each function takes a single dict of arguments and returns a string: the text of the tool_result you send back. Haijun's code awaits these functions with top-level await and parses results that it needs as structured data, for example rows = json.loads(await query_database({"sql": ""})).

Core concepts

The allowed_callers field

The allowed_callers field specifies which contexts can invoke a tool:

json
{
  "name": "query_database",
  "description": "Execute a SQL query against the database",
  "input_schema": {
    // ...
  },
  "allowed_callers": ["code_execution_20260120"]
}

Possible values:

  • ["direct"] - Haijun is guided to call this tool directly (default if omitted)
  • ["code_execution_20260120"] - Haijun is guided to call this tool only from within code execution
  • ["direct", "code_execution_20260120"] - Haijun may call this tool directly or from within code execution

Both "code_execution_20260120" and "code_execution_20260521" are accepted in allowed_callers and are interchangeable: a request using either code-execution tool version satisfies tools that list either caller. Response blocks always tag the caller as code_execution_20260120 regardless of which version the request declared.

Tip: Choose either ["direct"] or ["code_execution_20260120"] for each tool rather than enabling both, as this provides clearer guidance to Haijun for how best to use the tool.

Note: allowed_callers controls how the tool is presented to Haijun and is validated against tool_choice, but it is not a hard API-level block on direct invocation. Haijun is strongly guided to respect it, but your client should still be prepared to handle a direct tool_use for any tool it defines. Do not rely on allowed_callers as a security boundary.

The caller field in responses

Every tool use block includes a caller field indicating how it was invoked:

Direct invocation (traditional tool use):

json
{
  "type": "tool_use",
  "id": "toolu_abc123",
  "name": "query_database",
  "input": { "sql": "<sql>" },
  "caller": { "type": "direct" }
}

Programmatic invocation:

json
{
  "type": "tool_use",
  "id": "toolu_xyz789",
  "name": "query_database",
  "input": { "sql": "<sql>" },
  "caller": {
    "type": "code_execution_20260120",
    "tool_id": "srvtoolu_abc123"
  }
}

The tool_id is the id of the code execution server_tool_use block that made the call, so you can match each programmatic tool_use to the code execution run that produced it.

Container lifecycle

Programmatic tool calling uses the same containers as code execution:

  • Container creation: A new container is created for each request unless you reuse an existing one
  • Container ID: Returned in responses in the container field, along with an expires_at timestamp
  • Reuse: Pass the container ID back on the next request to keep state. While a programmatic tool call is waiting for your result, the container ID is required on that request, not optional: the API rejects the request without it.
  • Expiration: expires_at tells you how long the container has left. Idle containers are currently reclaimed after about 5 minutes, and no container can be reused more than 30 days after it was created.

Warning: While Haijun's code is waiting for a programmatic tool result, the pending call times out after about 4 minutes and raises a TimeoutError inside the code. Return each tool result well before the expires_at timestamp on the paused response. See Container expiration during tool call.

Example workflow

Here's how a complete programmatic tool calling flow works:

Step 1: Initial request

Send a request with code execution and a tool that allows programmatic calling. To enable programmatic calling, add the allowed_callers field to your tool definition.

Note: Provide detailed descriptions of your tool's output format in the tool description. If you specify that the tool returns JSON, Haijun attempts to deserialize and process the result in code. The more detail you provide about the output schema, the better Haijun can handle the response programmatically.

The request shape is identical to the Quick start example: include code_execution in your tools list, add allowed_callers: ["code_execution_20260120"] to any tool you want Haijun to invoke from code, and send your user message. The remaining steps in this workflow use the user message "Query customer purchase history from the last quarter and identify our top 5 customers by revenue".

Step 2: API response with tool call

Haijun writes code that calls your tool. The API pauses and returns:

json
{
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "I'll query the purchase history and analyze the results."
    },
    {
      "type": "server_tool_use",
      "id": "srvtoolu_abc123",
      "name": "code_execution",
      "input": {
        "code": "import json\n\nrows = json.loads(await query_database({'sql': '<sql>'}))\ntop_customers = sorted(rows, key=lambda x: x['revenue'], reverse=True)[:5]\nprint(f'Top 5 customers: {top_customers}')"
      }
    },
    {
      "type": "tool_use",
      "id": "toolu_def456",
      "name": "query_database",
      "input": { "sql": "<sql>" },
      "caller": {
        "type": "code_execution_20260120",
        "tool_id": "srvtoolu_abc123"
      }
    }
  ],
  "container": {
    "id": "container_xyz789",
    "expires_at": "2026-01-20T14:30:00Z"
  },
  "stop_reason": "tool_use"
}

Step 3: Provide tool result

Send the full conversation history plus your tool result. Three details matter on this request:

  • Pass the container ID from the paused response. The API rejects a continuation that has pending programmatic tool calls but no container ID.
  • Send the same tools array as the original request. The code execution tool must still be present for the paused code to resume, and the tools you send on this request are the definitions Haijun and the running code can use for the rest of the turn.
bash
  curl https://haijun.my.id/v1/messages \
      --header "x-api-key: $JUGLOW_API_KEY" \
      --header "juglow-version: 2023-06-01" \
      --header "content-type: application/json" \
      --data '{
          "model": "haijun-opus-5-5",
          "max_tokens": 4096,
          "container": "container_xyz789",
          "messages": [
              {
                  "role": "user",
                  "content": "Query customer purchase history from the last quarter and identify our top 5 customers by revenue"
              },
              {
                  "role": "assistant",
                  "content": [
                      {
                          "type": "text",
                          "text": "I'\''ll query the purchase history and analyze the results."
                      },
                      {
                          "type": "server_tool_use",
                          "id": "srvtoolu_abc123",
                          "name": "code_execution",
                          "input": {"code": "..."}
                      },
                      {
                          "type": "tool_use",
                          "id": "toolu_def456",
                          "name": "query_database",
                          "input": {"sql": "<sql>"},
                          "caller": {
                              "type": "code_execution_20260120",
                              "tool_id": "srvtoolu_abc123"
                          }
                      }
                  ]
              },
              {
                  "role": "user",
                  "content": [
                      {
                          "type": "tool_result",
                          "tool_use_id": "toolu_def456",
                          "content": "[{\"customer_id\": \"C1\", \"revenue\": 45000}, {\"customer_id\": \"C2\", \"revenue\": 38000}]"
                      }
                  ]
              }
          ],
          "tools": [
              {
                  "type": "code_execution_20260120",
                  "name": "code_execution"
              },
              {
                  "name": "query_database",
                  "description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
                  "input_schema": {
                      "type": "object",
                      "properties": {
                          "sql": {
                              "type": "string",
                              "description": "SQL query to execute"
                          }
                      },
                      "required": ["sql"]
                  },
                  "allowed_callers": ["code_execution_20260120"]
              }
          ]
      }'
bash
  ant messages create <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  container: container_xyz789
  messages:
    - role: user
      content: >-
        Query customer purchase history from the last quarter and identify our
        top 5 customers by revenue
    - role: assistant
      content:
        - type: text
          text: I'll query the purchase history and analyze the results.
        - type: server_tool_use
          id: srvtoolu_abc123
          name: code_execution
          input:
            code: "..."
        - type: tool_use
          id: toolu_def456
          name: query_database
          input:
            sql: "<sql>"
          caller:
            type: code_execution_20260120
            tool_id: srvtoolu_abc123
    - role: user
      content:
        - type: tool_result
          tool_use_id: toolu_def456
          content: >-
            [{"customer_id": "C1", "revenue": 45000}, {"customer_id": "C2",
            "revenue": 38000}, ...]
  # Same tools array as the original request
  tools:
    - type: code_execution_20260120
      name: code_execution
    - name: query_database
      description: >-
        Execute a SQL query against the sales database. Returns a list
        of rows as JSON objects.
      input_schema:
        type: object
        properties:
          sql:
            type: string
            description: SQL query to execute
        required:
          - sql
      allowed_callers:
        - code_execution_20260120
  YAML
python
  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      container="container_xyz789",  # Reuse the container
      messages=[
          {
              "role": "user",
              "content": "Query customer purchase history from the last quarter and identify our top 5 customers by revenue",
          },
          {
              "role": "assistant",
              "content": [
                  {
                      "type": "text",
                      "text": "I'll query the purchase history and analyze the results.",
                  },
                  {
                      "type": "server_tool_use",
                      "id": "srvtoolu_abc123",
                      "name": "code_execution",
                      "input": {"code": "..."},
                  },
                  {
                      "type": "tool_use",
                      "id": "toolu_def456",
                      "name": "query_database",
                      "input": {"sql": "<sql>"},
                      "caller": {
                          "type": "code_execution_20260120",
                          "tool_id": "srvtoolu_abc123",
                      },
                  },
              ],
          },
          {
              "role": "user",
              "content": [
                  {
                      "type": "tool_result",
                      "tool_use_id": "toolu_def456",
                      "content": '[{"customer_id": "C1", "revenue": 45000}, {"customer_id": "C2", "revenue": 38000}, ...]',
                  }
              ],
          },
      ],
      # Same tools array as the original request
      tools=[
          {"type": "code_execution_20260120", "name": "code_execution"},
          {
              "name": "query_database",
              "description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
              "input_schema": {
                  "type": "object",
                  "properties": {
                      "sql": {"type": "string", "description": "SQL query to execute"}
                  },
                  "required": ["sql"],
              },
              "allowed_callers": ["code_execution_20260120"],
          },
      ],
  )

  print(response)
typescript
  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: "container_xyz789", // Reuse the container
    messages: [
      {
        role: "user",
        content:
          "Query customer purchase history from the last quarter and identify our top 5 customers by revenue"
      },
      {
        role: "assistant",
        content: [
          { type: "text", text: "I'll query the purchase history and analyze the results." },
          {
            type: "server_tool_use",
            id: "srvtoolu_abc123",
            name: "code_execution",
            input: { code: "..." }
          },
          {
            type: "tool_use",
            id: "toolu_def456",
            name: "query_database",
            input: { sql: "<sql>" },
            caller: {
              type: "code_execution_20260120",
              tool_id: "srvtoolu_abc123"
            }
          }
        ]
      },
      {
        role: "user",
        content: [
          {
            type: "tool_result",
            tool_use_id: "toolu_def456",
            content:
              '[{"customer_id": "C1", "revenue": 45000}, {"customer_id": "C2", "revenue": 38000}, ...]'
          }
        ]
      }
    ],
    // Same tools array as the original request
    tools: [
      {
        type: "code_execution_20260120",
        name: "code_execution"
      },
      {
        name: "query_database",
        description:
          "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
        input_schema: {
          type: "object" as const,
          properties: {
            sql: {
              type: "string",
              description: "SQL query to execute"
            }
          },
          required: ["sql"]
        },
        allowed_callers: ["code_execution_20260120"]
      }
    ]
  });

  console.log(response);
csharp
  JuglowClient client = new();

  var parameters = new MessageCreateParams
  {
      Model = Model.HaijunOpus5_5,
      MaxTokens = 4096,
      Container = "container_xyz789",
      Messages =
      [
          new()
          {
              Role = Role.User,
              Content = "Query customer purchase history from the last quarter and identify our top 5 customers by revenue"
          },
          new()
          {
              Role = Role.Assistant,
              Content = new ContentBlock[]
              {
                  new TextBlock { Text = "I'll query the purchase history and analyze the results." },
                  new ServerToolUseBlock
                  {
                      Id = "srvtoolu_abc123",
                      Name = "code_execution",
                      Input = new { code = "..." }
                  },
                  new ToolUseBlock
                  {
                      Id = "toolu_def456",
                      Name = "query_database",
                      Input = new { sql = "<sql>" },
                      Caller = new ToolCaller
                      {
                          Type = "code_execution_20260120",
                          ToolId = "srvtoolu_abc123"
                      }
                  }
              }
          },
          new()
          {
              Role = Role.User,
              Content = new ContentBlockParam[]
              {
                  new ToolResultBlockParam
                  {
                      ToolUseID = "toolu_def456",
                      Content = "[{\"customer_id\": \"C1\", \"revenue\": 45000}, {\"customer_id\": \"C2\", \"revenue\": 38000}, ...]"
                  }
              }
          }
      ],
      // Same tools array as the original request
      Tools = [
          new CodeExecutionTool20260120(),
          new ToolUnion(new Tool()
          {
              Name = "query_database",
              Description = "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
              InputSchema = new InputSchema()
              {
                  Properties = new Dictionary<string, JsonElement>
                  {
                      ["sql"] = JsonSerializer.SerializeToElement(new { type = "string", description = "SQL query to execute" }),
                  },
                  Required = ["sql"],
              },
              AllowedCallers = ["code_execution_20260120"]
          }),
      ]
  };

  var message = await client.Messages.Create(parameters);
  Console.WriteLine(message);
go
  client := juglow.NewClient()

  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     juglow.ModelHaijunOpus5_5,
  	MaxTokens: 4096,
  	Container: juglow.MessageCreateParamsContainerUnion{
  		OfString: juglow.String("container_xyz789"),
  	},
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Query customer purchase history from the last quarter and identify our top 5 customers by revenue")),
  		{
  			Role: juglow.MessageParamRoleAssistant,
  			Content: []juglow.ContentBlockParamUnion{
  				juglow.NewTextBlock("I'll query the purchase history and analyze the results."),
  				{OfServerToolUse: &juglow.ServerToolUseBlockParam{
  					ID:    "srvtoolu_abc123",
  					Name:  juglow.ServerToolUseBlockParamNameCodeExecution,
  					Input: map[string]any{"code": "..."},
  				}},
  				{OfToolUse: &juglow.ToolUseBlockParam{
  					ID:    "toolu_def456",
  					Name:  "query_database",
  					Input: map[string]any{"sql": "<sql>"},
  					Caller: juglow.ServerToolUseBlockParamCallerUnion{
  						OfCodeExecution20260120: &juglow.ServerToolCaller20260120Param{
  							ToolID: "srvtoolu_abc123",
  						},
  					},
  				}},
  			},
  		},
  		{
  			Role: juglow.MessageParamRoleUser,
  			Content: []juglow.ContentBlockParamUnion{
  				{OfToolResult: &juglow.ToolResultBlockParam{
  					ToolUseID: "toolu_def456",
  					Content: []juglow.ToolResultBlockParamContentUnion{
  						{OfText: &juglow.TextBlockParam{
  							Text: `[{"customer_id": "C1", "revenue": 45000}, {"customer_id": "C2", "revenue": 38000}, ...]`,
  						}},
  					},
  				}},
  			},
  		},
  	},
  	// Same tools array as the original request
  	Tools: []juglow.ToolUnionParam{
  		{OfCodeExecutionTool20260120: &juglow.CodeExecutionTool20260120Param{}},
  		{OfTool: &juglow.ToolParam{
  			Name:        "query_database",
  			Description: juglow.String("Execute a SQL query against the sales database. Returns a list of rows as JSON objects."),
  			InputSchema: juglow.ToolInputSchemaParam{
  				Properties: map[string]any{
  					"sql": map[string]any{
  						"type":        "string",
  						"description": "SQL query to execute",
  					},
  				},
  				Required: []string{"sql"},
  			},
  			AllowedCallers: []string{"code_execution_20260120"},
  		}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response.RawJSON())
java
  import com.juglow.models.messages.CodeExecutionTool20260120;
  // ...

  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(4096L)
          .container("container_xyz789")
          .addUserMessage("Query customer purchase history from the last quarter and identify our top 5 customers by revenue")
          .addAssistantMessageOfBlockParams(List.of(
              ContentBlockParam.ofText(
                  TextBlockParam.builder()
                      .text("I'll query the purchase history and analyze the results.")
                      .build()),
              ContentBlockParam.ofServerToolUse(
                  ServerToolUseBlockParam.builder()
                      .id("srvtoolu_abc123")
                      .name("code_execution")
                      .input(JsonValue.from(Map.of("code", "...")))
                      .build()),
              ContentBlockParam.ofToolUse(
                  ToolUseBlockParam.builder()
                      .id("toolu_def456")
                      .name("query_database")
                      .input(JsonValue.from(Map.of("sql", "<sql>")))
                      .codeExecution20260120Caller("srvtoolu_abc123")
                      .build())
          ))
          .addUserMessageOfBlockParams(List.of(
              ContentBlockParam.ofToolResult(
                  ToolResultBlockParam.builder()
                      .toolUseId("toolu_def456")
                      .content("[{\"customer_id\": \"C1\", \"revenue\": 45000}, {\"customer_id\": \"C2\", \"revenue\": 38000}, ...]")
                      .build())
          ))
          // Same tools array as the original request
          .addTool(CodeExecutionTool20260120.builder().build())
          .addTool(Tool.builder()
              .name("query_database")
              .description("Execute a SQL query against the sales database. Returns a list of rows as JSON objects.")
              .inputSchema(InputSchema.builder()
                  .properties(JsonValue.from(Map.of(
                      "sql", Map.of(
                          "type", "string",
                          "description", "SQL query to execute"
                      )
                  )))
                  .putAdditionalProperty("required", JsonValue.from(List.of("sql")))
                  .build())
              .allowedCallers(List.of(Tool.AllowedCaller.of("code_execution_20260120")))
              .build())
          .build();

      Message response = client.messages().create(params);
      IO.println(response);
  }
php
  $client = new Client();

  $message = $client->messages->create(
      maxTokens: 4096,
      messages: [
          [
              'role' => 'user',
              'content' => 'Query customer purchase history from the last quarter and identify our top 5 customers by revenue',
          ],
          [
              'role' => 'assistant',
              'content' => [
                  [
                      'type' => 'text',
                      'text' => "I'll query the purchase history and analyze the results.",
                  ],
                  [
                      'type' => 'server_tool_use',
                      'id' => 'srvtoolu_abc123',
                      'name' => 'code_execution',
                      'input' => ['code' => '...'],
                  ],
                  [
                      'type' => 'tool_use',
                      'id' => 'toolu_def456',
                      'name' => 'query_database',
                      'input' => ['sql' => '<sql>'],
                      'caller' => [
                          'type' => 'code_execution_20260120',
                          'tool_id' => 'srvtoolu_abc123',
                      ],
                  ],
              ],
          ],
          [
              'role' => 'user',
              'content' => [
                  [
                      'type' => 'tool_result',
                      'tool_use_id' => 'toolu_def456',
                      'content' => '[{"customer_id": "C1", "revenue": 45000}, {"customer_id": "C2", "revenue": 38000}, ...]',
                  ],
              ],
          ],
      ],
      model: 'haijun-opus-5-5',
      container: 'container_xyz789',
      // Same tools array as the original request
      tools: [
          [
              'type' => 'code_execution_20260120',
              'name' => 'code_execution',
          ],
          [
              'name' => 'query_database',
              'description' => 'Execute a SQL query against the sales database. Returns a list of rows as JSON objects.',
              'input_schema' => [
                  'type' => 'object',
                  'properties' => [
                      'sql' => [
                          'type' => 'string',
                          'description' => 'SQL query to execute',
                      ],
                  ],
                  'required' => ['sql'],
              ],
              'allowed_callers' => ['code_execution_20260120'],
          ],
      ],
  );

  echo $message;
ruby
  require "juglow"

  client = Juglow::Client.new

  message = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: "container_xyz789",
    messages: [
      {
        role: "user",
        content: "Query customer purchase history from the last quarter and identify our top 5 customers by revenue"
      },
      {
        role: "assistant",
        content: [
          {
            type: "text",
            text: "I'll query the purchase history and analyze the results."
          },
          {
            type: "server_tool_use",
            id: "srvtoolu_abc123",
            name: "code_execution",
            input: { code: "..." }
          },
          {
            type: "tool_use",
            id: "toolu_def456",
            name: "query_database",
            input: { sql: "<sql>" },
            caller: {
              type: "code_execution_20260120",
              tool_id: "srvtoolu_abc123"
            }
          }
        ]
      },
      {
        role: "user",
        content: [
          {
            type: "tool_result",
            tool_use_id: "toolu_def456",
            content: '[{"customer_id": "C1", "revenue": 45000}, {"customer_id": "C2", "revenue": 38000}, ...]'
          }
        ]
      }
    ],
    # Same tools array as the original request
    tools: [
      {
        type: "code_execution_20260120",
        name: "code_execution"
      },
      {
        name: "query_database",
        description: "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
        input_schema: {
          type: "object",
          properties: {
            sql: {
              type: "string",
              description: "SQL query to execute"
            }
          },
          required: ["sql"]
        },
        allowed_callers: ["code_execution_20260120"]
      }
    ]
  )

  puts message

Step 4: Next tool call or completion

The code picks up where it paused and processes your result. Each continuation response either pauses again with more programmatic tool_use blocks, or completes the code execution and lets Haijun continue the turn (Step 5). Check stop_reason and each tool_use block's caller to tell the two apart: a response that pauses for you has stop_reason: "tool_use" and a tool_use block whose caller names a code execution version, and you repeat Step 3 with a tool_result for every pending programmatic call in one user message.

Step 5: Final response

Once the code execution completes, Haijun provides the final response:

json
{
  "content": [
    {
      "type": "code_execution_tool_result",
      "tool_use_id": "srvtoolu_abc123",
      "content": {
        "type": "code_execution_result",
        "stdout": "Top 5 customers: [{'customer_id': 'C1', 'revenue': 45000}, {'customer_id': 'C2', 'revenue': 38000}, {'customer_id': 'C5', 'revenue': 32000}, {'customer_id': 'C8', 'revenue': 28500}, {'customer_id': 'C3', 'revenue': 24000}]",
        "stderr": "",
        "return_code": 0,
        "content": []
      }
    },
    {
      "type": "text",
      "text": "I've analyzed the purchase history from last quarter. Your top 5 customers generated $167,500 in total revenue, with Customer C1 leading at $45,000."
    }
  ],
  "stop_reason": "end_turn"
}

Advanced patterns

Batch processing with loops

Haijun can write code that processes multiple items efficiently:

python
regions = ["West", "East", "Central", "North", "South"]
results = {}
for region in regions:
    rows = json.loads(await query_database({"sql": f"<sql for {region}>"}))
    results[region] = sum(row["revenue"] for row in rows)

# Process results programmatically
top_region = max(results.items(), key=lambda x: x[1])
print(f"Top region: {top_region[0]} with ${top_region[1]:,} in revenue")

This pattern:

  • Reduces model round-trips from N (one per region) to 1
  • Processes large result sets programmatically before returning to Haijun
  • Saves tokens by only returning aggregated conclusions instead of raw data

Early termination

Haijun can stop processing as soon as success criteria are met:

python
endpoints = ["us-east", "eu-west", "apac"]
for endpoint in endpoints:
    status = await check_health({"endpoint": endpoint})
    if status == "healthy":
        print(f"Found healthy endpoint: {endpoint}")
        break  # Stop early, don't check remaining

Conditional tool selection

python
path = "/tmp/example.txt"
file_info = json.loads(await get_file_info({"path": path}))
if file_info["size"] < 10000:
    content = await read_full_file({"path": path})
else:
    content = await read_file_summary({"path": path})
print(content)

Data filtering

python
server_id = "srv-01"
log_text = await fetch_logs({"server_id": server_id})
errors = [line for line in log_text.splitlines() if "ERROR" in line]
print(f"Found {len(errors)} errors")
for error in errors[-10:]:  # Only return last 10 errors
    print(error)

Response format

Programmatic tool call

When code execution calls a tool:

json
{
  "type": "tool_use",
  "id": "toolu_abc123",
  "name": "query_database",
  "input": { "sql": "<sql>" },
  "caller": {
    "type": "code_execution_20260120",
    "tool_id": "srvtoolu_xyz789"
  }
}

Tool result handling

Your tool result is passed back to the running code:

json
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_abc123",
      "content": "[{\"customer_id\": \"C1\", \"revenue\": 45000, \"orders\": 23}, {\"customer_id\": \"C2\", \"revenue\": 38000, \"orders\": 18}, ...]"
    }
  ]
}

Code execution completion

When all tool calls are satisfied and code completes:

json
{
  "type": "code_execution_tool_result",
  "tool_use_id": "srvtoolu_xyz789",
  "content": {
    "type": "code_execution_result",
    "stdout": "Analysis complete. Top 5 customers identified from 847 total records.",
    "stderr": "",
    "return_code": 0,
    "content": []
  }
}

Error handling

Common errors

ErrorWhere it appearsDescriptionSolution
invalid_tool_inputerror_code on the code_execution_tool_result error block in the responseInvalid parameters were passed to the code execution toolSee the code execution tool errors
invalid_request_error (on tool_choice)HTTP 400 error responsetool_choice names a tool whose allowed_callers does not include "direct"Either add "direct" to that tool's allowed_callers, or remove the tool from tool_choice and let Haijun invoke it from code

Container expiration during tool call

If your tool result doesn't arrive within about 4 minutes, the pending call raises a TimeoutError inside Haijun's running code. Haijun sees the error in stderr and typically retries the call:

json
{
  "type": "code_execution_tool_result",
  "tool_use_id": "srvtoolu_abc123",
  "content": {
    "type": "code_execution_result",
    "stdout": "",
    "stderr": "TimeoutError: Calling tool ['query_database'] timed out (no response after 270s).",
    "return_code": 0,
    "content": []
  }
}

To prevent timeouts:

  • Monitor the expires_at field in responses
  • Implement timeouts for your tool execution
  • Consider breaking long operations into smaller chunks

Tool execution errors

If your tool returns an error:

json
{
  "type": "tool_result",
  "tool_use_id": "toolu_abc123",
  "content": "Error: Query timeout - table lock exceeded 30 seconds"
}

Haijun's code receives this error and can handle it appropriately.

Constraints and limitations

Feature incompatibilities

  • Structured outputs: Tools with strict: true are not supported with programmatic calling
  • Tool choice: You cannot force programmatic calling of a specific tool through tool_choice
  • Parallel tool use: disable_parallel_tool_use: true is not supported with programmatic calling

Input schema limitations

Custom tools whose input_schema contains a recursive $ref (a reference cycle, such as a schema that refers to itself) cannot be enabled for programmatic calling. Including a code execution tool version in allowed_callers for such a tool causes the request to fail with a 400 invalid_request_error whose message contains Circular $ref detected. The same schema is accepted for direct tool calling.

To work around this, do one of the following:

  • Keep the tool direct-only by omitting allowed_callers (or setting it to ["direct"]). Other tools in the same request can still use programmatic calling.
  • Remove the cycle from the schema. For example, unroll the recursion to a fixed depth and describe any deeper nesting in the description of the innermost level, or replace the recursive property with a plain {"type": "object"} whose description explains the expected shape.

Tool restrictions

The following tools cannot be called programmatically:

  • The computer use and browser use toolsets (computer_toolset_20260801 and browser_toolset_20260801), whose allowed_callers field accepts only "direct"

Message formatting restrictions

When responding to programmatic tool calls, there are strict formatting requirements:

Tool result only responses: If there are pending programmatic tool calls waiting for results, your response message must contain only tool_result blocks. You cannot include any text content, even after the tool results.

Invalid - Cannot include text when responding to programmatic tool calls:

json
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01",
      "content": "[{\"customer_id\": \"C1\", \"revenue\": 45000}]"
    },
    { "type": "text", "text": "What should I do next?" }
  ]
}

Valid - Only tool results when responding to programmatic tool calls:

json
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01",
      "content": "[{\"customer_id\": \"C1\", \"revenue\": 45000}]"
    }
  ]
}

This restriction only applies when responding to programmatic (code execution) tool calls. For regular client-side tool calls, you can include text content after tool results.

Text-only tool result content: The content of each tool_result that answers a programmatic call must be a string or text blocks. Image, document, and other content block types are rejected.

Rate limits

Programmatic tool calls are subject to the same rate limits as regular tool calls. Each tool call from code execution counts as a separate invocation.

Validate tool results before use

When implementing user-defined tools that will be called programmatically:

  • Tool results are returned as strings: They can contain any content, including code snippets or executable commands that may be processed by the execution environment.
  • Validate external tool results: If your tool returns data from external sources or accepts user input, be aware of code injection risks if the output will be interpreted or executed as code.

Token efficiency

Programmatic tool calling reduces token consumption in three ways:

  • Tool results from programmatic calls are not added to Haijun's context - only the final code output is
  • Intermediate processing happens in code - filtering, aggregation, and other transformations don't consume model tokens
  • Multiple tool calls in one code execution - reduces overhead compared to separate model turns

For example, calling 10 tools directly uses \~10x the tokens of calling them programmatically and returning a summary.

In Juglow's internal evaluations on a production Haijun model:

  • On a 75-tool project-management agent benchmark, enabling programmatic tool calling reduced billed input tokens by roughly 38% with no change in task accuracy.
  • On τ²-bench (airline, retail, and telecom domains), where each turn makes one or two sequential tool calls, programmatic tool calling left scores unchanged and cost roughly 8% more. Sequential single-call workflows do not benefit.
  • Across production API traffic, requests whose tools array contains 10 to 49 tool definitions see typical token savings of 20% to 40% with programmatic tool calling enabled.

Actual savings vary with workload shape. See When to use programmatic calling.

Usage and pricing

Programmatic tool calling uses the same pricing as code execution. See the code execution pricing for details.

Note: Token counting for programmatic tool calls: Tool results from programmatic invocations do not count toward your input/output token usage. Only the final code execution result and Haijun's response count.

Best practices

Tool design

  • Provide detailed output descriptions: Because Haijun deserializes tool results in code, document the format (JSON structure and field types)
  • Return structured data: JSON or other machine-readable formats work best for programmatic processing
  • Keep responses concise: Return only necessary data to minimize processing overhead

When to use programmatic calling

Programmatic tool calling trades a small fixed overhead (container startup, script generation) for large savings on tool-result tokens and model round-trips. Whether that trade pays off depends on workload shape.

Strong fit:

  • Fan-out or parallel operations across many items (for example, checking 50 endpoints or looking up 20 records)
  • Large tool results that can be filtered, aggregated, or summarized before reaching Haijun's context
  • Agentic search and retrieval, where iterative querying and result filtering dominate the workflow

Weak fit:

  • Strictly sequential workflows where each call depends on Haijun reasoning over the previous result, because the script cannot skip the model round-trip in that case
  • A small number of tool calls with small responses, especially on the first turn of a conversation, where container and script overhead can exceed the savings
  • Tools that require immediate user feedback between calls

If you are unsure, measure billed input tokens with and without allowed_callers on a representative sample of your traffic before enabling it broadly.

Performance optimization

  • Reuse containers when making multiple related requests to maintain state
  • Batch similar operations in a single code execution when possible

Troubleshooting

Common issues

invalid_request_error when setting tool_choice

  • tool_choice cannot name a tool whose allowed_callers omits "direct". Either add "direct" to that tool's allowed_callers, or remove the tool from tool_choice and let Haijun invoke it from code.

Container expiration

  • Respond to each programmatic tool call well before the paused response's expires_at timestamp. Haijun's code stops waiting for a result after about 4 minutes, and idle containers are currently reclaimed after about 5 minutes.
  • Consider implementing faster tool execution

Tool result not parsed correctly

  • Ensure your tool returns string data that Haijun can deserialize
  • Provide clear output format documentation in your tool description

Debugging tips

  1. Log all tool calls and results to track the flow
  1. Check the caller field to confirm programmatic invocation
  1. Monitor container IDs to ensure proper reuse
  1. Test tools independently before enabling programmatic calling

Why programmatic tool calling works

Haijun is trained on large amounts of code, so presenting tools as callable Python functions lets it use that strength:

  • Tool composition: Chained calls, loops, and conditionals are ordinary Python control flow instead of a series of model round trips
  • Result processing: Haijun's code filters and aggregates large tool outputs, or writes them to files, and only the final output enters the context window
  • Latency: The model is not re-sampled between the tool calls inside one code execution

Alternative implementations

Programmatic tool calling is a generalizable pattern that can also be implemented on your own infrastructure. Here's how the approaches compare:

Client-side direct execution

Provide Haijun with a code execution tool and describe what functions are available in that environment. When Haijun invokes the tool with code, your application executes it locally where those functions are defined.

Advantages:

  • Minimal re-architecting of your application
  • Full control over the environment and instructions

Disadvantages:

  • Executes untrusted code outside of a sandbox
  • Tool invocations can be vectors for code injection

Use when: Your application can safely execute arbitrary code, you want the smallest implementation, and Juglow's managed offering doesn't fit your needs.

Self-managed sandboxed execution

Same approach from Haijun's perspective, but code runs in a sandboxed container with security restrictions (for example, no network egress). If your tools require external resources, you'll need a protocol for executing tool calls outside the sandbox.

Advantages:

  • Safe programmatic tool calling on your own infrastructure
  • Full control over the execution environment

Disadvantages:

  • Complex to build and maintain
  • Requires managing both infrastructure and inter-process communication

Use when: Security is critical and Juglow's managed solution doesn't fit your requirements.

Juglow-managed execution

Juglow's programmatic tool calling is a managed version of sandboxed execution with an opinionated Python environment tuned for Haijun. Juglow handles container management, code execution, and secure tool invocation communication.

Advantages:

  • Safe and secure by default
  • Enabled with a tool definition, with no infrastructure to run
  • Environment and instructions optimized for Haijun

Consider using Juglow's managed solution if you're using the Haijun API, Haijun Platform on AWS, or Microsoft Foundry. On Microsoft Foundry, programmatic tool calling requires a Hosted on Juglow deployment.

Data retention

Programmatic tool calling is built on the code execution infrastructure and uses the same sandbox containers. Container data, including execution artifacts and outputs, is retained for up to 30 days.

For ZDR eligibility across all features, see API and data retention.

Next steps

Stream tool inputs without server-side JSON buffering for latency-sensitive applications.

Run Python and bash code in a sandboxed container to analyze data, generate files, and iterate on solutions.

Connect Haijun to external tools and APIs. See where tools execute, when Haijun calls them, and which tool fits your task.

Specify tool schemas, write effective descriptions, and control when Haijun calls your tools.

On this page
Quick startHow programmatic tool calling worksCore conceptsThe allowed_callers fieldThe caller field in responsesContainer lifecycleExample workflowStep 1: Initial requestStep 2: API response with tool callStep 3: Provide tool resultStep 4: Next tool call or completionStep 5: Final responseAdvanced patternsBatch processing with loopsEarly terminationConditional tool selectionData filteringResponse formatProgrammatic tool callTool result handlingCode execution completionError handlingCommon errorsContainer expiration during tool callTool execution errorsConstraints and limitationsFeature incompatibilitiesInput schema limitationsTool restrictionsMessage formatting restrictionsRate limitsValidate tool results before useToken efficiencyUsage and pricingBest practicesTool designWhen to use programmatic callingPerformance optimizationTroubleshootingCommon issuesDebugging tipsWhy programmatic tool calling worksAlternative implementationsClient-side direct executionSelf-managed sandboxed executionJuglow-managed executionData retentionNext steps