Skip to content

Commit

Permalink
widgets: implement using websocket for sending buffers so compute ser…
Browse files Browse the repository at this point in the history
…vers fully work too.

- NOTE: we'll benchmark and maybe use http for non-compute servers; not
  sure.
  • Loading branch information
williamstein committed Jul 23, 2024
1 parent b5fe24a commit 504f6cc
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 29 deletions.
17 changes: 15 additions & 2 deletions src/packages/frontend/client/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,9 +506,22 @@ export class ProjectClient {
path: string,
model_id: string,
buffer_path: string,
useHttp?: boolean, // ONLY works for home base, NOT compute servers!
): Promise<ArrayBuffer> {
const url = ipywidgetsGetBufferUrl(project_id, path, model_id, buffer_path);
return await (await fetch(url)).arrayBuffer();
if (useHttp) {
const url = ipywidgetsGetBufferUrl(
project_id,
path,
model_id,
buffer_path,
);
return await (await fetch(url)).arrayBuffer();
}
const actions = redux.getEditorActions(project_id, path);
return await actions.jupyter_actions.ipywidgetsGetBuffer(
model_id,
buffer_path,
);
}

// getting, setting, editing, deleting, etc., the api keys for a project
Expand Down
13 changes: 12 additions & 1 deletion src/packages/frontend/jupyter/browser-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ import {
} from "../misc/local-storage";
import { parse_headings } from "./contents";
import { webapp_client } from "@cocalc/frontend/webapp-client";
import { bufferToBase64 } from "@cocalc/util/base64";
import { bufferToBase64, base64ToBuffer } from "@cocalc/util/base64";
import { reuseInFlight } from "@cocalc/util/reuse-in-flight";

export class JupyterActions extends JupyterActions0 {
public widget_manager?: WidgetManager;
Expand Down Expand Up @@ -476,6 +477,16 @@ export class JupyterActions extends JupyterActions0 {
return msg_id;
};

ipywidgetsGetBuffer = reuseInFlight(
async (model_id: string, buffer_path: string): Promise<ArrayBuffer> => {
const { buffer64 } = await this.api_call("ipywidgets-get-buffer", {
model_id,
buffer_path,
});
return base64ToBuffer(buffer64);
},
);

// NOTE: someday move this to the frame-tree actions, since it would
// be generically useful!
// Display a confirmation dialog, then return the chosen option.
Expand Down
2 changes: 1 addition & 1 deletion src/packages/frontend/jupyter/widgets/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type DeserializedModelState = { [key: string]: any };

export type SendCommFunction = (string, data) => string;

// const log = console.log;
//const log = console.log;
const log = (..._args) => {};

export class WidgetManager {
Expand Down
6 changes: 5 additions & 1 deletion src/packages/jupyter/kernel/kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -985,8 +985,12 @@ class JupyterKernel extends EventEmitter implements JupyterKernelInterface {

public ipywidgetsGetBuffer(
model_id: string,
buffer_path: string,
// buffer_path is the string[] *or* the JSON of that.
buffer_path: string | string[],
): Buffer | undefined {
if (typeof buffer_path != "string") {
buffer_path = JSON.stringify(buffer_path);
}
return this._actions?.syncdb.ipywidgets_state?.getBuffer(
model_id,
buffer_path,
Expand Down
20 changes: 20 additions & 0 deletions src/packages/jupyter/kernel/websocket-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ various messages related to working with Jupyter.

import { get_existing_kernel } from "@cocalc/jupyter/kernel";
import { get_kernel_data } from "@cocalc/jupyter/kernel/kernel-data";
import { bufferToBase64 } from "@cocalc/util/base64";

export async function handleApiRequest(
path: string,
Expand Down Expand Up @@ -37,15 +38,20 @@ export async function handleApiRequest(
case "save_ipynb_file":
await kernel.save_ipynb_file();
return {};

case "signal":
kernel.signal(query.signal);
return {};

case "kernel_info":
return await kernel.kernel_info();

case "more_output":
return kernel.more_output(query.id);

case "complete":
return await kernel.complete(get_code_and_cursor_pos(query));

case "introspect":
const { code, cursor_pos } = get_code_and_cursor_pos(query);
let detail_level = 0;
Expand All @@ -64,6 +70,7 @@ export async function handleApiRequest(
cursor_pos,
detail_level,
});

case "store":
const { key, value } = query;
if (value === undefined) {
Expand All @@ -77,8 +84,21 @@ export async function handleApiRequest(
kernel.store.set(key, value);
return {};
}

case "comm":
return kernel.send_comm_message_to_kernel(query);

case "ipywidgets-get-buffer":
const { model_id, buffer_path } = query;
const buffer = kernel.ipywidgetsGetBuffer(model_id, buffer_path);
if (buffer == null) {
throw Error(
`no buffer for model=${model_id}, buffer_path=${JSON.stringify(
buffer_path,
)}`,
);
}
return { buffer64: bufferToBase64(buffer) };
default:
throw Error(`unknown endpoint "${endpoint}"`);
}
Expand Down
23 changes: 0 additions & 23 deletions src/packages/project/jupyter/http-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,29 +64,6 @@ function jupyter_kernel_info_handler(router): void {
}
);

router.get(
BASE + "ipywidgets-get-buffer-info",
async function (req, res): Promise<void> {
try {
const { path, model_id, buffer_path } = req.query;
const kernel = get_existing_kernel(path);
if (kernel == null) {
res.status(404).send(`kernel associated to ${path} does not exist`);
return;
}
const buffer = kernel.ipywidgetsGetBuffer(model_id, buffer_path);
res.send({
path,
model_id,
buffer_path,
buffer_length: buffer?.length,
});
} catch (err) {
res.status(500).send(`Error getting ipywidgets buffer info - ${err}`);
}
}
);

// we are only actually using this to serve up the logo.
router.get(BASE + "kernelspecs/*", async function (req, res): Promise<void> {
try {
Expand Down
2 changes: 1 addition & 1 deletion src/packages/sync/editor/generic/ipywidgets-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ export class IpywidgetsState extends EventEmitter {
};

private clientGetBuffer = async (model_id: string, path: string) => {
// async get of the buffer efficiently via HTTP:
// async get of the buffer from backend
if (this.client.ipywidgetsGetBuffer == null) {
throw Error(
"NotImplementedError: frontend client must implement ipywidgetsGetBuffer in order to support binary buffers",
Expand Down

0 comments on commit 504f6cc

Please sign in to comment.