openai/chatkit-python

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
a202f603a00389ec4330c4faeb58dd41c64153a8

Branches

Tags

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

Clone

HTTPS

Download ZIP

chatkit/types.py

960lines · modecode

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