mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-02 22:02:13 +08:00
feat: add Apple Container support with comprehensive documentation and dev tools
Add native Apple Container support for better performance on macOS while maintaining full Docker compatibility. Enhance documentation with memory system details, development guidelines, and sandbox setup instructions. Improve dev experience with container image pre-pulling and unified cleanup tools. Key changes: - Auto-detect and prefer Apple Container on macOS with Docker fallback - Add APPLE_CONTAINER.md with complete usage and troubleshooting guide - Document memory system architecture in CLAUDE.md - Add make setup-sandbox for pre-pulling container images - Create cleanup-containers.sh for cross-runtime container cleanup - Update all related documentation (README, SETUP, config examples) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
56
Makefile
56
Makefile
@@ -4,17 +4,18 @@
|
||||
|
||||
help:
|
||||
@echo "DeerFlow Development Commands:"
|
||||
@echo " make check - Check if all required tools are installed"
|
||||
@echo " make install - Install all dependencies (frontend + backend)"
|
||||
@echo " make dev - Start all services (frontend + backend + nginx on localhost:2026)"
|
||||
@echo " make stop - Stop all running services"
|
||||
@echo " make clean - Clean up processes and temporary files"
|
||||
@echo " make check - Check if all required tools are installed"
|
||||
@echo " make install - Install all dependencies (frontend + backend)"
|
||||
@echo " make setup-sandbox - Pre-pull sandbox container image (recommended)"
|
||||
@echo " make dev - Start all services (frontend + backend + nginx on localhost:2026)"
|
||||
@echo " make stop - Stop all running services"
|
||||
@echo " make clean - Clean up processes and temporary files"
|
||||
@echo ""
|
||||
@echo "Docker Development Commands:"
|
||||
@echo " make docker-init - Initialize and install dependencies in Docker containers"
|
||||
@echo " make docker-start - Start all services in Docker (localhost:2026)"
|
||||
@echo " make docker-stop - Stop Docker development services"
|
||||
@echo " make docker-logs - View Docker development logs"
|
||||
@echo " make docker-logs - View Docker development logs"
|
||||
@echo " make docker-logs-web - View Docker frontend logs"
|
||||
@echo " make docker-logs-api - View Docker backend logs"
|
||||
|
||||
@@ -100,6 +101,43 @@ install:
|
||||
@echo "Installing frontend dependencies..."
|
||||
@cd frontend && pnpm install
|
||||
@echo "✓ All dependencies installed"
|
||||
@echo ""
|
||||
@echo "=========================================="
|
||||
@echo " Optional: Pre-pull Sandbox Image"
|
||||
@echo "=========================================="
|
||||
@echo ""
|
||||
@echo "If you plan to use Docker/Container-based sandbox, you can pre-pull the image:"
|
||||
@echo " make setup-sandbox"
|
||||
@echo ""
|
||||
|
||||
# Pre-pull sandbox Docker image (optional but recommended)
|
||||
setup-sandbox:
|
||||
@echo "=========================================="
|
||||
@echo " Pre-pulling Sandbox Container Image"
|
||||
@echo "=========================================="
|
||||
@echo ""
|
||||
@IMAGE=$$(grep -A 20 "# sandbox:" config.yaml 2>/dev/null | grep "image:" | awk '{print $$2}' | head -1); \
|
||||
if [ -z "$$IMAGE" ]; then \
|
||||
IMAGE="enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest"; \
|
||||
echo "Using default image: $$IMAGE"; \
|
||||
else \
|
||||
echo "Using configured image: $$IMAGE"; \
|
||||
fi; \
|
||||
echo ""; \
|
||||
if command -v container >/dev/null 2>&1 && [ "$$(uname)" = "Darwin" ]; then \
|
||||
echo "Detected Apple Container on macOS, pulling image..."; \
|
||||
container pull "$$IMAGE" || echo "⚠ Apple Container pull failed, will try Docker"; \
|
||||
fi; \
|
||||
if command -v docker >/dev/null 2>&1; then \
|
||||
echo "Pulling image using Docker..."; \
|
||||
docker pull "$$IMAGE"; \
|
||||
echo ""; \
|
||||
echo "✓ Sandbox image pulled successfully"; \
|
||||
else \
|
||||
echo "✗ Neither Docker nor Apple Container is available"; \
|
||||
echo " Please install Docker: https://docs.docker.com/get-docker/"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
# Start all services
|
||||
dev:
|
||||
@@ -110,7 +148,7 @@ dev:
|
||||
@-nginx -c $(PWD)/docker/nginx/nginx.local.conf -p $(PWD) -s quit 2>/dev/null || true
|
||||
@sleep 1
|
||||
@-pkill -9 nginx 2>/dev/null || true
|
||||
@-docker ps -q --filter "name=deer-flow-sandbox" | xargs -r docker stop 2>/dev/null || true
|
||||
@-./scripts/cleanup-containers.sh deer-flow-sandbox 2>/dev/null || true
|
||||
@sleep 1
|
||||
@echo ""
|
||||
@echo "=========================================="
|
||||
@@ -132,7 +170,7 @@ dev:
|
||||
sleep 1; \
|
||||
pkill -9 nginx 2>/dev/null || true; \
|
||||
echo "Cleaning up sandbox containers..."; \
|
||||
docker ps -q --filter "name=deer-flow-sandbox" | xargs -r docker stop 2>/dev/null || true; \
|
||||
./scripts/cleanup-containers.sh deer-flow-sandbox 2>/dev/null || true; \
|
||||
echo "✓ All services stopped"; \
|
||||
exit 0; \
|
||||
}; \
|
||||
@@ -183,7 +221,7 @@ stop:
|
||||
@sleep 1
|
||||
@-pkill -9 nginx 2>/dev/null || true
|
||||
@echo "Cleaning up sandbox containers..."
|
||||
@-docker ps -q --filter "name=deer-flow-sandbox" | xargs -r docker stop 2>/dev/null || true
|
||||
@-./scripts/cleanup-containers.sh deer-flow-sandbox 2>/dev/null || true
|
||||
@echo "✓ All services stopped"
|
||||
|
||||
# Clean up
|
||||
|
||||
11
README.md
11
README.md
@@ -41,18 +41,25 @@ If you prefer running services locally:
|
||||
make install
|
||||
```
|
||||
|
||||
3. **Start services**:
|
||||
3. **(Optional) Pre-pull sandbox image**:
|
||||
```bash
|
||||
# Recommended if using Docker/Container-based sandbox
|
||||
make setup-sandbox
|
||||
```
|
||||
|
||||
4. **Start services**:
|
||||
```bash
|
||||
make dev
|
||||
```
|
||||
|
||||
4. **Access**: http://localhost:2026
|
||||
5. **Access**: http://localhost:2026
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed local development guide.
|
||||
|
||||
## Features
|
||||
|
||||
- 🤖 **LangGraph-based Agents** - Multi-agent orchestration with sophisticated workflows
|
||||
- 🧠 **Persistent Memory** - LLM-powered context retention across conversations with automatic fact extraction
|
||||
- 🔧 **Model Context Protocol (MCP)** - Extensible tool integration
|
||||
- 🎯 **Skills System** - Reusable agent capabilities
|
||||
- 🛡️ **Sandbox Execution** - Safe code execution environment
|
||||
|
||||
@@ -40,6 +40,17 @@ deer-flow/
|
||||
└── custom/ # Custom skills (gitignored)
|
||||
```
|
||||
|
||||
## Important Development Guidelines
|
||||
|
||||
### Documentation Update Policy
|
||||
**CRITICAL: Always update README.md and CLAUDE.md after every code change**
|
||||
|
||||
When making code changes, you MUST update the relevant documentation:
|
||||
- Update `README.md` for user-facing changes (features, setup, usage instructions)
|
||||
- Update `CLAUDE.md` for development changes (architecture, commands, workflows, internal systems)
|
||||
- Keep documentation synchronized with the codebase at all times
|
||||
- Ensure accuracy and timeliness of all documentation
|
||||
|
||||
## Commands
|
||||
|
||||
**Root directory** (for full application):
|
||||
@@ -202,7 +213,49 @@ Configuration priority:
|
||||
5. `TitleMiddleware` - Generates conversation titles
|
||||
6. `TodoListMiddleware` - Tracks multi-step tasks (if plan_mode enabled)
|
||||
7. `ViewImageMiddleware` - Injects image details for vision models
|
||||
8. `ClarificationMiddleware` - Handles clarification requests (must be last)
|
||||
8. `MemoryMiddleware` - Automatic context retention and personalization (if enabled)
|
||||
9. `ClarificationMiddleware` - Handles clarification requests (must be last)
|
||||
|
||||
**Memory System** (`src/agents/memory/`)
|
||||
- LLM-powered personalization layer that automatically extracts and stores user context across conversations
|
||||
- Components:
|
||||
- `updater.py` - LLM-based memory updates with fact extraction and file I/O
|
||||
- `queue.py` - Debounced update queue for batching and performance optimization
|
||||
- `prompt.py` - Prompt templates and formatting utilities for memory updates
|
||||
- `MemoryMiddleware` (`src/agents/middlewares/memory_middleware.py`) - Queues conversations for memory updates
|
||||
- Gateway API (`src/gateway/routers/memory.py`) - REST endpoints for memory management
|
||||
- Storage: JSON file at `backend/.deer-flow/memory.json`
|
||||
|
||||
**Memory Data Structure**:
|
||||
- **User Context** (current state):
|
||||
- `workContext` - Work-related information (job, projects, technologies)
|
||||
- `personalContext` - Preferences, communication style, background
|
||||
- `topOfMind` - Current focus areas and immediate priorities
|
||||
- **History** (temporal context):
|
||||
- `recentMonths` - Recent activities and discussions
|
||||
- `earlierContext` - Important historical context
|
||||
- `longTermBackground` - Persistent background information
|
||||
- **Facts** (structured knowledge):
|
||||
- Discrete facts with categories: `preference`, `knowledge`, `context`, `behavior`, `goal`
|
||||
- Each fact includes: `id`, `content`, `category`, `confidence` (0-1), `createdAt`, `source` (thread ID)
|
||||
- Confidence threshold (default 0.7) filters low-quality facts
|
||||
- Max facts limit (default 100) keeps highest-confidence facts
|
||||
|
||||
**Memory Workflow**:
|
||||
1. **Post-Interaction**: `MemoryMiddleware` filters messages (user inputs + final AI responses only) and queues conversation
|
||||
2. **Debounced Processing**: Queue waits 30s (configurable), batches multiple updates, resets timer on new updates
|
||||
3. **LLM-Based Update**: Background thread loads memory, formats conversation, invokes LLM to extract:
|
||||
- Updated context summaries (1-3 sentences each)
|
||||
- New facts with confidence scores and categories
|
||||
- Facts to remove (contradictions)
|
||||
4. **Storage**: Applies updates atomically to `memory.json` with cache invalidation (mtime-based)
|
||||
5. **Injection**: Next interaction loads memory, formats top 15 facts + context, injects into `<memory>` tags in system prompt
|
||||
|
||||
**Memory API Endpoints** (`/api/memory`):
|
||||
- `GET /api/memory` - Retrieve current memory data
|
||||
- `POST /api/memory/reload` - Force reload from file (invalidates cache)
|
||||
- `GET /api/memory/config` - Get memory configuration
|
||||
- `GET /api/memory/status` - Get both config and data
|
||||
|
||||
### Config Schema
|
||||
|
||||
@@ -215,6 +268,15 @@ Models, tools, sandbox providers, skills, and middleware settings are configured
|
||||
- `skills.container_path`: Container mount path (default: `/mnt/skills`)
|
||||
- `title`: Automatic thread title generation configuration
|
||||
- `summarization`: Automatic conversation summarization configuration
|
||||
- `memory`: Memory system configuration
|
||||
- `enabled`: Master switch (boolean)
|
||||
- `storage_path`: Path to memory.json file (relative to backend/)
|
||||
- `debounce_seconds`: Wait time before processing updates (default: 30)
|
||||
- `model_name`: LLM model for memory updates (null = use default model)
|
||||
- `max_facts`: Maximum facts to store (default: 100)
|
||||
- `fact_confidence_threshold`: Minimum confidence to store fact (default: 0.7)
|
||||
- `injection_enabled`: Inject memory into system prompt (boolean)
|
||||
- `max_injection_tokens`: Token limit for memory injection (default: 2000)
|
||||
|
||||
**Extensions Configuration Schema** (`extensions_config.json`):
|
||||
- `mcpServers`: Map of MCP server name to configuration
|
||||
@@ -307,6 +369,29 @@ For models with `supports_vision: true`:
|
||||
- `view_image_tool` added to agent's toolset
|
||||
- Images automatically converted and injected into state
|
||||
|
||||
### Memory System
|
||||
|
||||
Persistent context retention and personalization across conversations:
|
||||
- **Automatic Extraction**: LLM analyzes conversations to extract user context, facts, and preferences
|
||||
- **Structured Storage**: Maintains user context, history, and confidence-scored facts in JSON format
|
||||
- **Smart Filtering**: Only processes meaningful messages (user inputs + final AI responses)
|
||||
- **Debounced Updates**: Batches updates to minimize LLM calls (configurable wait time)
|
||||
- **System Prompt Injection**: Automatically injects relevant memory context into agent prompts
|
||||
- **Cache Optimization**: File modification time-based cache invalidation for external edits
|
||||
- **Thread Safety**: Locks protect queue and cache for concurrent access
|
||||
- **REST API**: Full CRUD operations via `/api/memory` endpoints
|
||||
- **Frontend Integration**: Memory settings page for viewing and managing memory data
|
||||
|
||||
**Configuration**: Controlled via `memory` section in `config.yaml`
|
||||
- Enable/disable memory system
|
||||
- Configure storage path, debounce timing, fact limits
|
||||
- Control system prompt injection and token limits
|
||||
- Set confidence thresholds for fact storage
|
||||
|
||||
**Storage Location**: `backend/.deer-flow/memory.json`
|
||||
|
||||
See configuration section for detailed settings.
|
||||
|
||||
## Code Style
|
||||
|
||||
- Uses `ruff` for linting and formatting
|
||||
|
||||
238
backend/docs/APPLE_CONTAINER.md
Normal file
238
backend/docs/APPLE_CONTAINER.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# Apple Container Support
|
||||
|
||||
DeerFlow now supports Apple Container as the preferred container runtime on macOS, with automatic fallback to Docker.
|
||||
|
||||
## Overview
|
||||
|
||||
Starting with this version, DeerFlow automatically detects and uses Apple Container on macOS when available, falling back to Docker when:
|
||||
- Apple Container is not installed
|
||||
- Running on non-macOS platforms
|
||||
|
||||
This provides better performance on Apple Silicon Macs while maintaining compatibility across all platforms.
|
||||
|
||||
## Benefits
|
||||
|
||||
### On Apple Silicon Macs with Apple Container:
|
||||
- **Better Performance**: Native ARM64 execution without Rosetta 2 translation
|
||||
- **Lower Resource Usage**: Lighter weight than Docker Desktop
|
||||
- **Native Integration**: Uses macOS Virtualization.framework
|
||||
|
||||
### Fallback to Docker:
|
||||
- Full backward compatibility
|
||||
- Works on all platforms (macOS, Linux, Windows)
|
||||
- No configuration changes needed
|
||||
|
||||
## Requirements
|
||||
|
||||
### For Apple Container (macOS only):
|
||||
- macOS 15.0 or later
|
||||
- Apple Silicon (M1/M2/M3/M4)
|
||||
- Apple Container CLI installed
|
||||
|
||||
### Installation:
|
||||
```bash
|
||||
# Download from GitHub releases
|
||||
# https://github.com/apple/container/releases
|
||||
|
||||
# Verify installation
|
||||
container --version
|
||||
|
||||
# Start the service
|
||||
container system start
|
||||
```
|
||||
|
||||
### For Docker (all platforms):
|
||||
- Docker Desktop or Docker Engine
|
||||
|
||||
## How It Works
|
||||
|
||||
### Automatic Detection
|
||||
|
||||
The `AioSandboxProvider` automatically detects the available container runtime:
|
||||
|
||||
1. On macOS: Try `container --version`
|
||||
- Success → Use Apple Container
|
||||
- Failure → Fall back to Docker
|
||||
|
||||
2. On other platforms: Use Docker directly
|
||||
|
||||
### Runtime Differences
|
||||
|
||||
Both runtimes use nearly identical command syntax:
|
||||
|
||||
**Container Startup:**
|
||||
```bash
|
||||
# Apple Container
|
||||
container run --rm -d -p 8080:8080 -v /host:/container -e KEY=value image
|
||||
|
||||
# Docker
|
||||
docker run --rm -d -p 8080:8080 -v /host:/container -e KEY=value image
|
||||
```
|
||||
|
||||
**Container Cleanup:**
|
||||
```bash
|
||||
# Apple Container (with --rm flag)
|
||||
container stop <id> # Auto-removes due to --rm
|
||||
|
||||
# Docker (with --rm flag)
|
||||
docker stop <id> # Auto-removes due to --rm
|
||||
```
|
||||
|
||||
### Implementation Details
|
||||
|
||||
The implementation is in `backend/src/community/aio_sandbox/aio_sandbox_provider.py`:
|
||||
|
||||
- `_detect_container_runtime()`: Detects available runtime at startup
|
||||
- `_start_container()`: Uses detected runtime, skips Docker-specific options for Apple Container
|
||||
- `_stop_container()`: Uses appropriate stop command for the runtime
|
||||
|
||||
## Configuration
|
||||
|
||||
No configuration changes are needed! The system works automatically.
|
||||
|
||||
However, you can verify the runtime in use by checking the logs:
|
||||
|
||||
```
|
||||
INFO:src.community.aio_sandbox.aio_sandbox_provider:Detected Apple Container: container version 0.1.0
|
||||
INFO:src.community.aio_sandbox.aio_sandbox_provider:Starting sandbox container using container: ...
|
||||
```
|
||||
|
||||
Or for Docker:
|
||||
```
|
||||
INFO:src.community.aio_sandbox.aio_sandbox_provider:Apple Container not available, falling back to Docker
|
||||
INFO:src.community.aio_sandbox.aio_sandbox_provider:Starting sandbox container using docker: ...
|
||||
```
|
||||
|
||||
## Container Images
|
||||
|
||||
Both runtimes use OCI-compatible images. The default image works with both:
|
||||
|
||||
```yaml
|
||||
sandbox:
|
||||
use: src.community.aio_sandbox:AioSandboxProvider
|
||||
image: enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest # Default image
|
||||
```
|
||||
|
||||
Make sure your images are available for the appropriate architecture:
|
||||
- ARM64 for Apple Container on Apple Silicon
|
||||
- AMD64 for Docker on Intel Macs
|
||||
- Multi-arch images work on both
|
||||
|
||||
### Pre-pulling Images (Recommended)
|
||||
|
||||
**Important**: Container images are typically large (500MB+) and are pulled on first use, which can cause a long wait time without clear feedback.
|
||||
|
||||
**Best Practice**: Pre-pull the image during setup:
|
||||
|
||||
```bash
|
||||
# From project root
|
||||
make setup-sandbox
|
||||
```
|
||||
|
||||
This command will:
|
||||
1. Read the configured image from `config.yaml` (or use default)
|
||||
2. Detect available runtime (Apple Container or Docker)
|
||||
3. Pull the image with progress indication
|
||||
4. Verify the image is ready for use
|
||||
|
||||
**Manual pre-pull**:
|
||||
|
||||
```bash
|
||||
# Using Apple Container
|
||||
container pull enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest
|
||||
|
||||
# Using Docker
|
||||
docker pull enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest
|
||||
```
|
||||
|
||||
If you skip pre-pulling, the image will be automatically pulled on first agent execution, which may take several minutes depending on your network speed.
|
||||
|
||||
## Cleanup Scripts
|
||||
|
||||
The project includes a unified cleanup script that handles both runtimes:
|
||||
|
||||
**Script:** `scripts/cleanup-containers.sh`
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Clean up all DeerFlow sandbox containers
|
||||
./scripts/cleanup-containers.sh deer-flow-sandbox
|
||||
|
||||
# Custom prefix
|
||||
./scripts/cleanup-containers.sh my-prefix
|
||||
```
|
||||
|
||||
**Makefile Integration:**
|
||||
|
||||
All cleanup commands in `Makefile` automatically handle both runtimes:
|
||||
```bash
|
||||
make stop # Stops all services and cleans up containers
|
||||
make clean # Full cleanup including logs
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Test the container runtime detection:
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
python test_container_runtime.py
|
||||
```
|
||||
|
||||
This will:
|
||||
1. Detect the available runtime
|
||||
2. Optionally start a test container
|
||||
3. Verify connectivity
|
||||
4. Clean up
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Apple Container not detected on macOS
|
||||
|
||||
1. Check if installed:
|
||||
```bash
|
||||
which container
|
||||
container --version
|
||||
```
|
||||
|
||||
2. Check if service is running:
|
||||
```bash
|
||||
container system start
|
||||
```
|
||||
|
||||
3. Check logs for detection:
|
||||
```bash
|
||||
# Look for detection message in application logs
|
||||
grep "container runtime" logs/*.log
|
||||
```
|
||||
|
||||
### Containers not cleaning up
|
||||
|
||||
1. Manually check running containers:
|
||||
```bash
|
||||
# Apple Container
|
||||
container list
|
||||
|
||||
# Docker
|
||||
docker ps
|
||||
```
|
||||
|
||||
2. Run cleanup script manually:
|
||||
```bash
|
||||
./scripts/cleanup-containers.sh deer-flow-sandbox
|
||||
```
|
||||
|
||||
### Performance issues
|
||||
|
||||
- Apple Container should be faster on Apple Silicon
|
||||
- If experiencing issues, you can force Docker by temporarily renaming the `container` command:
|
||||
```bash
|
||||
# Temporary workaround - not recommended for permanent use
|
||||
sudo mv /opt/homebrew/bin/container /opt/homebrew/bin/container.bak
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [Apple Container GitHub](https://github.com/apple/container)
|
||||
- [Apple Container Documentation](https://github.com/apple/container/blob/main/docs/)
|
||||
- [OCI Image Spec](https://github.com/opencontainers/image-spec)
|
||||
@@ -49,6 +49,22 @@ The backend searches for `config.yaml` in this order:
|
||||
|
||||
**Recommended**: Place `config.yaml` in project root (`deer-flow/config.yaml`).
|
||||
|
||||
## Sandbox Setup (Optional but Recommended)
|
||||
|
||||
If you plan to use Docker/Container-based sandbox (configured in `config.yaml` under `sandbox.use: src.community.aio_sandbox:AioSandboxProvider`), it's highly recommended to pre-pull the container image:
|
||||
|
||||
```bash
|
||||
# From project root
|
||||
make setup-sandbox
|
||||
```
|
||||
|
||||
**Why pre-pull?**
|
||||
- The sandbox image (~500MB+) is pulled on first use, causing a long wait
|
||||
- Pre-pulling provides clear progress indication
|
||||
- Avoids confusion when first using the agent
|
||||
|
||||
If you skip this step, the image will be automatically pulled on first agent execution, which may take several minutes depending on your network speed.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Config file not found
|
||||
|
||||
@@ -32,14 +32,17 @@ IDLE_CHECK_INTERVAL = 60 # Check every 60 seconds
|
||||
|
||||
|
||||
class AioSandboxProvider(SandboxProvider):
|
||||
"""Sandbox provider that manages Docker containers running the AIO sandbox.
|
||||
"""Sandbox provider that manages containers running the AIO sandbox.
|
||||
|
||||
On macOS, automatically prefers Apple Container if available, otherwise falls back to Docker.
|
||||
On other platforms, uses Docker.
|
||||
|
||||
Configuration options in config.yaml under sandbox:
|
||||
use: src.community.aio_sandbox:AioSandboxProvider
|
||||
image: enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest # Docker image to use
|
||||
image: enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest # Container image to use (works with both runtimes)
|
||||
port: 8080 # Base port for sandbox containers
|
||||
base_url: http://localhost:8080 # If set, uses existing sandbox instead of starting new container
|
||||
auto_start: true # Whether to automatically start Docker container
|
||||
auto_start: true # Whether to automatically start container
|
||||
container_prefix: deer-flow-sandbox # Prefix for container names
|
||||
idle_timeout: 600 # Idle timeout in seconds (default: 600 = 10 minutes). Set to 0 to disable.
|
||||
mounts: # List of volume mounts
|
||||
@@ -62,6 +65,7 @@ class AioSandboxProvider(SandboxProvider):
|
||||
self._shutdown_called = False
|
||||
self._idle_checker_stop = threading.Event()
|
||||
self._idle_checker_thread: threading.Thread | None = None
|
||||
self._container_runtime = self._detect_container_runtime()
|
||||
|
||||
# Register shutdown handler to clean up containers on exit
|
||||
atexit.register(self.shutdown)
|
||||
@@ -184,6 +188,35 @@ class AioSandboxProvider(SandboxProvider):
|
||||
resolved[key] = str(value)
|
||||
return resolved
|
||||
|
||||
def _detect_container_runtime(self) -> str:
|
||||
"""Detect which container runtime to use.
|
||||
|
||||
On macOS, prefer Apple Container if available, otherwise fall back to Docker.
|
||||
On other platforms, use Docker.
|
||||
|
||||
Returns:
|
||||
"container" for Apple Container, "docker" for Docker.
|
||||
"""
|
||||
import platform
|
||||
|
||||
# Only try Apple Container on macOS
|
||||
if platform.system() == "Darwin":
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["container", "--version"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
timeout=5,
|
||||
)
|
||||
logger.info(f"Detected Apple Container: {result.stdout.strip()}")
|
||||
return "container"
|
||||
except (FileNotFoundError, subprocess.CalledProcessError, subprocess.TimeoutExpired):
|
||||
logger.info("Apple Container not available, falling back to Docker")
|
||||
|
||||
# Default to Docker
|
||||
return "docker"
|
||||
|
||||
def _is_sandbox_ready(self, base_url: str, timeout: int = 30) -> bool:
|
||||
"""Check if sandbox is ready to accept connections.
|
||||
|
||||
@@ -253,7 +286,10 @@ class AioSandboxProvider(SandboxProvider):
|
||||
return None
|
||||
|
||||
def _start_container(self, sandbox_id: str, port: int, extra_mounts: list[tuple[str, str, bool]] | None = None) -> str:
|
||||
"""Start a new Docker container for the sandbox.
|
||||
"""Start a new container for the sandbox.
|
||||
|
||||
On macOS, prefers Apple Container if available, otherwise uses Docker.
|
||||
On other platforms, uses Docker.
|
||||
|
||||
Args:
|
||||
sandbox_id: Unique identifier for the sandbox.
|
||||
@@ -267,17 +303,22 @@ class AioSandboxProvider(SandboxProvider):
|
||||
container_name = f"{self._config['container_prefix']}-{sandbox_id}"
|
||||
|
||||
cmd = [
|
||||
"docker",
|
||||
self._container_runtime,
|
||||
"run",
|
||||
"--security-opt",
|
||||
"seccomp=unconfined",
|
||||
]
|
||||
|
||||
# Add Docker-specific security options
|
||||
if self._container_runtime == "docker":
|
||||
cmd.extend(["--security-opt", "seccomp=unconfined"])
|
||||
|
||||
cmd.extend([
|
||||
"--rm",
|
||||
"-d",
|
||||
"-p",
|
||||
f"{port}:8080",
|
||||
"--name",
|
||||
container_name,
|
||||
]
|
||||
])
|
||||
|
||||
# Add configured environment variables
|
||||
for key, value in self._config["environment"].items():
|
||||
@@ -303,26 +344,28 @@ class AioSandboxProvider(SandboxProvider):
|
||||
|
||||
cmd.append(image)
|
||||
|
||||
logger.info(f"Starting sandbox container: {' '.join(cmd)}")
|
||||
logger.info(f"Starting sandbox container using {self._container_runtime}: {' '.join(cmd)}")
|
||||
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||
container_id = result.stdout.strip()
|
||||
logger.info(f"Started sandbox container {container_name} with ID {container_id}")
|
||||
logger.info(f"Started sandbox container {container_name} with ID {container_id} using {self._container_runtime}")
|
||||
return container_id
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"Failed to start sandbox container: {e.stderr}")
|
||||
logger.error(f"Failed to start sandbox container using {self._container_runtime}: {e.stderr}")
|
||||
raise RuntimeError(f"Failed to start sandbox container: {e.stderr}")
|
||||
|
||||
def _stop_container(self, container_id: str) -> None:
|
||||
"""Stop and remove a Docker container.
|
||||
"""Stop and remove a container.
|
||||
|
||||
Since we use --rm flag, the container is automatically removed after stopping.
|
||||
|
||||
Args:
|
||||
container_id: The container ID to stop.
|
||||
"""
|
||||
try:
|
||||
subprocess.run(["docker", "stop", container_id], capture_output=True, text=True, check=True)
|
||||
logger.info(f"Stopped sandbox container {container_id}")
|
||||
subprocess.run([self._container_runtime, "stop", container_id], capture_output=True, text=True, check=True)
|
||||
logger.info(f"Stopped sandbox container {container_id} using {self._container_runtime} (--rm will auto-remove)")
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.warning(f"Failed to stop sandbox container {container_id}: {e.stderr}")
|
||||
|
||||
|
||||
@@ -144,17 +144,20 @@ tools:
|
||||
sandbox:
|
||||
use: src.sandbox.local:LocalSandboxProvider
|
||||
|
||||
# Option 2: Docker-based AIO Sandbox
|
||||
# Executes commands in isolated Docker containers
|
||||
# Option 2: Container-based AIO Sandbox
|
||||
# Executes commands in isolated containers (Docker or Apple Container)
|
||||
# On macOS: Automatically prefers Apple Container if available, falls back to Docker
|
||||
# On other platforms: Uses Docker
|
||||
# Uncomment to use:
|
||||
# sandbox:
|
||||
# use: src.community.aio_sandbox:AioSandboxProvider
|
||||
#
|
||||
# # Optional: Use existing sandbox at this URL (no Docker container will be started)
|
||||
# # Optional: Use existing sandbox at this URL (no container will be started)
|
||||
# # base_url: http://localhost:8080
|
||||
#
|
||||
# # Optional: Docker image to use
|
||||
# # Optional: Container image to use (works with both Docker and Apple Container)
|
||||
# # Default: enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest
|
||||
# # Recommended: enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest (works on both x86_64 and arm64)
|
||||
# # image: enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest
|
||||
#
|
||||
# # Optional: Base port for sandbox containers (default: 8080)
|
||||
|
||||
96
scripts/cleanup-containers.sh
Executable file
96
scripts/cleanup-containers.sh
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# cleanup-containers.sh - Clean up DeerFlow sandbox containers
|
||||
#
|
||||
# This script cleans up both Docker and Apple Container runtime containers
|
||||
# to ensure compatibility across different container runtimes.
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
PREFIX="${1:-deer-flow-sandbox}"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo "Cleaning up sandbox containers with prefix: ${PREFIX}"
|
||||
|
||||
# Function to clean up Docker containers
|
||||
cleanup_docker() {
|
||||
if command -v docker &> /dev/null; then
|
||||
echo -n "Checking Docker containers... "
|
||||
DOCKER_CONTAINERS=$(docker ps -q --filter "name=${PREFIX}" 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$DOCKER_CONTAINERS" ]; then
|
||||
echo ""
|
||||
echo "Found Docker containers to clean up:"
|
||||
docker ps --filter "name=${PREFIX}" --format "table {{.ID}}\t{{.Names}}\t{{.Status}}"
|
||||
echo "Stopping Docker containers..."
|
||||
echo "$DOCKER_CONTAINERS" | xargs docker stop 2>/dev/null || true
|
||||
echo -e "${GREEN}✓ Docker containers stopped${NC}"
|
||||
else
|
||||
echo -e "${GREEN}none found${NC}"
|
||||
fi
|
||||
else
|
||||
echo "Docker not found, skipping..."
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to clean up Apple Container containers
|
||||
cleanup_apple_container() {
|
||||
if command -v container &> /dev/null; then
|
||||
echo -n "Checking Apple Container containers... "
|
||||
|
||||
# List all containers and filter by name
|
||||
CONTAINER_LIST=$(container list --format json 2>/dev/null || echo "[]")
|
||||
|
||||
if [ "$CONTAINER_LIST" != "[]" ] && [ -n "$CONTAINER_LIST" ]; then
|
||||
# Extract container IDs that match our prefix
|
||||
CONTAINER_IDS=$(echo "$CONTAINER_LIST" | python3 -c "
|
||||
import json
|
||||
import sys
|
||||
try:
|
||||
containers = json.load(sys.stdin)
|
||||
if isinstance(containers, list):
|
||||
for c in containers:
|
||||
if isinstance(c, dict):
|
||||
name = c.get('name', '')
|
||||
cid = c.get('id', '')
|
||||
if '${PREFIX}' in name and cid:
|
||||
print(cid)
|
||||
except:
|
||||
pass
|
||||
" 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$CONTAINER_IDS" ]; then
|
||||
echo ""
|
||||
echo "Found Apple Container containers to clean up:"
|
||||
echo "$CONTAINER_IDS" | while read -r cid; do
|
||||
echo " - $cid"
|
||||
done
|
||||
|
||||
echo "Stopping Apple Container containers..."
|
||||
echo "$CONTAINER_IDS" | while read -r cid; do
|
||||
container stop "$cid" 2>/dev/null || true
|
||||
container delete "$cid" 2>/dev/null || true
|
||||
done
|
||||
echo -e "${GREEN}✓ Apple Container containers stopped${NC}"
|
||||
else
|
||||
echo -e "${GREEN}none found${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${GREEN}none found${NC}"
|
||||
fi
|
||||
else
|
||||
echo "Apple Container not found, skipping..."
|
||||
fi
|
||||
}
|
||||
|
||||
# Clean up both runtimes
|
||||
cleanup_docker
|
||||
cleanup_apple_container
|
||||
|
||||
echo -e "${GREEN}✓ Container cleanup complete${NC}"
|
||||
Reference in New Issue
Block a user