clikernel 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {clikernel-0.2.0 → clikernel-0.2.2}/CHANGELOG.md +14 -0
- {clikernel-0.2.0/clikernel.egg-info → clikernel-0.2.2}/PKG-INFO +2 -2
- {clikernel-0.2.0 → clikernel-0.2.2}/clikernel/__init__.py +4 -1
- {clikernel-0.2.0 → clikernel-0.2.2}/clikernel/_modidx.py +5 -4
- {clikernel-0.2.0 → clikernel-0.2.2}/clikernel/cli.py +3 -1
- {clikernel-0.2.0 → clikernel-0.2.2}/clikernel/core.py +11 -31
- {clikernel-0.2.0 → clikernel-0.2.2}/clikernel/mcp.py +6 -3
- {clikernel-0.2.0 → clikernel-0.2.2/clikernel.egg-info}/PKG-INFO +2 -2
- {clikernel-0.2.0 → clikernel-0.2.2}/clikernel.egg-info/requires.txt +1 -1
- {clikernel-0.2.0 → clikernel-0.2.2}/pyproject.toml +1 -1
- {clikernel-0.2.0 → clikernel-0.2.2}/LICENSE +0 -0
- {clikernel-0.2.0 → clikernel-0.2.2}/MANIFEST.in +0 -0
- {clikernel-0.2.0 → clikernel-0.2.2}/README.md +0 -0
- {clikernel-0.2.0 → clikernel-0.2.2}/clikernel/skill.py +0 -0
- {clikernel-0.2.0 → clikernel-0.2.2}/clikernel/stream.py +0 -0
- {clikernel-0.2.0 → clikernel-0.2.2}/clikernel.egg-info/SOURCES.txt +0 -0
- {clikernel-0.2.0 → clikernel-0.2.2}/clikernel.egg-info/dependency_links.txt +0 -0
- {clikernel-0.2.0 → clikernel-0.2.2}/clikernel.egg-info/entry_points.txt +0 -0
- {clikernel-0.2.0 → clikernel-0.2.2}/clikernel.egg-info/top_level.txt +0 -0
- {clikernel-0.2.0 → clikernel-0.2.2}/setup.cfg +0 -0
- {clikernel-0.2.0 → clikernel-0.2.2}/tests/test_stream.py +0 -0
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- do not remove -->
|
|
4
4
|
|
|
5
|
+
## 0.2.2
|
|
6
|
+
|
|
7
|
+
### New Features
|
|
8
|
+
|
|
9
|
+
- skip AST inspectors on nested `run_cell` so tool-replayed cells (e.g. %nbrun) are not treated as user-typed input ([#32](https://github.com/AnswerDotAI/clikernel/issues/32))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## 0.2.1
|
|
13
|
+
|
|
14
|
+
### New Features
|
|
15
|
+
|
|
16
|
+
- Delegate traceback truncation and ANSI stripping to fastcore `render_text`(`tb_maxlen`=120); drop `render_outs` ([#31](https://github.com/AnswerDotAI/clikernel/issues/31))
|
|
17
|
+
|
|
18
|
+
|
|
5
19
|
## 0.2.0
|
|
6
20
|
|
|
7
21
|
### New Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clikernel
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Serve persistent Jupyter kernels to LLMs as concise text, over MCP or a plain stream protocol
|
|
5
5
|
Author: clikernel contributors
|
|
6
6
|
License: Apache-2.0
|
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
11
11
|
Requires-Python: >=3.11
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
-
Requires-Dist: fastcore>=2.
|
|
14
|
+
Requires-Dist: fastcore>=2.2.2
|
|
15
15
|
Requires-Dist: jupyasyncclient>=0.2.1
|
|
16
16
|
Requires-Dist: mcpmini>=0.0.1
|
|
17
17
|
Requires-Dist: aidialog>=0.0.7
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Modules:
|
|
4
4
|
|
|
5
|
+
- `clikernel.cli`: The stream-protocol frontend: the service on stdin/stdout for token-reading clients
|
|
6
|
+
- `clikernel.core`: Connect to jupygate-hosted kernels and turn execution into concise text
|
|
7
|
+
- `clikernel.mcp`: The MCP frontend: `Client` as tools on stdio
|
|
5
8
|
- `clikernel.skill`: Use the persistent `clikernel` MCP session as the default workspace for any task advanced through live Python execution -- stateful inspection, file-editing workflows, debugging, experiments, API probes, data transforms, or notebook-style work. Read this before writing, running, or debugging Python code in a session with `clikernel` connected.
|
|
6
9
|
- `clikernel.stream`: Streaming JSON-lines worker protocol: nbformat-shaped output events, and a supervisor for select-based UIs."""
|
|
7
10
|
|
|
8
|
-
__version__ = "0.2.
|
|
11
|
+
__version__ = "0.2.2"
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Autogenerated by nbdev
|
|
2
2
|
|
|
3
|
-
d = { 'settings': {'branch': 'main',
|
|
3
|
+
d = { 'settings': { 'branch': 'main',
|
|
4
|
+
'doc_baseurl': '/clikernel',
|
|
5
|
+
'doc_host': 'https://AnswerDotAI.github.io',
|
|
6
|
+
'git_url': 'https://github.com/AnswerDotAI/clikernel',
|
|
7
|
+
'lib_path': 'clikernel'},
|
|
4
8
|
'syms': { 'clikernel.cli': { 'clikernel.cli._new_delim': ('cli.html#_new_delim', 'clikernel/cli.py'),
|
|
5
9
|
'clikernel.cli._next_line': ('cli.html#_next_line', 'clikernel/cli.py'),
|
|
6
10
|
'clikernel.cli._read_block': ('cli.html#_read_block', 'clikernel/cli.py'),
|
|
@@ -23,11 +27,8 @@ d = { 'settings': {'branch': 'main', 'doc_baseurl': '/', 'doc_host': '', 'git_ur
|
|
|
23
27
|
'clikernel.core.Client.stop': ('core.html#client.stop', 'clikernel/core.py'),
|
|
24
28
|
'clikernel.core._inspector_setup': ('core.html#_inspector_setup', 'clikernel/core.py'),
|
|
25
29
|
'clikernel.core._startup_src': ('core.html#_startup_src', 'clikernel/core.py'),
|
|
26
|
-
'clikernel.core._tb_line': ('core.html#_tb_line', 'clikernel/core.py'),
|
|
27
|
-
'clikernel.core._trunc_tb': ('core.html#_trunc_tb', 'clikernel/core.py'),
|
|
28
30
|
'clikernel.core.cfg_dir': ('core.html#cfg_dir', 'clikernel/core.py'),
|
|
29
31
|
'clikernel.core.gateways': ('core.html#gateways', 'clikernel/core.py'),
|
|
30
|
-
'clikernel.core.render_outs': ('core.html#render_outs', 'clikernel/core.py'),
|
|
31
32
|
'clikernel.core.resolve': ('core.html#resolve', 'clikernel/core.py')},
|
|
32
33
|
'clikernel.mcp': { 'clikernel.mcp.main': ('mcp.html#main', 'clikernel/mcp.py'),
|
|
33
34
|
'clikernel.mcp.mk_server': ('mcp.html#mk_server', 'clikernel/mcp.py'),
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""The stream-protocol frontend: the service on stdin/stdout for token-reading clients
|
|
2
2
|
|
|
3
|
-
The `clikernel` command: the delimiter-framed stdin/stdout protocol v1 established (documented in the README, rationale unchanged — a client that reads stdout as tokens wants no echo, a cheap ack byte, and a per-process random delimiter to read until). The protocol machinery ports from v1 verbatim; underneath, the process is now a thin client of a jupygate kernel. Run bare it creates a kernel and stops it again on exit — whoever ran the command made that decision by running it — while `--kernel` attaches to an existing kernel and leaves it exactly as found. Ctrl-C during a long cell translates into a kernel interrupt, jupyter-console style, instead of killing the process.
|
|
3
|
+
The `clikernel` command: the delimiter-framed stdin/stdout protocol v1 established (documented in the README, rationale unchanged — a client that reads stdout as tokens wants no echo, a cheap ack byte, and a per-process random delimiter to read until). The protocol machinery ports from v1 verbatim; underneath, the process is now a thin client of a jupygate kernel. Run bare it creates a kernel and stops it again on exit — whoever ran the command made that decision by running it — while `--kernel` attaches to an existing kernel and leaves it exactly as found. Ctrl-C during a long cell translates into a kernel interrupt, jupyter-console style, instead of killing the process.
|
|
4
|
+
|
|
5
|
+
Docs: https://AnswerDotAI.github.io/clikernel/cli.html.md"""
|
|
4
6
|
|
|
5
7
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_cli.ipynb.
|
|
6
8
|
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
"""Connect to jupygate-hosted kernels and turn execution into concise text
|
|
2
2
|
|
|
3
|
-
clikernel is the LLM side of a two-process design: jupygate runs all the time and hosts the kernels; clikernel starts and stops with each conversation and holds nothing but a pointer. This module is the whole client: gateway resolution from `gateways.toml`, the concise-text rendering contract (ANSI-stripped, capped tracebacks), delivery of `startup.py` and `inspectors.py` into freshly created kernels, and `Client` — connect (create or attach), execute, interrupt, restart, stop, list. The MCP and CLI frontends are thin faces over `Client`; nothing here creates or kills a kernel except when asked.
|
|
3
|
+
clikernel is the LLM side of a two-process design: jupygate runs all the time and hosts the kernels; clikernel starts and stops with each conversation and holds nothing but a pointer. This module is the whole client: gateway resolution from `gateways.toml`, the concise-text rendering contract (ANSI-stripped, capped tracebacks), delivery of `startup.py` and `inspectors.py` into freshly created kernels, and `Client` — connect (create or attach), execute, interrupt, restart, stop, list. The MCP and CLI frontends are thin faces over `Client`; nothing here creates or kills a kernel except when asked.
|
|
4
|
+
|
|
5
|
+
Docs: https://AnswerDotAI.github.io/clikernel/core.html.md"""
|
|
4
6
|
|
|
5
7
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb.
|
|
6
8
|
|
|
7
9
|
# %% auto #0
|
|
8
|
-
__all__ = ['DEFAULT_URL', 'STATE_LOST', 'cfg_dir', 'gateways', 'resolve', '
|
|
10
|
+
__all__ = ['DEFAULT_URL', 'STATE_LOST', 'cfg_dir', 'gateways', 'resolve', 'Client']
|
|
9
11
|
|
|
10
12
|
# %% ../nbs/00_core.ipynb #2b3b7f4a
|
|
11
13
|
import os, tomllib
|
|
12
14
|
from fastcore.utils import *
|
|
13
|
-
from fastcore.ansi import strip_ansi
|
|
14
15
|
from fastcore.nbio import render_text
|
|
15
16
|
from fastcore.xdg import xdg_config_home
|
|
16
17
|
from jupyasyncclient import JupyAsyncMultiKernelManager, DeadKernelError
|
|
@@ -36,33 +37,6 @@ def resolve(host='', cfgdir=None):
|
|
|
36
37
|
return g['url'], g.get('token') or os.environ.get(g.get('token_env','')) or None
|
|
37
38
|
|
|
38
39
|
|
|
39
|
-
# %% ../nbs/00_core.ipynb #b739b087
|
|
40
|
-
def _tb_line(l, maxlen):
|
|
41
|
-
"One traceback line, capped at `maxlen`; `None` drops it (an over-long anchor line means nothing once cut). `File `/`Cell ` locations are exempt."
|
|
42
|
-
if len(l) <= maxlen: return l
|
|
43
|
-
if l.strip() and not (set(l) - set('~^ ')): return None
|
|
44
|
-
if l.lstrip().startswith(('File ', 'Cell ')): return l
|
|
45
|
-
return l[:maxlen] + '…'
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def _trunc_tb(outputs, maxlen=120):
|
|
49
|
-
"Cap over-long lines in error tracebacks: a cell magic's transformed source echoes its whole payload on one line. Locations survive whole, and so does the last chunk, which is the exception message in both IPython's format and the stdlib's."
|
|
50
|
-
for o in outputs:
|
|
51
|
-
if o.get("output_type") != "error": continue
|
|
52
|
-
# Each chunk keeps its own identity: the renderer joins the list with newlines, so
|
|
53
|
-
# flattening chunks into lines would silently drop the blank line between frames.
|
|
54
|
-
# Strip ANSI before capping: the cap can sever an escape sequence, leaving an unstrippable fragment.
|
|
55
|
-
tb = [strip_ansi(c) for c in o.get("traceback", [])]
|
|
56
|
-
o["traceback"] = ["\n".join(l for l in (_tb_line(x, maxlen) for x in chunk.split("\n")) if l is not None)
|
|
57
|
-
for chunk in tb[:-1]] + tb[-1:]
|
|
58
|
-
return outputs
|
|
59
|
-
|
|
60
|
-
# %% ../nbs/00_core.ipynb #f70c7acd
|
|
61
|
-
def render_outs(outs):
|
|
62
|
-
"Concise text for nbformat-style `outs`: cap tracebacks, render, strip ANSI"
|
|
63
|
-
return strip_ansi(render_text(_trunc_tb(outs)))
|
|
64
|
-
|
|
65
|
-
|
|
66
40
|
# %% ../nbs/00_core.ipynb #0d846754
|
|
67
41
|
def _startup_src(src, path):
|
|
68
42
|
"The startup file's source wrapped so `__file__` is bound to its path during the run, and absent after"
|
|
@@ -73,6 +47,7 @@ finally: del __file__'''
|
|
|
73
47
|
# %% ../nbs/00_core.ipynb #3dbf4cb6
|
|
74
48
|
_INSP_RUNNER = r'''
|
|
75
49
|
import inspect as _clik_inspect
|
|
50
|
+
import sys as _clik_sys
|
|
76
51
|
from IPython.core.error import InputRejected
|
|
77
52
|
class RuleBlock(InputRejected):
|
|
78
53
|
"Raise from an inspector to deliberately block a cell; any other inspector exception is a bug, and fails open"
|
|
@@ -81,6 +56,11 @@ class _ClikInspect:
|
|
|
81
56
|
"Calls each inspector once per cell: 1-arg get the AST, 2-arg also the raw source"
|
|
82
57
|
def __init__(self, fs): self.fs = fs
|
|
83
58
|
def visit(self, tree):
|
|
59
|
+
fr, n = _clik_sys._getframe(), 0
|
|
60
|
+
while fr:
|
|
61
|
+
n += fr.f_code.co_name == 'run_cell_async'
|
|
62
|
+
fr = fr.f_back
|
|
63
|
+
if n > 1: return tree # nested run_cell: cell replayed by a tool (%nbrun etc.), not typed
|
|
84
64
|
for f in self.fs:
|
|
85
65
|
try:
|
|
86
66
|
note = f(tree, _clik_src) if len(_clik_inspect.signature(f).parameters) > 1 else f(tree)
|
|
@@ -161,7 +141,7 @@ async def execute_outs(self:Client, code):
|
|
|
161
141
|
async def execute(self:Client, code):
|
|
162
142
|
"Run `code` in the current kernel; concise rendered text of its outputs"
|
|
163
143
|
r = await self.execute_outs(code)
|
|
164
|
-
return r if isinstance(r, str) else
|
|
144
|
+
return r if isinstance(r, str) else render_text(r, tb_maxlen=120)
|
|
165
145
|
|
|
166
146
|
# %% ../nbs/00_core.ipynb #1ef42fff
|
|
167
147
|
@patch
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""The MCP frontend: `Client` as tools on stdio
|
|
2
2
|
|
|
3
|
-
The frontend Claude Code launches per conversation: `mk_server` closes the tools over a `Client`, and `main` (the `clikernel-mcp` console script) serves it on stdio via mcpmini. There is no instructions machinery and nothing eager — usage is taught by skill text, the server answers `initialize` instantly, and nothing happens until the model calls `connect`. Tool descriptions carry v1's hard-won wording.
|
|
3
|
+
The frontend Claude Code launches per conversation: `mk_server` closes the tools over a `Client`, and `main` (the `clikernel-mcp` console script) serves it on stdio via mcpmini. There is no instructions machinery and nothing eager — usage is taught by skill text, the server answers `initialize` instantly, and nothing happens until the model calls `connect`. Tool descriptions carry v1's hard-won wording.
|
|
4
|
+
|
|
5
|
+
Docs: https://AnswerDotAI.github.io/clikernel/mcp.html.md"""
|
|
4
6
|
|
|
5
7
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_mcp.ipynb.
|
|
6
8
|
|
|
@@ -14,7 +16,8 @@ from mcpmini.core import MCPServer, serve_stdio
|
|
|
14
16
|
from aidialog.dialog import Message
|
|
15
17
|
from aidialog.hist import output_parts, merge_media
|
|
16
18
|
from aidialog.msg_parts import PartType, data_url
|
|
17
|
-
from .
|
|
19
|
+
from fastcore.nbio import render_text
|
|
20
|
+
from .core import Client
|
|
18
21
|
from . import __version__
|
|
19
22
|
|
|
20
23
|
|
|
@@ -41,7 +44,7 @@ def mk_server(c:Client):
|
|
|
41
44
|
"Run `code` in the current kernel, keeping state across calls (imports, variables, monkeypatches, cached objects). Requires a `connect` first. If the reply says the kernel died, `connect` again. Image outputs (plots etc.) come back as image blocks, resized to a token-friendly size, each preceded by its `<media id=...>` tag."
|
|
42
45
|
r = await c.execute_outs(code)
|
|
43
46
|
if isinstance(r, str): return r
|
|
44
|
-
res = merge_media(
|
|
47
|
+
res = merge_media(render_text(r, tb_maxlen=120), output_parts(Message(msg_type='code', output=r)))
|
|
45
48
|
return res if isinstance(res, str) else dict(content=[part2block(p) for p in res], isError=False)
|
|
46
49
|
|
|
47
50
|
async def list_kernels(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clikernel
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Serve persistent Jupyter kernels to LLMs as concise text, over MCP or a plain stream protocol
|
|
5
5
|
Author: clikernel contributors
|
|
6
6
|
License: Apache-2.0
|
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
11
11
|
Requires-Python: >=3.11
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
-
Requires-Dist: fastcore>=2.
|
|
14
|
+
Requires-Dist: fastcore>=2.2.2
|
|
15
15
|
Requires-Dist: jupyasyncclient>=0.2.1
|
|
16
16
|
Requires-Dist: mcpmini>=0.0.1
|
|
17
17
|
Requires-Dist: aidialog>=0.0.7
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|