openai/chatkit-python

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.6.2

Branches

Tags

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

Clone

HTTPS

Download ZIP

chatkit/types.py

999lines · modecode

1from __future__ import annotations
2
3from datetime import datetime
4from typing import Any, Generic, Literal
5
6from pydantic import (
7 AnyUrl,
8 BaseModel,
9 Field,
10 SerializationInfo,
11 model_serializer,
12)
13from typing_extensions import Annotated, TypeIs, TypeVar
14
15from chatkit.errors import ErrorCode
16
17from .actions import Action
18from .icons import IconName
19from .widgets import WidgetComponent, WidgetRoot
20
21T = TypeVar("T")
22
23
24class Page(BaseModel, Generic[T]):
25 """Paginated collection of records returned from the API."""
26
27 data: list[T] = []
28 has_more: bool = False
29 after: str | None = None
30
31
32### REQUEST TYPES
33
34
35class BaseReq(BaseModel):
36 """Base class for all request payloads."""
37
38 metadata: dict[str, Any] = Field(default_factory=dict)
39 """Arbitrary integration-specific metadata."""
40
41
42class ThreadsGetByIdReq(BaseReq):
43 """Request to fetch a single thread by its identifier."""
44
45 type: Literal["threads.get_by_id"] = "threads.get_by_id"
46 params: ThreadGetByIdParams
47
48
49class ThreadGetByIdParams(BaseModel):
50 """Parameters for retrieving a thread by id."""
51
52 thread_id: str
53
54
55class ThreadsCreateReq(BaseReq):
56 """Request to create a new thread from a user message."""
57
58 type: Literal["threads.create"] = "threads.create"
59 params: ThreadCreateParams
60
61
62class ThreadCreateParams(BaseModel):
63 """User input required to create a thread."""
64
65 input: UserMessageInput
66
67
68class ThreadListParams(BaseModel):
69 """Pagination parameters for listing threads."""
70
71 limit: int | None = None
72 order: Literal["asc", "desc"] = "desc"
73 after: str | None = None
74
75
76class ThreadsListReq(BaseReq):
77 """Request to list threads."""
78
79 type: Literal["threads.list"] = "threads.list"
80 params: ThreadListParams
81
82
83class ThreadsAddUserMessageReq(BaseReq):
84 """Request to append a user message to a thread."""
85
86 type: Literal["threads.add_user_message"] = "threads.add_user_message"
87 params: ThreadAddUserMessageParams
88
89
90class ThreadAddUserMessageParams(BaseModel):
91 """Parameters for adding a user message to a thread."""
92
93 input: UserMessageInput
94 thread_id: str
95
96
97class ThreadsAddClientToolOutputReq(BaseReq):
98 """Request to add a client tool's output to a thread."""
99
100 type: Literal["threads.add_client_tool_output"] = "threads.add_client_tool_output"
101 params: ThreadAddClientToolOutputParams
102
103
104class ThreadAddClientToolOutputParams(BaseModel):
105 """Parameters for recording tool output in a thread."""
106
107 thread_id: str
108 result: Any
109
110
111class ThreadsCustomActionReq(BaseReq):
112 """Request to execute a custom action within a thread."""
113
114 type: Literal["threads.custom_action"] = "threads.custom_action"
115 params: ThreadCustomActionParams
116
117
118class ThreadsSyncCustomActionReq(BaseReq):
119 """Request to execute a custom action and return a single item update."""
120
121 type: Literal["threads.sync_custom_action"] = "threads.sync_custom_action"
122 params: ThreadCustomActionParams
123
124
125class ThreadCustomActionParams(BaseModel):
126 """Parameters describing the custom action to execute."""
127
128 thread_id: str
129 item_id: str | None = None
130 action: Action[str, Any]
131
132
133class ThreadsRetryAfterItemReq(BaseReq):
134 """Request to retry processing after a specific thread item."""
135
136 type: Literal["threads.retry_after_item"] = "threads.retry_after_item"
137 params: ThreadRetryAfterItemParams
138
139
140class ThreadRetryAfterItemParams(BaseModel):
141 """Parameters specifying which item to retry."""
142
143 thread_id: str
144 item_id: str
145
146
147class ItemsFeedbackReq(BaseReq):
148 """Request to submit feedback on specific items."""
149
150 type: Literal["items.feedback"] = "items.feedback"
151 params: ItemFeedbackParams
152
153
154class ItemFeedbackParams(BaseModel):
155 """Parameters describing feedback targets and sentiment."""
156
157 thread_id: str
158 item_ids: list[str]
159 kind: FeedbackKind
160
161
162class AttachmentsDeleteReq(BaseReq):
163 """Request to remove an attachment."""
164
165 type: Literal["attachments.delete"] = "attachments.delete"
166 params: AttachmentDeleteParams
167
168
169class AttachmentDeleteParams(BaseModel):
170 """Parameters identifying an attachment to delete."""
171
172 attachment_id: str
173
174
175class AttachmentsCreateReq(BaseReq):
176 """Request to register a new attachment."""
177
178 type: Literal["attachments.create"] = "attachments.create"
179 params: AttachmentCreateParams
180
181
182class AttachmentCreateParams(BaseModel):
183 """Metadata needed to initialize an attachment."""
184
185 name: str
186 size: int
187 mime_type: str
188
189
190class InputTranscribeReq(BaseReq):
191 """Request to transcribe an audio payload into text."""
192
193 type: Literal["input.transcribe"] = "input.transcribe"
194 params: InputTranscribeParams
195
196
197class InputTranscribeParams(BaseModel):
198 """Parameters for speech transcription."""
199
200 audio_base64: str
201 """Base64-encoded audio bytes."""
202
203 mime_type: str
204 """Raw MIME type for the audio payload, e.g. "audio/webm;codecs=opus"."""
205
206
207class AudioInput(BaseModel):
208 """Audio input data for transcription."""
209
210 data: bytes
211 """Audio data bytes."""
212
213 mime_type: str
214 """Raw MIME type for the audio payload, e.g. "audio/webm;codecs=opus"."""
215
216 @property
217 def media_type(self) -> str:
218 """Media type for the audio payload, e.g. "audio/webm"."""
219 return self.mime_type.split(";", 1)[0]
220
221
222class TranscriptionResult(BaseModel):
223 """Input speech transcription result."""
224
225 text: str
226
227
228class ItemsListReq(BaseReq):
229 """Request to list items inside a thread."""
230
231 type: Literal["items.list"] = "items.list"
232 params: ItemsListParams
233
234
235class ItemsListParams(BaseModel):
236 """Pagination parameters for listing thread items."""
237
238 thread_id: str
239 limit: int | None = None
240 order: Literal["asc", "desc"] = "desc"
241 after: str | None = None
242
243
244class ThreadsUpdateReq(BaseReq):
245 """Request to update thread metadata."""
246
247 type: Literal["threads.update"] = "threads.update"
248 params: ThreadUpdateParams
249
250
251class ThreadUpdateParams(BaseModel):
252 """Parameters for updating a thread's properties."""
253
254 thread_id: str
255 title: str
256
257
258class ThreadsDeleteReq(BaseReq):
259 """Request to delete a thread."""
260
261 type: Literal["threads.delete"] = "threads.delete"
262 params: ThreadDeleteParams
263
264
265class ThreadDeleteParams(BaseModel):
266 """Parameters identifying a thread to delete."""
267
268 thread_id: str
269
270
271StreamingReq = (
272 ThreadsCreateReq
273 | ThreadsAddUserMessageReq
274 | ThreadsAddClientToolOutputReq
275 | ThreadsRetryAfterItemReq
276 | ThreadsCustomActionReq
277)
278"""Union of request types that produce streaming responses."""
279
280
281NonStreamingReq = (
282 ThreadsGetByIdReq
283 | ThreadsListReq
284 | ItemsListReq
285 | ItemsFeedbackReq
286 | AttachmentsCreateReq
287 | AttachmentsDeleteReq
288 | ThreadsUpdateReq
289 | ThreadsDeleteReq
290 | InputTranscribeReq
291 | ThreadsSyncCustomActionReq
292)
293"""Union of request types that yield immediate responses."""
294
295
296ChatKitReq = Annotated[
297 StreamingReq | NonStreamingReq,
298 Field(discriminator="type"),
299]
300
301
302def is_streaming_req(request: ChatKitReq) -> TypeIs[StreamingReq]:
303 """Return True if the given request should be processed as streaming."""
304 return isinstance(
305 request,
306 (
307 ThreadsCreateReq,
308 ThreadsAddUserMessageReq,
309 ThreadsRetryAfterItemReq,
310 ThreadsAddClientToolOutputReq,
311 ThreadsCustomActionReq,
312 ),
313 )
314
315
316### THREAD STREAM EVENT TYPES
317
318
319class ThreadCreatedEvent(BaseModel):
320 """Event emitted when a thread is created."""
321
322 type: Literal["thread.created"] = "thread.created"
323 thread: Thread
324
325
326class ThreadUpdatedEvent(BaseModel):
327 """Event emitted when a thread is updated."""
328
329 type: Literal["thread.updated"] = "thread.updated"
330 thread: Thread
331
332
333class ThreadItemAddedEvent(BaseModel):
334 """Event emitted when a new item is added to a thread."""
335
336 type: Literal["thread.item.added"] = "thread.item.added"
337 item: ThreadItem
338
339
340class ThreadItemUpdatedEvent(BaseModel):
341 """Event describing an update to an existing thread item."""
342
343 type: Literal["thread.item.updated"] = "thread.item.updated"
344 item_id: str
345 update: ThreadItemUpdate
346
347
348# Type alias for backwards compatibility
349ThreadItemUpdated = ThreadItemUpdatedEvent
350
351
352class ThreadItemDoneEvent(BaseModel):
353 """Event emitted when a thread item is marked complete."""
354
355 type: Literal["thread.item.done"] = "thread.item.done"
356 item: ThreadItem
357
358
359class ThreadItemRemovedEvent(BaseModel):
360 """Event emitted when a thread item is removed."""
361
362 type: Literal["thread.item.removed"] = "thread.item.removed"
363 item_id: str
364
365
366class ThreadItemReplacedEvent(BaseModel):
367 """Event emitted when a thread item is replaced."""
368
369 type: Literal["thread.item.replaced"] = "thread.item.replaced"
370 item: ThreadItem
371
372
373class StreamOptions(BaseModel):
374 """Settings that control runtime stream behavior."""
375
376 allow_cancel: bool
377 """Allow the client to request cancellation mid-stream."""
378
379
380class StreamOptionsEvent(BaseModel):
381 """Event emitted to set stream options at runtime."""
382
383 type: Literal["stream_options"] = "stream_options"
384 stream_options: StreamOptions
385
386
387class ProgressUpdateEvent(BaseModel):
388 """Event providing incremental progress from the assistant."""
389
390 type: Literal["progress_update"] = "progress_update"
391 icon: IconName | None = None
392 text: str
393
394
395class ClientEffectEvent(BaseModel):
396 """Event emitted to trigger a client side-effect."""
397
398 type: Literal["client_effect"] = "client_effect"
399 name: str
400 data: dict[str, Any] = Field(default_factory=dict)
401
402
403class ErrorEvent(BaseModel):
404 """Event indicating an error occurred while processing a thread."""
405
406 type: Literal["error"] = "error"
407 code: ErrorCode | Literal["custom"] = Field(default="custom")
408 message: str | None = None
409 allow_retry: bool = Field(default=False)
410
411
412class NoticeEvent(BaseModel):
413 """Event conveying a user-facing notice."""
414
415 type: Literal["notice"] = "notice"
416 level: Literal["info", "warning", "danger"]
417 message: str
418 """
419 Supports markdown e.g. "You've reached your limit of 100 messages. [Upgrade](https://...) to a paid plan."
420 """
421 title: str | None = None
422
423
424ThreadStreamEvent = Annotated[
425 ThreadCreatedEvent
426 | ThreadUpdatedEvent
427 | ThreadItemDoneEvent
428 | ThreadItemAddedEvent
429 | ThreadItemUpdated
430 | ThreadItemRemovedEvent
431 | ThreadItemReplacedEvent
432 | StreamOptionsEvent
433 | ProgressUpdateEvent
434 | ClientEffectEvent
435 | ErrorEvent
436 | NoticeEvent,
437 Field(discriminator="type"),
438]
439"""Union of all streaming events emitted to clients."""
440
441### THREAD ITEM UPDATE TYPES
442
443
444class AssistantMessageContentPartAdded(BaseModel):
445 """Event emitted when new assistant content is appended."""
446
447 type: Literal["assistant_message.content_part.added"] = (
448 "assistant_message.content_part.added"
449 )
450 content_index: int
451 content: AssistantMessageContent
452
453
454class AssistantMessageContentPartTextDelta(BaseModel):
455 """Event carrying incremental assistant text output."""
456
457 type: Literal["assistant_message.content_part.text_delta"] = (
458 "assistant_message.content_part.text_delta"
459 )
460 content_index: int
461 delta: str
462
463
464class AssistantMessageContentPartAnnotationAdded(BaseModel):
465 """Event announcing a new annotation on assistant content."""
466
467 type: Literal["assistant_message.content_part.annotation_added"] = (
468 "assistant_message.content_part.annotation_added"
469 )
470 content_index: int
471 annotation_index: int
472 annotation: Annotation
473
474
475class AssistantMessageContentPartDone(BaseModel):
476 """Event indicating an assistant content part is finalized."""
477
478 type: Literal["assistant_message.content_part.done"] = (
479 "assistant_message.content_part.done"
480 )
481 content_index: int
482 content: AssistantMessageContent
483
484
485class WidgetStreamingTextValueDelta(BaseModel):
486 """Event streaming widget text deltas."""
487
488 type: Literal["widget.streaming_text.value_delta"] = (
489 "widget.streaming_text.value_delta"
490 )
491 component_id: str
492 delta: str
493 done: bool
494
495
496class WidgetRootUpdated(BaseModel):
497 """Event published when the widget root changes."""
498
499 type: Literal["widget.root.updated"] = "widget.root.updated"
500 widget: WidgetRoot
501
502
503class WidgetComponentUpdated(BaseModel):
504 """Event emitted when a widget component updates."""
505
506 type: Literal["widget.component.updated"] = "widget.component.updated"
507 component_id: str
508 component: WidgetComponent
509
510
511class WorkflowTaskAdded(BaseModel):
512 """Event emitted when a workflow task is added."""
513
514 type: Literal["workflow.task.added"] = "workflow.task.added"
515 task_index: int
516 task: Task
517
518
519class WorkflowTaskUpdated(BaseModel):
520 """Event emitted when a workflow task is updated."""
521
522 type: Literal["workflow.task.updated"] = "workflow.task.updated"
523 task_index: int
524 task: Task
525
526
527class GeneratedImageUpdated(BaseModel):
528 """Event emitted when a generated image is updated."""
529
530 type: Literal["generated_image.updated"] = "generated_image.updated"
531 image: GeneratedImage
532 progress: float | None = None
533
534
535ThreadItemUpdate = (
536 AssistantMessageContentPartAdded
537 | AssistantMessageContentPartTextDelta
538 | AssistantMessageContentPartAnnotationAdded
539 | AssistantMessageContentPartDone
540 | WidgetStreamingTextValueDelta
541 | WidgetComponentUpdated
542 | WidgetRootUpdated
543 | WorkflowTaskAdded
544 | WorkflowTaskUpdated
545 | GeneratedImageUpdated
546)
547"""Union of possible updates applied to thread items."""
548
549
550class SyncCustomActionResponse(BaseModel):
551 """Single thread item update returned by a sync custom action."""
552 updated_item: ThreadItem | None = None
553
554
555### THREAD TYPES
556
557
558class ThreadMetadata(BaseModel):
559 """Metadata describing a thread without its items."""
560
561 title: str | None = None
562 id: str
563 created_at: datetime
564 status: ThreadStatus = Field(default_factory=lambda: ActiveStatus())
565 # TODO - make not client rendered
566 metadata: dict[str, Any] = Field(default_factory=dict)
567
568
569class ActiveStatus(BaseModel):
570 """Status indicating the thread is active."""
571
572 type: Literal["active"] = Field(default="active", frozen=True)
573
574
575class LockedStatus(BaseModel):
576 """Status indicating the thread is locked."""
577
578 type: Literal["locked"] = Field(default="locked", frozen=True)
579 reason: str | None = None
580
581
582class ClosedStatus(BaseModel):
583 """Status indicating the thread is closed."""
584
585 type: Literal["closed"] = Field(default="closed", frozen=True)
586 reason: str | None = None
587
588
589ThreadStatus = Annotated[
590 ActiveStatus | LockedStatus | ClosedStatus,
591 Field(discriminator="type"),
592]
593"""Union of lifecycle states for a thread."""
594
595
596class Thread(ThreadMetadata):
597 """Thread with its paginated items."""
598
599 items: Page[ThreadItem]
600
601
602### THREAD ITEM TYPES
603
604
605class ThreadItemBase(BaseModel):
606 """Base fields shared by all thread items."""
607
608 id: str
609 thread_id: str
610 created_at: datetime
611
612
613class UserMessageItem(ThreadItemBase):
614 """Thread item representing a user message."""
615
616 type: Literal["user_message"] = "user_message"
617 content: list[UserMessageContent]
618 attachments: list[Attachment] = Field(default_factory=list)
619 quoted_text: str | None = None
620 inference_options: InferenceOptions
621
622
623class AssistantMessageItem(ThreadItemBase):
624 """Thread item representing an assistant message."""
625
626 type: Literal["assistant_message"] = "assistant_message"
627 content: list[AssistantMessageContent]
628
629
630class ClientToolCallItem(ThreadItemBase):
631 """Thread item capturing a client tool call."""
632
633 type: Literal["client_tool_call"] = "client_tool_call"
634 status: Literal["pending", "completed"] = "pending"
635 call_id: str
636 name: str
637 arguments: dict[str, Any]
638 output: Any | None = None
639
640
641class WidgetItem(ThreadItemBase):
642 """Thread item containing widget content."""
643
644 type: Literal["widget"] = "widget"
645 widget: WidgetRoot
646 copy_text: str | None = None
647
648
649class GeneratedImage(BaseModel):
650 """Generated image."""
651
652 id: str
653 url: str
654
655
656class GeneratedImageItem(ThreadItemBase):
657 """Thread item containing a generated image."""
658
659 type: Literal["generated_image"] = "generated_image"
660 image: GeneratedImage | None = None
661
662
663class TaskItem(ThreadItemBase):
664 """Thread item containing a task."""
665
666 type: Literal["task"] = "task"
667 task: Task
668
669
670class WorkflowItem(ThreadItemBase):
671 """Thread item representing a workflow."""
672
673 type: Literal["workflow"] = "workflow"
674 workflow: Workflow
675
676
677class EndOfTurnItem(ThreadItemBase):
678 """Marker item indicating the assistant ends its turn."""
679
680 type: Literal["end_of_turn"] = "end_of_turn"
681
682
683class HiddenContextItem(ThreadItemBase):
684 """
685 HiddenContext is never sent to the client. It's not officially part of ChatKit.js.
686 It is only used internally to store additional context in a specific place in the thread.
687 """
688
689 type: Literal["hidden_context_item"] = "hidden_context_item"
690 content: Any
691
692
693class SDKHiddenContextItem(ThreadItemBase):
694 """
695 Hidden context that is used by the ChatKit Python SDK for storing additional context
696 for internal operations.
697 """
698
699 type: Literal["sdk_hidden_context"] = "sdk_hidden_context"
700 content: str
701
702
703ThreadItem = Annotated[
704 UserMessageItem
705 | AssistantMessageItem
706 | ClientToolCallItem
707 | WidgetItem
708 | GeneratedImageItem
709 | WorkflowItem
710 | TaskItem
711 | HiddenContextItem
712 | SDKHiddenContextItem
713 | EndOfTurnItem,
714 Field(discriminator="type"),
715]
716"""Union of all thread item variants."""
717
718
719### ASSISTANT MESSAGE TYPES
720
721
722class AssistantMessageContent(BaseModel):
723 """Assistant message content consisting of text and annotations."""
724
725 annotations: list[Annotation] = Field(default_factory=list)
726 text: str
727 type: Literal["output_text"] = "output_text"
728
729
730class Annotation(BaseModel):
731 """Reference to supporting context attached to assistant output."""
732
733 type: Literal["annotation"] = "annotation"
734 source: URLSource | FileSource | EntitySource
735 index: int | None = None
736
737
738### USER MESSAGE TYPES
739
740
741class UserMessageInput(BaseModel):
742 """Payload describing a user message submission."""
743
744 content: list[UserMessageContent]
745 attachments: list[str]
746 quoted_text: str | None = None
747 inference_options: InferenceOptions
748
749
750class UserMessageTextContent(BaseModel):
751 """User message content containing plaintext."""
752
753 type: Literal["input_text"] = "input_text"
754 text: str
755
756
757class UserMessageTagContent(BaseModel):
758 """User message content representing an interactive tag."""
759
760 type: Literal["input_tag"] = "input_tag"
761 id: str
762 text: str
763 data: dict[str, Any]
764 group: str | None = None
765 interactive: bool = False
766
767
768UserMessageContent = Annotated[
769 UserMessageTextContent | UserMessageTagContent, Field(discriminator="type")
770]
771"""Union of allowed user message content payloads."""
772
773
774class InferenceOptions(BaseModel):
775 """Model and tool configuration for message processing."""
776
777 tool_choice: ToolChoice | None = None
778 model: str | None = None
779
780
781class ToolChoice(BaseModel):
782 """Explicit tool selection for the assistant to invoke."""
783
784 id: str
785
786
787class AttachmentUploadDescriptor(BaseModel):
788 """Two-phase upload instructions."""
789
790 url: AnyUrl
791 method: Literal["POST", "PUT"]
792 """The HTTP method to use when uploading the file for two-phase upload."""
793 headers: dict[str, str] = Field(default_factory=dict)
794 """Optional headers to include in the upload request."""
795
796
797class AttachmentBase(BaseModel):
798 """Base metadata shared by all attachments."""
799
800 id: str
801 name: str
802 mime_type: str
803 upload_descriptor: AttachmentUploadDescriptor | None = None
804 """
805 Two-phase upload instructions.
806 Should be set to None after upload is complete or when using direct upload
807 where uploading happens when creating the attachment object.
808 """
809 thread_id: str | None = None
810 """
811 The thread the attachment belongs to, if any.
812 Added when the user message that contains the attachment is saved to store.
813 """
814 metadata: dict[str, Any] | None = None
815 """
816 Integration-only metadata stored with the attachment. Ignored by ChatKit and not
817 returned in ChatKitServer responses. If you serialize attachments from a custom
818 direct-upload endpoint and want to omit this field, pass context={"exclude_metadata": True}.
819 """
820
821 @model_serializer(mode="wrap")
822 def _serialize(self, serializer, info: SerializationInfo):
823 data = serializer(self)
824 if isinstance(data, dict) and (info.context or {}).get("exclude_metadata"):
825 data.pop("metadata", None)
826 return data
827
828
829class FileAttachment(AttachmentBase):
830 """Attachment representing a generic file."""
831
832 type: Literal["file"] = "file"
833
834
835class ImageAttachment(AttachmentBase):
836 """Attachment representing an image resource."""
837
838 type: Literal["image"] = "image"
839 preview_url: AnyUrl
840
841
842Attachment = Annotated[
843 FileAttachment | ImageAttachment,
844 Field(discriminator="type"),
845]
846"""Union of supported attachment types."""
847
848
849### WORKFLOW TYPES
850
851
852class Workflow(BaseModel):
853 """Workflow attached to a thread with optional summary."""
854
855 type: Literal["custom", "reasoning"]
856 tasks: list[Task]
857 summary: WorkflowSummary | None = None
858 expanded: bool = False
859
860
861class CustomSummary(BaseModel):
862 """Custom summary for a workflow."""
863
864 title: str
865 icon: IconName | None = None
866
867
868class DurationSummary(BaseModel):
869 """Summary providing total workflow duration."""
870
871 duration: int
872 """The duration of the workflow in seconds"""
873
874
875WorkflowSummary = CustomSummary | DurationSummary
876"""Summary variants available for workflows."""
877
878### TASK TYPES
879
880
881class BaseTask(BaseModel):
882 """Base fields common to all workflow tasks."""
883
884 status_indicator: Literal["none", "loading", "complete"] = "none"
885 """Only used when rendering the task as part of a workflow. Indicates the status of the task."""
886
887
888class CustomTask(BaseTask):
889 """Workflow task displaying custom content."""
890
891 type: Literal["custom"] = "custom"
892 title: str | None = None
893 icon: IconName | None = None
894 content: str | None = None
895
896
897class SearchTask(BaseTask):
898 """Workflow task representing a web search."""
899
900 type: Literal["web_search"] = "web_search"
901 title: str | None = None
902 title_query: str | None = None
903 queries: list[str] = Field(default_factory=list)
904 sources: list[URLSource] = Field(default_factory=list)
905
906
907class ThoughtTask(BaseTask):
908 """Workflow task capturing assistant reasoning."""
909
910 type: Literal["thought"] = "thought"
911 title: str | None = None
912 content: str
913
914
915class FileTask(BaseTask):
916 """Workflow task referencing file sources."""
917
918 type: Literal["file"] = "file"
919 title: str | None = None
920 sources: list[FileSource] = Field(default_factory=list)
921
922
923class ImageTask(BaseTask):
924 """Workflow task rendering image content."""
925
926 type: Literal["image"] = "image"
927 title: str | None = None
928
929
930Task = Annotated[
931 CustomTask | SearchTask | ThoughtTask | FileTask | ImageTask,
932 Field(discriminator="type"),
933]
934"""Union of workflow task variants."""
935
936
937### SOURCE TYPES
938
939
940class SourceBase(BaseModel):
941 """Base class for sources displayed to users."""
942
943 title: str
944 description: str | None = None
945 timestamp: str | None = None
946 group: str | None = None
947
948
949class FileSource(SourceBase):
950 """Source metadata for file-based references."""
951
952 type: Literal["file"] = "file"
953 filename: str
954
955
956class URLSource(SourceBase):
957 """Source metadata for external URLs."""
958
959 type: Literal["url"] = "url"
960 url: str
961 attribution: str | None = None
962
963
964class EntitySource(SourceBase):
965 """Source metadata for entity references."""
966
967 type: Literal["entity"] = "entity"
968 id: str
969 icon: IconName | None = None
970 label: str | None = None
971 """Optional label shown with the icon in the default entity hover header
972 when no preview callback is provided.
973 """
974 inline_label: str | None = None
975 """Optional label for the inline annotation view. When not provided, the icon is used instead."""
976 interactive: bool = False
977 """Per-entity toggle to wire client callbacks and render this entity as interactive."""
978 data: dict[str, Any] = Field(default_factory=dict)
979 """Additional data for the entity source that is passed to client entity callbacks."""
980
981 preview: Literal["lazy"] | None = Field(
982 default=None,
983 deprecated=True,
984 description="This field is ignored. Please use the entities.onRequestPreview ChatKit.js option instead.",
985 )
986
987
988Source = Annotated[
989 URLSource | FileSource | EntitySource,
990 Field(discriminator="type"),
991]
992"""Union of supported source types."""
993
994
995### MISC TYPES
996
997
998FeedbackKind = Literal["positive", "negative"]
999"""Literal type for feedback sentiment."""
1000