Skip to content

Commit 15b6de5

Browse files
authored
Stop streaming (#61)
* Add a button to stop streaming * Update dependency to jupyter-chat 0.9.0
1 parent 9bc1094 commit 15b6de5

File tree

5 files changed

+93
-9
lines changed

5 files changed

+93
-9
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"watch:labextension": "jupyter labextension watch ."
5757
},
5858
"dependencies": {
59-
"@jupyter/chat": "^0.8.1",
59+
"@jupyter/chat": "^0.9.0",
6060
"@jupyterlab/application": "^4.4.0-alpha.0",
6161
"@jupyterlab/apputils": "^4.5.0-alpha.0",
6262
"@jupyterlab/completer": "^4.4.0-alpha.0",

src/chat-handler.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,11 @@ export class ChatHandler extends ChatModel {
139139

140140
let content = '';
141141

142+
this._controller = new AbortController();
142143
try {
143144
for await (const chunk of await this._providerRegistry.currentChatModel.stream(
144-
messages
145+
messages,
146+
{ signal: this._controller.signal }
145147
)) {
146148
content += chunk.content ?? chunk;
147149
botMsg.body = content;
@@ -162,6 +164,7 @@ export class ChatHandler extends ChatModel {
162164
return false;
163165
} finally {
164166
this.updateWriters([]);
167+
this._controller = null;
165168
}
166169
}
167170

@@ -177,12 +180,17 @@ export class ChatHandler extends ChatModel {
177180
super.messageAdded(message);
178181
}
179182

183+
stopStreaming(): void {
184+
this._controller?.abort();
185+
}
186+
180187
private _providerRegistry: IAIProviderRegistry;
181188
private _personaName = 'AI';
182189
private _prompt: string;
183190
private _errorMessage: string = '';
184191
private _history: IChatHistory = { messages: [] };
185192
private _defaultErrorMessage = 'AI provider not configured';
193+
private _controller: AbortController | null = null;
186194
}
187195

188196
export namespace ChatHandler {

src/components/stop-button.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright (c) Jupyter Development Team.
3+
* Distributed under the terms of the Modified BSD License.
4+
*/
5+
6+
import StopIcon from '@mui/icons-material/Stop';
7+
import React from 'react';
8+
9+
import { InputToolbarRegistry, TooltippedButton } from '@jupyter/chat';
10+
11+
/**
12+
* Properties of the stop button.
13+
*/
14+
export interface IStopButtonProps
15+
extends InputToolbarRegistry.IToolbarItemProps {
16+
/**
17+
* The function to stop streaming.
18+
*/
19+
stopStreaming: () => void;
20+
}
21+
22+
/**
23+
* The stop button.
24+
*/
25+
export function StopButton(props: IStopButtonProps): JSX.Element {
26+
const tooltip = 'Stop streaming';
27+
return (
28+
<TooltippedButton
29+
onClick={props.stopStreaming}
30+
tooltip={tooltip}
31+
buttonProps={{
32+
size: 'small',
33+
variant: 'contained',
34+
title: tooltip
35+
}}
36+
>
37+
<StopIcon />
38+
</TooltippedButton>
39+
);
40+
}
41+
42+
/**
43+
* factory returning the toolbar item.
44+
*/
45+
export function stopItem(
46+
stopStreaming: () => void
47+
): InputToolbarRegistry.IToolbarItem {
48+
return {
49+
element: (props: InputToolbarRegistry.IToolbarItemProps) => {
50+
const stopProps: IStopButtonProps = { ...props, stopStreaming };
51+
return StopButton(stopProps);
52+
},
53+
position: 50,
54+
hidden: true /* hidden by default */
55+
};
56+
}

src/index.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
buildErrorWidget,
55
ChatCommandRegistry,
66
IActiveCellManager,
7-
IChatCommandRegistry
7+
IChatCommandRegistry,
8+
InputToolbarRegistry
89
} from '@jupyter/chat';
910
import {
1011
JupyterFrontEnd,
@@ -28,6 +29,7 @@ import { defaultProviderPlugins } from './default-providers';
2829
import { AIProviderRegistry } from './provider';
2930
import { aiSettingsRenderer, SettingConnector } from './settings';
3031
import { IAIProviderRegistry } from './tokens';
32+
import { stopItem } from './components/stop-button';
3133

3234
const chatCommandRegistryPlugin: JupyterFrontEndPlugin<IChatCommandRegistry> = {
3335
id: '@jupyterlite/ai:autocompletion-registry',
@@ -102,12 +104,30 @@ const chatPlugin: JupyterFrontEndPlugin<void> = {
102104
});
103105

104106
let chatWidget: ReactWidget | null = null;
107+
108+
const inputToolbarRegistry = InputToolbarRegistry.defaultToolbarRegistry();
109+
const stopButton = stopItem(() => chatHandler.stopStreaming());
110+
inputToolbarRegistry.addItem('stop', stopButton);
111+
112+
chatHandler.writersChanged.connect((_, users) => {
113+
if (
114+
users.filter(user => user.username === chatHandler.personaName).length
115+
) {
116+
inputToolbarRegistry.hide('send');
117+
inputToolbarRegistry.show('stop');
118+
} else {
119+
inputToolbarRegistry.hide('stop');
120+
inputToolbarRegistry.show('send');
121+
}
122+
});
123+
105124
try {
106125
chatWidget = buildChatSidebar({
107126
model: chatHandler,
108127
themeManager,
109128
rmRegistry,
110-
chatCommandRegistry
129+
chatCommandRegistry,
130+
inputToolbarRegistry
111131
});
112132
chatWidget.title.caption = 'Jupyterlite AI Chat';
113133
} catch (e) {

yarn.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -906,9 +906,9 @@ __metadata:
906906
languageName: node
907907
linkType: hard
908908

909-
"@jupyter/chat@npm:^0.8.1":
910-
version: 0.8.1
911-
resolution: "@jupyter/chat@npm:0.8.1"
909+
"@jupyter/chat@npm:^0.9.0":
910+
version: 0.9.0
911+
resolution: "@jupyter/chat@npm:0.9.0"
912912
dependencies:
913913
"@emotion/react": ^11.10.5
914914
"@emotion/styled": ^11.10.5
@@ -930,7 +930,7 @@ __metadata:
930930
clsx: ^2.1.0
931931
react: ^18.2.0
932932
react-dom: ^18.2.0
933-
checksum: d4a3635cd419642c62ca462ef9ec5327178bed7f898684e31a193246b081707685ba825ef0a83e3b6618316a7e7ffd68141194759cb1f2b107215cbe33ef5d59
933+
checksum: dee9a9e02ea8a6d25e1caebf4e9bece42c82fa40baa84dc655540bb64a676b4b1890373390c291f35eb1fe8f821d15935ec21fdd0e12063a497e575f4ddbcd78
934934
languageName: node
935935
linkType: hard
936936

@@ -2108,7 +2108,7 @@ __metadata:
21082108
version: 0.0.0-use.local
21092109
resolution: "@jupyterlite/ai@workspace:."
21102110
dependencies:
2111-
"@jupyter/chat": ^0.8.1
2111+
"@jupyter/chat": ^0.9.0
21122112
"@jupyterlab/application": ^4.4.0-alpha.0
21132113
"@jupyterlab/apputils": ^4.5.0-alpha.0
21142114
"@jupyterlab/builder": ^4.0.0

0 commit comments

Comments
 (0)