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.

Files changed (38) hide show
  1. chainlit/__init__.py +32 -23
  2. chainlit/auth.py +9 -10
  3. chainlit/cli/__init__.py +1 -2
  4. chainlit/config.py +13 -12
  5. chainlit/context.py +7 -3
  6. chainlit/data/__init__.py +375 -9
  7. chainlit/data/acl.py +6 -5
  8. chainlit/element.py +86 -123
  9. chainlit/emitter.py +117 -50
  10. chainlit/frontend/dist/assets/{index-71698725.js → index-6aee009a.js} +118 -292
  11. chainlit/frontend/dist/assets/{react-plotly-2c0acdf0.js → react-plotly-2f07c02a.js} +1 -1
  12. chainlit/frontend/dist/index.html +1 -1
  13. chainlit/haystack/callbacks.py +45 -43
  14. chainlit/hello.py +1 -1
  15. chainlit/langchain/callbacks.py +132 -120
  16. chainlit/llama_index/callbacks.py +68 -48
  17. chainlit/message.py +179 -207
  18. chainlit/oauth_providers.py +39 -34
  19. chainlit/playground/provider.py +44 -30
  20. chainlit/playground/providers/anthropic.py +4 -4
  21. chainlit/playground/providers/huggingface.py +2 -2
  22. chainlit/playground/providers/langchain.py +8 -10
  23. chainlit/playground/providers/openai.py +19 -13
  24. chainlit/server.py +155 -99
  25. chainlit/session.py +109 -40
  26. chainlit/socket.py +47 -36
  27. chainlit/step.py +393 -0
  28. chainlit/types.py +78 -21
  29. chainlit/user.py +32 -0
  30. chainlit/user_session.py +1 -5
  31. {chainlit-0.7.700.dist-info → chainlit-1.0.0rc0.dist-info}/METADATA +12 -31
  32. chainlit-1.0.0rc0.dist-info/RECORD +60 -0
  33. chainlit/client/base.py +0 -169
  34. chainlit/client/cloud.py +0 -502
  35. chainlit/prompt.py +0 -40
  36. chainlit-0.7.700.dist-info/RECORD +0 -61
  37. {chainlit-0.7.700.dist-info → chainlit-1.0.0rc0.dist-info}/WHEEL +0 -0
  38. {chainlit-0.7.700.dist-info → chainlit-1.0.0rc0.dist-info}/entry_points.txt +0 -0
chainlit/message.py CHANGED
@@ -1,76 +1,87 @@
1
+ import asyncio
1
2
  import json
2
3
  import uuid
3
- from abc import ABC, abstractmethod
4
- from datetime import datetime, timezone
4
+ from abc import ABC
5
+ from datetime import datetime
5
6
  from typing import Dict, List, Optional, Union, cast
6
7
 
7
8
  from chainlit.action import Action
8
- from chainlit.client.base import MessageDict
9
9
  from chainlit.config import config
10
10
  from chainlit.context import context
11
- from chainlit.data import chainlit_client
11
+ from chainlit.data import get_data_layer
12
12
  from chainlit.element import ElementBased
13
13
  from chainlit.logger import logger
14
- from chainlit.prompt import Prompt
14
+ from chainlit.step import StepDict
15
15
  from chainlit.telemetry import trace_event
16
16
  from chainlit.types import (
17
17
  AskActionResponse,
18
18
  AskActionSpec,
19
19
  AskFileResponse,
20
20
  AskFileSpec,
21
- AskResponse,
22
21
  AskSpec,
22
+ FileDict,
23
23
  )
24
- from syncer import asyncio
24
+ from chainlit_client.step import MessageStepType
25
25
 
26
26
 
27
27
  class MessageBase(ABC):
28
28
  id: str
29
+ thread_id: str
29
30
  author: str
30
31
  content: str = ""
32
+ type: MessageStepType = "assistant_message"
33
+ disable_feedback = False
31
34
  streaming = False
32
- created_at: Union[int, str, None] = None
35
+ created_at: Union[str, None] = None
33
36
  fail_on_persist_error: bool = False
34
37
  persisted = False
38
+ is_error = False
39
+ language: Optional[str] = None
40
+ wait_for_answer = False
41
+ indent: Optional[int] = None
35
42
 
36
43
  def __post_init__(self) -> None:
37
44
  trace_event(f"init {self.__class__.__name__}")
45
+ self.thread_id = context.session.thread_id
46
+
38
47
  if not getattr(self, "id", None):
39
48
  self.id = str(uuid.uuid4())
40
- if not self.created_at:
41
- self.created_at = datetime.now(timezone.utc).isoformat()
42
-
43
- @abstractmethod
44
- def to_dict(self) -> Dict:
45
- pass
46
-
47
- async def with_conversation_id(self):
48
- _dict = self.to_dict()
49
- _dict["conversationId"] = await context.session.get_conversation_id()
50
- return _dict
51
-
52
- async def _create(self):
53
- msg_dict = await self.with_conversation_id()
54
- asyncio.create_task(self._persist_create(msg_dict))
55
49
 
56
- if not config.features.prompt_playground:
57
- msg_dict.pop("prompt", None)
58
- return msg_dict
50
+ @classmethod
51
+ def from_dict(self, _dict: StepDict):
52
+ type = _dict.get("type", "assistant_message")
53
+ message = Message(
54
+ id=_dict["id"],
55
+ created_at=_dict["createdAt"],
56
+ content=_dict["output"],
57
+ author=_dict.get("name", config.ui.name),
58
+ type=type, # type: ignore
59
+ disable_feedback=_dict.get("disableFeedback", False),
60
+ language=_dict.get("language"),
61
+ )
59
62
 
60
- async def _persist_create(self, message: MessageDict):
61
- if not chainlit_client or self.persisted:
62
- return
63
+ return message
63
64
 
64
- try:
65
- persisted_id = await chainlit_client.create_message(message)
65
+ def to_dict(self) -> StepDict:
66
+ _dict: StepDict = {
67
+ "id": self.id,
68
+ "threadId": self.thread_id,
69
+ "createdAt": self.created_at,
70
+ "start": self.created_at,
71
+ "end": self.created_at,
72
+ "output": self.content,
73
+ "name": self.author,
74
+ "type": self.type,
75
+ "createdAt": self.created_at,
76
+ "language": self.language,
77
+ "streaming": self.streaming,
78
+ "disableFeedback": self.disable_feedback,
79
+ "isError": self.is_error,
80
+ "waitForAnswer": self.wait_for_answer,
81
+ "indent": self.indent,
82
+ }
66
83
 
67
- if persisted_id:
68
- self.id = persisted_id
69
- self.persisted = True
70
- except Exception as e:
71
- if self.fail_on_persist_error:
72
- raise e
73
- logger.error(f"Failed to persist message creation: {str(e)}")
84
+ return _dict
74
85
 
75
86
  async def update(
76
87
  self,
@@ -79,51 +90,63 @@ class MessageBase(ABC):
79
90
  Update a message already sent to the UI.
80
91
  """
81
92
  trace_event("update_message")
93
+
82
94
  if self.streaming:
83
95
  self.streaming = False
84
- msg_dict = self.to_dict()
85
- asyncio.create_task(self._persist_update(msg_dict))
86
- await context.emitter.update_message(msg_dict)
87
96
 
88
- return True
97
+ step_dict = self.to_dict()
98
+
99
+ data_layer = get_data_layer()
100
+ if data_layer:
101
+ try:
102
+ asyncio.create_task(data_layer.update_step(step_dict))
103
+ except Exception as e:
104
+ if self.fail_on_persist_error:
105
+ raise e
106
+ logger.error(f"Failed to persist message update: {str(e)}")
89
107
 
90
- async def _persist_update(self, message: MessageDict):
91
- if not chainlit_client or not self.id:
92
- return
108
+ await context.emitter.update_step(step_dict)
93
109
 
94
- try:
95
- await chainlit_client.update_message(self.id, message)
96
- except Exception as e:
97
- if self.fail_on_persist_error:
98
- raise e
99
- logger.error(f"Failed to persist message update: {str(e)}")
110
+ return True
100
111
 
101
112
  async def remove(self):
102
113
  """
103
114
  Remove a message already sent to the UI.
104
- This will not automatically remove potential nested messages and could lead to undesirable side effects in the UI.
105
115
  """
106
116
  trace_event("remove_message")
107
117
 
108
- if chainlit_client and self.id:
109
- await chainlit_client.delete_message(self.id)
118
+ step_dict = self.to_dict()
119
+ data_layer = get_data_layer()
120
+ if data_layer:
121
+ try:
122
+ asyncio.create_task(data_layer.delete_step(step_dict["id"]))
123
+ except Exception as e:
124
+ if self.fail_on_persist_error:
125
+ raise e
126
+ logger.error(f"Failed to persist message deletion: {str(e)}")
110
127
 
111
- await context.emitter.delete_message(self.to_dict())
128
+ await context.emitter.delete_step(step_dict)
112
129
 
113
130
  return True
114
131
 
115
- async def _persist_remove(self):
116
- if not chainlit_client or not self.id:
117
- return
132
+ async def _create(self):
133
+ step_dict = self.to_dict()
134
+ data_layer = get_data_layer()
135
+ if data_layer and not self.persisted:
136
+ try:
137
+ asyncio.create_task(data_layer.create_step(step_dict))
138
+ self.persisted = True
139
+ except Exception as e:
140
+ if self.fail_on_persist_error:
141
+ raise e
142
+ logger.error(f"Failed to persist message creation: {str(e)}")
118
143
 
119
- try:
120
- await chainlit_client.delete_message(self.id)
121
- except Exception as e:
122
- if self.fail_on_persist_error:
123
- raise e
124
- logger.error(f"Failed to persist message deletion: {str(e)}")
144
+ return step_dict
125
145
 
126
146
  async def send(self):
147
+ if not self.created_at:
148
+ self.created_at = datetime.utcnow().isoformat()
149
+
127
150
  if self.content is None:
128
151
  self.content = ""
129
152
 
@@ -133,9 +156,9 @@ class MessageBase(ABC):
133
156
  if self.streaming:
134
157
  self.streaming = False
135
158
 
136
- msg_dict = await self._create()
159
+ step_dict = await self._create()
137
160
 
138
- await context.emitter.send_message(msg_dict)
161
+ await context.emitter.send_step(step_dict)
139
162
 
140
163
  return self.id
141
164
 
@@ -147,8 +170,8 @@ class MessageBase(ABC):
147
170
 
148
171
  if not self.streaming:
149
172
  self.streaming = True
150
- msg_dict = self.to_dict()
151
- await context.emitter.stream_start(msg_dict)
173
+ step_dict = self.to_dict()
174
+ await context.emitter.stream_start(step_dict)
152
175
 
153
176
  if is_sequence:
154
177
  self.content = token
@@ -164,33 +187,27 @@ class MessageBase(ABC):
164
187
  class Message(MessageBase):
165
188
  """
166
189
  Send a message to the UI
167
- If a project ID is configured, the message will be persisted in the cloud.
168
190
 
169
191
  Args:
170
192
  content (Union[str, Dict]): The content of the message.
171
193
  author (str, optional): The author of the message, this will be used in the UI. Defaults to the chatbot name (see config).
172
- prompt (Prompt, optional): The prompt used to generate the message. If provided, enables the prompt playground for this message.
173
194
  language (str, optional): Language of the code is the content is code. See https://react-code-blocks-rajinwonderland.vercel.app/?path=/story/codeblock--supported-languages for a list of supported languages.
174
- parent_id (str, optional): If provided, the message will be nested inside the parent in the UI.
175
- indent (int, optional): If positive, the message will be nested in the UI. (deprecated, use parent_id instead)
176
195
  actions (List[Action], optional): A list of actions to send with the message.
177
196
  elements (List[ElementBased], optional): A list of elements to send with the message.
178
- disable_human_feedback (bool, optional): Hide the feedback buttons for this specific message
197
+ disable_feedback (bool, optional): Hide the feedback buttons for this specific message
179
198
  """
180
199
 
181
200
  def __init__(
182
201
  self,
183
202
  content: Union[str, Dict],
184
203
  author: str = config.ui.name,
185
- prompt: Optional[Prompt] = None,
186
204
  language: Optional[str] = None,
187
- parent_id: Optional[str] = None,
188
- indent: int = 0,
189
205
  actions: Optional[List[Action]] = None,
190
206
  elements: Optional[List[ElementBased]] = None,
191
- disable_human_feedback: Optional[bool] = False,
192
- author_is_user: Optional[bool] = False,
193
- id: Optional[uuid.UUID] = None,
207
+ disable_feedback: bool = False,
208
+ type: MessageStepType = "assistant_message",
209
+ id: Optional[str] = None,
210
+ created_at: Union[str, None] = None,
194
211
  ):
195
212
  self.language = language
196
213
 
@@ -200,87 +217,45 @@ class Message(MessageBase):
200
217
  self.language = "json"
201
218
  except TypeError:
202
219
  self.content = str(content)
203
- self.language = "python"
220
+ self.language = "text"
204
221
  elif isinstance(content, str):
205
222
  self.content = content
206
223
  else:
207
224
  self.content = str(content)
208
- self.language = "python"
225
+ self.language = "text"
209
226
 
210
227
  if id:
211
228
  self.id = str(id)
212
229
 
230
+ if created_at:
231
+ self.created_at = created_at
232
+
213
233
  self.author = author
214
- self.author_is_user = author_is_user
215
- self.prompt = prompt
216
- self.parent_id = parent_id
217
- self.indent = indent
234
+ self.type = type
218
235
  self.actions = actions if actions is not None else []
219
236
  self.elements = elements if elements is not None else []
220
- self.disable_human_feedback = disable_human_feedback
237
+ self.disable_feedback = disable_feedback
221
238
 
222
239
  super().__post_init__()
223
240
 
224
- @classmethod
225
- def from_dict(self, _dict: MessageDict):
226
- message = Message(
227
- content=_dict["content"],
228
- author=_dict.get("author", config.ui.name),
229
- prompt=_dict.get("prompt"),
230
- language=_dict.get("language"),
231
- parent_id=_dict.get("parentId"),
232
- indent=_dict.get("indent") or 0,
233
- disable_human_feedback=_dict.get("disableHumanFeedback"),
234
- author_is_user=_dict.get("authorIsUser"),
235
- )
236
-
237
- if _id := _dict.get("id"):
238
- message.id = _id
239
- if created_at := _dict.get("createdAt"):
240
- message.created_at = created_at
241
-
242
- return message
243
-
244
- def to_dict(self):
245
- _dict = {
246
- "createdAt": self.created_at,
247
- "content": self.content,
248
- "author": self.author,
249
- "authorIsUser": self.author_is_user,
250
- "language": self.language,
251
- "parentId": self.parent_id,
252
- "indent": self.indent,
253
- "streaming": self.streaming,
254
- "disableHumanFeedback": self.disable_human_feedback,
255
- }
256
-
257
- if self.prompt:
258
- _dict["prompt"] = self.prompt.to_dict()
259
-
260
- if self.id:
261
- _dict["id"] = self.id
262
-
263
- return _dict
264
-
265
241
  async def send(self) -> str:
266
242
  """
267
243
  Send the message to the UI and persist it in the cloud if a project ID is configured.
268
244
  Return the ID of the message.
269
245
  """
270
246
  trace_event("send_message")
271
- id = await super().send()
247
+ await super().send()
272
248
 
273
- if not self.parent_id:
274
- context.session.root_message = self
249
+ context.session.root_message = self
275
250
 
276
251
  # Create tasks for all actions and elements
277
- tasks = [action.send(for_id=id) for action in self.actions]
278
- tasks.extend(element.send(for_id=id) for element in self.elements)
252
+ tasks = [action.send(for_id=self.id) for action in self.actions]
253
+ tasks.extend(element.send(for_id=self.id) for element in self.elements)
279
254
 
280
255
  # Run all tasks concurrently
281
256
  await asyncio.gather(*tasks)
282
257
 
283
- return id
258
+ return self.id
284
259
 
285
260
  async def update(self):
286
261
  """
@@ -290,15 +265,16 @@ class Message(MessageBase):
290
265
  trace_event("send_message")
291
266
  await super().update()
292
267
 
293
- actions_to_update = [action for action in self.actions if action.forId is None]
268
+ # Update tasks for all actions and elements
269
+ tasks = [
270
+ action.send(for_id=self.id)
271
+ for action in self.actions
272
+ if action.forId is None
273
+ ]
274
+ tasks.extend(element.send(for_id=self.id) for element in self.elements)
294
275
 
295
- elements_to_update = [el for el in self.elements if self.id not in el.for_ids]
296
-
297
- for action in actions_to_update:
298
- await action.send(for_id=self.id)
299
-
300
- for element in elements_to_update:
301
- await element.send(for_id=self.id)
276
+ # Run all tasks concurrently
277
+ await asyncio.gather(*tasks)
302
278
 
303
279
  return True
304
280
 
@@ -323,29 +299,16 @@ class ErrorMessage(MessageBase):
323
299
  self,
324
300
  content: str,
325
301
  author: str = config.ui.name,
326
- parent_id: Optional[str] = None,
327
- indent: int = 0,
328
302
  fail_on_persist_error: bool = False,
329
303
  ):
330
304
  self.content = content
331
305
  self.author = author
332
- self.parent_id = parent_id
333
- self.indent = indent
306
+ self.type = "system_message"
307
+ self.is_error = True
334
308
  self.fail_on_persist_error = fail_on_persist_error
335
309
 
336
310
  super().__post_init__()
337
311
 
338
- def to_dict(self):
339
- return {
340
- "id": self.id,
341
- "createdAt": self.created_at,
342
- "content": self.content,
343
- "author": self.author,
344
- "parentId": self.parent_id,
345
- "indent": self.indent,
346
- "isError": True,
347
- }
348
-
349
312
  async def send(self):
350
313
  """
351
314
  Send the error message to the UI and persist it in the cloud if a project ID is configured.
@@ -371,7 +334,7 @@ class AskUserMessage(AskMessageBase):
371
334
  Args:
372
335
  content (str): The content of the prompt.
373
336
  author (str, optional): The author of the message, this will be used in the UI. Defaults to the chatbot name (see config).
374
- disable_human_feedback (bool, optional): Hide the feedback buttons for this specific message
337
+ disable_feedback (bool, optional): Hide the feedback buttons for this specific message
375
338
  timeout (int, optional): The number of seconds to wait for an answer before raising a TimeoutError.
376
339
  raise_on_timeout (bool, optional): Whether to raise a socketio TimeoutError if the user does not answer in time.
377
340
  """
@@ -380,45 +343,46 @@ class AskUserMessage(AskMessageBase):
380
343
  self,
381
344
  content: str,
382
345
  author: str = config.ui.name,
383
- disable_human_feedback: bool = False,
346
+ type: MessageStepType = "assistant_message",
347
+ disable_feedback: bool = False,
384
348
  timeout: int = 60,
385
349
  raise_on_timeout: bool = False,
386
350
  ):
387
351
  self.content = content
388
352
  self.author = author
389
353
  self.timeout = timeout
390
- self.disable_human_feedback = disable_human_feedback
354
+ self.type = type
355
+ self.disable_feedback = disable_feedback
391
356
  self.raise_on_timeout = raise_on_timeout
392
357
 
393
358
  super().__post_init__()
394
359
 
395
- def to_dict(self):
396
- return {
397
- "id": self.id,
398
- "createdAt": self.created_at,
399
- "content": self.content,
400
- "author": self.author,
401
- "waitForAnswer": True,
402
- "disableHumanFeedback": self.disable_human_feedback,
403
- }
404
-
405
- async def send(self) -> Union[AskResponse, None]:
360
+ async def send(self) -> Union[StepDict, None]:
406
361
  """
407
362
  Sends the question to ask to the UI and waits for the reply.
408
363
  """
409
364
  trace_event("send_ask_user")
365
+ if not self.created_at:
366
+ self.created_at = datetime.utcnow().isoformat()
367
+
368
+ if config.code.author_rename:
369
+ self.author = await config.code.author_rename(self.author)
410
370
 
411
371
  if self.streaming:
412
372
  self.streaming = False
413
373
 
414
- if config.code.author_rename:
415
- self.author = await config.code.author_rename(self.author)
374
+ self.wait_for_answer = True
416
375
 
417
- msg_dict = await self._create()
376
+ step_dict = await self._create()
418
377
 
419
378
  spec = AskSpec(type="text", timeout=self.timeout)
420
379
 
421
- res = await context.emitter.send_ask_user(msg_dict, spec, self.raise_on_timeout)
380
+ res = cast(
381
+ Union[None, StepDict],
382
+ await context.emitter.send_ask_user(step_dict, spec, self.raise_on_timeout),
383
+ )
384
+
385
+ self.wait_for_answer = False
422
386
 
423
387
  return res
424
388
 
@@ -435,7 +399,7 @@ class AskFileMessage(AskMessageBase):
435
399
  max_size_mb (int, optional): Maximum size per file in MB. Maximum value is 100.
436
400
  max_files (int, optional): Maximum number of files to upload. Maximum value is 10.
437
401
  author (str, optional): The author of the message, this will be used in the UI. Defaults to the chatbot name (see config).
438
- disable_human_feedback (bool, optional): Hide the feedback buttons for this specific message
402
+ disable_feedback (bool, optional): Hide the feedback buttons for this specific message
439
403
  timeout (int, optional): The number of seconds to wait for an answer before raising a TimeoutError.
440
404
  raise_on_timeout (bool, optional): Whether to raise a socketio TimeoutError if the user does not answer in time.
441
405
  """
@@ -447,7 +411,8 @@ class AskFileMessage(AskMessageBase):
447
411
  max_size_mb=2,
448
412
  max_files=1,
449
413
  author=config.ui.name,
450
- disable_human_feedback: bool = False,
414
+ type: MessageStepType = "assistant_message",
415
+ disable_feedback: bool = False,
451
416
  timeout=90,
452
417
  raise_on_timeout=False,
453
418
  ):
@@ -455,36 +420,32 @@ class AskFileMessage(AskMessageBase):
455
420
  self.max_size_mb = max_size_mb
456
421
  self.max_files = max_files
457
422
  self.accept = accept
423
+ self.type = type
458
424
  self.author = author
459
425
  self.timeout = timeout
460
426
  self.raise_on_timeout = raise_on_timeout
461
- self.disable_human_feedback = disable_human_feedback
427
+ self.disable_feedback = disable_feedback
462
428
 
463
429
  super().__post_init__()
464
430
 
465
- def to_dict(self):
466
- return {
467
- "id": self.id,
468
- "createdAt": self.created_at,
469
- "content": self.content,
470
- "author": self.author,
471
- "waitForAnswer": True,
472
- "disableHumanFeedback": self.disable_human_feedback,
473
- }
474
-
475
431
  async def send(self) -> Union[List[AskFileResponse], None]:
476
432
  """
477
433
  Sends the message to request a file from the user to the UI and waits for the reply.
478
434
  """
479
435
  trace_event("send_ask_file")
480
436
 
437
+ if not self.created_at:
438
+ self.created_at = datetime.utcnow().isoformat()
439
+
481
440
  if self.streaming:
482
441
  self.streaming = False
483
442
 
484
443
  if config.code.author_rename:
485
444
  self.author = await config.code.author_rename(self.author)
486
445
 
487
- msg_dict = await self._create()
446
+ self.wait_for_answer = True
447
+
448
+ step_dict = await self._create()
488
449
 
489
450
  spec = AskFileSpec(
490
451
  type="file",
@@ -494,10 +455,24 @@ class AskFileMessage(AskMessageBase):
494
455
  timeout=self.timeout,
495
456
  )
496
457
 
497
- res = await context.emitter.send_ask_user(msg_dict, spec, self.raise_on_timeout)
458
+ res = cast(
459
+ Union[None, List[FileDict]],
460
+ await context.emitter.send_ask_user(step_dict, spec, self.raise_on_timeout),
461
+ )
462
+
463
+ self.wait_for_answer = False
498
464
 
499
465
  if res:
500
- return [AskFileResponse(**r) for r in res]
466
+ return [
467
+ AskFileResponse(
468
+ id=r["id"],
469
+ name=r["name"],
470
+ path=str(r["path"]),
471
+ size=r["size"],
472
+ type=r["type"],
473
+ )
474
+ for r in res
475
+ ]
501
476
  else:
502
477
  return None
503
478
 
@@ -513,55 +488,49 @@ class AskActionMessage(AskMessageBase):
513
488
  content: str,
514
489
  actions: List[Action],
515
490
  author=config.ui.name,
516
- disable_human_feedback=False,
491
+ disable_feedback=False,
517
492
  timeout=90,
518
493
  raise_on_timeout=False,
519
494
  ):
520
495
  self.content = content
521
496
  self.actions = actions
522
497
  self.author = author
523
- self.disable_human_feedback = disable_human_feedback
498
+ self.disable_feedback = disable_feedback
524
499
  self.timeout = timeout
525
500
  self.raise_on_timeout = raise_on_timeout
526
501
 
527
502
  super().__post_init__()
528
503
 
529
- def to_dict(self):
530
- return {
531
- "id": self.id,
532
- "createdAt": self.created_at,
533
- "content": self.content,
534
- "author": self.author,
535
- "waitForAnswer": True,
536
- "disableHumanFeedback": self.disable_human_feedback,
537
- "timeout": self.timeout,
538
- "raiseOnTimeout": self.raise_on_timeout,
539
- }
540
-
541
504
  async def send(self) -> Union[AskActionResponse, None]:
542
505
  """
543
506
  Sends the question to ask to the UI and waits for the reply
544
507
  """
545
508
  trace_event("send_ask_action")
546
509
 
510
+ if not self.created_at:
511
+ self.created_at = datetime.utcnow().isoformat()
512
+
547
513
  if self.streaming:
548
514
  self.streaming = False
549
515
 
550
516
  if config.code.author_rename:
551
517
  self.author = await config.code.author_rename(self.author)
552
518
 
553
- msg_dict = await self._create()
519
+ self.wait_for_answer = True
520
+
521
+ step_dict = await self._create()
522
+
554
523
  action_keys = []
555
524
 
556
525
  for action in self.actions:
557
526
  action_keys.append(action.id)
558
- await action.send(for_id=str(msg_dict["id"]))
527
+ await action.send(for_id=str(step_dict["id"]))
559
528
 
560
529
  spec = AskActionSpec(type="action", timeout=self.timeout, keys=action_keys)
561
530
 
562
531
  res = cast(
563
532
  Union[AskActionResponse, None],
564
- await context.emitter.send_ask_user(msg_dict, spec, self.raise_on_timeout),
533
+ await context.emitter.send_ask_user(step_dict, spec, self.raise_on_timeout),
565
534
  )
566
535
 
567
536
  for action in self.actions:
@@ -570,6 +539,9 @@ class AskActionMessage(AskMessageBase):
570
539
  self.content = "Timed out: no action was taken"
571
540
  else:
572
541
  self.content = f'**Selected action:** {res["label"]}'
542
+
543
+ self.wait_for_answer = False
544
+
573
545
  await self.update()
574
546
 
575
547
  return res