Add RPC for executing code#63
Conversation
samclark2015
left a comment
There was a problem hiding this comment.
Overall, LGTM. One issue to fix with int overflow below.
Manual Tests
Tested against a live kcserver (no-auth mode) with an IPython 8.37 / Python 3.10.12 kernel.
| # | Scenario | Command | Result |
|---|---|---|---|
| 1 | Basic stdout | run --code "print('hello world')" |
✅ hello world captured in stream output |
| 2 | Expression result | run --code "2 + 3" |
✅ 5 in data["text/plain"] |
| 3 | stderr capture | run --code "import sys; sys.stderr.write('error output\n')" |
✅ stderr routed to stream with name: stderr |
| 4 | Error handling | run --code "raise ValueError('test error')" |
✅ status: Error, error_name/traceback populated |
| 5 | Sequential state | run --code "x = 42" then run --code "print(x * 2)" |
✅ state persisted across RPC calls, printed 84 |
| 6 | display_data output |
run --code "from IPython.display import display; display({'text/plain': 'hello', 'text/html': '<b>hello</b>'}, raw=True)" |
✅ both MIME types captured and returned |
| 7 | Timeout | run --code "import time; time.sleep(30)" --timeout 2 |
✅ ExecutionTimedOut after 2s, kernel interrupted |
| 8 | Post-timeout recovery | run --code "print('still alive')" (after timeout) |
✅ kernel still alive, execution succeeded |
| 9 | Session not found | run --session-id nonexistent-session --code "..." |
✅ SessionNotFound returned |
| 10 | Concurrent RPC + WebSocket | listen in background, then run |
✅ iopub messages forwarded to both; RPC and WebSocket independent |
| /* Disabled because there's no example. | ||
| Some("ExecuteCode") => { | ||
| let result = rt.block_on(client.execute_code( | ||
| "session_id_example".to_string(), | ||
| ??? | ||
| )); | ||
| info!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has<XSpanIdString>).get().clone()); | ||
| }, | ||
| */ |
There was a problem hiding this comment.
Should we note this through a print statement? Right now, it looks like you'll get the "Invalid operation provided" despite being listed in the operation enum.
There was a problem hiding this comment.
This file is autogenerated by the OpenAPI generator and not used in the system (we could just gitignore it but could be helpful for docs/agents?)
| // Collect output messages until we receive the execute_reply | ||
| let timeout_duration = execute_request | ||
| .timeout_seconds | ||
| .map(|s| std::time::Duration::from_secs(s as u64)); |
There was a problem hiding this comment.
If timeout_seconds is negative, this will overflow to a hugely long duration. Is this expected/wanted?
Possible option, treat negatives like no timeout (returns None):
| .map(|s| std::time::Duration::from_secs(s as u64)); | |
| .filter(|&s| s > 0) | |
| .map(|s| std::time::Duration::from_secs(s as u64)); |
There was a problem hiding this comment.
I don't know why someone would set a negative timeout? But we should have more reasonable behavior if they do! Fixed up.
This change adds a code execution API to Kallichore. Prior to this change, you needed to open a websocket to execute code; now you can do so using nothing but the REST API.
The new RPC accepts most of the same parameters as Jupyter's
execute_requestand returns theexecute_resultasdata(along with all the other messages/data emitted during execution, such as stdout/stderr/display data/etc).This is intended to help support code executions from clients like MCPs and Quarto; it will not be used in ordinary Positron consoles/notebooks.
Fixes #23.