Show notes
Same idea, four implementations#
Function calling is how a model reaches beyond chat and does things. The model decides when a function is needed, formats the arguments as JSON, and your code runs the operation. You return the result and the model continues reasoning. It is the foundation for agents, workflows and any tool-augmented system that goes beyond basic chat.
The concept is shared across providers, but the details are not, and those details matter once you ship. Formats differ, reliability on complex schemas differs, and so does behavior in edge cases such as parallel calls. The comparison below reflects the models discussed at the time of recording (April 2026): GPT-4o and GPT-4.1, Claude Opus 4 and Sonnet, Gemini 2.5 Pro and Flash, and DeepSeek V3 and R1.
How each provider handles tools#
- OpenAI. You send an array of tool definitions described in JSON Schema, and the model returns a
tool_callsarray. Parallel function calling lets it request several tools in one response. GPT-4o was very reliable at producing valid JSON within schema constraints, and GPT-4.1 improved instruction adherence when selecting tools. This format has become the de facto standard. - Anthropic. Tools also go in a
toolsarray, but Claude replies withtool_usecontent blocks instead of a separatetool_callsfield. Claude is notably thoughtful about whether a tool call is needed at all, and Opus 4 handled complex nested schemas well. Tool call IDs use a different format from OpenAI's, which matters if you parse responses manually. - Google. Gemini uses its own format with function declarations in the request and function call parts in the response. Gemini 2.5 Pro was competitive on accuracy but sometimes added explanatory text alongside tool calls. Flash was very fast for simple calls. The SDK's automatic function calling is handy for prototypes but takes control away in production.
- DeepSeek. DeepSeek V3 uses OpenAI-compatible function calling: same schemas, same response structure. It handled simple and moderate schemas well but could struggle with deeply nested ones, and its main appeal is a much lower price. The R1 reasoning model has more limited tool support.
The compatibility problem and the gateway fix#
Integrating each provider directly means four integration layers: OpenAI's tool_calls, Claude's tool_use blocks, Gemini's function call parts, and DeepSeek's OpenAI-compatible but not always identical behavior. That is a lot of glue code and a steady stream of "works with GPT-4o, breaks with Claude" bugs.
A normalizing gateway removes that layer. With Crazyrouter you define tools once in OpenAI format and send the request with any model name; the gateway translates the definitions for providers like Claude and Gemini, or passes them straight through to OpenAI-compatible models, and returns a response in the same structure regardless of which model handled it. Your code no longer needs to know whether the answer arrived as tool_use blocks or tool_calls, which makes comparing models on the same schema a configuration change rather than an integration project.
Which model for which job#
Based on the episode's real-world observations:
- High-reliability calls (financial transactions, database mutations, anything hard to undo): GPT-4o or Claude Opus 4, which produced the most consistently valid JSON with the fewest spurious calls.
- High-volume simple tools (data fetches, searches, lookups): Gemini Flash or DeepSeek V3, fast and inexpensive enough for the job.
- Complex agents with many tools: Claude Opus 4 was better at choosing the right tool from ten or twenty options, whereas GPT-4o sometimes picked the first plausible one.
- Parallel tool calling: GPT-4o and GPT-4.1 were strongest; Claude supports it but often prefers sequential calls.
Mixing models this way keeps costs down: you spend on frontier models only where correctness or tool selection is genuinely hard.
Five edge cases to test before you commit#
Before you commit to a model for tool-calling work, run these against your own tool schemas on every candidate. Running all of them through one normalized endpoint keeps the comparison fair, because only the model changes:
- Optional parameters. Does the model omit them correctly or fill sensible defaults?
- Enum values. Does it stay within the defined options or invent new ones?
- Nested objects. Can it handle a schema three levels deep?
- The don't-call case. Given a prompt that needs no tool, does it reply with text instead of forcing a call?
- Parallel calls. Given a request that needs two tools, does it call both or only one?
The broader lesson: do not lock your code to one provider's function-calling format. Normalize the interface, test several models on your real schemas, and route each task to the model that handles it best.
