Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/ai/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@
"watch:labextension": "jupyter-builder watch ."
},
"dependencies": {
"@jupyter/chat": "^0.21.1",
"@jupyter/chat": "^0.24.0-alpha.0",
"@jupyterlab/application": "^4.5.8",
"@jupyterlab/apputils": "^4.6.8",
"@jupyterlab/coreutils": "^6.5.8",
"@jupyterlab/docmanager": "^4.5.8",
"@jupyterlab/filebrowser": "^4.5.8",
"@jupyterlab/notebook": "^4.5.8",
"@jupyterlab/observables": "^5.5.8",
"@jupyterlab/rendermime": "^4.5.8",
"@jupyterlab/services": "^7.5.8",
"@jupyterlab/settingregistry": "^4.5.8",
Expand Down
79 changes: 78 additions & 1 deletion packages/ai/schema/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@
"jupyter.lab.shortcuts": [],
"title": "AI Chat",
"jupyter.lab.setting-icon": "@jupyternaut/agent:jupyternaut",
"jupyter.lab.transform": true,
"jupyter.lab.toolbars": {
"Chat": [
{
"name": "moveChat",
"command": "@jupyterlite/ai:move-chat"
},
{
"name": "usage"
},
{
"name": "saveChat"
}
]
},
"description": "Configuration for JupyterLite AI chat behavior",
"type": "object",
"properties": {
Expand Down Expand Up @@ -34,7 +49,69 @@
"description": "Directory where chat history backups are saved, relative to the server root. Default is the server root.",
"type": "string",
"default": ""
},
"toolbar": {
"title": "Chat widget toolbar items",
"description": "Note: To disable a toolbar item,\ncopy it to User Preferences and add the\n\"disabled\" key. The following example will disable the uploader button:\n{\n \"toolbar\": [\n {\n \"name\": \"uploader\",\n \"disabled\": true\n }\n ]\n}\n\nToolbar description:",
"items": {
"$ref": "#/definitions/toolbarItem"
},
"type": "array",
"default": []
}
},
"additionalProperties": false
"additionalProperties": false,
"definitions": {
"toolbarItem": {
"properties": {
"name": {
"title": "Unique name",
"type": "string"
},
"args": {
"title": "Command arguments",
"type": "object"
},
"command": {
"title": "Command id",
"type": "string",
"default": ""
},
"disabled": {
"title": "Whether the item is ignored or not",
"type": "boolean",
"default": false
},
"icon": {
"title": "Item icon id",
"description": "If defined, it will override the command icon",
"type": "string"
},
"label": {
"title": "Item label",
"description": "If defined, it will override the command label",
"type": "string"
},
"caption": {
"title": "Item caption",
"description": "If defined, it will override the command caption",
"type": "string"
},
"type": {
"title": "Item type",
"type": "string",
"enum": ["command", "spacer"]
},
"rank": {
"title": "Item rank",
"type": "number",
"minimum": 0,
"default": 50
}
},
"required": ["name"],
"additionalProperties": false,
"type": "object"
}
}
}
64 changes: 39 additions & 25 deletions packages/ai/src/chat-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,46 +292,61 @@ export class AIChatModel extends AbstractChatModel implements IAIChatModel {
* Sends a message to the AI and generates a response.
* @param message The user message to send
*/
async sendMessage(message: INewMessage): Promise<void> {
const hasBody = message.body.trim().length > 0;
const hasAttachments = this.input.attachments.length > 0;
if (!hasBody && !hasAttachments) {
return;
async sendMessage(message: INewMessage): Promise<string | null> {
const body =
!!message.body && message.body.trim().length > 0 ? message.body : '';

if (
!body &&
!message.mime_model &&
!message.attachments?.length &&
!message.sender?.bot
) {
return null;
}

// Add user message to chat
const userMessage: IMessageContent = {
body: message.body,
sender: this.user || { username: 'user', display_name: 'User' },
const msg: IMessageContent = {
body,
sender:
message.sender ??
(this.user || { username: 'user', display_name: 'User' }),
id: UUID.uuid4(),
time: Date.now() / 1000,
type: 'msg',
raw_time: false,
attachments: [...this.input.attachments],
mentions: this.input.mentions
raw_time: false
};

if (message.attachments?.length) {
msg.attachments = message.attachments;
}

if (message.mime_model) {
msg.mime_model = message.mime_model;
}

if (message.mentions?.length) {
msg.mentions = message.mentions;
}

// Check if we have valid configuration
if (!this.agentManager?.hasValidConfig()) {
this.messageAdded(userMessage);
return;
this.messageAdded(msg);
return null;
}

if (this._persona?.isBusy) {
if (this._persona?.isBusy && !msg.sender.bot) {
this._messageQueue.push({
id: UUID.uuid4(),
body: message.body,
_originalMsg: userMessage
body,
_originalMsg: msg
});
this.input.clearAttachments();
this.input.clearMentions();
this._updateQueueUI();
return;
return null;
}

this.messageAdded(userMessage);
this.input.clearAttachments();
this.input.clearMentions();
this.messageAdded(msg);
return msg.id;
}

/**
Expand Down Expand Up @@ -406,7 +421,7 @@ export class AIChatModel extends AbstractChatModel implements IAIChatModel {
const queueMessage: IMessageContent = {
body: '',
mime_model: queueBody,
sender: { username: 'system', display_name: '' },
sender: { username: 'system', display_name: '', bot: true },
id: this._queueMessageId,
time: Date.now() / 1000,
type: 'msg',
Expand All @@ -425,9 +440,8 @@ export class AIChatModel extends AbstractChatModel implements IAIChatModel {
}

const next = this._messageQueue.shift()!;
next._originalMsg.time = Date.now() / 1000;
this._updateQueueUI();
this.messageAdded(next._originalMsg);
this.sendMessage(next._originalMsg);
}

/**
Expand Down
6 changes: 4 additions & 2 deletions packages/ai/src/components/save-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,10 @@ export class SaveComponentWidget extends ReactWidget {
this._options = options;
}

protected render(): React.ReactElement {
return <SaveComponent {...this._options} />;
protected render(): React.ReactElement | null {
return this._options.model.saveAvailable ? (
<SaveComponent {...this._options} />
) : null;
}

private _options: ISaveButtonProps;
Expand Down
Loading
Loading