Skip to main content

What You’ll Learn

  • Implementing a Haystack @component with component.output_types that runs Python in a Declaw sandbox
  • Connecting the component in a Haystack Pipeline alongside OpenAIGenerator and PromptBuilder
  • Demo mode that exercises the component directly without needing an OpenAI key

Prerequisites

  • Declaw instance running and DECLAW_API_KEY / DECLAW_DOMAIN set
  • OPENAI_API_KEY (optional — the example runs in demo mode without it)
pip install declaw python-dotenv haystack-ai
This example is available in Python. TypeScript support coming soon.

Code Walkthrough

1. Define the DecawCodeRunner component

Use the @component decorator and declare output types with @component.output_types. The run() method receives typed inputs and returns a dict matching the declared output types:
from haystack import component
from declaw import Sandbox

@component
class DecawCodeRunner:
    """Haystack component that executes Python code in a Declaw sandbox."""

    @component.output_types(output=str, exit_code=int)
    def run(self, code: str) -> dict:
        sbx = Sandbox.create(template="python", timeout=300)
        try:
            sbx.files.write("/tmp/code.py", code)
            result = sbx.commands.run("python3 /tmp/code.py", timeout=30)
            return {"output": result.stdout, "exit_code": result.exit_code}
        finally:
            sbx.kill()
The output and exit_code output names can be connected to downstream components in the pipeline by referencing code_runner.output and code_runner.exit_code.

2. Build a pipeline with an LLM and the code runner

from haystack import Pipeline
from haystack.components.generators import OpenAIGenerator
from haystack.components.builders.prompt_builder import PromptBuilder

template = (
    "Write Python code (and nothing else) to solve: {{ query }}\n"
    "Output only the Python code, no markdown fences."
)

pipeline = Pipeline()
pipeline.add_component("prompt_builder", PromptBuilder(template=template))
pipeline.add_component("llm", OpenAIGenerator(model="gpt-4o-mini"))
pipeline.add_component("code_runner", DecawCodeRunner())

pipeline.connect("prompt_builder", "llm")
pipeline.connect("llm.replies", "code_runner.code")

3. Run the pipeline

query = "Calculate the factorial of 15 and print it"
result = pipeline.run({"prompt_builder": {"query": query}})
print(result["code_runner"]["output"])
print(result["code_runner"]["exit_code"])

4. Demo mode (no API key needed)

Run the component directly without building a pipeline:
from declaw import Sandbox

code = """\
import math
n = 15
print(f"{n}! = {math.factorial(n)}")
"""

sbx = Sandbox.create(template="python", timeout=300)
try:
    sbx.files.write("/tmp/code.py", code)
    result = sbx.commands.run("python3 /tmp/code.py", timeout=30)
    print(result.stdout)
finally:
    sbx.kill()

Expected Output

In demo mode:
=======================================================
Haystack + Declaw Sandbox Example
=======================================================
No OPENAI_API_KEY found -- running demo mode.

--- Component Definition ---
@component
class DecawCodeRunner:
    @component.output_types(output=str, exit_code=int)
    def run(self, code: str) -> dict:
        sbx = Sandbox.create(template="python", timeout=300)
        ...

--- Running Code Directly in Declaw Sandbox ---
Code:
import math
n = 15
print(f"{n}! = {math.factorial(n)}")

stdout: 15! = 1307674368000
stderr:
exit_code: 0

Sandbox cleaned up.

=======================================================
Done!
=======================================================