SpyBara
Go Premium

Documentation 2026-07-24 19:01 UTC to 2026-07-25 05:59 UTC

81 files changed +1,437 −1,434. View all changes and history on the product overview
2026
Sat 25 05:59 Fri 24 19:01 Thu 23 03:02 Wed 22 20:02 Tue 21 15:00 Mon 20 21:59 Sat 18 22:00 Fri 17 19:58 Thu 16 17:00 Wed 15 16:58 Tue 14 21:58 Mon 13 21:58 Sat 11 07:01 Fri 10 23:02 Thu 9 17:03 Wed 8 22:02 Mon 6 22:58 Sat 4 16:59
Details

21 21 

22```python22```python

23file = client.files.create(23file = client.files.create(

24 file=open("revenue-forecast.csv", "rb"),24 file=open("revenue-forecast.csv", "rb"), purpose="assistants"

25 purpose='assistants'

26)25)

27```26```

28 27 


49 description="You are great at creating beautiful data visualizations. You analyze data present in .csv files, understand trends, and come up with data visualizations relevant to those trends. You also share a brief text summary of the trends observed.",48 description="You are great at creating beautiful data visualizations. You analyze data present in .csv files, understand trends, and come up with data visualizations relevant to those trends. You also share a brief text summary of the trends observed.",

50 model="gpt-4o",49 model="gpt-4o",

51 tools=[{"type": "code_interpreter"}],50 tools=[{"type": "code_interpreter"}],

52 tool_resources={51 tool_resources={"code_interpreter": {"file_ids": [file.id]}},

53 "code_interpreter": {

54 "file_ids": [file.id]

55 }

56 }

57)52)

58```53```

59 54 


107 "role": "user",102 "role": "user",

108 "content": "Create 3 data visualizations based on the trends in this file.",103 "content": "Create 3 data visualizations based on the trends in this file.",

109 "attachments": [104 "attachments": [

110 {105 {"file_id": file.id, "tools": [{"type": "code_interpreter"}]}

111 "file_id": file.id,106 ],

112 "tools": [{"type": "code_interpreter"}]

113 }

114 ]

115 }107 }

116 ]108 ]

117)109)


165Tools cannot access image content unless specified. To pass image files to Code Interpreter, add the file ID in the message `attachments` list to allow the tool to read and analyze the input. Image URLs cannot be downloaded in Code Interpreter today.157Tools cannot access image content unless specified. To pass image files to Code Interpreter, add the file ID in the message `attachments` list to allow the tool to read and analyze the input. Image URLs cannot be downloaded in Code Interpreter today.

166 158 

167```python159```python

168file = client.files.create(160file = client.files.create(file=open("myimage.png", "rb"), purpose="vision")

169 file=open("myimage.png", "rb"),

170 purpose="vision"

171)

172thread = client.beta.threads.create(161thread = client.beta.threads.create(

173 messages=[162 messages=[

174 {163 {


176 "content": [165 "content": [

177 {166 {

178 "type": "text",167 "type": "text",

179 "text": "What is the difference between these images?"168 "text": "What is the difference between these images?",

180 },169 },

181 {170 {

182 "type": "image_url",171 "type": "image_url",

183 "image_url": {"url": "https://example.com/image.png"}172 "image_url": {

173 "url": "https://openai-documentation.vercel.app/images/cat_and_otter.png"

184 },174 },

185 {

186 "type": "image_file",

187 "image_file": {"file_id": file.id}

188 },175 },

176 {"type": "image_file", "image_file": {"file_id": file.id}},

189 ],177 ],

190 }178 }

191 ]179 ]


209 },198 },

210 {199 {

211 "type": "image_url",200 "type": "image_url",

212 "image_url": {"url": "https://example.com/image.png"}201 "image_url": {"url": "https://openai-documentation.vercel.app/images/cat_and_otter.png"}

213 },202 },

214 {203 {

215 "type": "image_file",204 "type": "image_file",


245},234},

246{235{

247"type": "image_url",236"type": "image_url",

248"image_url": {"url": "https://example.com/image.png"}237"image_url": {"url": "https://openai-documentation.vercel.app/images/cat_and_otter.png"}

249},238},

250{239{

251"type": "image_file",240"type": "image_file",


271 {260 {

272 "role": "user",261 "role": "user",

273 "content": [262 "content": [

274 {263 {"type": "text", "text": "What is this an image of?"},

275 "type": "text",

276 "text": "What is this an image of?"

277 },

278 {264 {

279 "type": "image_url",265 "type": "image_url",

280 "image_url": {266 "image_url": {

281 "url": "https://example.com/image.png",267 "url": "https://openai-documentation.vercel.app/images/cat_and_otter.png",

282 "detail": "high"268 "detail": "high",

283 }269 },

284 },270 },

285 ],271 ],

286 }272 }


301 {287 {

302 "type": "image_url",288 "type": "image_url",

303 "image_url": {289 "image_url": {

304 "url": "https://example.com/image.png",290 "url": "https://openai-documentation.vercel.app/images/cat_and_otter.png",

305 "detail": "high"291 "detail": "high"

306 }292 }

307 },293 },


328 {314 {

329 "type": "image_url",315 "type": "image_url",

330 "image_url": {316 "image_url": {

331 "url": "https://example.com/image.png",317 "url": "https://openai-documentation.vercel.app/images/cat_and_otter.png",

332 "detail": "high"318 "detail": "high"

333 }319 }

334 },320 },


373When annotations are present in the Message object, you'll see illegible model-generated substrings in the text that you should replace with the annotations. These strings may look something like `【13†source】` or `sandbox:/mnt/data/file.csv`. Here’s an example python code snippet that replaces these strings with the annotations.359When annotations are present in the Message object, you'll see illegible model-generated substrings in the text that you should replace with the annotations. These strings may look something like `【13†source】` or `sandbox:/mnt/data/file.csv`. Here’s an example python code snippet that replaces these strings with the annotations.

374 360 

375```python361```python

362import os

363from pathlib import Path

364 

365thread_id = os.environ["OPENAI_THREAD_ID"]

366message_id = os.environ["OPENAI_MESSAGE_ID"]

367downloads = Path("downloads")

368downloads.mkdir(exist_ok=True)

369 

376# Retrieve the message object370# Retrieve the message object

377message = client.beta.threads.messages.retrieve(371message = client.beta.threads.messages.retrieve(

378 thread_id="...",372 thread_id=thread_id,

379 message_id="..."373 message_id=message_id,

380)374)

381 375 

382# Extract the message content376# Extract the message content


387 381 

388# Iterate over the annotations and add footnotes382# Iterate over the annotations and add footnotes

389 383 

390for index, annotation in enumerate(annotations): # Replace the text with a footnote384for index, annotation in enumerate(annotations):

391message_content.value = message_content.value.replace(annotation.text, f' [{index}]')385 # Replace the text with a footnote.

386 message_content.value = message_content.value.replace(

387 annotation.text, f" [{index}]"

388 )

392 389 

393 # Gather citations based on annotation attributes390 # Gather citations based on annotation attributes

394 if (file_citation := getattr(annotation, 'file_citation', None)):391 if file_citation := getattr(annotation, "file_citation", None):

395 cited_file = client.files.retrieve(file_citation.file_id)392 cited_file = client.files.retrieve(file_citation.file_id)

396 citations.append(f'[{index}] {file_citation.quote} from {cited_file.filename}')393 citations.append(f"[{index}] {file_citation.quote} from {cited_file.filename}")

397 elif (file_path := getattr(annotation, 'file_path', None)):394 elif file_path := getattr(annotation, "file_path", None):

398 cited_file = client.files.retrieve(file_path.file_id)395 cited_file = client.files.retrieve(file_path.file_id)

399 citations.append(f'[{index}] Click <here> to download {cited_file.filename}')396 file_content = client.files.content(file_path.file_id)

400 # Note: File download functionality not implemented above for brevity397 output_path = downloads / Path(cited_file.filename).name

398 output_path.write_bytes(file_content.read())

399 citations.append(f"[{index}] Downloaded {output_path}")

401 400 

402# Add footnotes to the end of the message before displaying to user401# Add footnotes to the end of the message before displaying to user

403 402 

404message_content.value += '\n' + '\n'.join(citations)403message_content.value += "\n" + "\n".join(citations)

405```404```

406 405 

407 406 


412```python411```python

413run = client.beta.threads.runs.create(412run = client.beta.threads.runs.create(

414 thread_id=thread.id,413 thread_id=thread.id,

415 assistant_id=assistant.id414 assistant_id=assistant.id,

416)415)

417```416```

418 417 


442 assistant_id=assistant.id,441 assistant_id=assistant.id,

443 model="gpt-4o",442 model="gpt-4o",

444 instructions="New instructions that override the Assistant instructions",443 instructions="New instructions that override the Assistant instructions",

445 tools=[{"type": "code_interpreter"}, {"type": "file_search"}]444 tools=[{"type": "code_interpreter"}, {"type": "file_search"}],

446)445)

447```446```

448 447 

Details

94 client:load94 client:load

95 snippets={[95 snippets={[

96 {96 {

97 language: "python",97 language: "json",

98 code: `98 code: `

99{99{

100 "id": "run_FKIpcs5ECSwuCmehBqsqkORj",100 "id": "run_FKIpcs5ECSwuCmehBqsqkORj",


144 title: "Run object",144 title: "Run object",

145 },145 },

146 {146 {

147 language: "python",147 language: "json",

148 code: `148 code: `

149{149{

150 "id": "resp_687a7b53036c819baad6012d58b39bcb074adcd9e24850fc",150 "id": "resp_687a7b53036c819baad6012d58b39bcb074adcd9e24850fc",


237 237 

238### 2. Move new user chats over to conversations and responses238### 2. Move new user chats over to conversations and responses

239 239 

240We will not provide an automated tool for migrating Threads to Conversations. Instead, we recommend migrating new user threads onto conversations and backfilling old ones as necessary.240We will not provide an automated tool for migrating Threads to Conversations. Instead, we recommend migrating new user threads onto conversations and migrating older ones as necessary.

241 241 

242Here's an example for how you might backfill a thread:242Here's an example for how you might backfill a thread:

243 243 

244```python244```python

245thread_id = "thread_EIpHrTAVe0OzoLQg3TXfvrkG"245import os

246 246 

247for page in openai.beta.threads.messages.list(thread_id=thread_id, order="asc").iter_pages():247from openai import OpenAI

248 

249openai = OpenAI()

250messages = []

251thread_id = os.environ["OPENAI_THREAD_ID"]

252 

253for page in openai.beta.threads.messages.list(

254 thread_id=thread_id, order="asc"

255).iter_pages():

248 messages += page.data256 messages += page.data

249 257 

250items = []258items = []


256 match content.type:264 match content.type:

257 case "text":265 case "text":

258 item_content_type = "input_text" if m.role == "user" else "output_text"266 item_content_type = "input_text" if m.role == "user" else "output_text"

259 item_content += [{"type": item_content_type, "text": content.text.value}]267 item_content += [

268 {"type": item_content_type, "text": content.text.value}

269 ]

260 case "image_url":270 case "image_url":

261 item_content + [271 item_content += [

262 {272 {

263 "type": "input_image",273 "type": "input_image",

264 "image_url": content.image_url.url,274 "image_url": content.image_url.url,


276 286 

277## Comparing full examples287## Comparing full examples

278 288 

279Here’s a few simple examples of integrations using both the Assistants API and the Responses API so you can see how they compare.289Here are a few examples of integrations using both the Assistants API and the Responses API so you can see how they compare.

280 290 

281### User chat app291### User chat app

282 292 


285<div data-content-switcher-pane data-value="assistants">295<div data-content-switcher-pane data-value="assistants">

286 <div class="hidden">Assistants API</div>296 <div class="hidden">Assistants API</div>

287 ```python297 ```python

288thread = openai.threads.create()298threads_by_session: dict[str, str] = {}

299 

300 

301@app.post("/messages")

302async def message(message: Message):

303 thread_id = threads_by_session.get(message.session_id)

304 if thread_id is None:

305 thread_id = openai.beta.threads.create().id

306 threads_by_session[message.session_id] = thread_id

289 307 

290 @app.post("/messages")

291 async def message(message: Message):

292 openai.beta.threads.messages.create(308 openai.beta.threads.messages.create(

309 thread_id=thread_id,

293 role="user",310 role="user",

294 content=message.content311 content=message.content,

295 )312 )

296 313 

297 run = openai.beta.threads.runs.create(314 run = openai.beta.threads.runs.create(

298 assistant_id=os.getenv("ASSISTANT_ID"),315 assistant_id=os.environ["OPENAI_ASSISTANT_ID"],

299 thread_id=thread.id316 thread_id=thread_id,

300 )317 )

301 while run.status in ("queued", "in_progress"):318 while run.status in ("queued", "in_progress"):

302 await asyncio.sleep(1)319 await asyncio.sleep(1)

303 run = openai.beta.threads.runs.retrieve(thread_id=thread_id, run_id=run.id)320 run = openai.beta.threads.runs.retrieve(

321 thread_id=thread_id,

322 run_id=run.id,

323 )

304 324 

305 messages = openai.beta.threads.messages.list(325 messages = openai.beta.threads.messages.list(

306 order="desc", limit=1, thread_id=thread.id326 order="desc",

327 limit=1,

328 thread_id=thread_id,

307 )329 )

308 330 

309 return { "content": messages[-1].content }331 return {"content": messages.data[0].content}

310```332```

311 333 

312 334 


314 <div data-content-switcher-pane data-value="responses" hidden>336 <div data-content-switcher-pane data-value="responses" hidden>

315 <div class="hidden">Responses API</div>337 <div class="hidden">Responses API</div>

316 ```python338 ```python

317conversation = openai.conversations.create()339conversations_by_session: dict[str, str] = {}

340 

341 

342@app.post("/messages")

343async def message(message: Message):

344 conversation_id = conversations_by_session.get(message.session_id)

345 if conversation_id is None:

346 conversation_id = openai.conversations.create().id

347 conversations_by_session[message.session_id] = conversation_id

318 348 

319 @app.post("/messages")

320 async def message(message: Message):

321 response = openai.responses.create(349 response = openai.responses.create(

322 prompt={ "id": os.getenv("PROMPT_ID") },350 prompt={"id": os.environ["OPENAI_PROMPT_ID"]},

323 input=[{ "role": "user", "content": message.content }]351 input=[{"role": "user", "content": message.content}],

352 conversation=conversation_id,

324 )353 )

325 354 

326 return { "content": response.output_text }355 return {"content": response.output_text}

327```356```

328 357 

329 358 

Details

18assistant = client.beta.assistants.create(18assistant = client.beta.assistants.create(

19 instructions="You are a personal math tutor. When asked a math question, write and run code to answer the question.",19 instructions="You are a personal math tutor. When asked a math question, write and run code to answer the question.",

20 model="gpt-4o",20 model="gpt-4o",

21 tools=[{"type": "code_interpreter"}]21 tools=[{"type": "code_interpreter"}],

22)22)

23```23```

24 24 


53 53 

54```python54```python

55# Upload a file with an "assistants" purpose55# Upload a file with an "assistants" purpose

56file = client.files.create(56file = client.files.create(file=open("mydata.csv", "rb"), purpose="assistants")

57 file=open("mydata.csv", "rb"),

58 purpose='assistants'

59)

60 57 

61# Create an assistant using the file ID58# Create an assistant using the file ID

62assistant = client.beta.assistants.create(59assistant = client.beta.assistants.create(

63 instructions="You are a personal math tutor. When asked a math question, write and run code to answer the question.",60 instructions="You are a personal math tutor. When asked a math question, write and run code to answer the question.",

64 model="gpt-4o",61 model="gpt-4o",

65 tools=[{"type": "code_interpreter"}],62 tools=[{"type": "code_interpreter"}],

66 tool_resources={63 tool_resources={"code_interpreter": {"file_ids": [file.id]}},

67 "code_interpreter": {

68 "file_ids": [file.id]

69 }

70 }

71)64)

72```65```

73 66 


125 "role": "user",118 "role": "user",

126 "content": "I need to solve the equation `3x + 11 = 14`. Can you help me?",119 "content": "I need to solve the equation `3x + 11 = 14`. Can you help me?",

127 "attachments": [120 "attachments": [

128 {121 {"file_id": file.id, "tools": [{"type": "code_interpreter"}]}

129 "file_id": file.id,122 ],

130 "tools": [{"type": "code_interpreter"}]

131 }

132 ]

133 }123 }

134 ]124 ]

135)125)


203The file content can then be downloaded by passing the file ID to the Files API:193The file content can then be downloaded by passing the file ID to the Files API:

204 194 

205```python195```python

196import os

197 

206from openai import OpenAI198from openai import OpenAI

207 199 

200file_id = os.environ["OPENAI_FILE_ID"]

208client = OpenAI()201client = OpenAI()

209 202 

210image_data = client.files.content("file-abc123")203image_data = client.files.content(file_id)

211image_data_bytes = image_data.read()204image_data_bytes = image_data.read()

212 205 

213with open("./my-image.png", "wb") as file:206with open("./my-image.png", "wb") as file:


275By listing the steps of a Run that called Code Interpreter, you can inspect the code `input` and `outputs` logs of Code Interpreter:268By listing the steps of a Run that called Code Interpreter, you can inspect the code `input` and `outputs` logs of Code Interpreter:

276 269 

277```python270```python

271import os

272 

273thread_id = os.environ["OPENAI_THREAD_ID"]

274run_id = os.environ["OPENAI_RUN_ID"]

275 

278run_steps = client.beta.threads.runs.steps.list(276run_steps = client.beta.threads.runs.steps.list(

279 thread_id=thread.id,277 thread_id=thread_id,

280 run_id=run.id278 run_id=run_id,

281)279)

282```280```

283 281 

Details

29client = OpenAI()30client = OpenAI()

30 31 

31assistant = client.beta.assistants.create(32assistant = client.beta.assistants.create(

32instructions="You are a weather bot. Use the provided functions to answer questions.",33 instructions="You are a weather bot. Use the provided functions to answer questions.",

33model="gpt-4o",34 model="gpt-4o",

34tools=[35 tools=[

35{36 {

36"type": "function",37 "type": "function",

37"function": {38 "function": {

38"name": "get_current_temperature",39 "name": "get_current_temperature",

39"description": "Get the current temperature for a specific location",40 "description": "Get the current temperature for a specific location",

40"parameters": {41 "parameters": {

41"type": "object",42 "type": "object",

42"properties": {43 "properties": {

43"location": {44 "location": {

44"type": "string",45 "type": "string",

45"description": "The city and state, e.g., San Francisco, CA"46 "description": "The city and state, e.g., San Francisco, CA",

46},47 },

47"unit": {48 "unit": {

48"type": "string",49 "type": "string",

49"enum": ["Celsius", "Fahrenheit"],50 "enum": ["Celsius", "Fahrenheit"],

50"description": "The temperature unit to use. Infer this from the user's location."51 "description": "The temperature unit to use. Infer this from the user's location.",

51}52 },

52},53 },

53"required": ["location", "unit"]54 "required": ["location", "unit"],

54}55 },

55}56 },

56},57 },

57{58 {

58"type": "function",59 "type": "function",

59"function": {60 "function": {

60"name": "get_rain_probability",61 "name": "get_rain_probability",

61"description": "Get the probability of rain for a specific location",62 "description": "Get the probability of rain for a specific location",

62"parameters": {63 "parameters": {

63"type": "object",64 "type": "object",

64"properties": {65 "properties": {

65"location": {66 "location": {

66"type": "string",67 "type": "string",

67"description": "The city and state, e.g., San Francisco, CA"68 "description": "The city and state, e.g., San Francisco, CA",

68}69 }

69},70 },

70"required": ["location"]71 "required": ["location"],

71}72 },

72}73 },

73}74 },

74]75 ],

75)76)

76```77```

77 78 


221client = OpenAI()223client = OpenAI()

222 224 

223assistant = client.beta.assistants.create(225assistant = client.beta.assistants.create(

224instructions="You are a weather bot. Use the provided functions to answer questions.",226 instructions="You are a weather bot. Use the provided functions to answer questions.",

225model="gpt-4o-2024-08-06",227 model="gpt-4o-2024-08-06",

226tools=[228 tools=[

227{229 {

228"type": "function",230 "type": "function",

229"function": {231 "function": {

230"name": "get_current_temperature",232 "name": "get_current_temperature",

231"description": "Get the current temperature for a specific location",233 "description": "Get the current temperature for a specific location",

232"parameters": {234 "parameters": {

233"type": "object",235 "type": "object",

234"properties": {236 "properties": {

235"location": {237 "location": {

236"type": "string",238 "type": "string",

237"description": "The city and state, e.g., San Francisco, CA"239 "description": "The city and state, e.g., San Francisco, CA",

238},240 },

239"unit": {241 "unit": {

240"type": "string",242 "type": "string",

241"enum": ["Celsius", "Fahrenheit"],243 "enum": ["Celsius", "Fahrenheit"],

242"description": "The temperature unit to use. Infer this from the user's location."244 "description": "The temperature unit to use. Infer this from the user's location.",

243}245 },

244},246 },

245"required": ["location", "unit"],247 "required": ["location", "unit"],

246// highlight-start248 # highlight-start

247"additionalProperties": False249 "additionalProperties": False,

248// highlight-end250 # highlight-end

249},251 },

250// highlight-start252 # highlight-start

251"strict": True253 "strict": True,

252// highlight-end254 # highlight-end

253}255 },

254},256 },

255{257 {

256"type": "function",258 "type": "function",

257"function": {259 "function": {

258"name": "get_rain_probability",260 "name": "get_rain_probability",

259"description": "Get the probability of rain for a specific location",261 "description": "Get the probability of rain for a specific location",

260"parameters": {262 "parameters": {

261"type": "object",263 "type": "object",

262"properties": {264 "properties": {

263"location": {265 "location": {

264"type": "string",266 "type": "string",

265"description": "The city and state, e.g., San Francisco, CA"267 "description": "The city and state, e.g., San Francisco, CA",

266}268 }

267},269 },

268"required": ["location"],270 "required": ["location"],

269// highlight-start271 # highlight-start

270"additionalProperties": False272 "additionalProperties": False,

271// highlight-end273 # highlight-end

272},274 },

273// highlight-start275 # highlight-start

274"strict": True276 "strict": True,

275// highlight-end277 # highlight-end

276}278 },

277}279 },

278]280 ],

279)281)

280```282```

281 283 

Details

1# Advanced usage1# Advanced usage

2 2 

3OpenAI's text generation models (often called generative pre-trained transformers or large language models) have been trained to understand natural language, code, and images. The models provide text outputs in response to their inputs. The text inputs to these models are also referred to as "prompts". Designing a prompt is essentially how you “program” a large language model model, usually by providing instructions or some examples of how to successfully complete a task.3OpenAI's text generation models (often called generative pre-trained transformers or large language models) have been trained to understand natural language, code, and images. The models provide text outputs in response to their inputs. The text inputs to these models are also referred to as "prompts." Designing a prompt is essentially how you “program” a large language model, usually by providing instructions or some examples of how to successfully complete a task.

4 4 

5## Reproducible outputs5## Reproducible outputs

6 6 

7Chat Completions are non-deterministic by default (which means model outputs may differ from request to request). That being said, we offer some control towards deterministic outputs by giving you access to the [seed](https://developers.openai.com/api/docs/api-reference/chat/create#chat-create-seed) parameter and the [system_fingerprint](https://developers.openai.com/api/docs/api-reference/completions/object#completions/object-system_fingerprint) response field.7Chat Completions are non-deterministic by default (which means model outputs may differ from request to request). That being said, we offer some control towards deterministic outputs by giving you access to the [`seed`](https://developers.openai.com/api/docs/api-reference/chat/create#chat-create-seed) parameter and the [`system_fingerprint`](https://developers.openai.com/api/docs/api-reference/completions/object#completions/object-system_fingerprint) response field.

8 8 

9To receive (mostly) deterministic outputs across API calls, you can:9To receive (mostly) deterministic outputs across API calls, you can:

10 10 

11- Set the [seed](https://developers.openai.com/api/docs/api-reference/chat/create#chat-create-seed) parameter to any integer of your choice and use the same value across requests you'd like deterministic outputs for.11- Set the [seed](https://developers.openai.com/api/docs/api-reference/chat/create#chat-create-seed) parameter to any integer of your choice and use the same value across requests you'd like deterministic outputs for.

12- Ensure all other parameters (like `prompt` or `temperature`) are the exact same across requests.12- Ensure all other parameters (like `prompt` or `temperature`) are the exact same across requests.

13 13 

14Sometimes, determinism may be impacted due to necessary changes OpenAI makes to model configurations on our end. To help you keep track of these changes, we expose the [system_fingerprint](https://developers.openai.com/api/docs/api-reference/chat/object#chat/object-system_fingerprint) field. If this value is different, you may see different outputs due to changes we've made on our systems.14Sometimes, determinism may be impacted due to necessary changes OpenAI makes to model configurations on our end. To help you keep track of these changes, we expose the [`system_fingerprint`](https://developers.openai.com/api/docs/api-reference/chat/object#chat/object-system_fingerprint) field. If this value is different, you may see different outputs due to changes we've made on our systems.

15 15 

16<a16<a

17 href="https://cookbook.openai.com/examples/reproducible_outputs_with_the_seed_parameter"17 href="https://cookbook.openai.com/examples/reproducible_outputs_with_the_seed_parameter"


29 29 

30## Managing tokens30## Managing tokens

31 31 

32Language models read and write text in chunks called tokens. In English, a token can be as short as one character or as long as one word (e.g., `a` or ` apple`), and in some languages tokens can be even shorter than one character or even longer than one word.32Language models read and write text in chunks called tokens. In English, a token can be as short as one character or as long as one word (for example, `a` or ` apple`), and in some languages tokens can be even shorter than one character or even longer than one word.

33 33 

34As a rough rule of thumb, 1 token is approximately 4 characters or 0.75 words for English text.34As a rough rule of thumb, 1 token is approximately 4 characters or 0.75 words for English text.

35 35 


53 53 

54Both input and output tokens count toward these quantities. For example, if your API call used 10 tokens in the message input and you received 20 tokens in the message output, you would be billed for 30 tokens. Note however that for some models the price per token is different for tokens in the input vs. the output (see the [pricing](https://openai.com/api/pricing) page for more information).54Both input and output tokens count toward these quantities. For example, if your API call used 10 tokens in the message input and you received 20 tokens in the message output, you would be billed for 30 tokens. Note however that for some models the price per token is different for tokens in the input vs. the output (see the [pricing](https://openai.com/api/pricing) page for more information).

55 55 

56To see how many tokens are used by an API call, check the `usage` field in the API response (e.g., `response['usage']['total_tokens']`).56To see how many tokens are used by an API call, check the `usage` field in the API response (for example, `response['usage']['total_tokens']`).

57 57 

58Chat models like `gpt-3.5-turbo` and `gpt-4-turbo-preview` use tokens in the same way as the models available in the completions API, but because of their message-based formatting, it's more difficult to count how many tokens will be used by a conversation.58Chat models like `gpt-3.5-turbo` and `gpt-4-turbo-preview` use tokens in the same way as the models available in the completions API, but because of their message-based formatting, it's more difficult to count how many tokens will be used by a conversation.

59 59 


73 if model == "gpt-3.5-turbo-0613": # note: future models may deviate from this73 if model == "gpt-3.5-turbo-0613": # note: future models may deviate from this

74 num_tokens = 074 num_tokens = 0

75 for message in messages:75 for message in messages:

76 num_tokens += 4 # every message follows <im_start>{role/name}\n{content}<im_end>\n76 num_tokens += (

77 4 # every message follows <im_start>{role/name}\n{content}<im_end>\n

78 )

77 for key, value in message.items():79 for key, value in message.items():

78 num_tokens += len(encoding.encode(value))80 num_tokens += len(encoding.encode(value))

79 if key == "name": # if there's a name, the role is omitted81 if key == "name": # if there's a name, the role is omitted

80 num_tokens += -1 # role is always required and always 1 token82 num_tokens += -1 # role is always required and always 1 token

81 num_tokens += 2 # every reply is primed with <im_start>assistant83 num_tokens += 2 # every reply is primed with <im_start>assistant

82 return num_tokens84 return num_tokens

83 else:85 raise ValueError(

84 raise NotImplementedError(f"""num_tokens_from_messages() is not presently implemented for model {model}.""")86 f"num_tokens_from_messages() only supports gpt-3.5-turbo-0613, not {model}."

87 )

85```88```

86 89 

87 90 


89 92 

90```python93```python

91messages = [94messages = [

92 {"role": "system", "content": "You are a helpful, pattern-following assistant that translates corporate jargon into plain English."},95 {

93 {"role": "system", "name":"example_user", "content": "New synergies will help drive top-line growth."},96 "role": "system",

94 {"role": "system", "name": "example_assistant", "content": "Things working well together will increase revenue."},97 "content": "You are a helpful, pattern-following assistant that translates corporate jargon into plain English.",

95 {"role": "system", "name":"example_user", "content": "Let's circle back when we have more bandwidth to touch base on opportunities for increased leverage."},98 },

96 {"role": "system", "name": "example_assistant", "content": "Let's talk later when we're less busy about how to do better."},99 {

97 {"role": "user", "content": "This late pivot means we don't have time to boil the ocean for the client deliverable."},100 "role": "system",

101 "name": "example_user",

102 "content": "New synergies will help drive top-line growth.",

103 },

104 {

105 "role": "system",

106 "name": "example_assistant",

107 "content": "Things working well together will increase revenue.",

108 },

109 {

110 "role": "system",

111 "name": "example_user",

112 "content": "Let's circle back when we have more bandwidth to touch base on opportunities for increased leverage.",

113 },

114 {

115 "role": "system",

116 "name": "example_assistant",

117 "content": "Let's talk later when we're less busy about how to do better.",

118 },

119 {

120 "role": "user",

121 "content": "This late pivot means we don't have time to boil the ocean for the client deliverable.",

122 },

98]123]

99 124 

100model = "gpt-3.5-turbo-0613"125model = "gpt-3.5-turbo-0613"


117 temperature=0,143 temperature=0,

118)144)

119 145 

120print(f'{response.usage.prompt_tokens} prompt tokens used.')146print(f"{response.usage.prompt_tokens} prompt tokens used.")

121```147```

122 148 

123 149 


126 152 

127Each message passed to the API consumes the number of tokens in the content, role, and other fields, plus a few extra for behind-the-scenes formatting. This may change slightly in the future.153Each message passed to the API consumes the number of tokens in the content, role, and other fields, plus a few extra for behind-the-scenes formatting. This may change slightly in the future.

128 154 

129If a conversation has too many tokens to fit within a model’s maximum limit (e.g., more than 4097 tokens for `gpt-3.5-turbo` or more than 128k tokens for `gpt-4o`), you will have to truncate, omit, or otherwise shrink your text until it fits. Beware that if a message is removed from the messages input, the model will lose all knowledge of it.155If a conversation has too many tokens to fit within a model’s maximum limit (for example, more than 4097 tokens for `gpt-3.5-turbo` or more than 128k tokens for `gpt-4o`), you will have to truncate, omit, or otherwise shrink your text until it fits. Beware that if a message is removed from the messages input, the model will lose all knowledge of it.

130 156 

131Note that very long conversations are more likely to receive incomplete replies. For example, a `gpt-3.5-turbo` conversation that is 4090 tokens long will have its reply cut off after just 6 tokens.157Note that long conversations are more likely to receive incomplete replies. For example, a `gpt-3.5-turbo` conversation that is 4090 tokens long will have its reply cut off after just 6 tokens.

132 158 

133## Parameter details159## Parameter details

134 160 


141They work by directly modifying the logits (un-normalized log-probabilities) with an additive contribution.167They work by directly modifying the logits (un-normalized log-probabilities) with an additive contribution.

142 168 

143```python169```python

144mu[j] -> mu[j] - c[j] * alpha_frequency - float(c[j] > 0) * alpha_presence170mu[j] = mu[j] - c[j] * alpha_frequency - float(c[j] > 0) * alpha_presence

145```171```

146 172 

173 

147Where:174Where:

148 175 

149- `mu[j]` is the logits of the j-th token176- `mu[j]` is the logits of the j-th token


160 187 

161### Token log probabilities188### Token log probabilities

162 189 

163The [logprobs](https://developers.openai.com/api/docs/api-reference/chat/create#chat-create-logprobs) parameter found in the [Chat Completions API](https://developers.openai.com/api/docs/api-reference/chat/create) and [Legacy Completions API](https://developers.openai.com/api/docs/api-reference/completions), when requested, provides the log probabilities of each output token, and a limited number of the most likely tokens at each token position alongside their log probabilities. This can be useful in some cases to assess the confidence of the model in its output, or to examine alternative responses the model might have given.190The [`logprobs`](https://developers.openai.com/api/docs/api-reference/chat/create#chat-create-logprobs) parameter found in the [Chat Completions API](https://developers.openai.com/api/docs/api-reference/chat/create) and [Legacy Completions API](https://developers.openai.com/api/docs/api-reference/completions), when requested, provides the log probabilities of each output token, and a limited number of the most likely tokens at each token position alongside their log probabilities. This can be useful in some cases to assess the confidence of the model in its output, or to examine alternative responses the model might have given.

164 191 

165### Other parameters192### Other parameters

166 193 

Details

193 )193 )

194 194 

195 async for event in stream.stream_events():195 async for event in stream.stream_events():

196 if (196 if event.type == "raw_response_event" and isinstance(

197 event.type == "raw_response_event"197 event.data, ResponseTextDeltaEvent

198 and isinstance(event.data, ResponseTextDeltaEvent)

199 ):198 ):

200 print(event.data.delta, end="", flush=True)199 print(event.data.delta, end="", flush=True)

201 200 

Details

226agent = SandboxAgent(226agent = SandboxAgent(

227 name="Tax prep assistant",227 name="Tax prep assistant",

228 instructions="Use the mounted skill before preparing the return.",228 instructions="Use the mounted skill before preparing the return.",

229 capabilities=Capabilities.default() + [229 capabilities=Capabilities.default()

230 + [

230 Skills(from_=GitRepo(repo="owner/tax-prep-skills", ref="main")),231 Skills(from_=GitRepo(repo="owner/tax-prep-skills", ref="main")),

231 ],232 ],

232)233)


409from agents.run import RunConfig410from agents.run import RunConfig

410from agents.sandbox import SandboxRunConfig411from agents.sandbox import SandboxRunConfig

411from agents.sandbox.config import DEFAULT_PYTHON_SANDBOX_IMAGE412from agents.sandbox.config import DEFAULT_PYTHON_SANDBOX_IMAGE

412from agents.sandbox.sandboxes.docker import DockerSandboxClient, DockerSandboxClientOptions413from agents.sandbox.sandboxes.docker import (

414 DockerSandboxClient,

415 DockerSandboxClientOptions,

416)

413 417 

414docker_run_config = RunConfig(418docker_run_config = RunConfig(

415 sandbox=SandboxRunConfig(419 sandbox=SandboxRunConfig(

guides/audio.md +7 −18

Details

122 model="gpt-audio-1.5",122 model="gpt-audio-1.5",

123 modalities=["text", "audio"],123 modalities=["text", "audio"],

124 audio={"voice": "alloy", "format": "wav"},124 audio={"voice": "alloy", "format": "wav"},

125 messages=[125 messages=[{"role": "user", "content": "Is a golden retriever a good family dog?"}],

126 {

127 "role": "user",

128 "content": "Is a golden retriever a good family dog?"

129 }

130 ]

131)126)

132 127 

133print(completion.choices[0])128print(completion.choices[0])


203response = requests.get(url)198response = requests.get(url)

204response.raise_for_status()199response.raise_for_status()

205wav_data = response.content200wav_data = response.content

206encoded_string = base64.b64encode(wav_data).decode('utf-8')201encoded_string = base64.b64encode(wav_data).decode("utf-8")

207 202 

208completion = client.chat.completions.create(203completion = client.chat.completions.create(

209 model="gpt-audio-1.5",204 model="gpt-audio-1.5",


213 {208 {

214 "role": "user",209 "role": "user",

215 "content": [210 "content": [

216 { 211 {"type": "text", "text": "What is in this recording?"},

217 "type": "text",

218 "text": "What is in this recording?"

219 },

220 {212 {

221 "type": "input_audio",213 "type": "input_audio",

222 "input_audio": {214 "input_audio": {"data": encoded_string, "format": "wav"},

223 "data": encoded_string,

224 "format": "wav"

225 }

226 }

227 ]

228 },215 },

229 ]216 ],

217 },

218 ],

230)219)

231 220 

232print(completion.choices[0].message)221print(completion.choices[0].message)

Details

123```123```

124 124 

125```python125```python

126import os

127 

126from openai import OpenAI128from openai import OpenAI

129 

130response_id = os.environ["OPENAI_RESPONSE_ID"]

127client = OpenAI()131client = OpenAI()

128 132 

129resp = client.responses.cancel("resp_123")133resp = client.responses.cancel(response_id)

130 134 

131print(resp.status)135print(resp.status)

132```136```

guides/batch.md +22 −23

Details

29- `/v1/chat/completions` ([Chat Completions API](https://developers.openai.com/api/docs/api-reference/chat))29- `/v1/chat/completions` ([Chat Completions API](https://developers.openai.com/api/docs/api-reference/chat))

30- `/v1/embeddings` ([Embeddings API](https://developers.openai.com/api/docs/api-reference/embeddings))30- `/v1/embeddings` ([Embeddings API](https://developers.openai.com/api/docs/api-reference/embeddings))

31- `/v1/completions` ([Completions API](https://developers.openai.com/api/docs/api-reference/completions))31- `/v1/completions` ([Completions API](https://developers.openai.com/api/docs/api-reference/completions))

32- `/v1/moderations` ([Moderations guide](https://developers.openai.com/api/docs/guides/moderation))32- `/v1/moderations` ([Moderation guide](https://developers.openai.com/api/docs/guides/moderation))

33- `/v1/images/generations` ([Images API](https://developers.openai.com/api/docs/api-reference/images))33- `/v1/images/generations` ([Images API](https://developers.openai.com/api/docs/api-reference/images))

34- `/v1/images/edits` ([Images API](https://developers.openai.com/api/docs/api-reference/images))34- `/v1/images/edits` ([Images API](https://developers.openai.com/api/docs/api-reference/images))

35- `/v1/videos` ([Video generation guide](https://developers.openai.com/api/docs/guides/video-generation))35- `/v1/videos` ([Video generation guide](https://developers.openai.com/api/docs/guides/video-generation))


52{"custom_id": "request-2", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-3.5-turbo-0125", "messages": [{"role": "system", "content": "You are an unhelpful assistant."},{"role": "user", "content": "Hello world!"}],"max_tokens": 1000}}52{"custom_id": "request-2", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-3.5-turbo-0125", "messages": [{"role": "system", "content": "You are an unhelpful assistant."},{"role": "user", "content": "Hello world!"}],"max_tokens": 1000}}

53```53```

54 54 

55#### Moderations input examples55#### Moderation input examples

56 56 

57Text-only request:57Text-only request:

58 58 


68}68}

69```69```

70 70 

71Multimodal request:71Request with text and image input:

72 72 

73```jsonl73```jsonl

74{74{


121client = OpenAI()122client = OpenAI()

122 123 

123batch_input_file = client.files.create(124batch_input_file = client.files.create(

124 file=open("batchinput.jsonl", "rb"),125 file=open("batchinput.jsonl", "rb"), purpose="batch"

125 purpose="batch"

126)126)

127 127 

128print(batch_input_file)128print(batch_input_file)


162```162```

163 163 

164```python164```python

165from openai import OpenAI165batch = client.batches.create(

166client = OpenAI()166 input_file_id=batch_input_file.id,

167 

168batch_input_file_id = batch_input_file.id

169client.batches.create(

170 input_file_id=batch_input_file_id,

171 endpoint="/v1/chat/completions",167 endpoint="/v1/chat/completions",

172 completion_window="24h",168 completion_window="24h",

173 metadata={169 metadata={"description": "nightly eval job"},

174 "description": "nightly eval job"

175 }

176)170)

171print(batch)

177```172```

178 173 

179```bash174```bash


197 192 

198This request will return a [Batch object](https://developers.openai.com/api/docs/api-reference/batch/object) with metadata about your batch:193This request will return a [Batch object](https://developers.openai.com/api/docs/api-reference/batch/object) with metadata about your batch:

199 194 

200```python195```json

201{196{

202 "id": "batch_abc123",197 "id": "batch_abc123",

203 "object": "batch",198 "object": "batch",


238```233```

239 234 

240```python235```python

241from openai import OpenAI236batch = client.batches.retrieve(batch.id)

242client = OpenAI()

243 

244batch = client.batches.retrieve("batch_abc123")

245print(batch)237print(batch)

246```238```

247 239 


287```279```

288 280 

289```python281```python

282import os

283 

290from openai import OpenAI284from openai import OpenAI

285 

286output_file_id = os.environ["OPENAI_BATCH_OUTPUT_FILE_ID"]

291client = OpenAI()287client = OpenAI()

292 288 

293file_response = client.files.content("file-xyz123")289file_response = client.files.content(output_file_id)

294print(file_response.text)290print(file_response.text)

295```291```

296 292 


337```333```

338 334 

339```python335```python

336import os

337 

340from openai import OpenAI338from openai import OpenAI

339 

340batch_id = os.environ["OPENAI_BATCH_ID"]

341client = OpenAI()341client = OpenAI()

342 342 

343client.batches.cancel("batch_abc123")343client.batches.cancel(batch_id)

344```344```

345 345 

346```bash346```bash


401Batch API rate limits are separate from existing per-model rate limits. The Batch API has three types of rate limits:402Batch API rate limits are separate from existing per-model rate limits. The Batch API has three types of rate limits:

402 403 

4031. **Per-batch limits:** A single batch may include up to 50,000 requests, and a batch input file can be up to 200 MB in size. Note that `/v1/embeddings` batches are also restricted to a maximum of 50,000 embedding inputs across all requests in the batch.4041. **Per-batch limits:** A single batch may include up to 50,000 requests, and a batch input file can be up to 200 MB in size. Note that `/v1/embeddings` batches are also restricted to a maximum of 50,000 embedding inputs across all requests in the batch.

4042. **Enqueued prompt tokens per model:** Each model has a maximum number of enqueued prompt tokens allowed for batch processing. You can find these limits on the [Platform Settings page](https://platform.openai.com/settings/organization/limits).4052. **Queued prompt tokens per model:** Each model has a maximum number of prompt tokens that can be queued for batch processing. You can find these limits on the [Platform Settings page](https://platform.openai.com/settings/organization/limits).

4053. **Batch creation rate limit:** You can create up to 2,000 batches per hour. If you need to submit more requests, increase the number of requests per batch.4063. **Batch creation rate limit:** You can create up to 2,000 batches per hour. If you need to submit more requests, increase the number of requests per batch.

406 407 

407There are no limits for output tokens for the Batch API today. Because Batch API rate limits are a new, separate pool, **using the Batch API will not consume tokens from your standard per-model rate limits**, thereby offering you a convenient way to increase the number of requests and processed tokens you can use when querying our API.408The Batch API currently has no output-token limit. Because Batch API rate limits are a new, separate pool, **using the Batch API will not consume tokens from your standard per-model rate limits**, thereby offering you a convenient way to increase the number of requests and processed tokens you can use when querying our API.

408 409 

409## Batch expiration410## Batch expiration

410 411 

guides/chatkit.md +71 −23

Details

39 39 

40### 2. Set up ChatKit in your product40### 2. Set up ChatKit in your product

41 41 

42To set up ChatKit, you'll create a ChatKit session and create a backend endpoint, pass in your workflow ID, exchange the client secret, add a script to embed ChatKit on your site.42To set up ChatKit, you'll create a ChatKit session and a server endpoint, pass in your workflow ID, exchange the client secret, and add a script to embed ChatKit on your site.

43 43 

44**Important Security Note:** When creating a ChatKit session, you must pass in a `user` parameter, which should be unique for each individual end user. It is your backend's responsibility44**Important Security Note:** When creating a ChatKit session, you must pass in a `user` parameter, which should be unique for each individual end user. Your server must

45to authenticate your application's users and pass a unique identifier for them in this parameter.45authenticate your application's users and pass a unique identifier for them in this parameter.

46 46 

471. On your server, generate a client token.471. On your server, generate a client token.

48 48 

49 This snippet spins up a FastAPI service whose sole job is to create a new ChatKit session via the [OpenAI Python SDK](https://github.com/openai/chatkit-python) and hand back the session's client secret:49 This snippet spins up a FastAPI service whose sole job is to create a new ChatKit session through the OpenAI API and hand back the session's client secret:

50 50 

51 server.py51 server.py

52 52 

53```python53```python

54from fastapi import FastAPI54import hmac

55from pydantic import BaseModel55import json

56from openai import OpenAI

57import os56import os

57from typing import Annotated

58 

59import requests

60from fastapi import Depends, FastAPI, HTTPException

61from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer

62from pydantic import BaseModel

63 

64 

65api_key = os.environ["OPENAI_API_KEY"]

66workflow_id = os.environ["OPENAI_CHATKIT_WORKFLOW_ID"]

67authenticated_users: dict[str, str] = json.loads(

68 os.environ["CHATKIT_AUTHENTICATED_USERS"]

69)

70bearer_auth = HTTPBearer(auto_error=False)

71 

72 

73def get_authenticated_user_id(

74 credentials: Annotated[

75 HTTPAuthorizationCredentials | None,

76 Depends(bearer_auth),

77 ],

78) -> str:

79 if credentials is not None:

80 for token, user_id in authenticated_users.items():

81 if hmac.compare_digest(credentials.credentials, token):

82 return user_id

83 raise HTTPException(status_code=401, detail="Invalid authentication token")

84 

85 

86class ChatKitSession(BaseModel):

87 client_secret: str

88 

58 89 

59app = FastAPI()90app = FastAPI()

60openai = OpenAI(api_key=os.environ["OPENAI_API_KEY"])91 

61 92 

62@app.post("/api/chatkit/session")93@app.post("/api/chatkit/session")

63def create_chatkit_session():94def create_chatkit_session(

64 session = openai.chatkit.sessions.create({95 user_id: Annotated[str, Depends(get_authenticated_user_id)],

65 # ...96):

66 })97 response = requests.post(

67 return { client_secret: session.client_secret }98 "https://api.openai.com/v1/chatkit/sessions",

99 headers={

100 "Authorization": f"Bearer {api_key}",

101 "Content-Type": "application/json",

102 "OpenAI-Beta": "chatkit_beta=v1",

103 },

104 json={

105 "workflow": {"id": workflow_id},

106 "user": user_id,

107 },

108 timeout=30,

109 )

110 response.raise_for_status()

111 session = ChatKitSession.model_validate(response.json())

112 return {"client_secret": session.client_secret}

68```113```

69 114 

70 115 

116 Before starting the service, set `OPENAI_API_KEY`, `OPENAI_CHATKIT_WORKFLOW_ID`, and `CHATKIT_AUTHENTICATED_USERS`. The last value is a JSON map from your application's bearer tokens to stable user IDs. In production, replace this environment-backed map with your application's authentication or session lookup.

117 

712. In your server-side code, pass in your workflow ID and secret key to the session endpoint.1182. In your server-side code, pass in your workflow ID and secret key to the session endpoint.

72 119 

73 The client secret is the credential that your ChatKit frontend uses to open or refresh the chat session. You don't store it; you immediately hand it off to the ChatKit client library.120 The client secret is the credential that your ChatKit frontend uses to open or refresh the chat session. You don't store it; you immediately hand it off to the ChatKit client library.


134```181```

135 182 

136 183 

1375. Render ChatKit in your UI. This code fetches the client secret from your server and mounts a live chat widget connected to your workflow.1845. Render ChatKit in your UI. Pass the React `MyChat` component a `getAppAuthToken` function that returns the current user's bearer token. If you use the JavaScript tab, make the same function available in the snippet's scope. This code sends that credential to your server, fetches the client secret, and mounts a live chat widget connected to your workflow.

138 185 

139 Your frontend code186 Your frontend code

140 187 

141```react188```react

142import { ChatKit, useChatKit } from '@openai/chatkit-react';189import { ChatKit, useChatKit } from '@openai/chatkit-react';

143 190 

144 export function MyChat() {191 export function MyChat({ getAppAuthToken }) {

145 const { control } = useChatKit({192 const { control } = useChatKit({

146 api: {193 api: {

147 async getClientSecret(existing) {194 async getClientSecret(existing) {


149 // implement session refresh196 // implement session refresh

150 }197 }

151 198 

199 const appAuthToken = await getAppAuthToken();

152 const res = await fetch('/api/chatkit/session', {200 const res = await fetch('/api/chatkit/session', {

153 method: 'POST',201 method: 'POST',

154 headers: {202 headers: {

203 'Authorization': 'Bearer ' + appAuthToken,

155 'Content-Type': 'application/json',204 'Content-Type': 'application/json',

156 },205 },

157 });206 });


177 226 

178chatkit.setOptions({227chatkit.setOptions({

179 api: {228 api: {

180 async getClientSecret(currentClientSecret) {229 async getClientSecret() {

181 if (!currentClientSecret) {230 const appAuthToken = await getAppAuthToken();

182 const res = await fetch("/api/chatkit/start", { method: "POST" });231 const res = await fetch("/api/chatkit/session", {

183 const { client_secret } = await res.json();

184 return client_secret;

185 }

186 const res = await fetch("/api/chatkit/refresh", {

187 method: "POST",232 method: "POST",

188 body: JSON.stringify({ currentClientSecret }),

189 headers: {233 headers: {

234 Authorization: `Bearer ${appAuthToken}`,

190 "Content-Type": "application/json",235 "Content-Type": "application/json",

191 },236 },

192 });237 });

238 if (!res.ok) {

239 throw new Error(`ChatKit session request failed: ${res.status}`);

240 }

193 const { client_secret } = await res.json();241 const { client_secret } = await res.json();

194 return client_secret;242 return client_secret;

195 },243 },

Details

9Actions can be triggered by attaching an `ActionConfig` to any widget node that supports it. For example, you can respond to click events on Buttons. When a user clicks on this button, the action will be sent to your server where you can update the widget, run inference, stream new thread items, etc.9Actions can be triggered by attaching an `ActionConfig` to any widget node that supports it. For example, you can respond to click events on Buttons. When a user clicks on this button, the action will be sent to your server where you can update the widget, run inference, stream new thread items, etc.

10 10 

11```python11```python

12Button(12button = Button(

13 label="Example",13 label="Example",

14 onClickAction=ActionConfig(14 onClickAction=ActionConfig(

15 type="example",15 type="example",

16 payload={"id": 123},16 payload={"id": 123},

17 )17 ),

18)18)

19```19```

20 20 


34By default, actions are sent to your server. You can handle actions on your server by implementing the `action` method on `ChatKitServer`.35By default, actions are sent to your server. You can handle actions on your server by implementing the `action` method on `ChatKitServer`.

35 36 

36```python37```python

37class MyChatKitServer(ChatKitServer[RequestContext])38class MyChatKitServer(ChatKitServer[RequestContext]):

38 async def action(39 async def action(

39 self,40 self,

40 thread: ThreadMetadata,41 thread: ThreadMetadata,


43 context: RequestContext,44 context: RequestContext,

44 ) -> AsyncIterator[Event]:45 ) -> AsyncIterator[Event]:

45 if action.type == "example":46 if action.type == "example":

46 await do_thing(action.payload['id'])47 await do_thing(action.payload["id"])

47 48 

48 # often you'll want to add a HiddenContextItem so the model49 # Often you'll want to add a HiddenContextItem so the model

49 # can see that the user did something50 # can see that the user did something.

50 await self.store.add_thread_item(51 await self.store.add_thread_item(

51 thread.id,52 thread.id,

52 HiddenContextItem(53 HiddenContextItem(

53 id="item_123",54 id="item_123",

54 created_at=datetime.now(),55 created_at=datetime.now(),

55 content=(56 content="<USER_ACTION>The user did a thing</USER_ACTION>",

56 "<USER_ACTION>The user did a thing</USER_ACTION>"

57 ),

58 ),57 ),

59 context,58 context,

60 )59 )

61 60 

62 # then you might want to run inference to stream a response61 # Then you might want to run inference to stream a response

63 # back to the user.62 # back to the user.

64 async for e in self.generate(context, thread):63 async for event in self.generate(context, thread):

65 yield e64 yield event

66```65```

67 66 

68**NOTE:** As with any client/server interaction, actions and their payloads are sent by the client and should be treated as untrusted data.67 

68Treat actions and their payloads as untrusted data because the client sends them to your server.

69 69 

70### Client70### Client

71 71 

72Sometimes you’ll want to handle actions in your client integration. To do that you need to specify that the action should be sent to your client-side action handler by adding `handler="client` to the `ActionConfig`.72Sometimes you’ll want to handle actions in your client integration. To do that you need to specify that the action should be sent to your client-side action handler by adding `handler="client"` to the `ActionConfig`.

73 73 

74```python74```python

75Button(75button = Button(

76 label="Example",76 label="Example",

77 onClickAction=ActionConfig(77 onClickAction=ActionConfig(type="example", payload={"id": 123}, handler="client"),

78 type="example",

79 payload={"id": 123},

80 handler="client"

81 )

82)78)

83```79```

84 80 


106 103 

107## Strongly typed actions104## Strongly typed actions

108 105 

109By default `Action` and `ActionConfig` are not strongly typed. However, we do expose a `create` helper on `Action` making it easy to generate `ActionConfig`s from a set of strongly-typed actions.106By default `Action` and `ActionConfig` are not strongly typed. However, we do expose a `create` helper on `Action` that generates `ActionConfig`s from a set of strongly-typed actions.

110 107 

111```python108```python

112 109class ExamplePayload(BaseModel):

113class ExamplePayload(BaseModel)

114 id: int110 id: int

115 111 

112 

116ExampleAction = Action[Literal["example"], ExamplePayload]113ExampleAction = Action[Literal["example"], ExamplePayload]

117OtherAction = Action[Literal["other"], None]114OtherAction = Action[Literal["other"], None]

118 115 

119AppAction = Annotated[116AppAction = Annotated[

120 ExampleAction117 ExampleAction | OtherAction,

121 | OtherAction,

122 Field(discriminator="type"),118 Field(discriminator="type"),

123]119]

124 120 

125ActionAdapter: TypeAdapter[AppAction] = TypeAdapter(AppAction)121ActionAdapter: TypeAdapter[AppAction] = TypeAdapter(AppAction)

126 122 

127def parse_app_action(action: Action[str, Any]): AppAction123 

128 return ActionAdapter.model_validate(action)124def parse_app_action(action: Action[str, Any]) -> AppAction:

125 return ActionAdapter.validate_python(action)

126 

129 127 

130# Usage in a widget128# Usage in a widget

131# Action provides a create helper which makes it easy to generate129# Action provides a create helper which makes it easy to generate

132# ActionConfigs from strongly typed actions.130# ActionConfigs from strongly typed actions.

133Button(131button = Button(

134 label="Example",132 label="Example",

135 onClickAction=ExampleAction.create(ExamplePayload(id=123))133 onClickAction=ExampleAction.create(ExamplePayload(id=123)),

136)134)

137 135 

136 

138# usage in action handler137# usage in action handler

139class MyChatKitServer(ChatKitServer[RequestContext])138class MyChatKitServer(ChatKitServer[RequestContext]):

140 async def action(139 async def action(

141 self,140 self,

142 thread: ThreadMetadata,141 thread: ThreadMetadata,


146 ) -> AsyncIterator[Event]:145 ) -> AsyncIterator[Event]:

147 # add custom error handling if needed146 # add custom error handling if needed

148 app_action = parse_app_action(action)147 app_action = parse_app_action(action)

149 if (app_action.type == "example"):148 if app_action.type == "example":

150 await do_thing(app_action.payload.id)149 await do_thing(app_action.payload.id)

150 yield ThreadItemDoneEvent(

151 item=AssistantMessageItem(

152 id=self.store.generate_item_id("message", thread, context),

153 thread_id=thread.id,

154 created_at=datetime.now(),

155 content=[AssistantMessageContent(text="Action complete.")],

156 )

157 )

151```158```

152 159 

160 

153## Use widgets and actions to create custom forms161## Use widgets and actions to create custom forms

154 162 

155When widget nodes that take user input are mounted inside a `Form`, the values from those fields will be included in the `payload` of all actions that originate from within the `Form`.163When widget nodes that take user input are mounted inside a `Form`, the values from those fields will be included in the `payload` of all actions that originate from within the `Form`.


160- `Select(name="todo.title")` → `action.payload.todo.title`168- `Select(name="todo.title")` → `action.payload.todo.title`

161 169 

162```python170```python

163Form(171form = Form(

164 direction="col",172 direction="col",

165 validation="native"173 validation="native",

166 onSubmitAction=ActionConfig(174 onSubmitAction=ActionConfig(

167 type="update_todo",175 type="update_todo",

168 payload={"id": todo.id}176 payload={"id": todo.id},

169 ),177 ),

170 children=[178 children=[

171 Title(value="Edit Todo"),179 Title(value="Edit Todo"),


174 Text(181 Text(

175 value=todo.title,182 value=todo.title,

176 editable=EditableProps(name="title", required=True),183 editable=EditableProps(name="title", required=True),

177 )184 ),

178 

179 Text(value="Description", color="secondary", size="sm"),185 Text(value="Description", color="secondary", size="sm"),

180 Text(186 Text(

181 value=todo.description,187 value=todo.description,

182 editable=EditableProps(name="description"),188 editable=EditableProps(name="description"),

183 ),189 ),

184 190 Button(label="Save", submit=True),

185 Button(label="Save", type="submit")191 ],

186 ]

187)192)

188 193 

189class MyChatKitServer(ChatKitServer[RequestContext])194 

195class MyChatKitServer(ChatKitServer[RequestContext]):

190 async def action(196 async def action(

191 self,197 self,

192 thread: ThreadMetadata,198 thread: ThreadMetadata,


194 sender: WidgetItem | None,200 sender: WidgetItem | None,

195 context: RequestContext,201 context: RequestContext,

196 ) -> AsyncIterator[Event]:202 ) -> AsyncIterator[Event]:

197 if (action.type == "update_todo"):203 if action.type == "update_todo":

198 id = action.payload['id']204 todo_id = action.payload["id"]

199 # Any action that originates from within the Form will205 # Any action that originates from within the Form will

200 # include title and description206 # include title and description.

201 title = action.payload['title']207 title = action.payload["title"]

202 description = action.payload['description']208 description = action.payload["description"]

203 209 

204 # ...210 await update_todo(todo_id, title, description)

205 211 yield ThreadItemDoneEvent(

212 item=AssistantMessageItem(

213 id=self.store.generate_item_id("message", thread, context),

214 thread_id=thread.id,

215 created_at=datetime.now(),

216 content=[AssistantMessageContent(text="Todo updated.")],

217 )

218 )

206```219```

207 220 

221 

208### Validation222### Validation

209 223 

210`Form` uses basic native form validation; enforcing `required` and `pattern` on fields where they are configured and blocking submission when the form has any invalid field.224`Form` uses basic native form validation; enforcing `required` and `pattern` on fields where they are configured and blocking submission when the form has any invalid field.


224Use `ActionConfig.loadingBehavior` to control how actions trigger different loading states in a widget.238Use `ActionConfig.loadingBehavior` to control how actions trigger different loading states in a widget.

225 239 

226```python240```python

227Button(241button = Button(

228 label="This make take a while...",242 label="This may take a while...",

229 onClickAction=ActionConfig(243 onClickAction=ActionConfig(

230 type="long_running_action_that_should_block_other_ui_interactions",244 type="long_running_action_that_should_block_other_ui_interactions",

231 loadingBehavior="container"245 loadingBehavior="container",

232 )246 ),

233)247)

234```248```

235 249 

Details

204CITATION_STOP = "\ue201"204CITATION_STOP = "\ue201"

205 205 

206SOURCE_ID_RE = re.compile(r"^[A-Za-z0-9_-]+$")206SOURCE_ID_RE = re.compile(r"^[A-Za-z0-9_-]+$")

207LINE_LOCATOR_RE = re.compile(r"^L\\d+(?:-L\\d+)?$")207LINE_LOCATOR_RE = re.compile(r"^L\d+(?:-L\d+)?$")

208 208 

209 209 

210class Citation(TypedDict):210class Citation(TypedDict):

Details

29 29 

30const result = await openai.responses.create({30const result = await openai.responses.create({

31 model: "gpt-5.6",31 model: "gpt-5.6",

32 input: "Find the null pointer exception: ...your code here...",32 input: `Find the null pointer exception in this code:

33 

34def display_name(user):

35 return user.profile.name

36 

37print(display_name(None))

38`,

33 reasoning: { effort: "high" },39 reasoning: { effort: "high" },

34});40});

35 41 


42 49 

43result = client.responses.create(50result = client.responses.create(

44 model="gpt-5.6",51 model="gpt-5.6",

45 input="Find the null pointer exception: ...your code here...",52 input="""Find the null pointer exception in this code:

46 reasoning={ "effort": "high" },53 

54def display_name(user):

55 return user.profile.name

56 

57print(display_name(None))

58""",

59 reasoning={"effort": "high"},

47)60)

48 61 

49print(result.output_text)62print(result.output_text)


55 -H "Authorization: Bearer $OPENAI_API_KEY" \68 -H "Authorization: Bearer $OPENAI_API_KEY" \

56 -d '{69 -d '{

57 "model": "gpt-5.6",70 "model": "gpt-5.6",

58 "input": "Find the null pointer exception: ...your code here...",71 "input": "Find the null pointer exception in this code:\n\ndef display_name(user):\n return user.profile.name\n\nprint(display_name(None))\n",

59 "reasoning": { "effort": "high" }72 "reasoning": { "effort": "high" }

60 }'73 }'

61```74```

Details

118 118 

119```python119```python

120# Full window collected from prior turns120# Full window collected from prior turns

121long_input_items_array = [...]121long_input_items_array = [{"role": "user", "content": "Plan a trip to Kyoto."}]

122 122 

123# 1) Compact the current window123# 1) Compact the current window

124compacted = client.responses.compact(124compacted = client.responses.compact(

Details

9client = OpenAI()10client = OpenAI()

10 11 

11response = client.completions.create(12response = client.completions.create(

12model="gpt-3.5-turbo-instruct",13 model="gpt-3.5-turbo-instruct", prompt="Write a tagline for an ice cream shop."

13prompt="Write a tagline for an ice cream shop."

14)14)

15```15```

16 16 

Details

109 109 

110client = OpenAI()110client = OpenAI()

111 111 

112history = [112history = [{"role": "user", "content": "tell me a joke"}]

113 {

114 "role": "user",

115 "content": "tell me a joke"

116 }

117]

118 113 

119response = client.responses.create(114response = client.responses.create(

120 model="gpt-5.6",115 model="gpt-5.6",


127# Add all response output items, including encrypted reasoning items, to the conversation122# Add all response output items, including encrypted reasoning items, to the conversation

128history += response.output123history += response.output

129 124 

130history.append({ "role": "user", "content": "tell me another" })125history.append({"role": "user", "content": "tell me another"})

131 126 

132second_response = client.responses.create(127second_response = client.responses.create(

133 model="gpt-5.6",128 model="gpt-5.6",


169response = openai.responses.create(164response = openai.responses.create(

170 model="gpt-5.6",165 model="gpt-5.6",

171 input=[{"role": "user", "content": "What are the 5 Ds of dodgeball?"}],166 input=[{"role": "user", "content": "What are the 5 Ds of dodgeball?"}],

172 conversation="conv_689667905b048191b4740501625afd940c7533ace33a2dab"167 conversation=conversation.id,

173)168)

174```169```

175 170 

Details

27### 2. Implement a server class27### 2. Implement a server class

28 28 

29`ChatKitServer` drives the conversation. Override `respond` to stream events whenever a29`ChatKitServer` drives the conversation. Override `respond` to stream events whenever a

30user message or client tool output arrives. Helpers like `stream_agent_response` make it30user message or client tool output arrives. Helpers like `stream_agent_response` connect

31simple to connect to the Agents SDK.31the server to the Agents SDK.

32 32 

33```python33```python

34class MyChatKitServer(ChatKitServer):34class MyChatKitServer(ChatKitServer[RequestContext]):

35 def __init__(self, data_store: Store, file_store: FileStore | None = None):

36 super().__init__(data_store, file_store)

37 

38 assistant_agent = Agent[AgentContext](

39 model="gpt-5.6",

40 name="Assistant",

41 instructions="You are a helpful assistant",

42 )

43 

44 async def respond(35 async def respond(

45 self,36 self,

46 thread: ThreadMetadata,37 thread: ThreadMetadata,

47 input: UserMessageItem | ClientToolCallOutputItem,38 input: UserMessageItem | ClientToolCallOutputItem | None,

48 context: Any,39 context: RequestContext,

49 ) -> AsyncIterator[Event]:40 ) -> AsyncIterator[Event]:

41 items_page = await self.store.load_thread_items(

42 thread.id,

43 after=None,

44 limit=20,

45 order="desc",

46 context=context,

47 )

48 input_items = await simple_to_agent_input(list(reversed(items_page.data)))

50 agent_context = AgentContext(49 agent_context = AgentContext(

51 thread=thread,50 thread=thread,

52 store=self.store,51 store=self.store,

53 request_context=context,52 request_context=context,

54 )53 )

55 result = Runner.run_streamed(54 result = Runner.run_streamed(

56 self.assistant_agent,55 assistant_agent,

57 await to_input_item(input, self.to_message_content),56 input_items,

58 context=agent_context,57 context=agent_context,

59 )58 )

60 async for event in stream_agent_response(agent_context, result):59 async for event in stream_agent_response(agent_context, result):

61 yield event60 yield event

62 

63 async def to_message_content(

64 self, input: FilePart | ImagePart

65 ) -> ResponseInputContentParam:

66 raise NotImplementedError()

67```61```

68 62 

69 63 


73example, with FastAPI:67example, with FastAPI:

74 68 

75```python69```python

70from fastapi import FastAPI, Request, Response

71from fastapi.responses import StreamingResponse

72 

76app = FastAPI()73app = FastAPI()

77data_store = SQLiteStore()74data_store = MemoryStore()

78file_store = DiskFileStore(data_store)75server = MyChatKitServer(data_store)

79server = MyChatKitServer(data_store, file_store)76 

80 77 

81@app.post("/chatkit")78@app.post("/chatkit")

82async def chatkit_endpoint(request: Request):79async def chatkit_endpoint(request: Request):


90### 4. Establish data store contract87### 4. Establish data store contract

91 88 

92Implement `chatkit.store.Store` to persist threads, messages, and files using your89Implement `chatkit.store.Store` to persist threads, messages, and files using your

93preferred database. The default example uses SQLite for local development. Consider90preferred database. For local development, you can use an in-memory `Store`

94storing the models as JSON blobs so library updates can evolve the schema without91implementation. For production, use durable storage and consider storing the models as

95migrations.92JSON blobs so library updates can evolve the schema without migrations.

96 93 

97### 5. Provide file store contract94### 5. Provide file store contract

98 95 


150async def respond(148async def respond(

151 self,149 self,

152 thread: ThreadMetadata,150 thread: ThreadMetadata,

153 input: UserMessageItem | ClientToolCallOutputItem,151 input: UserMessageItem | ClientToolCallOutputItem | None,

154 context: Any,152 context: RequestContext,

155) -> AsyncIterator[Event]:153) -> AsyncIterator[Event]:

156 widget = Card(154 widget = Card(

157 children=[Text(155 children=[

156 Text(

158 id="description",157 id="description",

159 value="Generated summary",158 value="Generated summary",

160 )]159 )

160 ]

161 )161 )

162 async for event in stream_widget(162 async for event in stream_widget(

163 thread,163 thread,

164 widget,164 widget,

165 generate_id=lambda item_type: self.store.generate_item_id(item_type, thread, context),165 generate_id=lambda item_type: self.store.generate_item_id(

166 item_type, thread, context

167 ),

166 ):168 ):

167 yield event169 yield event

168```170```


215### Options reference217### Options reference

216 218 

217| Option | Type | Description | Default |219| Option | Type | Description | Default |

218| --------------- | -------------------------- | ------------------------------------------------------------ | -------------- |220| --------------- | -------------------------- | ---------------------------------------------------------- | -------------- |

219| `apiURL` | `string` | Endpoint that implements the ChatKit server protocol. | _required_ |221| `apiURL` | `string` | Endpoint that implements the ChatKit server protocol. | _required_ |

220| `fetch` | `typeof fetch` | Override fetch calls (for custom headers or auth). | `window.fetch` |222| `fetch` | `typeof fetch` | Override fetch calls (for custom headers or auth). | `window.fetch` |

221| `theme` | `"light" \| "dark"` | UI theme. | `"light"` |223| `theme` | `"light" \| "dark"` | UI theme. | `"light"` |


223| `clientTools` | `Record<string, Function>` | Client-executed tools exposed to the model. | |225| `clientTools` | `Record<string, Function>` | Client-executed tools exposed to the model. | |

224| `header` | `object \| boolean` | Header configuration or `false` to hide the header. | `true` |226| `header` | `object \| boolean` | Header configuration or `false` to hide the header. | `true` |

225| `newThreadView` | `object` | Customize greeting text and starter prompts. | |227| `newThreadView` | `object` | Customize greeting text and starter prompts. | |

226| `messages` | `object` | Configure message affordances (feedback, annotations, etc.). | |228| `messages` | `object` | Configure message features (feedback, annotations, etc.). | |

227| `composer` | `object` | Control attachments, entity tags, and placeholder text. | |229| `composer` | `object` | Control attachments, entity tags, and placeholder text. | |

228| `entities` | `object` | Callbacks for entity lookup, click handling, and previews. | |230| `entities` | `object` | Callbacks for entity lookup, click handling, and previews. | |

Details

12 12 

13```python13```python

14from openai import OpenAI14from openai import OpenAI

15 

15client = OpenAI(timeout=3600)16client = OpenAI(timeout=3600)

16 17 

18vector_store_ids = [

19 "<vector_store_id>",

20 "<vector_store_id_2>",

21]

22 

17input_text = """23input_text = """

18Research the economic impact of semaglutide on global healthcare systems.24Research the economic impact of semaglutide on global healthcare systems.

19Do:25Do:


35 {"type": "web_search_preview"},41 {"type": "web_search_preview"},

36 {42 {

37 "type": "file_search",43 "type": "file_search",

38 "vector_store_ids": [44 "vector_store_ids": vector_store_ids,

39 "vs_68870b8868b88191894165101435eef6",

40 "vs_12345abcde6789fghijk101112131415"

41 ]

42 },

43 {

44 "type": "code_interpreter",

45 "container": {"type": "auto"}

46 },45 },

46 {"type": "code_interpreter", "container": {"type": "auto"}},

47 ],47 ],

48)48)

49 49 


201IMPORTANT: Do NOT conduct any research yourself, just gather information that will be given to a researcher to conduct the research task.202IMPORTANT: Do NOT conduct any research yourself, just gather information that will be given to a researcher to conduct the research task.

202"""203"""

203 204 

204input_text = "Research surfboards for me. I'm interested in ...";205input_text = "Research surfboards for me. I'm interested in ..."

205 206 

206response = client.responses.create(207response = client.responses.create(

207 model="gpt-5.6",208 model="gpt-5.6",

Details

39client = OpenAI()40client = OpenAI()

40 41 

41response = client.embeddings.create(42response = client.embeddings.create(

42 input="Your text string goes here",43 input="Your text string goes here", model="text-embedding-3-small"

43 model="text-embedding-3-small"

44)44)

45 45 

46print(response.data[0].embedding)46print(response.data[0].embedding)


117 117 

118<span>Get_embeddings_from_dataset.ipynb</span> ```python118<span>Get_embeddings_from_dataset.ipynb</span> ```python

119from openai import OpenAI119from openai import OpenAI

120 

120client = OpenAI()121client = OpenAI()

121 122 

123 

122def get_embedding(text, model="text-embedding-3-small"):124def get_embedding(text, model="text-embedding-3-small"):

123 text = text.replace("\n", " ")125 text = text.replace("\n", " ")

124 return client.embeddings.create(input = [text], model=model).data[0].embedding126 return client.embeddings.create(input=[text], model=model).data[0].embedding

125 127 

126df['ada_embedding'] = df.combined.apply(lambda x: get_embedding(x, model='text-embedding-3-small'))128 

127df.to_csv('output/embedded_1k_reviews.csv', index=False)129df["ada_embedding"] = df.combined.apply(

130 lambda x: get_embedding(x, model="text-embedding-3-small")

131)

132df.to_csv("output/embedded_1k_reviews.csv", index=False)

128```133```

129 134 

130 135 


133```python138```python

134import pandas as pd139import pandas as pd

135 140 

136df = pd.read_csv('output/embedded_1k_reviews.csv')141df = pd.read_csv("output/embedded_1k_reviews.csv")

137df['ada_embedding'] = df.ada_embedding.apply(eval).apply(np.array)142df["ada_embedding"] = df.ada_embedding.apply(eval).apply(np.array)

138```143```

139 144 

140 145 


198 204 

199response = client.chat.completions.create(205response = client.chat.completions.create(

200 messages=[206 messages=[

201 {'role': 'system', 'content': 'You answer questions about the 2022 Winter Olympics.'},207 {

202 {'role': 'user', 'content': query},208 "role": "system",

209 "content": "You answer questions about the 2022 Winter Olympics.",

210 },

211 {"role": "user", "content": query},

203 ],212 ],

204 model=GPT_MODEL,213 model=GPT_MODEL,

205 temperature=0,214 temperature=0,


219To retrieve the most relevant documents we use the cosine similarity between the embedding vectors of the query and each document, and return the highest scored documents.228To retrieve the most relevant documents we use the cosine similarity between the embedding vectors of the query and each document, and return the highest scored documents.

220 229 

221```python230```python

222from openai.embeddings_utils import get_embedding, cosine_similarity

223 

224def search_reviews(df, product_description, n=3, pprint=True):231def search_reviews(df, product_description, n=3, pprint=True):

225 embedding = get_embedding(product_description, model='text-embedding-3-small')232 embedding = get_embedding(product_description, model="text-embedding-3-small")

226 df['similarities'] = df.ada_embedding.apply(lambda x: cosine_similarity(x, embedding))233 df["similarities"] = df.ada_embedding.apply(

227 res = df.sort_values('similarities', ascending=False).head(n)234 lambda x: cosine_similarity(x, embedding)

235 )

236 res = df.sort_values("similarities", ascending=False).head(n)

228 return res237 return res

229 238 

230res = search_reviews(df, 'delicious beans', n=3)239 

240res = search_reviews(df, "delicious beans", n=3)

231```241```

232 242 

233 243 


243To perform a code search, we embed the query in natural language using the same model. Then we calculate cosine similarity between the resulting query embedding and each of the function embeddings. The highest cosine similarity results are most relevant.253To perform a code search, we embed the query in natural language using the same model. Then we calculate cosine similarity between the resulting query embedding and each of the function embeddings. The highest cosine similarity results are most relevant.

244 254 

245```python255```python

246from openai.embeddings_utils import get_embedding, cosine_similarity256df["code_embedding"] = df["code"].apply(

257 lambda x: get_embedding(x, model="text-embedding-3-small")

258)

247 259 

248df['code_embedding'] = df['code'].apply(lambda x: get_embedding(x, model='text-embedding-3-small'))

249 260 

250def search_functions(df, code_query, n=3, pprint=True, n_lines=7):261def search_functions(df, code_query, n=3, pprint=True, n_lines=7):

251 embedding = get_embedding(code_query, model='text-embedding-3-small')262 embedding = get_embedding(code_query, model="text-embedding-3-small")

252 df['similarities'] = df.code_embedding.apply(lambda x: cosine_similarity(x, embedding))263 df["similarities"] = df.code_embedding.apply(

264 lambda x: cosine_similarity(x, embedding)

265 )

253 266 

254 res = df.sort_values('similarities', ascending=False).head(n)267 res = df.sort_values("similarities", ascending=False).head(n)

255 return res268 return res

256 269 

257res = search_functions(df, 'Completions API tests', n=3)270 

271res = search_functions(df, "Completions API tests", n=3)

258```272```

259 273 

260 274 


284 query_embedding = embeddings[index_of_source_string]298 query_embedding = embeddings[index_of_source_string]

285 299 

286 # get distances between the source embedding and other embeddings (function from embeddings_utils.py)300 # get distances between the source embedding and other embeddings (function from embeddings_utils.py)

287 distances = distances_from_embeddings(query_embedding, embeddings, distance_metric="cosine")301 distances = distances_from_embeddings(

302 query_embedding, embeddings, distance_metric="cosine"

303 )

288 304 

289 # get indices of nearest neighbors (function from embeddings_utils.py)305 # get indices of nearest neighbors (function from embeddings_utils.py)

290 indices_of_nearest_neighbors = indices_of_nearest_neighbors_from_distances(distances)306 indices_of_nearest_neighbors = indices_of_nearest_neighbors_from_distances(

307 distances

308 )

291 return indices_of_nearest_neighbors309 return indices_of_nearest_neighbors

292```310```

293 311 


312The visualization seems to have produced roughly 3 clusters, one of which has mostly negative reviews.330The visualization seems to have produced roughly 3 clusters, one of which has mostly negative reviews.

313 331 

314```python332```python

333import numpy as np

315import pandas as pd334import pandas as pd

316from sklearn.manifold import TSNE335from sklearn.manifold import TSNE

317import matplotlib.pyplot as plt336import matplotlib.pyplot as plt

318import matplotlib337import matplotlib

319 338 

320df = pd.read_csv('output/embedded_1k_reviews.csv')339df = pd.read_csv("output/embedded_1k_reviews.csv")

321matrix = df.ada_embedding.apply(eval).to_list()340matrix = np.array(df.ada_embedding.apply(eval).to_list())

322 341 

323# Create a t-SNE model and transform the data342# Create a t-SNE model and transform the data

324tsne = TSNE(n_components=2, perplexity=15, random_state=42, init='random', learning_rate=200)343tsne = TSNE(

344 n_components=2, perplexity=15, random_state=42, init="random", learning_rate=200

345)

325vis_dims = tsne.fit_transform(matrix)346vis_dims = tsne.fit_transform(matrix)

326 347 

327colors = ["red", "darkorange", "gold", "turquiose", "darkgreen"]348colors = ["red", "darkorange", "gold", "turquoise", "darkgreen"]

328x = [x for x,y in vis_dims]349x = [x for x, y in vis_dims]

329y = [y for x,y in vis_dims]350y = [y for x, y in vis_dims]

330color_indices = df.Score.values - 1351color_indices = df.Score.values - 1

331 352 

332colormap = matplotlib.colors.ListedColormap(colors)353colormap = matplotlib.colors.ListedColormap(colors)


352from sklearn.model_selection import train_test_split373from sklearn.model_selection import train_test_split

353 374 

354X_train, X_test, y_train, y_test = train_test_split(375X_train, X_test, y_train, y_test = train_test_split(

355 list(df.ada_embedding.values),376 list(df.ada_embedding.values), df.Score, test_size=0.2, random_state=42

356 df.Score,

357 test_size = 0.2,

358 random_state=42

359)377)

360```378```

361 379 


406We can use embeddings for zero shot classification without any labeled training data. For each class, we embed the class name or a short description of the class. To classify some new text in a zero-shot manner, we compare its embedding to all class embeddings and predict the class with the highest similarity.424We can use embeddings for zero shot classification without any labeled training data. For each class, we embed the class name or a short description of the class. To classify some new text in a zero-shot manner, we compare its embedding to all class embeddings and predict the class with the highest similarity.

407 425 

408```python426```python

409from openai.embeddings_utils import cosine_similarity, get_embedding427df = df[df.Score != 3]

410 428df["sentiment"] = df.Score.replace(

411df= df[df.Score!=3]429 {1: "negative", 2: "negative", 4: "positive", 5: "positive"}

412df['sentiment'] = df.Score.replace({1:'negative', 2:'negative', 4:'positive', 5:'positive'})430)

413 431 

414labels = ['negative', 'positive']432labels = ["negative", "positive"]

415label_embeddings = [get_embedding(label, model=model) for label in labels]433label_embeddings = [get_embedding(label, model=model) for label in labels]

416 434 

435 

417def label_score(review_embedding, label_embeddings):436def label_score(review_embedding, label_embeddings):

418 return cosine_similarity(review_embedding, label_embeddings[1]) - cosine_similarity(review_embedding, label_embeddings[0])437 return cosine_similarity(review_embedding, label_embeddings[1]) - cosine_similarity(

438 review_embedding, label_embeddings[0]

439 )

440 

419 441 

420prediction = 'positive' if label_score('Sample Review', label_embeddings) > 0 else 'negative'442prediction = (

443 "positive" if label_score(get_embedding("Sample Review", model=model), label_embeddings) > 0 else "negative"

444)

421```445```

422 446 

423 447 


433We evaluate the usefulness of these embeddings on a separate test set, where we plot similarity of the user and product embedding as a function of the rating. Interestingly, based on this approach, even before the user receives the product we can predict better than random whether they would like the product.457We evaluate the usefulness of these embeddings on a separate test set, where we plot similarity of the user and product embedding as a function of the rating. Interestingly, based on this approach, even before the user receives the product we can predict better than random whether they would like the product.

434 458 

435```python459```python

436user_embeddings = df.groupby('UserId').ada_embedding.apply(np.mean)460user_embeddings = df.groupby("UserId").ada_embedding.apply(np.mean)

437prod_embeddings = df.groupby('ProductId').ada_embedding.apply(np.mean)461prod_embeddings = df.groupby("ProductId").ada_embedding.apply(np.mean)

438```462```

439 463 

440 464 


456matrix = np.vstack(df.ada_embedding.values)480matrix = np.vstack(df.ada_embedding.values)

457n_clusters = 4481n_clusters = 4

458 482 

459kmeans = KMeans(n_clusters = n_clusters, init='k-means++', random_state=42)483kmeans = KMeans(n_clusters=n_clusters, init="k-means++", random_state=42)

460kmeans.fit(matrix)484kmeans.fit(matrix)

461df['Cluster'] = kmeans.labels_485df["Cluster"] = kmeans.labels_

462```486```

463 487 

464 488 

Details

225client = OpenAI()226client = OpenAI()

226 227 

227try:228try:

228 #Make your OpenAI API request here229 response = client.responses.create(model="gpt-5.6", input="Hello world")

229 response = client.responses.create(

230 model="gpt-5.6",

231 input="Hello world"

232 )

233except openai.APIError as e:

234 #Handle API error here, e.g. retry or log

235 print(f"OpenAI API returned an API Error: {e}")

236 pass

237except openai.APIConnectionError as e:230except openai.APIConnectionError as e:

238 #Handle connection error here

239 print(f"Failed to connect to OpenAI API: {e}")231 print(f"Failed to connect to OpenAI API: {e}")

240 pass

241except openai.RateLimitError as e:232except openai.RateLimitError as e:

242 #Handle rate limit error (we recommend using exponential backoff)

243 print(f"OpenAI API request exceeded rate limit: {e}")233 print(f"OpenAI API request exceeded rate limit: {e}")

244 pass234except openai.APIError as e:

235 print(f"OpenAI API returned an API Error: {e}")

236else:

237 print(response.output_text)

245```238```

guides/evals.md +7 −6

Details

317 319 

318```python320```python

319from openai import OpenAI321from openai import OpenAI

322 

320client = OpenAI()323client = OpenAI()

321 324 

322file = client.files.create(325file = client.files.create(file=open("tickets.jsonl", "rb"), purpose="evals")

323 file=open("tickets.jsonl", "rb"),

324 purpose="evals"

325)

326 326 

327print(file)327print(file)

328```328```


414 "input_messages": {415 "input_messages": {

415 "type": "template",416 "type": "template",

416 "template": [417 "template": [

417 {"role": "developer", "content": "You are an expert in categorizing IT support tickets. Given the support ticket below, categorize the request into one of 'Hardware', 'Software', or 'Other'. Respond with only one of those words."},418 {

419 "role": "developer",

420 "content": "You are an expert in categorizing IT support tickets. Given the support ticket below, categorize the request into one of 'Hardware', 'Software', or 'Other'. Respond with only one of those words.",

421 },

418 {"role": "user", "content": "{{ item.ticket_text }}"},422 {"role": "user", "content": "{{ item.ticket_text }}"},

419 ],423 ],

420 },424 },


515from openai import OpenAI519from openai import OpenAI

516client = OpenAI()520client = OpenAI()

517 521 

518run = client.evals.runs.retrieve("YOUR_EVAL_ID", "YOUR_RUN_ID")522run = client.evals.runs.retrieve("YOUR_RUN_ID", eval_id="YOUR_EVAL_ID")

519print(run)523print(run)

520```524```

521 525 

Details

160 },161 },

161 ],162 ],

162 },163 },

163 ]164 ],

164)165)

165 166 

166print(response.output_text)167print(response.output_text)


291 292 

292```python293```python

293from openai import OpenAI294from openai import OpenAI

295 

294client = OpenAI()296client = OpenAI()

295 297 

296file = client.files.create(298file = client.files.create(file=open("draconomicon.pdf", "rb"), purpose="user_data")

297 file=open("draconomicon.pdf", "rb"),

298 purpose="user_data"

299)

300 299 

301response = client.responses.create(300response = client.responses.create(

302 model="gpt-5.6",301 model="gpt-5.6",


312 "type": "input_text",311 "type": "input_text",

313 "text": "What is the first dragon in the book?",312 "text": "What is the first dragon in the book?",

314 },313 },

315 ]314 ],

316 }315 }

317 ]316 ],

318)317)

319 318 

320print(response.output_text)319print(response.output_text)


462 },462 },

463 ],463 ],

464 },464 },

465 ]465 ],

466)466)

467 467 

468print(response.output_text)468print(response.output_text)

Details

99def get_horoscope(sign):100def get_horoscope(sign):

100 return f"{sign}: Next Tuesday you will befriend a baby otter."101 return f"{sign}: Next Tuesday you will befriend a baby otter."

101 102 

103 

102# Create a running input list we will add to over time104# Create a running input list we will add to over time

103input_list = [105input_list = [{"role": "user", "content": "What is my horoscope? I am an Aquarius."}]

104 {"role": "user", "content": "What is my horoscope? I am an Aquarius."}

105]

106 106 

107# 2. Prompt the model with tools defined107# 2. Prompt the model with tools defined

108response = client.responses.create(108response = client.responses.create(


122 horoscope = get_horoscope(sign)122 horoscope = get_horoscope(sign)

123 123 

124 # 4. Provide function call results to the model124 # 4. Provide function call results to the model

125 input_list.append({125 input_list.append(

126 {

126 "type": "function_call_output",127 "type": "function_call_output",

127 "call_id": item.call_id,128 "call_id": item.call_id,

128 "output": horoscope,129 "output": horoscope,

129 })130 }

131 )

130 132 

131print("Final input:")133print("Final input:")

132print(input_list)134print(input_list)


391Execute function calls and append results393Execute function calls and append results

392 394 

393```python395```python

396input_messages += response.output

397 

394for tool_call in response.output:398for tool_call in response.output:

395 if tool_call.type != "function_call":399 if tool_call.type != "function_call":

396 continue400 continue


399 args = json.loads(tool_call.arguments)403 args = json.loads(tool_call.arguments)

400 404 

401 result = call_function(name, args)405 result = call_function(name, args)

402 input_messages.append({406 input_messages.append(

407 {

403 "type": "function_call_output",408 "type": "function_call_output",

404 "call_id": tool_call.call_id,409 "call_id": tool_call.call_id,

405 "output": str(result)410 "output": json.dumps(result),

406 })411 }

412 )

407```413```

408 414 

409```javascript415```javascript

416input.push(...response.output);

417 

410for (const toolCall of response.output) {418for (const toolCall of response.output) {

411 if (toolCall.type !== "function_call") {419 if (toolCall.type !== "function_call") {

412 continue;420 continue;


436 return get_weather(**args)444 return get_weather(**args)

437 if name == "send_email":445 if name == "send_email":

438 return send_email(**args)446 return send_email(**args)

447 raise ValueError(f"Unknown function: {name}")

439```448```

440 449 

441```javascript450```javascript


470response = client.responses.create(479response = client.responses.create(

471 model="gpt-5.6",480 model="gpt-5.6",

472 input=input_messages,481 input=input_messages,

473 tools=tools,482 tools=responses_tools,

474)483)

484 

485print(response.output_text)

475```486```

476 487 

477```javascript488```javascript


658 669 

659client = OpenAI()670client = OpenAI()

660 671 

661tools = [{672tools = [

673 {

662 "type": "function",674 "type": "function",

663 "name": "get_weather",675 "name": "get_weather",

664 "description": "Get current temperature for a given location.",676 "description": "Get current temperature for a given location.",


667 "properties": {679 "properties": {

668 "location": {680 "location": {

669 "type": "string",681 "type": "string",

670 "description": "City and country e.g. Bogotá, Colombia"682 "description": "City and country e.g. Bogotá, Colombia",

671 }683 }

672 },684 },

673 "required": [685 "required": ["location"],

674 "location"686 "additionalProperties": False,

675 ],687 },

676 "additionalProperties": False

677 }688 }

678}]689]

679 690 

680stream = client.responses.create(691stream = client.responses.create(

681 model="gpt-5.6",692 model="gpt-5.6",

682 input=[{"role": "user", "content": "What's the weather like in Paris today?"}],693 input=[{"role": "user", "content": "What's the weather like in Paris today?"}],

683 tools=tools,694 tools=tools,

684 stream=True695 stream=True,

685)696)

686 697 

687for event in stream:698for event in stream:


769final_tool_calls = {}780final_tool_calls = {}

770 781 

771for event in stream:782for event in stream:

772 if event.type === 'response.output_item.added':783 if event.type == "response.output_item.added":

773 final_tool_calls[event.output_index] = event.item;784 final_tool_calls[event.output_index] = event.item

774 elif event.type === 'response.function_call_arguments.delta':785 elif event.type == "response.function_call_arguments.delta":

775 index = event.output_index786 index = event.output_index

776 787 

777 if final_tool_calls[index]:788 if final_tool_calls[index]:


840 "name": "code_exec",851 "name": "code_exec",

841 "description": "Executes arbitrary Python code.",852 "description": "Executes arbitrary Python code.",

842 }853 }

843 ]854 ],

844)855)

845print(response.output)856print(response.output)

846```857```


928 "definition": grammar,939 "definition": grammar,

929 },940 },

930 }941 }

931 ]942 ],

932)943)

933print(response.output)944print(response.output)

934```945```


1102 "definition": grammar,1113 "definition": grammar,

1103 },1114 },

1104 }1115 }

1105 ]1116 ],

1106)1117)

1107print(response.output)1118print(response.output)

1108```1119```

guides/graders.md +26 −39

Details

17- [Score model grader](#score-model-graders)17- [Score model grader](#score-model-graders)

18- [Python code execution](#python-graders)18- [Python code execution](#python-graders)

19 19 

20In reinforcement fine-tuning, you can nest and combine graders by using [multigraders](#multigraders).20In reinforcement fine-tuning, you can nest and combine graders by using [`multigrader` objects](#combined-graders).

21 21 

22Use this guide to learn about each grader type and see starter examples. To build a grader and get started with reinforcement fine-tuning, see the [RFT guide](https://developers.openai.com/api/docs/guides/reinforcement-fine-tuning). Or to get started with evals, see the [Evals guide](https://developers.openai.com/api/docs/guides/evals).22Use this guide to learn about each grader type and see starter examples. To build a grader and get started with reinforcement fine-tuning, see the [RFT guide](https://developers.openai.com/api/docs/guides/reinforcement-fine-tuning). Or to get started with evals, see the [Evals guide](https://developers.openai.com/api/docs/guides/evals).

23 23 


25 25 

26The inputs to certain graders use a templating syntax to grade multiple examples with the same configuration. Any string with `{{ }}` double curly braces will be substituted with the variable value.26The inputs to certain graders use a templating syntax to grade multiple examples with the same configuration. Any string with `{{ }}` double curly braces will be substituted with the variable value.

27 27 

28Each input inside the `{{}}` must include a _namespace_ and a _variable_ with the following format `{{ namespace.variable }}`. The only supported namespaces are `item` and `sample`.28Each input inside the `{{}}` must include a _namespace_ and a _variable_ with the following format `{{ namespace.variable }}`. The only supported namespace values are `item` and `sample`.

29 29 

30All nested variables can be accessed with JSON path like syntax.30All nested variables can be accessed with JSON path like syntax.

31 31 


86 86 

87The `arguments` grader is prone to under-rewarding the model if the function arguments are subtly incorrect, like if `1` is submitted instead of the floating point `1.0`, or if a state name is given as an abbreviation instead of spelling it out. To avoid this, you can use a `text_similarity` grader instead of a `string_check` grader, or a `score_model` grader to have a LLM check for semantic similarity.87The `arguments` grader is prone to under-rewarding the model if the function arguments are subtly incorrect, like if `1` is submitted instead of the floating point `1.0`, or if a state name is given as an abbreviation instead of spelling it out. To avoid this, you can use a `text_similarity` grader instead of a `string_check` grader, or a `score_model` grader to have a LLM check for semantic similarity.

88 88 

89## String check grader89## String check graders

90 90 

91Use these simple string operations to return a 0 or 1. String check graders are good for scoring straightforward pass or fail answers—for example, the correct name of a city, a yes or no answer, or an answer containing or starting with the correct information.91Use these basic string operations to return a 0 or 1. String check graders are good for scoring straightforward pass or fail answers—for example, the correct name of a city, a yes or no answer, or an answer containing or starting with the correct information.

92 92 

93```json93```json

94{94{


107- `like`: Returns 1 if the input contains the reference (case-sensitive), 0 otherwise107- `like`: Returns 1 if the input contains the reference (case-sensitive), 0 otherwise

108- `ilike`: Returns 1 if the input contains the reference (not case-sensitive), 0 otherwise108- `ilike`: Returns 1 if the input contains the reference (not case-sensitive), 0 otherwise

109 109 

110## Text similarity grader110## Text similarity graders

111 111 

112Use text similarity graders when to evaluate how close the model-generated output is to the reference, scored with various evaluation frameworks.112Use text similarity graders when to evaluate how close the model-generated output is to the reference, scored with various evaluation frameworks.

113 113 


182api_key = os.environ["OPENAI_API_KEY"]182api_key = os.environ["OPENAI_API_KEY"]

183headers = {"Authorization": f"Bearer {api_key}"}183headers = {"Authorization": f"Bearer {api_key}"}

184 184 

185# define a dummy grader for illustration purposes185# Define a score-model grader.

186grader = {186grader = {

187 "type": "score_model",187 "type": "score_model",

188 "name": "my_score_model",188 "name": "my_score_model",

189 "input": [189 "input": [

190 {190 {

191 "role": "system",191 "role": "system",

192 "content": "You are an expert grader. If the reference and model answer are exact matches, output a score of 1. If they are somewhat similar in meaning, output a score in 0.5. Otherwise, give a score of 0."192 "content": "You are an expert grader. If the reference and model answer are exact matches, output a score of 1. If they are somewhat similar in meaning, output a score in 0.5. Otherwise, give a score of 0.",

193 },193 },

194 {194 {

195 "role": "user",195 "role": "user",

196 "content": "Reference: {{ item.reference_answer }}. Model answer: {{ sample.output_text }}"196 "content": "Reference: {{ item.reference_answer }}. Model answer: {{ sample.output_text }}",

197 }197 },

198 ],198 ],

199 "pass_threshold": 0.5,199 "pass_threshold": 0.5,

200 "model": "o4-mini-2025-04-16",200 "model": "o4-mini-2025-04-16",


202 "sampling_params": {202 "sampling_params": {

203 "max_completions_tokens": 32768,203 "max_completions_tokens": 32768,

204 "top_p": 1,204 "top_p": 1,

205 "reasoning_effort": "medium"205 "reasoning_effort": "medium",

206 },206 },

207}207}

208 208 


211response = requests.post(211response = requests.post(

212 "https://api.openai.com/v1/fine_tuning/alpha/graders/validate",212 "https://api.openai.com/v1/fine_tuning/alpha/graders/validate",

213 json=payload,213 json=payload,

214 headers=headers214 headers=headers,

215)215)

216print("validate response:", response.text)216print("validate response:", response.text)

217 217 

218# run the grader with a test reference and sample218# run the grader with a test reference and sample

219payload = {219payload = {"grader": grader, "item": {"reference_answer": 1.0}, "model_sample": "0.9"}

220 "grader": grader,

221 "item": {

222 "reference_answer": 1.0

223 },

224 "model_sample": "0.9"

225}

226response = requests.post(220response = requests.post(

227 "https://api.openai.com/v1/fine_tuning/alpha/graders/run",221 "https://api.openai.com/v1/fine_tuning/alpha/graders/run",

228 json=payload,222 json=payload,

229 headers=headers223 headers=headers,

230)224)

231print("run response:", response.text)225print("run response:", response.text)

232```226```


252}246}

253```247```

254 248 

255This format queries the model not just for the numeric `result` (the reward value for the query), but also provides the model some space to think through the reasoning behind the score. When you are writing your grader prompt, it may be useful to refer to these two fields by name explicitly (e.g. "include reasoning about the type of chemical bonds present in the molecule in the conclusion of your reasoning step", or "return a value of -1.0 in the `result` field if the inputs do not satisfy condition X").249This format queries the model not just for the numeric `result` (the reward value for the query), but also provides the model some space to think through the reasoning behind the score. When you are writing your grader prompt, it may be useful to refer to these two fields by name explicitly (for example, “include reasoning about the type of chemical bonds present in the molecule in the conclusion of your reasoning step, or return a value of 1.0 in the `result` field if the inputs do not satisfy condition X).

256 250 

257### Model grader constraints251### Model grader constraints

258 252 


357 return fuzz.WRatio(output_text, reference_answer, processor=utils.default_process) / 100.0353 return fuzz.WRatio(output_text, reference_answer, processor=utils.default_process) / 100.0

358"""354"""

359 355 

360# define a dummy grader for illustration purposes356# Define a Python grader.

361grader = {357grader = {"type": "python", "source": grading_function}

362 "type": "python",

363 "source": grading_function

364}

365 358 

366# validate the grader359# validate the grader

367payload = {"grader": grader}360payload = {"grader": grader}

368response = requests.post(361response = requests.post(

369 "https://api.openai.com/v1/fine_tuning/alpha/graders/validate",362 "https://api.openai.com/v1/fine_tuning/alpha/graders/validate",

370 json=payload,363 json=payload,

371 headers=headers364 headers=headers,

372)365)

373print("validate request_id:", response.headers["x-request-id"])366print("validate request_id:", response.headers["x-request-id"])

374print("validate response:", response.text)367print("validate response:", response.text)


376# run the grader with a test reference and sample369# run the grader with a test reference and sample

377payload = {370payload = {

378 "grader": grader,371 "grader": grader,

379 "item": {372 "item": {"reference_answer": "fuzzy wuzzy had no hair"},

380 "reference_answer": "fuzzy wuzzy had no hair"373 "model_sample": "fuzzy wuzzy was a bear",

381 },

382 "model_sample": "fuzzy wuzzy was a bear"

383}374}

384response = requests.post(375response = requests.post(

385 "https://api.openai.com/v1/fine_tuning/alpha/graders/run",376 "https://api.openai.com/v1/fine_tuning/alpha/graders/run",

386 json=payload,377 json=payload,

387 headers=headers378 headers=headers,

388)379)

389print("run request_id:", response.headers["x-request-id"])380print("run request_id:", response.headers["x-request-id"])

390print("run response:", response.text)381print("run response:", response.text)


399import inspect390import inspect

400 391 

401grader_module = importlib.import_module("grader")392grader_module = importlib.import_module("grader")

402grader = {393grader = {"type": "python", "source": inspect.getsource(grader_module)}

403 "type": "python",

404 "source": inspect.getsource(grader_module)

405}

406```394```

407 395 

396 

408This will automatically use the entire source code of your `grader.py` file as the grader which can be helpful for longer graders.397This will automatically use the entire source code of your `grader.py` file as the grader which can be helpful for longer graders.

409 398 

410### Technical constraints399### Technical constraints


435ast-grep-py==0.36.2424ast-grep-py==0.36.2

436```425```

437 426 

438Additionally the following nltk corpora are available:427Additionally, the following NLTK corpora are available:

439 428 

440```429```

441punkt430punkt


445names434names

446```435```

447 436 

448## Multigraders437## Combined graders

449 438 

450> Currently, this grader is only used for Reinforcement fine-tuning439> Currently, this grader is only used for Reinforcement fine-tuning

451 440 

452A `multigrader` object combines the output of multiple graders to produce a single score. Multigraders work by computing grades over the fields of other grader objects and turning those sub-grades into an overall grade. This is useful when a correct answer depends on multiple things being true—for example, that the text is similar _and_ that the answer contains a specific string.441A `multigrader` object combines the output of multiple graders to produce a single score. Combined graders compute grades over the fields of other grader objects and turn those sub-grades into an overall grade. This is useful when a correct answer depends on multiple things being true—for example, that the text is similar _and_ that the answer contains a specific string.

453 442 

454As an example, say you wanted the model to output JSON with the following two fields:443As an example, say you wanted the model to output JSON with the following two fields:

455 444 


490 479 

491In this example, it’s important for the model to get the email exactly right (`string_check` returns either 0 or 1) but we tolerate some misspellings on the name (`text_similarity` returns range from 0 to 1). Samples that get the email wrong will score between 0-0.5, and samples that get the email right will score between 0.5-1.0.480In this example, it’s important for the model to get the email exactly right (`string_check` returns either 0 or 1) but we tolerate some misspellings on the name (`text_similarity` returns range from 0 to 1). Samples that get the email wrong will score between 0-0.5, and samples that get the email right will score between 0.5-1.0.

492 481 

493You cannot create a multigrader with a nested multigrader inside.482You cannot nest one `multigrader` inside another.

494 483 

495The calculate output field will have the keys of the input `graders` as possible variables and the following features are supported:484The calculate output field will have the keys of the input `graders` as possible variables and the following features are supported:

496 485 

Details

98listen to the heartbeat of a baby otter.99listen to the heartbeat of a baby otter.

99"""100"""

100 101 

101result = client.images.generate(102result = client.images.generate(model="gpt-image-2", prompt=prompt)

102 model="gpt-image-2",

103 prompt=prompt

104)

105 103 

106image_base64 = result.data[0].b64_json104image_base64 = result.data[0].b64_json

107image_bytes = base64.b64decode(image_base64)105image_bytes = base64.b64decode(image_base64)


412)410)

413 411 

414image_generation_calls = [412image_generation_calls = [

415 output413 output for output in response.output if output.type == "image_generation_call"

416 for output in response.output

417 if output.type == "image_generation_call"

418]414]

419 415 

420image_data = [output.result for output in image_generation_calls]416image_data = [output.result for output in image_generation_calls]


709 open("incense-kit.png", "rb"),708 open("incense-kit.png", "rb"),

710 open("soap.png", "rb"),709 open("soap.png", "rb"),

711 ],710 ],

712 prompt=prompt711 prompt=prompt,

713)712)

714 713 

715image_base64 = result.data[0].b64_json714image_base64 = result.data[0].b64_json


833 {834 {

834 "type": "input_image",835 "type": "input_image",

835 "file_id": fileId,836 "file_id": fileId,

836 }837 },

837 ],838 ],

838 },839 },

839 ],840 ],


843 "quality": "high",844 "quality": "high",

844 "input_image_mask": {845 "input_image_mask": {

845 "file_id": maskId,846 "file_id": maskId,

846 }847 },

847 },848 },

848 ],849 ],

849)850)


931 model="gpt-image-2",932 model="gpt-image-2",

932 image=open("sunlit_lounge.png", "rb"),933 image=open("sunlit_lounge.png", "rb"),

933 mask=open("mask.png", "rb"),934 mask=open("mask.png", "rb"),

934 prompt="A sunlit indoor lounge area with a pool containing a flamingo"935 prompt="A sunlit indoor lounge area with a pool containing a flamingo",

935)936)

936 937 

937image_base64 = result.data[0].b64_json938image_base64 = result.data[0].b64_json

Details

160 160 

161response = client.responses.create(161response = client.responses.create(

162 model="gpt-5.6",162 model="gpt-5.6",

163 input=[{163 input=[

164 {

164 "role": "user",165 "role": "user",

165 "content": [166 "content": [

166 {"type": "input_text", "text": "what's in this image?"},167 {"type": "input_text", "text": "what's in this image?"},


169 "image_url": "https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg",170 "image_url": "https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg",

170 },171 },

171 ],172 ],

172 }],173 }

174 ],

173)175)

174 176 

175print(response.output_text)177print(response.output_text)


288 {291 {

289 "role": "user",292 "role": "user",

290 "content": [293 "content": [

291 { "type": "input_text", "text": "what's in this image?" },294 {"type": "input_text", "text": "what's in this image?"},

292 {295 {

293 "type": "input_image",296 "type": "input_image",

294 "image_url": f"data:image/jpeg;base64,{base64_image}",297 "image_url": f"data:image/jpeg;base64,{base64_image}",


398 403 

399response = client.responses.create(404response = client.responses.create(

400 model="gpt-5.6",405 model="gpt-5.6",

401 input=[{406 input=[

407 {

402 "role": "user",408 "role": "user",

403 "content": [409 "content": [

404 {"type": "input_text", "text": "what's in this image?"},410 {"type": "input_text", "text": "what's in this image?"},


407 "file_id": file_id,413 "file_id": file_id,

408 },414 },

409 ],415 ],

410 }],416 }

417 ],

411)418)

412 419 

413print(response.output_text)420print(response.output_text)

Details

78 78 

79```python79```python

80from openai import OpenAI80from openai import OpenAI

81import os

82 81 

83client = OpenAI(82client = OpenAI()

84 api_key=os.environ.get(

85 "OPENAI_API_KEY", "<your OpenAI API key if not set as env var>"

86 )

87)

88 83 

89SYS_PROMPT_SWEBENCH = """84SYS_PROMPT_SWEBENCH = """

90You will be tasked to fix an issue from an open-source repository.85You will be tasked to fix an issue from an open-source repository.


252 instructions=SYS_PROMPT_SWEBENCH,247 instructions=SYS_PROMPT_SWEBENCH,

253 model="gpt-4.1-2025-04-14",248 model="gpt-4.1-2025-04-14",

254 tools=[python_bash_patch_tool],249 tools=[python_bash_patch_tool],

255 input=f"Please answer the following question:\nBug: Typerror..."250 input="Please answer the following question:\nBug: Typerror...",

256)251)

257 252 

258response.to_dict()["output"]253response.to_dict()["output"]

Details

314 314 

315```python315```python

316response = client.responses.create(316response = client.responses.create(

317model="gpt-5.1",317 model="gpt-5.1", input=RESPONSE_INPUT, tools=[{"type": "apply_patch"}]

318input=RESPONSE_INPUT,

319tools=[{"type": "apply_patch"}]

320)318)

321```319```

322 320 


351 "type": "apply_patch_call_output",350 "type": "apply_patch_call_output",

352 "call_id": call["call_id"],351 "call_id": call["call_id"],

353 "status": "completed" if success else "failed",352 "status": "completed" if success else "failed",

354 "output": log_output353 "output": log_output,

355}354}

356```355```

357 356 

Details

91response = client.responses.create(92response = client.responses.create(

92 model="gpt-5.2",93 model="gpt-5.2",

93 input="Think carefully and outline your steps before answering. How much gold would it take to coat the Statue of Liberty in a 1mm layer?",94 input="Think carefully and outline your steps before answering. How much gold would it take to coat the Statue of Liberty in a 1mm layer?",

94 reasoning={95 reasoning={"effort": "none"},

95 "effort": "none"

96 }

97)96)

98 97 

99print(response)98print(response)


150response = client.responses.create(150response = client.responses.create(

151 model="gpt-5.2",151 model="gpt-5.2",

152 input="What is the answer to the ultimate question of life, the universe, and everything?",152 input="What is the answer to the ultimate question of life, the universe, and everything?",

153 text={153 text={"verbosity": "low"},

154 "verbosity": "low"

155 }

156)154)

157 155 

158print(response)156print(response)


604 "role": "user",602 "role": "user",

605 "content": "write a very long poem about a dog.",603 "content": "write a very long poem about a dog.",

606 },604 },

607 ]605 ],

608)606)

609 607 

610 608 


619 "role": "user",617 "role": "user",

620 "content": "write a very long poem about a dog.",618 "content": "write a very long poem about a dog.",

621 },619 },

622 output_json[0]620 output_json[0],

623 ]621 ],

624)622)

625 623 

626 624 

Details

241 241 

242The easiest way to implement apply_patch is with our first-class implementation in the Responses API, but you can also use our freeform tool implementation with [context-free grammar](https://developers.openai.com/cookbook/examples/gpt-5/gpt-5_new_params_and_tools?utm_source=chatgpt.com#3-contextfree-grammar-cfg). Both are demonstrated below.242The easiest way to implement apply_patch is with our first-class implementation in the Responses API, but you can also use our freeform tool implementation with [context-free grammar](https://developers.openai.com/cookbook/examples/gpt-5/gpt-5_new_params_and_tools?utm_source=chatgpt.com#3-contextfree-grammar-cfg). Both are demonstrated below.

243 243 

244```py244```python

245# Sample script to demonstrate the server-defined apply_patch tool245# Sample script to demonstrate the server-defined apply_patch tool

246 246 

247import json247import json


546 },547 },

547}548}

548 549 

549...550TOOLS = [GIT_TOOL]

550 551 

551PROMPT_TOOL_USE_DIRECTIVE = "- Strictly avoid raw `cmd`/terminal when a dedicated tool exists. Default to solver tools: `git` (all git), `list_dir`, `apply_patch`. Use `cmd`/`run_terminal_cmd` only when no listed tool can perform the action." # update with your desired tools552PROMPT_TOOL_USE_DIRECTIVE = (

553 "- Strictly avoid raw `cmd`/terminal for Git operations. Use the dedicated "

554 "`git` tool instead."

555)

552```556```

553 557 

558 

554### Other Custom Tools (web search, semantic search, memory, etc.)559### Other Custom Tools (web search, semantic search, memory, etc.)

555 560 

556The model hasn’t necessarily been post-trained to excel at these tools, but we have seen success here as well. To get the most out of these tools, we recommend:561The model hasn’t necessarily been post-trained to excel at these tools, but we have seen success here as well. To get the most out of these tools, we recommend:

Details

94response = client.responses.create(95response = client.responses.create(

95 model="gpt-5.4",96 model="gpt-5.4",

96 input="Think carefully and outline your steps before answering. How much gold would it take to coat the Statue of Liberty in a 1mm layer?",97 input="Think carefully and outline your steps before answering. How much gold would it take to coat the Statue of Liberty in a 1mm layer?",

97 reasoning={98 reasoning={"effort": "none"},

98 "effort": "none"

99 }

100)99)

101 100 

102print(response)101print(response)


153response = client.responses.create(153response = client.responses.create(

154 model="gpt-5.4",154 model="gpt-5.4",

155 input="What is the answer to the ultimate question of life, the universe, and everything?",155 input="What is the answer to the ultimate question of life, the universe, and everything?",

156 text={156 text={"verbosity": "low"},

157 "verbosity": "low"

158 }

159)157)

160 158 

161print(response)159print(response)

Details

128 128 

129```python129```python

130context = [130context = [

131 { "role": "system", "content": "You are a helpful assistant." },131 {"role": "system", "content": "You are a helpful assistant."},

132 { "role": "user", "content": "Hello!" }132 {"role": "user", "content": "Hello!"},

133]133]

134 134 

135completion = client.chat.completions.create(135completion = client.chat.completions.create(model="gpt-5.6", messages=context)

136 model="gpt-5.6",

137 messages=context

138)

139 136 

140response = client.responses.create(137response = client.responses.create(model="gpt-5.6", input=context)

141 model="gpt-5.6",

142 input=context

143)

144```138```

145 139 

146 140 


174 model="gpt-5.6",169 model="gpt-5.6",

175 messages=[170 messages=[

176 {"role": "system", "content": "You are a helpful assistant."},171 {"role": "system", "content": "You are a helpful assistant."},

177 {"role": "user", "content": "Hello!"}172 {"role": "user", "content": "Hello!"},

178 ]173 ],

179)174)

180print(completion.choices[0].message.content)175print(completion.choices[0].message.content)

181```176```


219client = OpenAI()215client = OpenAI()

220 216 

221response = client.responses.create(217response = client.responses.create(

222 model="gpt-5.6",218 model="gpt-5.6", instructions="You are a helpful assistant.", input="Hello!"

223 instructions="You are a helpful assistant.",

224 input="Hello!"

225)219)

226print(response.output_text)220print(response.output_text)

227```221```


297```python291```python

298messages = [292messages = [

299 {"role": "system", "content": "You are a helpful assistant."},293 {"role": "system", "content": "You are a helpful assistant."},

300 {"role": "user", "content": "What is the capital of France?"}294 {"role": "user", "content": "What is the capital of France?"},

301]295]

302res1 = client.chat.completions.create(model="gpt-5.6", messages=messages)296res1 = client.chat.completions.create(model="gpt-5.6", messages=messages)

303 297 


316 Multi-turn conversation310 Multi-turn conversation

317 311 

318```python312```python

319context = [313context = [{"role": "user", "content": "What is the capital of France?"}]

320 { "role": "user", "content": "What is the capital of France?" }

321]

322res1 = client.responses.create(314res1 = client.responses.create(

323 model="gpt-5.6",315 model="gpt-5.6",

324 input=context,316 input=context,


328context += res1.output320context += res1.output

329 321 

330# Add the next user message322# Add the next user message

331context += [323context += [{"role": "user", "content": "And its population?"}]

332 { "role": "user", "content": "And its population?" }

333]

334 324 

335res2 = client.responses.create(325res2 = client.responses.create(

336 model="gpt-5.6",326 model="gpt-5.6",


380 370 

381```python371```python

382res1 = client.responses.create(372res1 = client.responses.create(

383 model="gpt-5.6",373 model="gpt-5.6", input="What is the capital of France?", store=True

384 input="What is the capital of France?",

385 store=True

386)374)

387 375 

388res2 = client.responses.create(376res2 = client.responses.create(

389 model="gpt-5.6",377 model="gpt-5.6",

390 input="And its population?",378 input="And its population?",

391 previous_response_id=res1.id,379 previous_response_id=res1.id,

392 store=True380 store=True,

393)381)

394```382```

395 383 


500 "schema": {489 "schema": {

501 "type": "object",490 "type": "object",

502 "properties": {491 "properties": {

503 "name": {492 "name": {"type": "string", "minLength": 1},

504 "type": "string",493 "age": {"type": "number", "minimum": 0, "maximum": 130},

505 "minLength": 1494 },

495 "required": ["name", "age"],

496 "additionalProperties": False,

506 },497 },

507 "age": {

508 "type": "number",

509 "minimum": 0,

510 "maximum": 130

511 }

512 },498 },

513 "required": [

514 "name",

515 "age"

516 ],

517 "additionalProperties": False

518 }

519 }

520 },499 },

521 reasoning_effort="medium"500 reasoning_effort="medium",

522)501)

523```502```

524 503 


611 "schema": {590 "schema": {

612 "type": "object",591 "type": "object",

613 "properties": {592 "properties": {

614 "name": {593 "name": {"type": "string", "minLength": 1},

615 "type": "string",594 "age": {"type": "number", "minimum": 0, "maximum": 130},

616 "minLength": 1

617 },595 },

618 "age": {596 "required": ["name", "age"],

619 "type": "number",597 "additionalProperties": False,

620 "minimum": 0,

621 "maximum": 130

622 }

623 },598 },

624 "required": [

625 "name",

626 "age"

627 ],

628 "additionalProperties": False

629 }

630 }

631 }599 }

600 },

632)601)

633```602```

634 603 


729 model="gpt-5.6",700 model="gpt-5.6",

730 messages=[701 messages=[

731 {"role": "system", "content": "You are a helpful assistant."},702 {"role": "system", "content": "You are a helpful assistant."},

732 {"role": "user", "content": "Who is the current president of France?"}703 {"role": "user", "content": "Who is the current president of France?"},

733 ],704 ],

734 functions=[705 functions=[

735 {706 {


738 "parameters": {709 "parameters": {

739 "type": "object",710 "type": "object",

740 "properties": {"query": {"type": "string"}},711 "properties": {"query": {"type": "string"}},

741 "required": ["query"]712 "required": ["query"],

742 }713 },

743 }714 }

744 ]715 ],

745)716)

746```717```

747 718 


772answer = client.responses.create(743answer = client.responses.create(

773 model="gpt-5.6",744 model="gpt-5.6",

774 input="Who is the current president of France?",745 input="Who is the current president of France?",

775 tools=[{"type": "web_search"}]746 tools=[{"type": "web_search"}],

776)747)

777 748 

778print(answer.output_text)749print(answer.output_text)

Details

45 46 

46input_moderation = response.moderation.input47input_moderation = response.moderation.input

47output_moderation = response.moderation.output48output_moderation = response.moderation.output

49if input_moderation.type == "error":

50 raise RuntimeError(input_moderation.message)

51if output_moderation.type == "error":

52 raise RuntimeError(output_moderation.message)

48 53 

49print(input_moderation.flagged)54print(input_moderation.flagged)

50print(output_moderation.flagged)55print(output_moderation.flagged)

Details

91completion = client.chat.completions.create(91completion = client.chat.completions.create(

92 model="gpt-4.1",92 model="gpt-4.1",

93 messages=[93 messages=[

94 {94 {"role": "user", "content": refactor_prompt},

95 "role": "user",95 {"role": "user", "content": code},

96 "content": refactor_prompt

97 },

98 {

99 "role": "user",

100 "content": code

101 }

102 ],96 ],

103 prediction={97 prediction={"type": "content", "content": code},

104 "type": "content",

105 "content": code

106 }

107)98)

108 99 

109print(completion)100print(completion)


240stream = client.chat.completions.create(231stream = client.chat.completions.create(

241 model="gpt-4.1",232 model="gpt-4.1",

242 messages=[233 messages=[

243 {234 {"role": "user", "content": refactor_prompt},

244 "role": "user",235 {"role": "user", "content": code},

245 "content": refactor_prompt

246 },

247 {

248 "role": "user",

249 "content": code

250 }

251 ],236 ],

252 prediction={237 prediction={"type": "content", "content": code},

253 "type": "content",238 stream=True,

254 "content": code

255 },

256 stream=True

257)239)

258 240 

259for chunk in stream:241for chunk in stream:

Details

45response = client.responses.create(45response = client.responses.create(

46 model="gpt-5.6",46 model="gpt-5.6",

47 input="What does 'fit check for my napalm era' mean?",47 input="What does 'fit check for my napalm era' mean?",

48 service_tier="priority"48 service_tier="priority",

49)49)

50print(response)50print(response)

51```51```

Details

26 27 

27response = client.responses.create(28response = client.responses.create(

28 model="gpt-5.6",29 model="gpt-5.6",

29 input="Write a one-sentence bedtime story about a unicorn."30 input="Write a one-sentence bedtime story about a unicorn.",

30)31)

31 32 

32print(response.output_text)33print(response.output_text)


277 model="gpt-5.6",280 model="gpt-5.6",

278 reasoning={"effort": "low"},281 reasoning={"effort": "low"},

279 input=[282 input=[

280 {283 {"role": "developer", "content": "Talk like a pirate."},

281 "role": "developer",284 {"role": "user", "content": "Are semicolons optional in JavaScript?"},

282 "content": "Talk like a pirate."285 ],

283 },

284 {

285 "role": "user",

286 "content": "Are semicolons optional in JavaScript?"

287 }

288 ]

289)286)

290 287 

291print(response.output_text)288print(response.output_text)

Details

25 <div class="hidden">Text-out</div>25 <div class="hidden">Text-out</div>

26 Text meta-prompt26 Text meta-prompt

27 27 

28```python28````python

29from openai import OpenAI29from openai import OpenAI

30 30 

31client = OpenAI()31client = OpenAI()


92 )93 )

93 94 

94 return completion.choices[0].message.content95 return completion.choices[0].message.content

95```96````

96 97 

97 </div>98 </div>

98 <div data-content-switcher-pane data-value="audio-out" hidden>99 <div data-content-switcher-pane data-value="audio-out" hidden>


173 <div class="hidden">Text-out</div>175 <div class="hidden">Text-out</div>

174 Text meta-prompt for edits176 Text meta-prompt for edits

175 177 

176```python178````python

177from openai import OpenAI179from openai import OpenAI

178 180 

179client = OpenAI()181client = OpenAI()


259 )262 )

260 263 

261 return completion.choices[0].message.content264 return completion.choices[0].message.content

262```265````

263 266 

264 </div>267 </div>

265 <div data-content-switcher-pane data-value="audio-out" hidden>268 <div data-content-switcher-pane data-value="audio-out" hidden>


418 "schema": {422 "schema": {

419 "type": "object",423 "type": "object",

420 "properties": {424 "properties": {

421 "name": {425 "name": {"type": "string", "description": "The name of the schema"},

422 "type": "string",

423 "description": "The name of the schema"

424 },

425 "type": {426 "type": {

426 "type": "string",427 "type": "string",

427 "enum": [428 "enum": ["object", "array", "string", "number", "boolean", "null"],

428 "object",

429 "array",

430 "string",

431 "number",

432 "boolean",

433 "null"

434 ]

435 },429 },

436 "properties": {430 "properties": {

437 "type": "object",431 "type": "object",

438 "additionalProperties": {432 "additionalProperties": {"$ref": "#/$defs/schema_definition"},

439 "$ref": "#/$defs/schema_definition"

440 }

441 },433 },

442 "items": {434 "items": {

443 "anyOf": [435 "anyOf": [

444 {436 {"$ref": "#/$defs/schema_definition"},

445 "$ref": "#/$defs/schema_definition"437 {"type": "array", "items": {"$ref": "#/$defs/schema_definition"}},

446 },

447 {

448 "type": "array",

449 "items": {

450 "$ref": "#/$defs/schema_definition"

451 }

452 }

453 ]438 ]

454 },439 },

455 "required": {440 "required": {"type": "array", "items": {"type": "string"}},

456 "type": "array",441 "additionalProperties": {"type": "boolean"},

457 "items": {

458 "type": "string"

459 }

460 },442 },

461 "additionalProperties": {443 "required": ["type"],

462 "type": "boolean"

463 }

464 },

465 "required": [

466 "type"

467 ],

468 "additionalProperties": False,444 "additionalProperties": False,

469 "if": {445 "if": {"properties": {"type": {"const": "object"}}},

470 "properties": {446 "then": {"required": ["properties"]},

471 "type": {

472 "const": "object"

473 }

474 }

475 },

476 "then": {

477 "required": [

478 "properties"

479 ]

480 },

481 "$defs": {447 "$defs": {

482 "schema_definition": {448 "schema_definition": {

483 "type": "object",449 "type": "object",


490 "string",456 "string",

491 "number",457 "number",

492 "boolean",458 "boolean",

493 "null"459 "null",

494 ]460 ],

495 },461 },

496 "properties": {462 "properties": {

497 "type": "object",463 "type": "object",

498 "additionalProperties": {464 "additionalProperties": {"$ref": "#/$defs/schema_definition"},

499 "$ref": "#/$defs/schema_definition"

500 }

501 },465 },

502 "items": {466 "items": {

503 "anyOf": [467 "anyOf": [

504 {468 {"$ref": "#/$defs/schema_definition"},

505 "$ref": "#/$defs/schema_definition"

506 },

507 {469 {

508 "type": "array",470 "type": "array",

509 "items": {471 "items": {"$ref": "#/$defs/schema_definition"},

510 "$ref": "#/$defs/schema_definition"

511 }

512 }

513 ]

514 },472 },

515 "required": {473 ]

516 "type": "array",

517 "items": {

518 "type": "string"

519 }

520 },474 },

521 "additionalProperties": {475 "required": {"type": "array", "items": {"type": "string"}},

522 "type": "boolean"476 "additionalProperties": {"type": "boolean"},

523 }

524 },477 },

525 "required": [478 "required": ["type"],

526 "type"

527 ],

528 "additionalProperties": False,479 "additionalProperties": False,

529 "if": {480 "if": {"properties": {"type": {"const": "object"}}},

530 "properties": {481 "then": {"required": ["properties"]},

531 "type": {

532 "const": "object"

533 }

534 }482 }

535 },483 },

536 "then": {484 },

537 "required": [

538 "properties"

539 ]

540 }

541 }

542 }

543 }

544}485}

545 486 

546META_PROMPT = """487META_PROMPT = """


744 "schema": {686 "schema": {

745 "type": "object",687 "type": "object",

746 "properties": {688 "properties": {

747 "name": {689 "name": {"type": "string", "description": "The name of the function"},

748 "type": "string",

749 "description": "The name of the function"

750 },

751 "description": {690 "description": {

752 "type": "string",691 "type": "string",

753 "description": "A description of what the function does"692 "description": "A description of what the function does",

754 },693 },

755 "parameters": {694 "parameters": {

756 "$ref": "#/$defs/schema_definition",695 "$ref": "#/$defs/schema_definition",

757 "description": "A JSON schema that defines the function's parameters"696 "description": "A JSON schema that defines the function's parameters",

758 }

759 },697 },

760 "required": [698 },

761 "name",699 "required": ["name", "description", "parameters"],

762 "description",

763 "parameters"

764 ],

765 "additionalProperties": False,700 "additionalProperties": False,

766 "$defs": {701 "$defs": {

767 "schema_definition": {702 "schema_definition": {


775 "string",710 "string",

776 "number",711 "number",

777 "boolean",712 "boolean",

778 "null"713 "null",

779 ]714 ],

780 },715 },

781 "properties": {716 "properties": {

782 "type": "object",717 "type": "object",

783 "additionalProperties": {718 "additionalProperties": {"$ref": "#/$defs/schema_definition"},

784 "$ref": "#/$defs/schema_definition"

785 }

786 },719 },

787 "items": {720 "items": {

788 "anyOf": [721 "anyOf": [

789 {722 {"$ref": "#/$defs/schema_definition"},

790 "$ref": "#/$defs/schema_definition"

791 },

792 {723 {

793 "type": "array",724 "type": "array",

794 "items": {725 "items": {"$ref": "#/$defs/schema_definition"},

795 "$ref": "#/$defs/schema_definition"

796 }

797 }

798 ]

799 },726 },

800 "required": {727 ]

801 "type": "array",

802 "items": {

803 "type": "string"

804 }

805 },728 },

806 "additionalProperties": {729 "required": {"type": "array", "items": {"type": "string"}},

807 "type": "boolean"730 "additionalProperties": {"type": "boolean"},

808 }

809 },731 },

810 "required": [732 "required": ["type"],

811 "type"

812 ],

813 "additionalProperties": False,733 "additionalProperties": False,

814 "if": {734 "if": {"properties": {"type": {"const": "object"}}},

815 "properties": {735 "then": {"required": ["properties"]},

816 "type": {

817 "const": "object"

818 }

819 }736 }

820 },737 },

821 "then": {738 },

822 "required": [

823 "properties"

824 ]

825 }

826 }

827 }

828 }

829}739}

830 740 

831META_PROMPT = """741META_PROMPT = """

Details

30```30```

31 31 

32```python32```python

33import os

34 

33from openai import OpenAI35from openai import OpenAI

34 36 

35client = OpenAI()37client = OpenAI()

38prompt_id = os.environ["OPENAI_PROMPT_ID"]

36 39 

37response = client.responses.create(40response = client.responses.create(

38 prompt={41 prompt={

39 "prompt_id": "pmpt_123",42 "prompt_id": prompt_id,

40 "version": "1",43 "version": "1",

41 "variables": {44 "variables": {

42 "customer_name": "Acme",45 "customer_name": "Acme",

Details

108 108 

109```python109```python

110from openai import OpenAI110from openai import OpenAI

111client = OpenAI()

112 

113from tenacity import (111from tenacity import (

114retry,112 retry,

115stop_after_attempt,113 stop_after_attempt,

116wait_random_exponential,114 wait_random_exponential,

117) # for exponential backoff115) # for exponential backoff

118 116 

117client = OpenAI()

118 

119 

119@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6))120@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6))

120def completion_with_backoff(**kwargs):121def completion_with_backoff(**kwargs):

121return client.completions.create(**kwargs)122 return client.completions.create(**kwargs)

123 

122 124 

123completion_with_backoff(model="gpt-3.5-turbo-instruct", prompt="Once upon a time,")125completion_with_backoff(

126 model="gpt-3.5-turbo-instruct",

127 prompt="Once upon a time,",

128)

124```129```

125 130 

126 131 


137import backoff142import backoff

138import openai143import openai

139from openai import OpenAI144from openai import OpenAI

145 

140client = OpenAI()146client = OpenAI()

141 147 

148 

142@backoff.on_exception(backoff.expo, openai.RateLimitError)149@backoff.on_exception(backoff.expo, openai.RateLimitError)

143def completions_with_backoff(**kwargs):150def completions_with_backoff(**kwargs):

144return client.completions.create(**kwargs)151 return client.completions.create(**kwargs)

152 

145 153 

146completions_with_backoff(model="gpt-3.5-turbo-instruct", prompt="Once upon a time,")154completions_with_backoff(

155 model="gpt-3.5-turbo-instruct",

156 prompt="Once upon a time,",

157)

147```158```

148 159 

149 160 


165 177 

166# define a retry decorator178# define a retry decorator

167 179 

180 

168def retry_with_exponential_backoff(181def retry_with_exponential_backoff(

169func,182 func,

170initial_delay: float = 1,183 initial_delay: float = 1,

171exponential_base: float = 2,184 exponential_base: float = 2,

172jitter: bool = True,185 jitter: bool = True,

173max_retries: int = 10,186 max_retries: int = 10,

174errors: tuple = (openai.RateLimitError,),187 errors: tuple = (openai.RateLimitError,),

175):188):

176"""Retry a function with exponential backoff."""189 """Retry a function with exponential backoff."""

177 190 

178 def wrapper(*args, **kwargs):191 def wrapper(*args, **kwargs):

179 # Initialize variables192 # Initialize variables


186 return func(*args, **kwargs)199 return func(*args, **kwargs)

187 200 

188 # Retry on specific errors201 # Retry on specific errors

189 except errors as e:202 except errors:

190 # Increment retries203 # Increment retries

191 num_retries += 1204 num_retries += 1

192 205 


203 time.sleep(delay)216 time.sleep(delay)

204 217 

205 # Raise exceptions for any errors not specified218 # Raise exceptions for any errors not specified

206 except Exception as e:219 except Exception:

207 raise e220 raise

208 221 

209 return wrapper222 return wrapper

210 223 

224 

211@retry_with_exponential_backoff225@retry_with_exponential_backoff

212def completions_with_backoff(**kwargs):226def completions_with_backoff(**kwargs):

213return client.completions.create(**kwargs)227 return client.completions.create(**kwargs)

214```228```

215 229 

216Again, OpenAI makes no guarantees on the security or efficiency of this solution but it can be a good starting place for your own solution.230Again, OpenAI makes no guarantees on the security or efficiency of this solution but it can be a good starting place for your own solution.

Details

81```python81```python

82event = {82event = {

83 "type": "session.update",83 "type": "session.update",

84 session: {84 "session": {

85 type: "realtime",85 "type": "realtime",

86 model: "gpt-realtime-2.1",86 "model": "gpt-realtime-2.1",

87 # Lock the output to audio (add "text" if you also want text)87 # Lock the output to audio (add "text" if you also want text).

88 output_modalities: ["audio"],88 "output_modalities": ["audio"],

89 audio: {89 "audio": {

90 input: {90 "input": {

91 format: {91 "format": {

92 type: "audio/pcm",92 "type": "audio/pcm",

93 rate: 24000,93 "rate": 24000,

94 },94 },

95 turn_detection: {95 "turn_detection": {"type": "semantic_vad"},

96 type: "semantic_vad"

97 }

98 },96 },

99 output: {97 "output": {

100 format: {98 "format": {

101 type: "audio/pcmu",99 "type": "audio/pcmu",

100 },

101 "voice": "marin",

102 },102 },

103 voice: "marin",

104 }

105 },103 },

106 # Use a server-stored prompt by ID. Optionally pin a version and pass variables.104 # Use a server-stored prompt by ID. Optionally pin a version and pass variables.

107 prompt: {105 "prompt": {

108 id: "pmpt_123", // your stored prompt ID106 "id": "pmpt_123", # Your stored prompt ID.

109 version: "89", // optional: pin a specific version107 "version": "89", # Optional: pin a specific version.

110 variables: {108 "variables": {

111 city: "Paris" // example variable used by your prompt109 "city": "Paris", # Example variable used by your prompt.

112 }110 },

111 },

112 # Direct session fields override prompt fields if they overlap.

113 "instructions": "Speak clearly and briefly. Confirm understanding before taking actions.",

113 },114 },

114 # You can still set direct session fields; these override prompt fields if they overlap:

115 instructions: "Speak clearly and briefly. Confirm understanding before taking actions."

116 }

117}115}

118ws.send(json.dumps(event))116ws.send(json.dumps(event))

119```117```


176 "type": "input_text",174 "type": "input_text",

177 "text": "What Prince album sold the most copies?",175 "text": "What Prince album sold the most copies?",

178 }176 }

179 ]177 ],

180 }178 },

181}179}

182ws.send(json.dumps(event))180ws.send(json.dumps(event))

183```181```


200```198```

201 199 

202```python200```python

203event = {201event = {"type": "response.create", "response": {"output_modalities": ["text"]}}

204 "type": "response.create",

205 "response": {

206 "output_modalities": [ "text" ]

207 }

208}

209ws.send(json.dumps(event))202ws.send(json.dumps(event))

210```203```

211 204 


233```python226```python

234def on_message(ws, message):227def on_message(ws, message):

235 server_event = json.loads(message)228 server_event = json.loads(message)

236 if server_event.type == "response.done":229 if server_event["type"] == "response.done":

237 print(server_event.response.output[0])230 print(server_event["response"]["output"][0])

238```231```

239 232 

240 233 


490 483 

491# ... create websocket-client named ws ...484# ... create websocket-client named ws ...

492 485 

486 

493def float_to_16bit_pcm(float32_array):487def float_to_16bit_pcm(float32_array):

494 clipped = [max(-1.0, min(1.0, x)) for x in float32_array]488 clipped = [max(-1.0, min(1.0, x)) for x in float32_array]

495 pcm16 = b''.join(struct.pack('<h', int(x * 32767)) for x in clipped)489 pcm16 = b"".join(struct.pack("<h", int(x * 32767)) for x in clipped)

496 return pcm16490 return pcm16

497 491 

492 

498def base64_encode_audio(float32_array):493def base64_encode_audio(float32_array):

499 pcm_bytes = float_to_16bit_pcm(float32_array)494 pcm_bytes = float_to_16bit_pcm(float32_array)

500 encoded = base64.b64encode(pcm_bytes).decode('ascii')495 encoded = base64.b64encode(pcm_bytes).decode("ascii")

501 return encoded496 return encoded

502 497 

503files = [498 

504 './path/to/sample1.wav',499files = ["./path/to/sample1.wav", "./path/to/sample2.wav", "./path/to/sample3.wav"]

505 './path/to/sample2.wav',

506 './path/to/sample3.wav'

507]

508 500 

509for filename in files:501for filename in files:

510 data, samplerate = sf.read(filename, dtype='float32')502 data, samplerate = sf.read(filename, dtype="float32")

511 channel_data = data[:, 0] if data.ndim > 1 else data503 channel_data = data[:, 0] if data.ndim > 1 else data

512 base64_chunk = base64_encode_audio(channel_data)504 base64_chunk = base64_encode_audio(channel_data)

513 505 

514 # Send the client event506 # Send the client event

515 event = {507 event = {"type": "input_audio_buffer.append", "audio": base64_chunk}

516 "type": "input_audio_buffer.append",

517 "audio": base64_chunk

518 }

519 ws.send(json.dumps(event))508 ws.send(json.dumps(event))

520```509```

521 510 


601 server_event = json.loads(message)590 server_event = json.loads(message)

602 if server_event["type"] == "response.output_audio.delta":591 if server_event["type"] == "response.output_audio.delta":

603 # Access Base64-encoded audio chunks:592 # Access Base64-encoded audio chunks:

604 # print(server_event["delta"])593 print(server_event["delta"])

605```594```

606 595 

607 596 


703 # Setting to "none" indicates the response is out of band,692 # Setting to "none" indicates the response is out of band,

704 # and will not be added to the default conversation693 # and will not be added to the default conversation

705 "conversation": "none",694 "conversation": "none",

706 

707 # Set metadata to help identify responses sent back from the model695 # Set metadata to help identify responses sent back from the model

708 "metadata": { "topic": "classification" },696 "metadata": {"topic": "classification"},

709 

710 # Set any other available response fields697 # Set any other available response fields

711 "output_modalities": [ "text" ],698 "output_modalities": ["text"],

712 "instructions": prompt,699 "instructions": prompt,

713 },700 },

714}701}


748 735 

749 # See if metadata is present736 # See if metadata is present

750 try:737 try:

751 topic = server_event.response.metadata.topic738 topic = server_event["response"]["metadata"]["topic"]

752 except AttributeError:739 except KeyError:

753 print("topic not set")740 print("topic not set")

754 741 

755 if server_event.type == "response.done" and topic == "classification":742 if server_event["type"] == "response.done" and topic == "classification":

756 # this server event pertained to our OOB model response743 # this server event pertained to our OOB model response

757 print(server_event.response.output[0])744 print(server_event["response"]["output"][0])

758```745```

759 746 

760 747 


803 "type": "response.create",790 "type": "response.create",

804 "response": {791 "response": {

805 "conversation": "none",792 "conversation": "none",

806 "metadata": { "topic": "pizza" },793 "metadata": {"topic": "pizza"},

807 "output_modalities": [ "text" ],794 "output_modalities": ["text"],

808 

809 # Create a custom input array for this request with whatever795 # Create a custom input array for this request with whatever

810 # context is appropriate796 # context is appropriate

811 "input": [797 "input": [

812 # potentially include existing conversation items:798 # potentially include existing conversation items:

813 {799 {"type": "item_reference", "id": "some_conversation_item_id"},

814 "type": "item_reference",

815 "id": "some_conversation_item_id"

816 },

817 

818 # include new content as well800 # include new content as well

819 {801 {

820 "type": "message",802 "type": "message",


825 "text": "Is it okay to put pineapple on pizza?",807 "text": "Is it okay to put pineapple on pizza?",

826 }808 }

827 ],809 ],

828 }810 },

829 ],811 ],

830 },812 },

831}813}

Details

223```223```

224 224 

225```python225```python

226import os

227 

228connector_authorization = os.environ["OPENAI_CONNECTOR_AUTHORIZATION"]

229 

226event = {230event = {

227 "type": "session.update",231 "type": "session.update",

228 "session": {232 "session": {


234 "type": "mcp",238 "type": "mcp",

235 "server_label": "google_calendar",239 "server_label": "google_calendar",

236 "connector_id": "connector_googlecalendar",240 "connector_id": "connector_googlecalendar",

237 "authorization": "<google-oauth-access-token>",241 "authorization": connector_authorization,

238 "allowed_tools": ["search_events", "read_event"],242 "allowed_tools": ["search_events", "read_event"],

239 "require_approval": "never",243 "require_approval": "never",

240 }244 }

Details

2 2 

3`gpt-realtime-2` is our state-of-the-art reasoning voice model for low-latency speech-to-speech applications. It can think before it speaks, follow instructions more reliably, use a larger context window, and call tools with greater precision than earlier realtime models.3`gpt-realtime-2` is our state-of-the-art reasoning voice model for low-latency speech-to-speech applications. It can think before it speaks, follow instructions more reliably, use a larger context window, and call tools with greater precision than earlier realtime models.

4 4 

5To take advantage of these gains, design prompts with more intent. Define the assistant's responsibilities, decision points, tool-calling behavior, and guardrails clearly: what it should do, when it should do it, and what it should avoid.5To take advantage of these gains, design prompts with more intent. Explicitly define the assistant's responsibilities, decision points, tool-calling behavior, and guardrails: what it should do, when it should do it, and what it should avoid.

6 6 

7Start simple. Do not over-prompt upfront. Begin with a minimal prompt, run7Start simple. Do not over-prompt upfront. Begin with a minimal prompt, run

8 evaluations, then add instructions only for behaviors that fail in testing.8 evaluations, then add instructions only for behaviors that fail in testing.


1396tools = [1396tools = [

1397 {1397 {

1398 "name": "lookup_account",1398 "name": "lookup_account",

1399 "description": "Retrieve a customer account using either an email or phone number to enable verification and account-specific actions.1399 "description": """Retrieve a customer account using either an email or phone number to enable verification and account-specific actions.

1400 1400 

1401Preamble sample phrases:1401Preamble sample phrases:

1402- For security, I’ll pull up your account using the email on file.1402- For security, I’ll pull up your account using the email on file.

1403- Let me look up your account by {email} now.1403- Let me look up your account by {email} now.

1404- I’m fetching the account linked to {phone} to verify access.1404- I’m fetching the account linked to {phone} to verify access.

1405- One moment—I’m opening your account details."1405- One moment—I’m opening your account details.""",

1406 "parameters": {1406 "parameters": {

1407 "..."1407 "type": "object",

1408 }1408 "properties": {

1409 "email": {"type": "string"},

1410 "phone": {"type": "string"},

1411 },

1412 "additionalProperties": False,

1413 },

1409 },1414 },

1410 {1415 {

1411 "name": "check_outage",1416 "name": "check_outage",

1412 "description": "Check for network outages affecting a given service address and return status and ETA if applicable.1417 "description": """Check for network outages affecting a given service address and return status and ETA if applicable.

1413 1418 

1414Preamble sample phrases:1419Preamble sample phrases:

1415- I’ll check for any outages at {service_address} right now.1420- I’ll check for any outages at {service_address} right now.

1416- Let me look up network status for your area.1421- Let me look up network status for your area.

1417- I’m checking whether there’s an active outage impacting your address.1422- I’m checking whether there’s an active outage impacting your address.

1418- One sec—verifying service status and any posted ETA.",1423- One sec—verifying service status and any posted ETA.""",

1419 "parameters": {1424 "parameters": {

1420 "..."1425 "type": "object",

1421 }1426 "properties": {

1422 }1427 "service_address": {"type": "string"},

1428 },

1429 "required": ["service_address"],

1430 "additionalProperties": False,

1431 },

1432 },

1423]1433]

1424 

1425```1434```

1426 1435 

1436 

1427### Tool Calls Without Confirmation1437### Tool Calls Without Confirmation

1428 1438 

1429Sometimes the model might ask for confirmation before a tool call. For some use cases, this can lead to poor experience for the end user since the model is not being proactive.1439Sometimes the model might ask for confirmation before a tool call. For some use cases, this can lead to poor experience for the end user since the model is not being proactive.


1547 1557 

1548Tool returns:1558Tool returns:

1549 1559 

1550```python1560```text

1551I just sent you an email with the verification link. Please open it and click “Confirm”.1561I just sent you an email with the verification link. Please open it and click “Confirm”.

1552```1562```

1553 1563 


1917# Allowed transitions1927# Allowed transitions

1918TRANSITIONS: Dict[State, List[State]] = {1928TRANSITIONS: Dict[State, List[State]] = {

1919 "verify": ["resolve"],1929 "verify": ["resolve"],

1920 "resolve": [] # terminal1930 "resolve": [], # terminal

1921}1931}

1922 1932 

1933 

1923def build_state_change_tool(current: State) -> dict:1934def build_state_change_tool(current: State) -> dict:

1924 allowed = TRANSITIONS[current]1935 allowed = TRANSITIONS[current]

1925 readable = ", ".join(allowed) if allowed else "no further states (terminal)"1936 readable = ", ".join(allowed) if allowed else "no further states (terminal)"


1933 ),1944 ),

1934 "parameters": {1945 "parameters": {

1935 "type": "object",1946 "type": "object",

1936 "properties": {1947 "properties": {"next_state": {"type": "string", "enum": allowed}},

1937 "next_state": {"type": "string", "enum": allowed}1948 "required": ["next_state"],

1938 },1949 },

1939 "required": ["next_state"]

1940 }

1941 }1950 }

1942 1951 

1952 

1943# Minimal business tools per state1953# Minimal business tools per state

1944TOOLS_BY_STATE: Dict[State, List[dict]] = {1954TOOLS_BY_STATE: Dict[State, List[dict]] = {

1945 "verify": [{1955 "verify": [

1956 {

1946 "type": "function",1957 "type": "function",

1947 "name": "lookup_account",1958 "name": "lookup_account",

1948 "description": "Fetch account by email or phone.",1959 "description": "Fetch account by email or phone.",

1949 "parameters": {1960 "parameters": {

1950 "type": "object",1961 "type": "object",

1951 "properties": {"email_or_phone": {"type": "string"}},1962 "properties": {"email_or_phone": {"type": "string"}},

1952 "required": ["email_or_phone"]1963 "required": ["email_or_phone"],

1964 },

1953 }1965 }

1954 }],1966 ],

1955 "resolve": [{1967 "resolve": [

1968 {

1956 "type": "function",1969 "type": "function",

1957 "name": "schedule_technician",1970 "name": "schedule_technician",

1958 "description": "Book a technician visit.",1971 "description": "Book a technician visit.",


1960 "type": "object",1973 "type": "object",

1961 "properties": {1974 "properties": {

1962 "account_id": {"type": "string"},1975 "account_id": {"type": "string"},

1963 "window": {"type": "string", "enum": ["10-12 ET", "14-16 ET"]}1976 "window": {"type": "string", "enum": ["10-12 ET", "14-16 ET"]},

1977 },

1978 "required": ["account_id", "window"],

1964 },1979 },

1965 "required": ["account_id", "window"]

1966 }1980 }

1967 }]1981 ],

1968}1982}

1969 1983 

1970# Short, phase-specific instructions1984# Short, phase-specific instructions


1976 "- Ask for the email or phone on the account.\n"1990 "- Ask for the email or phone on the account.\n"

1977 "- Read back digits one-by-one (e.g., '4-1-5… Is that correct?').\n"1991 "- Read back digits one-by-one (e.g., '4-1-5… Is that correct?').\n"

1978 "Exit when: Account ID is returned.\n"1992 "Exit when: Account ID is returned.\n"

1979 "When exit is satisfied: call set_conversation_state(next_state=\"resolve\")."1993 'When exit is satisfied: call set_conversation_state(next_state="resolve").'

1980 ),1994 ),

1981 "resolve": (1995 "resolve": (

1982 "# Role & Objective\n"1996 "# Role & Objective\n"


1986 "- Book the chosen window.\n"2000 "- Book the chosen window.\n"

1987 "Exit when: Appointment is confirmed.\n"2001 "Exit when: Appointment is confirmed.\n"

1988 "When exit is satisfied: end the call politely."2002 "When exit is satisfied: end the call politely."

1989 )2003 ),

1990}2004}

1991 2005 

2006 

1992def build_session_update(state: State) -> dict:2007def build_session_update(state: State) -> dict:

1993 """Return the JSON payload for a Realtime `session.update` event."""2008 """Return the JSON payload for a Realtime `session.update` event."""

1994 return {2009 return {

1995 "type": "session.update",2010 "type": "session.update",

1996 "session": {2011 "session": {

1997 "instructions": INSTRUCTIONS_BY_STATE[state],2012 "instructions": INSTRUCTIONS_BY_STATE[state],

1998 "tools": TOOLS_BY_STATE[state] + [build_state_change_tool(state)]2013 "tools": TOOLS_BY_STATE[state] + [build_state_change_tool(state)],

1999 }2014 },

2000 }2015 }

2001```2016```

2002 2017 

Details

211import websockets211import websockets

212 212 

213app = Flask(__name__)213app = Flask(__name__)

214client = OpenAI(214client = OpenAI(webhook_secret=os.environ["OPENAI_WEBHOOK_SECRET"])

215 webhook_secret=os.environ["OPENAI_WEBHOOK_SECRET"]

216)

217 215 

218AUTH_HEADER = {216AUTH_HEADER = {"Authorization": "Bearer " + os.environ["OPENAI_API_KEY"]}

219 "Authorization": "Bearer " + os.getenv("OPENAI_API_KEY")

220}

221 217 

222call_accept = {218call_accept = {

223 "type": "realtime",219 "type": "realtime",


228response_create = {224response_create = {

229 "type": "response.create",225 "type": "response.create",

230 "response": {226 "response": {

231 "instructions": (227 "instructions": ("Say to the user 'Thank you for calling, how can I help you'")

232 "Say to the user 'Thank you for calling, how can I help you'"

233 )

234 },228 },

235}229}

236 230 


264 json=call_accept,258 json=call_accept,

265 )259 )

266 threading.Thread(260 threading.Thread(

267 target=lambda: asyncio.run(261 target=lambda: asyncio.run(websocket_task(event.data.call_id)),

268 websocket_task(event.data.call_id)

269 ),

270 daemon=True,262 daemon=True,

271 ).start()263 ).start()

272 return Response(status=200)264 return Response(status=200)

Details

51import json51import json

52import websocket52import websocket

53 53 

54OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")54OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]

55 55 

56url = "wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1"56url = "wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1"

57headers = [57headers = [

Details

52response = client.responses.create(52response = client.responses.create(

53 model="gpt-5.6",53 model="gpt-5.6",

54 reasoning={"effort": "low"},54 reasoning={"effort": "low"},

55 input=[55 input=[{"role": "user", "content": prompt}],

56 {

57 "role": "user",

58 "content": prompt

59 }

60 ]

61)56)

62 57 

63print(response.output_text)58print(response.output_text)


224response = client.responses.create(219response = client.responses.create(

225 model="gpt-5.6",220 model="gpt-5.6",

226 reasoning={"effort": "medium"},221 reasoning={"effort": "medium"},

227 input=[222 input=[{"role": "user", "content": prompt}],

228 {

229 "role": "user",

230 "content": prompt

231 }

232 ],

233 max_output_tokens=300,223 max_output_tokens=300,

234)224)

235 225 

236if response.status == "incomplete" and response.incomplete_details.reason == "max_output_tokens":226if (

227 response.status == "incomplete"

228 and response.incomplete_details.reason == "max_output_tokens"

229):

237 print("Ran out of tokens")230 print("Ran out of tokens")

238 if response.output_text:231 if response.output_text:

239 print("Partial output:", response.output_text)232 print("Partial output:", response.output_text)


303from openai import OpenAI296from openai import OpenAI

304 297 

305client = OpenAI()298client = OpenAI()

299model = "gpt-5.6"

306 300 

307first = client.responses.create(301first = client.responses.create(

308 model="YOUR_MODEL_ID",302 model=model,

309 input="Inspect this repository and identify the likely bug.",303 input="Inspect this repository and identify the likely bug.",

310 reasoning={"context": "current_turn"},304 reasoning={"context": "current_turn"},

311)305)

312 306 

313second = client.responses.create(307second = client.responses.create(

314 model="YOUR_MODEL_ID",308 model=model,

315 previous_response_id=first.id,309 previous_response_id=first.id,

316 input="Now patch the bug and explain the change.",310 input="Now patch the bug and explain the change.",

317 reasoning={"context": "all_turns"},311 reasoning={"context": "all_turns"},


390from openai import OpenAI384from openai import OpenAI

391 385 

392client = OpenAI()386client = OpenAI()

387model = "gpt-5.6"

393 388 

394history = [389history = [

395 {390 {


399]394]

400 395 

401first = client.responses.create(396first = client.responses.create(

402 model="YOUR_MODEL_ID",397 model=model,

403 store=False,398 store=False,

404 input=history,399 input=history,

405 reasoning={"context": "current_turn"},400 reasoning={"context": "current_turn"},


415)410)

416 411 

417second = client.responses.create(412second = client.responses.create(

418 model="YOUR_MODEL_ID",413 model=model,

419 store=False,414 store=False,

420 input=history,415 input=history,

421 reasoning={"context": "all_turns"},416 reasoning={"context": "all_turns"},


460response = client.responses.create(456response = client.responses.create(

461 model="gpt-5.6",457 model="gpt-5.6",

462 input="What is the capital of France?",458 input="What is the capital of France?",

463 reasoning={459 reasoning={"effort": "low", "summary": "auto"},

464 "effort": "low",

465 "summary": "auto"

466 }

467)460)

468 461 

469print(response.output)462print(response.output)

Details

358 360 

359response_format = dict(361response_format = dict(

360 type="json_schema",362 type="json_schema",

361 json_schema=dict(363 json_schema=dict(name=MyCustomClass.__name__, strict=True, schema=schema),

362 name=MyCustomClass.__name__,

363 strict=True,

364 schema=schema

365 )

366)364)

367```365```

368 366 

Details

521 521 

522def run_multi_agent(connection):522def run_multi_agent(connection):

523 previous_response_id: str | None = None523 previous_response_id: str | None = None

524 pending_input: list[dict[str, object]] = [524 pending_input: list[dict[str, object]] = [{"role": "user", "content": input()}]

525 {"role": "user", "content": input()}

526 ]

527 525 

528 while pending_input:526 while pending_input:

529 request = {527 request = {


583 if event.error.code != "response_already_completed":581 if event.error.code != "response_already_completed":

584 raise RuntimeError(event.error)582 raise RuntimeError(event.error)

585 583 

586 next_input.extend(584 next_input.extend(item.model_dump(mode="json") for item in event.input)

587 item.model_dump(mode="json") for item in event.input

588 )

589 585 

590 elif event_type == "response.completed":586 elif event_type == "response.completed":

591 completed_response = event.response587 completed_response = event.response

Details

1# Retrieval1# Retrieval

2 2 

3The **Retrieval API** allows you to perform [**semantic search**](#semantic-search) over your data, which is a technique that surfaces semantically similar results even when they match few or no keywords. Retrieval is useful on its own, but is especially powerful when combined with our models to synthesize responses.3The **Retrieval API** allows you to perform [**semantic search**](#semantic-search) over your data, which is a technique that surfaces semantically similar results—even when they match few or no keywords. Retrieval is useful on its own, but is especially powerful when combined with our models to synthesize responses.

4 4 

5![Retrieval depiction](https://cdn.openai.com/API/docs/images/retrieval-depiction.png)5![Retrieval depiction](https://cdn.openai.com/API/docs/images/retrieval-depiction.png)

6 6 


83| The first man on the moon was Neil Armstrong. | 27% | 43% |84| The first man on the moon was Neil Armstrong. | 27% | 43% |

84| When I ate the moon cake, it was delicious. | 40% | 28% |85| When I ate the moon cake, it was delicious. | 40% | 28% |

85 86 

86_([Jaccard](https://en.wikipedia.org/wiki/Jaccard_index) used for keyword, [cosine](https://en.wikipedia.org/wiki/Cosine_similarity) with `text-embedding-3-small` used for semantic.)_87_(Keyword similarity uses [intersection over union](https://en.wikipedia.org/wiki/Jaccard_index); semantic similarity uses [cosine similarity](https://en.wikipedia.org/wiki/Cosine_similarity) with `text-embedding-3-small`.)_

87 88 

88Notice how the most relevant result contains none of the words in the search query. This flexibility makes semantic search a very powerful technique for querying knowledge bases of any size.89Notice how the most relevant result contains none of the words in the search query. This flexibility makes semantic search a powerful technique for querying knowledge bases of any size.

89 90 

90Semantic search is powered by [vector stores](#vector-stores), which we cover in detail later in the guide. This section will focus on the mechanics of semantic search.91Semantic search is powered by [vector stores](#vector-stores), which we cover in detail later in the guide. This section will focus on the mechanics of semantic search.

91 92 


158```159```

159 160 

160 161 

161A response will contain 10 results maximum by default, but you can set up to 50 using the `max_num_results` param.162A response will contain 10 results maximum by default, but you can set up to 50 using the `max_num_results` parameter.

162 163 

163### Query rewriting164### Query rewriting

164 165 


432 433 

433### Vector store file operations434### Vector store file operations

434 435 

435Some operations, like `create` for `vector_store.file`, are asynchronous and may take time to complete use our helper functions, like `create_and_poll` to block until it is. Otherwise, you may check the status. Removing files from a vector store is eventually consistent, and search results may still include content from a removed file for a short period.436Some operations, like `create` for `vector_store.file`, are asynchronous and may take time to complete—use our helper functions, like `create_and_poll` to block until it is. Otherwise, you may check the status. Removing files from a vector store is eventually consistent, and search results may still include content from a removed file for a short period.

436 437 

437Adding files is rate limited per vector store ID. Requests to [`/vector_stores/{vector_store_id}/files`](https://developers.openai.com/api/docs/api-reference/vector-stores/createFile) and [`/vector_stores/{vector_store_id}/file_batches`](https://developers.openai.com/api/docs/api-reference/vector-stores/createBatch) share a per-vector-store limit of 300 requests per minute.438Adding files is rate limited per vector store ID. Requests to [`/vector_stores/{vector_store_id}/files`](https://developers.openai.com/api/docs/api-reference/vector-stores/createFile) and [`/vector_stores/{vector_store_id}/file_batches`](https://developers.openai.com/api/docs/api-reference/vector-stores/createBatch) share a per-vector-store limit of 300 requests per minute.

438 439 


644 </div>645 </div>

645 <div data-content-switcher-pane data-value="list" hidden>646 <div data-content-switcher-pane data-value="list" hidden>

646 <div class="hidden">List</div>647 <div class="hidden">List</div>

647 Batch list operation648 List files in a batch

648 649 

649```python650```python

650client.vector_stores.file_batches.list(651client.vector_stores.file_batches.list_files(

652 "vsfb_123",

651 vector_store_id="vs_123"653 vector_store_id="vs_123"

652)654)

653```655```

654 656 

655```javascript657```javascript

656await client.vector_stores.file_batches.list({658await client.vectorStores.fileBatches.listFiles("vsfb_123", {

657 vector_store_id: "vs_123"659 vector_store_id: "vs_123"

658});660});

659```661```


731 733 

732By default, `max_chunk_size_tokens` is set to `800` and `chunk_overlap_tokens` is set to `400`, meaning every file is indexed by being split up into 800-token chunks, with 400-token overlap between consecutive chunks.734By default, `max_chunk_size_tokens` is set to `800` and `chunk_overlap_tokens` is set to `400`, meaning every file is indexed by being split up into 800-token chunks, with 400-token overlap between consecutive chunks.

733 735 

734You can adjust this by setting [`chunking_strategy`](https://developers.openai.com/api/docs/api-reference/vector-stores-files/createFile#vector-stores-files-createfile-chunking_strategy) when adding files to the vector store. There are certain limitations to `chunking_strategy`:736You can adjust this by setting [`chunking_strategy`](https://developers.openai.com/api/docs/api-reference/vector-stores-files/createFile#vector-stores-files-createfile-chunking_strategy) when adding files to the vector store. The strategy has certain limitations:

735 737 

736- `max_chunk_size_tokens` must be between 100 and 4096 inclusive.738- `max_chunk_size_tokens` must be between 100 and 4096 inclusive.

737- `chunk_overlap_tokens` must be non-negative and should not exceed `max_chunk_size_tokens / 2`.739- `chunk_overlap_tokens` must be non-negative and should not exceed `max_chunk_size_tokens / 2`.


804```python807```python

805formatted_results = format_results(results.data)808formatted_results = format_results(results.data)

806 809 

807'\n'.join('\n'.join(c.text) for c in result.content for result in results.data)810"\n".join("\n".join(c.text for c in result.content) for result in results.data)

808 811 

809completion = client.chat.completions.create(812completion = client.chat.completions.create(

810 model="gpt-5.6",813 model="gpt-5.6",

811 messages=[814 messages=[

812 {815 {

813 "role": "developer",816 "role": "developer",

814 "content": "Produce a concise answer to the query based on the provided sources."817 "content": "Produce a concise answer to the query based on the provided sources.",

815 },818 },

816 {819 {

817 "role": "user",820 "role": "user",

818 "content": f"Sources: {formatted_results}\n\nQuery: '{user_query}'"821 "content": f"Sources: {formatted_results}\n\nQuery: '{user_query}'",

819 }822 },

820 ],823 ],

821)824)

822 825 


857 860 

858```python861```python

859def format_results(results):862def format_results(results):

860 formatted_results = ''863 formatted_results = ""

861 for result in results.data:864 for result in results.data:

862 formatted_result = f"<result file_id='{result.file_id}' file_name='{result.file_name}'>"865 formatted_result = (

866 f"<result file_id='{result.file_id}' file_name='{result.file_name}'>"

867 )

863 for part in result.content:868 for part in result.content:

864 formatted_result += f"<content>{part.text}</content>"869 formatted_result += f"<content>{part.text}</content>"

865 formatted_results += formatted_result + "</result>"870 formatted_results += formatted_result + "</result>"

Details

65client = OpenAI()66client = OpenAI()

66 67 

67response = client.chat.completions.create(68response = client.chat.completions.create(

68model="gpt-5.6",69 model="gpt-5.6",

69messages=[70 messages=[{"role": "user", "content": "This is a test"}],

70{"role": "user", "content": "This is a test"}71 max_completion_tokens=5,

71],72 safety_identifier="user_123456",

72max_completion_tokens=5,

73safety_identifier="user_123456"

74)73)

75```74```

76 75 

Details

36client = OpenAI()37client = OpenAI()

37 38 

38response = client.responses.create(39response = client.responses.create(

39model="gpt-5.6-terra",40 model="gpt-5.6-terra",

40input="This is a test",41 input="This is a test",

41safety_identifier="user_123456",42 safety_identifier="user_123456",

42)43)

43```44```

44 45 


63client = OpenAI()65client = OpenAI()

64 66 

65response = client.chat.completions.create(67response = client.chat.completions.create(

66model="gpt-5.6-terra",68 model="gpt-5.6-terra",

67messages=[69 messages=[{"role": "user", "content": "This is a test"}],

68{"role": "user", "content": "This is a test"}70 safety_identifier="user_123456",

69],

70safety_identifier="user_123456"

71)71)

72```72```

73 73 

Details

16- Transcribe audio into whatever language the audio is in.16- Transcribe audio into whatever language the audio is in.

17- Translate and transcribe the audio into English.17- Translate and transcribe the audio into English.

18 18 

19File uploads are currently limited to 25 MB, and the following input file types are supported: `mp3`, `mp4`, `mpeg`, `mpga`, `m4a`, `wav`, and `webm`. Known speaker reference clips for diarization accept the same formats when provided as data URLs.19File uploads are currently limited to 25 MB, and the following input file types are supported: `mp3`, `mp4`, `mpeg`, `mpga`, `m4a`, `wav`, and `webm`. Known speaker reference clips for speaker identification accept the same formats when provided as data URLs.

20 20 

21Use this guide for file uploads and bounded audio requests. If your21Use this guide for file uploads and bounded audio requests. If your

22 application needs live transcript deltas from a microphone, call, or media22 application needs live transcript deltas from a microphone, call, or media


53from openai import OpenAI53from openai import OpenAI

54 54 

55client = OpenAI()55client = OpenAI()

56audio_file= open("/path/to/file/audio.mp3", "rb")56audio_file = open("audio.wav", "rb")

57 57 

58transcription = client.audio.transcriptions.create(58transcription = client.audio.transcriptions.create(

59 model="gpt-4o-transcribe", 59 model="gpt-4o-transcribe", file=audio_file

60 file=audio_file

61)60)

62 61 

63print(transcription.text)62print(transcription.text)


81```80```

82 81 

83 82 

84By default, the response type will be json with the raw text included.83By default, the response type will be JSON with the raw text included.

85 84 

86```example-content85```example-content

87{86{


113from openai import OpenAI112from openai import OpenAI

114 113 

115client = OpenAI()114client = OpenAI()

116audio_file = open("/path/to/file/speech.mp3", "rb")115audio_file = open("speech.wav", "rb")

117 116 

118transcription = client.audio.transcriptions.create(117transcription = client.audio.transcriptions.create(

119 model="gpt-4o-transcribe", 118 model="gpt-4o-transcribe", file=audio_file, response_format="text"

120 file=audio_file,

121 response_format="text"

122)119)

123 120 

124print(transcription.text)121print(transcription.text)


143 30 seconds (`"auto"` is recommended) and does not support prompts, logprobs,140 30 seconds (`"auto"` is recommended) and does not support prompts, logprobs,

144 or `timestamp_granularities[]`.141 or `timestamp_granularities[]`.

145 142 

146### Speaker diarization143### Identify speakers

147 144 

148`gpt-4o-transcribe-diarize` produces speaker-aware transcripts. Request the `diarized_json` response format to receive an array of segments with `speaker`, `start`, and `end` metadata. Set `chunking_strategy` (either `"auto"` or a Voice Activity Detection configuration) so that the service can split the audio into segments; this is required when the input is longer than 30 seconds.145`gpt-4o-transcribe-diarize` produces speaker-aware transcripts. Request the `diarized_json` response format to receive an array of segments with `speaker`, `start`, and `end` metadata. Set `chunking_strategy` (either `"auto"` or a Voice Activity Detection configuration) so that the service can split the audio into segments; this is required when the input is longer than 30 seconds.

149 146 


221```220```

222 221 

223 222 

224When `stream=true`, diarized responses emit `transcript.text.segment` events whenever a segment completes. `transcript.text.delta` events include a `segment_id` field, but diarized deltas do not stream partial speaker assignments until each segment is finalized.223When `stream=true`, responses from the speaker-aware model emit `transcript.text.segment` events whenever a segment completes. `transcript.text.delta` events include a `segment_id` field, but these deltas do not stream partial speaker assignments until each segment is finalized.

225 224 

226`gpt-4o-transcribe-diarize` is currently available via225`gpt-4o-transcribe-diarize` is currently available via

227 `/v1/audio/transcriptions` only and is not yet supported in the Realtime API.226 `/v1/audio/transcriptions` only and is not yet supported in the Realtime API.


250from openai import OpenAI249from openai import OpenAI

251 250 

252client = OpenAI()251client = OpenAI()

253audio_file = open("/path/to/file/german.mp3", "rb")252audio_file = open("german.wav", "rb")

254 253 

255translation = client.audio.translations.create(254translation = client.audio.translations.create(

256 model="whisper-1",255 model="whisper-1",


286 285 

287While the underlying model was trained on 98 languages, we only list the languages that exceeded \<50% [word error rate](https://en.wikipedia.org/wiki/Word_error_rate) (WER) which is an industry standard benchmark for speech to text model accuracy. The model will return results for languages not listed above but the quality will be low.286While the underlying model was trained on 98 languages, we only list the languages that exceeded \<50% [word error rate](https://en.wikipedia.org/wiki/Word_error_rate) (WER) which is an industry standard benchmark for speech to text model accuracy. The model will return results for languages not listed above but the quality will be low.

288 287 

289We support some ISO 639-1 and 639-3 language codes for GPT-4o based models. For language codes we don’t have, try prompting for specific languages (i.e., “Output in English”).288We support some ISO 639-1 and 639-3 language codes for GPT-4o based models. For language codes we don’t have, try prompting for specific languages (that is, “Output in English”).

290 289 

291## Timestamps290## Timestamps

292 291 

293By default, the Transcriptions API will output a transcript of the provided audio in text. The [`timestamp_granularities[]` parameter](/api/docs/api-reference/audio/createTranscription#audio-createtranscription-timestamp_granularities) enables a more structured and timestamped json output format, with timestamps at the segment, word level, or both. This enables word-level precision for transcripts and video edits, which allows for the removal of specific frames tied to individual words.292By default, the Transcriptions API will output a transcript of the provided audio in text. The [`timestamp_granularities[]` parameter](/api/docs/api-reference/audio/createTranscription#audio-createtranscription-timestamp_granularities) enables a more structured and timestamped JSON output format, with timestamps at the segment, word level, or both. This enables word-level precision for transcripts and video edits, which allows for the removal of specific frames tied to individual words.

294 293 

295Timestamp options294Timestamp options

296 295 


314from openai import OpenAI313from openai import OpenAI

315 314 

316client = OpenAI()315client = OpenAI()

317audio_file = open("/path/to/file/speech.mp3", "rb")316audio_file = open("speech.wav", "rb")

318 317 

319transcription = client.audio.transcriptions.create(318transcription = client.audio.transcriptions.create(

320 file=audio_file,319 file=audio_file,

321 model="whisper-1",320 model="whisper-1",

322 response_format="verbose_json",321 response_format="verbose_json",

323 timestamp_granularities=["word"]322 timestamp_granularities=["word"],

324)323)

325 324 

326print(transcription.words)325print(transcription.words)


348```python347```python

349from pydub import AudioSegment348from pydub import AudioSegment

350 349 

351song = AudioSegment.from_mp3("good_morning.mp3")350song = AudioSegment.from_wav("good_morning.wav")

352 351 

353# PyDub handles time in milliseconds352# PyDub handles time in milliseconds

354ten_minutes = 10 * 60 * 1000353ten_minutes = 10 * 60 * 1000

355 354 

356first_10_minutes = song[:ten_minutes]355first_10_minutes = song[:ten_minutes]

357 356 

358first_10_minutes.export("good_morning_10.mp3", format="mp3")357first_10_minutes.export("good_morning_10.wav", format="wav")

359```358```

360 359 

360 

361_OpenAI makes no guarantees about the usability or security of 3rd party software like PyDub._361_OpenAI makes no guarantees about the usability or security of 3rd party software like PyDub._

362 362 

363## Prompting363## Prompting


387from openai import OpenAI387from openai import OpenAI

388 388 

389client = OpenAI()389client = OpenAI()

390audio_file = open("/path/to/file/speech.mp3", "rb")390audio_file = open("speech.wav", "rb")

391 391 

392transcription = client.audio.transcriptions.create(392transcription = client.audio.transcriptions.create(

393 model="gpt-4o-transcribe",393 model="gpt-4o-transcribe",

394 file=audio_file,394 file=audio_file,

395 response_format="text",395 response_format="text",

396 prompt="The following conversation is a lecture about the recent developments around OpenAI, GPT-4.5 and the future of AI."396 prompt="The following conversation is a lecture about the recent developments around OpenAI, GPT-4.5 and the future of AI.",

397)397)

398 398 

399print(transcription.text)399print(transcription.text)


414 414 

415Here are some examples of how prompting can help in different scenarios:415Here are some examples of how prompting can help in different scenarios:

416 416 

4171. Prompts can help correct specific words or acronyms that the model misrecognizes in the audio. For example, the following prompt improves the transcription of the words DALL·E and GPT-3, which were previously written as "GDP 3" and "DALI": "The transcript is about OpenAI which makes technology like DALL·E, GPT-3, and ChatGPT with the hope of one day building an AGI system that benefits all of humanity."4171. Prompts can help correct specific words or acronyms that the model transcribes incorrectly. For example, the following prompt improves the transcription of the words DALL·E and GPT-3, which were previously written as "GDP 3" and "DALI": "The transcript is about OpenAI which makes technology like DALL·E, GPT-3, and ChatGPT with the hope of one day building an AGI system that benefits all of humanity."

4182. To preserve the context of a file that was split into segments, prompt the model with the transcript of the preceding segment. The model uses relevant information from the previous audio, improving transcription accuracy. The `whisper-1` model only considers the final 224 tokens of the prompt and ignores anything earlier. For multilingual inputs, Whisper uses a custom tokenizer. For English-only inputs, it uses the standard GPT-2 tokenizer. Find both tokenizers in the open source [Whisper Python package](https://github.com/openai/whisper/blob/main/whisper/tokenizer.py#L361).4182. To preserve the context of a file that was split into segments, prompt the model with the transcript of the preceding segment. The model uses relevant information from the previous audio, improving transcription accuracy. The `whisper-1` model only considers the final 224 tokens of the prompt and ignores anything earlier. For multilingual inputs, Whisper uses a custom tokenizer. For English-only inputs, it uses the standard GPT-2 tokenizer. Find both implementations in the open source [Whisper Python package](https://github.com/openai/whisper/blob/main/whisper/tokenizer.py#L361).

4193. Sometimes the model skips punctuation in the transcript. To prevent this, use a simple prompt that includes punctuation: "Hello, welcome to my lecture."4193. Sometimes the model skips punctuation in the transcript. To prevent this, use a short prompt that includes punctuation: "Hello, welcome to my lecture."

4204. The model may also leave out common filler words in the audio. If you want to keep the filler words in your transcript, use a prompt that contains them: "Umm, let me think like, hmm... Okay, here's what I'm, like, thinking."4204. The model may also leave out common filler words in the audio. If you want to keep the filler words in your transcript, use a prompt that contains them: "Um, let me think like, hmm Okay, here's what I'm, like, thinking."

4215. Some languages can be written in different ways, such as simplified or traditional Chinese. The model might not always use the writing style that you want for your transcript by default. You can improve this by using a prompt in your preferred writing style.4215. Some languages can be written in different ways, such as simplified or traditional Chinese. The model might not always use the writing style that you want for your transcript by default. You can improve this by using a prompt in your preferred writing style.

422 422 

423For `whisper-1`, the model tries to match the style of the prompt, so it's more likely to use capitalization and punctuation if the prompt does too. However, the current prompting system is more limited than our other language models and provides limited control over the generated text.423For `whisper-1`, the model tries to match the style of the prompt, so it's more likely to use capitalization and punctuation if the prompt does too. However, the current prompting system is more limited than our other language models and provides limited control over the generated text.


430 430 

431 431 

432 432 

433There are two ways you can stream your transcription depending on your use case and whether you are trying to transcribe an already completed audio recording or handle an ongoing stream of audio and use OpenAI for turn detection.433You can stream a transcription in two ways, depending on whether you want to transcribe a completed audio recording or handle an ongoing audio stream with OpenAI turn detection.

434 434 

435### Streaming the transcription of a completed audio recording435### Streaming the transcription of a completed audio recording

436 436 


464from openai import OpenAI464from openai import OpenAI

465 465 

466client = OpenAI()466client = OpenAI()

467audio_file = open("/path/to/file/speech.mp3", "rb")467audio_file = open("speech.wav", "rb")

468 468 

469stream = client.audio.transcriptions.create(469stream = client.audio.transcriptions.create(

470 model="gpt-4o-mini-transcribe",470 model="gpt-4o-mini-transcribe",

471 file=audio_file,471 file=audio_file,

472 response_format="text",472 response_format="text",

473 # highlight-start473 # highlight-start

474 stream=True474 stream=True,

475 # highlight-end475 # highlight-end

476)476)

477 477 


536from openai import OpenAI536from openai import OpenAI

537 537 

538client = OpenAI()538client = OpenAI()

539audio_file = open("/path/to/file/speech.mp3", "rb")539audio_file = open("speech.wav", "rb")

540 540 

541transcription = client.audio.transcriptions.create(541transcription = client.audio.transcriptions.create(

542 model="whisper-1",542 model="whisper-1",

543 file=audio_file,543 file=audio_file,

544 response_format="text",544 response_format="text",

545 prompt="ZyntriQix, Digique Plus, CynapseFive, VortiQore V8, EchoNix Array, OrbitalLink Seven, DigiFractal Matrix, PULSE, RAPT, B.R.I.C.K., Q.U.A.R.T.Z., F.L.I.N.T."545 prompt="ZyntriQix, Digique Plus, CynapseFive, VortiQore V8, EchoNix Array, OrbitalLink Seven, DigiFractal Matrix, PULSE, RAPT, B.R.I.C.K., Q.U.A.R.T.Z., F.L.I.N.T.",

546)546)

547 547 

548print(transcription.text)548print(transcription.text)


616 model="gpt-4.1",617 model="gpt-4.1",

617 temperature=temperature,618 temperature=temperature,

618 messages=[619 messages=[

619 {620 {"role": "system", "content": system_prompt},

620 "role": "system",621 {"role": "user", "content": transcribe(audio_file, "")},

621 "content": system_prompt622 ],

622 },

623 {

624 "role": "user",

625 "content": transcribe(audio_file, "")

626 }

627 ]

628 )623 )

629 return completion.choices[0].message.content624 return response.choices[0].message.content

630corrected_text = generate_corrected_transcript(625 

631 0, system_prompt, fake_company_filepath626 

632)627corrected_text = generate_corrected_transcript(0, system_prompt, fake_company_filepath)

633```628```

634 629 

635 630 

Details

95For a full list of event types, see the [API reference for streaming](https://developers.openai.com/api/docs/api-reference/responses-streaming). Here are a few examples:96For a full list of event types, see the [API reference for streaming](https://developers.openai.com/api/docs/api-reference/responses-streaming). Here are a few examples:

96 97 

97```python98```python

98type StreamingEvent =99StreamingEvent = (

99 | ResponseCreatedEvent100 ResponseCreatedEvent

100 | ResponseInProgressEvent101 | ResponseInProgressEvent

101 | ResponseFailedEvent102 | ResponseFailedEvent

102 | ResponseCompletedEvent103 | ResponseCompletedEvent


120 | ResponseCodeInterpreterCallInterpreting121 | ResponseCodeInterpreterCallInterpreting

121 | ResponseCodeInterpreterCallCompleted122 | ResponseCodeInterpreterCallCompleted

122 | Error123 | Error

124)

123```125```

124 126 

125 127 

Details

130response = client.responses.parse(132response = client.responses.parse(

131 model="gpt-5.6",133 model="gpt-5.6",

132 input=[134 input=[

133 {"role": "system", "content": "You are a helpful math tutor. Guide the user through the solution step by step."},135 {

136 "role": "system",

137 "content": "You are a helpful math tutor. Guide the user through the solution step by step.",

138 },

134 {"role": "user", "content": "how can I solve 8x + 7 = -23"},139 {"role": "user", "content": "how can I solve 8x + 7 = -23"},

135 ],140 ],

136 text_format=MathReasoning,141 text_format=MathReasoning,

guides/text.md +4 −10

Details

24 25 

25response = client.responses.create(26response = client.responses.create(

26 model="gpt-5.6",27 model="gpt-5.6",

27 input="Write a one-sentence bedtime story about a unicorn."28 input="Write a one-sentence bedtime story about a unicorn.",

28)29)

29 30 

30print(response.output_text)31print(response.output_text)


265 model="gpt-5.6",268 model="gpt-5.6",

266 reasoning={"effort": "low"},269 reasoning={"effort": "low"},

267 input=[270 input=[

268 {271 {"role": "developer", "content": "Talk like a pirate."},

269 "role": "developer",272 {"role": "user", "content": "Are semicolons optional in JavaScript?"},

270 "content": "Talk like a pirate."273 ],

271 },

272 {

273 "role": "user",

274 "content": "Are semicolons optional in JavaScript?"

275 }

276 ]

277)274)

278 275 

279print(response.output_text)276print(response.output_text)

Details

31client = OpenAI()31client = OpenAI()

32 32 

33response = client.responses.input_tokens.count(33response = client.responses.input_tokens.count(

34 model="gpt-5.6",34 model="gpt-5.6", input="Tell me a joke."

35 input="Tell me a joke."

36)35)

37print(response.input_tokens)36print(response.input_tokens)

38```37```


207 {206 {

208 "role": "user",207 "role": "user",

209 "content": [208 "content": [

210 {"type": "input_image", "image_url": "https://example.com/chart.png"},209 {

210 "type": "input_image",

211 "image_url": "https://example.com/chart.png",

212 },

211 {"type": "input_text", "text": "Summarize this chart."},213 {"type": "input_text", "text": "Summarize this chart."},

212 ],214 ],

213 }215 }

guides/tools.md +2 −5

Details

28response = client.responses.create(29response = client.responses.create(

29 model="gpt-5.6",30 model="gpt-5.6",

30 tools=[{"type": "web_search"}],31 tools=[{"type": "web_search"}],

31 input="What was a positive news story from today?"32 input="What was a positive news story from today?",

32)33)

33 34 

34print(response.output_text)35print(response.output_text)


100response = client.responses.create(102response = client.responses.create(

101 model="gpt-5.6",103 model="gpt-5.6",

102 input="What is deep research by OpenAI?",104 input="What is deep research by OpenAI?",

103 tools=[{105 tools=[{"type": "file_search", "vector_store_ids": ["<vector_store_id>"]}],

104 "type": "file_search",

105 "vector_store_ids": ["<vector_store_id>"]

106 }]

107)106)

108print(response)107print(response)

109```108```

Details

83# - update lib/fib.py83# - update lib/fib.py

84# - update run.py84# - update run.py

85patch_calls = [85patch_calls = [

86 item for item in response.output86 item.model_dump() for item in response.output if item.type == "apply_patch_call"

87 if item["type"] == "apply_patch_call"

88]87]

89```88```

90 89 


127 op = call["operation"]126 op = call["operation"]

128 success, maybe_log_output = apply_operation(op)127 success, maybe_log_output = apply_operation(op)

129 128 

130 results.append({129 results.append(

130 {

131 "type": "apply_patch_call_output",131 "type": "apply_patch_call_output",

132 "call_id": call["call_id"],132 "call_id": call["call_id"],

133 "status": "completed" if success else "failed",133 "status": "completed" if success else "failed",

134 "output": maybe_log_output,134 "output": maybe_log_output,

135 })135 }

136 )

136 137 

137followup = client.responses.create(138followup = client.responses.create(

138 model="gpt-5.6",139 model="gpt-5.6",

Details

65 tools=[65 tools=[

66 {66 {

67 "type": "code_interpreter",67 "type": "code_interpreter",

68 "container": {"type": "auto", "memory_limit": "4g"}68 "container": {"type": "auto", "memory_limit": "4g"},

69 }69 }

70 ],70 ],

71 instructions=instructions,71 instructions=instructions,


124 125 

125response = client.responses.create(126response = client.responses.create(

126 model="gpt-5.6",127 model="gpt-5.6",

127 tools=[{128 tools=[{"type": "code_interpreter", "container": container.id}],

128 "type": "code_interpreter",

129 "container": container.id

130 }],

131 tool_choice="required",129 tool_choice="required",

132 input="use the python tool to calculate what is 4 * 3.82. and then find its square root and then find the square root of that result"130 input="use the python tool to calculate what is 4 * 3.82. and then find its square root and then find the square root of that result",

133)131)

134 132 

135print(response.output_text)133print(response.output_text)

Details

809 case "wait":809 case "wait":

810 time.sleep(2)810 time.sleep(2)

811 case "screenshot":811 case "screenshot":

812 pass812 # The caller captures a screenshot after every action.

813 continue

813 case _:814 case _:

814 raise ValueError(f"Unsupported action: {action.type}")815 raise ValueError(f"Unsupported action: {action.type}")

815```816```


958 match action.type:959 match action.type:

959 case "click":960 case "click":

960 reject_modifiers(action)961 reject_modifiers(action)

961 button = normalize_xdotool_button(962 button = normalize_xdotool_button(getattr(action, "button", "left"))

962 getattr(action, "button", "left")

963 )

964 docker_exec(963 docker_exec(

965 f"DISPLAY={vm.display} xdotool mousemove {action.x} {action.y} click {button}",964 f"DISPLAY={vm.display} xdotool mousemove {action.x} {action.y} click {button}",

966 vm.container_name,965 vm.container_name,


1026 case "wait":1025 case "wait":

1027 time.sleep(2)1026 time.sleep(2)

1028 case "screenshot":1027 case "screenshot":

1029 pass1028 # The caller captures a screenshot after every action.

1029 continue

1030 case _:1030 case _:

1031 raise ValueError(f"Unsupported action: {action.type}")1031 raise ValueError(f"Unsupported action: {action.type}")

1032```1032```


1246 case "wait":1246 case "wait":

1247 time.sleep(2)1247 time.sleep(2)

1248 case "screenshot":1248 case "screenshot":

1249 pass1249 # The caller captures a screenshot after every action.

1250 continue

1250 case _:1251 case _:

1251 raise ValueError(f"Unsupported action: {action.type}")1252 raise ValueError(f"Unsupported action: {action.type}")

1252```1253```


1427 for action in actions:1428 for action in actions:

1428 match action.type:1429 match action.type:

1429 case "click":1430 case "click":

1430 button = normalize_xdotool_button(1431 button = normalize_xdotool_button(getattr(action, "button", "left"))

1431 getattr(action, "button", "left")

1432 )

1433 with_modifiers(1432 with_modifiers(

1434 vm,1433 vm,

1435 getattr(action, "keys", None),1434 getattr(action, "keys", None),


1510 case "wait":1509 case "wait":

1511 time.sleep(2)1510 time.sleep(2)

1512 case "screenshot":1511 case "screenshot":

1513 pass1512 # The caller captures a screenshot after every action.

1513 continue

1514 case _:1514 case _:

1515 raise ValueError(f"Unsupported action: {action.type}")1515 raise ValueError(f"Unsupported action: {action.type}")

1516```1516```


2054# "openai",2054# "openai",

2055# ]2055# ]

2056# ///2056# ///

2057# Run with: `uv run cua_code_mode_py_async.py`2057# Run with:

2058# \`uv run python test/run_example.py tools/cua/015-code-execution-harness-example.py\`

2058# Override the user prompt with:2059# Override the user prompt with:

2059# `uv run cua_code_mode_py_async.py --prompt "Go to example.com and summarize the page."`2060# \`uv run python test/run_example.py tools/cua/015-code-execution-harness-example.py --prompt "Go to example.com and summarize the page."\`

2060# Requires `OPENAI_API_KEY` and `OPENAI_EXAMPLE_CODE_EXECUTION_URL`.2061# Requires \`OPENAI_API_KEY\` and \`OPENAI_EXAMPLE_CODE_EXECUTION_URL\`.

2061 2062 

2062"""Async Python analogue of cua_code_mode.ts.2063"""Async Python analogue of cua_code_mode.ts.

2063 2064 


2095 if out:2096 if out:

2096 return "\n".join(out)2097 return "\n".join(out)

2097 except Exception:2098 except Exception:

2098 pass2099 return str(item)

2099 return str(item)2100 return str(item)

2100 2101 

2101 2102 


2115 )2116 )

2116 2117 

2117 2118 

2118def _execute_in_sandbox(code: str, session_id: str) -> list[dict[str, Any]]:2119def _execute_in_sandbox(

2119 endpoint = os.environ.get("OPENAI_EXAMPLE_CODE_EXECUTION_URL")2120 code: str,

2120 if not endpoint:2121 session_id: str,

2121 return [2122 endpoint: str,

2122 {2123) -> list[dict[str, Any]]:

2123 "type": "input_text",

2124 "text": (

2125 "Execution blocked. Configure OPENAI_EXAMPLE_CODE_EXECUTION_URL "

2126 "with a separately isolated sandbox service."

2127 ),

2128 }

2129 ]

2130 

2131 headers = {"Content-Type": "application/json"}2124 headers = {"Content-Type": "application/json"}

2132 token = os.environ.get("OPENAI_EXAMPLE_CODE_EXECUTION_TOKEN")2125 token = os.environ.get("OPENAI_EXAMPLE_CODE_EXECUTION_TOKEN")

2133 if token:2126 if token:


2165 max_steps: int = 20,2158 max_steps: int = 20,

2166 model: str = "gpt-5.6",2159 model: str = "gpt-5.6",

2167) -> None:2160) -> None:

2161 code_execution_url = os.environ["OPENAI_EXAMPLE_CODE_EXECUTION_URL"]

2168 client = OpenAI()2162 client = OpenAI()

2169 session_id = str(uuid.uuid4())2163 session_id = str(uuid.uuid4())

2170 2164 


2229 for item in resp.output:2223 for item in resp.output:

2230 item_type = getattr(item, "type", None)2224 item_type = getattr(item, "type", None)

2231 2225 

2232 if item_type == "function_call" and getattr(item, "name", None) == "exec_py":2226 if (

2227 item_type == "function_call"

2228 and getattr(item, "name", None) == "exec_py"

2229 ):

2233 had_tool_call = True2230 had_tool_call = True

2234 raw_args = getattr(item, "arguments", "{}") or "{}"2231 raw_args = getattr(item, "arguments", "{}") or "{}"

2235 try:2232 try:


2241 print(code)2238 print(code)

2242 print("----")2239 print("----")

2243 2240 

2244 if not os.environ.get("OPENAI_EXAMPLE_CODE_EXECUTION_URL"):

2245 py_output = _execute_in_sandbox(code, session_id)

2246 else:

2247 approval = await _ainput(2241 approval = await _ainput(

2248 "Send this generated Python to the isolated runtime? "2242 "Send this generated Python to the isolated runtime? "

2249 "Type yes to continue: "2243 "Type yes to continue: "


2262 _execute_in_sandbox,2256 _execute_in_sandbox,

2263 code,2257 code,

2264 session_id,2258 session_id,

2259 code_execution_url,

2265 ),2260 ),

2266 timeout=EXECUTION_TIMEOUT_SECONDS,2261 timeout=EXECUTION_TIMEOUT_SECONDS,

2267 )2262 )


2288 print("PY IMAGE: [base64 string omitted]")2283 print("PY IMAGE: [base64 string omitted]")

2289 print("=====")2284 print("=====")

2290 2285 

2291 elif item_type == "function_call" and getattr(item, "name", None) == "ask_user":2286 elif (

2287 item_type == "function_call"

2288 and getattr(item, "name", None) == "ask_user"

2289 ):

2292 had_tool_call = True2290 had_tool_call = True

2293 raw_args = getattr(item, "arguments", "{}") or "{}"2291 raw_args = getattr(item, "arguments", "{}") or "{}"

2294 try:2292 try:

Details

191```191```

192 192 

193```python193```python

194import os

195 

194from openai import OpenAI196from openai import OpenAI

195 197 

196client = OpenAI()198client = OpenAI()

199connector_authorization = os.environ["OPENAI_CONNECTOR_AUTHORIZATION"]

197 200 

198resp = client.responses.create(201resp = client.responses.create(

199 model="gpt-5.6",202 model="gpt-5.6",


202 "type": "mcp",205 "type": "mcp",

203 "server_label": "Dropbox",206 "server_label": "Dropbox",

204 "connector_id": "connector_dropbox",207 "connector_id": "connector_dropbox",

205 "authorization": "<oauth access token>",208 "authorization": connector_authorization,

206 "require_approval": "never",209 "require_approval": "never",

207 },210 },

208 ],211 ],


386 389 

387resp = client.responses.create(390resp = client.responses.create(

388 model="gpt-5.6",391 model="gpt-5.6",

389 tools=[{392 tools=[

393 {

390 "type": "mcp",394 "type": "mcp",

391 "server_label": "dmcp",395 "server_label": "dmcp",

392 "server_description": "A Dungeons and Dragons MCP server to assist with dice rolling.",396 "server_description": "A Dungeons and Dragons MCP server to assist with dice rolling.",

393 "server_url": "https://dmcp-server.deno.dev/mcp",397 "server_url": "https://dmcp-server.deno.dev/mcp",

394 "require_approval": "never",398 "require_approval": "never",

395 "allowed_tools": ["roll"],399 "allowed_tools": ["roll"],

396 }],400 }

401 ],

397 input="Roll 2d4+1",402 input="Roll 2d4+1",

398)403)

399 404 


524 529 

525resp = client.responses.create(530resp = client.responses.create(

526 model="gpt-5.6",531 model="gpt-5.6",

527 tools=[{532 tools=[

533 {

528 "type": "mcp",534 "type": "mcp",

529 "server_label": "dmcp",535 "server_label": "dmcp",

530 "server_description": "A Dungeons and Dragons MCP server to assist with dice rolling.",536 "server_description": "A Dungeons and Dragons MCP server to assist with dice rolling.",

531 "server_url": "https://dmcp-server.deno.dev/mcp",537 "server_url": "https://dmcp-server.deno.dev/mcp",

532 "require_approval": "always",538 "require_approval": "always",

533 }],539 }

540 ],

534 previous_response_id="resp_682d498bdefc81918b4a6aa477bfafd904ad1e533afccbfa",541 previous_response_id="resp_682d498bdefc81918b4a6aa477bfafd904ad1e533afccbfa",

535 input=[{542 input=[

543 {

536 "type": "mcp_approval_response",544 "type": "mcp_approval_response",

537 "approve": True,545 "approve": True,

538 "approval_request_id": "mcpr_682d498e3bd4819196a0ce1664f8e77b04ad1e533afccbfa"546 "approval_request_id": "mcpr_682d498e3bd4819196a0ce1664f8e77b04ad1e533afccbfa",

539 }],547 }

548 ],

540)549)

541 550 

542print(resp.output_text)551print(resp.output_text)


640 "server_label": "deepwiki",649 "server_label": "deepwiki",

641 "server_url": "https://mcp.deepwiki.com/mcp",650 "server_url": "https://mcp.deepwiki.com/mcp",

642 "require_approval": {651 "require_approval": {

643 "never": {652 "never": {"tool_names": ["ask_question", "read_wiki_structure"]}

644 "tool_names": ["ask_question", "read_wiki_structure"]653 },

645 }

646 }

647 },654 },

648 ],655 ],

649 input="What transport protocols does the 2025-03-26 version of the MCP spec (modelcontextprotocol/modelcontextprotocol) support?",656 input="What transport protocols does the 2025-03-26 version of the MCP spec (modelcontextprotocol/modelcontextprotocol) support?",


721```728```

722 729 

723```python730```python

731import os

724from openai import OpenAI732from openai import OpenAI

725 733 

726client = OpenAI()734client = OpenAI()

735authorization = os.environ["STRIPE_OAUTH_ACCESS_TOKEN"]

727 736 

728resp = client.responses.create(737resp = client.responses.create(

729 model="gpt-5.6",738 model="gpt-5.6",


733 "type": "mcp",742 "type": "mcp",

734 "server_label": "stripe",743 "server_label": "stripe",

735 "server_url": "https://mcp.stripe.com",744 "server_url": "https://mcp.stripe.com",

736 "authorization": "$STRIPE_OAUTH_ACCESS_TOKEN"745 "authorization": authorization,

737 }746 }

738 ]747 ],

739)748)

740 749 

741print(resp.output_text)750print(resp.output_text)


845```854```

846 855 

847```python856```python

857import os

848from openai import OpenAI858from openai import OpenAI

849 859 

850client = OpenAI()860client = OpenAI()

861authorization = os.environ["GOOGLE_CALENDAR_OAUTH_ACCESS_TOKEN"]

851 862 

852resp = client.responses.create(863resp = client.responses.create(

853 model="gpt-5.6",864 model="gpt-5.6",


856 "type": "mcp",867 "type": "mcp",

857 "server_label": "google_calendar",868 "server_label": "google_calendar",

858 "connector_id": "connector_googlecalendar",869 "connector_id": "connector_googlecalendar",

859 "authorization": "ya29.A0AS3H6...",870 "authorization": authorization,

860 "require_approval": "never",871 "require_approval": "never",

861 },872 },

862 ],873 ],

Details

277)277)

278 278 

279image_generation_calls = [279image_generation_calls = [

280 output280 output for output in response.output if output.type == "image_generation_call"

281 for output in response.output

282 if output.type == "image_generation_call"

283]281]

284 282 

285image_data = [output.result for output in image_generation_calls]283image_data = [output.result for output in image_generation_calls]

Details

82 item_type = getattr(item, "type", None)82 item_type = getattr(item, "type", None)

83 if item_type == "local_shell_call":83 if item_type == "local_shell_call":

84 shell_calls.append(item)84 shell_calls.append(item)

85 elif item_type == "tool_call" and getattr(item, "tool_name", None) == "local_shell":85 elif (

86 item_type == "tool_call"

87 and getattr(item, "tool_name", None) == "local_shell"

88 ):

86 shell_calls.append(item)89 shell_calls.append(item)

87 if not shell_calls:90 if not shell_calls:

88 # No more commands — the assistant is done.91 # No more commands — the assistant is done.

Details

342from openai import OpenAI343from openai import OpenAI

343 344 

344client = OpenAI()345client = OpenAI()

346model = "gpt-5.6"

345 347 

346 348 

347def get_inventory(sku):349def get_inventory(sku):


412 414 

413while True:415while True:

414 response = client.responses.create(416 response = client.responses.create(

415 model="YOUR_MODEL_ID",417 model=model,

416 store=False,418 store=False,

417 input=input_items,419 input=input_items,

418 tools=tools,420 tools=tools,


422 raise RuntimeError(f"Response ended with status {response.status}")424 raise RuntimeError(f"Response ended with status {response.status}")

423 425 

424 # Preserve every output item, including program and reasoning items.426 # Preserve every output item, including program and reasoning items.

425 input_items.extend(427 input_items.extend(item.model_dump(exclude_none=True) for item in response.output)

426 item.model_dump(exclude_none=True) for item in response.output

427 )

428 428 

429 calls = [item for item in response.output if item.type == "function_call"]429 calls = [item for item in response.output if item.type == "function_call"]

430 if not calls:430 if not calls:

431 message = next((item for item in response.output if item.type == "message"), None)431 message = next(

432 (item for item in response.output if item.type == "message"), None

433 )

432 if message:434 if message:

433 refusal = next(435 refusal = next(

434 (part.refusal for part in message.content if part.type == "refusal"),436 (part.refusal for part in message.content if part.type == "refusal"),

Details

208```208```

209 209 

210```python210```python

211from openai import OpenAI

212 

213client = OpenAI()

214 

215response = client.responses.create(211response = client.responses.create(

216 model="gpt-5.6",212 model="gpt-5.6",

217 tools=[213 tools=[


219 "type": "shell",215 "type": "shell",

220 "environment": {216 "environment": {

221 "type": "container_reference",217 "type": "container_reference",

222 "container_id": "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe",218 "container_id": container.id,

223 },219 },

224 }220 }

225 ],221 ],


275```271```

276 272 

277```python273```python

274import os

278from openai import OpenAI275from openai import OpenAI

279 276 

280client = OpenAI()277client = OpenAI()

278skill_id = os.environ["OPENAI_SKILL_ID"]

281 279 

282container = client.containers.create(280container = client.containers.create(

283 name="skill-container",281 name="skill-container",

284 skills=[282 skills=[

285 {"type": "skill_reference", "skill_id": "skill_4db6f1a2c9e73508b41f9da06e2c7b5f"},283 {

286 {"type": "skill_reference", "skill_id": "openai-spreadsheets", "version": "latest"},284 "type": "skill_reference",

285 "skill_id": skill_id,

286 },

287 {

288 "type": "skill_reference",

289 "skill_id": "openai-spreadsheets",

290 "version": "latest",

291 },

287 ],292 ],

288)293)

289 294 


377 "type": "container_auto",382 "type": "container_auto",

378 "network_policy": {383 "network_policy": {

379 "type": "allowlist",384 "type": "allowlist",

380 "allowed_domains": ["pypi.org", "files.pythonhosted.org", "github.com"],385 "allowed_domains": [

386 "pypi.org",

387 "files.pythonhosted.org",

388 "github.com",

389 ],

381 },390 },

382 },391 },

383 }392 }


625```634```

626 635 

627```python636```python

637import os

628from openai import OpenAI638from openai import OpenAI

629 639 

630client = OpenAI()640client = OpenAI()

641container_id = os.environ["OPENAI_CONTAINER_ID"]

631 642 

632deleted = client.containers.delete("container_id")643deleted = client.containers.delete(container_id)

633 644 

634print(deleted)645print(deleted)

635```646```

Details

110```110```

111 111 

112```python112```python

113from openai import OpenAI

114 

115client = OpenAI()

116 

117response = client.responses.create(113response = client.responses.create(

118 model="gpt-5.6",114 model="gpt-5.6",

119 tools=[115 tools=[


123 "type": "container_auto",119 "type": "container_auto",

124 "skills": [120 "skills": [

125 {"type": "skill_reference", "skill_id": "<skill_id>"},121 {"type": "skill_reference", "skill_id": "<skill_id>"},

126 {"type": "skill_reference", "skill_id": "<skill_id>", "version": 2},122 {

123 "type": "skill_reference",

124 "skill_id": "<skill_id>",

125 "version": 2,

126 },

127 ],127 ],

128 },128 },

129 }129 }


205```205```

206 206 

207```python207```python

208from openai import OpenAI

209 

210client = OpenAI()

211 

212response = client.responses.create(208response = client.responses.create(

213 model="gpt-5.6",209 model="gpt-5.6",

214 tools=[210 tools=[

Details

342 message = getattr(342 message = getattr(

343 getattr(video, "error", None), "message", "Video generation failed"343 getattr(video, "error", None), "message", "Video generation failed"

344 )344 )

345 print(message)345 raise RuntimeError(message)

346 return

347 346 

348print("Video generation completed:", video)347print("Video generation completed:", video)

349print("Downloading video content...")348print("Downloading video content...")

Details

199Signature verification with the OpenAI SDK201Signature verification with the OpenAI SDK

200 202 

201```python203```python

204import os

205 

206from flask import request

207from openai import OpenAI

208 

202client = OpenAI()209client = OpenAI()

203webhook_secret = os.environ["OPENAI_WEBHOOK_SECRET"]210webhook_secret = os.environ["OPENAI_WEBHOOK_SECRET"]

204 211 

205# will raise if the signature is invalid212# will raise if the signature is invalid

206event = client.webhooks.unwrap(request.data, request.headers, secret=webhook_secret)213event = client.webhooks.unwrap(

214 request.data,

215 request.headers,

216 secret=webhook_secret,

217)

207```218```

208 219 

209```javascript220```javascript

Details

65 Key=workload,Value=batch-ingest \65 Key=workload,Value=batch-ingest \

66 --query "WebIdentityToken" \66 --query "WebIdentityToken" \

67 --output text)67 --output text)

68export TOKEN

68```69```

69 70 

70### Verify the AWS-issued token71### Verify the AWS-issued token

71 72 

72Before configuring workload identity federation, decode a sample AWS-issued token locally and inspect its claims:73Before configuring workload identity federation, export the AWS-issued token as `TOKEN`, then run this script locally to inspect its claims:

73 74 

74```bash75```python

75TOKEN="$TOKEN" python3 - <<'PY'

76import base6476import base64

77import json77import json

78import os78import os


80payload = os.environ["TOKEN"].split(".")[1]80payload = os.environ["TOKEN"].split(".")[1]

81payload += "=" * (-len(payload) % 4)81payload += "=" * (-len(payload) % 4)

82print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))82print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))

83PY

84```83```

85 84 

85 

86This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.86This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.

87 87 

88A decoded AWS-issued OIDC token will look similar to:88A decoded AWS-issued OIDC token will look similar to:


565 565 

566### Verify the EKS token566### Verify the EKS token

567 567 

568Before configuring workload identity federation, decode a sample projected service account token locally and inspect its claims. From a running pod with the projected token mounted:568Before configuring workload identity federation, decode a sample projected service account token locally and inspect its claims. From a running pod with the projected token mounted, retrieve the token and export it as `TOKEN`:

569 569 

570```bash570```bash

571TOKEN=$(kubectl exec -n default openai-wif-app -- cat /var/run/secrets/tokens/token)571TOKEN=$(kubectl exec -n default openai-wif-app -- cat /var/run/secrets/tokens/token)

572export TOKEN

573```

572 574 

573TOKEN="$TOKEN" python3 - <<'PY'575Then run this script:

576 

577```python

574import base64578import base64

575import json579import json

576import os580import os


578payload = os.environ["TOKEN"].split(".")[1]582payload = os.environ["TOKEN"].split(".")[1]

579payload += "=" * (-len(payload) % 4)583payload += "=" * (-len(payload) % 4)

580print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))584print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))

581PY

582```585```

583 586 

587 

584This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.588This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.

585 589 

586A decoded EKS projected service account token will look similar to:590A decoded EKS projected service account token will look similar to:

Details

24 24 

25TOKEN=$(curl -sSf -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \25TOKEN=$(curl -sSf -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \

26 "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${ENCODED_AUDIENCE}" | jq -r .value)26 "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${ENCODED_AUDIENCE}" | jq -r .value)

27export TOKEN

27```28```

28 29 

29Important GitHub OIDC claims include:30Important GitHub OIDC claims include:


43 44 

44## Verify the token45## Verify the token

45 46 

46Before configuring workload identity federation, decode a sample GitHub OIDC token in the workflow runner and inspect its claims. After requesting the token in a workflow step:47Before configuring workload identity federation, export the GitHub OIDC token as `TOKEN`, then run this script in the workflow runner to inspect its claims:

47 48 

48```bash49```python

49TOKEN="$TOKEN" python3 - <<'PY'

50import base6450import base64

51import json51import json

52import os52import os


54payload = os.environ["TOKEN"].split(".")[1]54payload = os.environ["TOKEN"].split(".")[1]

55payload += "=" * (-len(payload) % 4)55payload += "=" * (-len(payload) % 4)

56print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))56print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))

57PY

58```57```

59 58 

59 

60This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools. Never log the raw GitHub OIDC token or the exchanged OpenAI access token.60This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools. Never log the raw GitHub OIDC token or the exchanged OpenAI access token.

61 61 

62A decoded GitHub Actions OIDC token will look similar to:62A decoded GitHub Actions OIDC token will look similar to:

Details

39TOKEN=$(curl -sS -G -H "Metadata-Flavor: Google" \39TOKEN=$(curl -sS -G -H "Metadata-Flavor: Google" \

40 "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity" \40 "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity" \

41 --data-urlencode "audience=${AUDIENCE}")41 --data-urlencode "audience=${AUDIENCE}")

42export TOKEN

42```43```

43 44 

44The metadata server returns a Google-signed JWT. For more information about the metadata server identity endpoint, see Google's guide to [verify VM identity](https://docs.cloud.google.com/compute/docs/instances/verifying-instance-identity).45The metadata server returns a Google-signed JWT. For more information about the metadata server identity endpoint, see Google's guide to [verify VM identity](https://docs.cloud.google.com/compute/docs/instances/verifying-instance-identity).

45 46 

46### Verify the token47### Verify the token

47 48 

48Before configuring workload identity federation, decode a sample Google identity token locally and inspect its claims:49Before configuring workload identity federation, export the Google identity token as `TOKEN`, then run this script locally to inspect its claims:

49 50 

50```bash51```python

51TOKEN="$TOKEN" python3 - <<'PY'

52import base6452import base64

53import json53import json

54import os54import os


56payload = os.environ["TOKEN"].split(".")[1]56payload = os.environ["TOKEN"].split(".")[1]

57payload += "=" * (-len(payload) % 4)57payload += "=" * (-len(payload) % 4)

58print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))58print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))

59PY

60```59```

61 60 

61 

62This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.62This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.

63 63 

64A decoded Google metadata server identity token will look similar to:64A decoded Google metadata server identity token will look similar to:


202 token = response.read().decode("utf-8").strip()202 token = response.read().decode("utf-8").strip()

203 203 

204 if not token:204 if not token:

205 raise RuntimeError("Google metadata server did not return an identity token.")205 raise RuntimeError(

206 "Google metadata server did not return an identity token."

207 )

206 return token208 return token

207 209 

208 return {"token_type": "jwt", "get_token": get_token}210 return {"token_type": "jwt", "get_token": get_token}


591 594 

592### Verify the token595### Verify the token

593 596 

594Before configuring workload identity federation, decode a sample projected service account token locally and inspect its claims. From a running pod with the projected token mounted:597Before configuring workload identity federation, decode a sample projected service account token locally and inspect its claims. From a running pod with the projected token mounted, retrieve the token and export it as `TOKEN`:

595 598 

596```bash599```bash

597TOKEN=$(kubectl exec -n default openai-wif-app -- cat /var/run/secrets/tokens/token)600TOKEN=$(kubectl exec -n default openai-wif-app -- cat /var/run/secrets/tokens/token)

601export TOKEN

602```

603 

604Then run this script:

598 605 

599TOKEN="$TOKEN" python3 - <<'PY'606```python

600import base64607import base64

601import json608import json

602import os609import os


604payload = os.environ["TOKEN"].split(".")[1]611payload = os.environ["TOKEN"].split(".")[1]

605payload += "=" * (-len(payload) % 4)612payload += "=" * (-len(payload) % 4)

606print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))613print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))

607PY

608```614```

609 615 

616 

610This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.617This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.

611 618 

612A decoded GKE projected service account token will look similar to:619A decoded GKE projected service account token will look similar to:

Details

55 55 

56## Verify the token56## Verify the token

57 57 

58Before configuring workload identity federation, decode a sample projected service account token locally and inspect its claims. From a running pod with the projected token mounted:58Before configuring workload identity federation, decode a sample projected service account token locally and inspect its claims. From a running pod with the projected token mounted, retrieve the token and export it as `TOKEN`:

59 59 

60```bash60```bash

61TOKEN=$(kubectl exec -n default openai-wif-app -- cat /var/run/secrets/tokens/token)61TOKEN=$(kubectl exec -n default openai-wif-app -- cat /var/run/secrets/tokens/token)

62export TOKEN

63```

64 

65Then run this script:

62 66 

63TOKEN="$TOKEN" python3 - <<'PY'67```python

64import base6468import base64

65import json69import json

66import os70import os


68payload = os.environ["TOKEN"].split(".")[1]72payload = os.environ["TOKEN"].split(".")[1]

69payload += "=" * (-len(payload) % 4)73payload += "=" * (-len(payload) % 4)

70print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))74print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))

71PY

72```75```

73 76 

77 

74This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.78This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.

75 79 

76A decoded Kubernetes projected service account token will look similar to:80A decoded Kubernetes projected service account token will look similar to:

Details

35 --data-urlencode "api-version=2018-02-01" \35 --data-urlencode "api-version=2018-02-01" \

36 --data-urlencode "resource=${APPLICATION_ID_URI}" \36 --data-urlencode "resource=${APPLICATION_ID_URI}" \

37 | jq -r .access_token)37 | jq -r .access_token)

38export TOKEN

38```39```

39 40 

40If the resource has multiple user-assigned managed identities, add the `client_id`, `object_id`, or `msi_res_id` query parameter for the managed identity you want to use. Microsoft documents the IMDS token request parameters in [Use managed identities on a virtual machine to acquire access token](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token).41If the resource has multiple user-assigned managed identities, add the `client_id`, `object_id`, or `msi_res_id` query parameter for the managed identity you want to use. Microsoft documents the IMDS token request parameters in [Use managed identities on a virtual machine to acquire access token](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token).

41 42 

42### Verify the token43### Verify the token

43 44 

44Before configuring workload identity federation, decode a sample token locally and inspect its claims:45Before configuring workload identity federation, export the Microsoft Entra token as `TOKEN`, then run this script locally to inspect its claims:

45 46 

46```bash47```python

47TOKEN="$TOKEN" python3 - <<'PY'

48import base6448import base64

49import json49import json

50import os50import os


52payload = os.environ["TOKEN"].split(".")[1]52payload = os.environ["TOKEN"].split(".")[1]

53payload += "=" * (-len(payload) % 4)53payload += "=" * (-len(payload) % 4)

54print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))54print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))

55PY

56```55```

57 56 

57 

58This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.58This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.

59 59 

60A decoded Microsoft Entra ID managed identity token will look similar to:60A decoded Microsoft Entra ID managed identity token will look similar to:


619 620 

620### Verify the token621### Verify the token

621 622 

622Before configuring workload identity federation, decode a sample projected service account token locally and inspect its claims. From a running pod with the projected token mounted:623Before configuring workload identity federation, decode a sample projected service account token locally and inspect its claims. From a running pod with the projected token mounted, retrieve the token and export it as `TOKEN`:

623 624 

624```bash625```bash

625TOKEN=$(kubectl exec -n default openai-wif-app -- cat /var/run/secrets/tokens/token)626TOKEN=$(kubectl exec -n default openai-wif-app -- cat /var/run/secrets/tokens/token)

627export TOKEN

628```

629 

630Then run this script:

626 631 

627TOKEN="$TOKEN" python3 - <<'PY'632```python

628import base64633import base64

629import json634import json

630import os635import os


632payload = os.environ["TOKEN"].split(".")[1]637payload = os.environ["TOKEN"].split(".")[1]

633payload += "=" * (-len(payload) % 4)638payload += "=" * (-len(payload) % 4)

634print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))639print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))

635PY

636```640```

637 641 

642 

638This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.643This command decodes the JWT payload without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.

639 644 

640A decoded AKS projected service account token will look similar to:645A decoded AKS projected service account token will look similar to:

Details

63To inspect a JWT-SVID from a workload that can call the SPIFFE Workload API, request one for the same audience you will configure in OpenAI. Run this command in the same workload context as the application, because Workload API authorization depends on the identity of the calling process.63To inspect a JWT-SVID from a workload that can call the SPIFFE Workload API, request one for the same audience you will configure in OpenAI. Run this command in the same workload context as the application, because Workload API authorization depends on the identity of the calling process.

64 64 

65```bash65```bash

66spire-agent api fetch jwt \66TOKEN=$(spire-agent api fetch jwt \

67 -socketPath /run/spire/sockets/agent.sock \67 -socketPath /run/spire/sockets/agent.sock \

68 -audience "https://api.openai.com/v1"68 -audience "https://api.openai.com/v1" | sed -n '2p')

69export TOKEN

69```70```

70 71 

71If your workload has more than one SPIFFE ID, request the specific identity:72If your workload has more than one SPIFFE ID, request the specific identity:

72 73 

73```bash74```bash

74spire-agent api fetch jwt \75TOKEN=$(spire-agent api fetch jwt \

75 -socketPath /run/spire/sockets/agent.sock \76 -socketPath /run/spire/sockets/agent.sock \

76 -spiffeID "spiffe://example.org/ns/production/sa/openai-wif" \77 -spiffeID "spiffe://example.org/ns/production/sa/openai-wif" \

77 -audience "https://api.openai.com/v1"78 -audience "https://api.openai.com/v1" | sed -n '2p')

79export TOKEN

78```80```

79 81 

80## Verify the token82## Verify the token

81 83 

82Before configuring workload identity federation, decode a sample JWT-SVID locally and inspect its header and claims:84Before configuring workload identity federation, export the JWT-SVID as `TOKEN`, then run this script locally to inspect its header and claims:

83 85 

84```bash86```python

85TOKEN="$SPIFFE_JWT_SVID" python3 - <<'PY'

86import base6487import base64

87import json88import json

88import os89import os


99print(json.dumps(decode(parts[0]), indent=2))102print(json.dumps(decode(parts[0]), indent=2))

100print("\nPayload:")103print("\nPayload:")

101print(json.dumps(decode(parts[1]), indent=2))104print(json.dumps(decode(parts[1]), indent=2))

102PY

103```105```

104 106 

107 

105This command decodes the JWT without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.108This command decodes the JWT without verifying the token signature. Use a local decoder for production tokens, and avoid pasting production tokens into third-party tools.

106 109 

107A decoded SPIFFE JWT-SVID will look similar to:110A decoded SPIFFE JWT-SVID will look similar to:

mcp.md +27 −26

Details

205logger = logging.getLogger(__name__)206logger = logging.getLogger(__name__)

206 207 

207# OpenAI configuration208# OpenAI configuration

208OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")209OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]

209VECTOR_STORE_ID = os.environ.get("VECTOR_STORE_ID", "")210VECTOR_STORE_ID = os.environ["VECTOR_STORE_ID"]

210 211 

211# Initialize OpenAI client212# Initialize OpenAI client

212openai_client = OpenAI()213openai_client = OpenAI(api_key=OPENAI_API_KEY)

213 214 

214server_instructions = """215server_instructions = """

215This MCP server provides search and document retrieval capabilities216This MCP server provides search and document retrieval capabilities


223 """Create and configure the MCP server with search and fetch tools."""224 """Create and configure the MCP server with search and fetch tools."""

224 225 

225 # Initialize the FastMCP server226 # Initialize the FastMCP server

226 mcp = FastMCP(name="Sample MCP Server",227 mcp = FastMCP(name="Sample MCP Server", instructions=server_instructions)

227 instructions=server_instructions)

228 228 

229 @mcp.tool(output_schema=SearchOutput.model_json_schema())229 @mcp.tool(output_schema=SearchOutput.model_json_schema())

230 async def search(query: str) -> SearchOutput:230 async def search(query: str) -> SearchOutput:


247 247 

248 if not openai_client:248 if not openai_client:

249 logger.error("OpenAI client not initialized - API key missing")249 logger.error("OpenAI client not initialized - API key missing")

250 raise ValueError(250 raise ValueError("OpenAI API key is required for vector store search")

251 "OpenAI API key is required for vector store search")

252 251 

253 # Search the vector store using OpenAI API252 # Search the vector store using OpenAI API

254 logger.info(f"Searching {VECTOR_STORE_ID} for query: '{query}'")253 logger.info(f"Searching {VECTOR_STORE_ID} for query: '{query}'")

255 254 

256 response = openai_client.vector_stores.search(255 response = openai_client.vector_stores.search(

257 vector_store_id=VECTOR_STORE_ID, query=query)256 vector_store_id=VECTOR_STORE_ID, query=query

257 )

258 258 

259 results = []259 results = []

260 260 

261 # Process the vector store search results261 # Process the vector store search results

262 if hasattr(response, 'data') and response.data:262 if hasattr(response, "data") and response.data:

263 for i, item in enumerate(response.data):263 for i, item in enumerate(response.data):

264 # Extract file_id, filename, and content264 # Extract file_id, filename, and content

265 item_id = getattr(item, 'file_id', f"vs_{i}")265 item_id = getattr(item, "file_id", f"vs_{i}")

266 item_filename = getattr(item, 'filename', f"Document {i+1}")266 item_filename = getattr(item, "filename", f"Document {i + 1}")

267 267 

268 result = SearchResult(268 result = SearchResult(

269 id=item_id,269 id=item_id,


301 if not openai_client:301 if not openai_client:

302 logger.error("OpenAI client not initialized - API key missing")302 logger.error("OpenAI client not initialized - API key missing")

303 raise ValueError(303 raise ValueError(

304 "OpenAI API key is required for vector store file retrieval")304 "OpenAI API key is required for vector store file retrieval"

305 )

305 306 

306 logger.info(f"Fetching content from vector store for file ID: {id}")307 logger.info(f"Fetching content from vector store for file ID: {id}")

307 308 

308 # Fetch file content from vector store309 # Fetch file content from vector store

309 content_response = openai_client.vector_stores.files.content(310 content_response = openai_client.vector_stores.files.content(

310 vector_store_id=VECTOR_STORE_ID, file_id=id)311 vector_store_id=VECTOR_STORE_ID, file_id=id

312 )

311 313 

312 # Get file metadata314 # Get file metadata

313 file_info = openai_client.vector_stores.files.retrieve(315 file_info = openai_client.vector_stores.files.retrieve(

314 vector_store_id=VECTOR_STORE_ID, file_id=id)316 vector_store_id=VECTOR_STORE_ID, file_id=id

317 )

315 318 

316 # Extract content from paginated response319 # Extract content from paginated response

317 file_content = ""320 file_content = ""

318 if hasattr(content_response, 'data') and content_response.data:321 if hasattr(content_response, "data") and content_response.data:

319 # Combine all content chunks from FileContentResponse objects322 # Combine all content chunks from FileContentResponse objects

320 content_parts = []323 content_parts = []

321 for content_item in content_response.data:324 for content_item in content_response.data:

322 if hasattr(content_item, 'text'):325 if hasattr(content_item, "text"):

323 content_parts.append(content_item.text)326 content_parts.append(content_item.text)

324 file_content = "\n".join(content_parts)327 file_content = "\n".join(content_parts)

325 else:328 else:

326 file_content = "No content available"329 file_content = "No content available"

327 330 

328 # Use filename as title and create proper URL for citations331 # Use filename as title and create proper URL for citations

329 filename = getattr(file_info, 'filename', f"Document {id}")332 filename = getattr(file_info, "filename", f"Document {id}")

330 333 

331 result = FetchOutput(334 result = FetchOutput(

332 id=id,335 id=id,


336 )339 )

337 340 

338 # Add metadata if available from file info341 # Add metadata if available from file info

339 if hasattr(file_info, 'attributes') and file_info.attributes:342 if hasattr(file_info, "attributes") and file_info.attributes:

340 result.metadata = dict(file_info.attributes)343 result.metadata = dict(file_info.attributes)

341 344 

342 logger.info(f"Fetched vector store file: {id}")345 logger.info(f"Fetched vector store file: {id}")


347 350 

348def main():351def main():

349 """Main function to start the MCP server."""352 """Main function to start the MCP server."""

350 # Verify OpenAI client is initialized

351 if not openai_client:

352 logger.error(

353 "OpenAI API key not found. Please set OPENAI_API_KEY environment variable."

354 )

355 raise ValueError("OpenAI API key is required")

356 

357 logger.info(f"Using vector store: {VECTOR_STORE_ID}")353 logger.info(f"Using vector store: {VECTOR_STORE_ID}")

358 354 

359 # Create the MCP server355 # Create the MCP server


365 361 

366 try:362 try:

367 # Use FastMCP's built-in run method with SSE transport363 # Use FastMCP's built-in run method with SSE transport

368 server.run(transport="sse", host="0.0.0.0", port=8000)364 port = int(os.environ.get("OPENAI_EXAMPLE_PORT", "8000"))

365 server.run(

366 transport="sse",

367 host="0.0.0.0",

368 port=port,

369 uvicorn_config={"loop": "asyncio"},

370 )

369 except KeyboardInterrupt:371 except KeyboardInterrupt:

370 logger.info("Server stopped by user")372 logger.info("Server stopped by user")

371 except Exception as e:373 except Exception as e:

quickstart.md +13 −21

Details

229 },230 },

230 {231 {

231 "type": "input_image",232 "type": "input_image",

232 "image_url": "https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg"233 "image_url": "https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg",

233 }234 },

234 ]235 ],

235 }236 }

236 ]237 ],

237)238)

238 239 

239print(response.output_text)240print(response.output_text)


357 },359 },

358 ],360 ],

359 },361 },

360 ]362 ],

361)363)

362 364 

363print(response.output_text)365print(response.output_text)


480 482 

481```python483```python

482from openai import OpenAI484from openai import OpenAI

485 

483client = OpenAI()486client = OpenAI()

484 487 

485file = client.files.create(488file = client.files.create(file=open("draconomicon.pdf", "rb"), purpose="user_data")

486 file=open("draconomicon.pdf", "rb"),

487 purpose="user_data"

488)

489 489 

490response = client.responses.create(490response = client.responses.create(

491 model="gpt-5.6",491 model="gpt-5.6",


501 "type": "input_text",501 "type": "input_text",

502 "text": "What is the first dragon in the book?",502 "text": "What is the first dragon in the book?",

503 },503 },

504 ]504 ],

505 }505 }

506 ]506 ],

507)507)

508 508 

509print(response.output_text)509print(response.output_text)


605response = client.responses.create(606response = client.responses.create(

606 model="gpt-5.6",607 model="gpt-5.6",

607 tools=[{"type": "web_search"}],608 tools=[{"type": "web_search"}],

608 input="What was a positive news story from today?"609 input="What was a positive news story from today?",

609)610)

610 611 

611print(response.output_text)612print(response.output_text)


677response = client.responses.create(679response = client.responses.create(

678 model="gpt-5.6",680 model="gpt-5.6",

679 input="What is deep research by OpenAI?",681 input="What is deep research by OpenAI?",

680 tools=[{682 tools=[{"type": "file_search", "vector_store_ids": ["<vector_store_id>"]}],

681 "type": "file_search",

682 "vector_store_ids": ["<vector_store_id>"]

683 }]

684)683)

685print(response)684print(response)

686```685```


771response = client.responses.create(771response = client.responses.create(

772 model="gpt-5.6",772 model="gpt-5.6",

773 instructions="You are a personal math tutor. When asked a math question, write and run code to answer the question.",773 instructions="You are a personal math tutor. When asked a math question, write and run code to answer the question.",

774 tools=[{774 tools=[{"type": "code_interpreter", "container": {"type": "auto"}}],

775 "type": "code_interpreter",775 input="I need to solve the equation 3x + 11 = 14. Can you help me?",

776 "container": {"type": "auto"}

777 }],

778 input="I need to solve the equation 3x + 11 = 14. Can you help me?"

779)776)

780 777 

781print(response.output_text)778print(response.output_text)

Details

55transcribe it:55transcribe it:

56 56 

57```python57```python

58from docx import Document

58from openai import OpenAI59from openai import OpenAI

59 60 

60client = OpenAI(61client = OpenAI()

61 # defaults to os.environ.get("OPENAI_API_KEY")62 

62 # api_key="My API Key",

63)

64from docx import Document

65 63 

66def transcribe_audio(audio_file_path):64def transcribe_audio(audio_file_path):

67 with open(audio_file_path, 'rb') as audio_file:65 with open(audio_file_path, "rb") as audio_file:

68 transcription = client.audio.transcriptions.create("whisper-1", audio_file)66 transcription = client.audio.transcriptions.create(

69 return transcription['text']67 file=audio_file,

68 model="whisper-1",

69 )

70 return transcription.text

70```71```

71 72 

72 73 


87 action_items = action_item_extraction(transcription)88 action_items = action_item_extraction(transcription)

88 sentiment = sentiment_analysis(transcription)89 sentiment = sentiment_analysis(transcription)

89 return {90 return {

90 'abstract_summary': abstract_summary,91 "abstract_summary": abstract_summary,

91 'key_points': key_points,92 "key_points": key_points,

92 'action_items': action_items,93 "action_items": action_items,

93 'sentiment': sentiment94 "sentiment": sentiment,

94 }95 }

95```96```

96 97 


110 messages=[111 messages=[

111 {112 {

112 "role": "system",113 "role": "system",

113 "content": "You are a highly skilled AI trained in language comprehension and summarization. I would like you to read the following text and summarize it into a concise abstract paragraph. Aim to retain the most important points, providing a coherent and readable summary that could help a person understand the main points of the discussion without needing to read the entire text. Please avoid unnecessary details or tangential points."114 "content": "You are a highly skilled AI trained in language comprehension and summarization. I would like you to read the following text and summarize it into a concise abstract paragraph. Aim to retain the most important points, providing a coherent and readable summary that could help a person understand the main points of the discussion without needing to read the entire text. Please avoid unnecessary details or tangential points.",

114 },115 },

115 {116 {"role": "user", "content": transcription},

116 "role": "user",117 ],

117 "content": transcription

118 }

119 ]

120 )118 )

121 return completion.choices[0].message.content119 return response.choices[0].message.content or ""

122```120```

123 121 

124 122 


133 messages=[131 messages=[

134 {132 {

135 "role": "system",133 "role": "system",

136 "content": "You are a proficient AI with a specialty in distilling information into key points. Based on the following text, identify and list the main points that were discussed or brought up. These should be the most important ideas, findings, or topics that are crucial to the essence of the discussion. Your goal is to provide a list that someone could read to quickly understand what was talked about."134 "content": "You are a proficient AI with a specialty in distilling information into key points. Based on the following text, identify and list the main points that were discussed or brought up. These should be the most important ideas, findings, or topics that are crucial to the essence of the discussion. Your goal is to provide a list that someone could read to quickly understand what was talked about.",

137 },135 },

138 {136 {"role": "user", "content": transcription},

139 "role": "user",137 ],

140 "content": transcription

141 }

142 ]

143 )138 )

144 return completion.choices[0].message.content139 return response.choices[0].message.content or ""

145```140```

146 141 

147 142 


156 messages=[151 messages=[

157 {152 {

158 "role": "system",153 "role": "system",

159 "content": "You are an AI expert in analyzing conversations and extracting action items. Please review the text and identify any tasks, assignments, or actions that were agreed upon or mentioned as needing to be done. These could be tasks assigned to specific individuals, or general actions that the group has decided to take. Please list these action items clearly and concisely."154 "content": "You are an AI expert in analyzing conversations and extracting action items. Please review the text and identify any tasks, assignments, or actions that were agreed upon or mentioned as needing to be done. These could be tasks assigned to specific individuals, or general actions that the group has decided to take. Please list these action items clearly and concisely.",

160 },155 },

161 {156 {"role": "user", "content": transcription},

162 "role": "user",157 ],

163 "content": transcription

164 }

165 ]

166 )158 )

167 return completion.choices[0].message.content159 return response.choices[0].message.content or ""

168```160```

169 161 

170 162 


179 messages=[171 messages=[

180 {172 {

181 "role": "system",173 "role": "system",

182 "content": "As an AI with expertise in language and emotion analysis, your task is to analyze the sentiment of the following text. Please consider the overall tone of the discussion, the emotion conveyed by the language used, and the context in which words and phrases are used. Indicate whether the sentiment is generally positive, negative, or neutral, and provide brief explanations for your analysis where possible."174 "content": "As an AI with expertise in language and emotion analysis, your task is to analyze the sentiment of the following text. Please consider the overall tone of the discussion, the emotion conveyed by the language used, and the context in which words and phrases are used. Indicate whether the sentiment is generally positive, negative, or neutral, and provide brief explanations for your analysis where possible.",

183 },175 },

184 {176 {"role": "user", "content": transcription},

185 "role": "user",177 ],

186 "content": transcription

187 }

188 ]

189 )178 )

190 return completion.choices[0].message.content179 return response.choices[0].message.content or ""

191```180```

192 181 

193 182 


217 doc = Document()206 doc = Document()

218 for key, value in minutes.items():207 for key, value in minutes.items():

219 # Replace underscores with spaces and capitalize each word for the heading208 # Replace underscores with spaces and capitalize each word for the heading

220 heading = ' '.join(word.capitalize() for word in key.split('_'))209 heading = " ".join(word.capitalize() for word in key.split("_"))

221 doc.add_heading(heading, level=1)210 doc.add_heading(heading, level=1)

222 doc.add_paragraph(value)211 doc.add_paragraph(value)

223 # Add a line break between sections212 # Add a line break between sections


236minutes = meeting_minutes(transcription)225minutes = meeting_minutes(transcription)

237print(minutes)226print(minutes)

238 227 

239save_as_docx(minutes, 'meeting_minutes.docx')228save_as_docx(minutes, "meeting_minutes.docx")

240```229```

241 230 

242 231 

Details

58import os58import os

59 59 

60# Regex pattern to match a URL60# Regex pattern to match a URL

61HTTP_URL_PATTERN = r'^http[s]*://.+'61HTTP_URL_PATTERN = r"^http[s]*://.+"

62 62 

63domain = "openai.com" # <- put your domain to be crawled63domain = "openai.com" # <- put your domain to be crawled

64full_url = "https://openai.com/" # <- put your domain to be crawled with https or http64full_url = "https://openai.com/" # <- put your domain to be crawled with https or http


85```python86```python

86# Function to get the hyperlinks from a URL87# Function to get the hyperlinks from a URL

87def get_hyperlinks(url):88def get_hyperlinks(url):

88 

89 # Try to open the URL and read the HTML89 # Try to open the URL and read the HTML

90 try:90 try:

91 # Open the URL and read the HTML91 with urllib.request.urlopen(url, timeout=30) as response:

92 with urllib.request.urlopen(url) as response:

93 

94 # If the response is not HTML, return an empty list92 # If the response is not HTML, return an empty list

95 if not response.info().get('Content-Type').startswith("text/html"):93 if not response.info().get("Content-Type", "").startswith("text/html"):

96 return []94 return []

97 95 

98 # Decode the HTML96 # Decode the HTML

99 html = response.read().decode('utf-8')97 html = response.read().decode("utf-8")

100 except Exception as e:98 except Exception as error:

101 print(e)99 print(error)

102 return []100 return []

103 101 

104 # Create the HTML Parser and then Parse the HTML to get hyperlinks102 # Create the HTML Parser and then Parse the HTML to get hyperlinks


160 if not os.path.exists("text/"):158 if not os.path.exists("text/"):

161 os.mkdir("text/")159 os.mkdir("text/")

162 160 

163 if not os.path.exists("text/"+local_domain+"/"):161 if not os.path.exists("text/" + local_domain + "/"):

164 os.mkdir("text/" + local_domain + "/")162 os.mkdir("text/" + local_domain + "/")

165 163 

166 # Create a directory to store the csv files164 # Create a directory to store the csv files


175 print(url) # for debugging and to see the progress172 print(url) # for debugging and to see the progress

176 173 

177 # Save text from the url to a <url>.txt file174 # Save text from the url to a <url>.txt file

178 with open('text/'+local_domain+'/'+url[8:].replace("/", "_") + ".txt", "w", encoding="UTF-8") as f:175 page_name = (local_domain + urlparse(url).path).replace("/", "_")

179 176 with open(

177 "text/" + local_domain + "/" + page_name + ".txt",

178 "w",

179 encoding="UTF-8",

180 ) as f:

180 # Get the text from the URL using BeautifulSoup181 # Get the text from the URL using BeautifulSoup

181 soup = BeautifulSoup(requests.get(url).text, "html.parser")182 response = requests.get(url, timeout=10)

183 response.raise_for_status()

184 soup = BeautifulSoup(response.text, "html.parser")

182 185 

183 # Get the text but remove the tags186 # Get the text but remove the tags

184 text = soup.get_text()187 text = soup.get_text()

185 188 

186 # If the crawler gets to a page that requires JavaScript, it will stop the crawl189 # If the crawler gets to a page that requires JavaScript, it will stop the crawl

187 if ("You need to enable JavaScript to run this app." in text):190 if "You need to enable JavaScript to run this app." in text:

188 print("Unable to parse page " + url + " due to JavaScript being required")191 print(

192 "Unable to parse page " + url + " due to JavaScript being required"

193 )

189 194 

190 # Otherwise, write the text to the file in the text directory195 # Otherwise, write the text to the file in the text directory

191 f.write(text)196 f.write(text)


224 230 

225```python231```python

226def remove_newlines(serie):232def remove_newlines(serie):

227 serie = serie.str.replace('\n', ' ')233 serie = serie.str.replace("\n", " ")

228 serie = serie.str.replace('\\n', ' ')234 serie = serie.str.replace("\\n", " ")

229 serie = serie.str.replace(' ', ' ')235 serie = serie.str.replace(" ", " ")

230 serie = serie.str.replace(' ', ' ')236 serie = serie.str.replace(" ", " ")

231 return serie237 return serie

232```238```

233 239 


243import pandas as pd249import pandas as pd

244 250 

245# Create a list to store the text files251# Create a list to store the text files

246texts=[]252texts = []

247 253 

248# Get all the text files in the text directory254# Get all the text files in the text directory

249for file in os.listdir("text/" + domain + "/"):255for file in os.listdir("text/" + domain + "/"):


252 with open("text/" + domain + "/" + file, "r", encoding="UTF-8") as f:257 with open("text/" + domain + "/" + file, "r", encoding="UTF-8") as f:

253 text = f.read()258 text = f.read()

254 259 

255 # Omit the first 11 lines and the last 4 lines, then replace -, _, and #update with spaces.260 page_name = Path(file).stem

256 texts.append((file[11:-4].replace('-',' ').replace('_', ' ').replace('#update',''), text))261 domain_prefix = f"{domain}_"

262 if page_name.startswith(domain_prefix):

263 page_name = page_name[len(domain_prefix) :]

264 elif page_name == domain:

265 page_name = "index"

266 

267 # Replace -, _, and #update with spaces.

268 texts.append(

269 (

270 page_name.replace("-", " ").replace("_", " ").replace("#update", ""),

271 text,

272 )

273 )

257 274 

258# Create a dataframe from the list of texts275# Create a dataframe from the list of texts

259df = pd.DataFrame(texts, columns = ['fname', 'text'])276df = pd.DataFrame(texts, columns=["fname", "text"])

260 277 

261# Set the text column to be the raw text with the newlines removed278# Set the text column to be the raw text with the newlines removed

262df['text'] = df.fname + ". " + remove_newlines(df.text)279df["text"] = df.fname + ". " + remove_newlines(df.text)

263df.to_csv('processed/scraped.csv')280df.to_csv("processed/scraped.csv")

264df.head()281df.head()

265```282```

266 283 


277# Load the cl100k_base tokenizer which is designed to work with the ada-002 model294# Load the cl100k_base tokenizer which is designed to work with the ada-002 model

278tokenizer = tiktoken.get_encoding("cl100k_base")295tokenizer = tiktoken.get_encoding("cl100k_base")

279 296 

280df = pd.read_csv('processed/scraped.csv', index_col=0)297df = pd.read_csv("processed/scraped.csv", index_col=0)

281df.columns = ['title', 'text']298df.columns = ["title", "text"]

282 299 

283# Tokenize the text and save the number of tokens to a new column300# Tokenize the text and save the number of tokens to a new column

284df['n_tokens'] = df.text.apply(lambda x: len(tokenizer.encode(x)))301df["n_tokens"] = df.text.apply(lambda x: len(tokenizer.encode(x)))

285 302 

286# Visualize the distribution of the number of tokens per row using a histogram303# Visualize the distribution of the number of tokens per row using a histogram

287df.n_tokens.hist()304df.n_tokens.hist()


303```python320```python

304max_tokens = 500321max_tokens = 500

305 322 

323 

306# Function to split the text into chunks of a maximum number of tokens324# Function to split the text into chunks of a maximum number of tokens

307def split_into_many(text, max_tokens = max_tokens):325def split_into_many(text, max_tokens=max_tokens):

308 326 

309 # Split the text into sentences327 # Split the text into sentences

310 sentences = text.split('. ')328 sentences = text.split(". ")

311 329 

312 # Get the number of tokens for each sentence330 # Get the number of tokens for each sentence

313 n_tokens = [len(tokenizer.encode(" " + sentence)) for sentence in sentences]331 n_tokens = [len(tokenizer.encode(" " + sentence)) for sentence in sentences]


343 360 

344# Loop through the dataframe361# Loop through the dataframe

345for row in df.iterrows():362for row in df.iterrows():

346 

347 # If the text is None, go to the next row363 # If the text is None, go to the next row

348 if row[1]['text'] is None:364 if row[1]["text"] is None:

349 continue365 continue

350 366 

351 # If the number of tokens is greater than the max number of tokens, split the text into chunks367 # If the number of tokens is greater than the max number of tokens, split the text into chunks

352 if row[1]['n_tokens'] > max_tokens:368 if row[1]["n_tokens"] > max_tokens:

353 shortened += split_into_many(row[1]['text'])369 shortened += split_into_many(row[1]["text"])

354 370 

355 # Otherwise, add the text to the list of shortened texts371 # Otherwise, add the text to the list of shortened texts

356 else:372 else:

357 shortened.append( row[1]['text'] )373 shortened.append(row[1]["text"])

358```374```

359 375 

360 376 

361Visualizing the updated histogram again can help to confirm if the rows were successfully split into shortened sections.377Visualizing the updated histogram again can help to confirm if the rows were successfully split into shortened sections.

362 378 

363```python379```python

364df = pd.DataFrame(shortened, columns = ['text'])380df = pd.DataFrame(shortened, columns=["text"])

365df['n_tokens'] = df.text.apply(lambda x: len(tokenizer.encode(x)))381df["n_tokens"] = df.text.apply(lambda x: len(tokenizer.encode(x)))

366df.n_tokens.hist()382df.n_tokens.hist()

367```383```

368 384 


382```python398```python

383from openai import OpenAI399from openai import OpenAI

384 400 

385client = OpenAI(401client = OpenAI()

386 api_key=os.environ.get("OPENAI_API_KEY"),

387)

388 402 

389df['embeddings'] = df.text.apply(lambda x: client.embeddings.create(input=x, engine='text-embedding-ada-002')['data'][0]['embedding'])403df["embeddings"] = df.text.apply(

404 lambda x: client.embeddings.create(

405 input=x, model="text-embedding-3-small"

406 ).data[0].embedding

407)

390 408 

391df.to_csv('processed/embeddings.csv')409df.to_csv("processed/embeddings.csv")

392df.head()410df.head()

393```411```

394 412 


418 436 

419```python437```python

420import numpy as np438import numpy as np

421from openai.embeddings_utils import distances_from_embeddings

422 439 

423df=pd.read_csv('processed/embeddings.csv', index_col=0)440df = pd.read_csv("processed/embeddings.csv", index_col=0)

424df['embeddings'] = df['embeddings'].apply(eval).apply(np.array)441df["embeddings"] = df["embeddings"].apply(eval).apply(np.array)

425 442 

426df.head()443df.head()

427```444```


430The question needs to be converted to an embedding with a simple function, now that the data is ready. This is important because the search with embeddings compares the vector of numbers (which was the conversion of the raw text) using cosine distance. The vectors are likely related and might be the answer to the question if they are close in cosine distance. The OpenAI python package has a built in `distances_from_embeddings` function which is useful here.447The question needs to be converted to an embedding with a simple function, now that the data is ready. This is important because the search with embeddings compares the vector of numbers (which was the conversion of the raw text) using cosine distance. The vectors are likely related and might be the answer to the question if they are close in cosine distance. The OpenAI python package has a built in `distances_from_embeddings` function which is useful here.

431 448 

432```python449```python

433def create_context(450def create_context(question, df, max_len=1800, size="ada"):

434 question, df, max_len=1800, size="ada"

435):

436 """451 """

437 Create a context for a question by finding the most similar context from the dataframe452 Create a context for a question by finding the most similar context from the dataframe

438 """453 """

439 454 

440 # Get the embeddings for the question455 # Get the embeddings for the question

441 q_embeddings = client.embeddings.create(input=question, engine='text-embedding-ada-002')['data'][0]['embedding']456 q_embeddings = (

457 client.embeddings.create(input=question, model="text-embedding-3-small")

458 .data[0]

459 .embedding

460 )

442 461 

443 # Get the distances from the embeddings462 # Get the distances from the embeddings

444 df['distances'] = distances_from_embeddings(q_embeddings, df['embeddings'].values, distance_metric='cosine')463 df["distances"] = distances_from_embeddings(

445 464 q_embeddings, df["embeddings"].values, distance_metric="cosine"

465 )

446 466 

447 returns = []467 returns = []

448 cur_len = 0468 cur_len = 0

449 469 

450 # Sort by distance and add the text to the context until the context is too long470 # Sort by distance and add the text to the context until the context is too long

451 for i, row in df.sort_values('distances', ascending=True).iterrows():471 for _, row in df.sort_values("distances", ascending=True).iterrows():

452 

453 # Add the length of the text to the current length472 # Add the length of the text to the current length

454 cur_len += row['n_tokens'] + 4473 cur_len += row["n_tokens"] + 4

455 474 

456 # If the context is too long, break475 # If the context is too long, break

457 if cur_len > max_len:476 if cur_len > max_len:


482 size="ada",501 size="ada",

483 debug=False,502 debug=False,

484 max_tokens=150,503 max_tokens=150,

485 stop_sequence=None504 stop_sequence=None,

486):505):

487 """506 """

488 Answer a question based on the most similar context from the dataframe texts507 Answer a question based on the most similar context from the dataframe texts


502 # Create a completion using the question and context521 # Create a completion using the question and context

503 response = client.completions.create(522 response = client.completions.create(

504 model=model,523 model=model,

505 prompt=f"Answer the question based on the context below, and if the question can't be answered based on the context, say \"I don't know\"\n\nContext: {context}\n\n---\n\nQuestion: {question}\nAnswer:",524 prompt=(

525 "Answer the question based on the context below, and if the "

526 "question can't be answered based on the context, say "

527 '"I don\'t know"'

528 f"\n\nContext: {context}\n\n---\n\nQuestion: {question}\nAnswer:"

529 ),

506 temperature=0,530 temperature=0,

507 max_tokens=max_tokens,531 max_tokens=max_tokens,

508 top_p=1,532 top_p=1,


511 stop=stop_sequence,535 stop=stop_sequence,

512 )536 )

513 return response.choices[0].text.strip()537 return response.choices[0].text.strip()

514 except Exception as e:538 except Exception as error:

515 print(e)539 print(error)

516 return ""540 return ""

517```541```

518 542