openai/chatkit-python

Public

mirrored fromhttps://github.com/openai/chatkit-pythonAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.6.5

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

docs/guides/browse-past-threads.md

139lines · modecode

1# Let users browse past threads
2
3Let users return to previous conversations, see readable titles in a history list, and decide which threads can be continued.
4
5## Enable thread history in the client
6
7The ChatKit React hooks support a built-in history view that lists past threads. History is enabled by default, but you can configure it explicitly when you create your ChatKit controller:
8
9```tsx
10const chatkit = useChatKit({
11 // ...
12 history: {
13 enabled: true,
14 showDelete: true,
15 showRename: true,
16 },
17});
18```
19
20With `history.enabled: true`, ChatKit.js will:
21
22- Fetch threads from your ChatKit server.
23- Show them in a history list using `thread.title` when available.
24- Let users click a past thread to load its items and continue the conversation.
25- Let users delete and rename threads.
26
27Set `history.enabled: false` if you want a single-thread, stateless chat experience with no history UI.
28
29## Show readable titles in history
30
31Threads start untitled. Give them short, descriptive titles so the history list is easy to scan.
32
33### Set a title directly
34
35Set `thread.title` on the server and persist it with your store:
36
37```python
38from chatkit.server import ChatKitServer
39
40
41class MyChatKitServer(ChatKitServer[RequestContext]):
42 async def respond(...):
43 ...
44 if not thread.title:
45 thread.title = "Order #1234"
46 await self.store.save_thread(thread, context=context)
47```
48
49ChatKit will emit a `ThreadUpdatedEvent` so connected clients update the title in their history views.
50
51### Auto-generate a title after the first turn
52
53Generate a concise title after the first assistant turn once you have enough context. Skip if the thread already has a title or if there isn’t enough content to summarize.
54
55```python
56class MyChatKitServer(ChatKitServer[RequestContext]):
57 async def respond(...):
58 updating_thread_title = asyncio.create_task(
59 self._maybe_update_thread_title(thread, context)
60 )
61
62 # Stream your main response
63 async for event in stream_agent_response(agent_context, result):
64 yield event
65
66 # Await so the title update streams back as a ThreadUpdatedEvent
67 await updating_thread_title
68
69 async def _maybe_update_thread_title(
70 self, thread: ThreadMetadata, context: RequestContext
71 ) -> None:
72 if thread.title is not None:
73 return
74 items = await self.store.load_thread_items(
75 thread.id,
76 after=None,
77 limit=6,
78 order="desc",
79 context=context,
80 )
81 thread.title = await generate_short_title(items.data) # your model call
82 await self.store.save_thread(thread, context=context)
83```
84
85Use any model call you like for `generate_short_title`: run a tiny Agent, a simple completion, or your own heuristic. Keep titles brief (for example, 3–6 words).
86
87## Decide which threads can be continued
88
89By default, users can continue any past thread: selecting it in the history view loads its items and reuses the same thread when they send a new message.
90
91Use `thread.status` to mark conversations that should no longer accept new messages. Locked and closed threads still appear in history, but the composer UI changes.
92
93There are two ways to stop new user messages: temporarily lock a thread or permanently close it when the conversation is finished.
94
95| State | When to use | Input UI | What the user sees |
96|---------|------------------------------------------------|------------------------------------------------|--------------------|
97| Locked | Temporary pause for moderation or admin action | Composer stays on screen but is disabled; the placeholder shows the lock reason. | The reason for the lock in the disabled composer. |
98| Closed | Final state when the conversation is done | The input UI is replaced with an informational banner. | A static default message or a custom reason, if provided. |
99
100### Update thread status (lock, close, or re-open)
101
102
103```python
104from chatkit.types import ActiveStatus, LockedStatus, ClosedStatus
105
106# lock (temporary pause)
107thread.status = LockedStatus(reason="Escalated to support.")
108await store.save_thread(thread, context=context)
109
110# close (final state)
111thread.status = ClosedStatus(reason="Resolved.")
112await store.save_thread(thread, context=context)
113
114# re-open
115thread.status = ActiveStatus()
116await store.save_thread(thread, context=context)
117```
118
119When you persist a new status during `respond`, ChatKit emits a `ThreadUpdatedEvent` so all viewers see the updated state.
120
121You can also update the thread status from a custom client-facing endpoint that updates the store directly (outside of the ChatKit server request flow). If the user is currently viewing the thread, have the client call `chatkit.fetchUpdates()` after the status is persisted so the UI picks up the latest thread state.
122
123### Block server-side work when locked or closed
124
125Thread status only affects the composer UI; `ChatKitServer` does not automatically reject actions, tool calls, or imperative message adds. Your integration should short-circuit handlers when a thread is disabled:
126
127```python
128class MyChatKitServer(...):
129 async def respond(thread, input_user_message, context):
130 if thread.status.type in {"locked", "closed"}:
131 return
132 # normal processing
133
134 async def action(thread, action, sender, context):
135 if thread.status.type in {"locked", "closed"}:
136 return
137 # normal processing
138```
139
140