chainlit 0.7.700__py3-none-any.whl → 1.0.0rc0__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.
- chainlit/__init__.py +32 -23
- chainlit/auth.py +9 -10
- chainlit/cli/__init__.py +1 -2
- chainlit/config.py +13 -12
- chainlit/context.py +7 -3
- chainlit/data/__init__.py +375 -9
- chainlit/data/acl.py +6 -5
- chainlit/element.py +86 -123
- chainlit/emitter.py +117 -50
- chainlit/frontend/dist/assets/{index-71698725.js → index-6aee009a.js} +118 -292
- chainlit/frontend/dist/assets/{react-plotly-2c0acdf0.js → react-plotly-2f07c02a.js} +1 -1
- chainlit/frontend/dist/index.html +1 -1
- chainlit/haystack/callbacks.py +45 -43
- chainlit/hello.py +1 -1
- chainlit/langchain/callbacks.py +132 -120
- chainlit/llama_index/callbacks.py +68 -48
- chainlit/message.py +179 -207
- chainlit/oauth_providers.py +39 -34
- chainlit/playground/provider.py +44 -30
- chainlit/playground/providers/anthropic.py +4 -4
- chainlit/playground/providers/huggingface.py +2 -2
- chainlit/playground/providers/langchain.py +8 -10
- chainlit/playground/providers/openai.py +19 -13
- chainlit/server.py +155 -99
- chainlit/session.py +109 -40
- chainlit/socket.py +47 -36
- chainlit/step.py +393 -0
- chainlit/types.py +78 -21
- chainlit/user.py +32 -0
- chainlit/user_session.py +1 -5
- {chainlit-0.7.700.dist-info → chainlit-1.0.0rc0.dist-info}/METADATA +12 -31
- chainlit-1.0.0rc0.dist-info/RECORD +60 -0
- chainlit/client/base.py +0 -169
- chainlit/client/cloud.py +0 -502
- chainlit/prompt.py +0 -40
- chainlit-0.7.700.dist-info/RECORD +0 -61
- {chainlit-0.7.700.dist-info → chainlit-1.0.0rc0.dist-info}/WHEEL +0 -0
- {chainlit-0.7.700.dist-info → chainlit-1.0.0rc0.dist-info}/entry_points.txt +0 -0
chainlit/data/__init__.py
CHANGED
|
@@ -1,13 +1,379 @@
|
|
|
1
|
+
import functools
|
|
1
2
|
import os
|
|
2
|
-
from
|
|
3
|
+
from collections import deque
|
|
4
|
+
from typing import TYPE_CHECKING, Dict, List, Optional
|
|
3
5
|
|
|
4
|
-
from chainlit.
|
|
5
|
-
from chainlit.
|
|
6
|
+
from chainlit.context import context
|
|
7
|
+
from chainlit.logger import logger
|
|
8
|
+
from chainlit.session import WebsocketSession
|
|
9
|
+
from chainlit.types import Feedback, Pagination, ThreadDict, ThreadFilter
|
|
10
|
+
from chainlit.user import PersistedUser, User, UserDict
|
|
11
|
+
from chainlit_client import Attachment
|
|
12
|
+
from chainlit_client import Feedback as ClientFeedback
|
|
13
|
+
from chainlit_client import PageInfo, PaginatedResponse
|
|
14
|
+
from chainlit_client import Step as ClientStep
|
|
15
|
+
from chainlit_client.thread import NumberListFilter, StringFilter, StringListFilter
|
|
16
|
+
from chainlit_client.thread import ThreadFilter as ClientThreadFilter
|
|
6
17
|
|
|
7
|
-
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from chainlit.element import Element, ElementDict
|
|
20
|
+
from chainlit.step import FeedbackDict, StepDict
|
|
8
21
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
)
|
|
22
|
+
_data_layer = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def queue_until_user_message():
|
|
26
|
+
def decorator(method):
|
|
27
|
+
@functools.wraps(method)
|
|
28
|
+
async def wrapper(self, *args, **kwargs):
|
|
29
|
+
if (
|
|
30
|
+
isinstance(context.session, WebsocketSession)
|
|
31
|
+
and not context.session.has_user_message
|
|
32
|
+
):
|
|
33
|
+
# Queue the method invocation waiting for the first user message
|
|
34
|
+
queues = context.session.thread_queues
|
|
35
|
+
method_name = method.__name__
|
|
36
|
+
if method_name not in queues:
|
|
37
|
+
queues[method_name] = deque()
|
|
38
|
+
queues[method_name].append((method, self, args, kwargs))
|
|
39
|
+
|
|
40
|
+
else:
|
|
41
|
+
# Otherwise, Execute the method immediately
|
|
42
|
+
return await method(self, *args, **kwargs)
|
|
43
|
+
|
|
44
|
+
return wrapper
|
|
45
|
+
|
|
46
|
+
return decorator
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class BaseDataLayer:
|
|
50
|
+
"""Base class for data persistence."""
|
|
51
|
+
|
|
52
|
+
async def get_user(self, identifier: str) -> Optional["PersistedUser"]:
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
async def create_user(self, user: "User") -> Optional["PersistedUser"]:
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
async def upsert_feedback(
|
|
59
|
+
self,
|
|
60
|
+
feedback: Feedback,
|
|
61
|
+
) -> str:
|
|
62
|
+
return ""
|
|
63
|
+
|
|
64
|
+
@queue_until_user_message()
|
|
65
|
+
async def create_element(self, element_dict: "ElementDict"):
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
async def get_element(
|
|
69
|
+
self, thread_id: str, element_id: str
|
|
70
|
+
) -> Optional["ElementDict"]:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
@queue_until_user_message()
|
|
74
|
+
async def delete_element(self, element_id: str):
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
@queue_until_user_message()
|
|
78
|
+
async def create_step(self, step_dict: "StepDict"):
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
@queue_until_user_message()
|
|
82
|
+
async def update_step(self, step_dict: "StepDict"):
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
@queue_until_user_message()
|
|
86
|
+
async def delete_step(self, step_id: str):
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
async def get_thread_author(self, thread_id: str) -> str:
|
|
90
|
+
return ""
|
|
91
|
+
|
|
92
|
+
async def delete_thread(self, thread_id: str):
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
async def list_threads(
|
|
96
|
+
self, pagination: "Pagination", filters: "ThreadFilter"
|
|
97
|
+
) -> "PaginatedResponse[ThreadDict]":
|
|
98
|
+
return PaginatedResponse(
|
|
99
|
+
data=[], pageInfo=PageInfo(hasNextPage=False, endCursor=None)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
async def get_thread(self, thread_id: str) -> "Optional[ThreadDict]":
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
async def update_thread(
|
|
106
|
+
self,
|
|
107
|
+
thread_id: str,
|
|
108
|
+
user_id: Optional[str] = None,
|
|
109
|
+
metadata: Optional[Dict] = None,
|
|
110
|
+
tags: Optional[List[str]] = None,
|
|
111
|
+
):
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class ChainlitDataLayer:
|
|
116
|
+
def __init__(
|
|
117
|
+
self, api_key: str, chainlit_server: Optional[str] = "https://cloud.chainlit.io"
|
|
118
|
+
):
|
|
119
|
+
from chainlit_client import ChainlitClient
|
|
120
|
+
|
|
121
|
+
self.client = ChainlitClient(api_key=api_key, url=chainlit_server)
|
|
122
|
+
logger.info("Chainlit data layer initialized")
|
|
123
|
+
|
|
124
|
+
def attachment_to_element_dict(self, attachment: Attachment) -> "ElementDict":
|
|
125
|
+
metadata = attachment.metadata or {}
|
|
126
|
+
return {
|
|
127
|
+
"chainlitKey": None,
|
|
128
|
+
"display": metadata.get("display", "side"),
|
|
129
|
+
"language": metadata.get("language"),
|
|
130
|
+
"size": metadata.get("size"),
|
|
131
|
+
"type": metadata.get("type", "file"),
|
|
132
|
+
"forId": attachment.step_id,
|
|
133
|
+
"id": attachment.id or "",
|
|
134
|
+
"mime": attachment.mime,
|
|
135
|
+
"name": attachment.name or "",
|
|
136
|
+
"objectKey": attachment.object_key,
|
|
137
|
+
"url": attachment.url,
|
|
138
|
+
"threadId": attachment.thread_id,
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
def feedback_to_feedback_dict(
|
|
142
|
+
self, feedback: Optional[ClientFeedback]
|
|
143
|
+
) -> "Optional[FeedbackDict]":
|
|
144
|
+
if not feedback:
|
|
145
|
+
return None
|
|
146
|
+
return {
|
|
147
|
+
"id": feedback.id or "",
|
|
148
|
+
"forId": feedback.step_id or "",
|
|
149
|
+
"value": feedback.value or 0, # type: ignore
|
|
150
|
+
"comment": feedback.comment,
|
|
151
|
+
"strategy": "BINARY",
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
def step_to_step_dict(self, step: ClientStep) -> "StepDict":
|
|
155
|
+
metadata = step.metadata or {}
|
|
156
|
+
return {
|
|
157
|
+
"createdAt": step.created_at,
|
|
158
|
+
"id": step.id or "",
|
|
159
|
+
"threadId": step.thread_id or "",
|
|
160
|
+
"parentId": step.parent_id,
|
|
161
|
+
"feedback": self.feedback_to_feedback_dict(step.feedback),
|
|
162
|
+
"start": step.start_time,
|
|
163
|
+
"end": step.end_time,
|
|
164
|
+
"type": step.type or "undefined",
|
|
165
|
+
"name": step.name or "",
|
|
166
|
+
"generation": step.generation.to_dict() if step.generation else None,
|
|
167
|
+
"input": step.input or "",
|
|
168
|
+
"output": step.output or "",
|
|
169
|
+
"showInput": metadata.get("showInput", False),
|
|
170
|
+
"disableFeedback": metadata.get("disableFeedback", False),
|
|
171
|
+
"indent": metadata.get("indent"),
|
|
172
|
+
"language": metadata.get("language"),
|
|
173
|
+
"isError": metadata.get("isError", False),
|
|
174
|
+
"waitForAnswer": metadata.get("waitForAnswer", False),
|
|
175
|
+
"feedback": self.feedback_to_feedback_dict(step.feedback),
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async def get_user(self, identifier: str) -> Optional[PersistedUser]:
|
|
179
|
+
user = await self.client.api.get_user(identifier=identifier)
|
|
180
|
+
if not user:
|
|
181
|
+
return None
|
|
182
|
+
return PersistedUser(
|
|
183
|
+
id=user.id or "",
|
|
184
|
+
identifier=user.identifier or "",
|
|
185
|
+
metadata=user.metadata,
|
|
186
|
+
createdAt=user.created_at or "",
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
async def create_user(self, user: User) -> Optional[PersistedUser]:
|
|
190
|
+
_user = await self.client.api.get_user(identifier=user.identifier)
|
|
191
|
+
if not _user:
|
|
192
|
+
_user = await self.client.api.create_user(
|
|
193
|
+
identifier=user.identifier, metadata=user.metadata
|
|
194
|
+
)
|
|
195
|
+
return PersistedUser(
|
|
196
|
+
id=_user.id or "",
|
|
197
|
+
identifier=_user.identifier or "",
|
|
198
|
+
metadata=_user.metadata,
|
|
199
|
+
createdAt=_user.created_at or "",
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
async def upsert_feedback(
|
|
203
|
+
self,
|
|
204
|
+
feedback: Feedback,
|
|
205
|
+
):
|
|
206
|
+
if feedback.id:
|
|
207
|
+
await self.client.api.update_feedback(
|
|
208
|
+
id=feedback.id,
|
|
209
|
+
update_params={
|
|
210
|
+
"comment": feedback.comment,
|
|
211
|
+
"strategy": feedback.strategy,
|
|
212
|
+
"value": feedback.value,
|
|
213
|
+
},
|
|
214
|
+
)
|
|
215
|
+
return feedback.id
|
|
216
|
+
else:
|
|
217
|
+
created = await self.client.api.create_feedback(
|
|
218
|
+
step_id=feedback.forId,
|
|
219
|
+
value=feedback.value,
|
|
220
|
+
comment=feedback.comment,
|
|
221
|
+
strategy=feedback.strategy,
|
|
222
|
+
)
|
|
223
|
+
return created.id or ""
|
|
224
|
+
|
|
225
|
+
@queue_until_user_message()
|
|
226
|
+
async def create_element(self, element: "Element"):
|
|
227
|
+
metadata = {
|
|
228
|
+
"size": element.size,
|
|
229
|
+
"language": element.language,
|
|
230
|
+
"display": element.display,
|
|
231
|
+
"type": element.type,
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
await self.client.api.create_attachment(
|
|
235
|
+
thread_id=element.thread_id,
|
|
236
|
+
step_id=element.for_id or "",
|
|
237
|
+
mime=element.mime,
|
|
238
|
+
name=element.name,
|
|
239
|
+
url=element.url,
|
|
240
|
+
content=element.content,
|
|
241
|
+
path=element.path,
|
|
242
|
+
metadata=metadata,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
async def get_element(
|
|
246
|
+
self, thread_id: str, element_id: str
|
|
247
|
+
) -> Optional["ElementDict"]:
|
|
248
|
+
attachment = await self.client.api.get_attachment(id=element_id)
|
|
249
|
+
if not attachment:
|
|
250
|
+
return None
|
|
251
|
+
return self.attachment_to_element_dict(attachment)
|
|
252
|
+
|
|
253
|
+
@queue_until_user_message()
|
|
254
|
+
async def delete_element(self, element_id: str):
|
|
255
|
+
await self.client.api.delete_attachment(id=element_id)
|
|
256
|
+
|
|
257
|
+
@queue_until_user_message()
|
|
258
|
+
async def create_step(self, step_dict: "StepDict"):
|
|
259
|
+
metadata = {
|
|
260
|
+
"disableFeedback": step_dict.get("disableFeedback"),
|
|
261
|
+
"isError": step_dict.get("isError"),
|
|
262
|
+
"waitForAnswer": step_dict.get("waitForAnswer"),
|
|
263
|
+
"language": step_dict.get("language"),
|
|
264
|
+
"showInput": step_dict.get("showInput"),
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
await self.client.api.send_steps(
|
|
268
|
+
[
|
|
269
|
+
{
|
|
270
|
+
"createdAt": step_dict.get("createdAt"),
|
|
271
|
+
"startTime": step_dict.get("start"),
|
|
272
|
+
"endTime": step_dict.get("end"),
|
|
273
|
+
"generation": step_dict.get("generation"),
|
|
274
|
+
"id": step_dict.get("id"),
|
|
275
|
+
"parentId": step_dict.get("parentId"),
|
|
276
|
+
"input": step_dict.get("input"),
|
|
277
|
+
"output": step_dict.get("output"),
|
|
278
|
+
"name": step_dict.get("name"),
|
|
279
|
+
"threadId": step_dict.get("threadId"),
|
|
280
|
+
"type": step_dict.get("type"),
|
|
281
|
+
"metadata": metadata,
|
|
282
|
+
}
|
|
283
|
+
]
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
@queue_until_user_message()
|
|
287
|
+
async def update_step(self, step_dict: "StepDict"):
|
|
288
|
+
await self.create_step(step_dict)
|
|
289
|
+
|
|
290
|
+
@queue_until_user_message()
|
|
291
|
+
async def delete_step(self, step_id: str):
|
|
292
|
+
await self.client.api.delete_step(id=step_id)
|
|
293
|
+
|
|
294
|
+
async def get_thread_author(self, thread_id: str) -> str:
|
|
295
|
+
thread = await self.get_thread(thread_id)
|
|
296
|
+
if not thread:
|
|
297
|
+
return ""
|
|
298
|
+
user = thread.get("user")
|
|
299
|
+
if not user:
|
|
300
|
+
return ""
|
|
301
|
+
return user.get("identifier") or ""
|
|
302
|
+
|
|
303
|
+
async def delete_thread(self, thread_id: str):
|
|
304
|
+
await self.client.api.delete_thread(id=thread_id)
|
|
305
|
+
|
|
306
|
+
async def list_threads(
|
|
307
|
+
self, pagination: "Pagination", filters: "ThreadFilter"
|
|
308
|
+
) -> "PaginatedResponse[ThreadDict]":
|
|
309
|
+
if not filters.userIdentifier:
|
|
310
|
+
raise ValueError("userIdentifier is required")
|
|
311
|
+
|
|
312
|
+
client_filters = ClientThreadFilter(
|
|
313
|
+
participantsIdentifier=StringListFilter(
|
|
314
|
+
operator="in", value=[filters.userIdentifier]
|
|
315
|
+
),
|
|
316
|
+
)
|
|
317
|
+
if filters.search:
|
|
318
|
+
client_filters.search = StringFilter(operator="ilike", value=filters.search)
|
|
319
|
+
if filters.feedback:
|
|
320
|
+
client_filters.feedbacksValue = NumberListFilter(
|
|
321
|
+
operator="in", value=[filters.feedback]
|
|
322
|
+
)
|
|
323
|
+
return await self.client.api.list_threads(
|
|
324
|
+
first=pagination.first, after=pagination.cursor, filters=client_filters
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
async def get_thread(self, thread_id: str) -> "Optional[ThreadDict]":
|
|
328
|
+
thread = await self.client.api.get_thread(id=thread_id)
|
|
329
|
+
if not thread:
|
|
330
|
+
return None
|
|
331
|
+
elements = [] # List[ElementDict]
|
|
332
|
+
steps = [] # List[StepDict]
|
|
333
|
+
if thread.steps:
|
|
334
|
+
for step in thread.steps:
|
|
335
|
+
for attachment in step.attachments:
|
|
336
|
+
elements.append(self.attachment_to_element_dict(attachment))
|
|
337
|
+
steps.append(self.step_to_step_dict(step))
|
|
338
|
+
|
|
339
|
+
user = None # type: Optional["UserDict"]
|
|
340
|
+
|
|
341
|
+
if thread.user:
|
|
342
|
+
user = {
|
|
343
|
+
"id": thread.user.id or "",
|
|
344
|
+
"identifier": thread.user.identifier or "",
|
|
345
|
+
"metadata": thread.user.metadata,
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
"createdAt": thread.created_at or "",
|
|
350
|
+
"id": thread.id,
|
|
351
|
+
"steps": steps,
|
|
352
|
+
"elements": elements,
|
|
353
|
+
"metadata": thread.metadata,
|
|
354
|
+
"user": user,
|
|
355
|
+
"tags": thread.tags,
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
async def update_thread(
|
|
359
|
+
self,
|
|
360
|
+
thread_id: str,
|
|
361
|
+
user_id: Optional[str] = None,
|
|
362
|
+
metadata: Optional[Dict] = None,
|
|
363
|
+
tags: Optional[List[str]] = None,
|
|
364
|
+
):
|
|
365
|
+
await self.client.api.upsert_thread(
|
|
366
|
+
thread_id=thread_id,
|
|
367
|
+
participant_id=user_id,
|
|
368
|
+
metadata=metadata,
|
|
369
|
+
tags=tags,
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
if api_key := os.environ.get("CHAINLIT_API_KEY"):
|
|
374
|
+
chainlit_server = os.environ.get("CHAINLIT_SERVER")
|
|
375
|
+
_data_layer = ChainlitDataLayer(api_key=api_key, chainlit_server=chainlit_server)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def get_data_layer():
|
|
379
|
+
return _data_layer
|
chainlit/data/acl.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
from chainlit.data import
|
|
1
|
+
from chainlit.data import get_data_layer
|
|
2
2
|
from fastapi import HTTPException
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
async def
|
|
6
|
-
|
|
5
|
+
async def is_thread_author(username: str, thread_id: str):
|
|
6
|
+
data_layer = get_data_layer()
|
|
7
|
+
if not data_layer:
|
|
7
8
|
raise HTTPException(status_code=401, detail="Unauthorized")
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
thread_author = await data_layer.get_thread_author(thread_id)
|
|
10
11
|
|
|
11
|
-
if
|
|
12
|
+
if thread_author != username:
|
|
12
13
|
raise HTTPException(status_code=401, detail="Unauthorized")
|
|
13
14
|
else:
|
|
14
15
|
return True
|