agentops/app/deploy/AGENTSTACK_JOCKEY_INTEGRATI...

44 KiB

AgentStack-Jockey Integration: Complete Discovery & Implementation Plan

Executive Summary

This document outlines the complete discovery findings and implementation plan for integrating AgentStack with Jockey to enable automatic detection, deployment, and HTTP API generation for CrewAI projects in Kubernetes environments.

Discovery Findings

AgentStack Architecture Analysis

Core Serve Module Structure

File: /Users/tcdent/Work/AgentOps.Next/deploy/AgentStack/agentstack/serve/serve.py

The AgentStack serve module implements a Flask-based API server with these key components:

  • ProjectServer Class: Main Flask application wrapper with WebSocket support
  • Core Routes:
    • GET / - Serves UI (index.html from project path)
    • GET /health - Health check endpoint
    • POST /process - Main endpoint for agent execution with webhook callbacks
    • WebSocket /ws - Real-time chat interface for streaming responses

Key Implementation Details:

class ProjectServer:
    app: Flask
    sock: Sock
    webhook_url: Optional[str] = None
    
    def process_request(self):
        # Validates webhook URL and inputs
        # Calls run_project() with API inputs
        # Sends results to webhook

Project Detection Mechanisms

File: /Users/tcdent/Work/AgentOps.Next/deploy/AgentStack/agentstack/frameworks/crewai.py

Enhanced Dynamic Detection Pattern:

def get_entrypoint() -> CrewFile:
    """
    Get the CrewAI entrypoint file.
    Uses dynamic detection to find actual crew file if default doesn't exist.
    """
    # Try the default entrypoint first
    default_path = conf.PATH / ENTRYPOINT
    if default_path.exists():
        return CrewFile(default_path)

    # If default doesn't exist, use dynamic detection
    crew_file_path = _find_crew_file_dynamically(conf.PATH)
    if crew_file_path:
        return CrewFile(crew_file_path)

    raise ValidationError(f"No CrewAI crew file found in {conf.PATH}.")

def _find_crew_file_dynamically(project_path: Path) -> Optional[Path]:
    """
    Dynamically find the CrewAI file using AST analysis.
    Searches Python files in priority order, excluding virtual environments.
    """

Detection Criteria (Enhanced):

  1. Primary: agentstack.json exists with framework specification
  2. Secondary: AST-based detection of CrewAI patterns:
    • CrewAI imports (from crewai import Crew, Agent, Task)
    • Crew() constructor calls
    • .kickoff() method invocations
  3. Prioritized file search:
    • Common paths: src/crew.py, crew.py, main.py, src/main.py
    • Files with "crew" in name
    • All Python files (excluding venv, site-packages, etc.)

CrewAI Project Structure Analysis

File: /Users/tcdent/Work/AgentOps.Next/deploy/AgentStack/agentstack/frameworks/crewai.py

Supported CrewAI Project Patterns:

1. AgentStack Template Projects:

project_root/
├── agentstack.json          # Contains {"framework": "crewai"}
├── pyproject.toml           # Python dependencies
├── src/
│   ├── main.py             # Entry point with run() function
│   ├── crew.py             # CrewAI-specific crew definition (@CrewBase)
│   ├── config/
│   │   ├── agents.yaml     # Agent configurations
│   │   ├── tasks.yaml      # Task configurations
│   │   └── inputs.yaml     # Input parameters
│   └── tools/
│       └── __init__.py     # Tool definitions

2. Native CrewAI Projects (Now Supported):

project_root/
├── main.py                 # Contains crew creation and kickoff()
├── pyproject.toml          # Python dependencies
└── requirements.txt        # Alternative dependency format

3. Complex Native Projects:

project_root/
├── simple_crew.py          # Minimal crew implementation
├── template_crew.py        # @CrewBase template pattern
├── market_research.py      # Domain-specific crew
└── utils/
    └── helpers.py

Enhanced Code Pattern Detection:

def _check_file_for_crew_patterns(file_path: Path) -> bool:
    """
    Check if a specific file contains CrewAI patterns.
    Returns True if crew patterns are found (covers both template and native patterns).
    """
    # Check for CrewAI imports
    all_imports = asttools.get_all_imports(tree)
    has_crewai_imports = any(
        import_node.module and import_node.module.startswith('crewai')
        for import_node in all_imports
        if import_node.names and any(
            alias.name in ['Crew', 'Agent', 'Task'] 
            for alias in import_node.names
        )
    )

    # Check for Crew instantiation patterns
    for node in ast.walk(tree):
        if isinstance(node, ast.Call):
            if isinstance(node.func, ast.Name) and node.func.id == 'Crew':
                return True

    # Check for .kickoff() calls which strongly indicate crew usage
    kickoff_calls = asttools.find_kickoff_calls(tree)
    return len(kickoff_calls) > 0

Project Execution Flow

File: /Users/tcdent/Work/AgentOps.Next/deploy/AgentStack/agentstack/run.py

Dynamic Module Loading:

def _import_project_module(path: Path):
    spec = importlib.util.spec_from_file_location(MAIN_MODULE_NAME, str(path / MAIN_FILENAME))
    project_module = importlib.util.module_from_spec(spec)
    sys.path.insert(0, str((path / MAIN_FILENAME).parent))
    spec.loader.exec_module(project_module)
    return project_module

def run_project(command: str = 'run', api_inputs: Optional[dict[str, str]] = None):
    """Run a user project by importing and executing main.py"""
    project_main = _import_project_module(conf.PATH)
    main = getattr(project_main, command)  # Get the 'run' function
    
    if asyncio.iscoroutinefunction(main):
        asyncio.run(main())
    else:
        main()  # Calls user's run() function which triggers crew.kickoff()

Input Management System

File: /Users/tcdent/Work/AgentOps.Next/deploy/AgentStack/agentstack/inputs.py

Input Override Pattern:

def run_project(command: str = 'run', api_inputs: Optional[dict[str, str]] = None):
    run.preflight()
    if api_inputs:
        for key, value in api_inputs.items():
            inputs.add_input_for_run(key, value)
    run.run_project(command=command)

Input Flow:

  1. Static inputs loaded from src/config/inputs.yaml
  2. API inputs override static inputs during execution
  3. Inputs passed to crew via agentstack.get_inputs()

Current Jockey Architecture

Image Building System

File: /Users/tcdent/Work/AgentOps.Next/deploy/jockey/backend/models/image.py

Current Image Model:

@dataclass
class Image:
    name: str
    namespace: str
    tag: str = "latest"
    dockerfile_template: str = "python-agent"
    dockerfile_vars: Dict[str, str] = field(default_factory=dict)
    build_context: Optional[str] = None
    repository_name: Optional[str] = None

    def _get_docker_client(self) -> docker.DockerClient:
        """Get Docker client, using DOCKER_HOST if set, otherwise from environment."""
        if DOCKER_HOST:
            return docker.DockerClient(base_url=DOCKER_HOST)
        return docker.from_env()

    def generate_dockerfile(self) -> str:
        """Generate a Dockerfile using the specified template from templates/docker/ directory."""
        template_vars = {
            'base_image': 'python:3.12-slim-bookworm',
            'requirements_file': 'pyproject.toml',
            'install_agentstack': True,
            'agentstack_branch': 'deploy-command',
            'port': 6969,
            'run_command': ["/app/.venv/bin/agentstack", "run"],
            'repository_name': self.repository_name,
        }
        template_vars.update(self.dockerfile_vars)
        template_path = f"docker/{self.dockerfile_template}.j2"
        return render_template(template_path, template_vars)

Template System

File: /Users/tcdent/Work/AgentOps.Next/deploy/jockey/template.py

Template Rendering:

TEMPLATES_DIR = Path(__file__).parent / 'templates'

def render_template(template_path: str, template_vars: Dict[str, Any]) -> str:
    """Render a template with the given variables."""
    full_template_path = TEMPLATES_DIR / template_path
    env = Environment(loader=FileSystemLoader(str(TEMPLATES_DIR)))
    template = env.get_template(template_path)
    return template.render(**template_vars)

Repository Management

File: /Users/tcdent/Work/AgentOps.Next/deploy/jockey/backend/models/repository.py

Current Repository Model Structure:

@dataclass
class Repository:
    url: str
    namespace: str
    commit_hash: Optional[str] = None
    branch: str = "main"

    def clone(self) -> Generator[RepositoryEvent, None, str]:
        # Clone repository and yield events
        # Returns local path after successful clone

API Layer

File: /Users/tcdent/Work/AgentOps.Next/deploy/jockey/api.py

Current Deployment Flow:

def build_and_deploy_with_events(config: DeploymentConfig) -> Generator[Union[BuildEvent, PushEvent, DeploymentEvent, Deployment], None, Deployment]:
    # 1. Clone repository if URL provided
    # 2. Build Docker image
    # 3. Push to registry
    # 4. Deploy to Kubernetes

Implementation Plan

Phase 1: AgentStack Modifications

1.1 Create Deployment-Optimized Serve Module

New File: AgentStack/agentstack/serve/deployment_serve.py

"""Production-ready server for containerized deployments."""

class DeploymentServer(ProjectServer):
    """Headless server optimized for Kubernetes deployment."""
    
    def __init__(self):
        super().__init__()
        self.configure_for_deployment()
    
    def configure_for_deployment(self):
        """Configure server for production container deployment."""
        self.app.config['ENV'] = 'production'
        self.app.config['DEBUG'] = False
        # Remove UI serving capabilities
        # Enhanced health checks for K8s
        
    def register_routes(self):
        """Register only API endpoints, no UI."""
        self.app.add_url_rule('/health', 'health', self.health_check, methods=['GET'])
        self.app.add_url_rule('/ready', 'ready', self.readiness_check, methods=['GET'])
        self.app.add_url_rule('/process', 'process', self.process_request, methods=['POST'])
        # No WebSocket routes for simplified deployment
        
    def readiness_check(self):
        """Kubernetes readiness probe endpoint."""
        try:
            # Use enhanced crew detection to validate deployment readiness
            from agentstack.frameworks.crewai import get_entrypoint
            from agentstack import conf
            
            # Validate crew file can be found and loaded
            crew_file = get_entrypoint()
            if not crew_file:
                raise Exception("No crew entrypoint found")
                
            # Validate project structure
            if not conf.PATH.exists():
                raise Exception("Project path not found")
                
            # Check required dependencies are available
            try:
                import crewai
            except ImportError:
                raise Exception("CrewAI not installed")
                
            return {'status': 'ready', 'crew_file': str(crew_file.path), 'timestamp': time.time()}
        except Exception as e:
            return {'status': 'not_ready', 'error': str(e)}, 503
            
    def validate_crew_structure(self):
        """Enhanced validation using dynamic crew detection."""
        try:
            crew_file = get_entrypoint()
            
            # Validate crew file has required patterns
            if hasattr(crew_file, 'get_base_class'):
                # AgentStack template project
                base_class = crew_file.get_base_class()
                agents = crew_file.get_agent_methods()
                tasks = crew_file.get_task_methods()
                
                if not agents or not tasks:
                    raise Exception("Crew missing required agents or tasks")
                    
            return True
        except Exception as e:
            raise Exception(f"Crew validation failed: {e}")

1.2 Enhanced Project Analysis

New File: AgentStack/agentstack/detection/deployment_analyzer.py

"""Advanced project analysis for deployment optimization."""

@dataclass
class DeploymentAnalysis:
    project_info: ProjectInfo
    dependencies: List[str]
    tools: List[str]
    estimated_memory: str  # e.g., "512Mi"
    estimated_cpu: str     # e.g., "500m" 
    required_env_vars: List[str]
    health_check_path: str
    api_endpoints: List[str]
    
def analyze_for_deployment(project_path: str) -> DeploymentAnalysis:
    """Comprehensive analysis for deployment planning."""
    # Parse agentstack.json for deployment config
    # Analyze pyproject.toml for dependencies
    # Scan tools directory for custom tools
    # Estimate resource requirements based on complexity
    # Identify required environment variables
    # Generate API endpoint specifications

1.3 CLI Enhancements

Modify File: AgentStack/agentstack/cli/main.py

# New command
@cli.command()
@click.option('--format', default='jockey', help='Export format')
@click.option('--output', help='Output file path')
def export_deployment(format, output):
    """Export project configuration for deployment platforms."""
    if format == 'jockey':
        analysis = analyze_for_deployment('.')
        jockey_config = convert_to_jockey_format(analysis)
        save_deployment_config(jockey_config, output)

@cli.command()
def validate_deployment():
    """Validate project for containerized deployment."""
    # Check all deployment requirements
    # Validate configuration consistency
    # Test import capabilities

Phase 2: Jockey Integration

2.1 Project Detection System

New File: /Users/tcdent/Work/AgentOps.Next/deploy/jockey/backend/models/project_detector.py

"""Comprehensive project type detection for deployment optimization."""

from enum import Enum
from dataclasses import dataclass
from pathlib import Path
import json
import ast
import subprocess
import sys

class ProjectType(Enum):
    """Supported project frameworks."""
    CREWAI = "crewai"
    LANGGRAPH = "langgraph"
    LLAMAINDEX = "llamaindex"
    OPENAI_SWARM = "openai_swarm"
    GENERIC_PYTHON = "generic_python"
    UNKNOWN = "unknown"

@dataclass
class ProjectInfo:
    """Comprehensive project information."""
    project_type: ProjectType
    framework: Optional[str] = None
    agentstack_version: Optional[str] = None
    tools: List[str] = field(default_factory=list)
    has_config_files: bool = False
    entry_point: Optional[str] = None
    dockerfile_template: str = "python-agent"
    run_command: List[str] = field(default_factory=list)
    estimated_resources: Dict[str, str] = field(default_factory=dict)
    required_env_vars: Dict[str, str] = field(default_factory=dict)
    api_endpoints: List[str] = field(default_factory=list)
    crew_file_path: Optional[str] = None  # NEW: Track actual crew file location

def detect_project_type(repo_path: str) -> ProjectInfo:
    """Primary project detection entry point."""
    repo_path = Path(repo_path)
    
    # Primary: Check for agentstack.json
    agentstack_file = repo_path / "agentstack.json"
    if agentstack_file.exists():
        return _detect_agentstack_project(agentstack_file, repo_path)
    
    # Secondary: Use AgentStack's enhanced detection
    crew_info = _detect_crewai_with_agentstack(repo_path)
    if crew_info:
        return crew_info
    
    # Fallback: Generic Python
    return ProjectInfo(project_type=ProjectType.GENERIC_PYTHON)

def _detect_agentstack_project(agentstack_file: Path, repo_path: Path) -> ProjectInfo:
    """Detect and analyze AgentStack projects."""
    with open(agentstack_file, 'r') as f:
        config = json.load(f)
    
    framework = config.get('framework', '').lower()
    project_type = ProjectType(framework) if framework in [e.value for e in ProjectType] else ProjectType.UNKNOWN
    
    # Deep analysis for AgentStack projects
    return ProjectInfo(
        project_type=project_type,
        framework=framework,
        agentstack_version=config.get('agentstack_version'),
        tools=_detect_tools(repo_path),
        has_config_files=_validate_config_structure(repo_path, framework),
        entry_point=_find_entry_point(repo_path, framework),
        dockerfile_template=_select_dockerfile_template(framework),
        run_command=_generate_run_command(framework),
        estimated_resources=_estimate_resources(repo_path, framework),
        required_env_vars=_get_required_env_vars(framework),
        api_endpoints=_discover_api_endpoints(repo_path, framework)
    )

def _detect_crewai_with_agentstack(repo_path: Path) -> Optional[ProjectInfo]:
    """Use AgentStack's enhanced detection capabilities for CrewAI projects."""
    try:
        # Try to use AgentStack's dynamic detection by invoking it as a subprocess
        # This leverages all the new detection logic without code duplication
        result = subprocess.run([
            sys.executable, '-c', f'''
import sys
sys.path.insert(0, "/Users/tcdent/Work/AgentOps.Next/deploy/AgentStack")
from pathlib import Path
from agentstack.frameworks.crewai import _find_crew_file_dynamically
from agentstack import conf

# Temporarily set the path
original_path = conf.PATH
conf.PATH = Path("{repo_path}")

try:
    crew_file = _find_crew_file_dynamically(conf.PATH)
    if crew_file:
        print(f"FOUND:{crew_file}")
    else:
        print("NOT_FOUND")
finally:
    conf.PATH = original_path
            '''], 
            capture_output=True, 
            text=True, 
            timeout=30
        )
        
        if result.returncode == 0 and result.stdout.startswith("FOUND:"):
            crew_file_path = result.stdout.strip().replace("FOUND:", "")
            
            return ProjectInfo(
                project_type=ProjectType.CREWAI,
                framework="crewai",
                entry_point=crew_file_path,
                crew_file_path=crew_file_path,
                dockerfile_template="agentstack-crewai",
                run_command=["python", "-m", "agentstack.serve.deployment_serve"],
                estimated_resources={"memory": "512Mi", "cpu": "500m"},
                required_env_vars={
                    "PYTHONPATH": "/app/src:/app",
                    "CREWAI_TELEMETRY_OPT_OUT": "true",
                    "AGENTSTACK_DEPLOYMENT_MODE": "true"
                },
                api_endpoints=["/health", "/ready", "/process"],
                has_config_files=_validate_agentstack_config_structure(repo_path),
                tools=_detect_agentstack_tools(repo_path)
            )
            
    except (subprocess.TimeoutExpired, subprocess.CalledProcessError):
        pass
    
    # Fallback to simplified detection
    return _has_crewai_patterns_fallback(repo_path)

def _has_crewai_patterns_fallback(repo_path: Path) -> Optional[ProjectInfo]:
    """Fallback CrewAI detection using simplified patterns."""
    # Check common file locations for crew patterns
    potential_files = [
        repo_path / "main.py",
        repo_path / "crew.py", 
        repo_path / "simple_crew.py",
        repo_path / "src" / "crew.py",
        repo_path / "src" / "main.py"
    ] + list(repo_path.rglob("*crew*.py"))
    
    for file_path in potential_files:
        if not file_path.exists() or _should_skip_file(file_path):
            continue
            
        try:
            with open(file_path, 'r') as f:
                content = f.read()
                
            # Simple pattern matching
            if ('from crewai import' in content or 'import crewai' in content) and \
               ('.kickoff(' in content or 'Crew(' in content):
                return ProjectInfo(
                    project_type=ProjectType.CREWAI,
                    framework="crewai",
                    entry_point=str(file_path),
                    crew_file_path=str(file_path),
                    dockerfile_template="agentstack-crewai",
                    run_command=["python", str(file_path.relative_to(repo_path))],
                    estimated_resources={"memory": "512Mi", "cpu": "500m"},
                    required_env_vars={
                        "PYTHONPATH": "/app",
                        "CREWAI_TELEMETRY_OPT_OUT": "true"
                    },
                    api_endpoints=["/health", "/ready"]
                )
                
        except (SyntaxError, FileNotFoundError, UnicodeDecodeError):
            continue
    
    return None

def _should_skip_file(file_path: Path) -> bool:
    """Check if a file should be skipped during detection."""
    path_parts = file_path.parts
    skip_dirs = {'venv', '.venv', 'env', '.env', 'site-packages', 'node_modules', '.git', '__pycache__'}
    return any(part in skip_dirs for part in path_parts)

def _detect_tools(repo_path: Path) -> List[str]:
    """Detect custom tools in the project."""
    tools_dir = repo_path / "src" / "tools"
    if not tools_dir.exists():
        return []
    
    tools = []
    for tool_file in tools_dir.glob("*.py"):
        if tool_file.name != "__init__.py":
            tools.append(tool_file.stem)
    return tools

def _validate_config_structure(repo_path: Path, framework: str) -> bool:
    """Validate that required configuration files exist."""
    if framework == "crewai":
        config_dir = repo_path / "src" / "config"
        required_files = ["agents.yaml", "tasks.yaml"]
        return all((config_dir / f).exists() for f in required_files)
    return True

def _select_dockerfile_template(framework: str) -> str:
    """Select appropriate Dockerfile template based on framework."""
    template_map = {
        'crewai': 'agentstack-crewai',
        'langgraph': 'agentstack-langgraph',
        'llamaindex': 'agentstack-llamaindex',
        'openai_swarm': 'agentstack-openai-swarm'
    }
    return template_map.get(framework, 'python-agent')

def _generate_run_command(framework: str) -> List[str]:
    """Generate appropriate container run command."""
    if framework in ['crewai', 'langgraph', 'llamaindex', 'openai_swarm']:
        return ["python", "-m", "agentstack.serve.deployment_serve"]
    return ["/app/.venv/bin/python", "src/main.py"]

def _estimate_resources(repo_path: Path, framework: str) -> Dict[str, str]:
    """Estimate resource requirements based on project complexity."""
    base_memory = "256Mi"
    base_cpu = "250m"
    
    # Adjust based on framework
    if framework == "crewai":
        base_memory = "512Mi"
        base_cpu = "500m"
    
    # Adjust based on number of tools, agents, etc.
    tools_count = len(_detect_tools(repo_path))
    if tools_count > 3:
        base_memory = "1Gi"
        base_cpu = "750m"
    
    return {
        "memory": base_memory,
        "cpu": base_cpu
    }

def _get_required_env_vars(framework: str) -> Dict[str, str]:
    """Get framework-specific environment variables."""
    env_vars = {
        "PYTHONPATH": "/app/src:/app"
    }
    
    if framework == "crewai":
        env_vars.update({
            "CREWAI_TELEMETRY_OPT_OUT": "true",
            "AGENTSTACK_DEPLOYMENT_MODE": "true"
        })
    
    return env_vars

def _discover_api_endpoints(repo_path: Path, framework: str) -> List[str]:
    """Discover available API endpoints for the project."""
    base_endpoints = ["/health", "/ready"]
    
    if framework in ['crewai', 'langgraph', 'llamaindex', 'openai_swarm']:
        base_endpoints.append("/process")
    
    return base_endpoints

2.2 Enhanced Image Building

Modify File: /Users/tcdent/Work/AgentOps.Next/deploy/jockey/backend/models/image.py

# Add project_info to Image dataclass
@dataclass
class Image:
    name: str
    namespace: str
    tag: str = "latest"
    dockerfile_template: str = "python-agent"
    dockerfile_vars: Dict[str, str] = field(default_factory=dict)
    build_context: Optional[str] = None
    repository_name: Optional[str] = None
    project_info: Optional[ProjectInfo] = None  # NEW

    def generate_dockerfile(self) -> str:
        """Generate Dockerfile with project-aware optimizations."""
        # Base template variables
        template_vars = {
            'base_image': 'python:3.12-slim-bookworm',
            'requirements_file': 'pyproject.toml',
            'install_agentstack': False,
            'agentstack_branch': 'deploy-command',
            'port': 6969,
            'run_command': ["/app/.venv/bin/python", "src/main.py"],
            'repository_name': self.repository_name,
        }

        # Apply project-specific configurations
        if self.project_info:
            if self.project_info.project_type in [ProjectType.CREWAI, ProjectType.LANGGRAPH]:
                template_vars.update({
                    'install_agentstack': True,
                    'framework': self.project_info.framework,
                    'run_command': self.project_info.run_command,
                    'tools': self.project_info.tools,
                    'estimated_memory': self.project_info.estimated_resources.get('memory', '512Mi'),
                    'required_env_vars': self.project_info.required_env_vars
                })

        # Override with user-provided vars
        template_vars.update(self.dockerfile_vars)
        
        # Select template based on project info
        dockerfile_template = (
            self.project_info.dockerfile_template 
            if self.project_info 
            else self.dockerfile_template
        )
        
        template_path = f"docker/{dockerfile_template}.j2"
        return render_template(template_path, template_vars)

2.3 Repository Integration

Modify File: /Users/tcdent/Work/AgentOps.Next/deploy/jockey/backend/models/repository.py

# Add project detection to Repository class
from .project_detector import detect_project_type, ProjectInfo

@dataclass
class Repository:
    # ... existing fields ...
    _project_info: Optional[ProjectInfo] = field(default=None, init=False)

    @property
    def project_info(self) -> Optional[ProjectInfo]:
        """Get project information after cloning."""
        return self._project_info

    def clone(self) -> Generator[RepositoryEvent, None, str]:
        """Clone repository and detect project type."""
        # ... existing clone logic ...
        
        yield RepositoryEvent(
            EventStatus.COMPLETED,
            repository_url=self.url,
            local_path=self.local_path,
            commit_hash=repo.head.commit.hexsha,
            message=f"Repository cloned successfully to {self.local_path}",
        )
        
        # Detect project type after successful clone
        try:
            self._project_info = detect_project_type(self.local_path)
            yield RepositoryEvent(
                EventStatus.PROGRESS,
                message=f"Detected {self._project_info.project_type.value} project",
                repository_url=self.url
            )
        except Exception as e:
            yield RepositoryEvent(
                EventStatus.PROGRESS,
                message=f"Project detection failed: {e}",
                repository_url=self.url
            )
            self._project_info = None
        
        return self.local_path

2.4 New Dockerfile Templates

New File: /Users/tcdent/Work/AgentOps.Next/deploy/jockey/templates/docker/agentstack-crewai.j2

# AgentStack CrewAI Deployment Container
FROM {{ base_image | default('python:3.12-slim-bookworm') }}

# Metadata
LABEL framework="crewai"
LABEL agentstack.version="{{ agentstack_version | default('latest') }}"

# Set working directory
WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    gcc \
    build-essential \
    curl \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Install Python package managers
RUN pip install --no-cache-dir uv psutil

{% if install_agentstack %}
# Install AgentStack with deployment optimizations
RUN pip install git+https://github.com/AgentOps-AI/AgentStack.git@{{ agentstack_branch | default('deploy-command') }}
{% endif %}

# Copy requirements first for better caching
COPY {{ repository_name }}/{{ requirements_file | default('pyproject.toml') }} ./

# Create and activate virtual environment
RUN uv venv .venv
RUN .venv/bin/pip install --no-cache-dir -r {{ requirements_file | default('pyproject.toml') }}

# Install framework-specific dependencies
RUN .venv/bin/pip install crewai agentops python-dotenv

{% if tools %}
# Install additional tools if detected
{% for tool in tools %}
# Custom tool: {{ tool }}
{% endfor %}
{% endif %}

# Copy application code
COPY {{ repository_name }}/ ./

# Ensure proper directory structure
RUN mkdir -p src/config src/tools

# Set environment variables
ENV PYTHONPATH=/app/src:/app
{% for key, value in required_env_vars.items() %}
ENV {{ key }}="{{ value }}"
{% endfor %}

# Health checks for Kubernetes
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
    CMD curl -f http://localhost:{{ port | default(6969) }}/health || exit 1

# Expose application port  
EXPOSE {{ port | default(6969) }}

# Set resource hints for Kubernetes
LABEL resources.requests.memory="{{ estimated_memory | default('512Mi') }}"
LABEL resources.requests.cpu="250m"
LABEL resources.limits.memory="{{ estimated_memory | default('512Mi') }}"
LABEL resources.limits.cpu="500m"

# Start the deployment server
CMD {{ run_command | default('["python", "-m", "agentstack.serve.deployment_serve"]') | tojson }}

2.5 Enhanced API Layer

Modify File: /Users/tcdent/Work/AgentOps.Next/deploy/jockey/api.py

def build_and_deploy_with_events(config: DeploymentConfig) -> Generator[Union[BuildEvent, PushEvent, DeploymentEvent, Deployment], None, Deployment]:
    """Enhanced deployment with automatic project detection."""
    
    # Clone repository if URL provided
    repo_path = None
    repository_name = None
    project_info = None

    if config.repository_url:
        repository = Repository(
            url=config.repository_url,
            namespace=config.namespace,
            commit_hash=config.commit_hash,
            branch=config.branch,
        )
        
        # Yield repository events
        for event in repository.clone():
            if hasattr(event, 'event_type'):
                yield event
                
        repo_path = repository.local_path
        repository_name = repository.repository_name
        project_info = repository.project_info
        
        # Override configuration based on project detection
        if project_info:
            if project_info.dockerfile_template != "python-agent":
                config.dockerfile_template = project_info.dockerfile_template
                yield RepositoryEvent(
                    EventStatus.PROGRESS,
                    message=f"Using {project_info.dockerfile_template} template for {project_info.project_type.value} project"
                )

    # Create and build the image with project info
    image = Image(
        name=config.app_name,
        tag=config.version,
        namespace=config.namespace,
        build_context=repo_path,
        repository_name=repository_name,
        dockerfile_template=config.dockerfile_template,
        project_info=project_info,  # Pass project info to image builder
    )

    # ... rest of deployment logic remains the same

2.6 CLI Enhancements

Modify File: /Users/tcdent/Work/AgentOps.Next/deploy/jockey/__main__.py

@cli.command()
@click.argument('repo_path')
def detect_project(repo_path):
    """Analyze repository and detect project type."""
    try:
        from .backend.models.project_detector import detect_project_type
        
        project_info = detect_project_type(repo_path)
        
        click.echo(f"📋 Project Analysis Results:")
        click.echo(f"   Type: {project_info.project_type.value}")
        if project_info.framework:
            click.echo(f"   Framework: {project_info.framework}")
        if project_info.agentstack_version:
            click.echo(f"   AgentStack Version: {project_info.agentstack_version}")
        click.echo(f"   Dockerfile Template: {project_info.dockerfile_template}")
        click.echo(f"   Entry Point: {project_info.entry_point or 'Not detected'}")
        click.echo(f"   Config Files: {'✅' if project_info.has_config_files else '❌'}")
        
        if project_info.tools:
            click.echo(f"   Custom Tools: {', '.join(project_info.tools)}")
        
        if project_info.estimated_resources:
            click.echo(f"   Estimated Resources:")
            for resource, value in project_info.estimated_resources.items():
                click.echo(f"     {resource}: {value}")
                
        if project_info.api_endpoints:
            click.echo(f"   API Endpoints: {', '.join(project_info.api_endpoints)}")
            
    except Exception as e:
        click.echo(f"❌ Error analyzing project: {e}", err=True)
        raise click.Abort()

@cli.command()
@click.option('--repo-url', required=True, help='Repository URL to deploy')
@click.option('--name', help='Application name (auto-detected if not provided)')
@click.option('--namespace', default='default', help='Kubernetes namespace')
@click.option('--auto-detect/--no-auto-detect', default=True, help='Enable automatic project detection')
def deploy_agentstack(repo_url, name, namespace, auto_detect):
    """Deploy AgentStack project with automatic configuration."""
    try:
        from .api import DeploymentConfig, build_and_deploy_with_events
        from .backend.event import EventStatus
        
        # Auto-generate name from repo if not provided
        if not name:
            name = repo_url.split('/')[-1].replace('.git', '').lower()
            
        click.echo(f"🚀 Deploying AgentStack project:")
        click.echo(f"   Repository: {repo_url}")
        click.echo(f"   Name: {name}")
        click.echo(f"   Namespace: {namespace}")
        click.echo(f"   Auto-detection: {'Enabled' if auto_detect else 'Disabled'}")
        click.echo()

        config = DeploymentConfig(
            app_name=name,
            namespace=namespace,
            repository_url=repo_url,
            auto_detect_framework=auto_detect,
        )

        deployment = None
        for event in build_and_deploy_with_events(config):
            # Handle different event types with enhanced messaging
            if hasattr(event, 'event_type'):
                if event.event_type == 'repository':
                    if event.status == EventStatus.PROGRESS:
                        if 'Detected' in event.message:
                            click.echo(f"🔍 {event.message}")
                        else:
                            click.echo(f"   {event.message}")
                elif event.event_type == 'build':
                    if event.status == EventStatus.STARTED:
                        click.echo("🔨 Building Docker image...")
                    elif event.status == EventStatus.PROGRESS and event.message:
                        if not event.message.startswith(' ---'):
                            click.echo(f"   {event.message.strip()}")
                    elif event.status == EventStatus.COMPLETED:
                        click.echo(f"✅ Build completed: {event.image_name}")
                # ... handle other event types
            elif hasattr(event, 'name'):  # Final deployment object
                deployment = event

        if deployment:
            click.echo("\n🎉 AgentStack deployment successful!")
            click.echo(f"   Name: {deployment.name}")
            click.echo(f"   Image: {deployment.image}")
            click.echo(f"   Namespace: {deployment.namespace}")
            if hasattr(deployment, 'service_url'):
                click.echo(f"   API URL: {deployment.service_url}")
            click.echo("\n💡 Your CrewAI project is now available as an HTTP API!")
            click.echo("   Use POST /process with inputs and webhook_url to execute your crew.")
            
    except Exception as e:
        click.echo(f"❌ Error during AgentStack deployment: {e}", err=True)
        raise click.Abort()

@cli.command()
@click.option('--deployment-name', required=True, help='Name of the deployed application')
@click.option('--namespace', default='default', help='Kubernetes namespace')
def test_api(deployment_name, namespace):
    """Test deployed AgentStack API endpoints."""
    try:
        from .backend.models.deployment import Deployment
        import requests
        import json
        
        # Get deployment info
        deployment = Deployment.get(name=deployment_name, namespace=namespace)
        if not deployment:
            click.echo(f"❌ Deployment '{deployment_name}' not found in namespace '{namespace}'")
            raise click.Abort()
            
        # Construct API URL (this would need service discovery logic)
        api_url = f"http://{deployment_name}.{namespace}.svc.cluster.local:6969"
        
        click.echo(f"🧪 Testing API endpoints for {deployment_name}:")
        
        # Test health endpoint
        try:
            response = requests.get(f"{api_url}/health", timeout=5)
            if response.status_code == 200:
                click.echo("✅ Health check: PASSED")
            else:
                click.echo(f"❌ Health check: FAILED ({response.status_code})")
        except requests.RequestException as e:
            click.echo(f"❌ Health check: FAILED ({e})")
        
        # Test readiness endpoint
        try:
            response = requests.get(f"{api_url}/ready", timeout=5)
            if response.status_code == 200:
                click.echo("✅ Readiness check: PASSED")
                click.echo("💡 API is ready to process requests")
                
                # Show example request
                click.echo("\n📝 Example API usage:")
                click.echo(f"curl -X POST {api_url}/process \\")
                click.echo('  -H "Content-Type: application/json" \\')
                click.echo('  -d \'{"inputs": {"topic": "AI"}, "webhook_url": "https://your-webhook.com/callback"}\'')
            else:
                click.echo(f"❌ Readiness check: FAILED ({response.status_code})")
        except requests.RequestException as e:
            click.echo(f"❌ Readiness check: FAILED ({e})")
            
    except Exception as e:
        click.echo(f"❌ Error testing API: {e}", err=True)
        raise click.Abort()

Identified Gaps and Implementation Needs

1. AgentStack Gaps

Missing Production Features (PARTIALLY ADDRESSED)

  • No headless deployment mode: Current serve.py includes UI serving which adds unnecessary overhead
  • Limited health checks: Basic health endpoint but no Kubernetes-specific readiness probes
  • No resource optimization: Containers include development dependencies
  • Missing telemetry configuration: AgentOps integration not optimized for production

Required Modifications (UPDATED)

  1. Create deployment_serve.py - Production-optimized server without UI Enhanced with dynamic crew detection
  2. Enhanced health endpoints - Kubernetes-ready health and readiness probes Now validates crew file detection
  3. Resource estimation - Analyze projects for appropriate resource allocation
  4. Configuration export - Export deployment configs in standard formats
  5. Enhanced crew detection integration - COMPLETED: Leverage new _find_crew_file_dynamically function

2. Jockey Gaps

Missing Project Intelligence (SIGNIFICANTLY IMPROVED)

  • No project type detection: Currently assumes generic Python projects ENHANCED: Now uses AgentStack's dynamic detection
  • Static Dockerfile templates: No framework-specific optimization ENHANCED: Framework-aware template selection
  • No resource awareness: Fixed resource allocation regardless of project complexity IMPROVED: Project-based resource estimation
  • Limited validation: No pre-deployment project structure validation ENHANCED: Crew file validation via AgentStack

Required Implementations (UPDATED)

  1. Project detection system - COMPLETED: Leverages AgentStack's AST parsing and enhanced crew detection
  2. Framework-specific templates - ENHANCED: Detects actual crew file locations for optimized builds
  3. Resource estimation - IMPROVED: Dynamic allocation based on project complexity and detected patterns
  4. Validation pipeline - ENHANCED: Pre-deployment checks using AgentStack's validation capabilities
  5. Native CrewAI support - NEW: Supports projects without agentstack.json using pattern detection

3. Integration Gaps

Missing Communication Layer

  • No shared configuration format: AgentStack and Jockey use different config structures
  • No deployment feedback: AgentStack can't report deployment status back to Jockey
  • Limited error propagation: Generic error messages don't help with framework-specific issues

Required Bridges

  1. Configuration translation - Convert between AgentStack and Jockey formats
  2. Event propagation - Forward AgentStack events through Jockey's event system
  3. Error contextualization - Framework-aware error messages and debugging

4. Operational Gaps

Missing Production Features

  • No API versioning: Deployed APIs lack version management
  • No scaling configuration: Manual scaling without usage-based optimization
  • Limited monitoring: Basic health checks but no performance metrics
  • No rollback capability: Deployments can't be easily reverted

Required Operations Support

  1. API gateway integration - Routing, authentication, rate limiting
  2. Metrics collection - Performance monitoring and alerting
  3. Deployment strategies - Blue/green, canary deployments
  4. Backup and recovery - State management for stateful crew operations

Success Metrics & Validation

Technical Validation

  • Detection Accuracy: >95% correct project type identification ENHANCED: Now supports both AgentStack and native CrewAI projects
  • Build Performance: <3 minute average build time for CrewAI projects
  • Container Efficiency: <300MB final image size for typical projects
  • API Response Time: <2 second response for crew kickoff operations
  • Resource Usage: Accurate resource estimation within 20% of actual usage IMPROVED: Dynamic resource allocation

Developer Experience Validation

  • One-Command Deployment: jockey deploy --repo-url <url> works for any CrewAI project ENHANCED: Supports native CrewAI projects
  • Zero Configuration: Projects deploy without manual Dockerfile or configuration IMPROVED: Automatic crew file detection
  • Error Clarity: Framework-specific error messages with actionable solutions ENHANCED: Crew validation in readiness checks
  • Documentation Coverage: Complete examples for each supported framework

Production Readiness Validation

  • Health Monitoring: Kubernetes-native health and readiness probes ENHANCED: Validates crew file detection
  • Scaling Behavior: Proper resource allocation and horizontal scaling
  • Security Compliance: Container security scanning and vulnerability management
  • Operational Monitoring: Comprehensive logging and metrics collection

Implementation Priority Updates

Immediate Benefits (Ready to Implement)

  1. Enhanced Project Detection - READY: AgentStack's dynamic crew detection is implemented
  2. Flexible Deployment - READY: Can deploy both template and native CrewAI projects
  3. Improved Validation - READY: Crew file validation in readiness probes

Next Phase Implementation

  1. Production Deployment Server - Create AgentStack's deployment_serve.py
  2. Framework-Specific Dockerfiles - Implement agentstack-crewai.j2 template
  3. Enhanced Jockey CLI - Add detect-project and deploy-agentstack commands

This comprehensive analysis provides the foundation for implementing a robust AgentStack-Jockey integration that bridges the gap between development and production for AI agent applications. The enhanced crew detection capabilities significantly improve the integration's robustness and flexibility.