chainlit 2.7.0__py3-none-any.whl → 2.7.1__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 (85) hide show
  1. {chainlit-2.7.0.dist-info → chainlit-2.7.1.dist-info}/METADATA +1 -1
  2. chainlit-2.7.1.dist-info/RECORD +4 -0
  3. chainlit/__init__.py +0 -207
  4. chainlit/__main__.py +0 -4
  5. chainlit/_utils.py +0 -8
  6. chainlit/action.py +0 -33
  7. chainlit/auth/__init__.py +0 -95
  8. chainlit/auth/cookie.py +0 -197
  9. chainlit/auth/jwt.py +0 -42
  10. chainlit/cache.py +0 -45
  11. chainlit/callbacks.py +0 -433
  12. chainlit/chat_context.py +0 -64
  13. chainlit/chat_settings.py +0 -34
  14. chainlit/cli/__init__.py +0 -235
  15. chainlit/config.py +0 -621
  16. chainlit/context.py +0 -112
  17. chainlit/data/__init__.py +0 -111
  18. chainlit/data/acl.py +0 -19
  19. chainlit/data/base.py +0 -107
  20. chainlit/data/chainlit_data_layer.py +0 -687
  21. chainlit/data/dynamodb.py +0 -616
  22. chainlit/data/literalai.py +0 -501
  23. chainlit/data/sql_alchemy.py +0 -741
  24. chainlit/data/storage_clients/__init__.py +0 -0
  25. chainlit/data/storage_clients/azure.py +0 -84
  26. chainlit/data/storage_clients/azure_blob.py +0 -94
  27. chainlit/data/storage_clients/base.py +0 -28
  28. chainlit/data/storage_clients/gcs.py +0 -101
  29. chainlit/data/storage_clients/s3.py +0 -88
  30. chainlit/data/utils.py +0 -29
  31. chainlit/discord/__init__.py +0 -6
  32. chainlit/discord/app.py +0 -364
  33. chainlit/element.py +0 -454
  34. chainlit/emitter.py +0 -450
  35. chainlit/hello.py +0 -12
  36. chainlit/input_widget.py +0 -182
  37. chainlit/langchain/__init__.py +0 -6
  38. chainlit/langchain/callbacks.py +0 -682
  39. chainlit/langflow/__init__.py +0 -25
  40. chainlit/llama_index/__init__.py +0 -6
  41. chainlit/llama_index/callbacks.py +0 -206
  42. chainlit/logger.py +0 -16
  43. chainlit/markdown.py +0 -57
  44. chainlit/mcp.py +0 -99
  45. chainlit/message.py +0 -619
  46. chainlit/mistralai/__init__.py +0 -50
  47. chainlit/oauth_providers.py +0 -835
  48. chainlit/openai/__init__.py +0 -53
  49. chainlit/py.typed +0 -0
  50. chainlit/secret.py +0 -9
  51. chainlit/semantic_kernel/__init__.py +0 -111
  52. chainlit/server.py +0 -1616
  53. chainlit/session.py +0 -304
  54. chainlit/sidebar.py +0 -55
  55. chainlit/slack/__init__.py +0 -6
  56. chainlit/slack/app.py +0 -427
  57. chainlit/socket.py +0 -381
  58. chainlit/step.py +0 -490
  59. chainlit/sync.py +0 -43
  60. chainlit/teams/__init__.py +0 -6
  61. chainlit/teams/app.py +0 -348
  62. chainlit/translations/bn.json +0 -214
  63. chainlit/translations/el-GR.json +0 -214
  64. chainlit/translations/en-US.json +0 -214
  65. chainlit/translations/fr-FR.json +0 -214
  66. chainlit/translations/gu.json +0 -214
  67. chainlit/translations/he-IL.json +0 -214
  68. chainlit/translations/hi.json +0 -214
  69. chainlit/translations/ja.json +0 -214
  70. chainlit/translations/kn.json +0 -214
  71. chainlit/translations/ml.json +0 -214
  72. chainlit/translations/mr.json +0 -214
  73. chainlit/translations/nl.json +0 -214
  74. chainlit/translations/ta.json +0 -214
  75. chainlit/translations/te.json +0 -214
  76. chainlit/translations/zh-CN.json +0 -214
  77. chainlit/translations.py +0 -60
  78. chainlit/types.py +0 -334
  79. chainlit/user.py +0 -43
  80. chainlit/user_session.py +0 -153
  81. chainlit/utils.py +0 -173
  82. chainlit/version.py +0 -8
  83. chainlit-2.7.0.dist-info/RECORD +0 -84
  84. {chainlit-2.7.0.dist-info → chainlit-2.7.1.dist-info}/WHEEL +0 -0
  85. {chainlit-2.7.0.dist-info → chainlit-2.7.1.dist-info}/entry_points.txt +0 -0
chainlit/types.py DELETED
@@ -1,334 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from enum import Enum
4
- from pathlib import Path
5
- from typing import (
6
- TYPE_CHECKING,
7
- Any,
8
- Dict,
9
- Generic,
10
- List,
11
- Literal,
12
- Optional,
13
- Protocol,
14
- TypedDict,
15
- TypeVar,
16
- Union,
17
- )
18
-
19
- if TYPE_CHECKING:
20
- from chainlit.element import ElementDict
21
- from chainlit.step import StepDict
22
-
23
- from dataclasses_json import DataClassJsonMixin
24
- from pydantic import BaseModel
25
- from pydantic.dataclasses import dataclass
26
-
27
- InputWidgetType = Literal[
28
- "switch", "slider", "select", "textinput", "tags", "numberinput"
29
- ]
30
- ToastType = Literal["info", "success", "warning", "error"]
31
-
32
-
33
- class ThreadDict(TypedDict):
34
- id: str
35
- createdAt: str
36
- name: Optional[str]
37
- userId: Optional[str]
38
- userIdentifier: Optional[str]
39
- tags: Optional[List[str]]
40
- metadata: Optional[Dict]
41
- steps: List[StepDict]
42
- elements: Optional[List[ElementDict]]
43
-
44
-
45
- class Pagination(BaseModel):
46
- first: int
47
- cursor: Optional[str] = None
48
-
49
-
50
- class ThreadFilter(BaseModel):
51
- feedback: Literal[0, 1] | None = None
52
- userId: str | None = None
53
- search: str | None = None
54
-
55
-
56
- @dataclass
57
- class PageInfo:
58
- hasNextPage: bool
59
- startCursor: Optional[str]
60
- endCursor: Optional[str]
61
-
62
- def to_dict(self):
63
- return {
64
- "hasNextPage": self.hasNextPage,
65
- "startCursor": self.startCursor,
66
- "endCursor": self.endCursor,
67
- }
68
-
69
- @classmethod
70
- def from_dict(cls, page_info_dict: Dict) -> PageInfo:
71
- hasNextPage = page_info_dict.get("hasNextPage", False)
72
- startCursor = page_info_dict.get("startCursor", None)
73
- endCursor = page_info_dict.get("endCursor", None)
74
- return cls(
75
- hasNextPage=hasNextPage, startCursor=startCursor, endCursor=endCursor
76
- )
77
-
78
-
79
- T = TypeVar("T", covariant=True)
80
-
81
-
82
- class HasFromDict(Protocol[T]):
83
- @classmethod
84
- def from_dict(cls, obj_dict: Any) -> T:
85
- raise NotImplementedError
86
-
87
-
88
- @dataclass
89
- class PaginatedResponse(Generic[T]):
90
- pageInfo: PageInfo
91
- data: List[T]
92
-
93
- def to_dict(self):
94
- return {
95
- "pageInfo": self.pageInfo.to_dict(),
96
- "data": [
97
- (d.to_dict() if hasattr(d, "to_dict") and callable(d.to_dict) else d)
98
- for d in self.data
99
- ],
100
- }
101
-
102
- @classmethod
103
- def from_dict(
104
- cls, paginated_response_dict: Dict, the_class: HasFromDict[T]
105
- ) -> PaginatedResponse[T]:
106
- pageInfo = PageInfo.from_dict(paginated_response_dict.get("pageInfo", {}))
107
-
108
- data = [the_class.from_dict(d) for d in paginated_response_dict.get("data", [])]
109
-
110
- return cls(pageInfo=pageInfo, data=data)
111
-
112
-
113
- @dataclass
114
- class FileSpec(DataClassJsonMixin):
115
- accept: Union[List[str], Dict[str, List[str]]]
116
- max_files: int
117
- max_size_mb: int
118
-
119
-
120
- @dataclass
121
- class ActionSpec(DataClassJsonMixin):
122
- keys: List[str]
123
-
124
-
125
- @dataclass
126
- class AskSpec(DataClassJsonMixin):
127
- """Specification for asking the user."""
128
-
129
- timeout: int
130
- type: Literal["text", "file", "action", "element"]
131
- step_id: str
132
-
133
-
134
- @dataclass
135
- class AskFileSpec(FileSpec, AskSpec, DataClassJsonMixin):
136
- """Specification for asking the user a file."""
137
-
138
-
139
- @dataclass
140
- class AskActionSpec(ActionSpec, AskSpec, DataClassJsonMixin):
141
- """Specification for asking the user an action"""
142
-
143
-
144
- @dataclass
145
- class AskElementSpec(AskSpec, DataClassJsonMixin):
146
- """Specification for asking the user a custom element"""
147
-
148
- element_id: str
149
-
150
-
151
- class FileReference(TypedDict):
152
- id: str
153
-
154
-
155
- class FileDict(TypedDict):
156
- id: str
157
- name: str
158
- path: Path
159
- size: int
160
- type: str
161
-
162
-
163
- class MessagePayload(TypedDict):
164
- message: StepDict
165
- fileReferences: Optional[List[FileReference]]
166
-
167
-
168
- class InputAudioChunkPayload(TypedDict):
169
- isStart: bool
170
- mimeType: str
171
- elapsedTime: float
172
- data: bytes
173
-
174
-
175
- @dataclass
176
- class InputAudioChunk:
177
- isStart: bool
178
- mimeType: str
179
- elapsedTime: float
180
- data: bytes
181
-
182
-
183
- class OutputAudioChunk(TypedDict):
184
- track: str
185
- mimeType: str
186
- data: bytes
187
-
188
-
189
- @dataclass
190
- class AskFileResponse:
191
- id: str
192
- name: str
193
- path: str
194
- size: int
195
- type: str
196
-
197
-
198
- class AskActionResponse(TypedDict):
199
- name: str
200
- payload: Dict
201
- label: str
202
- tooltip: str
203
- forId: str
204
- id: str
205
-
206
-
207
- class AskElementResponse(TypedDict, total=False):
208
- submitted: bool
209
-
210
-
211
- class UpdateThreadRequest(BaseModel):
212
- threadId: str
213
- name: str
214
-
215
-
216
- class DeleteThreadRequest(BaseModel):
217
- threadId: str
218
-
219
-
220
- class DeleteFeedbackRequest(BaseModel):
221
- feedbackId: str
222
-
223
-
224
- class GetThreadsRequest(BaseModel):
225
- pagination: Pagination
226
- filter: ThreadFilter
227
-
228
-
229
- class CallActionRequest(BaseModel):
230
- action: Dict
231
- sessionId: str
232
-
233
-
234
- class ConnectStdioMCPRequest(BaseModel):
235
- sessionId: str
236
- clientType: Literal["stdio"]
237
- name: str
238
- fullCommand: str
239
-
240
-
241
- class ConnectSseMCPRequest(BaseModel):
242
- sessionId: str
243
- clientType: Literal["sse"]
244
- name: str
245
- url: str
246
- # Optional HTTP headers to forward to the MCP transport (e.g. Authorization)
247
- headers: Optional[Dict[str, str]] = None
248
-
249
-
250
- class ConnectStreamableHttpMCPRequest(BaseModel):
251
- sessionId: str
252
- clientType: Literal["streamable-http"]
253
- name: str
254
- url: str
255
- # Optional HTTP headers to forward to the MCP transport (e.g. Authorization)
256
- headers: Dict[str, str] | None = None
257
-
258
-
259
- ConnectMCPRequest = Union[
260
- ConnectStdioMCPRequest, ConnectSseMCPRequest, ConnectStreamableHttpMCPRequest
261
- ]
262
-
263
-
264
- class DisconnectMCPRequest(BaseModel):
265
- sessionId: str
266
- name: str
267
-
268
-
269
- class ElementRequest(BaseModel):
270
- element: Dict
271
- sessionId: str
272
-
273
-
274
- class Theme(str, Enum):
275
- light = "light"
276
- dark = "dark"
277
-
278
-
279
- @dataclass
280
- class Starter(DataClassJsonMixin):
281
- """Specification for a starter that can be chosen by the user at the thread start."""
282
-
283
- label: str
284
- message: str
285
- command: Optional[str] = None
286
- icon: Optional[str] = None
287
-
288
-
289
- @dataclass
290
- class ChatProfile(DataClassJsonMixin):
291
- """Specification for a chat profile that can be chosen by the user at the thread start."""
292
-
293
- name: str
294
- markdown_description: str
295
- icon: Optional[str] = None
296
- default: bool = False
297
- starters: Optional[List[Starter]] = None
298
- config_overrides: Any = None
299
-
300
-
301
- FeedbackStrategy = Literal["BINARY"]
302
-
303
-
304
- class CommandDict(TypedDict):
305
- # The identifier of the command, will be displayed in the UI
306
- id: str
307
- # The description of the command, will be displayed in the UI
308
- description: str
309
- # The lucide icon name
310
- icon: str
311
- # Display the command as a button in the composer
312
- button: Optional[bool]
313
- # Whether the command will be persistent unless the user toggles it
314
- persistent: Optional[bool]
315
-
316
-
317
- class FeedbackDict(TypedDict):
318
- forId: str
319
- id: Optional[str]
320
- value: Literal[0, 1]
321
- comment: Optional[str]
322
-
323
-
324
- @dataclass
325
- class Feedback:
326
- forId: str
327
- value: Literal[0, 1]
328
- threadId: Optional[str] = None
329
- id: Optional[str] = None
330
- comment: Optional[str] = None
331
-
332
-
333
- class UpdateFeedbackRequest(BaseModel):
334
- feedback: Feedback
chainlit/user.py DELETED
@@ -1,43 +0,0 @@
1
- from typing import Dict, Literal, Optional, TypedDict
2
-
3
- from dataclasses_json import DataClassJsonMixin
4
- from pydantic import Field
5
- from pydantic.dataclasses import dataclass
6
-
7
- Provider = Literal[
8
- "credentials",
9
- "header",
10
- "github",
11
- "google",
12
- "azure-ad",
13
- "azure-ad-hybrid",
14
- "okta",
15
- "auth0",
16
- "descope",
17
- ]
18
-
19
-
20
- class UserDict(TypedDict):
21
- id: str
22
- identifier: str
23
- display_name: Optional[str]
24
- metadata: Dict
25
-
26
-
27
- # Used when logging-in a user
28
- @dataclass
29
- class User(DataClassJsonMixin):
30
- identifier: str
31
- display_name: Optional[str] = None
32
- metadata: Dict = Field(default_factory=dict)
33
-
34
-
35
- @dataclass
36
- class PersistedUserFields:
37
- id: str
38
- createdAt: str
39
-
40
-
41
- @dataclass
42
- class PersistedUser(User, PersistedUserFields):
43
- pass
chainlit/user_session.py DELETED
@@ -1,153 +0,0 @@
1
- from typing import Callable, Dict, Generic, Optional, TypeVar
2
-
3
- from chainlit.context import context
4
-
5
- user_sessions: Dict[str, Dict] = {}
6
-
7
- T = TypeVar("T")
8
-
9
-
10
- class UserSession:
11
- """
12
- Developer facing user session class.
13
- Useful for the developer to store user specific data between calls.
14
- """
15
-
16
- def get(self, key, default=None):
17
- if not context.session:
18
- return default
19
-
20
- if context.session.id not in user_sessions:
21
- # Create a new user session
22
- user_sessions[context.session.id] = {}
23
-
24
- user_session = user_sessions[context.session.id]
25
-
26
- # Copy important fields from the session
27
- user_session["id"] = context.session.id
28
- user_session["env"] = context.session.user_env
29
- user_session["chat_settings"] = context.session.chat_settings
30
- user_session["user"] = context.session.user
31
- user_session["chat_profile"] = context.session.chat_profile
32
- user_session["client_type"] = context.session.client_type
33
-
34
- return user_session.get(key, default)
35
-
36
- def set(self, key, value):
37
- if not context.session:
38
- return None
39
-
40
- if context.session.id not in user_sessions:
41
- user_sessions[context.session.id] = {}
42
-
43
- user_session = user_sessions[context.session.id]
44
- user_session[key] = value
45
-
46
- def create_accessor(
47
- self, key: str, default: T, *, apply_fn: Optional[Callable[[T], T]] = None
48
- ) -> "SessionAccessor[T]":
49
- """
50
- Create a typed session accessor object for the given key and default value.
51
-
52
- #### Note: Creates the accessor configuration. The session value itself is only stored/updated when `.set()`, `.reset()`, or `.apply()` are called.
53
-
54
- Parameters
55
- ----------
56
- key : str
57
- The session dictionary key to store the value under
58
- default : T
59
- Default value to return when key is not present in session
60
- apply_fn : Optional[Callable[[T], T]], default None
61
- Optional function to transform the value when apply() is called
62
-
63
- Returns
64
- -------
65
- SessionAccessor[T]
66
- A typed accessor object bound to the specified session key
67
-
68
- Examples
69
- --------
70
-
71
- ```python
72
- count = cl.user_session.create_accessor("count", 0)
73
- count.get() # returns 0
74
- count.set(5) # type-safe setter
75
- count.get() # returns 5
76
-
77
- # With transform function
78
- counter = cl.user_session.create_accessor("counter", 0, apply_fn=lambda x: x + 1)
79
- counter.apply() # increments value and returns new value (1)
80
-
81
- @cl.on_message
82
- async def on_message(message: cl.Message):
83
- await cl.Message(content=f"You sent {counter.apply()} messages").send() # You sent 2 messages
84
- ```
85
- """
86
- return SessionAccessor(key, default, apply_fn=apply_fn)
87
-
88
-
89
- user_session = UserSession()
90
-
91
-
92
- class SessionAccessor(Generic[T]):
93
- """
94
- Extended session accessor class to store user specific data between calls with type safety.
95
-
96
- Provides a typed wrapper around user_session dictionary access with default values
97
- and optional transform functions. The session value is only stored in memory when
98
- explicitly modified through `.set()`, `.reset()`, or `.apply()` methods.
99
-
100
- Examples
101
- --------
102
- ```python
103
- count = cl.user_session.create_accessor("count", 0)
104
- count.get() # returns 0
105
- count.set(5) # type-safe setter
106
- count.get() # returns 5
107
-
108
- # With transform function
109
- counter = cl.user_session.create_accessor("counter", 0, apply_fn=lambda x: x + 1)
110
- counter.apply() # increments value and returns new value (1)
111
-
112
- @cl.on_message
113
- async def on_message(message: cl.Message):
114
- await cl.Message(content=f"You sent {counter.apply()} messages").send() # You sent 2 messages
115
- ```
116
- """
117
-
118
- def __init__(
119
- self, key: str, default: T, *, apply_fn: Optional[Callable[[T], T]] = None
120
- ):
121
- self._key = key
122
- self._default = default
123
- self._apply_fn = apply_fn
124
-
125
- def get(self) -> T:
126
- """
127
- Get the current value of the accessor.
128
- """
129
- return user_session.get(self._key, self._default)
130
-
131
- def set(self, value: T) -> None:
132
- """
133
- Set the value of the accessor.
134
- """
135
- return user_session.set(self._key, value)
136
-
137
- def reset(self) -> None:
138
- """
139
- Reset the value to the default.
140
- """
141
- return self.set(self._default)
142
-
143
- def apply(self) -> T:
144
- """
145
- Apply the transform function to the current value, store the result, and return it.
146
-
147
- Returns the current value if no transform function is provided.
148
- """
149
- value = self.get()
150
- if self._apply_fn:
151
- value = self._apply_fn(value)
152
- self.set(value)
153
- return value
chainlit/utils.py DELETED
@@ -1,173 +0,0 @@
1
- import functools
2
- import importlib
3
- import inspect
4
- import os
5
- from asyncio import CancelledError
6
- from datetime import datetime, timezone
7
- from typing import Callable
8
-
9
- import click
10
- from fastapi import FastAPI, Request
11
- from fastapi.responses import JSONResponse
12
- from packaging import version
13
- from starlette.middleware.base import BaseHTTPMiddleware
14
-
15
- from chainlit.auth import ensure_jwt_secret
16
- from chainlit.context import context
17
- from chainlit.logger import logger
18
-
19
-
20
- def utc_now():
21
- dt = datetime.now(timezone.utc).replace(tzinfo=None)
22
- return dt.isoformat() + "Z"
23
-
24
-
25
- def timestamp_utc(timestamp: float):
26
- dt = datetime.fromtimestamp(timestamp, timezone.utc).replace(tzinfo=None)
27
- return dt.isoformat() + "Z"
28
-
29
-
30
- def wrap_user_function(user_function: Callable, with_task=False) -> Callable:
31
- """
32
- Wraps a user-defined function to accept arguments as a dictionary.
33
-
34
- Args:
35
- user_function (Callable): The user-defined function to wrap.
36
-
37
- Returns:
38
- Callable: The wrapped function.
39
- """
40
-
41
- @functools.wraps(user_function)
42
- async def wrapper(*args):
43
- # Get the parameter names of the user-defined function
44
- user_function_params = list(inspect.signature(user_function).parameters.keys())
45
-
46
- # Create a dictionary of parameter names and their corresponding values from *args
47
- params_values = {
48
- param_name: arg for param_name, arg in zip(user_function_params, args)
49
- }
50
-
51
- if with_task:
52
- await context.emitter.task_start()
53
-
54
- try:
55
- # Call the user-defined function with the arguments
56
- if inspect.iscoroutinefunction(user_function):
57
- return await user_function(**params_values)
58
- else:
59
- return user_function(**params_values)
60
- except CancelledError:
61
- pass
62
- except Exception as e:
63
- logger.exception(e)
64
- if with_task:
65
- from chainlit.message import ErrorMessage
66
-
67
- await ErrorMessage(
68
- content=str(e) or e.__class__.__name__, author="Error"
69
- ).send()
70
- finally:
71
- if with_task:
72
- await context.emitter.task_end()
73
-
74
- return wrapper
75
-
76
-
77
- def make_module_getattr(registry):
78
- """Leverage PEP 562 to make imports lazy in an __init__.py
79
-
80
- The registry must be a dictionary with the items to import as keys and the
81
- modules they belong to as a value.
82
- """
83
-
84
- def __getattr__(name):
85
- module_path = registry[name]
86
- module = importlib.import_module(module_path, __package__)
87
- return getattr(module, name)
88
-
89
- return __getattr__
90
-
91
-
92
- def check_module_version(name, required_version):
93
- """
94
- Check the version of a module.
95
-
96
- Args:
97
- name (str): A module name.
98
- version (str): Minimum version.
99
-
100
- Returns:
101
- (bool): Return True if the module is installed and the version
102
- match the minimum required version.
103
- """
104
- try:
105
- module = importlib.import_module(name)
106
- except ModuleNotFoundError:
107
- return False
108
- return version.parse(module.__version__) >= version.parse(required_version)
109
-
110
-
111
- def check_file(target: str):
112
- # Define accepted file extensions for Chainlit
113
- ACCEPTED_FILE_EXTENSIONS = ("py", "py3")
114
-
115
- _, extension = os.path.splitext(target)
116
-
117
- # Check file extension
118
- if extension[1:] not in ACCEPTED_FILE_EXTENSIONS:
119
- if extension[1:] == "":
120
- raise click.BadArgumentUsage(
121
- "Chainlit requires raw Python (.py) files, but the provided file has no extension."
122
- )
123
- else:
124
- raise click.BadArgumentUsage(
125
- f"Chainlit requires raw Python (.py) files, not {extension}."
126
- )
127
-
128
- if not os.path.exists(target):
129
- raise click.BadParameter(f"File does not exist: {target}")
130
-
131
-
132
- def mount_chainlit(app: FastAPI, target: str, path="/chainlit"):
133
- from chainlit.config import config, load_module
134
- from chainlit.server import app as chainlit_app
135
-
136
- config.run.debug = os.environ.get("CHAINLIT_DEBUG", False)
137
- os.environ["CHAINLIT_ROOT_PATH"] = path
138
-
139
- api_full_path = path
140
-
141
- if app.root_path:
142
- parent_root_path = app.root_path.rstrip("/")
143
- api_full_path = parent_root_path + path
144
- os.environ["CHAINLIT_PARENT_ROOT_PATH"] = parent_root_path
145
-
146
- check_file(target)
147
- # Load the module provided by the user
148
- config.run.module_name = target
149
- load_module(config.run.module_name)
150
-
151
- ensure_jwt_secret()
152
-
153
- class ChainlitMiddleware(BaseHTTPMiddleware):
154
- """Middleware to handle path routing for submounted Chainlit applications.
155
-
156
- When Chainlit is submounted within a larger FastAPI application, its default route
157
- `@router.get("/{full_path:path}")` can conflict with the main app's routing. This
158
- middleware ensures requests are only forwarded to Chainlit if they match the
159
- designated subpath, preventing routing collisions.
160
-
161
- If a request's path doesn't start with the configured subpath, the middleware
162
- returns a 404 response instead of forwarding to Chainlit's default route.
163
- """
164
-
165
- async def dispatch(self, request: Request, call_next):
166
- if not request.url.path.startswith(api_full_path):
167
- return JSONResponse(status_code=404, content={"detail": "Not found"})
168
-
169
- return await call_next(request)
170
-
171
- chainlit_app.add_middleware(ChainlitMiddleware)
172
-
173
- app.mount(path, chainlit_app)
chainlit/version.py DELETED
@@ -1,8 +0,0 @@
1
- from importlib import metadata
2
-
3
- try:
4
- __version__ = metadata.version(__package__)
5
- except metadata.PackageNotFoundError:
6
- # Case where package metadata is not available, default to a 'non-outdated' version.
7
- # Ref: config.py::load_settings()
8
- __version__ = "2.7.0"