Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 52 additions & 207 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ openai = "^1.23.3"
transformers = "^4.53.0"
black = "^24.4.2"
python-dotenv = "^1.0.1"
anthropic = "0.35.0"
anthropic = "^0.79.0"
pymilvus = "^2.4.6"

# These dependencies should only be needed for fine tuning
Expand Down
Empty file.
14 changes: 14 additions & 0 deletions services/global_agent/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Global agent configuration

# Router configuration (lightweight routing)
router:
model: "claude-haiku-4-5-20251001"
max_tokens: 500
temperature: 0.0

# Planner configuration (complex orchestration)
planner:
model: "claude-sonnet-4-20250514"
max_tokens: 8192
temperature: 1.0
max_tool_calls: 10
47 changes: 47 additions & 0 deletions services/global_agent/config_loader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Configuration loader for global agent."""
import os
import yaml
from pathlib import Path


class ConfigLoader:
"""Loads and manages configuration from YAML files."""

def __init__(self, config_path: str = "config.yaml", prompts_path: str = "prompts.yaml"):
"""
Initialize config loader.

Args:
config_path: Path to config file relative to this module
prompts_path: Path to prompts file relative to this module
"""
# Get directory where this file lives
module_dir = Path(__file__).parent

# Load config
full_config_path = module_dir / config_path
if not full_config_path.exists():
raise FileNotFoundError(f"Config file not found: {full_config_path}")

with open(full_config_path, 'r') as f:
self.config = yaml.safe_load(f)

# Load prompts
full_prompts_path = module_dir / prompts_path
if not full_prompts_path.exists():
raise FileNotFoundError(f"Prompts file not found: {full_prompts_path}")

with open(full_prompts_path, 'r') as f:
self.prompts = yaml.safe_load(f)

def get(self, key: str, default=None):
"""Get config value by key."""
return self.config.get(key, default)

def get_prompt(self, prompt_key: str) -> str:
"""Get prompt by key from prompts.yaml."""
return self.prompts.get("prompts", {}).get(prompt_key, "")

def __getitem__(self, key: str):
"""Allow dict-style access."""
return self.config[key]
99 changes: 99 additions & 0 deletions services/global_agent/global_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""
Global Agent - Main entry point.

This is the supervisor agent that coordinates subagents and tools.
"""
import os
from typing import Dict, Any, List, Optional
from dataclasses import dataclass

# Import utilities from parent services directory
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).parent.parent))

from util import ApolloError, create_logger
from global_agent.config_loader import ConfigLoader
from global_agent.router import RouterAgent

logger = create_logger(__name__)


@dataclass
class Payload:
"""Input payload for global agent."""
content: str # User message (required)
existing_yaml: Optional[str] = None # YAML as STRING
errors: Optional[str] = None # Error context
context: Optional[Dict] = None # Job code context
history: Optional[List[Dict]] = None # Chat history
api_key: Optional[str] = None # Override API key
stream: Optional[bool] = False # Streaming flag (not implemented yet)
read_only: Optional[bool] = False # Read-only mode

@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "Payload":
"""Validate and create Payload from dict."""
if "content" not in data:
raise ApolloError(400, "content is required")

return cls(
content=data["content"],
existing_yaml=data.get("existing_yaml"),
errors=data.get("errors"),
context=data.get("context"),
history=data.get("history"),
api_key=data.get("api_key"),
stream=data.get("stream", False),
read_only=data.get("read_only", False)
)


def main(data_dict: dict) -> dict:
"""
Main entry point for global agent service.

Args:
data_dict: Input payload as dictionary

Returns:
Response dictionary with text, YAML, history, usage, meta
"""
try:
# 1. Validate payload
data = Payload.from_dict(data_dict)
logger.info(f"Global agent called with content: {data.content[:100]}...")

# 2. Load configuration
config_loader = ConfigLoader("config.yaml")

# 3. Initialize router
router = RouterAgent(config_loader, data.api_key)

# 4. Route and execute
result = router.route_and_execute(
content=data.content,
existing_yaml=data.existing_yaml,
errors=data.errors,
context=data.context,
history=data.history or [],
read_only=data.read_only,
stream=data.stream
)

# 5. Return structured response
return {
"response": result.response,
"response_yaml": result.response_yaml,
"suggested_code": result.suggested_code,
"history": result.history,
"usage": result.usage,
"meta": result.meta
}

except ApolloError as e:
logger.error(f"ApolloError in global_agent: {e}")
raise e
except Exception as e:
logger.exception("Unexpected error in global_agent")
raise ApolloError(500, str(e))
Loading