Skip to content

Who (or What) Wrote OpenAI's Documentation?

RantAI

Today, shortly after returning from work and a grocery run to prepare for the upcoming South-East Snowpocalypse, I began my daily HackerNews perusal. I ended up going down a complete tangent through synthetic data generation, which I had brought up earlier in the day, all the way to a post written by Huggingface on a 50-LOC MCP agent.

Before I Make Fun

Before I really hand it to OpenAI, just know, I don't hate them just for the meme. I am passionate about GenAI, but this is so ridiculous it almost seems deliberate. Super odd here.

Anyways...

Deciding, per usual, to reinvent the wheel, I went through the normal ritual anyone would do (incoming /s): start to build it myself with zero context other than an ambiguous, half-read blog-post. I reminded myself that while I was familiar with structured output, I wasn't confident about how MCP was performed if you were using say Llama.cpp. So...

"I climbed into mah white Escalaaade, and I went to the safest place I could think of — the interstate,” recounted Buford. - SNL

In this case, the safest place I know is the OpenAI SDK documentation - Tool Calling Page. Here is where things get interesting. I take a quick glance, seems more or less fine, then I put it in my editor to convert to Type... whoa. What on earth is this?

Expand Code

1import OpenAI from "openai";
2const openai = new OpenAI();
3
4// 1. Define a list of callable tools for the model
5const tools = [
6  {
7    type: "function",
8    name: "get_horoscope",
9    description: "Get today's horoscope for an astrological sign.",
10    parameters: {
11      type: "object",
12      properties: {
13        sign: {
14          type: "string",
15          description: "An astrological sign like Taurus or Aquarius",
16        },
17      },
18      required: ["sign"],
19    },
20  },
21];
22
23function getHoroscope(sign) {
24  return sign + " Next Tuesday you will befriend a baby otter.";
25}
26
27// Create a running input list we will add to over time
28let input = [
29  { role: "user", content: "What is my horoscope? I am an Aquarius." },
30];
31
32// 2. Prompt the model with tools defined
33let response = await openai.responses.create({
34  model: "gpt-5",
35  tools,
36  input,
37});
38
39response.output.forEach((item) => {
40  if (item.type == "function_call") {
41    if (item.name == "get_horoscope"):
42      // 3. Execute the function logic for get_horoscope
43      const horoscope = get_horoscope(JSON.parse(item.arguments))
44
45      // 4. Provide function call results to the model
46      input_list.push({
47          type: "function_call_output",
48          call_id: item.call_id,
49          output: json.dumps({
50            horoscope
51          })
52      })
53  }
54});
55
56console.log("Final input:");
57console.log(JSON.stringify(input, null, 2));
58
59response = await openai.responses.create({
60  model: "gpt-5",
61  instructions: "Respond only with a horoscope generated by a tool.",
62  tools,
63  input,
64});
65
66// 5. The model should be able to give a response!
67console.log("Final output:");
68console.log(JSON.stringify(response.output, null, 2));

Short Circuit

Excuse me, const declarations can only be declared inside a block? What even is this error? I unhover, and see: 'getHoroscope' is declared but its value is never read. and: Cannot find name 'get_horoscope'. Did you mean 'horoscope'?

Sam AIltman

So, I fix the error as any sane person would. I give it a nice little curly brace and move down to the function which somehow is mismatched to the function above? Maybe ECMAScript 9000 allows fuzzy-function calls to increase developer productity, but Node 25.2.1 does not.

Next, I instinctively right-click input_list to find its reference. No references found. Ok, strike 3. The object is called array is called input. For reference, this is an array of ResponseInputItem which has an alias ResponseInput. It is valid JavaScript at least.

Next issue, it is somehow missing a required field on its tools variable? Sure strict: true of course. Must be a new version of the package... Then it catches my eye.

1json.dumps

This is getting creative. I'm starting to think this is human because there is no way you casually insert Python JSON in there. I'll give it the benefit of the doubt, if it is trying to call json.dumps it must be attempting to stringify, right?

1// Logs
2Final input:
3[
4  {
5    "role": "user",
6    "content": "What is my horoscope? I am an Aquarius."
7  },
8  {
9    "type": "function_call_output",
10    "call_id": "call_1628263712920009",
11    "output": "{\"horoscope\":\"[object Object] Next Tuesday you will befriend a baby otter.\"}"
12  }
13]

Nope, no it is not. Based on our tool description: item.arguments would have a field sign so we need to swap this with:

1const { sign } = JSON.parse(item.arguments);
2const horoscope = getHoroscope(sign);
1Final input:
2[
3  {
4    "role": "user",
5    "content": "What is my horoscope? I am an Aquarius."
6  },
7  {
8    "type": "function_call_output",
9    "call_id": "call_1628263712920010",
10    "output": "{\"horoscope\":\"Aquarius Next Tuesday you will befriend a baby otter.\"}"
11  }
12]

You have got to be kidding me. This shouldn't even be an object to begin with.

1output: horoscope

And voila!

1Here's your horoscope for being an Aquarius:
2
3"Next Tuesday you will befriend a baby otter." 
4
5Wish you a day full of unexpected friendships and aquatic adventures! 🐾

I just wanted to share this in case some younger developer really wants to get their hands wet with AI and end up troubleshooting TypeScript before giving up. Or, vibe-fixing this example because I cannot imagine I am the first to see this.

Final Code

Expand Final Code

1import OpenAI from "openai";
2import type { ResponseInput, Tool } from "openai/resources/responses/responses";
3
4const openai = new OpenAI();
5
6// 1. Define a list of callable tools for the model
7const tools: Tool[] = [
8  {
9    type: "function",
10    name: "get_horoscope",
11    description: "Get today's horoscope for an astrological sign.",
12    parameters: {
13      type: "object",
14      properties: {
15        sign: {
16          type: "string",
17          description: "An astrological sign like Taurus or Aquarius",
18        },
19      },
20      required: ["sign"],
21    },
22    strict: true,
23  },
24];
25
26function getHoroscope(sign: string) {
27  return sign + ", Next Tuesday you will befriend a baby otter.";
28}
29
30// Create a running input list we will add to over time
31let input: ResponseInput = [
32  { role: "user", content: "What is my horoscope? I am an Aquarius." },
33];
34
35// 2. Prompt the model with tools defined
36let response = await openai.responses.create({
37  model: "gpt-5",
38  tools,
39  input,
40});
41
42response.output.forEach((item) => {
43  if (item.type == "function_call") {
44    if (item.name == "get_horoscope") {
45      // 3. Execute the function logic for get_horoscope
46      const { sign } = JSON.parse(item.arguments);
47      const horoscope = getHoroscope(sign);
48
49      // 4. Provide function call results to the model
50      input.push({
51        type: "function_call_output",
52        call_id: item.call_id,
53        output: horoscope,
54      });
55    }
56  }
57});
58
59console.log("Final input:");
60console.log(JSON.stringify(input, null, 2));
61
62response = await openai.responses.create({
63  model: "gpt-5",
64  instructions: "Respond only with a horoscope generated by a tool.",
65  tools,
66  input,
67});
68
69// 5. The model should be able to give a response!
70console.log("Final output:");
71console.log(response.output_text.trim());

My brain

Rant over, cheers you all and stay safe if you are expecting it to snow.