chainlit 1.0.401__py3-none-any.whl → 2.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of chainlit might be problematic. Click here for more details.

Files changed (113) hide show
  1. chainlit/__init__.py +98 -279
  2. chainlit/_utils.py +8 -0
  3. chainlit/action.py +12 -10
  4. chainlit/{auth.py → auth/__init__.py} +28 -36
  5. chainlit/auth/cookie.py +123 -0
  6. chainlit/auth/jwt.py +39 -0
  7. chainlit/cache.py +4 -6
  8. chainlit/callbacks.py +362 -0
  9. chainlit/chat_context.py +64 -0
  10. chainlit/chat_settings.py +3 -1
  11. chainlit/cli/__init__.py +77 -8
  12. chainlit/config.py +191 -102
  13. chainlit/context.py +42 -13
  14. chainlit/copilot/dist/index.js +8750 -903
  15. chainlit/data/__init__.py +101 -416
  16. chainlit/data/acl.py +6 -2
  17. chainlit/data/base.py +107 -0
  18. chainlit/data/chainlit_data_layer.py +614 -0
  19. chainlit/data/dynamodb.py +590 -0
  20. chainlit/data/literalai.py +500 -0
  21. chainlit/data/sql_alchemy.py +721 -0
  22. chainlit/data/storage_clients/__init__.py +0 -0
  23. chainlit/data/storage_clients/azure.py +81 -0
  24. chainlit/data/storage_clients/azure_blob.py +89 -0
  25. chainlit/data/storage_clients/base.py +26 -0
  26. chainlit/data/storage_clients/gcs.py +88 -0
  27. chainlit/data/storage_clients/s3.py +75 -0
  28. chainlit/data/utils.py +29 -0
  29. chainlit/discord/__init__.py +6 -0
  30. chainlit/discord/app.py +354 -0
  31. chainlit/element.py +91 -33
  32. chainlit/emitter.py +81 -29
  33. chainlit/frontend/dist/assets/DailyMotion-Ce9dQoqZ.js +1 -0
  34. chainlit/frontend/dist/assets/Dataframe-C1XonMcV.js +22 -0
  35. chainlit/frontend/dist/assets/Facebook-DVVt6lrr.js +1 -0
  36. chainlit/frontend/dist/assets/FilePlayer-c7stW4vz.js +1 -0
  37. chainlit/frontend/dist/assets/Kaltura-BmMmgorA.js +1 -0
  38. chainlit/frontend/dist/assets/Mixcloud-Cw8hDmiO.js +1 -0
  39. chainlit/frontend/dist/assets/Mux-DiRZfeUf.js +1 -0
  40. chainlit/frontend/dist/assets/Preview-6Jt2mRHx.js +1 -0
  41. chainlit/frontend/dist/assets/SoundCloud-DKwcT58_.js +1 -0
  42. chainlit/frontend/dist/assets/Streamable-BVdxrEeX.js +1 -0
  43. chainlit/frontend/dist/assets/Twitch-DFqZR7Gu.js +1 -0
  44. chainlit/frontend/dist/assets/Vidyard-0BQAAtVk.js +1 -0
  45. chainlit/frontend/dist/assets/Vimeo-CRFSH0Vu.js +1 -0
  46. chainlit/frontend/dist/assets/Wistia-CKrmdQaG.js +1 -0
  47. chainlit/frontend/dist/assets/YouTube-CQpL-rvU.js +1 -0
  48. chainlit/frontend/dist/assets/index-DQmLRKyv.css +1 -0
  49. chainlit/frontend/dist/assets/index-QdmxtIMQ.js +8665 -0
  50. chainlit/frontend/dist/assets/react-plotly-B9hvVpUG.js +3484 -0
  51. chainlit/frontend/dist/index.html +2 -4
  52. chainlit/haystack/callbacks.py +4 -7
  53. chainlit/input_widget.py +8 -4
  54. chainlit/langchain/callbacks.py +103 -68
  55. chainlit/langflow/__init__.py +1 -0
  56. chainlit/llama_index/callbacks.py +65 -40
  57. chainlit/markdown.py +22 -6
  58. chainlit/message.py +54 -56
  59. chainlit/mistralai/__init__.py +50 -0
  60. chainlit/oauth_providers.py +266 -8
  61. chainlit/openai/__init__.py +10 -18
  62. chainlit/secret.py +1 -1
  63. chainlit/server.py +789 -228
  64. chainlit/session.py +108 -90
  65. chainlit/slack/__init__.py +6 -0
  66. chainlit/slack/app.py +397 -0
  67. chainlit/socket.py +199 -116
  68. chainlit/step.py +141 -89
  69. chainlit/sync.py +2 -1
  70. chainlit/teams/__init__.py +6 -0
  71. chainlit/teams/app.py +338 -0
  72. chainlit/translations/bn.json +244 -0
  73. chainlit/translations/en-US.json +122 -8
  74. chainlit/translations/gu.json +244 -0
  75. chainlit/translations/he-IL.json +244 -0
  76. chainlit/translations/hi.json +244 -0
  77. chainlit/translations/ja.json +242 -0
  78. chainlit/translations/kn.json +244 -0
  79. chainlit/translations/ml.json +244 -0
  80. chainlit/translations/mr.json +244 -0
  81. chainlit/translations/nl-NL.json +242 -0
  82. chainlit/translations/ta.json +244 -0
  83. chainlit/translations/te.json +244 -0
  84. chainlit/translations/zh-CN.json +243 -0
  85. chainlit/translations.py +60 -0
  86. chainlit/types.py +133 -28
  87. chainlit/user.py +14 -3
  88. chainlit/user_session.py +6 -3
  89. chainlit/utils.py +52 -5
  90. chainlit/version.py +3 -2
  91. {chainlit-1.0.401.dist-info → chainlit-2.0.4.dist-info}/METADATA +48 -50
  92. chainlit-2.0.4.dist-info/RECORD +107 -0
  93. chainlit/cli/utils.py +0 -24
  94. chainlit/frontend/dist/assets/index-9711593e.js +0 -723
  95. chainlit/frontend/dist/assets/index-d088547c.css +0 -1
  96. chainlit/frontend/dist/assets/react-plotly-d8762cc2.js +0 -3602
  97. chainlit/playground/__init__.py +0 -2
  98. chainlit/playground/config.py +0 -40
  99. chainlit/playground/provider.py +0 -108
  100. chainlit/playground/providers/__init__.py +0 -13
  101. chainlit/playground/providers/anthropic.py +0 -118
  102. chainlit/playground/providers/huggingface.py +0 -75
  103. chainlit/playground/providers/langchain.py +0 -89
  104. chainlit/playground/providers/openai.py +0 -408
  105. chainlit/playground/providers/vertexai.py +0 -171
  106. chainlit/translations/pt-BR.json +0 -155
  107. chainlit-1.0.401.dist-info/RECORD +0 -66
  108. /chainlit/copilot/dist/assets/{logo_dark-2a3cf740.svg → logo_dark-IkGJ_IwC.svg} +0 -0
  109. /chainlit/copilot/dist/assets/{logo_light-b078e7bc.svg → logo_light-Bb_IPh6r.svg} +0 -0
  110. /chainlit/frontend/dist/assets/{logo_dark-2a3cf740.svg → logo_dark-IkGJ_IwC.svg} +0 -0
  111. /chainlit/frontend/dist/assets/{logo_light-b078e7bc.svg → logo_light-Bb_IPh6r.svg} +0 -0
  112. {chainlit-1.0.401.dist-info → chainlit-2.0.4.dist-info}/WHEEL +0 -0
  113. {chainlit-1.0.401.dist-info → chainlit-2.0.4.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,243 @@
1
+ {
2
+ "components": {
3
+ "atoms": {
4
+ "buttons": {
5
+ "userButton": {
6
+ "menu": {
7
+ "settings": "设置",
8
+ "settingsKey": "S",
9
+ "APIKeys": "API 密钥",
10
+ "logout": "登出"
11
+ }
12
+ }
13
+ }
14
+ },
15
+ "molecules": {
16
+ "newChatButton": {
17
+ "newChat": "新建对话"
18
+ },
19
+ "tasklist": {
20
+ "TaskList": {
21
+ "title": "\ud83d\uddd2\ufe0f 任务列表",
22
+ "loading": "加载中...",
23
+ "error": "发生错误"
24
+ }
25
+ },
26
+ "attachments": {
27
+ "cancelUpload": "取消上传",
28
+ "removeAttachment": "移除附件"
29
+ },
30
+ "newChatDialog": {
31
+ "createNewChat": "创建新对话?",
32
+ "clearChat": "这将清除当前消息并开始新的对话。",
33
+ "cancel": "取消",
34
+ "confirm": "确认"
35
+ },
36
+ "settingsModal": {
37
+ "settings": "设置",
38
+ "expandMessages": "展开消息",
39
+ "hideChainOfThought": "隐藏思考链",
40
+ "darkMode": "暗色模式"
41
+ },
42
+ "detailsButton": {
43
+ "using": "使用",
44
+ "used": "已用"
45
+ },
46
+ "auth": {
47
+ "authLogin": {
48
+ "title": "登录以访问应用。",
49
+ "form": {
50
+ "email": "电子邮箱地址",
51
+ "password": "密码",
52
+ "noAccount": "没有账户?",
53
+ "alreadyHaveAccount": "已有账户?",
54
+ "signup": "注册",
55
+ "signin": "登录",
56
+ "or": "或者",
57
+ "continue": "继续",
58
+ "forgotPassword": "忘记密码?",
59
+ "passwordMustContain": "您的密码必须包含:",
60
+ "emailRequired": "电子邮箱是必填项",
61
+ "passwordRequired": "密码是必填项"
62
+ },
63
+ "error": {
64
+ "default": "无法登录。",
65
+ "signin": "尝试使用不同的账户登录。",
66
+ "oauthsignin": "尝试使用不同的账户登录。",
67
+ "redirect_uri_mismatch": "重定向URI与OAuth应用配置不匹配。",
68
+ "oauthcallbackerror": "尝试使用不同的账户登录。",
69
+ "oauthcreateaccount": "尝试使用不同的账户登录。",
70
+ "emailcreateaccount": "尝试使用不同的账户登录。",
71
+ "callback": "尝试使用不同的账户登录。",
72
+ "oauthaccountnotlinked": "为了验证您的身份,请使用最初使用的同一账户登录。",
73
+ "emailsignin": "无法发送邮件。",
74
+ "emailverify": "请验证您的电子邮件,已发送一封新邮件。",
75
+ "credentialssignin": "登录失败。请检查您提供的详细信息是否正确。",
76
+ "sessionrequired": "请登录以访问此页面。"
77
+ }
78
+ },
79
+ "authVerifyEmail": {
80
+ "almostThere": "您快成功了!我们已向 ",
81
+ "verifyEmailLink": "请单击该邮件中的链接以完成注册。",
82
+ "didNotReceive": "没找到邮件?",
83
+ "resendEmail": "重新发送邮件",
84
+ "goBack": "返回",
85
+ "emailSent": "邮件已成功发送。",
86
+ "verifyEmail": "验证您的电子邮件地址"
87
+ },
88
+ "providerButton": {
89
+ "continue": "使用{{provider}}继续",
90
+ "signup": "使用{{provider}}注册"
91
+ },
92
+ "authResetPassword": {
93
+ "newPasswordRequired": "新密码是必填项",
94
+ "passwordsMustMatch": "密码必须一致",
95
+ "confirmPasswordRequired": "确认密码是必填项",
96
+ "newPassword": "新密码",
97
+ "confirmPassword": "确认密码",
98
+ "resetPassword": "重置密码"
99
+ },
100
+ "authForgotPassword": {
101
+ "email": "电子邮箱地址",
102
+ "emailRequired": "电子邮箱是必填项",
103
+ "emailSent": "请检查电子邮箱{{email}}以获取重置密码的指示。",
104
+ "enterEmail": "请输入您的电子邮箱地址,我们将发送重置密码的指示。",
105
+ "resendEmail": "重新发送邮件",
106
+ "continue": "继续",
107
+ "goBack": "返回"
108
+ }
109
+ }
110
+ },
111
+ "organisms": {
112
+ "chat": {
113
+ "history": {
114
+ "index": {
115
+ "showHistory": "显示历史",
116
+ "lastInputs": "最后输入",
117
+ "noInputs": "如此空旷...",
118
+ "loading": "加载中..."
119
+ }
120
+ },
121
+ "inputBox": {
122
+ "input": {
123
+ "placeholder": "在这里输入您的消息..."
124
+ },
125
+ "speechButton": {
126
+ "start": "开始录音",
127
+ "stop": "停止录音",
128
+ "loading": "连接中"
129
+ },
130
+ "SubmitButton": {
131
+ "sendMessage": "发送消息",
132
+ "stopTask": "停止任务"
133
+ },
134
+ "UploadButton": {
135
+ "attachFiles": "附加文件"
136
+ },
137
+ "waterMark": {
138
+ "text": "使用"
139
+ }
140
+ },
141
+ "Messages": {
142
+ "index": {
143
+ "running": "运行中",
144
+ "executedSuccessfully": "执行成功",
145
+ "failed": "失败",
146
+ "feedbackUpdated": "反馈更新",
147
+ "updating": "正在更新"
148
+ },
149
+ "copyButton": {
150
+ "copyToClipboard": "拷贝到剪贴板",
151
+ "copied": "已拷贝!"
152
+ },
153
+ "feedbackButton": {
154
+ "helpful": "Helpful",
155
+ "notHelpful": "Not helpful",
156
+ "editFeedback": "Edit feedback"
157
+ },
158
+ "feedbackDialog": {
159
+ "dialogTitle": "Add a comment",
160
+ "submitButton": "Submit feedback"
161
+ }
162
+ },
163
+ "dropScreen": {
164
+ "dropYourFilesHere": "在这里拖放您的文件"
165
+ },
166
+ "index": {
167
+ "failedToUpload": "上传失败",
168
+ "cancelledUploadOf": "取消上传",
169
+ "couldNotReachServer": "无法连接到服务器",
170
+ "continuingChat": "继续之前的对话"
171
+ },
172
+ "settings": {
173
+ "settingsPanel": "设置面板",
174
+ "reset": "重置",
175
+ "cancel": "取消",
176
+ "confirm": "确认"
177
+ }
178
+ },
179
+ "threadHistory": {
180
+ "sidebar": {
181
+ "filters": {
182
+ "FeedbackSelect": {
183
+ "feedbackAll": "反馈:全部",
184
+ "feedbackPositive": "反馈:正面",
185
+ "feedbackNegative": "反馈:负面"
186
+ },
187
+ "SearchBar": {
188
+ "search": "搜索"
189
+ }
190
+ },
191
+ "DeleteThreadButton": {
192
+ "confirmMessage": "这将删除线程及其消息和元素。",
193
+ "cancel": "取消",
194
+ "confirm": "确认",
195
+ "deletingChat": "删除对话",
196
+ "chatDeleted": "对话已删除"
197
+ },
198
+ "index": {
199
+ "pastChats": "过往对话"
200
+ },
201
+ "ThreadList": {
202
+ "empty": "空的...",
203
+ "today": "今天",
204
+ "yesterday": "昨天",
205
+ "previous7days": "前7天",
206
+ "previous30days": "前30天"
207
+ },
208
+ "TriggerButton": {
209
+ "closeSidebar": "关闭侧边栏",
210
+ "openSidebar": "打开侧边栏"
211
+ }
212
+ },
213
+ "Thread": {
214
+ "backToChat": "返回对话",
215
+ "chatCreatedOn": "此对话创建于"
216
+ }
217
+ },
218
+ "header": {
219
+ "chat": "对话",
220
+ "readme": "说明"
221
+ }
222
+ }
223
+ },
224
+ "hooks": {
225
+ "useLLMProviders": {
226
+ "failedToFetchProviders": "获取提供者失败:"
227
+ }
228
+ },
229
+ "pages": {
230
+ "Design": {},
231
+ "Env": {
232
+ "savedSuccessfully": "保存成功",
233
+ "requiredApiKeys": "必需的API密钥",
234
+ "requiredApiKeysInfo": "要使用此应用,需要以下API密钥。这些密钥存储在您的设备本地存储中。"
235
+ },
236
+ "Page": {
237
+ "notPartOfProject": "您不是此项目的一部分。"
238
+ },
239
+ "ResumeButton": {
240
+ "resumeChat": "恢复对话"
241
+ }
242
+ }
243
+ }
@@ -0,0 +1,60 @@
1
+ # TODO:
2
+ # - Support linting plural
3
+ # - Support interpolation
4
+
5
+
6
+ def compare_json_structures(truth, to_compare, path=""):
7
+ """
8
+ Compare the structure of two deeply nested JSON objects.
9
+ Args:
10
+ truth (dict): The 'truth' JSON object.
11
+ to_compare (dict): The 'to_compare' JSON object.
12
+ path (str): The current path for error reporting (used internally).
13
+ Returns:
14
+ A list of differences found.
15
+ """
16
+ if not isinstance(truth, dict) or not isinstance(to_compare, dict):
17
+ raise ValueError("Both inputs must be dictionaries.")
18
+
19
+ errors = []
20
+
21
+ truth_keys = set(truth.keys())
22
+ to_compare_keys = set(to_compare.keys())
23
+
24
+ extra_keys = to_compare_keys - truth_keys
25
+ missing_keys = truth_keys - to_compare_keys
26
+
27
+ for key in extra_keys:
28
+ errors.append(f"⚠️ Extra key: '{path + '.' + key if path else key}'")
29
+
30
+ for key in missing_keys:
31
+ errors.append(f"❌ Missing key: '{path + '.' + key if path else key}'")
32
+
33
+ for key in truth_keys & to_compare_keys:
34
+ if isinstance(truth[key], dict) and isinstance(to_compare[key], dict):
35
+ # Recursive call to navigate through nested dictionaries
36
+ errors += compare_json_structures(
37
+ truth[key], to_compare[key], path + "." + key if path else key
38
+ )
39
+ elif not isinstance(truth[key], dict) and not isinstance(to_compare[key], dict):
40
+ # If both are not dicts, we are at leaf nodes and structure matches; skip value comparison
41
+ continue
42
+ else:
43
+ # Structure mismatch: one is a dict, the other is not
44
+ errors.append(
45
+ f"❌ Structure mismatch at: '{path + '.' + key if path else key}'"
46
+ )
47
+
48
+ return errors
49
+
50
+
51
+ def lint_translation_json(file, truth, to_compare):
52
+ print(f"\nLinting {file}...")
53
+
54
+ errors = compare_json_structures(truth, to_compare)
55
+
56
+ if errors:
57
+ for error in errors:
58
+ print(f"{error}")
59
+ else:
60
+ print(f"✅ No errors found in {file}")
chainlit/types.py CHANGED
@@ -1,13 +1,24 @@
1
1
  from enum import Enum
2
- from typing import TYPE_CHECKING, Dict, List, Literal, Optional, TypedDict, Union
2
+ from pathlib import Path
3
+ from typing import (
4
+ TYPE_CHECKING,
5
+ Any,
6
+ Dict,
7
+ Generic,
8
+ List,
9
+ Literal,
10
+ Optional,
11
+ Protocol,
12
+ TypedDict,
13
+ TypeVar,
14
+ Union,
15
+ )
3
16
 
4
17
  if TYPE_CHECKING:
5
18
  from chainlit.element import ElementDict
6
- from chainlit.user import UserDict
7
19
  from chainlit.step import StepDict
8
20
 
9
21
  from dataclasses_json import DataClassJsonMixin
10
- from literalai import ChatGeneration, CompletionGeneration
11
22
  from pydantic import BaseModel
12
23
  from pydantic.dataclasses import dataclass
13
24
 
@@ -20,7 +31,8 @@ class ThreadDict(TypedDict):
20
31
  id: str
21
32
  createdAt: str
22
33
  name: Optional[str]
23
- user: Optional["UserDict"]
34
+ userId: Optional[str]
35
+ userIdentifier: Optional[str]
24
36
  tags: Optional[List[str]]
25
37
  metadata: Optional[Dict]
26
38
  steps: List["StepDict"]
@@ -33,11 +45,68 @@ class Pagination(BaseModel):
33
45
 
34
46
 
35
47
  class ThreadFilter(BaseModel):
36
- feedback: Optional[Literal[-1, 0, 1]] = None
37
- userIdentifier: Optional[str] = None
48
+ feedback: Optional[Literal[0, 1]] = None
49
+ userId: Optional[str] = None
38
50
  search: Optional[str] = None
39
51
 
40
52
 
53
+ @dataclass
54
+ class PageInfo:
55
+ hasNextPage: bool
56
+ startCursor: Optional[str]
57
+ endCursor: Optional[str]
58
+
59
+ def to_dict(self):
60
+ return {
61
+ "hasNextPage": self.hasNextPage,
62
+ "startCursor": self.startCursor,
63
+ "endCursor": self.endCursor,
64
+ }
65
+
66
+ @classmethod
67
+ def from_dict(cls, page_info_dict: Dict) -> "PageInfo":
68
+ hasNextPage = page_info_dict.get("hasNextPage", False)
69
+ startCursor = page_info_dict.get("startCursor", None)
70
+ endCursor = page_info_dict.get("endCursor", None)
71
+ return cls(
72
+ hasNextPage=hasNextPage, startCursor=startCursor, endCursor=endCursor
73
+ )
74
+
75
+
76
+ T = TypeVar("T", covariant=True)
77
+
78
+
79
+ class HasFromDict(Protocol[T]):
80
+ @classmethod
81
+ def from_dict(cls, obj_dict: Any) -> T:
82
+ raise NotImplementedError
83
+
84
+
85
+ @dataclass
86
+ class PaginatedResponse(Generic[T]):
87
+ pageInfo: PageInfo
88
+ data: List[T]
89
+
90
+ def to_dict(self):
91
+ return {
92
+ "pageInfo": self.pageInfo.to_dict(),
93
+ "data": [
94
+ (d.to_dict() if hasattr(d, "to_dict") and callable(d.to_dict) else d)
95
+ for d in self.data
96
+ ],
97
+ }
98
+
99
+ @classmethod
100
+ def from_dict(
101
+ cls, paginated_response_dict: Dict, the_class: HasFromDict[T]
102
+ ) -> "PaginatedResponse[T]":
103
+ pageInfo = PageInfo.from_dict(paginated_response_dict.get("pageInfo", {}))
104
+
105
+ data = [the_class.from_dict(d) for d in paginated_response_dict.get("data", [])]
106
+
107
+ return cls(pageInfo=pageInfo, data=data)
108
+
109
+
41
110
  @dataclass
42
111
  class FileSpec(DataClassJsonMixin):
43
112
  accept: Union[List[str], Dict[str, List[str]]]
@@ -75,16 +144,37 @@ class FileReference(TypedDict):
75
144
  class FileDict(TypedDict):
76
145
  id: str
77
146
  name: str
78
- path: str
147
+ path: Path
79
148
  size: int
80
149
  type: str
81
150
 
82
151
 
83
- class UIMessagePayload(TypedDict):
152
+ class MessagePayload(TypedDict):
84
153
  message: "StepDict"
85
154
  fileReferences: Optional[List[FileReference]]
86
155
 
87
156
 
157
+ class InputAudioChunkPayload(TypedDict):
158
+ isStart: bool
159
+ mimeType: str
160
+ elapsedTime: float
161
+ data: bytes
162
+
163
+
164
+ @dataclass
165
+ class InputAudioChunk:
166
+ isStart: bool
167
+ mimeType: str
168
+ elapsedTime: float
169
+ data: bytes
170
+
171
+
172
+ class OutputAudioChunk(TypedDict):
173
+ track: str
174
+ mimeType: str
175
+ data: bytes
176
+
177
+
88
178
  @dataclass
89
179
  class AskFileResponse:
90
180
  id: str
@@ -96,43 +186,55 @@ class AskFileResponse:
96
186
 
97
187
  class AskActionResponse(TypedDict):
98
188
  name: str
99
- value: str
189
+ payload: Dict
100
190
  label: str
101
- description: str
191
+ tooltip: str
102
192
  forId: str
103
193
  id: str
104
- collapsed: bool
105
-
106
-
107
- class GenerationRequest(BaseModel):
108
- chatGeneration: Optional[ChatGeneration] = None
109
- completionGeneration: Optional[CompletionGeneration] = None
110
- userEnv: Dict[str, str]
111
194
 
112
- @property
113
- def generation(self):
114
- if self.chatGeneration:
115
- return self.chatGeneration
116
- return self.completionGeneration
117
195
 
118
- def is_chat(self):
119
- return self.chatGeneration is not None
196
+ class UpdateThreadRequest(BaseModel):
197
+ threadId: str
198
+ name: str
120
199
 
121
200
 
122
201
  class DeleteThreadRequest(BaseModel):
123
202
  threadId: str
124
203
 
125
204
 
205
+ class DeleteFeedbackRequest(BaseModel):
206
+ feedbackId: str
207
+
208
+
126
209
  class GetThreadsRequest(BaseModel):
127
210
  pagination: Pagination
128
211
  filter: ThreadFilter
129
212
 
130
213
 
214
+ class CallActionRequest(BaseModel):
215
+ action: Dict
216
+ sessionId: str
217
+
218
+
219
+ class ElementRequest(BaseModel):
220
+ element: Dict
221
+ sessionId: str
222
+
223
+
131
224
  class Theme(str, Enum):
132
225
  light = "light"
133
226
  dark = "dark"
134
227
 
135
228
 
229
+ @dataclass
230
+ class Starter(DataClassJsonMixin):
231
+ """Specification for a starter that can be chosen by the user at the thread start."""
232
+
233
+ label: str
234
+ message: str
235
+ icon: Optional[str] = None
236
+
237
+
136
238
  @dataclass
137
239
  class ChatProfile(DataClassJsonMixin):
138
240
  """Specification for a chat profile that can be chosen by the user at the thread start."""
@@ -140,22 +242,25 @@ class ChatProfile(DataClassJsonMixin):
140
242
  name: str
141
243
  markdown_description: str
142
244
  icon: Optional[str] = None
245
+ default: bool = False
246
+ starters: Optional[List[Starter]] = None
143
247
 
144
248
 
145
249
  FeedbackStrategy = Literal["BINARY"]
146
250
 
147
251
 
148
252
  class FeedbackDict(TypedDict):
149
- value: Literal[-1, 0, 1]
150
- strategy: FeedbackStrategy
253
+ forId: str
254
+ id: Optional[str]
255
+ value: Literal[0, 1]
151
256
  comment: Optional[str]
152
257
 
153
258
 
154
259
  @dataclass
155
260
  class Feedback:
156
261
  forId: str
157
- value: Literal[-1, 0, 1]
158
- strategy: FeedbackStrategy = "BINARY"
262
+ value: Literal[0, 1]
263
+ threadId: Optional[str] = None
159
264
  id: Optional[str] = None
160
265
  comment: Optional[str] = None
161
266
 
chainlit/user.py CHANGED
@@ -1,16 +1,26 @@
1
- from typing import Dict, Literal, TypedDict
1
+ from typing import Dict, Literal, Optional, TypedDict
2
2
 
3
3
  from dataclasses_json import DataClassJsonMixin
4
- from pydantic.dataclasses import Field, dataclass
4
+ from pydantic import Field
5
+ from pydantic.dataclasses import dataclass
5
6
 
6
7
  Provider = Literal[
7
- "credentials", "header", "github", "google", "azure-ad", "okta", "auth0", "descope"
8
+ "credentials",
9
+ "header",
10
+ "github",
11
+ "google",
12
+ "azure-ad",
13
+ "azure-ad-hybrid",
14
+ "okta",
15
+ "auth0",
16
+ "descope",
8
17
  ]
9
18
 
10
19
 
11
20
  class UserDict(TypedDict):
12
21
  id: str
13
22
  identifier: str
23
+ display_name: Optional[str]
14
24
  metadata: Dict
15
25
 
16
26
 
@@ -18,6 +28,7 @@ class UserDict(TypedDict):
18
28
  @dataclass
19
29
  class User(DataClassJsonMixin):
20
30
  identifier: str
31
+ display_name: Optional[str] = None
21
32
  metadata: Dict = Field(default_factory=dict)
22
33
 
23
34
 
chainlit/user_session.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from typing import Dict
2
2
 
3
- from chainlit.context import context
3
+ from chainlit.context import WebsocketSession, context
4
4
 
5
5
  user_sessions: Dict[str, Dict] = {}
6
6
 
@@ -27,9 +27,12 @@ class UserSession:
27
27
  user_session["chat_settings"] = context.session.chat_settings
28
28
  user_session["user"] = context.session.user
29
29
  user_session["chat_profile"] = context.session.chat_profile
30
+ user_session["http_referer"] = context.session.http_referer
31
+ user_session["client_type"] = context.session.client_type
32
+ user_session["http_cookie"] = context.session.http_cookie
30
33
 
31
- if context.session.root_message:
32
- user_session["root_message"] = context.session.root_message
34
+ if isinstance(context.session, WebsocketSession):
35
+ user_session["languages"] = context.session.languages
33
36
 
34
37
  return user_session.get(key, default)
35
38
 
chainlit/utils.py CHANGED
@@ -1,12 +1,18 @@
1
1
  import functools
2
2
  import importlib
3
3
  import inspect
4
+ import os
5
+ from asyncio import CancelledError
4
6
  from typing import Callable
5
7
 
8
+ import click
9
+ from fastapi import FastAPI
10
+ from packaging import version
11
+
12
+ from chainlit.auth import ensure_jwt_secret
6
13
  from chainlit.context import context
7
14
  from chainlit.logger import logger
8
15
  from chainlit.message import ErrorMessage
9
- from packaging import version
10
16
 
11
17
 
12
18
  def wrap_user_function(user_function: Callable, with_task=False) -> Callable:
@@ -39,13 +45,14 @@ def wrap_user_function(user_function: Callable, with_task=False) -> Callable:
39
45
  return await user_function(**params_values)
40
46
  else:
41
47
  return user_function(**params_values)
42
- except InterruptedError:
48
+ except CancelledError:
43
49
  pass
44
50
  except Exception as e:
45
51
  logger.exception(e)
46
- await ErrorMessage(
47
- content=str(e) or e.__class__.__name__, author="Error"
48
- ).send()
52
+ if with_task:
53
+ await ErrorMessage(
54
+ content=str(e) or e.__class__.__name__, author="Error"
55
+ ).send()
49
56
  finally:
50
57
  if with_task:
51
58
  await context.emitter.task_end()
@@ -85,3 +92,43 @@ def check_module_version(name, required_version):
85
92
  except ModuleNotFoundError:
86
93
  return False
87
94
  return version.parse(module.__version__) >= version.parse(required_version)
95
+
96
+
97
+ def check_file(target: str):
98
+ # Define accepted file extensions for Chainlit
99
+ ACCEPTED_FILE_EXTENSIONS = ("py", "py3")
100
+
101
+ _, extension = os.path.splitext(target)
102
+
103
+ # Check file extension
104
+ if extension[1:] not in ACCEPTED_FILE_EXTENSIONS:
105
+ if extension[1:] == "":
106
+ raise click.BadArgumentUsage(
107
+ "Chainlit requires raw Python (.py) files, but the provided file has no extension."
108
+ )
109
+ else:
110
+ raise click.BadArgumentUsage(
111
+ f"Chainlit requires raw Python (.py) files, not {extension}."
112
+ )
113
+
114
+ if not os.path.exists(target):
115
+ raise click.BadParameter(f"File does not exist: {target}")
116
+
117
+
118
+ def mount_chainlit(app: FastAPI, target: str, path="/chainlit"):
119
+ os.environ["CHAINLIT_ROOT_PATH"] = path
120
+ os.environ["CHAINLIT_SUBMOUNT"] = "true"
121
+ from chainlit.config import config, load_module
122
+ from chainlit.server import app as chainlit_app
123
+
124
+ config.run.root_path = path
125
+ config.run.debug = os.environ.get("CHAINLIT_DEBUG", False)
126
+
127
+ check_file(target)
128
+ # Load the module provided by the user
129
+ config.run.module_name = target
130
+ load_module(config.run.module_name)
131
+
132
+ ensure_jwt_secret()
133
+
134
+ app.mount(path, chainlit_app)