docs
0
# Threads and Messages
1
2
Create conversational threads and manage messages within the Assistants API. Threads maintain conversation state and messages represent user inputs and assistant responses.
3
4
## Threads
5
6
### Create Thread
7
8
Create a new conversation thread.
9
10
```python { .api }
11
def create(
12
self,
13
*,
14
messages: list[dict] | Omit = omit,
15
metadata: dict[str, str] | Omit = omit,
16
tool_resources: dict | Omit = omit,
17
extra_headers: dict[str, str] | None = None,
18
extra_query: dict[str, object] | None = None,
19
extra_body: dict[str, object] | None = None,
20
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
21
) -> Thread:
22
"""
23
Create a conversation thread.
24
25
Args:
26
messages: Initial messages to add to thread.
27
[{"role": "user", "content": "Hello"}]
28
29
metadata: Key-value pairs (max 16). Keys max 64 chars, values max 512 chars.
30
31
tool_resources: Resources for tools.
32
- {"code_interpreter": {"file_ids": [...]}}
33
- {"file_search": {"vector_store_ids": [...]}}
34
35
extra_headers: Additional HTTP headers.
36
extra_query: Additional query parameters.
37
extra_body: Additional JSON fields.
38
timeout: Request timeout in seconds.
39
40
Returns:
41
Thread: Created thread.
42
"""
43
```
44
45
Usage examples:
46
47
```python
48
from openai import OpenAI
49
50
client = OpenAI()
51
52
# Create empty thread
53
thread = client.beta.threads.create()
54
print(f"Thread ID: {thread.id}")
55
56
# Create with initial messages
57
thread = client.beta.threads.create(
58
messages=[
59
{"role": "user", "content": "Hello!"},
60
{"role": "user", "content": "How are you?"}
61
]
62
)
63
64
# With metadata
65
thread = client.beta.threads.create(
66
metadata={
67
"user_id": "user-123",
68
"session": "abc"
69
}
70
)
71
72
# With tool resources
73
thread = client.beta.threads.create(
74
tool_resources={
75
"file_search": {
76
"vector_store_ids": ["vs_abc123"]
77
}
78
}
79
)
80
```
81
82
### Retrieve Thread
83
84
Get thread details.
85
86
```python { .api }
87
def retrieve(
88
self,
89
thread_id: str,
90
*,
91
extra_headers: dict[str, str] | None = None,
92
extra_query: dict[str, object] | None = None,
93
extra_body: dict[str, object] | None = None,
94
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
95
) -> Thread:
96
"""Get thread details."""
97
```
98
99
### Update Thread
100
101
Modify thread metadata.
102
103
```python { .api }
104
def update(
105
self,
106
thread_id: str,
107
*,
108
metadata: dict[str, str] | Omit = omit,
109
tool_resources: dict | Omit = omit,
110
extra_headers: dict[str, str] | None = None,
111
extra_query: dict[str, object] | None = None,
112
extra_body: dict[str, object] | None = None,
113
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
114
) -> Thread:
115
"""Update thread properties."""
116
```
117
118
### Delete Thread
119
120
Delete a thread.
121
122
```python { .api }
123
def delete(
124
self,
125
thread_id: str,
126
*,
127
extra_headers: dict[str, str] | None = None,
128
extra_query: dict[str, object] | None = None,
129
extra_body: dict[str, object] | None = None,
130
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
131
) -> ThreadDeleted:
132
"""Delete a thread."""
133
```
134
135
### Create and Run
136
137
Create thread and immediately run assistant (convenience method).
138
139
```python { .api }
140
def create_and_run(
141
self,
142
*,
143
assistant_id: str,
144
instructions: str | Omit = omit,
145
metadata: dict[str, str] | Omit = omit,
146
model: str | Omit = omit,
147
thread: dict | Omit = omit,
148
tools: list[dict] | Omit = omit,
149
stream: bool | Omit = omit,
150
temperature: float | Omit = omit,
151
tool_choice: str | dict | Omit = omit,
152
top_p: float | Omit = omit,
153
extra_headers: dict[str, str] | None = None,
154
extra_query: dict[str, object] | None = None,
155
extra_body: dict[str, object] | None = None,
156
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
157
) -> Run:
158
"""
159
Create thread and run assistant in one call.
160
161
Args:
162
assistant_id: The assistant ID.
163
instructions: Override assistant instructions.
164
metadata: Thread metadata.
165
model: Override assistant model.
166
thread: Thread configuration including messages.
167
tools: Override assistant tools.
168
stream: Enable streaming.
169
temperature: Sampling temperature.
170
tool_choice: Tool choice configuration.
171
top_p: Nucleus sampling.
172
173
Returns:
174
Run: Created run.
175
"""
176
```
177
178
Usage example:
179
180
```python
181
# Create thread and run
182
run = client.beta.threads.create_and_run(
183
assistant_id="asst_abc123",
184
thread={
185
"messages": [
186
{"role": "user", "content": "Hello!"}
187
]
188
}
189
)
190
191
print(f"Run ID: {run.id}")
192
print(f"Thread ID: {run.thread_id}")
193
```
194
195
### Create and Run with Polling
196
197
Create a thread, start a run, and automatically poll until the run reaches a terminal state.
198
199
```python { .api }
200
def create_and_run_poll(
201
self,
202
*,
203
assistant_id: str,
204
instructions: str | Omit = omit,
205
max_completion_tokens: int | Omit = omit,
206
max_prompt_tokens: int | Omit = omit,
207
metadata: dict[str, str] | Omit = omit,
208
model: str | Omit = omit,
209
parallel_tool_calls: bool | Omit = omit,
210
response_format: dict | Omit = omit,
211
temperature: float | Omit = omit,
212
thread: dict | Omit = omit,
213
tool_choice: str | dict | Omit = omit,
214
tool_resources: dict | Omit = omit,
215
tools: list[dict] | Omit = omit,
216
top_p: float | Omit = omit,
217
truncation_strategy: dict | Omit = omit,
218
poll_interval_ms: int | Omit = omit,
219
extra_headers: dict[str, str] | None = None,
220
extra_query: dict[str, object] | None = None,
221
extra_body: dict[str, object] | None = None,
222
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
223
) -> Run:
224
"""
225
Create a thread, start a run, and poll for completion.
226
227
Helper method that automatically polls the run until it reaches a terminal state
228
(completed, failed, cancelled, expired). More information on Run lifecycles:
229
https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps
230
231
Args:
232
assistant_id: The assistant ID to run.
233
poll_interval_ms: Polling interval in milliseconds (defaults to 1000ms).
234
thread: Thread configuration including messages.
235
instructions: Override assistant instructions.
236
tools: Override assistant tools.
237
metadata: Thread metadata.
238
(Additional parameters same as create_and_run)
239
240
Returns:
241
Run: Completed run in terminal state.
242
"""
243
```
244
245
Usage example:
246
247
```python
248
# Create and poll automatically
249
run = client.beta.threads.create_and_run_poll(
250
assistant_id="asst_abc123",
251
thread={
252
"messages": [{"role": "user", "content": "Explain quantum computing"}]
253
},
254
poll_interval_ms=500 # Poll every 500ms
255
)
256
257
# Run is guaranteed to be in terminal state
258
print(f"Status: {run.status}") # completed, failed, cancelled, or expired
259
```
260
261
### Create and Run with Streaming
262
263
Create a thread, start a run, and stream the response back in real-time.
264
265
```python { .api }
266
def create_and_run_stream(
267
self,
268
*,
269
assistant_id: str,
270
instructions: str | Omit = omit,
271
max_completion_tokens: int | Omit = omit,
272
max_prompt_tokens: int | Omit = omit,
273
metadata: dict[str, str] | Omit = omit,
274
model: str | Omit = omit,
275
parallel_tool_calls: bool | Omit = omit,
276
response_format: dict | Omit = omit,
277
temperature: float | Omit = omit,
278
thread: dict | Omit = omit,
279
tool_choice: str | dict | Omit = omit,
280
tool_resources: dict | Omit = omit,
281
tools: list[dict] | Omit = omit,
282
top_p: float | Omit = omit,
283
truncation_strategy: dict | Omit = omit,
284
event_handler: AssistantEventHandler | None = None,
285
extra_headers: dict[str, str] | None = None,
286
extra_query: dict[str, object] | None = None,
287
extra_body: dict[str, object] | None = None,
288
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
289
) -> AssistantStreamManager[AssistantEventHandler]:
290
"""
291
Create a thread and stream the run back in real-time.
292
293
Args:
294
assistant_id: The assistant ID to run.
295
event_handler: Optional custom event handler for processing stream events.
296
thread: Thread configuration including messages.
297
instructions: Override assistant instructions.
298
(Additional parameters same as create_and_run)
299
300
Returns:
301
AssistantStreamManager: Stream manager for handling assistant events.
302
"""
303
```
304
305
Usage example:
306
307
```python
308
# Stream with default handler
309
with client.beta.threads.create_and_run_stream(
310
assistant_id="asst_abc123",
311
thread={
312
"messages": [{"role": "user", "content": "Tell me a story"}]
313
}
314
) as stream:
315
for event in stream:
316
if event.event == 'thread.message.delta':
317
print(event.data.delta.content[0].text.value, end='', flush=True)
318
319
# With custom event handler
320
from openai import AssistantEventHandler
321
322
class MyHandler(AssistantEventHandler):
323
def on_text_delta(self, delta, snapshot):
324
print(delta.value, end='', flush=True)
325
326
with client.beta.threads.create_and_run_stream(
327
assistant_id="asst_abc123",
328
thread={
329
"messages": [{"role": "user", "content": "Hello"}]
330
},
331
event_handler=MyHandler()
332
) as stream:
333
stream.until_done()
334
```
335
336
## Messages
337
338
### Create Message
339
340
Add a message to a thread.
341
342
```python { .api }
343
def create(
344
self,
345
thread_id: str,
346
*,
347
role: Literal["user", "assistant"],
348
content: str | list[dict],
349
attachments: list[dict] | Omit = omit,
350
metadata: dict[str, str] | Omit = omit,
351
extra_headers: dict[str, str] | None = None,
352
extra_query: dict[str, object] | None = None,
353
extra_body: dict[str, object] | None = None,
354
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
355
) -> Message:
356
"""
357
Add a message to a thread.
358
359
Args:
360
thread_id: The thread ID.
361
362
role: Message role. "user" or "assistant".
363
364
content: Message content. Can be:
365
- String: "Hello"
366
- List with text/images: [{"type": "text", "text": "..."}, {"type": "image_url", "image_url": {...}}]
367
368
attachments: File attachments with tools.
369
[{"file_id": "file-abc", "tools": [{"type": "code_interpreter"}]}]
370
371
metadata: Key-value pairs.
372
373
Returns:
374
Message: Created message.
375
"""
376
```
377
378
Usage examples:
379
380
```python
381
# Simple text message
382
message = client.beta.threads.messages.create(
383
thread_id="thread_abc123",
384
role="user",
385
content="What is the weather today?"
386
)
387
388
# With attachments
389
message = client.beta.threads.messages.create(
390
thread_id="thread_abc123",
391
role="user",
392
content="Analyze this data",
393
attachments=[
394
{
395
"file_id": "file-abc123",
396
"tools": [{"type": "code_interpreter"}]
397
}
398
]
399
)
400
401
# Multimodal message (text + image)
402
message = client.beta.threads.messages.create(
403
thread_id="thread_abc123",
404
role="user",
405
content=[
406
{"type": "text", "text": "What's in this image?"},
407
{
408
"type": "image_url",
409
"image_url": {"url": "https://example.com/image.jpg"}
410
}
411
]
412
)
413
414
# With metadata
415
message = client.beta.threads.messages.create(
416
thread_id="thread_abc123",
417
role="user",
418
content="Question here",
419
metadata={"source": "web_ui"}
420
)
421
```
422
423
### Retrieve Message
424
425
Get message details.
426
427
```python { .api }
428
def retrieve(
429
self,
430
thread_id: str,
431
message_id: str,
432
*,
433
extra_headers: dict[str, str] | None = None,
434
extra_query: dict[str, object] | None = None,
435
extra_body: dict[str, object] | None = None,
436
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
437
) -> Message:
438
"""Get message details."""
439
```
440
441
### Update Message
442
443
Modify message metadata.
444
445
```python { .api }
446
def update(
447
self,
448
thread_id: str,
449
message_id: str,
450
*,
451
metadata: dict[str, str] | Omit = omit,
452
extra_headers: dict[str, str] | None = None,
453
extra_query: dict[str, object] | None = None,
454
extra_body: dict[str, object] | None = None,
455
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
456
) -> Message:
457
"""Update message metadata."""
458
```
459
460
### List Messages
461
462
List messages in a thread.
463
464
```python { .api }
465
def list(
466
self,
467
thread_id: str,
468
*,
469
after: str | Omit = omit,
470
before: str | Omit = omit,
471
limit: int | Omit = omit,
472
order: Literal["asc", "desc"] | Omit = omit,
473
run_id: str | Omit = omit,
474
extra_headers: dict[str, str] | None = None,
475
extra_query: dict[str, object] | None = None,
476
extra_body: dict[str, object] | None = None,
477
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
478
) -> SyncCursorPage[Message]:
479
"""
480
List messages in a thread.
481
482
Args:
483
thread_id: The thread ID.
484
after: Cursor for next page.
485
before: Cursor for previous page.
486
limit: Number to retrieve (max 100). Default 20.
487
order: Sort order. "asc" or "desc". Default "desc" (newest first).
488
run_id: Filter by run ID.
489
490
Returns:
491
SyncCursorPage[Message]: Paginated messages.
492
"""
493
```
494
495
Usage example:
496
497
```python
498
# List all messages
499
messages = client.beta.threads.messages.list(thread_id="thread_abc123")
500
501
for message in messages:
502
print(f"{message.role}: {message.content[0].text.value}")
503
504
# List in chronological order
505
messages = client.beta.threads.messages.list(
506
thread_id="thread_abc123",
507
order="asc"
508
)
509
510
# Pagination
511
page1 = client.beta.threads.messages.list(thread_id="thread_abc123", limit=10)
512
page2 = client.beta.threads.messages.list(
513
thread_id="thread_abc123",
514
limit=10,
515
after=page1.data[-1].id
516
)
517
```
518
519
### Delete Message
520
521
Delete a message.
522
523
```python { .api }
524
def delete(
525
self,
526
thread_id: str,
527
message_id: str,
528
*,
529
extra_headers: dict[str, str] | None = None,
530
extra_query: dict[str, object] | None = None,
531
extra_body: dict[str, object] | None = None,
532
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
533
) -> MessageDeleted:
534
"""Delete a message."""
535
```
536
537
## Types
538
539
```python { .api }
540
from typing import Literal
541
from pydantic import BaseModel
542
543
class Thread(BaseModel):
544
"""Conversation thread."""
545
id: str
546
created_at: int
547
metadata: dict[str, str] | None
548
object: Literal["thread"]
549
tool_resources: dict | None
550
551
class ThreadDeleted(BaseModel):
552
"""Deletion confirmation."""
553
id: str
554
deleted: bool
555
object: Literal["thread.deleted"]
556
557
class Message(BaseModel):
558
"""Thread message."""
559
id: str
560
assistant_id: str | None
561
attachments: list[dict] | None
562
completed_at: int | None
563
content: list[MessageContent]
564
created_at: int
565
incomplete_at: int | None
566
incomplete_details: dict | None
567
metadata: dict[str, str] | None
568
object: Literal["thread.message"]
569
role: Literal["user", "assistant"]
570
run_id: str | None
571
status: Literal["in_progress", "incomplete", "completed"]
572
thread_id: str
573
574
class MessageContent(BaseModel):
575
"""Message content part."""
576
type: Literal["text", "image_file", "image_url"]
577
text: TextContent | None
578
image_file: ImageFile | None
579
image_url: ImageURL | None
580
581
class TextContent(BaseModel):
582
"""Text content."""
583
value: str
584
annotations: list[Annotation]
585
586
class ImageFile(BaseModel):
587
"""Image file reference."""
588
file_id: str
589
detail: Literal["auto", "low", "high"] | None
590
591
class ImageURL(BaseModel):
592
"""Image URL reference."""
593
url: str
594
detail: Literal["auto", "low", "high"] | None
595
596
class Annotation(BaseModel):
597
"""Content annotation (file citation/path)."""
598
type: Literal["file_citation", "file_path"]
599
text: str
600
start_index: int
601
end_index: int
602
file_citation: dict | None
603
file_path: dict | None
604
605
class MessageDeleted(BaseModel):
606
"""Deletion confirmation."""
607
id: str
608
deleted: bool
609
object: Literal["thread.message.deleted"]
610
```
611
612
## Complete Example
613
614
```python
615
from openai import OpenAI
616
617
client = OpenAI()
618
619
# 1. Create assistant
620
assistant = client.beta.assistants.create(
621
name="Helper",
622
model="gpt-4",
623
instructions="You are helpful."
624
)
625
626
# 2. Create thread
627
thread = client.beta.threads.create()
628
629
# 3. Add user message
630
message = client.beta.threads.messages.create(
631
thread_id=thread.id,
632
role="user",
633
content="Tell me a joke"
634
)
635
636
# 4. Run assistant
637
run = client.beta.threads.runs.create(
638
thread_id=thread.id,
639
assistant_id=assistant.id
640
)
641
642
# 5. Wait for completion
643
import time
644
645
while run.status not in ["completed", "failed"]:
646
time.sleep(1)
647
run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id)
648
649
# 6. Get messages
650
messages = client.beta.threads.messages.list(thread_id=thread.id)
651
652
for message in messages:
653
role = message.role
654
content = message.content[0].text.value
655
print(f"{role}: {content}")
656
```
657
658
## Async Usage
659
660
```python
661
import asyncio
662
from openai import AsyncOpenAI
663
664
async def create_conversation():
665
client = AsyncOpenAI()
666
667
thread = await client.beta.threads.create()
668
669
message = await client.beta.threads.messages.create(
670
thread_id=thread.id,
671
role="user",
672
content="Hello!"
673
)
674
675
return thread.id, message.id
676
677
thread_id, message_id = asyncio.run(create_conversation())
678
```
679