Skip to content

Commit

Permalink
Merge branch 'main' into feature/markdown-alert-new
Browse files Browse the repository at this point in the history
  • Loading branch information
hexart authored Jan 28, 2025
2 parents 7ca659c + e4bdcc0 commit be44ef4
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 3 deletions.
14 changes: 13 additions & 1 deletion backend/chainlit/emitter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import asyncio
import uuid
from typing import Any, Dict, List, Literal, Optional, Union, cast
from typing import Any, Dict, List, Literal, Optional, Union, cast, get_args

from literalai.helper import utc_now
from socketio.exceptions import TimeoutError
Expand All @@ -22,6 +22,7 @@
MessagePayload,
OutputAudioChunk,
ThreadDict,
ToastType,
)
from chainlit.user import PersistedUser

Expand Down Expand Up @@ -141,6 +142,10 @@ async def send_window_message(self, data: Any):
"""Stub method to send custom data to the host window."""
pass

def send_toast(self, message: str, type: Optional[ToastType] = "info"):
"""Stub method to send a toast message to the UI."""
pass


class ChainlitEmitter(BaseChainlitEmitter):
"""
Expand Down Expand Up @@ -423,3 +428,10 @@ def set_commands(self, commands: List[CommandDict]):
def send_window_message(self, data: Any):
"""Send custom data to the host window."""
return self.emit("window_message", data)

def send_toast(self, message: str, type: Optional[ToastType] = "info"):
"""Send a toast message to the UI."""
# check that the type is valid using ToastType
if type not in get_args(ToastType):
raise ValueError(f"Invalid toast type: {type}")
return self.emit("toast", {"message": message, "type": type})
1 change: 1 addition & 0 deletions backend/chainlit/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
InputWidgetType = Literal[
"switch", "slider", "select", "textinput", "tags", "numberinput"
]
ToastType = Literal["info", "success", "warning", "error"]


class ThreadDict(TypedDict):
Expand Down
26 changes: 26 additions & 0 deletions backend/tests/test_emitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,29 @@ async def test_stream_start(
}
await emitter.stream_start(step_dict)
mock_websocket_session.emit.assert_called_once_with("stream_start", step_dict)


async def test_send_toast(
emitter: ChainlitEmitter, mock_websocket_session: MagicMock
) -> None:
message = "This is a test message"
await emitter.send_toast(message)
mock_websocket_session.emit.assert_called_once_with(
"toast", {"message": message, "type": "info"}
)


async def test_send_toast_with_type(
emitter: ChainlitEmitter, mock_websocket_session: MagicMock
) -> None:
message = "This is a test message"
await emitter.send_toast(message, type="error")
mock_websocket_session.emit.assert_called_once_with(
"toast", {"message": message, "type": "error"}
)


async def test_send_toast_invalid_type(emitter: ChainlitEmitter) -> None:
message = "This is a test message"
with pytest.raises(ValueError, match="Invalid toast type: invalid"):
await emitter.send_toast(message, type="invalid") # type: ignore[arg-type]
2 changes: 1 addition & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function App() {
storageKey="vite-ui-theme"
defaultTheme={data?.default_theme}
>
<Toaster className="toast" position="top-right" />
<Toaster richColors className="toast" position="top-right" />

<ChatSettingsModal />
<RouterProvider router={router} />
Expand Down
3 changes: 2 additions & 1 deletion frontend/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ export default defineConfig({
plugins: [react(), tsconfigPaths(), svgr()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
'@': path.resolve(__dirname, './src'),
// To prevent conflicts with packages in @chainlit/react-client, we need to specify the resolution paths for these dependencies.
react: path.resolve(__dirname, './node_modules/react'),
'usehooks-ts': path.resolve(__dirname, './node_modules/usehooks-ts'),
sonner: path.resolve(__dirname, './node_modules/sonner'),
lodash: path.resolve(__dirname, './node_modules/lodash'),
recoil: path.resolve(__dirname, './node_modules/recoil')
}
Expand Down
1 change: 1 addition & 0 deletions libs/react-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"jwt-decode": "^3.1.2",
"lodash": "^4.17.21",
"socket.io-client": "^4.7.2",
"sonner": "^1.7.1",
"swr": "^2.2.2",
"uuid": "^9.0.0"
},
Expand Down
14 changes: 14 additions & 0 deletions libs/react-client/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions libs/react-client/src/useChatSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
useSetRecoilState
} from 'recoil';
import io from 'socket.io-client';
import { toast } from 'sonner';
import {
actionState,
askUserState,
Expand Down Expand Up @@ -365,6 +366,31 @@ const useChatSession = () => {
window.parent.postMessage(data, '*');
}
});

socket.on('toast', (data: { message: string; type: string }) => {
if (!data.message) {
console.warn('No message received for toast.');
return;
}

switch (data.type) {
case 'info':
toast.info(data.message);
break;
case 'error':
toast.error(data.message);
break;
case 'success':
toast.success(data.message);
break;
case 'warning':
toast.warning(data.message);
break;
default:
toast(data.message);
break;
}
});
},
[setSession, sessionId, idToResume, chatProfile]
);
Expand Down

0 comments on commit be44ef4

Please sign in to comment.