From c7d2dfdb0912c15604980fe7fb21f292fe0d2ec9 Mon Sep 17 00:00:00 2001 From: Eric Charles Date: Thu, 23 Jul 2026 15:15:12 +0200 Subject: [PATCH 1/7] fallback --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index d92c348b..3f99f408 100755 --- a/Makefile +++ b/Makefile @@ -151,9 +151,9 @@ EXAMPLES_LOCAL_ENV = \ VITE_BASE_URL_CODEMODE=http://localhost:8766 BEDROCK_ENV = \ - AWS_ACCESS_KEY_ID=${DATALAYER_BEDROCK_AWS_ACCESS_KEY_ID} \ - AWS_SECRET_ACCESS_KEY=${DATALAYER_BEDROCK_AWS_SECRET_ACCESS_KEY} \ - AWS_DEFAULT_REGION=${DATALAYER_BEDROCK_AWS_DEFAULT_REGION} + AWS_ACCESS_KEY_ID=$${DATALAYER_BEDROCK_AWS_ACCESS_KEY_ID:-$${AWS_ACCESS_KEY_ID}} \ + AWS_SECRET_ACCESS_KEY=$${DATALAYER_BEDROCK_AWS_SECRET_ACCESS_KEY:-$${AWS_SECRET_ACCESS_KEY}} \ + AWS_DEFAULT_REGION=$${DATALAYER_BEDROCK_AWS_DEFAULT_REGION:-$${AWS_DEFAULT_REGION}} RUFF_TARGETS = \ agent_runtimes/specs/agents/ \ From 6a533248141aa8d65c615ccee7ef834f324b11a2 Mon Sep 17 00:00:00 2001 From: Eric Charles Date: Thu, 23 Jul 2026 18:25:25 +0200 Subject: [PATCH 2/7] code sandboxes --- Makefile | 8 +- .../services/code_sandbox_manager.py | 22 +- agent_runtimes/specs/agents/agents.py | 350 +++++++++++++++ pyproject.toml | 8 +- scripts/codegen/generate_sandbox_agents.py | 144 +++++++ src/examples/AgentCodeSandboxesExample.tsx | 317 ++++++++++++++ src/examples/example-selector.ts | 6 + src/examples/index.ts | 1 + src/specs/agents/agents.ts | 399 ++++++++++++++++++ 9 files changed, 1245 insertions(+), 10 deletions(-) create mode 100644 scripts/codegen/generate_sandbox_agents.py create mode 100644 src/examples/AgentCodeSandboxesExample.tsx diff --git a/Makefile b/Makefile index 3f99f408..2a967f80 100755 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ SHELL=/bin/bash examples examples\:prod examples\:proxy examples-proxy agent agent-nodes agent-nodes\:proxy agent-nodes-proxy agent-notebook agent-document dev-notebook dev-document jupyter-server agent-serve \ docker-build docker-push docker-release agent-runtime-docker-build agent-runtime-docker-push agent-runtime-docker-release node-agent-artifact-build node-agents-docker-build agent-nodes-docker-build agent-nodes-docker-push agent-nodes-docker-start agent-nodes-docker-stop agent-nodes-docker-logs \ agents list-specs specs specs-clone specs-generate specs-format \ + specs-sandbox-variants \ loop loop-simple loop-data-acquisition loop-financial loop-demo loop-example-nocodemode AGENTSPECS_REPO ?= https://github.com/datalayer/agentspecs.git @@ -391,7 +392,12 @@ loop-demo-nocodemode: # loop-demo-nocodemode list-specs: # list specs agent-runtimes list-specs -specs: specs-clone specs-generate specs-format ## generate Python and TypeScript code from YAML specifications (agents, teams, MCP servers, skills, envvars) +specs: specs-clone specs-sandbox-variants specs-generate specs-format ## generate Python and TypeScript code from YAML specifications (agents, teams, MCP servers, skills, envvars) + +specs-sandbox-variants: ## scaffold sandbox example agent specs for all supported sandbox variants + $(call step,Generating sandbox variant example agents) + python scripts/codegen/generate_sandbox_agents.py \ + --agents-dir $(AGENTSPECS_DIR)/agentspecs/agents specs-clone: ## clone/update agentspecs repository $(call step,Cloning agentspecs repository ($(AGENTSPECS_BRANCH))) diff --git a/agent_runtimes/services/code_sandbox_manager.py b/agent_runtimes/services/code_sandbox_manager.py index ab76bad2..1568818d 100644 --- a/agent_runtimes/services/code_sandbox_manager.py +++ b/agent_runtimes/services/code_sandbox_manager.py @@ -9,7 +9,7 @@ Code Sandbox Manager for Agent Runtimes. This module provides a centralized manager for code sandbox instances, -allowing runtime configuration of the sandbox variant (eval or jupyter). +allowing runtime configuration of the sandbox variant. It also provides :class:`ManagedSandbox`, a transparent proxy that delegates every call to the manager's current sandbox. All consumers @@ -55,7 +55,15 @@ logger = logging.getLogger(__name__) -SandboxVariant = Literal["eval", "jupyter"] +SandboxVariant = Literal[ + "eval", + "jupyter", + "docker", + "datalayer", + "colab", + "monty", + "modal", +] @dataclass @@ -337,11 +345,13 @@ class CodeSandboxManager: - Automatic sandbox lifecycle management (start/stop) - Per-agent sandbox isolation (each agent gets its own sandbox) - The manager supports three sandbox variants: + The manager supports sandbox variants: - eval: Uses Python exec() for code execution (default) - jupyter: Connects to an *existing* Jupyter server (URL required) - jupyter: Delegates to code_sandboxes to start its own Jupyter server on a random free port (no external URL needed) + - docker, datalayer, colab, monty, modal: Delegated to the + code_sandboxes variant factory. """ _instance: CodeSandboxManager | None = None @@ -764,7 +774,9 @@ def _create_sandbox(self, variant: SandboxVariant | None = None) -> Sandbox: return JupyterSandbox() else: - raise ValueError(f"Unknown sandbox variant: {effective_variant}") + from code_sandboxes import Sandbox as CodeSandbox + + return CodeSandbox.create(variant=effective_variant) def stop(self) -> None: """Stop the current sandbox if running.""" @@ -795,7 +807,7 @@ def create_agent_sandbox( Args: agent_id: Unique agent identifier. - variant: The sandbox variant (``"eval"`` or ``"jupyter"``). + variant: The sandbox variant. env_vars: Environment variables to inject into the sandbox after it starts. diff --git a/agent_runtimes/specs/agents/agents.py b/agent_runtimes/specs/agents/agents.py index dc6f632f..b1668272 100644 --- a/agent_runtimes/specs/agents/agents.py +++ b/agent_runtimes/specs/agents/agents.py @@ -1362,6 +1362,349 @@ subagents=None, ) +EXAMPLE_SANDBOX_COLAB_AGENTSPEC_0_0_1 = Agentspec( + id="example-sandbox-colab", + version="0.0.1", + name="Example Sandbox Colab Agent", + description="Demonstration agent configured to run codemode code execution with the 'colab' sandbox variant.", + tags=["sandbox", "codemode", "colab"], + enabled=True, + model="bedrock:us.anthropic.claude-opus-4-8", + inference_provider=None, + mcp_servers=[MCP_SERVER_CATALOG["tavily"]], + skills=["events:0.0.1"], + tools=["runtime-echo:0.0.1"], + frontend_tools=["jupyter-notebook:0.0.1", "lexical-document:0.0.1"], + environment_name="ai-agents-env", + icon="package", + emoji="E", + color="#1F6FEB", + suggestions=[ + "Use execute_code to print('sandbox variant: colab')", + "Use execute_code to compute sum(i*i for i in range(20))", + "Use execute_code to load pandas and build a small DataFrame", + ], + welcome_message="You're connected to the colab sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcome_notebook=None, + welcome_document=None, + sandbox_variant="colab", + system_prompt="""You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.""", + system_prompt_codemode_addons="""Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.""", + goal=None, + protocol=None, + ui_extension=None, + trigger=None, + model_configuration=None, + mcp_server_tools=None, + guardrails=None, + evals=None, + codemode={"enabled": True, "token_reduction": "~80%", "speedup": "~1.5x"}, + output=None, + advanced=None, + authorization_policy=None, + notifications=None, + memory="ephemeral", + pre_hooks=None, + post_hooks=None, + tool_hooks=None, + parameters=None, + subagents=None, +) + +EXAMPLE_SANDBOX_DATALAYER_AGENTSPEC_0_0_1 = Agentspec( + id="example-sandbox-datalayer", + version="0.0.1", + name="Example Sandbox Datalayer Agent", + description="Demonstration agent configured to run codemode code execution with the 'datalayer' sandbox variant.", + tags=["sandbox", "codemode", "datalayer"], + enabled=True, + model="bedrock:us.anthropic.claude-opus-4-8", + inference_provider=None, + mcp_servers=[MCP_SERVER_CATALOG["tavily"]], + skills=["events:0.0.1"], + tools=["runtime-echo:0.0.1"], + frontend_tools=["jupyter-notebook:0.0.1", "lexical-document:0.0.1"], + environment_name="ai-agents-env", + icon="package", + emoji="D", + color="#1F6FEB", + suggestions=[ + "Use execute_code to print('sandbox variant: datalayer')", + "Use execute_code to compute sum(i*i for i in range(20))", + "Use execute_code to load pandas and build a small DataFrame", + ], + welcome_message="You're connected to the datalayer sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcome_notebook=None, + welcome_document=None, + sandbox_variant="datalayer", + system_prompt="""You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.""", + system_prompt_codemode_addons="""Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.""", + goal=None, + protocol=None, + ui_extension=None, + trigger=None, + model_configuration=None, + mcp_server_tools=None, + guardrails=None, + evals=None, + codemode={"enabled": True, "token_reduction": "~80%", "speedup": "~1.5x"}, + output=None, + advanced=None, + authorization_policy=None, + notifications=None, + memory="ephemeral", + pre_hooks=None, + post_hooks=None, + tool_hooks=None, + parameters=None, + subagents=None, +) + +EXAMPLE_SANDBOX_DOCKER_AGENTSPEC_0_0_1 = Agentspec( + id="example-sandbox-docker", + version="0.0.1", + name="Example Sandbox Docker Agent", + description="Demonstration agent configured to run codemode code execution with the 'docker' sandbox variant.", + tags=["sandbox", "codemode", "docker"], + enabled=True, + model="bedrock:us.anthropic.claude-opus-4-8", + inference_provider=None, + mcp_servers=[MCP_SERVER_CATALOG["tavily"]], + skills=["events:0.0.1"], + tools=["runtime-echo:0.0.1"], + frontend_tools=["jupyter-notebook:0.0.1", "lexical-document:0.0.1"], + environment_name="ai-agents-env", + icon="package", + emoji="C", + color="#1F6FEB", + suggestions=[ + "Use execute_code to print('sandbox variant: docker')", + "Use execute_code to compute sum(i*i for i in range(20))", + "Use execute_code to load pandas and build a small DataFrame", + ], + welcome_message="You're connected to the docker sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcome_notebook=None, + welcome_document=None, + sandbox_variant="docker", + system_prompt="""You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.""", + system_prompt_codemode_addons="""Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.""", + goal=None, + protocol=None, + ui_extension=None, + trigger=None, + model_configuration=None, + mcp_server_tools=None, + guardrails=None, + evals=None, + codemode={"enabled": True, "token_reduction": "~80%", "speedup": "~1.5x"}, + output=None, + advanced=None, + authorization_policy=None, + notifications=None, + memory="ephemeral", + pre_hooks=None, + post_hooks=None, + tool_hooks=None, + parameters=None, + subagents=None, +) + +EXAMPLE_SANDBOX_EVAL_AGENTSPEC_0_0_1 = Agentspec( + id="example-sandbox-eval", + version="0.0.1", + name="Example Sandbox Eval Agent", + description="Demonstration agent configured to run codemode code execution with the 'eval' sandbox variant.", + tags=["sandbox", "codemode", "eval"], + enabled=True, + model="bedrock:us.anthropic.claude-opus-4-8", + inference_provider=None, + mcp_servers=[MCP_SERVER_CATALOG["tavily"]], + skills=["events:0.0.1"], + tools=["runtime-echo:0.0.1"], + frontend_tools=["jupyter-notebook:0.0.1", "lexical-document:0.0.1"], + environment_name="ai-agents-env", + icon="package", + emoji="A", + color="#1F6FEB", + suggestions=[ + "Use execute_code to print('sandbox variant: eval')", + "Use execute_code to compute sum(i*i for i in range(20))", + "Use execute_code to load pandas and build a small DataFrame", + ], + welcome_message="You're connected to the eval sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcome_notebook=None, + welcome_document=None, + sandbox_variant="eval", + system_prompt="""You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.""", + system_prompt_codemode_addons="""Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.""", + goal=None, + protocol=None, + ui_extension=None, + trigger=None, + model_configuration=None, + mcp_server_tools=None, + guardrails=None, + evals=None, + codemode={"enabled": True, "token_reduction": "~80%", "speedup": "~1.5x"}, + output=None, + advanced=None, + authorization_policy=None, + notifications=None, + memory="ephemeral", + pre_hooks=None, + post_hooks=None, + tool_hooks=None, + parameters=None, + subagents=None, +) + +EXAMPLE_SANDBOX_JUPYTER_AGENTSPEC_0_0_1 = Agentspec( + id="example-sandbox-jupyter", + version="0.0.1", + name="Example Sandbox Jupyter Agent", + description="Demonstration agent configured to run codemode code execution with the 'jupyter' sandbox variant.", + tags=["sandbox", "codemode", "jupyter"], + enabled=True, + model="bedrock:us.anthropic.claude-opus-4-8", + inference_provider=None, + mcp_servers=[MCP_SERVER_CATALOG["tavily"]], + skills=["events:0.0.1"], + tools=["runtime-echo:0.0.1"], + frontend_tools=["jupyter-notebook:0.0.1", "lexical-document:0.0.1"], + environment_name="ai-agents-env", + icon="package", + emoji="B", + color="#1F6FEB", + suggestions=[ + "Use execute_code to print('sandbox variant: jupyter')", + "Use execute_code to compute sum(i*i for i in range(20))", + "Use execute_code to load pandas and build a small DataFrame", + ], + welcome_message="You're connected to the jupyter sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcome_notebook=None, + welcome_document=None, + sandbox_variant="jupyter", + system_prompt="""You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.""", + system_prompt_codemode_addons="""Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.""", + goal=None, + protocol=None, + ui_extension=None, + trigger=None, + model_configuration=None, + mcp_server_tools=None, + guardrails=None, + evals=None, + codemode={"enabled": True, "token_reduction": "~80%", "speedup": "~1.5x"}, + output=None, + advanced=None, + authorization_policy=None, + notifications=None, + memory="ephemeral", + pre_hooks=None, + post_hooks=None, + tool_hooks=None, + parameters=None, + subagents=None, +) + +EXAMPLE_SANDBOX_MODAL_AGENTSPEC_0_0_1 = Agentspec( + id="example-sandbox-modal", + version="0.0.1", + name="Example Sandbox Modal Agent", + description="Demonstration agent configured to run codemode code execution with the 'modal' sandbox variant.", + tags=["sandbox", "codemode", "modal"], + enabled=True, + model="bedrock:us.anthropic.claude-opus-4-8", + inference_provider=None, + mcp_servers=[MCP_SERVER_CATALOG["tavily"]], + skills=["events:0.0.1"], + tools=["runtime-echo:0.0.1"], + frontend_tools=["jupyter-notebook:0.0.1", "lexical-document:0.0.1"], + environment_name="ai-agents-env", + icon="package", + emoji="G", + color="#1F6FEB", + suggestions=[ + "Use execute_code to print('sandbox variant: modal')", + "Use execute_code to compute sum(i*i for i in range(20))", + "Use execute_code to load pandas and build a small DataFrame", + ], + welcome_message="You're connected to the modal sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcome_notebook=None, + welcome_document=None, + sandbox_variant="modal", + system_prompt="""You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.""", + system_prompt_codemode_addons="""Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.""", + goal=None, + protocol=None, + ui_extension=None, + trigger=None, + model_configuration=None, + mcp_server_tools=None, + guardrails=None, + evals=None, + codemode={"enabled": True, "token_reduction": "~80%", "speedup": "~1.5x"}, + output=None, + advanced=None, + authorization_policy=None, + notifications=None, + memory="ephemeral", + pre_hooks=None, + post_hooks=None, + tool_hooks=None, + parameters=None, + subagents=None, +) + +EXAMPLE_SANDBOX_MONTY_AGENTSPEC_0_0_1 = Agentspec( + id="example-sandbox-monty", + version="0.0.1", + name="Example Sandbox Monty Agent", + description="Demonstration agent configured to run codemode code execution with the 'monty' sandbox variant.", + tags=["sandbox", "codemode", "monty"], + enabled=True, + model="bedrock:us.anthropic.claude-opus-4-8", + inference_provider=None, + mcp_servers=[MCP_SERVER_CATALOG["tavily"]], + skills=["events:0.0.1"], + tools=["runtime-echo:0.0.1"], + frontend_tools=["jupyter-notebook:0.0.1", "lexical-document:0.0.1"], + environment_name="ai-agents-env", + icon="package", + emoji="F", + color="#1F6FEB", + suggestions=[ + "Use execute_code to print('sandbox variant: monty')", + "Use execute_code to compute sum(i*i for i in range(20))", + "Use execute_code to load pandas and build a small DataFrame", + ], + welcome_message="You're connected to the monty sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcome_notebook=None, + welcome_document=None, + sandbox_variant="monty", + system_prompt="""You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.""", + system_prompt_codemode_addons="""Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.""", + goal=None, + protocol=None, + ui_extension=None, + trigger=None, + model_configuration=None, + mcp_server_tools=None, + guardrails=None, + evals=None, + codemode={"enabled": True, "token_reduction": "~80%", "speedup": "~1.5x"}, + output=None, + advanced=None, + authorization_policy=None, + notifications=None, + memory="ephemeral", + pre_hooks=None, + post_hooks=None, + tool_hooks=None, + parameters=None, + subagents=None, +) + EXAMPLE_SHARED_STATE_AGENTSPEC_0_0_1 = Agentspec( id="example-shared-state", version="0.0.1", @@ -5692,6 +6035,13 @@ "example-otel": EXAMPLE_OTEL_AGENTSPEC_0_0_1, "example-output": EXAMPLE_OUTPUT_AGENTSPEC_0_0_1, "example-parameters": EXAMPLE_PARAMETERS_AGENTSPEC_0_0_1, + "example-sandbox-colab": EXAMPLE_SANDBOX_COLAB_AGENTSPEC_0_0_1, + "example-sandbox-datalayer": EXAMPLE_SANDBOX_DATALAYER_AGENTSPEC_0_0_1, + "example-sandbox-docker": EXAMPLE_SANDBOX_DOCKER_AGENTSPEC_0_0_1, + "example-sandbox-eval": EXAMPLE_SANDBOX_EVAL_AGENTSPEC_0_0_1, + "example-sandbox-jupyter": EXAMPLE_SANDBOX_JUPYTER_AGENTSPEC_0_0_1, + "example-sandbox-modal": EXAMPLE_SANDBOX_MODAL_AGENTSPEC_0_0_1, + "example-sandbox-monty": EXAMPLE_SANDBOX_MONTY_AGENTSPEC_0_0_1, "example-shared-state": EXAMPLE_SHARED_STATE_AGENTSPEC_0_0_1, "example-simple": EXAMPLE_SIMPLE_AGENTSPEC_0_0_1, "example-skills": EXAMPLE_SKILLS_AGENTSPEC_0_0_1, diff --git a/pyproject.toml b/pyproject.toml index 42e2db01..2c844aff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,13 +29,13 @@ dependencies = [ "a2ui-agent-sdk>=0.4.0", "ag-ui-protocol>=0.1.19", "agent-client-protocol>=0.7.1", - "agent-codemode>=0.1.0", - "agent-skills>=0.1.4", + "agent-codemode", + "agent-skills", "anthropic>=0.105.0,<1.0.0", "boto3>=1.42.96,<1.43.0", - "code-sandboxes>=0.0.14", + "code-sandboxes", "croniter>=6.0.0", - "datalayer-core>=1.1.48", + "datalayer-core", "dbos>=2.10.0,<2.23.0", "fasta2a @ git+https://github.com/datalayer-externals/fasta2a.git@feat/extensions#egg=fasta2a", "fastapi", diff --git a/scripts/codegen/generate_sandbox_agents.py b/scripts/codegen/generate_sandbox_agents.py new file mode 100644 index 00000000..fd77cf76 --- /dev/null +++ b/scripts/codegen/generate_sandbox_agents.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +# Copyright (c) 2025-2026 Datalayer, Inc. +# Distributed under the terms of the Modified BSD License. + +"""Generate example sandbox agent specs for each sandbox variant. + +This keeps sandbox demonstration specs aligned with supported sandbox variants +so examples can offer a consistent variant picker. +""" + +from __future__ import annotations + +import argparse +from pathlib import Path + + +VARIANTS: tuple[str, ...] = ( + "eval", + "jupyter", + "docker", + "datalayer", + "colab", + "monty", + "modal", +) + + +def _emoji_for_variant(variant: str) -> str: + mapping = { + "eval": "A", + "jupyter": "B", + "docker": "C", + "datalayer": "D", + "colab": "E", + "monty": "F", + "modal": "G", + } + return mapping.get(variant, "A") + + +def _content_for_variant(variant: str) -> str: + variant_title = variant.capitalize() + spec_id = f"example-sandbox-{variant}" + emoji = _emoji_for_variant(variant) + + return f"""# Copyright (c) 2025-2026 Datalayer, Inc. +# Distributed under the terms of the Modified BSD License. + +# Agent Specification: Sandbox Variant ({variant}) + +id: {spec_id} +version: 0.0.1 +name: Example Sandbox {variant_title} Agent +description: >- + Demonstration agent configured to run codemode code execution with the + '{variant}' sandbox variant. + +tags: + - sandbox + - codemode + - {variant} + +enabled: true +model: "bedrock:us.anthropic.claude-opus-4-8" + +sandbox_variant: {variant} +memory: ephemeral + +mcp_servers: + - tavily:0.0.1 + +skills: + - events:0.0.1 + +tools: + - runtime-echo:0.0.1 + +frontend_tools: + - jupyter-notebook:0.0.1 + - lexical-document:0.0.1 + +environment_name: ai-agents-env + +icon: package +emoji: "{emoji}" +color: "#1F6FEB" + +suggestions: + - "Use execute_code to print('sandbox variant: {variant}')" + - "Use execute_code to compute sum(i*i for i in range(20))" + - "Use execute_code to load pandas and build a small DataFrame" + +welcome_message: >- + You're connected to the {variant} sandbox variant demo. Ask me to run + Python code and I will use execute_code in codemode. + +system_prompt: >- + You are a sandbox-variant demonstration assistant. Prefer executing + Python code via execute_code for computations, data checks, and quick + experiments, then summarize results clearly. + +system_prompt_codemode_addons: >- + Always use execute_code when the user requests calculations, scripts, + DataFrame operations, package checks, or shell-style diagnostics. + +codemode: + enabled: true + token_reduction: "~80%" + speedup: "~1.5x" + +welcome_notebook: null +welcome_document: null +trigger: null +""" + + +def generate_specs(agents_dir: Path) -> int: + agents_dir.mkdir(parents=True, exist_ok=True) + count = 0 + for variant in VARIANTS: + path = agents_dir / f"example-sandbox-{variant}.yaml" + path.write_text(_content_for_variant(variant), encoding="utf-8") + count += 1 + return count + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Generate example sandbox agentspec files by sandbox variant." + ) + parser.add_argument( + "--agents-dir", + type=Path, + required=True, + help="Path to agentspecs/agents directory.", + ) + args = parser.parse_args() + + generated = generate_specs(args.agents_dir) + print(f"Generated {generated} sandbox variant agentspec files in {args.agents_dir}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/examples/AgentCodeSandboxesExample.tsx b/src/examples/AgentCodeSandboxesExample.tsx new file mode 100644 index 00000000..e55fe925 --- /dev/null +++ b/src/examples/AgentCodeSandboxesExample.tsx @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2025-2026 Datalayer, Inc. + * Distributed under the terms of the Modified BSD License. + */ + +/// + +import React, { useCallback, useMemo, useState } from 'react'; +import { Box } from '@datalayer/primer-addons'; +import { Button, Heading, Label, Spinner, Text } from '@primer/react'; +import { PackageIcon } from '@primer/octicons-react'; +import { useSimpleAuthStore } from '@datalayer/core/lib/views/otel'; +import { ThemedProvider } from './utils/themedProvider'; +import { AuthRequiredView, ErrorView } from './components'; +import { uniqueAgentId } from './utils/agentId'; +import { useExampleAgentRuntimesUrl } from './utils/useExampleAgentRuntimesUrl'; +import { Chat } from '../chat'; + +type SandboxVariant = + 'eval' | 'jupyter' | 'docker' | 'datalayer' | 'colab' | 'monty' | 'modal'; + +interface SandboxSpecOption { + variant: SandboxVariant; + specId: string; + title: string; + description: string; +} + +const SANDBOX_SPEC_OPTIONS: SandboxSpecOption[] = [ + { + variant: 'eval', + specId: 'example-sandbox-eval', + title: 'Eval Sandbox', + description: 'In-process Python execution for quick local iteration.', + }, + { + variant: 'jupyter', + specId: 'example-sandbox-jupyter', + title: 'Jupyter Sandbox', + description: 'Kernel-backed execution with notebook-compatible behavior.', + }, + { + variant: 'docker', + specId: 'example-sandbox-docker', + title: 'Docker Sandbox', + description: 'Containerized execution for stronger process isolation.', + }, + { + variant: 'datalayer', + specId: 'example-sandbox-datalayer', + title: 'Datalayer Sandbox', + description: 'Cloud sandbox runtime powered by Datalayer environments.', + }, + { + variant: 'colab', + specId: 'example-sandbox-colab', + title: 'Colab Sandbox', + description: 'Google Colab runtime bridge for remote notebook kernels.', + }, + { + variant: 'monty', + specId: 'example-sandbox-monty', + title: 'Monty Sandbox', + description: 'Secure in-process interpreter focused on safe snippets.', + }, + { + variant: 'modal', + specId: 'example-sandbox-modal', + title: 'Modal Sandbox', + description: 'Modal cloud sandbox for scalable remote code execution.', + }, +]; + +const AgentCodeSandboxesInner: React.FC<{ onLogout: () => void }> = ({ + onLogout, +}) => { + const { token } = useSimpleAuthStore(); + const baseUrl = useExampleAgentRuntimesUrl(); + + const [selectedSpecId, setSelectedSpecId] = useState( + SANDBOX_SPEC_OPTIONS[0].specId, + ); + const [agentId, setAgentId] = useState(null); + const [activeSpecId, setActiveSpecId] = useState(null); + const [isLaunching, setIsLaunching] = useState(false); + const [error, setError] = useState(null); + + const selectedOption = useMemo( + () => + SANDBOX_SPEC_OPTIONS.find(option => option.specId === selectedSpecId) ?? + SANDBOX_SPEC_OPTIONS[0], + [selectedSpecId], + ); + + const activeOption = useMemo( + () => + SANDBOX_SPEC_OPTIONS.find(option => option.specId === activeSpecId) ?? + selectedOption, + [activeSpecId, selectedOption], + ); + + const authFetch = useCallback( + (url: string, opts: RequestInit = {}) => + fetch(url, { + ...opts, + headers: { + 'Content-Type': 'application/json', + ...(token ? { Authorization: `Bearer ${token}` } : {}), + ...(opts.headers ?? {}), + }, + }), + [token], + ); + + const launchAgent = useCallback(async () => { + setIsLaunching(true); + setError(null); + + try { + if (agentId) { + await authFetch(`${baseUrl}/api/v1/agents/${agentId}`, { + method: 'DELETE', + }).catch(() => { + // Ignore teardown errors while switching specs. + }); + } + + const agentName = uniqueAgentId(`code-sandbox-${selectedOption.variant}`); + + const response = await authFetch(`${baseUrl}/api/v1/agents`, { + method: 'POST', + body: JSON.stringify({ + name: agentName, + description: `Code sandbox demo (${selectedOption.variant})`, + agent_library: 'pydantic-ai', + transport: 'vercel-ai', + agent_spec_id: selectedOption.specId, + enable_codemode: true, + enable_skills: true, + tools: [], + }), + }); + + if (!response.ok) { + const contentType = response.headers.get('content-type') || ''; + let detail = ''; + if (contentType.includes('application/json')) { + const payload = await response.json().catch(() => null); + detail = + (typeof payload?.detail === 'string' && payload.detail) || + (typeof payload?.message === 'string' && payload.message) || + ''; + } else { + detail = await response.text().catch(() => ''); + } + + throw new Error( + detail || `Failed to create agent (${response.status})`, + ); + } + + const payload = await response.json(); + const createdAgentId = payload?.id || agentName; + + setAgentId(createdAgentId); + setActiveSpecId(selectedOption.specId); + } catch (e) { + setError(e instanceof Error ? e.message : 'Failed to launch agent'); + } finally { + setIsLaunching(false); + } + }, [agentId, authFetch, baseUrl, selectedOption]); + + if (!agentId) { + return ( + + + SANDBOX VARIANT DEMO + + + Agent Code Sandboxes + + + Choose a sandbox variant-backed spec, launch the agent, then run code + from chat to compare behavior across sandboxes. + + + + + + + + + + Sandbox Variant + + + {selectedOption.description} + + + + + + {error && } + + ); + } + + return ( + } + showHeader={true} + showModelSelector={true} + showToolsMenu={true} + showSkillsMenu={true} + showTokenUsage={true} + showInformation={true} + autoFocus + height="100vh" + historyEndpoint={`${baseUrl}/api/v1/history`} + suggestions={[ + { + title: 'Identify sandbox variant', + message: + 'Use execute_code to print(os.getenv("DATALAYER_CODE_SANDBOX_VARIANT")) and summarize the result.', + }, + { + title: 'Run numeric workload', + message: + 'Use execute_code to compute sum(i*i for i in range(10000)) and report timing and result.', + }, + { + title: 'Check package availability', + message: + 'Use execute_code to import pandas and print(pandas.__version__).', + }, + ]} + submitOnSuggestionClick + /> + ); +}; + +const AgentCodeSandboxesExample: React.FC = () => { + const { token, clearAuth } = useSimpleAuthStore(); + + const handleLogout = useCallback(() => { + clearAuth(); + }, [clearAuth]); + + if (!token) { + return ; + } + + return ( + + + + ); +}; + +export default AgentCodeSandboxesExample; diff --git a/src/examples/example-selector.ts b/src/examples/example-selector.ts index e17ea90d..ba75af83 100644 --- a/src/examples/example-selector.ts +++ b/src/examples/example-selector.ts @@ -180,6 +180,12 @@ export const EXAMPLE_ENTRIES: ExampleEntry[] = [ () => import('./AgentCodemodeExample'), 'Code mode execution and tool orchestration example.', ), + makeEntry( + 'AgentCodeSandboxesExample', + () => import('./AgentCodeSandboxesExample'), + 'Launch sandbox-variant agent specs and compare code execution across backends.', + ['example', 'agent', 'sandbox', 'codemode'], + ), makeEntry( 'AgentEvalsExample', () => import('./AgentEvalsExample'), diff --git a/src/examples/index.ts b/src/examples/index.ts index 8a8419b5..fed30075 100644 --- a/src/examples/index.ts +++ b/src/examples/index.ts @@ -23,6 +23,7 @@ export { default as CustomExample } from './ChatCustomExample'; export { default as NotebookCollaborationExample } from './NotebookCollaborationExample'; export { default as AgentCheckpointsExample } from './AgentCheckpointsExample'; export { default as AgentCodemodeExample } from './AgentCodemodeExample'; +export { default as AgentCodeSandboxesExample } from './AgentCodeSandboxesExample'; export { default as AgentEvalsExample } from './AgentEvalsExample'; export { default as AgentGuardrailsExample } from './AgentGuardrailsExample'; export { default as AgentHooksExample } from './AgentHooksExample'; diff --git a/src/specs/agents/agents.ts b/src/specs/agents/agents.ts index adc6a76e..3ef5409d 100644 --- a/src/specs/agents/agents.ts +++ b/src/specs/agents/agents.ts @@ -1630,6 +1630,398 @@ export const EXAMPLE_PARAMETERS_AGENTSPEC_0_0_1: Agentspec = { subagents: undefined, }; +export const EXAMPLE_SANDBOX_COLAB_AGENTSPEC_0_0_1: Agentspec = { + id: 'example-sandbox-colab', + version: '0.0.1', + name: 'Example Sandbox Colab Agent', + description: `Demonstration agent configured to run codemode code execution with the 'colab' sandbox variant.`, + tags: ['sandbox', 'codemode', 'colab'], + enabled: true, + model: 'bedrock:us.anthropic.claude-opus-4-8', + mcpServers: [MCP_SERVER_MAP['tavily:0.0.1']], + skills: [ + SKILL_MAP['events:0.0.1'] + ? toAgentSkillSpec(SKILL_MAP['events:0.0.1']) + : undefined, + ].filter(Boolean) as SkillSpec[], + tools: [TOOL_MAP['runtime-echo:0.0.1']], + frontendTools: [ + FRONTEND_TOOL_MAP['jupyter-notebook:0.0.1'], + FRONTEND_TOOL_MAP['lexical-document:0.0.1'], + ], + environmentName: 'ai-agents-env', + icon: 'package', + emoji: 'E', + color: '#1F6FEB', + suggestions: [ + "Use execute_code to print('sandbox variant: colab')", + 'Use execute_code to compute sum(i*i for i in range(20))', + 'Use execute_code to load pandas and build a small DataFrame', + ], + welcomeMessage: + "You're connected to the colab sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcomeNotebook: undefined, + welcomeDocument: undefined, + sandboxVariant: 'colab', + systemPrompt: `You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.`, + systemPromptCodemodeAddons: `Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.`, + goal: undefined, + protocol: undefined, + uiExtension: undefined, + trigger: undefined, + modelConfig: undefined, + mcpServerTools: undefined, + guardrails: undefined, + evals: undefined, + codemode: { enabled: true, token_reduction: '~80%', speedup: '~1.5x' }, + output: undefined, + advanced: undefined, + authorizationPolicy: undefined, + notifications: undefined, + memory: 'ephemeral', + preHooks: undefined, + postHooks: undefined, + toolHooks: undefined, + parameters: undefined, + subagents: undefined, +}; + +export const EXAMPLE_SANDBOX_DATALAYER_AGENTSPEC_0_0_1: Agentspec = { + id: 'example-sandbox-datalayer', + version: '0.0.1', + name: 'Example Sandbox Datalayer Agent', + description: `Demonstration agent configured to run codemode code execution with the 'datalayer' sandbox variant.`, + tags: ['sandbox', 'codemode', 'datalayer'], + enabled: true, + model: 'bedrock:us.anthropic.claude-opus-4-8', + mcpServers: [MCP_SERVER_MAP['tavily:0.0.1']], + skills: [ + SKILL_MAP['events:0.0.1'] + ? toAgentSkillSpec(SKILL_MAP['events:0.0.1']) + : undefined, + ].filter(Boolean) as SkillSpec[], + tools: [TOOL_MAP['runtime-echo:0.0.1']], + frontendTools: [ + FRONTEND_TOOL_MAP['jupyter-notebook:0.0.1'], + FRONTEND_TOOL_MAP['lexical-document:0.0.1'], + ], + environmentName: 'ai-agents-env', + icon: 'package', + emoji: 'D', + color: '#1F6FEB', + suggestions: [ + "Use execute_code to print('sandbox variant: datalayer')", + 'Use execute_code to compute sum(i*i for i in range(20))', + 'Use execute_code to load pandas and build a small DataFrame', + ], + welcomeMessage: + "You're connected to the datalayer sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcomeNotebook: undefined, + welcomeDocument: undefined, + sandboxVariant: 'datalayer', + systemPrompt: `You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.`, + systemPromptCodemodeAddons: `Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.`, + goal: undefined, + protocol: undefined, + uiExtension: undefined, + trigger: undefined, + modelConfig: undefined, + mcpServerTools: undefined, + guardrails: undefined, + evals: undefined, + codemode: { enabled: true, token_reduction: '~80%', speedup: '~1.5x' }, + output: undefined, + advanced: undefined, + authorizationPolicy: undefined, + notifications: undefined, + memory: 'ephemeral', + preHooks: undefined, + postHooks: undefined, + toolHooks: undefined, + parameters: undefined, + subagents: undefined, +}; + +export const EXAMPLE_SANDBOX_DOCKER_AGENTSPEC_0_0_1: Agentspec = { + id: 'example-sandbox-docker', + version: '0.0.1', + name: 'Example Sandbox Docker Agent', + description: `Demonstration agent configured to run codemode code execution with the 'docker' sandbox variant.`, + tags: ['sandbox', 'codemode', 'docker'], + enabled: true, + model: 'bedrock:us.anthropic.claude-opus-4-8', + mcpServers: [MCP_SERVER_MAP['tavily:0.0.1']], + skills: [ + SKILL_MAP['events:0.0.1'] + ? toAgentSkillSpec(SKILL_MAP['events:0.0.1']) + : undefined, + ].filter(Boolean) as SkillSpec[], + tools: [TOOL_MAP['runtime-echo:0.0.1']], + frontendTools: [ + FRONTEND_TOOL_MAP['jupyter-notebook:0.0.1'], + FRONTEND_TOOL_MAP['lexical-document:0.0.1'], + ], + environmentName: 'ai-agents-env', + icon: 'package', + emoji: 'C', + color: '#1F6FEB', + suggestions: [ + "Use execute_code to print('sandbox variant: docker')", + 'Use execute_code to compute sum(i*i for i in range(20))', + 'Use execute_code to load pandas and build a small DataFrame', + ], + welcomeMessage: + "You're connected to the docker sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcomeNotebook: undefined, + welcomeDocument: undefined, + sandboxVariant: 'docker', + systemPrompt: `You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.`, + systemPromptCodemodeAddons: `Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.`, + goal: undefined, + protocol: undefined, + uiExtension: undefined, + trigger: undefined, + modelConfig: undefined, + mcpServerTools: undefined, + guardrails: undefined, + evals: undefined, + codemode: { enabled: true, token_reduction: '~80%', speedup: '~1.5x' }, + output: undefined, + advanced: undefined, + authorizationPolicy: undefined, + notifications: undefined, + memory: 'ephemeral', + preHooks: undefined, + postHooks: undefined, + toolHooks: undefined, + parameters: undefined, + subagents: undefined, +}; + +export const EXAMPLE_SANDBOX_EVAL_AGENTSPEC_0_0_1: Agentspec = { + id: 'example-sandbox-eval', + version: '0.0.1', + name: 'Example Sandbox Eval Agent', + description: `Demonstration agent configured to run codemode code execution with the 'eval' sandbox variant.`, + tags: ['sandbox', 'codemode', 'eval'], + enabled: true, + model: 'bedrock:us.anthropic.claude-opus-4-8', + mcpServers: [MCP_SERVER_MAP['tavily:0.0.1']], + skills: [ + SKILL_MAP['events:0.0.1'] + ? toAgentSkillSpec(SKILL_MAP['events:0.0.1']) + : undefined, + ].filter(Boolean) as SkillSpec[], + tools: [TOOL_MAP['runtime-echo:0.0.1']], + frontendTools: [ + FRONTEND_TOOL_MAP['jupyter-notebook:0.0.1'], + FRONTEND_TOOL_MAP['lexical-document:0.0.1'], + ], + environmentName: 'ai-agents-env', + icon: 'package', + emoji: 'A', + color: '#1F6FEB', + suggestions: [ + "Use execute_code to print('sandbox variant: eval')", + 'Use execute_code to compute sum(i*i for i in range(20))', + 'Use execute_code to load pandas and build a small DataFrame', + ], + welcomeMessage: + "You're connected to the eval sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcomeNotebook: undefined, + welcomeDocument: undefined, + sandboxVariant: 'eval', + systemPrompt: `You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.`, + systemPromptCodemodeAddons: `Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.`, + goal: undefined, + protocol: undefined, + uiExtension: undefined, + trigger: undefined, + modelConfig: undefined, + mcpServerTools: undefined, + guardrails: undefined, + evals: undefined, + codemode: { enabled: true, token_reduction: '~80%', speedup: '~1.5x' }, + output: undefined, + advanced: undefined, + authorizationPolicy: undefined, + notifications: undefined, + memory: 'ephemeral', + preHooks: undefined, + postHooks: undefined, + toolHooks: undefined, + parameters: undefined, + subagents: undefined, +}; + +export const EXAMPLE_SANDBOX_JUPYTER_AGENTSPEC_0_0_1: Agentspec = { + id: 'example-sandbox-jupyter', + version: '0.0.1', + name: 'Example Sandbox Jupyter Agent', + description: `Demonstration agent configured to run codemode code execution with the 'jupyter' sandbox variant.`, + tags: ['sandbox', 'codemode', 'jupyter'], + enabled: true, + model: 'bedrock:us.anthropic.claude-opus-4-8', + mcpServers: [MCP_SERVER_MAP['tavily:0.0.1']], + skills: [ + SKILL_MAP['events:0.0.1'] + ? toAgentSkillSpec(SKILL_MAP['events:0.0.1']) + : undefined, + ].filter(Boolean) as SkillSpec[], + tools: [TOOL_MAP['runtime-echo:0.0.1']], + frontendTools: [ + FRONTEND_TOOL_MAP['jupyter-notebook:0.0.1'], + FRONTEND_TOOL_MAP['lexical-document:0.0.1'], + ], + environmentName: 'ai-agents-env', + icon: 'package', + emoji: 'B', + color: '#1F6FEB', + suggestions: [ + "Use execute_code to print('sandbox variant: jupyter')", + 'Use execute_code to compute sum(i*i for i in range(20))', + 'Use execute_code to load pandas and build a small DataFrame', + ], + welcomeMessage: + "You're connected to the jupyter sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcomeNotebook: undefined, + welcomeDocument: undefined, + sandboxVariant: 'jupyter', + systemPrompt: `You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.`, + systemPromptCodemodeAddons: `Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.`, + goal: undefined, + protocol: undefined, + uiExtension: undefined, + trigger: undefined, + modelConfig: undefined, + mcpServerTools: undefined, + guardrails: undefined, + evals: undefined, + codemode: { enabled: true, token_reduction: '~80%', speedup: '~1.5x' }, + output: undefined, + advanced: undefined, + authorizationPolicy: undefined, + notifications: undefined, + memory: 'ephemeral', + preHooks: undefined, + postHooks: undefined, + toolHooks: undefined, + parameters: undefined, + subagents: undefined, +}; + +export const EXAMPLE_SANDBOX_MODAL_AGENTSPEC_0_0_1: Agentspec = { + id: 'example-sandbox-modal', + version: '0.0.1', + name: 'Example Sandbox Modal Agent', + description: `Demonstration agent configured to run codemode code execution with the 'modal' sandbox variant.`, + tags: ['sandbox', 'codemode', 'modal'], + enabled: true, + model: 'bedrock:us.anthropic.claude-opus-4-8', + mcpServers: [MCP_SERVER_MAP['tavily:0.0.1']], + skills: [ + SKILL_MAP['events:0.0.1'] + ? toAgentSkillSpec(SKILL_MAP['events:0.0.1']) + : undefined, + ].filter(Boolean) as SkillSpec[], + tools: [TOOL_MAP['runtime-echo:0.0.1']], + frontendTools: [ + FRONTEND_TOOL_MAP['jupyter-notebook:0.0.1'], + FRONTEND_TOOL_MAP['lexical-document:0.0.1'], + ], + environmentName: 'ai-agents-env', + icon: 'package', + emoji: 'G', + color: '#1F6FEB', + suggestions: [ + "Use execute_code to print('sandbox variant: modal')", + 'Use execute_code to compute sum(i*i for i in range(20))', + 'Use execute_code to load pandas and build a small DataFrame', + ], + welcomeMessage: + "You're connected to the modal sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcomeNotebook: undefined, + welcomeDocument: undefined, + sandboxVariant: 'modal', + systemPrompt: `You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.`, + systemPromptCodemodeAddons: `Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.`, + goal: undefined, + protocol: undefined, + uiExtension: undefined, + trigger: undefined, + modelConfig: undefined, + mcpServerTools: undefined, + guardrails: undefined, + evals: undefined, + codemode: { enabled: true, token_reduction: '~80%', speedup: '~1.5x' }, + output: undefined, + advanced: undefined, + authorizationPolicy: undefined, + notifications: undefined, + memory: 'ephemeral', + preHooks: undefined, + postHooks: undefined, + toolHooks: undefined, + parameters: undefined, + subagents: undefined, +}; + +export const EXAMPLE_SANDBOX_MONTY_AGENTSPEC_0_0_1: Agentspec = { + id: 'example-sandbox-monty', + version: '0.0.1', + name: 'Example Sandbox Monty Agent', + description: `Demonstration agent configured to run codemode code execution with the 'monty' sandbox variant.`, + tags: ['sandbox', 'codemode', 'monty'], + enabled: true, + model: 'bedrock:us.anthropic.claude-opus-4-8', + mcpServers: [MCP_SERVER_MAP['tavily:0.0.1']], + skills: [ + SKILL_MAP['events:0.0.1'] + ? toAgentSkillSpec(SKILL_MAP['events:0.0.1']) + : undefined, + ].filter(Boolean) as SkillSpec[], + tools: [TOOL_MAP['runtime-echo:0.0.1']], + frontendTools: [ + FRONTEND_TOOL_MAP['jupyter-notebook:0.0.1'], + FRONTEND_TOOL_MAP['lexical-document:0.0.1'], + ], + environmentName: 'ai-agents-env', + icon: 'package', + emoji: 'F', + color: '#1F6FEB', + suggestions: [ + "Use execute_code to print('sandbox variant: monty')", + 'Use execute_code to compute sum(i*i for i in range(20))', + 'Use execute_code to load pandas and build a small DataFrame', + ], + welcomeMessage: + "You're connected to the monty sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcomeNotebook: undefined, + welcomeDocument: undefined, + sandboxVariant: 'monty', + systemPrompt: `You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.`, + systemPromptCodemodeAddons: `Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.`, + goal: undefined, + protocol: undefined, + uiExtension: undefined, + trigger: undefined, + modelConfig: undefined, + mcpServerTools: undefined, + guardrails: undefined, + evals: undefined, + codemode: { enabled: true, token_reduction: '~80%', speedup: '~1.5x' }, + output: undefined, + advanced: undefined, + authorizationPolicy: undefined, + notifications: undefined, + memory: 'ephemeral', + preHooks: undefined, + postHooks: undefined, + toolHooks: undefined, + parameters: undefined, + subagents: undefined, +}; + export const EXAMPLE_SHARED_STATE_AGENTSPEC_0_0_1: Agentspec = { id: 'example-shared-state', version: '0.0.1', @@ -6589,6 +6981,13 @@ export const AGENTSPECS: Record = { 'example-otel': EXAMPLE_OTEL_AGENTSPEC_0_0_1, 'example-output': EXAMPLE_OUTPUT_AGENTSPEC_0_0_1, 'example-parameters': EXAMPLE_PARAMETERS_AGENTSPEC_0_0_1, + 'example-sandbox-colab': EXAMPLE_SANDBOX_COLAB_AGENTSPEC_0_0_1, + 'example-sandbox-datalayer': EXAMPLE_SANDBOX_DATALAYER_AGENTSPEC_0_0_1, + 'example-sandbox-docker': EXAMPLE_SANDBOX_DOCKER_AGENTSPEC_0_0_1, + 'example-sandbox-eval': EXAMPLE_SANDBOX_EVAL_AGENTSPEC_0_0_1, + 'example-sandbox-jupyter': EXAMPLE_SANDBOX_JUPYTER_AGENTSPEC_0_0_1, + 'example-sandbox-modal': EXAMPLE_SANDBOX_MODAL_AGENTSPEC_0_0_1, + 'example-sandbox-monty': EXAMPLE_SANDBOX_MONTY_AGENTSPEC_0_0_1, 'example-shared-state': EXAMPLE_SHARED_STATE_AGENTSPEC_0_0_1, 'example-simple': EXAMPLE_SIMPLE_AGENTSPEC_0_0_1, 'example-skills': EXAMPLE_SKILLS_AGENTSPEC_0_0_1, From 6f0dd54b727dd7a96448391f1167a728f72b65bb Mon Sep 17 00:00:00 2001 From: Eric Charles Date: Fri, 24 Jul 2026 14:28:53 +0200 Subject: [PATCH 3/7] feat: propagate sandbox streaming support --- agent_runtimes/app.py | 5 + agent_runtimes/decorators/datalayer.py | 2 +- agent_runtimes/integrations/codemode.py | 5 + agent_runtimes/routes/sandbox.py | 42 ++++++++ agent_runtimes/services/agent_factory.py | 4 + .../services/code_sandbox_manager.py | 39 ++++++-- agent_runtimes/specs/agents/agents.py | 50 ++++++++++ .../tests/test_sandbox_interrupt.py | 78 +++++++++++++++ .../tests/test_sandbox_route_streaming.py | 95 +++++++++++++++++++ docs/docs/programmatic-tools/index.mdx | 3 + scripts/codegen/generate_sandbox_agents.py | 3 +- src/examples/AgentCodeSandboxesExample.tsx | 19 +++- src/specs/agents/agents.ts | 57 +++++++++++ src/types/agentspecs.ts | 2 +- src/types/config.ts | 2 +- 15 files changed, 390 insertions(+), 16 deletions(-) create mode 100644 agent_runtimes/tests/test_sandbox_route_streaming.py diff --git a/agent_runtimes/app.py b/agent_runtimes/app.py index 8bc08a08..f60f136e 100644 --- a/agent_runtimes/app.py +++ b/agent_runtimes/app.py @@ -701,6 +701,11 @@ def rebuild_codemode(new_servers: list[str | dict[str, str]]) -> Any: skills_path=skills_folder_path or str((repo_root / "skills").resolve()), allow_direct_tool_calls=False, + **( + {} + if os.getenv("AGENT_RUNTIMES_SANDBOX_GPU") is None + else {"sandbox_gpu": os.getenv("AGENT_RUNTIMES_SANDBOX_GPU")} + ), **( {} if mcp_proxy_url is None diff --git a/agent_runtimes/decorators/datalayer.py b/agent_runtimes/decorators/datalayer.py index 11abf164..44f010f0 100644 --- a/agent_runtimes/decorators/datalayer.py +++ b/agent_runtimes/decorators/datalayer.py @@ -183,7 +183,7 @@ def wrapper(*args: Any, **kwargs: Any) -> Any: # print("function_source:", function_source) # print("function_call:", function_call) - client = AgentClient(token=token) # Resolves token from param/env/keyring + client = AgentClient(api_key=token) # Resolves token from param/env/keyring with client.create_runtime( name=runtime_name_decorated, snapshot_name=snapshot_name_decorated, diff --git a/agent_runtimes/integrations/codemode.py b/agent_runtimes/integrations/codemode.py index ec6566cf..255aa9d2 100644 --- a/agent_runtimes/integrations/codemode.py +++ b/agent_runtimes/integrations/codemode.py @@ -55,6 +55,7 @@ def __init__( mcp_manager: Optional[MCPManager] = None, skills_path: str = "./skills", sandbox_variant: str = "eval", + sandbox_gpu: str | None = None, ): """ Initialize the integration. @@ -63,10 +64,13 @@ def __init__( mcp_manager: Optional MCPManager from agent-runtimes. skills_path: Directory for skill storage. sandbox_variant: Sandbox type for code execution. + sandbox_gpu: Optional GPU flavor / accelerator for supported + sandbox variants. """ self.mcp_manager = mcp_manager or get_mcp_manager() self.skills_path = skills_path self.sandbox_variant = sandbox_variant + self.sandbox_gpu = sandbox_gpu # Lazy imports for optional dependencies self._registry = None @@ -124,6 +128,7 @@ async def setup(self) -> None: config = CodeModeConfig( skills_path=self.skills_path, sandbox_variant=self.sandbox_variant, + **({} if self.sandbox_gpu is None else {"sandbox_gpu": self.sandbox_gpu}), **({} if mcp_proxy_url is None else {"mcp_proxy_url": mcp_proxy_url}), ) self._executor = CodeModeExecutor(self._registry, config) diff --git a/agent_runtimes/routes/sandbox.py b/agent_runtimes/routes/sandbox.py index 8461c251..5d864764 100644 --- a/agent_runtimes/routes/sandbox.py +++ b/agent_runtimes/routes/sandbox.py @@ -44,6 +44,13 @@ class SandboxExecuteRequest(BaseModel): timeout: Optional[float] = Field( default=None, description="Execution timeout in seconds." ) + stream: bool = Field( + default=False, + description=( + "When true, execute via sandbox streaming API and aggregate streamed " + "events into the response." + ), + ) class SandboxExecuteResponse(BaseModel): @@ -85,6 +92,41 @@ async def execute_sandbox_code( client = CodeSandboxClient(sandbox) try: + if request.stream and hasattr(client, "execute_code_streaming_async"): + stdout_lines: list[str] = [] + stderr_lines: list[str] = [] + results: list[str] = [] + error: str | None = None + + async for event in client.execute_code_streaming_async( + request.code, + language=request.language, + timeout=request.timeout, + ): + if hasattr(event, "line"): + line = getattr(event, "line", "") or "" + if bool(getattr(event, "error", False)): + stderr_lines.append(line) + else: + stdout_lines.append(line) + elif hasattr(event, "data"): + data = getattr(event, "data", {}) or {} + text = data.get("text/plain") + if text is not None: + results.append(str(text)) + elif hasattr(event, "name") and hasattr(event, "value"): + error = f"{getattr(event, 'name', 'Error')}: {getattr(event, 'value', '')}" + + return SandboxExecuteResponse( + success=error is None, + execution_ok=True, + stdout="\n".join(stdout_lines), + stderr="\n".join(stderr_lines), + results=results, + error=error, + variant=str(client.variant) if client.variant else None, + ) + outcome = await client.execute_code_async( request.code, language=request.language, diff --git a/agent_runtimes/services/agent_factory.py b/agent_runtimes/services/agent_factory.py index a0c59230..069c233f 100644 --- a/agent_runtimes/services/agent_factory.py +++ b/agent_runtimes/services/agent_factory.py @@ -411,6 +411,10 @@ def replace(match: re.Match[str]) -> str: if effective_variant: config_kwargs["sandbox_variant"] = effective_variant + sandbox_gpu = os.getenv("AGENT_RUNTIMES_SANDBOX_GPU") + if sandbox_gpu: + config_kwargs["sandbox_gpu"] = sandbox_gpu + # When discovery tools are disabled, treat this as sandbox-only mode # and prevent the executor from materializing ``generated/`` bindings # or extending the sandbox ``sys.path``. diff --git a/agent_runtimes/services/code_sandbox_manager.py b/agent_runtimes/services/code_sandbox_manager.py index 1568818d..74a1faf2 100644 --- a/agent_runtimes/services/code_sandbox_manager.py +++ b/agent_runtimes/services/code_sandbox_manager.py @@ -237,9 +237,10 @@ async def run_code_streaming_async( envs: Optional[dict[str, str]] = None, timeout: Optional[float] = None, ) -> AsyncIterator[Any]: - return await self._sandbox().run_code_streaming_async( + async for item in self._sandbox().run_code_streaming_async( code, language=language, context=context, envs=envs, timeout=timeout - ) + ): + yield item def create_context(self, name: Optional[str] = None) -> Any: return self._sandbox().create_context(name=name) @@ -744,11 +745,17 @@ def _create_sandbox(self, variant: SandboxVariant | None = None) -> Sandbox: ValueError: If configuration is invalid. """ effective_variant = variant or self._config.variant + try: + from code_sandboxes import Sandbox as CodeSandbox + except (ImportError, AttributeError): + CodeSandbox = None if effective_variant == "eval": - from code_sandboxes.eval_sandbox import EvalSandbox + if CodeSandbox is None: + from code_sandboxes.eval_sandbox import EvalSandbox - return EvalSandbox() + return EvalSandbox() + return CodeSandbox.create(variant="eval") elif effective_variant == "jupyter": # In sidecar mode, companion must provide a concrete Jupyter URL. @@ -761,21 +768,33 @@ def _create_sandbox(self, variant: SandboxVariant | None = None) -> Sandbox: "Jupyter sidecar mode requires jupyter_url before sandbox start" ) - from code_sandboxes.jupyter_sandbox import JupyterSandbox - if self._config.jupyter_url: - return JupyterSandbox( + if CodeSandbox is None: + from code_sandboxes.jupyter_sandbox import JupyterSandbox + + return JupyterSandbox( + server_url=self._config.jupyter_url, + token=self._config.jupyter_token, + ) + return CodeSandbox.create( + variant="jupyter", server_url=self._config.jupyter_url, token=self._config.jupyter_token, ) # No external URL configured: let code_sandboxes start its own # local Jupyter server on a free port. - return JupyterSandbox() + if CodeSandbox is None: + from code_sandboxes.jupyter_sandbox import JupyterSandbox - else: - from code_sandboxes import Sandbox as CodeSandbox + return JupyterSandbox() + return CodeSandbox.create(variant="jupyter") + else: + if CodeSandbox is None: + raise ImportError( + "code_sandboxes.Sandbox is required for non-jupyter/eval variants" + ) return CodeSandbox.create(variant=effective_variant) def stop(self) -> None: diff --git a/agent_runtimes/specs/agents/agents.py b/agent_runtimes/specs/agents/agents.py index b1668272..438ce12e 100644 --- a/agent_runtimes/specs/agents/agents.py +++ b/agent_runtimes/specs/agents/agents.py @@ -1607,6 +1607,55 @@ subagents=None, ) +EXAMPLE_SANDBOX_KAGGLE_AGENTSPEC_0_0_1 = Agentspec( + id="example-sandbox-kaggle", + version="0.0.1", + name="Example Sandbox Kaggle Agent", + description="Demonstration agent configured to run codemode code execution with the 'kaggle' sandbox variant.", + tags=["sandbox", "codemode", "kaggle"], + enabled=True, + model="bedrock:us.anthropic.claude-opus-4-8", + inference_provider=None, + mcp_servers=[MCP_SERVER_CATALOG["tavily"]], + skills=["events:0.0.1"], + tools=["runtime-echo:0.0.1"], + frontend_tools=["jupyter-notebook:0.0.1", "lexical-document:0.0.1"], + environment_name="ai-agents-env", + icon="package", + emoji="H", + color="#1F6FEB", + suggestions=[ + "Use execute_code to print('sandbox variant: kaggle')", + "Use execute_code to compute sum(i*i for i in range(20))", + "Use execute_code to load pandas and build a small DataFrame", + ], + welcome_message="You're connected to the kaggle sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcome_notebook=None, + welcome_document=None, + sandbox_variant="kaggle", + system_prompt="""You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.""", + system_prompt_codemode_addons="""Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.""", + goal=None, + protocol=None, + ui_extension=None, + trigger=None, + model_configuration=None, + mcp_server_tools=None, + guardrails=None, + evals=None, + codemode={"enabled": True, "token_reduction": "~80%", "speedup": "~1.5x"}, + output=None, + advanced=None, + authorization_policy=None, + notifications=None, + memory="ephemeral", + pre_hooks=None, + post_hooks=None, + tool_hooks=None, + parameters=None, + subagents=None, +) + EXAMPLE_SANDBOX_MODAL_AGENTSPEC_0_0_1 = Agentspec( id="example-sandbox-modal", version="0.0.1", @@ -6040,6 +6089,7 @@ "example-sandbox-docker": EXAMPLE_SANDBOX_DOCKER_AGENTSPEC_0_0_1, "example-sandbox-eval": EXAMPLE_SANDBOX_EVAL_AGENTSPEC_0_0_1, "example-sandbox-jupyter": EXAMPLE_SANDBOX_JUPYTER_AGENTSPEC_0_0_1, + "example-sandbox-kaggle": EXAMPLE_SANDBOX_KAGGLE_AGENTSPEC_0_0_1, "example-sandbox-modal": EXAMPLE_SANDBOX_MODAL_AGENTSPEC_0_0_1, "example-sandbox-monty": EXAMPLE_SANDBOX_MONTY_AGENTSPEC_0_0_1, "example-shared-state": EXAMPLE_SHARED_STATE_AGENTSPEC_0_0_1, diff --git a/agent_runtimes/tests/test_sandbox_interrupt.py b/agent_runtimes/tests/test_sandbox_interrupt.py index e7341af1..3dad9ee4 100644 --- a/agent_runtimes/tests/test_sandbox_interrupt.py +++ b/agent_runtimes/tests/test_sandbox_interrupt.py @@ -3,6 +3,7 @@ """Tests for sandbox interrupt and execution status features.""" +import asyncio import sys import types from typing import Any @@ -35,6 +36,12 @@ def interrupt(self) -> bool: self._executing = False return True + async def run_code_streaming_async(self, *args: Any, **kwargs: Any): + _ = (args, kwargs) + for item in ["a", "b", "c"]: + await asyncio.sleep(0) + yield item + class TestManagedSandboxInterrupt: """Tests for ManagedSandbox.is_executing and interrupt().""" @@ -83,6 +90,18 @@ def test_interrupt_returns_false_when_no_sandbox(self) -> None: result = managed.interrupt() assert result is False + @pytest.mark.asyncio + async def test_run_code_streaming_async_yields_items(self) -> None: + sandbox = DummySandbox(executing=False) + manager = self._make_manager_with_sandbox(sandbox) + managed = ManagedSandbox(manager) + + observed = [] + async for item in managed.run_code_streaming_async("print('hi')"): + observed.append(item) + + assert observed == ["a", "b", "c"] + class TestSandboxStatusEndpoint: """Tests for the /sandbox/interrupt configure endpoint and SandboxStatus model.""" @@ -142,3 +161,62 @@ class DummyJupyterSandbox: sandbox = manager._create_sandbox() assert isinstance(sandbox, DummyJupyterSandbox) + + def test_jupyter_with_url_uses_high_level_factory(self, monkeypatch: Any) -> None: + manager = CodeSandboxManager() + manager.configure( + variant="jupyter", + jupyter_url="http://localhost:8888", + jupyter_token="MY_TOKEN", + ) + + fake_package = types.ModuleType("code_sandboxes") + + class DummySandbox: + @staticmethod + def create(*, variant: str, server_url: str | None = None, token: str | None = None): + return { + "variant": variant, + "server_url": server_url, + "token": token, + } + + fake_package.Sandbox = DummySandbox + monkeypatch.setitem(sys.modules, "code_sandboxes", fake_package) + + sandbox = manager._create_sandbox() + + assert sandbox == { + "variant": "jupyter", + "server_url": "http://localhost:8888", + "token": "MY_TOKEN", + } + + def test_jupyter_with_url_falls_back_when_sandbox_symbol_missing( + self, monkeypatch: Any + ) -> None: + manager = CodeSandboxManager() + manager.configure( + variant="jupyter", + jupyter_url="http://localhost:8888", + jupyter_token="MY_TOKEN", + ) + + fake_package = types.ModuleType("code_sandboxes") + fake_package.__path__ = [] + fake_module = types.ModuleType("code_sandboxes.jupyter_sandbox") + + class DummyJupyterSandbox: + def __init__(self, server_url: str | None = None, token: str | None = None) -> None: + self.server_url = server_url + self.token = token + + fake_module.JupyterSandbox = DummyJupyterSandbox + monkeypatch.setitem(sys.modules, "code_sandboxes", fake_package) + monkeypatch.setitem(sys.modules, "code_sandboxes.jupyter_sandbox", fake_module) + + sandbox = manager._create_sandbox() + + assert isinstance(sandbox, DummyJupyterSandbox) + assert sandbox.server_url == "http://localhost:8888" + assert sandbox.token == "MY_TOKEN" diff --git a/agent_runtimes/tests/test_sandbox_route_streaming.py b/agent_runtimes/tests/test_sandbox_route_streaming.py new file mode 100644 index 00000000..040e63ed --- /dev/null +++ b/agent_runtimes/tests/test_sandbox_route_streaming.py @@ -0,0 +1,95 @@ +# Copyright (c) 2025-2026 Datalayer, Inc. +# +# BSD 3-Clause License + +"""Tests for sandbox execute route streaming behavior.""" + +from __future__ import annotations + +import sys +from types import SimpleNamespace + +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from agent_runtimes.routes import sandbox as sandbox_route + + +class _FakeManager: + def get_agent_sandbox(self, _agent_id): + return None + + def get_managed_sandbox(self): + return object() + + +class _FakeStreamingClient: + def __init__(self, _sandbox): + self.variant = "kaggle" + + async def execute_code_streaming_async(self, code: str, language: str = "python", timeout=None): + _ = (code, language, timeout) + yield SimpleNamespace(line="[kaggle] status: RUNNING", error=False) + yield SimpleNamespace(line="hello", error=False) + yield SimpleNamespace(data={"text/plain": "42"}, is_main_result=True, extra={}) + + +class _FakeSyncClient(_FakeStreamingClient): + async def execute_code_async(self, code: str, language: str = "python", timeout=None): + _ = (code, language, timeout) + return SimpleNamespace( + success=True, + execution_ok=True, + stdout="hello", + stderr="", + results=["42"], + error=None, + ) + + +def _build_client() -> TestClient: + app = FastAPI() + app.include_router(sandbox_route.router, prefix="/api/v1") + return TestClient(app) + + +def test_execute_route_streaming_aggregates_events(monkeypatch): + monkeypatch.setattr( + "agent_runtimes.services.code_sandbox_manager.get_code_sandbox_manager", + lambda: _FakeManager(), + ) + monkeypatch.setitem(sys.modules, "code_sandboxes", SimpleNamespace(CodeSandboxClient=_FakeSyncClient)) + + client = _build_client() + response = client.post( + "/api/v1/sandbox/execute", + json={"code": "print('hi')", "stream": True}, + ) + + assert response.status_code == 200 + payload = response.json() + assert payload["success"] is True + assert "status: RUNNING" in payload["stdout"] + assert "hello" in payload["stdout"] + assert payload["results"] == ["42"] + assert payload["variant"] == "kaggle" + + +def test_execute_route_non_streaming_path(monkeypatch): + monkeypatch.setattr( + "agent_runtimes.services.code_sandbox_manager.get_code_sandbox_manager", + lambda: _FakeManager(), + ) + monkeypatch.setitem(sys.modules, "code_sandboxes", SimpleNamespace(CodeSandboxClient=_FakeSyncClient)) + + client = _build_client() + response = client.post( + "/api/v1/sandbox/execute", + json={"code": "print('hi')"}, + ) + + assert response.status_code == 200 + payload = response.json() + assert payload["success"] is True + assert payload["stdout"] == "hello" + assert payload["results"] == ["42"] diff --git a/docs/docs/programmatic-tools/index.mdx b/docs/docs/programmatic-tools/index.mdx index b431d580..337d6028 100644 --- a/docs/docs/programmatic-tools/index.mdx +++ b/docs/docs/programmatic-tools/index.mdx @@ -247,6 +247,9 @@ print(result) - `keywords`/`limit` on `list_tool_names`: faster, filtered discovery. - `include_deferred` on `search_tools`/`list_tool_names`: control discovery of tools marked `defer_loading`. - `max_tool_calls`: optional per-run safety cap on tool invocations inside `execute_code`. +- `AGENT_RUNTIMES_SANDBOX_GPU`: optional GPU flavor / accelerator hint forwarded + to code-sandboxes (for example `T4` or `A100`; for Kaggle batch, values like + `NvidiaTeslaT4` or aliases such as `T4`). ## `allow_direct_tool_calls` - **What it does:** Controls whether the `call_tool` shortcut is exposed. When off, all tool usage flows through `execute_code`, which keeps execution auditable and code-first. diff --git a/scripts/codegen/generate_sandbox_agents.py b/scripts/codegen/generate_sandbox_agents.py index fd77cf76..73b4362d 100644 --- a/scripts/codegen/generate_sandbox_agents.py +++ b/scripts/codegen/generate_sandbox_agents.py @@ -13,13 +13,13 @@ import argparse from pathlib import Path - VARIANTS: tuple[str, ...] = ( "eval", "jupyter", "docker", "datalayer", "colab", + "kaggle", "monty", "modal", ) @@ -32,6 +32,7 @@ def _emoji_for_variant(variant: str) -> str: "docker": "C", "datalayer": "D", "colab": "E", + "kaggle": "H", "monty": "F", "modal": "G", } diff --git a/src/examples/AgentCodeSandboxesExample.tsx b/src/examples/AgentCodeSandboxesExample.tsx index e55fe925..ee8e5354 100644 --- a/src/examples/AgentCodeSandboxesExample.tsx +++ b/src/examples/AgentCodeSandboxesExample.tsx @@ -17,7 +17,14 @@ import { useExampleAgentRuntimesUrl } from './utils/useExampleAgentRuntimesUrl'; import { Chat } from '../chat'; type SandboxVariant = - 'eval' | 'jupyter' | 'docker' | 'datalayer' | 'colab' | 'monty' | 'modal'; + | 'eval' + | 'jupyter' + | 'docker' + | 'datalayer' + | 'colab' + | 'kaggle' + | 'monty' + | 'modal'; interface SandboxSpecOption { variant: SandboxVariant; @@ -55,7 +62,15 @@ const SANDBOX_SPEC_OPTIONS: SandboxSpecOption[] = [ variant: 'colab', specId: 'example-sandbox-colab', title: 'Colab Sandbox', - description: 'Google Colab runtime bridge for remote notebook kernels.', + description: + 'Google Colab runtime connector (reuse an already-running kernel).', + }, + { + variant: 'kaggle', + specId: 'example-sandbox-kaggle', + title: 'Kaggle Sandbox', + description: + 'Kaggle runtime connector (create kernel with API token or attach existing).', }, { variant: 'monty', diff --git a/src/specs/agents/agents.ts b/src/specs/agents/agents.ts index 3ef5409d..637dfee9 100644 --- a/src/specs/agents/agents.ts +++ b/src/specs/agents/agents.ts @@ -1910,6 +1910,62 @@ export const EXAMPLE_SANDBOX_JUPYTER_AGENTSPEC_0_0_1: Agentspec = { subagents: undefined, }; +export const EXAMPLE_SANDBOX_KAGGLE_AGENTSPEC_0_0_1: Agentspec = { + id: 'example-sandbox-kaggle', + version: '0.0.1', + name: 'Example Sandbox Kaggle Agent', + description: `Demonstration agent configured to run codemode code execution with the 'kaggle' sandbox variant.`, + tags: ['sandbox', 'codemode', 'kaggle'], + enabled: true, + model: 'bedrock:us.anthropic.claude-opus-4-8', + mcpServers: [MCP_SERVER_MAP['tavily:0.0.1']], + skills: [ + SKILL_MAP['events:0.0.1'] + ? toAgentSkillSpec(SKILL_MAP['events:0.0.1']) + : undefined, + ].filter(Boolean) as SkillSpec[], + tools: [TOOL_MAP['runtime-echo:0.0.1']], + frontendTools: [ + FRONTEND_TOOL_MAP['jupyter-notebook:0.0.1'], + FRONTEND_TOOL_MAP['lexical-document:0.0.1'], + ], + environmentName: 'ai-agents-env', + icon: 'package', + emoji: 'H', + color: '#1F6FEB', + suggestions: [ + "Use execute_code to print('sandbox variant: kaggle')", + 'Use execute_code to compute sum(i*i for i in range(20))', + 'Use execute_code to load pandas and build a small DataFrame', + ], + welcomeMessage: + "You're connected to the kaggle sandbox variant demo. Ask me to run Python code and I will use execute_code in codemode.", + welcomeNotebook: undefined, + welcomeDocument: undefined, + sandboxVariant: 'kaggle', + systemPrompt: `You are a sandbox-variant demonstration assistant. Prefer executing Python code via execute_code for computations, data checks, and quick experiments, then summarize results clearly.`, + systemPromptCodemodeAddons: `Always use execute_code when the user requests calculations, scripts, DataFrame operations, package checks, or shell-style diagnostics.`, + goal: undefined, + protocol: undefined, + uiExtension: undefined, + trigger: undefined, + modelConfig: undefined, + mcpServerTools: undefined, + guardrails: undefined, + evals: undefined, + codemode: { enabled: true, token_reduction: '~80%', speedup: '~1.5x' }, + output: undefined, + advanced: undefined, + authorizationPolicy: undefined, + notifications: undefined, + memory: 'ephemeral', + preHooks: undefined, + postHooks: undefined, + toolHooks: undefined, + parameters: undefined, + subagents: undefined, +}; + export const EXAMPLE_SANDBOX_MODAL_AGENTSPEC_0_0_1: Agentspec = { id: 'example-sandbox-modal', version: '0.0.1', @@ -6986,6 +7042,7 @@ export const AGENTSPECS: Record = { 'example-sandbox-docker': EXAMPLE_SANDBOX_DOCKER_AGENTSPEC_0_0_1, 'example-sandbox-eval': EXAMPLE_SANDBOX_EVAL_AGENTSPEC_0_0_1, 'example-sandbox-jupyter': EXAMPLE_SANDBOX_JUPYTER_AGENTSPEC_0_0_1, + 'example-sandbox-kaggle': EXAMPLE_SANDBOX_KAGGLE_AGENTSPEC_0_0_1, 'example-sandbox-modal': EXAMPLE_SANDBOX_MODAL_AGENTSPEC_0_0_1, 'example-sandbox-monty': EXAMPLE_SANDBOX_MONTY_AGENTSPEC_0_0_1, 'example-shared-state': EXAMPLE_SHARED_STATE_AGENTSPEC_0_0_1, diff --git a/src/types/agentspecs.ts b/src/types/agentspecs.ts index 59c57652..c0abef61 100644 --- a/src/types/agentspecs.ts +++ b/src/types/agentspecs.ts @@ -73,7 +73,7 @@ export interface Agentspec { welcomeNotebook?: string; /** Path to Lexical document to show on agent creation */ welcomeDocument?: string; - /** Sandbox variant to use for this agent ('eval', 'jupyter') */ + /** Sandbox variant to use for this agent (e.g. 'eval', 'jupyter', 'kaggle'). */ sandboxVariant?: string; /** User-facing objective for the agent */ goal?: string; diff --git a/src/types/config.ts b/src/types/config.ts index 5bf07cc7..18c29c00 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -57,7 +57,7 @@ export interface AgentConfig { inferenceProvider?: string; /** Enable codemode if supported by the selected spec/runtime. */ enableCodemode?: boolean; - /** Optional sandbox variant (e.g. eval, jupyter). */ + /** Optional sandbox variant (e.g. eval, jupyter, kaggle). */ sandboxVariant?: string; /** Optional Jupyter sandbox URL for jupyter-backed sandbox mode. */ jupyterSandbox?: string; From 65a3ce8539c7b82ff42dea581b21bf9d4559e4b4 Mon Sep 17 00:00:00 2001 From: Eric Charles Date: Fri, 24 Jul 2026 15:22:59 +0200 Subject: [PATCH 4/7] Apply pre-commit autofixes for code-style CI --- agent_runtimes/chat/cli.py | 4 +--- agent_runtimes/integrations/codemode.py | 6 +++++- .../tests/test_sandbox_interrupt.py | 8 ++++++-- .../tests/test_sandbox_route_streaming.py | 20 +++++++++++++++---- scripts/codegen/generate_sandbox_agents.py | 2 +- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/agent_runtimes/chat/cli.py b/agent_runtimes/chat/cli.py index 90c8c3eb..61668c99 100644 --- a/agent_runtimes/chat/cli.py +++ b/agent_runtimes/chat/cli.py @@ -714,9 +714,7 @@ def _pick_agentspec_interactive() -> str: if model_spec is not None: model_ok = model_spec.id in available_model_ids model_color = GREEN_LIGHT if model_ok else RED - print( - f" model: {model_color}{model_spec.id}{RESET}" - ) + print(f" model: {model_color}{model_spec.id}{RESET}") else: print(f" model: {GRAY}{model_ref} (custom/unknown){RESET}") diff --git a/agent_runtimes/integrations/codemode.py b/agent_runtimes/integrations/codemode.py index 255aa9d2..615eb0a0 100644 --- a/agent_runtimes/integrations/codemode.py +++ b/agent_runtimes/integrations/codemode.py @@ -128,7 +128,11 @@ async def setup(self) -> None: config = CodeModeConfig( skills_path=self.skills_path, sandbox_variant=self.sandbox_variant, - **({} if self.sandbox_gpu is None else {"sandbox_gpu": self.sandbox_gpu}), + **( + {} + if self.sandbox_gpu is None + else {"sandbox_gpu": self.sandbox_gpu} + ), **({} if mcp_proxy_url is None else {"mcp_proxy_url": mcp_proxy_url}), ) self._executor = CodeModeExecutor(self._registry, config) diff --git a/agent_runtimes/tests/test_sandbox_interrupt.py b/agent_runtimes/tests/test_sandbox_interrupt.py index 3dad9ee4..1464d5e4 100644 --- a/agent_runtimes/tests/test_sandbox_interrupt.py +++ b/agent_runtimes/tests/test_sandbox_interrupt.py @@ -174,7 +174,9 @@ def test_jupyter_with_url_uses_high_level_factory(self, monkeypatch: Any) -> Non class DummySandbox: @staticmethod - def create(*, variant: str, server_url: str | None = None, token: str | None = None): + def create( + *, variant: str, server_url: str | None = None, token: str | None = None + ): return { "variant": variant, "server_url": server_url, @@ -207,7 +209,9 @@ def test_jupyter_with_url_falls_back_when_sandbox_symbol_missing( fake_module = types.ModuleType("code_sandboxes.jupyter_sandbox") class DummyJupyterSandbox: - def __init__(self, server_url: str | None = None, token: str | None = None) -> None: + def __init__( + self, server_url: str | None = None, token: str | None = None + ) -> None: self.server_url = server_url self.token = token diff --git a/agent_runtimes/tests/test_sandbox_route_streaming.py b/agent_runtimes/tests/test_sandbox_route_streaming.py index 040e63ed..61cd759b 100644 --- a/agent_runtimes/tests/test_sandbox_route_streaming.py +++ b/agent_runtimes/tests/test_sandbox_route_streaming.py @@ -27,7 +27,9 @@ class _FakeStreamingClient: def __init__(self, _sandbox): self.variant = "kaggle" - async def execute_code_streaming_async(self, code: str, language: str = "python", timeout=None): + async def execute_code_streaming_async( + self, code: str, language: str = "python", timeout=None + ): _ = (code, language, timeout) yield SimpleNamespace(line="[kaggle] status: RUNNING", error=False) yield SimpleNamespace(line="hello", error=False) @@ -35,7 +37,9 @@ async def execute_code_streaming_async(self, code: str, language: str = "python" class _FakeSyncClient(_FakeStreamingClient): - async def execute_code_async(self, code: str, language: str = "python", timeout=None): + async def execute_code_async( + self, code: str, language: str = "python", timeout=None + ): _ = (code, language, timeout) return SimpleNamespace( success=True, @@ -58,7 +62,11 @@ def test_execute_route_streaming_aggregates_events(monkeypatch): "agent_runtimes.services.code_sandbox_manager.get_code_sandbox_manager", lambda: _FakeManager(), ) - monkeypatch.setitem(sys.modules, "code_sandboxes", SimpleNamespace(CodeSandboxClient=_FakeSyncClient)) + monkeypatch.setitem( + sys.modules, + "code_sandboxes", + SimpleNamespace(CodeSandboxClient=_FakeSyncClient), + ) client = _build_client() response = client.post( @@ -80,7 +88,11 @@ def test_execute_route_non_streaming_path(monkeypatch): "agent_runtimes.services.code_sandbox_manager.get_code_sandbox_manager", lambda: _FakeManager(), ) - monkeypatch.setitem(sys.modules, "code_sandboxes", SimpleNamespace(CodeSandboxClient=_FakeSyncClient)) + monkeypatch.setitem( + sys.modules, + "code_sandboxes", + SimpleNamespace(CodeSandboxClient=_FakeSyncClient), + ) client = _build_client() response = client.post( diff --git a/scripts/codegen/generate_sandbox_agents.py b/scripts/codegen/generate_sandbox_agents.py index 73b4362d..be2c1ad5 100644 --- a/scripts/codegen/generate_sandbox_agents.py +++ b/scripts/codegen/generate_sandbox_agents.py @@ -142,4 +142,4 @@ def main() -> None: if __name__ == "__main__": - main() \ No newline at end of file + main() From db6118ce09822b7c9486daa936cafa8e2012f08a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 24 Jul 2026 13:24:08 +0000 Subject: [PATCH 5/7] Automatic application of license header --- agent_runtimes/tests/test_sandbox_route_streaming.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/agent_runtimes/tests/test_sandbox_route_streaming.py b/agent_runtimes/tests/test_sandbox_route_streaming.py index 61cd759b..047c9a45 100644 --- a/agent_runtimes/tests/test_sandbox_route_streaming.py +++ b/agent_runtimes/tests/test_sandbox_route_streaming.py @@ -1,3 +1,6 @@ +# Copyright (c) 2025-2026 Datalayer, Inc. +# Distributed under the terms of the Modified BSD License. + # Copyright (c) 2025-2026 Datalayer, Inc. # # BSD 3-Clause License From 69b368b43ab4120cba56ea7ef49f0e0330f34d5b Mon Sep 17 00:00:00 2001 From: Eric Charles Date: Fri, 24 Jul 2026 15:27:37 +0200 Subject: [PATCH 6/7] Add missing test annotations for mypy strict checks --- .../tests/test_sandbox_interrupt.py | 8 +++++--- .../tests/test_sandbox_route_streaming.py | 20 ++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/agent_runtimes/tests/test_sandbox_interrupt.py b/agent_runtimes/tests/test_sandbox_interrupt.py index 1464d5e4..8605572a 100644 --- a/agent_runtimes/tests/test_sandbox_interrupt.py +++ b/agent_runtimes/tests/test_sandbox_interrupt.py @@ -6,7 +6,7 @@ import asyncio import sys import types -from typing import Any +from typing import Any, AsyncGenerator import pytest @@ -36,7 +36,9 @@ def interrupt(self) -> bool: self._executing = False return True - async def run_code_streaming_async(self, *args: Any, **kwargs: Any): + async def run_code_streaming_async( + self, *args: Any, **kwargs: Any + ) -> AsyncGenerator[str, None]: _ = (args, kwargs) for item in ["a", "b", "c"]: await asyncio.sleep(0) @@ -176,7 +178,7 @@ class DummySandbox: @staticmethod def create( *, variant: str, server_url: str | None = None, token: str | None = None - ): + ) -> dict[str, str | None]: return { "variant": variant, "server_url": server_url, diff --git a/agent_runtimes/tests/test_sandbox_route_streaming.py b/agent_runtimes/tests/test_sandbox_route_streaming.py index 047c9a45..4304c3c1 100644 --- a/agent_runtimes/tests/test_sandbox_route_streaming.py +++ b/agent_runtimes/tests/test_sandbox_route_streaming.py @@ -11,7 +11,9 @@ import sys from types import SimpleNamespace +from typing import AsyncGenerator +import pytest from fastapi import FastAPI from fastapi.testclient import TestClient @@ -19,20 +21,20 @@ class _FakeManager: - def get_agent_sandbox(self, _agent_id): + def get_agent_sandbox(self, _agent_id: str) -> None: return None - def get_managed_sandbox(self): + def get_managed_sandbox(self) -> object: return object() class _FakeStreamingClient: - def __init__(self, _sandbox): + def __init__(self, _sandbox: object) -> None: self.variant = "kaggle" async def execute_code_streaming_async( - self, code: str, language: str = "python", timeout=None - ): + self, code: str, language: str = "python", timeout: int | None = None + ) -> AsyncGenerator[SimpleNamespace, None]: _ = (code, language, timeout) yield SimpleNamespace(line="[kaggle] status: RUNNING", error=False) yield SimpleNamespace(line="hello", error=False) @@ -41,8 +43,8 @@ async def execute_code_streaming_async( class _FakeSyncClient(_FakeStreamingClient): async def execute_code_async( - self, code: str, language: str = "python", timeout=None - ): + self, code: str, language: str = "python", timeout: int | None = None + ) -> SimpleNamespace: _ = (code, language, timeout) return SimpleNamespace( success=True, @@ -60,7 +62,7 @@ def _build_client() -> TestClient: return TestClient(app) -def test_execute_route_streaming_aggregates_events(monkeypatch): +def test_execute_route_streaming_aggregates_events(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr( "agent_runtimes.services.code_sandbox_manager.get_code_sandbox_manager", lambda: _FakeManager(), @@ -86,7 +88,7 @@ def test_execute_route_streaming_aggregates_events(monkeypatch): assert payload["variant"] == "kaggle" -def test_execute_route_non_streaming_path(monkeypatch): +def test_execute_route_non_streaming_path(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr( "agent_runtimes.services.code_sandbox_manager.get_code_sandbox_manager", lambda: _FakeManager(), From 1185d45f4abfde67c120865de5fb17fc559b74fc Mon Sep 17 00:00:00 2001 From: Eric Charles Date: Fri, 24 Jul 2026 15:31:59 +0200 Subject: [PATCH 7/7] Apply ruff-format output for sandbox route streaming test --- agent_runtimes/tests/test_sandbox_route_streaming.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/agent_runtimes/tests/test_sandbox_route_streaming.py b/agent_runtimes/tests/test_sandbox_route_streaming.py index 4304c3c1..07168767 100644 --- a/agent_runtimes/tests/test_sandbox_route_streaming.py +++ b/agent_runtimes/tests/test_sandbox_route_streaming.py @@ -62,7 +62,9 @@ def _build_client() -> TestClient: return TestClient(app) -def test_execute_route_streaming_aggregates_events(monkeypatch: pytest.MonkeyPatch) -> None: +def test_execute_route_streaming_aggregates_events( + monkeypatch: pytest.MonkeyPatch, +) -> None: monkeypatch.setattr( "agent_runtimes.services.code_sandbox_manager.get_code_sandbox_manager", lambda: _FakeManager(),