cocoonstack-sandbox-langchain 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,7 @@
1
+ """LangChain tools backed by cocoon microVM sandboxes."""
2
+
3
+ from .toolkit import CocoonToolkit
4
+
5
+ __all__ = [
6
+ "CocoonToolkit",
7
+ ]
@@ -0,0 +1,127 @@
1
+ """LangChain toolkit over cocoon microVM sandboxes: one claimed sandbox
2
+ behind a set of StructuredTools (exec, file read/write, list). The claim is
3
+ lazy — building an agent costs nothing until a tool actually runs — and the
4
+ toolkit is a context manager whose exit releases the sandbox."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import asyncio
9
+ import json
10
+ import threading
11
+
12
+ from cocoonsandbox import Client, Sandbox
13
+ from langchain_core.tools import StructuredTool
14
+ from pydantic import BaseModel, Field
15
+
16
+
17
+ class ExecInput(BaseModel):
18
+ command: str = Field(description="shell command, run via sh -c inside the sandbox")
19
+ cwd: str = Field(default="", description="working directory (optional)")
20
+
21
+
22
+ class WriteFileInput(BaseModel):
23
+ path: str = Field(description="absolute path in the sandbox")
24
+ content: str = Field(description="file content to write")
25
+
26
+
27
+ class PathInput(BaseModel):
28
+ path: str = Field(description="absolute path in the sandbox")
29
+
30
+
31
+ class CocoonToolkit:
32
+ """LangChain tools backed by one sandbox, claimed on first tool use.
33
+
34
+ from_checkpoint branches a fresh sandbox from that checkpoint's captured
35
+ moment instead of claiming a clean template — agents resume from
36
+ prepared state.
37
+ """
38
+
39
+ def __init__(self, addr: str, api_token: str = "", template: str = "rt:24.04",
40
+ net: str = "", ttl_seconds: int = 0, from_checkpoint: str = ""):
41
+ self._client = Client(addr, api_token=api_token)
42
+ self._template = template
43
+ self._net = net
44
+ self._ttl = ttl_seconds
45
+ self._from_checkpoint = from_checkpoint
46
+ self._lock = threading.Lock()
47
+ self._sb: Sandbox | None = None
48
+ self._closed = False
49
+
50
+ def __enter__(self) -> CocoonToolkit:
51
+ return self
52
+
53
+ def __exit__(self, *exc) -> None:
54
+ self.close()
55
+
56
+ def get_tools(self) -> list[StructuredTool]:
57
+ """The sandbox tool set; sync-native (_run), async via to_thread."""
58
+ return [
59
+ self._tool("sandbox_exec",
60
+ "Run a shell command in the sandbox; returns stdout, stderr, "
61
+ "and the exit code. State on disk persists across calls.",
62
+ ExecInput, self._exec),
63
+ self._tool("sandbox_write_file",
64
+ "Write a text file in the sandbox (atomic).",
65
+ WriteFileInput, self._write_file),
66
+ self._tool("sandbox_read_file",
67
+ "Read a text file from the sandbox.",
68
+ PathInput, self._read_file),
69
+ self._tool("sandbox_list_dir",
70
+ "List a sandbox directory as JSON entries.",
71
+ PathInput, self._list_dir),
72
+ ]
73
+
74
+ def close(self) -> None:
75
+ """Releases the sandbox; safe to call twice or before any claim.
76
+ Tools invoked after close raise instead of silently claiming a
77
+ sandbox nothing would ever release."""
78
+ with self._lock:
79
+ sb, self._sb = self._sb, None
80
+ self._closed = True
81
+ if sb is not None:
82
+ sb.close()
83
+
84
+ def sandbox(self) -> Sandbox:
85
+ """The claimed sandbox, claiming (or branching) on first use."""
86
+ with self._lock:
87
+ if self._closed:
88
+ raise RuntimeError("toolkit is closed")
89
+ if self._sb is None:
90
+ self._sb = self._claim()
91
+ return self._sb
92
+
93
+ def _claim(self) -> Sandbox:
94
+ if self._from_checkpoint:
95
+ return self._client.checkpoint(self._from_checkpoint).new(ttl_seconds=self._ttl)
96
+ return self._client.new(self._template, net=self._net, ttl_seconds=self._ttl)
97
+
98
+ def _tool(self, name: str, description: str, schema: type[BaseModel], func) -> StructuredTool:
99
+ async def arun(**kwargs):
100
+ return await asyncio.to_thread(func, **kwargs)
101
+
102
+ return StructuredTool.from_function(func=func, coroutine=arun, name=name,
103
+ description=description, args_schema=schema)
104
+
105
+ def _exec(self, command: str, cwd: str = "") -> str:
106
+ out: list[bytes] = []
107
+ errs: list[bytes] = []
108
+ code = self.sandbox().run(["sh", "-c", command], cwd=cwd,
109
+ on_stdout=out.append, on_stderr=errs.append)
110
+ stdout = b"".join(out).decode(errors="replace")
111
+ stderr = b"".join(errs).decode(errors="replace")
112
+ result = stdout
113
+ if stderr:
114
+ result += ("\n" if result else "") + "stderr: " + stderr
115
+ if code != 0:
116
+ result += ("\n" if result else "") + f"exit code: {code}"
117
+ return result or "(no output)"
118
+
119
+ def _write_file(self, path: str, content: str) -> str:
120
+ self.sandbox().write_file(path, content.encode())
121
+ return f"wrote {path}"
122
+
123
+ def _read_file(self, path: str) -> str:
124
+ return self.sandbox().read_file(path).decode(errors="replace")
125
+
126
+ def _list_dir(self, path: str) -> str:
127
+ return json.dumps(self.sandbox().list_dir(path))
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.4
2
+ Name: cocoonstack-sandbox-langchain
3
+ Version: 0.1.0
4
+ Summary: LangChain tools backed by cocoon microVM sandboxes
5
+ License: Apache-2.0
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: langchain-core>=1.0
9
+ Requires-Dist: cocoonstack-sandbox>=0.1
10
+
11
+ # cocoonstack-sandbox-langchain
12
+
13
+ LangChain tools backed by cocoon microVM sandboxes: a `CocoonToolkit`
14
+ claims one sandbox lazily on first tool use and exposes it as
15
+ `StructuredTool`s an agent can call — shell exec, file read/write, and
16
+ directory listing, with disk state persisting across calls.
17
+
18
+ ```python
19
+ from cocoonsandbox_langchain import CocoonToolkit
20
+
21
+ with CocoonToolkit("10.0.0.5:7777", api_token="...") as kit:
22
+ agent = create_react_agent(model, kit.get_tools())
23
+ agent.invoke({"messages": [("user", "run the tests in /work")]})
24
+ ```
25
+
26
+ `from_checkpoint="ck_..."` branches the sandbox from a checkpoint's
27
+ captured moment instead of a clean template — agents resume from prepared
28
+ state (dependencies installed, repo cloned) in milliseconds.
29
+
30
+ Sync-native (`_run` calls the stdlib-only cocoonstack-sandbox SDK
31
+ directly); async agents get `_arun` bridged via `asyncio.to_thread`.
32
+
33
+ Full reference: https://cocoonstack.github.io/sandbox/langchain
@@ -0,0 +1,6 @@
1
+ cocoonsandbox_langchain/__init__.py,sha256=9_otWHxffnMJBm2_1XcZQdA2InXQParAM1Pha-SyXt4,130
2
+ cocoonsandbox_langchain/toolkit.py,sha256=hcCcp24ldUZz7iKd4H88EZiwaJ8AxuKf3SFOwUk63Ts,5008
3
+ cocoonstack_sandbox_langchain-0.1.0.dist-info/METADATA,sha256=HSPW_g5wCG_QN31LTH-QHiPv6-ueLGFeKUcHxFnjk2c,1249
4
+ cocoonstack_sandbox_langchain-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
5
+ cocoonstack_sandbox_langchain-0.1.0.dist-info/top_level.txt,sha256=H0cckpx__PMsnQoWLd94bpqIQtu8sB0qJxKmhib3ntI,24
6
+ cocoonstack_sandbox_langchain-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ cocoonsandbox_langchain