Skip to main content

Overview

ADK-TS provides multiple code execution environments for running Python code generated by LLMs. From unsafe local execution for development to secure container-based and cloud execution for production, you can choose the right environment for your needs.

BaseCodeExecutor

All code executors extend BaseCodeExecutor:
import { BaseCodeExecutor } from '@iqai/adk';
import type { CodeExecutionInput, CodeExecutionResult } from '@iqai/adk';

abstract class BaseCodeExecutor {
  abstract executeCode(
    invocationContext: InvocationContext,
    codeExecutionInput: CodeExecutionInput,
  ): Promise<CodeExecutionResult>;
}
Source: packages/adk/src/code-executors/base-code-executor.ts:44

Configuration Options

interface BaseCodeExecutorConfig {
  // Extract data files from model request
  optimizeDataFile?: boolean;
  
  // Maintain state between executions
  stateful?: boolean;
  
  // Retry attempts on errors
  errorRetryAttempts?: number;
  
  // Code block delimiters
  codeBlockDelimiters?: Array<[string, string]>;
  
  // Result formatting
  executionResultDelimiters?: [string, string];
}

Built-in Executors

UnsafeLocalCodeExecutor

Development Only: This executor runs Python code directly on your machine without sandboxing. Never use in production or with untrusted code.
Executes code using the local Python interpreter:
import { UnsafeLocalCodeExecutor } from '@iqai/adk';

const executor = new UnsafeLocalCodeExecutor({
  stateful: true,              // Maintain variables between executions
  errorRetryAttempts: 2,       // Retry on errors
  optimizeDataFile: false,
});

// Execute code
const result = await executor.executeCode(context, {
  code: `
import math
result = math.sqrt(16)
print(f"Square root: {result}")
  `,
});

console.log(result.stdout); // "Square root: 4.0"
Use Cases:
  • Local development
  • Rapid prototyping
  • Testing code generation
Limitations:
  • No sandboxing
  • Security risk with untrusted code
  • Requires Python installed locally

ContainerCodeExecutor

Executes code in isolated Docker containers for security:
import { ContainerCodeExecutor } from '@iqai/adk';

const executor = new ContainerCodeExecutor({
  // Option 1: Use pre-built image
  image: 'python:3.11-slim',
  
  // Option 2: Build from Dockerfile
  // dockerPath: './docker/python-executor',
  
  executionTimeout: 30000,     // 30 second timeout
});

const result = await executor.executeCode(context, {
  code: `
import pandas as pd
import numpy as np

data = {'A': [1, 2, 3], 'B': [4, 5, 6]}
df = pd.DataFrame(data)
print(df.describe())
  `,
});
Source: packages/adk/src/code-executors/container-code-executor.ts:52

Features

Isolated Execution: Each execution runs in a separate container ✅ Timeout Control: Prevent infinite loops ✅ Custom Images: Bring your own dependencies ✅ Production Ready: Secure by design

Docker Configuration

const executor = new ContainerCodeExecutor({
  image: 'python:3.11-slim',
  executionTimeout: 30000,
});

Dockerfile Example

# docker/python-executor/Dockerfile
FROM python:3.11-slim

RUN pip install --no-cache-dir \
    pandas \
    numpy \
    matplotlib \
    scipy \
    scikit-learn

WORKDIR /workspace

Container Lifecycle

const executor = new ContainerCodeExecutor({ image: 'python:3.11' });

// Container starts on first execution
await executor.executeCode(context, { code: '...' });

// Reuses container for subsequent calls
await executor.executeCode(context, { code: '...' });

// Clean up manually if needed
await executor.dispose();
ContainerCodeExecutor automatically cleans up containers on process exit.

VertexAICodeExecutor

Executes code using Google Cloud’s Vertex AI Code Interpreter:
import { VertexAICodeExecutor } from '@iqai/adk';

const executor = new VertexAICodeExecutor({
  projectId: 'my-gcp-project',
  location: 'us-central1',
});

const result = await executor.executeCode(context, {
  code: `
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)

plt.plot(x, y)
plt.title('Sine Wave')
plt.savefig('sine.png')
print('Plot saved')
  `,
});

// Access output files
if (result.outputFiles?.length > 0) {
  console.log('Generated files:', result.outputFiles);
}
Features: Managed Infrastructure: No container management ✅ Scalable: Handles concurrent executions ✅ File Support: Generate and retrieve output files ✅ Pre-installed Libraries: NumPy, Pandas, Matplotlib, etc. Requirements:
  • Google Cloud project with Vertex AI enabled
  • Application Default Credentials configured
  • Vertex AI API permissions

Using with Agents

Code Execution Agent

import { AgentBuilder, ContainerCodeExecutor } from '@iqai/adk';

const executor = new ContainerCodeExecutor({
  image: 'python:3.11-slim',
});

const agent = new AgentBuilder()
  .withName('DataAnalyst')
  .withModel('gpt-4')
  .withCodeExecutor(executor)
  .withInstruction(`
    You are a data analysis assistant.
    Use Python code to analyze data and generate insights.
    You can use pandas, numpy, and matplotlib.
  `)
  .buildLlm();

const response = await agent.ask(
  'Analyze the correlation between temperature and ice cream sales in this dataset: [data]'
);

With Code Execution Flow

import { CodeExecutionFlow } from '@iqai/adk';

const flow = new CodeExecutionFlow({
  codeExecutor: new ContainerCodeExecutor({
    image: 'python:3.11-slim',
  }),
});

const agent = new AgentBuilder()
  .withModel('claude-sonnet-4')
  .withFlow(flow)
  .buildLlm();
Source: packages/adk/src/flows/llm-flows/code-execution.ts

Code Execution Results

interface CodeExecutionResult {
  stdout: string;              // Standard output
  stderr: string;              // Error output
  outputFiles: Array<{         // Generated files (if any)
    name: string;
    content: Buffer;
    mimeType: string;
  }>;
}

const result = await executor.executeCode(context, { code });

if (result.stderr) {
  console.error('Execution error:', result.stderr);
}

if (result.outputFiles.length > 0) {
  for (const file of result.outputFiles) {
    console.log(`Generated ${file.name} (${file.mimeType})`);
    // Save or process the file
  }
}

Error Handling

Retry Logic

const executor = new ContainerCodeExecutor({
  image: 'python:3.11',
  errorRetryAttempts: 3, // Retry up to 3 times
});

try {
  const result = await executor.executeCode(context, { code });
  
  if (result.stderr && result.stderr.includes('Error')) {
    // Handle execution errors
    console.error('Code execution failed:', result.stderr);
  }
} catch (error) {
  // Handle executor errors (timeout, container failure, etc.)
  console.error('Executor error:', error);
}

Timeout Handling

const executor = new ContainerCodeExecutor({
  image: 'python:3.11',
  executionTimeout: 10000, // 10 seconds
});

const result = await executor.executeCode(context, { code });

if (result.stderr?.includes('timeout')) {
  console.log('Execution timed out - consider increasing limit');
}

Security Best Practices

Production Guidelines:
  1. ⛔ Never use UnsafeLocalCodeExecutor in production
  2. ✅ Always use containerized or cloud executors
  3. ✅ Set reasonable execution timeouts
  4. ✅ Limit resource usage (CPU, memory)
  5. ✅ Validate generated code before execution
  6. ✅ Monitor execution patterns for abuse

Resource Limits

// Docker resource constraints
const executor = new ContainerCodeExecutor({
  image: 'python:3.11',
  executionTimeout: 30000,
  // These would need custom implementation:
  // maxMemory: '512M',
  // maxCpu: '0.5',
});

Code Validation

import { BaseLlmRequestProcessor } from '@iqai/adk';

class CodeValidationProcessor extends BaseLlmRequestProcessor {
  async *runAsync(ctx, request) {
    // Inspect generated code before execution
    const code = this.extractCode(request);
    
    if (this.containsDangerousOperations(code)) {
      throw new Error('Code contains forbidden operations');
    }
  }
  
  private containsDangerousOperations(code: string): boolean {
    const forbidden = [
      'os.system',
      'subprocess',
      'eval(',
      'exec(',
      '__import__',
    ];
    
    return forbidden.some(op => code.includes(op));
  }
}

Advanced Patterns

Stateful Execution

const executor = new UnsafeLocalCodeExecutor({
  stateful: true, // Maintain variables between calls
});

// First execution
await executor.executeCode(context, {
  code: 'x = 10; y = 20',
});

// Second execution - variables persist
const result = await executor.executeCode(context, {
  code: 'print(x + y)',
});

console.log(result.stdout); // "30"

Data File Optimization

const executor = new ContainerCodeExecutor({
  image: 'python:3.11',
  optimizeDataFile: true, // Extract CSV files from request
});

// The executor will detect and extract data files
// from the LLM request and make them available
// to the code execution environment

Custom Code Delimiters

const executor = new ContainerCodeExecutor({
  image: 'python:3.11',
  codeBlockDelimiters: [
    ['```python\n', '\n```'],
    ['<code>\n', '\n</code>'],
  ],
  executionResultDelimiters: ['<output>\n', '\n</output>'],
});

Performance Considerations

  1. Container Warmup: First execution is slower (container startup)
  2. Reuse Containers: Keep executors alive for multiple executions
  3. Parallel Execution: Multiple ContainerCodeExecutor instances run concurrently
  4. Cleanup: Dispose executors when done to free resources
// Good: Reuse executor
const executor = new ContainerCodeExecutor({ image: 'python:3.11' });

for (const task of tasks) {
  await executor.executeCode(context, { code: task.code });
}

await executor.dispose();

// Avoid: Creating new executor for each execution
for (const task of tasks) {
  const executor = new ContainerCodeExecutor({ image: 'python:3.11' });
  await executor.executeCode(context, { code: task.code });
  await executor.dispose();
}

Monitoring and Debugging

import { Logger } from '@iqai/adk';

const logger = new Logger({ name: 'CodeExecutor' });

const executor = new ContainerCodeExecutor({
  image: 'python:3.11',
});

const result = await executor.executeCode(context, { code });

logger.debug('Execution complete', {
  exitCode: result.exitCode,
  stdoutLength: result.stdout.length,
  stderrLength: result.stderr.length,
  outputFiles: result.outputFiles.length,
});

Next Steps

Streaming

Stream responses in real-time with Events

Authentication

Secure tool access with auth patterns

Build docs developers (and LLMs) love