chainlit 1.0.400__py3-none-any.whl → 2.0.3__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 +122 -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 +181 -101
  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 +608 -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 +80 -29
  33. chainlit/frontend/dist/assets/DailyMotion-C_XC7xJI.js +1 -0
  34. chainlit/frontend/dist/assets/Dataframe-Cs4l4hA1.js +22 -0
  35. chainlit/frontend/dist/assets/Facebook-CUeCH7hk.js +1 -0
  36. chainlit/frontend/dist/assets/FilePlayer-CB-fYkx8.js +1 -0
  37. chainlit/frontend/dist/assets/Kaltura-YX6qaq72.js +1 -0
  38. chainlit/frontend/dist/assets/Mixcloud-DGV0ldjP.js +1 -0
  39. chainlit/frontend/dist/assets/Mux-CmRss5oc.js +1 -0
  40. chainlit/frontend/dist/assets/Preview-DBVJn7-H.js +1 -0
  41. chainlit/frontend/dist/assets/SoundCloud-qLUb18oY.js +1 -0
  42. chainlit/frontend/dist/assets/Streamable-BvYP7bFp.js +1 -0
  43. chainlit/frontend/dist/assets/Twitch-CTHt-sGZ.js +1 -0
  44. chainlit/frontend/dist/assets/Vidyard-B-0mCJbm.js +1 -0
  45. chainlit/frontend/dist/assets/Vimeo-Dnp7ri8q.js +1 -0
  46. chainlit/frontend/dist/assets/Wistia-DW0x_UBn.js +1 -0
  47. chainlit/frontend/dist/assets/YouTube--98FipvA.js +1 -0
  48. chainlit/frontend/dist/assets/index-D71nZ46o.js +8665 -0
  49. chainlit/frontend/dist/assets/index-g8LTJwwr.css +1 -0
  50. chainlit/frontend/dist/assets/react-plotly-Cn_BQTQw.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 +107 -72
  55. chainlit/langflow/__init__.py +1 -0
  56. chainlit/llama_index/__init__.py +2 -2
  57. chainlit/llama_index/callbacks.py +67 -42
  58. chainlit/markdown.py +22 -6
  59. chainlit/message.py +54 -56
  60. chainlit/mistralai/__init__.py +50 -0
  61. chainlit/oauth_providers.py +266 -8
  62. chainlit/openai/__init__.py +10 -18
  63. chainlit/secret.py +1 -1
  64. chainlit/server.py +789 -228
  65. chainlit/session.py +108 -90
  66. chainlit/slack/__init__.py +6 -0
  67. chainlit/slack/app.py +397 -0
  68. chainlit/socket.py +199 -116
  69. chainlit/step.py +141 -89
  70. chainlit/sync.py +2 -1
  71. chainlit/teams/__init__.py +6 -0
  72. chainlit/teams/app.py +338 -0
  73. chainlit/translations/bn.json +235 -0
  74. chainlit/translations/en-US.json +83 -4
  75. chainlit/translations/gu.json +235 -0
  76. chainlit/translations/he-IL.json +235 -0
  77. chainlit/translations/hi.json +235 -0
  78. chainlit/translations/kn.json +235 -0
  79. chainlit/translations/ml.json +235 -0
  80. chainlit/translations/mr.json +235 -0
  81. chainlit/translations/nl-NL.json +233 -0
  82. chainlit/translations/ta.json +235 -0
  83. chainlit/translations/te.json +235 -0
  84. chainlit/translations/zh-CN.json +233 -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.400.dist-info → chainlit-2.0.3.dist-info}/METADATA +48 -50
  92. chainlit-2.0.3.dist-info/RECORD +106 -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.400.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.400.dist-info → chainlit-2.0.3.dist-info}/WHEEL +0 -0
  113. {chainlit-1.0.400.dist-info → chainlit-2.0.3.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,52 @@ 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
90
+ # Automatically tag threads with the current chat profile (if a chat profile is used)
91
+ auto_tag_thread = true
71
92
 
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"
93
+ # Allow users to edit their own messages
94
+ edit_message = true
77
95
 
78
- [UI]
79
- # Name of the app and chatbot.
80
- name = "Chatbot"
96
+ # Authorize users to spontaneously upload files with messages
97
+ [features.spontaneous_file_upload]
98
+ enabled = true
99
+ accept = ["*/*"]
100
+ max_files = 20
101
+ max_size_mb = 500
81
102
 
82
- # Show the readme while the thread is empty.
83
- show_readme_as_default = true
103
+ [features.audio]
104
+ # Sample rate of the audio
105
+ sample_rate = 24000
84
106
 
85
- # Description of the app and chatbot. This is used for HTML tags.
86
- # description = ""
107
+ [UI]
108
+ # Name of the assistant.
109
+ name = "Assistant"
87
110
 
88
- # Large size content are by default collapsed for a cleaner ui
89
- default_collapse_content = true
111
+ # default_theme = "dark"
90
112
 
91
- # The default value for the expand messages settings.
92
- default_expand_messages = false
113
+ # layout = "wide"
93
114
 
94
- # Hide the chain of thought details from the user in the UI.
95
- hide_cot = false
115
+ # Description of the assistant. This is used for HTML tags.
116
+ # description = ""
117
+
118
+ # Chain of Thought (CoT) display mode. Can be "hidden", "tool_call" or "full".
119
+ cot = "full"
96
120
 
97
121
  # Link to your github repo. This will add a github button in the UI's header.
98
122
  # github = ""
@@ -105,39 +129,22 @@ hide_cot = false
105
129
  # The Javascript file can be served from the public directory.
106
130
  # custom_js = "/public/test.js"
107
131
 
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"
132
+ # Specify a custom meta image url.
133
+ # custom_meta_image_url = "https://chainlit-cloud.s3.eu-west-3.amazonaws.com/logo/chainlit_banner.png"
132
134
 
135
+ # Specify a custom build directory for the frontend.
136
+ # This can be used to customize the frontend code.
137
+ # Be careful: If this is a relative path, it should not start with a slash.
138
+ # custom_build = "./public/build"
133
139
 
134
140
  [meta]
135
141
  generated_by = "{__version__}"
136
142
  """
137
143
 
138
144
 
139
- DEFAULT_HOST = "0.0.0.0"
145
+ DEFAULT_HOST = "127.0.0.1"
140
146
  DEFAULT_PORT = 8000
147
+ DEFAULT_ROOT_PATH = ""
141
148
 
142
149
 
143
150
  @dataclass()
@@ -146,6 +153,9 @@ class RunSettings:
146
153
  module_name: Optional[str] = None
147
154
  host: str = DEFAULT_HOST
148
155
  port: int = DEFAULT_PORT
156
+ ssl_cert: Optional[str] = None
157
+ ssl_key: Optional[str] = None
158
+ root_path: str = DEFAULT_ROOT_PATH
149
159
  headless: bool = False
150
160
  watch: bool = False
151
161
  no_cache: bool = False
@@ -160,50 +170,60 @@ class PaletteOptions(DataClassJsonMixin):
160
170
  dark: Optional[str] = ""
161
171
 
162
172
 
173
+ @dataclass()
174
+ class TextOptions(DataClassJsonMixin):
175
+ primary: Optional[str] = ""
176
+ secondary: Optional[str] = ""
177
+
178
+
163
179
  @dataclass()
164
180
  class Palette(DataClassJsonMixin):
165
181
  primary: Optional[PaletteOptions] = None
166
182
  background: Optional[str] = ""
167
183
  paper: Optional[str] = ""
184
+ text: Optional[TextOptions] = None
168
185
 
169
186
 
170
- @dataclass()
171
- class Theme(DataClassJsonMixin):
172
- font_family: Optional[str] = None
173
- light: Optional[Palette] = None
174
- dark: Optional[Palette] = None
187
+ @dataclass
188
+ class SpontaneousFileUploadFeature(DataClassJsonMixin):
189
+ enabled: Optional[bool] = None
190
+ accept: Optional[Union[List[str], Dict[str, List[str]]]] = None
191
+ max_files: Optional[int] = None
192
+ max_size_mb: Optional[int] = None
175
193
 
176
194
 
177
195
  @dataclass
178
- class SpeechToTextFeature:
179
- enabled: Optional[bool] = None
180
- language: Optional[str] = None
196
+ class AudioFeature(DataClassJsonMixin):
197
+ sample_rate: int = 24000
198
+ enabled: bool = False
181
199
 
182
200
 
183
201
  @dataclass()
184
202
  class FeaturesSettings(DataClassJsonMixin):
185
- prompt_playground: bool = True
186
- multi_modal: bool = True
203
+ spontaneous_file_upload: Optional[SpontaneousFileUploadFeature] = None
204
+ audio: Optional[AudioFeature] = Field(default_factory=AudioFeature)
187
205
  latex: bool = False
188
206
  unsafe_allow_html: bool = False
189
- speech_to_text: Optional[SpeechToTextFeature] = None
207
+ auto_tag_thread: bool = True
208
+ edit_message: bool = True
190
209
 
191
210
 
192
211
  @dataclass()
193
212
  class UISettings(DataClassJsonMixin):
194
213
  name: str
195
- show_readme_as_default: bool = True
196
214
  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
215
+ cot: Literal["hidden", "tool_call", "full"] = "full"
216
+ font_family: Optional[str] = None
217
+ default_theme: Optional[Literal["light", "dark"]] = "dark"
218
+ layout: Optional[Literal["default", "wide"]] = "default"
201
219
  github: Optional[str] = None
202
- theme: Optional[Theme] = None
203
220
  # Optional custom CSS file that allows you to customize the UI
204
221
  custom_css: Optional[str] = None
205
222
  custom_js: Optional[str] = None
206
- custom_font: Optional[str] = None
223
+ # Optional custom meta tag for image preview
224
+ custom_meta_image_url: Optional[str] = None
225
+ # Optional custom build directory for the frontend
226
+ custom_build: Optional[str] = None
207
227
 
208
228
 
209
229
  @dataclass()
@@ -213,27 +233,42 @@ class CodeSettings:
213
233
  # Module object loaded from the module_name
214
234
  module: Any = None
215
235
  # 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
236
+ password_auth_callback: Optional[
237
+ Callable[[str, str], Awaitable[Optional["User"]]]
238
+ ] = None
239
+ header_auth_callback: Optional[Callable[[Headers], Awaitable[Optional["User"]]]] = (
240
+ None
241
+ )
218
242
  oauth_callback: Optional[
219
- Callable[[str, str, Dict[str, str], "User"], Optional["User"]]
243
+ Callable[[str, str, Dict[str, str], "User"], Awaitable[Optional["User"]]]
220
244
  ] = None
221
245
  on_logout: Optional[Callable[["Request", "Response"], Any]] = None
222
246
  on_stop: Optional[Callable[[], Any]] = None
223
247
  on_chat_start: Optional[Callable[[], Any]] = None
224
248
  on_chat_end: Optional[Callable[[], Any]] = None
225
249
  on_chat_resume: Optional[Callable[["ThreadDict"], Any]] = None
226
- on_message: Optional[Callable[[str], Any]] = None
227
- author_rename: Optional[Callable[[str], str]] = None
250
+ on_message: Optional[Callable[["Message"], Any]] = None
251
+ on_window_message: Optional[Callable[[str], Any]] = None
252
+ on_audio_start: Optional[Callable[[], Any]] = None
253
+ on_audio_chunk: Optional[Callable[["InputAudioChunk"], Any]] = None
254
+ on_audio_end: Optional[Callable[[], Any]] = None
255
+
256
+ author_rename: Optional[Callable[[str], Awaitable[str]]] = None
228
257
  on_settings_update: Optional[Callable[[Dict[str, Any]], Any]] = None
229
- set_chat_profiles: Optional[Callable[[Optional["User"]], List["ChatProfile"]]] = (
258
+ set_chat_profiles: Optional[
259
+ Callable[[Optional["User"]], Awaitable[List["ChatProfile"]]]
260
+ ] = None
261
+ set_starters: Optional[Callable[[Optional["User"]], Awaitable[List["Starter"]]]] = (
230
262
  None
231
263
  )
264
+ data_layer: Optional[Callable[[], BaseDataLayer]] = None
232
265
 
233
266
 
234
267
  @dataclass()
235
268
  class ProjectSettings(DataClassJsonMixin):
236
269
  allow_origins: List[str] = Field(default_factory=lambda: ["*"])
270
+ # Socket.io client transports option
271
+ transports: Optional[List[str]] = None
237
272
  enable_telemetry: bool = True
238
273
  # 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
274
  user_env: Optional[List[str]] = None
@@ -242,10 +277,10 @@ class ProjectSettings(DataClassJsonMixin):
242
277
  # Path to the local chat db
243
278
  # Duration (in seconds) during which the session is saved when the connection is lost
244
279
  session_timeout: int = 3600
280
+ # Duration (in seconds) of the user session expiry
281
+ user_session_timeout: int = 1296000 # 15 days
245
282
  # Enable third parties caching (e.g LangChain cache)
246
283
  cache: bool = False
247
- # Follow symlink for asset mount (see https://github.com/Chainlit/chainlit/issues/317)
248
- follow_symlink: bool = False
249
284
 
250
285
 
251
286
  @dataclass()
@@ -263,23 +298,44 @@ class ChainlitConfig:
263
298
  def load_translation(self, language: str):
264
299
  translation = {}
265
300
  default_language = "en-US"
301
+ # fallback to root language (ex: `de` when `de-DE` is not found)
302
+ parent_language = language.split("-")[0]
266
303
 
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
- )
304
+ translation_dir = Path(config_translation_dir)
273
305
 
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):
306
+ translation_lib_file_path = translation_dir / f"{language}.json"
307
+ translation_lib_parent_language_file_path = (
308
+ translation_dir / f"{parent_language}.json"
309
+ )
310
+ default_translation_lib_file_path = translation_dir / f"{default_language}.json"
311
+
312
+ if (
313
+ is_path_inside(translation_lib_file_path, translation_dir)
314
+ and translation_lib_file_path.is_file()
315
+ ):
316
+ translation = json.loads(
317
+ translation_lib_file_path.read_text(encoding="utf-8")
318
+ )
319
+ elif (
320
+ is_path_inside(translation_lib_parent_language_file_path, translation_dir)
321
+ and translation_lib_parent_language_file_path.is_file()
322
+ ):
323
+ logger.warning(
324
+ f"Translation file for {language} not found. Using parent translation {parent_language}."
325
+ )
326
+ translation = json.loads(
327
+ translation_lib_parent_language_file_path.read_text(encoding="utf-8")
328
+ )
329
+ elif (
330
+ is_path_inside(default_translation_lib_file_path, translation_dir)
331
+ and default_translation_lib_file_path.is_file()
332
+ ):
278
333
  logger.warning(
279
334
  f"Translation file for {language} not found. Using default translation {default_language}."
280
335
  )
281
- with open(default_translation_lib_file_path, "r", encoding="utf-8") as f:
282
- translation = json.load(f)
336
+ translation = json.loads(
337
+ default_translation_lib_file_path.read_text(encoding="utf-8")
338
+ )
283
339
 
284
340
  return translation
285
341
 
@@ -305,7 +361,7 @@ def init_config(log=False):
305
361
  dst = os.path.join(config_translation_dir, file)
306
362
  if not os.path.exists(dst):
307
363
  src = os.path.join(TRANSLATIONS_DIR, file)
308
- with open(src, "r", encoding="utf-8") as f:
364
+ with open(src, encoding="utf-8") as f:
309
365
  translation = json.load(f)
310
366
  with open(dst, "w", encoding="utf-8") as f:
311
367
  json.dump(translation, f, indent=4)
@@ -322,12 +378,16 @@ def load_module(target: str, force_refresh: bool = False):
322
378
  sys.path.insert(0, target_dir)
323
379
 
324
380
  if force_refresh:
381
+ # Get current site packages dirs
382
+ site_package_dirs = site.getsitepackages()
383
+
325
384
  # Clear the modules related to the app from sys.modules
326
385
  for module_name, module in list(sys.modules.items()):
327
386
  if (
328
387
  hasattr(module, "__file__")
329
388
  and module.__file__
330
389
  and module.__file__.startswith(target_dir)
390
+ and not any(module.__file__.startswith(p) for p in site_package_dirs)
331
391
  ):
332
392
  sys.modules.pop(module_name, None)
333
393
 
@@ -358,7 +418,7 @@ def load_settings():
358
418
 
359
419
  if not meta or meta.get("generated_by") <= "0.3.0":
360
420
  raise ValueError(
361
- "Your config file is outdated. Please delete it and restart the app to regenerate it."
421
+ f"Your config file '{config_file}' is outdated. Please delete it and restart the app to regenerate it."
362
422
  )
363
423
 
364
424
  lc_cache_path = os.path.join(config_dir, ".langchain.db")
@@ -372,11 +432,13 @@ def load_settings():
372
432
 
373
433
  ui_settings = UISettings(**ui_settings)
374
434
 
435
+ code_settings = CodeSettings(action_callbacks={})
436
+
375
437
  return {
376
438
  "features": features_settings,
377
439
  "ui": ui_settings,
378
440
  "project": project_settings,
379
- "code": CodeSettings(action_callbacks={}),
441
+ "code": code_settings,
380
442
  }
381
443
 
382
444
 
@@ -411,4 +473,22 @@ def load_config():
411
473
  return config
412
474
 
413
475
 
476
+ def lint_translations():
477
+ # Load the ground truth (en-US.json file from chainlit source code)
478
+ src = os.path.join(TRANSLATIONS_DIR, "en-US.json")
479
+ with open(src, encoding="utf-8") as f:
480
+ truth = json.load(f)
481
+
482
+ # Find the local app translations
483
+ for file in os.listdir(config_translation_dir):
484
+ if file.endswith(".json"):
485
+ # Load the translation file
486
+ to_lint = os.path.join(config_translation_dir, file)
487
+ with open(to_lint, encoding="utf-8") as f:
488
+ translation = json.load(f)
489
+
490
+ # Lint the translation file
491
+ lint_translation_json(file, truth, translation)
492
+
493
+
414
494
  config = load_config()