chainlit 0.2.110__tar.gz → 0.3.0__tar.gz

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 (48) hide show
  1. {chainlit-0.2.110 → chainlit-0.3.0}/PKG-INFO +15 -14
  2. {chainlit-0.2.110 → chainlit-0.3.0}/README.md +3 -3
  3. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/__init__.py +48 -32
  4. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/action.py +12 -12
  5. chainlit-0.3.0/chainlit/cache.py +20 -0
  6. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/cli/__init__.py +45 -29
  7. chainlit-0.3.0/chainlit/cli/mock.py +48 -0
  8. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/client.py +111 -89
  9. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/config.py +22 -3
  10. chainlit-0.3.0/chainlit/element.py +159 -0
  11. chainlit-0.2.110/chainlit/sdk.py → chainlit-0.3.0/chainlit/emitter.py +55 -64
  12. chainlit-0.3.0/chainlit/frontend/dist/assets/index-0b7e367e.js +717 -0
  13. chainlit-0.3.0/chainlit/frontend/dist/assets/index-0cc9e355.css +1 -0
  14. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/frontend/dist/index.html +3 -3
  15. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/hello.py +3 -3
  16. chainlit-0.3.0/chainlit/lc/__init__.py +11 -0
  17. chainlit-0.3.0/chainlit/lc/agent.py +32 -0
  18. chainlit-0.3.0/chainlit/lc/callbacks.py +411 -0
  19. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/message.py +72 -96
  20. chainlit-0.3.0/chainlit/server.py +434 -0
  21. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/session.py +4 -2
  22. chainlit-0.3.0/chainlit/sync.py +37 -0
  23. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/types.py +18 -1
  24. chainlit-0.3.0/chainlit/user_session.py +44 -0
  25. {chainlit-0.2.110 → chainlit-0.3.0}/pyproject.toml +12 -11
  26. chainlit-0.2.110/chainlit/element.py +0 -188
  27. chainlit-0.2.110/chainlit/frontend/dist/assets/index-36bf9cab.js +0 -713
  28. chainlit-0.2.110/chainlit/frontend/dist/assets/index-bdffdaa0.css +0 -1
  29. chainlit-0.2.110/chainlit/lc/__init__.py +0 -0
  30. chainlit-0.2.110/chainlit/lc/chainlit_handler.py +0 -271
  31. chainlit-0.2.110/chainlit/lc/monkey.py +0 -28
  32. chainlit-0.2.110/chainlit/lc/new_monkey.py +0 -167
  33. chainlit-0.2.110/chainlit/lc/old_monkey.py +0 -119
  34. chainlit-0.2.110/chainlit/lc/utils.py +0 -38
  35. chainlit-0.2.110/chainlit/server.py +0 -349
  36. chainlit-0.2.110/chainlit/user_session.py +0 -44
  37. chainlit-0.2.110/chainlit/watch.py +0 -54
  38. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/__main__.py +0 -0
  39. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/cli/auth.py +0 -0
  40. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/cli/deploy.py +0 -0
  41. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/cli/utils.py +0 -0
  42. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/frontend/dist/assets/logo_dark-bc7401f6.svg +0 -0
  43. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/frontend/dist/assets/logo_light-f19fc2ea.svg +0 -0
  44. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/frontend/dist/favicon.svg +0 -0
  45. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/logger.py +0 -0
  46. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/markdown.py +0 -0
  47. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/telemetry.py +0 -0
  48. {chainlit-0.2.110 → chainlit-0.3.0}/chainlit/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: chainlit
3
- Version: 0.2.110
3
+ Version: 0.3.0
4
4
  Summary: A faster way to build chatbot UIs.
5
5
  Home-page: https://github.com/Chainlit/chainlit
6
6
  License: Apache-2.0 license
@@ -13,23 +13,24 @@ Classifier: Programming Language :: Python :: 3.8
13
13
  Classifier: Programming Language :: Python :: 3.9
14
14
  Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
+ Requires-Dist: aiofiles (>=23.1.0,<24.0.0)
17
+ Requires-Dist: aiohttp (>=3.8.4,<4.0.0)
18
+ Requires-Dist: asyncer (>=0.0.2,<0.0.3)
16
19
  Requires-Dist: auth0-python (>=4.1.1,<5.0.0)
17
20
  Requires-Dist: click (>=8.1.3,<9.0.0)
18
21
  Requires-Dist: dataclasses_json (>=0.5.7,<0.6.0)
19
- Requires-Dist: flask_cors (>=3.0.10,<4.0.0)
20
- Requires-Dist: flask_socketio (>=5.3.3,<6.0.0)
21
- Requires-Dist: gevent (>=22.10.2,<23.0.0)
22
- Requires-Dist: gevent_websocket (>=0.10.1,<0.11.0)
23
- Requires-Dist: openai (>=0.27.2,<0.28.0)
24
- Requires-Dist: pydantic (>=1.10.6,<2.0.0)
22
+ Requires-Dist: fastapi (>=0.96.0,<0.97.0)
23
+ Requires-Dist: fastapi-socketio (>=0.0.10,<0.0.11)
24
+ Requires-Dist: nest-asyncio (>=1.5.6,<2.0.0)
25
+ Requires-Dist: openai (>=0.27.7,<0.28.0)
26
+ Requires-Dist: pydantic (>=1.10.8,<2.0.0)
25
27
  Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
26
28
  Requires-Dist: python-graphql-client (>=0.4.3,<0.5.0)
29
+ Requires-Dist: syncer (>=2.0.3,<3.0.0)
27
30
  Requires-Dist: tomli (>=2.0.1,<3.0.0)
28
- Requires-Dist: typing-inspect (==0.8.0)
29
- Requires-Dist: typing_extensions (==4.5.0)
30
31
  Requires-Dist: uptrace (>=1.18.0,<2.0.0)
31
- Requires-Dist: watchdog (>=3.0.0,<4.0.0)
32
- Requires-Dist: watchdog_gevent (>=0.1.1,<0.2.0)
32
+ Requires-Dist: uvicorn (>=0.22.0,<0.23.0)
33
+ Requires-Dist: watchfiles (>=0.19.0,<0.20.0)
33
34
  Project-URL: Repository, https://github.com/Chainlit/chainlit
34
35
  Description-Content-Type: text/markdown
35
36
 
@@ -72,12 +73,12 @@ import chainlit as cl
72
73
 
73
74
 
74
75
  @cl.on_message # this function will be called every time a user inputs a message in the UI
75
- def main(message: str):
76
+ async def main(message: str):
76
77
  # this is an intermediate step
77
- cl.Message(author="Tool 1", content=f"Response from tool1", indent=1).send()
78
+ await cl.Message(author="Tool 1", content=f"Response from tool1", indent=1).send()
78
79
 
79
80
  # send back the final answer
80
- cl.Message(content=f"This is the final answer").send()
81
+ await cl.Message(content=f"This is the final answer").send()
81
82
  ```
82
83
 
83
84
  Now run it!
@@ -37,12 +37,12 @@ import chainlit as cl
37
37
 
38
38
 
39
39
  @cl.on_message # this function will be called every time a user inputs a message in the UI
40
- def main(message: str):
40
+ async def main(message: str):
41
41
  # this is an intermediate step
42
- cl.Message(author="Tool 1", content=f"Response from tool1", indent=1).send()
42
+ await cl.Message(author="Tool 1", content=f"Response from tool1", indent=1).send()
43
43
 
44
44
  # send back the final answer
45
- cl.Message(content=f"This is the final answer").send()
45
+ await cl.Message(content=f"This is the final answer").send()
46
46
  ```
47
47
 
48
48
  Now run it!
@@ -1,28 +1,28 @@
1
- import gevent
2
- from gevent import monkey
3
-
4
- monkey.patch_all()
5
-
6
- from chainlit.lc import monkey
7
-
8
- monkey.patch()
1
+ from dotenv import load_dotenv
2
+ from typing import Callable, Any
3
+ import inspect
4
+ import os
5
+ import asyncio
9
6
 
7
+ from chainlit.lc import LANGCHAIN_INSTALLED
10
8
  from chainlit.config import config
11
9
  from chainlit.telemetry import trace
12
10
  from chainlit.version import __version__
13
11
  from chainlit.logger import logger
14
- from chainlit.server import socketio
15
- from chainlit.sdk import get_sdk
12
+ from chainlit.emitter import ChainlitEmitter
16
13
  from chainlit.types import LLMSettings
17
14
  from chainlit.message import ErrorMessage
18
15
  from chainlit.action import Action
19
- from chainlit.element import LocalImage, RemoteImage, Text, Pdf
16
+ from chainlit.element import Image, Text, Pdf, Avatar
20
17
  from chainlit.message import Message, ErrorMessage, AskUserMessage, AskFileMessage
21
18
  from chainlit.user_session import user_session
22
- from typing import Callable, Any
23
- from dotenv import load_dotenv
24
- import inspect
25
- import os
19
+ from chainlit.sync import run_sync, make_async
20
+
21
+ if LANGCHAIN_INSTALLED:
22
+ from chainlit.lc.callbacks import (
23
+ ChainlitCallbackHandler,
24
+ AsyncChainlitCallbackHandler,
25
+ )
26
26
 
27
27
 
28
28
  env_found = load_dotenv(dotenv_path=os.path.join(os.getcwd(), ".env"))
@@ -42,8 +42,7 @@ def wrap_user_function(user_function: Callable, with_task=False) -> Callable:
42
42
  Callable: The wrapped function.
43
43
  """
44
44
 
45
- def wrapper(*args):
46
- sdk = get_sdk()
45
+ async def wrapper(*args, __chainlit_emitter__: ChainlitEmitter):
47
46
  # Get the parameter names of the user-defined function
48
47
  user_function_params = list(inspect.signature(user_function).parameters.keys())
49
48
 
@@ -52,25 +51,29 @@ def wrap_user_function(user_function: Callable, with_task=False) -> Callable:
52
51
  param_name: arg for param_name, arg in zip(user_function_params, args)
53
52
  }
54
53
 
55
- if with_task and sdk:
56
- sdk.task_start()
54
+ if with_task:
55
+ await __chainlit_emitter__.task_start()
57
56
 
58
57
  try:
59
58
  # Call the user-defined function with the arguments
60
- return user_function(**params_values)
59
+ if inspect.iscoroutinefunction(user_function):
60
+ return await user_function(**params_values)
61
+ else:
62
+ return user_function(**params_values)
63
+ except InterruptedError:
64
+ pass
61
65
  except Exception as e:
62
66
  logger.exception(e)
63
- if sdk:
64
- ErrorMessage(content=str(e), author="Error").send()
67
+ await ErrorMessage(content=str(e), author="Error").send()
65
68
  finally:
66
- if with_task and sdk:
67
- sdk.task_end()
69
+ if with_task:
70
+ await __chainlit_emitter__.task_end()
68
71
 
69
72
  return wrapper
70
73
 
71
74
 
72
75
  @trace
73
- def langchain_factory(func: Callable) -> Callable:
76
+ def langchain_factory(use_async: bool) -> Callable:
74
77
  """
75
78
  Plug and play decorator for the LangChain library.
76
79
  The decorated function should instantiate a new LangChain instance (Chain, Agent...).
@@ -78,14 +81,24 @@ def langchain_factory(func: Callable) -> Callable:
78
81
  The per user instance is called every time a new message is received.
79
82
 
80
83
  Args:
81
- func (Callable[[], Any]): The factory function to create a new LangChain instance.
84
+ use_async bool: Whether to call the the agent asynchronously or not. Defaults to False.
82
85
 
83
86
  Returns:
84
87
  Callable[[], Any]: The decorated factory function.
85
88
  """
86
89
 
87
- config.lc_factory = wrap_user_function(func, with_task=True)
88
- return func
90
+ # Check if the factory is called with the correct parameter
91
+ if type(use_async) != bool:
92
+ error_message = "langchain_factory use_async parameter is required"
93
+ raise ValueError(error_message)
94
+
95
+ def decorator(func: Callable) -> Callable:
96
+ config.lc_factory = wrap_user_function(func, with_task=True)
97
+ return func
98
+
99
+ config.lc_agent_is_async = use_async
100
+
101
+ return decorator
89
102
 
90
103
 
91
104
  @trace
@@ -136,7 +149,6 @@ def langchain_run(func: Callable[[Any, str], str]) -> Callable:
136
149
  Returns:
137
150
  Callable[[Any, str], Any]: The decorated function.
138
151
  """
139
-
140
152
  config.lc_run = wrap_user_function(func)
141
153
  return func
142
154
 
@@ -209,7 +221,7 @@ def sleep(duration: int):
209
221
  Args:
210
222
  duration (int): The duration in seconds.
211
223
  """
212
- return socketio.sleep(duration)
224
+ return asyncio.sleep(duration)
213
225
 
214
226
 
215
227
  __all__ = [
@@ -217,9 +229,9 @@ __all__ = [
217
229
  "LLMSettings",
218
230
  "Action",
219
231
  "Pdf",
220
- "LocalImage",
221
- "RemoteImage",
232
+ "Image",
222
233
  "Text",
234
+ "Avatar",
223
235
  "Message",
224
236
  "ErrorMessage",
225
237
  "AskUserMessage",
@@ -232,4 +244,8 @@ __all__ = [
232
244
  "on_stop",
233
245
  "action_callback",
234
246
  "sleep",
247
+ "ChainlitCallbackHandler",
248
+ "AsyncChainlitCallbackHandler",
249
+ "run_sync",
250
+ "make_async",
235
251
  ]
@@ -1,6 +1,7 @@
1
1
  from pydantic.dataclasses import dataclass
2
2
  from dataclasses_json import dataclass_json
3
- from chainlit.sdk import get_emit
3
+
4
+ from chainlit.emitter import get_emit_fn
4
5
  from chainlit.telemetry import trace_event
5
6
 
6
7
 
@@ -20,16 +21,15 @@ class Action:
20
21
 
21
22
  def __post_init__(self) -> None:
22
23
  trace_event(f"init {self.__class__.__name__}")
24
+ self.emit = get_emit_fn()
25
+ if not self.emit:
26
+ raise RuntimeError("Action should be instantiated in a Chainlit context")
23
27
 
24
- def send(self, for_id: str):
25
- emit = get_emit()
26
- if emit:
27
- trace_event(f"send {self.__class__.__name__}")
28
- self.forId = for_id
29
- emit("action", self.to_dict())
28
+ async def send(self, for_id: str):
29
+ trace_event(f"send {self.__class__.__name__}")
30
+ self.forId = for_id
31
+ await self.emit("action", self.to_dict())
30
32
 
31
- def remove(self):
32
- emit = get_emit()
33
- if emit:
34
- trace_event(f"remove {self.__class__.__name__}")
35
- emit("remove_action", self.to_dict())
33
+ async def remove(self):
34
+ trace_event(f"remove {self.__class__.__name__}")
35
+ await self.emit("remove_action", self.to_dict())
@@ -0,0 +1,20 @@
1
+ import os
2
+
3
+ from chainlit.config import config
4
+ from chainlit.logger import logger
5
+ from chainlit.lc import LANGCHAIN_INSTALLED
6
+
7
+
8
+ def init_lc_cache():
9
+ use_cache = (
10
+ config.run_settings.no_cache is False and config.run_settings.ci is False
11
+ )
12
+
13
+ if LANGCHAIN_INSTALLED and use_cache:
14
+ import langchain
15
+ from langchain.cache import SQLiteCache
16
+
17
+ if config.lc_cache_path:
18
+ langchain.llm_cache = SQLiteCache(database_path=config.lc_cache_path)
19
+ if not os.path.exists(config.lc_cache_path):
20
+ logger.info(f"LangChain cache enabled: {config.lc_cache_path}")
@@ -1,15 +1,28 @@
1
1
  import click
2
2
  import os
3
3
  import sys
4
- import webbrowser
5
- from chainlit.config import config, init_config, load_module
6
- from chainlit.watch import watch_directory
4
+ import uvicorn
5
+ import asyncio
6
+ import nest_asyncio
7
+
8
+ nest_asyncio.apply()
9
+
10
+ from chainlit.config import (
11
+ config,
12
+ init_config,
13
+ load_module,
14
+ PACKAGE_ROOT,
15
+ DEFAULT_HOST,
16
+ DEFAULT_PORT,
17
+ )
7
18
  from chainlit.markdown import init_markdown
8
19
  from chainlit.cli.auth import login, logout
9
20
  from chainlit.cli.deploy import deploy
10
21
  from chainlit.cli.utils import check_file
11
22
  from chainlit.telemetry import trace_event
23
+ from chainlit.cache import init_lc_cache
12
24
  from chainlit.logger import logger
25
+ from chainlit.server import app
13
26
 
14
27
 
15
28
  # Create the main command group for Chainlit CLI
@@ -20,11 +33,11 @@ def cli():
20
33
 
21
34
 
22
35
  # Define the function to run Chainlit with provided options
23
- def run_chainlit(target: str, watch=False, headless=False, debug=False):
24
- DEFAULT_HOST = "0.0.0.0"
25
- DEFAULT_PORT = 8000
36
+ def run_chainlit(target: str):
26
37
  host = os.environ.get("CHAINLIT_HOST", DEFAULT_HOST)
27
38
  port = int(os.environ.get("CHAINLIT_PORT", DEFAULT_PORT))
39
+ config.run_settings.host = host
40
+ config.run_settings.port = port
28
41
 
29
42
  check_file(target)
30
43
  # Load the module provided by the user
@@ -34,28 +47,20 @@ def run_chainlit(target: str, watch=False, headless=False, debug=False):
34
47
  # Create the chainlit.md file if it doesn't exist
35
48
  init_markdown(config.root)
36
49
 
37
- # Enable file watching if the user specified it
38
- if watch:
39
- watch_directory()
50
+ # Initialize the LangChain cache if installed and enabled
51
+ init_lc_cache()
40
52
 
41
- from chainlit.server import socketio, app
53
+ log_level = "debug" if config.run_settings.debug else "error"
42
54
 
43
- # Open the browser if in development mode
44
- def open_browser(headless: bool):
45
- if not headless:
46
- if host == DEFAULT_HOST:
47
- url = f"http://localhost:{port}"
48
- else:
49
- url = f"http://{host}:{port}"
50
- # Wait two seconds to allow the server to start
51
- socketio.sleep(2)
52
-
53
- logger.info(f"Your app is available at {url}")
54
- webbrowser.open(url)
55
-
56
- socketio.start_background_task(open_browser, headless)
57
55
  # Start the server
58
- socketio.run(app, host=host, port=port, debug=debug, use_reloader=False)
56
+ async def start():
57
+ config = uvicorn.Config(app, host=host, port=port, log_level=log_level)
58
+ server = uvicorn.Server(config)
59
+ await server.serve()
60
+
61
+ # Run the asyncio event loop instead of uvloop to enable re entrance
62
+ asyncio.run(start())
63
+ # uvicorn.run(app, host=host, port=port)
59
64
 
60
65
 
61
66
  # Define the "run" command for Chainlit CLI
@@ -65,9 +70,10 @@ def run_chainlit(target: str, watch=False, headless=False, debug=False):
65
70
  @click.option("-h", "--headless", default=False, is_flag=True, envvar="HEADLESS")
66
71
  @click.option("-d", "--debug", default=False, is_flag=True, envvar="DEBUG")
67
72
  @click.option("-c", "--ci", default=False, is_flag=True, envvar="CI")
73
+ @click.option("--no-cache", default=False, is_flag=True, envvar="NO_CACHE")
68
74
  @click.option("--host")
69
75
  @click.option("--port")
70
- def chainlit_run(target, watch, headless, debug, ci, host, port):
76
+ def chainlit_run(target, watch, headless, debug, ci, no_cache, host, port):
71
77
  if host:
72
78
  os.environ["CHAINLIT_HOST"] = host
73
79
  if port:
@@ -75,10 +81,21 @@ def chainlit_run(target, watch, headless, debug, ci, host, port):
75
81
  if ci:
76
82
  logger.info("Running in CI mode")
77
83
  config.enable_telemetry = False
84
+ no_cache = True
85
+ from chainlit.cli.mock import mock_openai
86
+
87
+ mock_openai()
88
+
78
89
  else:
79
90
  trace_event("chainlit run")
80
91
 
81
- run_chainlit(target, watch, headless, debug)
92
+ config.run_settings.headless = headless
93
+ config.run_settings.debug = debug
94
+ config.run_settings.no_cache = no_cache
95
+ config.run_settings.ci = ci
96
+ config.run_settings.watch = watch
97
+
98
+ run_chainlit(target)
82
99
 
83
100
 
84
101
  @cli.command("deploy")
@@ -94,8 +111,7 @@ def chainlit_deploy(target, args=None, **kwargs):
94
111
  @click.argument("args", nargs=-1)
95
112
  def chainlit_hello(args=None, **kwargs):
96
113
  trace_event("chainlit hello")
97
- dir_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
98
- hello_path = os.path.join(dir_path, "hello.py")
114
+ hello_path = os.path.join(PACKAGE_ROOT, "hello.py")
99
115
  run_chainlit(hello_path)
100
116
 
101
117
 
@@ -0,0 +1,48 @@
1
+ def mock_openai():
2
+ import os
3
+ from aioresponses import aioresponses
4
+ import responses
5
+
6
+ # Mock the openai api key
7
+ os.environ["OPENAI_API_KEY"] = "sk-FAKE-OPENAI-API-KEY"
8
+
9
+ mocked_json_reply = {
10
+ "id": "cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7",
11
+ "object": "text_completion",
12
+ "created": 1589478378,
13
+ "model": "text-davinci-003",
14
+ "choices": [
15
+ {
16
+ "text": "\n\n```text\n3*3\n```",
17
+ "index": 0,
18
+ "logprobs": None,
19
+ "finish_reason": "length",
20
+ }
21
+ ],
22
+ "usage": {
23
+ "prompt_tokens": 5,
24
+ "completion_tokens": 7,
25
+ "total_tokens": 12,
26
+ },
27
+ }
28
+
29
+ # Mock the sync openai api
30
+
31
+ responses.start()
32
+
33
+ responses.add(
34
+ responses.POST,
35
+ "https://api.openai.com/v1/completions",
36
+ json=mocked_json_reply,
37
+ )
38
+
39
+ # Mock the async openai api
40
+ aioresponses_obj = aioresponses()
41
+
42
+ aioresponses_obj.start()
43
+
44
+ aioresponses_obj.add(
45
+ url="https://api.openai.com/v1/completions",
46
+ method="POST",
47
+ payload=mocked_json_reply,
48
+ )