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
chainlit/cli/__init__.py CHANGED
@@ -5,24 +5,38 @@ import click
5
5
  import nest_asyncio
6
6
  import uvicorn
7
7
 
8
+ # Not sure if it is necessary to call nest_asyncio.apply() before the other imports
8
9
  nest_asyncio.apply()
9
10
 
11
+ # ruff: noqa: E402
10
12
  from chainlit.auth import ensure_jwt_secret
11
13
  from chainlit.cache import init_lc_cache
12
- from chainlit.cli.utils import check_file
13
14
  from chainlit.config import (
14
15
  BACKEND_ROOT,
15
16
  DEFAULT_HOST,
16
17
  DEFAULT_PORT,
18
+ DEFAULT_ROOT_PATH,
17
19
  config,
18
20
  init_config,
21
+ lint_translations,
19
22
  load_module,
20
23
  )
21
24
  from chainlit.logger import logger
22
25
  from chainlit.markdown import init_markdown
23
26
  from chainlit.secret import random_secret
24
- from chainlit.server import app, register_wildcard_route_handler
25
27
  from chainlit.telemetry import trace_event
28
+ from chainlit.utils import check_file
29
+
30
+
31
+ def assert_app():
32
+ if (
33
+ not config.code.on_chat_start
34
+ and not config.code.on_message
35
+ and not config.code.on_audio_chunk
36
+ ):
37
+ raise Exception(
38
+ "You need to configure at least one of on_chat_start, on_message or on_audio_chunk callback"
39
+ )
26
40
 
27
41
 
28
42
  # Create the main command group for Chainlit CLI
@@ -34,8 +48,14 @@ def cli():
34
48
 
35
49
  # Define the function to run Chainlit with provided options
36
50
  def run_chainlit(target: str):
51
+ from chainlit.server import app
52
+
37
53
  host = os.environ.get("CHAINLIT_HOST", DEFAULT_HOST)
38
54
  port = int(os.environ.get("CHAINLIT_PORT", DEFAULT_PORT))
55
+ root_path = os.environ.get("CHAINLIT_ROOT_PATH", DEFAULT_ROOT_PATH)
56
+
57
+ ssl_certfile = os.environ.get("CHAINLIT_SSL_CERT", None)
58
+ ssl_keyfile = os.environ.get("CHAINLIT_SSL_KEY", None)
39
59
 
40
60
  ws_per_message_deflate_env = os.environ.get(
41
61
  "UVICORN_WS_PER_MESSAGE_DEFLATE", "true"
@@ -46,8 +66,11 @@ def run_chainlit(target: str):
46
66
  "yes",
47
67
  ] # Convert to boolean
48
68
 
69
+ ws_protocol = os.environ.get("UVICORN_WS_PROTOCOL", "auto")
70
+
49
71
  config.run.host = host
50
72
  config.run.port = port
73
+ config.run.root_path = root_path
51
74
 
52
75
  check_file(target)
53
76
  # Load the module provided by the user
@@ -55,8 +78,7 @@ def run_chainlit(target: str):
55
78
  load_module(config.run.module_name)
56
79
 
57
80
  ensure_jwt_secret()
58
-
59
- register_wildcard_route_handler()
81
+ assert_app()
60
82
 
61
83
  # Create the chainlit.md file if it doesn't exist
62
84
  init_markdown(config.root)
@@ -72,8 +94,11 @@ def run_chainlit(target: str):
72
94
  app,
73
95
  host=host,
74
96
  port=port,
97
+ ws=ws_protocol,
75
98
  log_level=log_level,
76
99
  ws_per_message_deflate=ws_per_message_deflate,
100
+ ssl_keyfile=ssl_keyfile,
101
+ ssl_certfile=ssl_certfile,
77
102
  )
78
103
  server = uvicorn.Server(config)
79
104
  await server.serve()
@@ -125,13 +150,47 @@ def run_chainlit(target: str):
125
150
  envvar="NO_CACHE",
126
151
  help="Useful to disable third parties cache, such as langchain.",
127
152
  )
153
+ @click.option(
154
+ "--ssl-cert",
155
+ default=None,
156
+ envvar="CHAINLIT_SSL_CERT",
157
+ help="Specify the file path for the SSL certificate.",
158
+ )
159
+ @click.option(
160
+ "--ssl-key",
161
+ default=None,
162
+ envvar="CHAINLIT_SSL_KEY",
163
+ help="Specify the file path for the SSL key",
164
+ )
128
165
  @click.option("--host", help="Specify a different host to run the server on")
129
166
  @click.option("--port", help="Specify a different port to run the server on")
130
- def chainlit_run(target, watch, headless, debug, ci, no_cache, host, port):
167
+ @click.option("--root-path", help="Specify a different root path to run the server on")
168
+ def chainlit_run(
169
+ target,
170
+ watch,
171
+ headless,
172
+ debug,
173
+ ci,
174
+ no_cache,
175
+ ssl_cert,
176
+ ssl_key,
177
+ host,
178
+ port,
179
+ root_path,
180
+ ):
131
181
  if host:
132
182
  os.environ["CHAINLIT_HOST"] = host
133
183
  if port:
134
184
  os.environ["CHAINLIT_PORT"] = port
185
+ if bool(ssl_cert) != bool(ssl_key):
186
+ raise click.UsageError(
187
+ "Both --ssl-cert and --ssl-key must be provided together."
188
+ )
189
+ if ssl_cert:
190
+ os.environ["CHAINLIT_SSL_CERT"] = ssl_cert
191
+ os.environ["CHAINLIT_SSL_KEY"] = ssl_key
192
+ if root_path:
193
+ os.environ["CHAINLIT_ROOT_PATH"] = root_path
135
194
  if ci:
136
195
  logger.info("Running in CI mode")
137
196
 
@@ -139,8 +198,8 @@ def chainlit_run(target, watch, headless, debug, ci, no_cache, host, port):
139
198
  no_cache = True
140
199
  # This is required to have OpenAI LLM providers available for the CI run
141
200
  os.environ["OPENAI_API_KEY"] = "sk-FAKE-OPENAI-API-KEY"
142
- # This is required for authenticationt tests
143
- os.environ["CHAINLIT_AUTH_SECRET"] = "SUPER_SECRET"
201
+ # This is required for authentication tests
202
+ os.environ["CHAINLIT_AUTH_SECRET"] = "SUPER_SECRET" # nosec B105
144
203
  else:
145
204
  trace_event("chainlit run")
146
205
 
@@ -149,6 +208,8 @@ def chainlit_run(target, watch, headless, debug, ci, no_cache, host, port):
149
208
  config.run.no_cache = no_cache
150
209
  config.run.ci = ci
151
210
  config.run.watch = watch
211
+ config.run.ssl_cert = ssl_cert
212
+ config.run.ssl_key = ssl_key
152
213
 
153
214
  run_chainlit(target)
154
215
 
@@ -174,5 +235,13 @@ def chainlit_create_secret(args=None, **kwargs):
174
235
  trace_event("chainlit secret")
175
236
 
176
237
  print(
177
- f"Copy the following secret into your .env file. Once it is set, changing it will logout all users with active sessions.\nCHAINLIT_AUTH_SECRET={random_secret()}"
238
+ f'Copy the following secret into your .env file. Once it is set, changing it will logout all users with active sessions.\nCHAINLIT_AUTH_SECRET="{random_secret()}"'
178
239
  )
240
+
241
+
242
+ @cli.command("lint-translations")
243
+ @click.argument("args", nargs=-1)
244
+ def chainlit_lint_translations(args=None, **kwargs):
245
+ trace_event("chainlit lint-translation")
246
+
247
+ lint_translations()
chainlit/config.py CHANGED
@@ -1,23 +1,46 @@
1
1
  import json
2
2
  import os
3
+ import site
3
4
  import sys
4
5
  from importlib import util
5
6
  from pathlib import Path
6
- from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional
7
+ from typing import (
8
+ TYPE_CHECKING,
9
+ Any,
10
+ Awaitable,
11
+ Callable,
12
+ Dict,
13
+ List,
14
+ Literal,
15
+ Optional,
16
+ Union,
17
+ )
7
18
 
8
19
  import tomli
9
- from chainlit.logger import logger
10
- from chainlit.version import __version__
11
20
  from dataclasses_json import DataClassJsonMixin
12
- from pydantic.dataclasses import Field, dataclass
21
+ from pydantic import Field
22
+ from pydantic.dataclasses import dataclass
13
23
  from starlette.datastructures import Headers
14
24
 
25
+ from chainlit.data.base import BaseDataLayer
26
+ from chainlit.logger import logger
27
+ from chainlit.translations import lint_translation_json
28
+ from chainlit.version import __version__
29
+
30
+ from ._utils import is_path_inside
31
+
15
32
  if TYPE_CHECKING:
16
- from chainlit.action import Action
17
- from chainlit.types import ChatProfile, ThreadDict
18
- from chainlit.user import User
19
33
  from fastapi import Request, Response
20
34
 
35
+ from chainlit.action import Action
36
+ from chainlit.message import Message
37
+ from chainlit.types import ChatProfile, InputAudioChunk, Starter, ThreadDict
38
+ from chainlit.user import User
39
+ else:
40
+ # Pydantic needs to resolve forward annotations. Because all of these are used
41
+ # within `typing.Callable`, alias to `Any` as Pydantic does not perform validation
42
+ # of callable argument/return types anyway.
43
+ Request = Response = Action = Message = ChatProfile = InputAudioChunk = Starter = ThreadDict = User = Any # fmt: off
21
44
 
22
45
  BACKEND_ROOT = os.path.dirname(__file__)
23
46
  PACKAGE_ROOT = os.path.dirname(os.path.dirname(BACKEND_ROOT))
@@ -25,13 +48,14 @@ TRANSLATIONS_DIR = os.path.join(BACKEND_ROOT, "translations")
25
48
 
26
49
 
27
50
  # Get the directory the script is running from
28
- APP_ROOT = os.getcwd()
51
+ APP_ROOT = os.getenv("CHAINLIT_APP_ROOT", os.getcwd())
29
52
 
30
53
  # Create the directory to store the uploaded files
31
54
  FILES_DIRECTORY = Path(APP_ROOT) / ".files"
32
55
  FILES_DIRECTORY.mkdir(exist_ok=True)
33
56
 
34
57
  config_dir = os.path.join(APP_ROOT, ".chainlit")
58
+ public_dir = os.path.join(APP_ROOT, "public")
35
59
  config_file = os.path.join(config_dir, "config.toml")
36
60
  config_translation_dir = os.path.join(config_dir, "translations")
37
61
 
@@ -47,52 +71,61 @@ user_env = []
47
71
  # Duration (in seconds) during which the session is saved when the connection is lost
48
72
  session_timeout = 3600
49
73
 
74
+ # Duration (in seconds) of the user session expiry
75
+ user_session_timeout = 1296000 # 15 days
76
+
50
77
  # Enable third parties caching (e.g LangChain cache)
51
78
  cache = false
52
79
 
53
- # Authorized origins
80
+ # Authorized origins
54
81
  allow_origins = ["*"]
55
82
 
56
- # Follow symlink for asset mount (see https://github.com/Chainlit/chainlit/issues/317)
57
- # follow_symlink = false
58
-
59
83
  [features]
60
- # Show the prompt playground
61
- prompt_playground = true
62
-
63
84
  # Process and display HTML in messages. This can be a security risk (see https://stackoverflow.com/questions/19603097/why-is-it-dangerous-to-render-user-generated-html-or-javascript)
64
85
  unsafe_allow_html = false
65
86
 
66
87
  # Process and display mathematical expressions. This can clash with "$" characters in messages.
67
88
  latex = false
68
89
 
69
- # Authorize users to upload files with messages
70
- multi_modal = true
71
-
72
- # Allows user to use speech to text
73
- [features.speech_to_text]
74
- enabled = false
75
- # See all languages here https://github.com/JamesBrill/react-speech-recognition/blob/HEAD/docs/API.md#language-string
76
- # language = "en-US"
90
+ # Automatically tag threads with the current chat profile (if a chat profile is used)
91
+ auto_tag_thread = true
92
+
93
+ # Allow users to edit their own messages
94
+ edit_message = true
95
+
96
+ # Authorize users to spontaneously upload files with messages
97
+ [features.spontaneous_file_upload]
98
+ enabled = true
99
+ # Define accepted file types using MIME types
100
+ # Examples:
101
+ # 1. For specific file types:
102
+ # accept = ["image/jpeg", "image/png", "application/pdf"]
103
+ # 2. For all files of certain type:
104
+ # accept = ["image/*", "audio/*", "video/*"]
105
+ # 3. For specific file extensions:
106
+ # accept = {{ "application/octet-stream" = [".xyz", ".pdb"] }}
107
+ # Note: Using "*/*" is not recommended as it may cause browser warnings
108
+ accept = ["*/*"]
109
+ max_files = 20
110
+ max_size_mb = 500
111
+
112
+ [features.audio]
113
+ # Sample rate of the audio
114
+ sample_rate = 24000
77
115
 
78
116
  [UI]
79
- # Name of the app and chatbot.
80
- name = "Chatbot"
81
-
82
- # Show the readme while the thread is empty.
83
- show_readme_as_default = true
117
+ # Name of the assistant.
118
+ name = "Assistant"
84
119
 
85
- # Description of the app and chatbot. This is used for HTML tags.
86
- # description = ""
120
+ # default_theme = "dark"
87
121
 
88
- # Large size content are by default collapsed for a cleaner ui
89
- default_collapse_content = true
122
+ # layout = "wide"
90
123
 
91
- # The default value for the expand messages settings.
92
- default_expand_messages = false
124
+ # Description of the assistant. This is used for HTML tags.
125
+ # description = ""
93
126
 
94
- # Hide the chain of thought details from the user in the UI.
95
- hide_cot = false
127
+ # Chain of Thought (CoT) display mode. Can be "hidden", "tool_call" or "full".
128
+ cot = "full"
96
129
 
97
130
  # Link to your github repo. This will add a github button in the UI's header.
98
131
  # github = ""
@@ -105,39 +138,22 @@ hide_cot = false
105
138
  # The Javascript file can be served from the public directory.
106
139
  # custom_js = "/public/test.js"
107
140
 
108
- # Specify a custom font url.
109
- # custom_font = "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap"
110
-
111
- # Override default MUI light theme. (Check theme.ts)
112
- [UI.theme]
113
- #font_family = "Inter, sans-serif"
114
- [UI.theme.light]
115
- #background = "#FAFAFA"
116
- #paper = "#FFFFFF"
117
-
118
- [UI.theme.light.primary]
119
- #main = "#F80061"
120
- #dark = "#980039"
121
- #light = "#FFE7EB"
122
-
123
- # Override default MUI dark theme. (Check theme.ts)
124
- [UI.theme.dark]
125
- #background = "#FAFAFA"
126
- #paper = "#FFFFFF"
127
-
128
- [UI.theme.dark.primary]
129
- #main = "#F80061"
130
- #dark = "#980039"
131
- #light = "#FFE7EB"
141
+ # Specify a custom meta image url.
142
+ # custom_meta_image_url = "https://chainlit-cloud.s3.eu-west-3.amazonaws.com/logo/chainlit_banner.png"
132
143
 
144
+ # Specify a custom build directory for the frontend.
145
+ # This can be used to customize the frontend code.
146
+ # Be careful: If this is a relative path, it should not start with a slash.
147
+ # custom_build = "./public/build"
133
148
 
134
149
  [meta]
135
150
  generated_by = "{__version__}"
136
151
  """
137
152
 
138
153
 
139
- DEFAULT_HOST = "0.0.0.0"
154
+ DEFAULT_HOST = "127.0.0.1"
140
155
  DEFAULT_PORT = 8000
156
+ DEFAULT_ROOT_PATH = ""
141
157
 
142
158
 
143
159
  @dataclass()
@@ -146,6 +162,9 @@ class RunSettings:
146
162
  module_name: Optional[str] = None
147
163
  host: str = DEFAULT_HOST
148
164
  port: int = DEFAULT_PORT
165
+ ssl_cert: Optional[str] = None
166
+ ssl_key: Optional[str] = None
167
+ root_path: str = DEFAULT_ROOT_PATH
149
168
  headless: bool = False
150
169
  watch: bool = False
151
170
  no_cache: bool = False
@@ -160,50 +179,60 @@ class PaletteOptions(DataClassJsonMixin):
160
179
  dark: Optional[str] = ""
161
180
 
162
181
 
182
+ @dataclass()
183
+ class TextOptions(DataClassJsonMixin):
184
+ primary: Optional[str] = ""
185
+ secondary: Optional[str] = ""
186
+
187
+
163
188
  @dataclass()
164
189
  class Palette(DataClassJsonMixin):
165
190
  primary: Optional[PaletteOptions] = None
166
191
  background: Optional[str] = ""
167
192
  paper: Optional[str] = ""
193
+ text: Optional[TextOptions] = None
168
194
 
169
195
 
170
- @dataclass()
171
- class Theme(DataClassJsonMixin):
172
- font_family: Optional[str] = None
173
- light: Optional[Palette] = None
174
- dark: Optional[Palette] = None
196
+ @dataclass
197
+ class SpontaneousFileUploadFeature(DataClassJsonMixin):
198
+ enabled: Optional[bool] = None
199
+ accept: Optional[Union[List[str], Dict[str, List[str]]]] = None
200
+ max_files: Optional[int] = None
201
+ max_size_mb: Optional[int] = None
175
202
 
176
203
 
177
204
  @dataclass
178
- class SpeechToTextFeature:
179
- enabled: Optional[bool] = None
180
- language: Optional[str] = None
205
+ class AudioFeature(DataClassJsonMixin):
206
+ sample_rate: int = 24000
207
+ enabled: bool = False
181
208
 
182
209
 
183
210
  @dataclass()
184
211
  class FeaturesSettings(DataClassJsonMixin):
185
- prompt_playground: bool = True
186
- multi_modal: bool = True
212
+ spontaneous_file_upload: Optional[SpontaneousFileUploadFeature] = None
213
+ audio: Optional[AudioFeature] = Field(default_factory=AudioFeature)
187
214
  latex: bool = False
188
215
  unsafe_allow_html: bool = False
189
- speech_to_text: Optional[SpeechToTextFeature] = None
216
+ auto_tag_thread: bool = True
217
+ edit_message: bool = True
190
218
 
191
219
 
192
220
  @dataclass()
193
221
  class UISettings(DataClassJsonMixin):
194
222
  name: str
195
- show_readme_as_default: bool = True
196
223
  description: str = ""
197
- hide_cot: bool = False
198
- # Large size content are by default collapsed for a cleaner ui
199
- default_collapse_content: bool = True
200
- default_expand_messages: bool = False
224
+ cot: Literal["hidden", "tool_call", "full"] = "full"
225
+ font_family: Optional[str] = None
226
+ default_theme: Optional[Literal["light", "dark"]] = "dark"
227
+ layout: Optional[Literal["default", "wide"]] = "default"
201
228
  github: Optional[str] = None
202
- theme: Optional[Theme] = None
203
229
  # Optional custom CSS file that allows you to customize the UI
204
230
  custom_css: Optional[str] = None
205
231
  custom_js: Optional[str] = None
206
- custom_font: Optional[str] = None
232
+ # Optional custom meta tag for image preview
233
+ custom_meta_image_url: Optional[str] = None
234
+ # Optional custom build directory for the frontend
235
+ custom_build: Optional[str] = None
207
236
 
208
237
 
209
238
  @dataclass()
@@ -213,27 +242,42 @@ class CodeSettings:
213
242
  # Module object loaded from the module_name
214
243
  module: Any = None
215
244
  # Bunch of callbacks defined by the developer
216
- password_auth_callback: Optional[Callable[[str, str], Optional["User"]]] = None
217
- header_auth_callback: Optional[Callable[[Headers], Optional["User"]]] = None
245
+ password_auth_callback: Optional[
246
+ Callable[[str, str], Awaitable[Optional["User"]]]
247
+ ] = None
248
+ header_auth_callback: Optional[Callable[[Headers], Awaitable[Optional["User"]]]] = (
249
+ None
250
+ )
218
251
  oauth_callback: Optional[
219
- Callable[[str, str, Dict[str, str], "User"], Optional["User"]]
252
+ Callable[[str, str, Dict[str, str], "User"], Awaitable[Optional["User"]]]
220
253
  ] = None
221
254
  on_logout: Optional[Callable[["Request", "Response"], Any]] = None
222
255
  on_stop: Optional[Callable[[], Any]] = None
223
256
  on_chat_start: Optional[Callable[[], Any]] = None
224
257
  on_chat_end: Optional[Callable[[], Any]] = None
225
258
  on_chat_resume: Optional[Callable[["ThreadDict"], Any]] = None
226
- on_message: Optional[Callable[[str], Any]] = None
227
- author_rename: Optional[Callable[[str], str]] = None
259
+ on_message: Optional[Callable[["Message"], Any]] = None
260
+ on_window_message: Optional[Callable[[str], Any]] = None
261
+ on_audio_start: Optional[Callable[[], Any]] = None
262
+ on_audio_chunk: Optional[Callable[["InputAudioChunk"], Any]] = None
263
+ on_audio_end: Optional[Callable[[], Any]] = None
264
+
265
+ author_rename: Optional[Callable[[str], Awaitable[str]]] = None
228
266
  on_settings_update: Optional[Callable[[Dict[str, Any]], Any]] = None
229
- set_chat_profiles: Optional[Callable[[Optional["User"]], List["ChatProfile"]]] = (
267
+ set_chat_profiles: Optional[
268
+ Callable[[Optional["User"]], Awaitable[List["ChatProfile"]]]
269
+ ] = None
270
+ set_starters: Optional[Callable[[Optional["User"]], Awaitable[List["Starter"]]]] = (
230
271
  None
231
272
  )
273
+ data_layer: Optional[Callable[[], BaseDataLayer]] = None
232
274
 
233
275
 
234
276
  @dataclass()
235
277
  class ProjectSettings(DataClassJsonMixin):
236
278
  allow_origins: List[str] = Field(default_factory=lambda: ["*"])
279
+ # Socket.io client transports option
280
+ transports: Optional[List[str]] = None
237
281
  enable_telemetry: bool = True
238
282
  # List of environment variables to be provided by each user to use the app. If empty, no environment variables will be asked to the user.
239
283
  user_env: Optional[List[str]] = None
@@ -242,10 +286,10 @@ class ProjectSettings(DataClassJsonMixin):
242
286
  # Path to the local chat db
243
287
  # Duration (in seconds) during which the session is saved when the connection is lost
244
288
  session_timeout: int = 3600
289
+ # Duration (in seconds) of the user session expiry
290
+ user_session_timeout: int = 1296000 # 15 days
245
291
  # Enable third parties caching (e.g LangChain cache)
246
292
  cache: bool = False
247
- # Follow symlink for asset mount (see https://github.com/Chainlit/chainlit/issues/317)
248
- follow_symlink: bool = False
249
293
 
250
294
 
251
295
  @dataclass()
@@ -263,23 +307,44 @@ class ChainlitConfig:
263
307
  def load_translation(self, language: str):
264
308
  translation = {}
265
309
  default_language = "en-US"
310
+ # fallback to root language (ex: `de` when `de-DE` is not found)
311
+ parent_language = language.split("-")[0]
266
312
 
267
- translation_lib_file_path = os.path.join(
268
- config_translation_dir, f"{language}.json"
269
- )
270
- default_translation_lib_file_path = os.path.join(
271
- config_translation_dir, f"{default_language}.json"
272
- )
313
+ translation_dir = Path(config_translation_dir)
273
314
 
274
- if os.path.exists(translation_lib_file_path):
275
- with open(translation_lib_file_path, "r", encoding="utf-8") as f:
276
- translation = json.load(f)
277
- elif os.path.exists(default_translation_lib_file_path):
315
+ translation_lib_file_path = translation_dir / f"{language}.json"
316
+ translation_lib_parent_language_file_path = (
317
+ translation_dir / f"{parent_language}.json"
318
+ )
319
+ default_translation_lib_file_path = translation_dir / f"{default_language}.json"
320
+
321
+ if (
322
+ is_path_inside(translation_lib_file_path, translation_dir)
323
+ and translation_lib_file_path.is_file()
324
+ ):
325
+ translation = json.loads(
326
+ translation_lib_file_path.read_text(encoding="utf-8")
327
+ )
328
+ elif (
329
+ is_path_inside(translation_lib_parent_language_file_path, translation_dir)
330
+ and translation_lib_parent_language_file_path.is_file()
331
+ ):
332
+ logger.warning(
333
+ f"Translation file for {language} not found. Using parent translation {parent_language}."
334
+ )
335
+ translation = json.loads(
336
+ translation_lib_parent_language_file_path.read_text(encoding="utf-8")
337
+ )
338
+ elif (
339
+ is_path_inside(default_translation_lib_file_path, translation_dir)
340
+ and default_translation_lib_file_path.is_file()
341
+ ):
278
342
  logger.warning(
279
343
  f"Translation file for {language} not found. Using default translation {default_language}."
280
344
  )
281
- with open(default_translation_lib_file_path, "r", encoding="utf-8") as f:
282
- translation = json.load(f)
345
+ translation = json.loads(
346
+ default_translation_lib_file_path.read_text(encoding="utf-8")
347
+ )
283
348
 
284
349
  return translation
285
350
 
@@ -305,7 +370,7 @@ def init_config(log=False):
305
370
  dst = os.path.join(config_translation_dir, file)
306
371
  if not os.path.exists(dst):
307
372
  src = os.path.join(TRANSLATIONS_DIR, file)
308
- with open(src, "r", encoding="utf-8") as f:
373
+ with open(src, encoding="utf-8") as f:
309
374
  translation = json.load(f)
310
375
  with open(dst, "w", encoding="utf-8") as f:
311
376
  json.dump(translation, f, indent=4)
@@ -322,12 +387,16 @@ def load_module(target: str, force_refresh: bool = False):
322
387
  sys.path.insert(0, target_dir)
323
388
 
324
389
  if force_refresh:
390
+ # Get current site packages dirs
391
+ site_package_dirs = site.getsitepackages()
392
+
325
393
  # Clear the modules related to the app from sys.modules
326
394
  for module_name, module in list(sys.modules.items()):
327
395
  if (
328
396
  hasattr(module, "__file__")
329
397
  and module.__file__
330
398
  and module.__file__.startswith(target_dir)
399
+ and not any(module.__file__.startswith(p) for p in site_package_dirs)
331
400
  ):
332
401
  sys.modules.pop(module_name, None)
333
402
 
@@ -358,7 +427,7 @@ def load_settings():
358
427
 
359
428
  if not meta or meta.get("generated_by") <= "0.3.0":
360
429
  raise ValueError(
361
- "Your config file is outdated. Please delete it and restart the app to regenerate it."
430
+ f"Your config file '{config_file}' is outdated. Please delete it and restart the app to regenerate it."
362
431
  )
363
432
 
364
433
  lc_cache_path = os.path.join(config_dir, ".langchain.db")
@@ -372,11 +441,13 @@ def load_settings():
372
441
 
373
442
  ui_settings = UISettings(**ui_settings)
374
443
 
444
+ code_settings = CodeSettings(action_callbacks={})
445
+
375
446
  return {
376
447
  "features": features_settings,
377
448
  "ui": ui_settings,
378
449
  "project": project_settings,
379
- "code": CodeSettings(action_callbacks={}),
450
+ "code": code_settings,
380
451
  }
381
452
 
382
453
 
@@ -411,4 +482,22 @@ def load_config():
411
482
  return config
412
483
 
413
484
 
485
+ def lint_translations():
486
+ # Load the ground truth (en-US.json file from chainlit source code)
487
+ src = os.path.join(TRANSLATIONS_DIR, "en-US.json")
488
+ with open(src, encoding="utf-8") as f:
489
+ truth = json.load(f)
490
+
491
+ # Find the local app translations
492
+ for file in os.listdir(config_translation_dir):
493
+ if file.endswith(".json"):
494
+ # Load the translation file
495
+ to_lint = os.path.join(config_translation_dir, file)
496
+ with open(to_lint, encoding="utf-8") as f:
497
+ translation = json.load(f)
498
+
499
+ # Lint the translation file
500
+ lint_translation_json(file, truth, translation)
501
+
502
+
414
503
  config = load_config()