From fc60c72ceb9355a7b94fb595831f6ced2e844214 Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Fri, 30 May 2025 19:20:01 -0500 Subject: [PATCH 1/5] Remove unused MCP functions to create a clean separation between API functions in main and MCP functions in mcp_tools Signed-off-by: Michael Yuan --- app/main.py | 48 +----- app/mcp_server.py | 205 ------------------------- app/mcp_service.py | 348 ------------------------------------------ mcp-proxy-config.json | 13 -- 4 files changed, 4 insertions(+), 610 deletions(-) delete mode 100644 app/mcp_server.py delete mode 100644 app/mcp_service.py delete mode 100644 mcp-proxy-config.json diff --git a/app/main.py b/app/main.py index dc9883f..b1db5ea 100644 --- a/app/main.py +++ b/app/main.py @@ -18,7 +18,6 @@ from app.compiler import RustCompiler from app.llm_client import LlamaEdgeClient from app.vector_store import QdrantStore -from app.mcp_service import RustCompilerMCP app = FastAPI(title="Rust Project Generator API") @@ -118,9 +117,6 @@ async def compile_rust(request: dict): if "code" not in request: raise HTTPException(status_code=400, detail="Missing 'code' field") - # Remove MCP service reference - # return rust_mcp.compile_rust_code(request["code"]) - # Create temp directory with tempfile.TemporaryDirectory() as temp_dir: # Parse the multi-file content @@ -155,35 +151,6 @@ async def compile_rust(request: dict): "error_details": error_context } -@app.post("/compile-and-fix") -async def compile_and_fix(request: CompileAndFixRequest): - """Compile Rust code and fix errors with LLM""" - code = request.code - description = request.description - max_attempts = request.max_attempts - - # Create MCP service instance - mcp_service = RustCompilerMCP( - vector_store=get_vector_store(), - llm_client=llm_client - ) - - # Compile and fix code - result = mcp_service.compile_and_fix_rust_code( - code_content=code, - description=description, - max_attempts=max_attempts - ) - - # Add combined_text field with the entire project in flat text format - combined_text = "" - for filename, content in result["final_files"].items(): - combined_text += f"[filename: {filename}]\n{content}\n\n" - - result["combined_text"] = combined_text.strip() - - return result - @app.post("/compile-and-fix") async def compile_and_fix_rust(request: dict): """Endpoint to compile and fix Rust code""" @@ -195,16 +162,9 @@ async def compile_and_fix_rust(request: dict): # Pre-process code to fix common syntax errors code = request["code"] # Fix missing parenthesis in println! macro - if "println!(" in code and ");" not in code: - code = code.replace("println!(\"", "println!(\"") - code = code.replace("\" //", "\"); //") - - # Remove MCP service reference - # result = rust_mcp.compile_and_fix_rust_code( - # code, - # request["description"], - # max_attempts=max_attempts - # ) + # if "println!(" in code and ");" not in code: + # code = code.replace("println!(\"", "println!(\"") + # code = code.replace("\" //", "\"); //") # Create temp directory with tempfile.TemporaryDirectory() as temp_dir: @@ -722,4 +682,4 @@ async def generate_project_sync(request: ProjectRequest): return PlainTextResponse(content=all_files_content) except Exception as e: - raise HTTPException(status_code=500, detail=f"Error generating project: {str(e)}") \ No newline at end of file + raise HTTPException(status_code=500, detail=f"Error generating project: {str(e)}") diff --git a/app/mcp_server.py b/app/mcp_server.py deleted file mode 100644 index 0e2addc..0000000 --- a/app/mcp_server.py +++ /dev/null @@ -1,205 +0,0 @@ -""" -DEPRECATED: This file is deprecated. All MCP functionality has been moved to app/mcp_tools.py. -This file is kept for reference purposes only and will be removed in a future release. -""" - -import os -import json -import sys -from typing import Dict, Any, Optional, List - -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) - -# Use FastMCP instead of direct Server inheritance -from mcp.server.fastmcp import FastMCP - -from app.compiler import RustCompiler -from app.response_parser import ResponseParser -from app.vector_store import QdrantStore -from app.llm_client import LlamaEdgeClient -from app.mcp_service import RustCompilerMCP - -# Create a RustMCPServer class for the run_mcp_server.py script -class RustMCPServer: - def __init__(self, mcp_service): - self.mcp = FastMCP("Rust Compiler") - self.mcp_service = mcp_service - - # Register tools - @self.mcp.tool() - def compile(code: str) -> Dict[str, Any]: - """Compile Rust code""" - result = self.mcp_service.compile_rust_code(code) - if result["success"]: - return "success" - else: - return {"status": "error", "message": f"Compilation failed: {result.get('build_output', '')}"} - - @self.mcp.tool() - def compileAndFix(code: str, description: str, max_attempts: int = 3) -> Dict[str, Any]: - """Compile and fix Rust code""" - result = self.mcp_service.compile_and_fix_rust_code(code, description, max_attempts) - - if result["success"]: - # Format fixed files as raw text with filename markers - output_text = "" - for filename, content in result["final_files"].items(): - output_text += f"[filename: {filename}]\n{content}\n\n" - - return output_text.strip() - else: - return {"status": "error", "message": f"Failed to fix code: {result.get('build_output', '')}"} - - @self.mcp.tool() - def vectorSearch(query: str, collection: str, limit: int = 3) -> Dict[str, Any]: - """Search vector database for similar examples""" - embedding = self.mcp_service.llm_client.get_embeddings([query])[0] - results = self.mcp_service.vector_store.search(collection, embedding, limit=limit) - return {"results": results} - - def run(self, host="0.0.0.0", port=3001): - """Run the MCP server""" - # Set the server host and port using environment variables - os.environ["MCP_HOST"] = host - os.environ["MCP_PORT"] = str(port) - - print(f"Starting MCP server on {host}:{port}") - # Use sse transport instead of http - self.mcp.run(transport="sse") # or "http" depending on cmcp version - -# For direct invocation -mcp = FastMCP("Rust Compiler") -mcp_service = None # Will be initialized in main - -@mcp.tool() -def compile_and_fix(code: str, description: str, max_attempts: int = 3) -> Dict[str, Any]: - """ - Compile and fix Rust code - - Args: - code: Multi-file Rust code with [filename:] markers - description: Project description for context - max_attempts: Maximum number of attempts to fix errors - - Returns: - Fixed code or error details - """ - global mcp_service - - if not code or not description: - return {"status": "error", "message": "Missing required parameters"} - - try: - result = mcp_service.compile_and_fix_rust_code(code, description, max_attempts) - - if result["success"]: - # Format fixed files as raw text with filename markers - output_text = "" - for filename, content in result["final_files"].items(): - output_text += f"[filename: {filename}]\n{content}\n\n" - - return output_text.strip() - else: - # For errors, return error message - return {"status": "error", "message": f"Failed to fix code: {result.get('build_output', '')}"} - except Exception as e: - return {"status": "error", "message": str(e)} - -# For the standalone mcp tool definitions - -@mcp.tool() -def compile(code: str) -> Dict[str, Any]: - """Compile Rust code""" - global mcp_service - - if not code: - return {"status": "error", "message": "Missing required parameters"} - - try: - result = mcp_service.compile_rust_code(code) - if result["success"]: - return "success" - else: - return {"status": "error", "message": f"Compilation failed: {result.get('build_output', '')}"} - except Exception as e: - return {"status": "error", "message": str(e)} - -@mcp.tool() -def compileAndFix(code: str, description: str, max_attempts: int = 3) -> Dict[str, Any]: - """Compile and fix Rust code""" - global mcp_service - - if not code or not description: - return {"status": "error", "message": "Missing required parameters"} - - try: - result = mcp_service.compile_and_fix_rust_code(code, description, max_attempts) - - if result["success"]: - # Format fixed files as raw text with filename markers - output_text = "" - for filename, content in result["final_files"].items(): - output_text += f"[filename: {filename}]\n{content}\n\n" - - return output_text.strip() - else: - return {"status": "error", "message": f"Failed to fix code: {result.get('build_output', '')}"} - except Exception as e: - return {"status": "error", "message": str(e)} - -if __name__ == "__main__": - # Only run server setup if script is executed directly - # Initialize required components - from dotenv import load_dotenv - - load_dotenv() - - # Get API key from environment variable - api_key = os.getenv("LLM_API_KEY") - if not api_key: - raise ValueError("LLM_API_KEY environment variable not set") - - # Get embedding size from environment variable - llm_embed_size = int(os.getenv("LLM_EMBED_SIZE", "1536")) - - # Initialize components - llm_client = LlamaEdgeClient(api_key=api_key) - vector_store = QdrantStore(embedding_size=llm_embed_size) - - # Create collections with error handling - try: - vector_store.create_collection("project_examples") - print("Created collection: project_examples") - except Exception as e: - if "already exists" in str(e): - print("Collection project_examples already exists") - else: - raise - - try: - vector_store.create_collection("error_examples") - print("Created collection: error_examples") - except Exception as e: - if "already exists" in str(e): - print("Collection error_examples already exists") - else: - raise - - # Initialize MCP service - mcp_service = RustCompilerMCP(vector_store=vector_store, llm_client=llm_client) - - # Determine transport mode from arguments or environment - transport = os.getenv("MCP_TRANSPORT", "stdio") - - # Run server with appropriate transport - if transport == "stdio": - # Run in STDIO mode - mcp.run(transport="stdio") - else: - # Run in HTTP/SSE mode - host = os.getenv("MCP_HOST", "0.0.0.0") - port = int(os.getenv("MCP_PORT", "3001")) - print(f"Starting MCP server on {host}:{port}") - os.environ["MCP_HOST"] = host - os.environ["MCP_PORT"] = str(port) - mcp.run(transport="sse") # or "http" depending on cmcp version \ No newline at end of file diff --git a/app/mcp_service.py b/app/mcp_service.py deleted file mode 100644 index 48ddbe0..0000000 --- a/app/mcp_service.py +++ /dev/null @@ -1,348 +0,0 @@ -""" -This file contains the core business logic for the Rust Compiler MCP service. -Note that the API integration and HTTP endpoints are now handled by app/mcp_tools.py. -""" - -import os -import tempfile -import shutil -from typing import Dict, List, Optional, Tuple - -from app.compiler import RustCompiler -from app.response_parser import ResponseParser -from app.vector_store import QdrantStore -from app.llm_client import LlamaEdgeClient - -class RustCompilerMCP: - def __init__(self, vector_store=None, llm_client=None): - """ - Initialize the Rust Compiler MCP service - - Args: - vector_store: Vector store for searching similar examples - llm_client: LLM client for generating fixes - """ - self.compiler = RustCompiler() - self.parser = ResponseParser() - - # Use provided vector store or create a new one - self.vector_store = vector_store - if self.vector_store is None: - self.vector_store = QdrantStore() - self.vector_store.create_collection("project_examples") - self.vector_store.create_collection("error_examples") - - # Use provided LLM client or create a new one - self.llm_client = llm_client - if self.llm_client is None: - import os - from dotenv import load_dotenv - load_dotenv() - api_key = os.getenv("LLM_API_KEY") - if api_key: - self.llm_client = LlamaEdgeClient(api_key=api_key) - else: - raise ValueError("LLM_API_KEY environment variable not set") - - def compile_rust_code(self, code_content: str) -> Dict: - """ - MCP function to compile Rust code - - Args: - code_content: String containing Rust code with file markers - - Returns: - Dict with compilation status, output, and error messages - """ - # Create temp directory - with tempfile.TemporaryDirectory() as temp_dir: - # Parse the multi-file content - files = self.parser.parse_response(code_content) - - # Ensure project structure - os.makedirs(os.path.join(temp_dir, "src"), exist_ok=True) - - # Write files - file_paths = self.parser.write_files(files, temp_dir) - - # Compile the project - success, output = self.compiler.build_project(temp_dir) - - if success: - # Try running if compilation succeeded - run_success, run_output = self.compiler.run_project(temp_dir) - - # Store successful project in vector store for future reference - self._store_successful_project(files, code_content) - - return { - "success": True, - "files": file_paths, - "build_output": output or "Build successful", - "run_output": run_output if run_success else "Failed to run project" - } - else: - # Return error details - error_context = self.compiler.extract_error_context(output) - return { - "success": False, - "files": file_paths, - "build_output": output, - "error_details": error_context - } - - def compile_and_fix_rust_code(self, - code_content: str, - description: str, - max_attempts: int = 3) -> Dict: - """ - MCP function to compile Rust code and fix errors using LLM - - Args: - code_content: String containing Rust code with file markers - description: Project description for context - max_attempts: Maximum number of fix attempts - - Returns: - Dict with compilation status, output, fixes applied, and final code - """ - # Create temp directory for this compilation - with tempfile.TemporaryDirectory() as temp_dir: - # Parse the multi-file content - files = self.parser.parse_response(code_content) - - # Ensure project structure - os.makedirs(os.path.join(temp_dir, "src"), exist_ok=True) - - # Write files - file_paths = self.parser.write_files(files, temp_dir) - - # Check if we have similar projects in our dataset for reference - project_reference = self._find_similar_project(description) - - attempts = [] - current_files = files.copy() - - # Try compiling and fixing up to max_attempts - for attempt in range(max_attempts): - # Compile the project - success, output = self.compiler.build_project(temp_dir) - - # Store this attempt - attempts.append({ - "attempt": attempt + 1, - "success": success, - "output": output - }) - - if success: - # Try running if compilation succeeded - run_success, run_output = self.compiler.run_project(temp_dir) - - # Store this successful fix in our vector database - if attempt > 0: # Only store if we actually fixed something - self._store_successful_fix(code_content, current_files, output) - - return { - "success": True, - "attempts": attempts, - "final_files": current_files, - "build_output": output or "Build successful", - "run_output": run_output if run_success else "Failed to run project", - "similar_project_used": project_reference is not None - } - - # If we've reached max attempts without success, stop - if attempt == max_attempts - 1: - break - - # Extract error context for LLM - error_context = self.compiler.extract_error_context(output) - - # Find similar errors in vector DB - similar_errors = self._find_similar_errors(error_context["full_error"]) - - # Generate fix prompt - fix_prompt = self._generate_fix_prompt( - description, - error_context, - similar_errors, - project_reference - ) - - # Get fix from LLM - fix_response = self.llm_client.generate_text(fix_prompt) - - # Parse and apply fixes - fixed_files = self.parser.parse_response(fix_response) - for filename, content in fixed_files.items(): - file_path = os.path.join(temp_dir, filename) - os.makedirs(os.path.dirname(file_path), exist_ok=True) - with open(file_path, 'w') as f: - f.write(content) - current_files[filename] = content - - # If we've exhausted all attempts - return { - "success": False, - "attempts": attempts, - "final_files": current_files, - "build_output": attempts[-1]["output"], - "error_details": self.compiler.extract_error_context(attempts[-1]["output"]), - "similar_project_used": project_reference is not None, - "similar_errors_found": len(similar_errors) if 'similar_errors' in locals() else 0 - } - - def _find_similar_project(self, description: str) -> Optional[Dict]: - """Find similar project from vector store""" - if not self.vector_store: - return None - - try: - embeddings = self.llm_client.get_embeddings([description])[0] - similar_projects = self.vector_store.search("project_examples", embeddings, limit=1) - - if similar_projects: - return similar_projects[0] - except Exception as e: - print(f"Error finding similar project: {e}") - - return None - - def _find_similar_errors(self, error_text: str) -> List[Dict]: - """Find similar error examples from vector store""" - if not self.vector_store: - return [] - - try: - embeddings = self.llm_client.get_embeddings([error_text])[0] - similar_errors = self.vector_store.search("error_examples", embeddings, limit=3) - return similar_errors - except Exception as e: - print(f"Error finding similar errors: {e}") - return [] - - def _generate_fix_prompt(self, - description: str, - error_context: Dict, - similar_errors: List[Dict], - project_reference: Optional[Dict]) -> str: - """Generate prompt for fixing compilation errors""" - fix_examples = "" - if similar_errors: - fix_examples = "Here are some examples of similar errors and their fixes:\n\n" - for i, err in enumerate(similar_errors): - fix_examples += f"Example {i+1}:\n{err['error']}\nFix: {err['solution']}\n\n" - - reference_text = "" - if project_reference: - reference_text = f"\nHere's a similar project for reference:\n{project_reference['example']}\n" - - return f""" -Here is a Rust project that failed to compile. Help me fix the compilation errors. - -Project description: {description} -{reference_text} - -Compilation error: -{error_context["full_error"]} - -{fix_examples} - -Please provide the fixed code for all affected files. -""" - - def _store_successful_project(self, files: Dict[str, str], code_content: str): - """Store successful project compilation in vector store""" - if not self.vector_store: - return - - try: - # Create a project description - if "README.md" in files: - description = files["README.md"][:500] # Use first 500 chars of README - else: - # Default to cargo.toml content - description = files.get("Cargo.toml", "Rust project") - - # Try to get embedding or use dummy embedding if failed - try: - embeddings = self.llm_client.get_embeddings([description])[0] - except Exception as e: - print(f"Error getting embeddings: {e}") - # Use a dummy embedding of correct size - embeddings = [0.0] * self.vector_store.embedding_size # Use configurable size - - # Store in vector store - self.vector_store.add_item( - collection_name="project_examples", - vector=embeddings, - item={ - "example": code_content[:10000], # Limit size - "description": description - } - ) - except Exception as e: - print(f"Failed to store project in vector DB: {e}") - - def _store_successful_fix(self, original_code: str, fixed_files: Dict[str, str], error_output: str): - """Store successful error fix in vector store""" - if not self.vector_store: - return - - try: - # Create a combined fixed code representation - fixed_code = "\n\n".join([ - f"[filename: {filename}]\n{content}" - for filename, content in fixed_files.items() - ]) - - # Get embedding of the error - error_context = self.compiler.extract_error_context(error_output) - embeddings = self.llm_client.get_embeddings([error_context["full_error"]])[0] - - # Store in vector store - self.vector_store.add_item( - collection_name="error_examples", - vector=embeddings, - item={ - "error": error_context["full_error"], - "solution": fixed_code[:5000], # Limit size - "original_code": original_code[:5000] # Limit size - } - ) - except Exception as e: - print(f"Failed to store error fix in vector DB: {e}") - - def _fix_errors_with_llm(self, error_output, current_files, description, project_reference=None): - """Use LLM to fix compilation errors""" - if not self.llm_client: - print("Warning: No LLM client available, returning original files") - return current_files - - try: - # Create prompt for error fixing - prompt = self.prompt_generator.generate_error_fix_prompt( - error_output=error_output, - current_files=current_files, - description=description, - project_reference=project_reference - ) - - # Get LLM response - response = self.llm_client.generate_text( - prompt=prompt, - system_message="You are an expert Rust developer fixing compilation errors.", - max_tokens=4000, - temperature=0.2 # Lower temperature for more precise fixes - ) - - # Parse response into fixed files - fixed_files = self.parser.parse_response(response) - - # Return the fixed files - return fixed_files - except Exception as e: - print(f"Error fixing with LLM: {str(e)}") - # Return original files if LLM fails - return current_files \ No newline at end of file diff --git a/mcp-proxy-config.json b/mcp-proxy-config.json deleted file mode 100644 index 2dbf3bc..0000000 --- a/mcp-proxy-config.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "providers": [ - { - "name": "rust-compiler", - "url": "http://mcp-server:3001/sse", - "methods": ["compile", "compileAndFix", "vectorSearch"] - } - ], - "name": "rust-compiler.compile", - "arguments": { - "code": "..." - } -} \ No newline at end of file From 19546f7d2b55f7319d7a528aa8617370c47088f2 Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Fri, 30 May 2025 20:11:53 -0500 Subject: [PATCH 2/5] Remove comments Signed-off-by: Michael Yuan --- Dockerfile | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index d57b074..f440e50 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,16 +9,6 @@ RUN apt-get update && apt-get install -y curl build-essential && \ # Add cargo to PATH ENV PATH="/root/.cargo/bin:${PATH}" -# Directly download and install OpenMCP from source to avoid binary compatibility issues -# RUN apt-get install -y git && \ -# git clone https://github.com/decentralized-mcp/proxy.git && \ -# cd proxy && \ -# cargo build --release && \ -# cp target/release/openmcp /usr/local/bin/ && \ -# chmod +x /usr/local/bin/openmcp && \ -# cd .. && \ -# rm -rf proxy - # Install openmcp proxy via the installer RUN curl -sSfL 'https://raw.githubusercontent.com/decentralized-mcp/proxy/refs/heads/master/install.sh' | bash From 90ec2b568fa7470bea2bc8adc86382d8bc1fcf66 Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Fri, 30 May 2025 20:12:23 -0500 Subject: [PATCH 3/5] Allow the use of env vars to configure API Signed-off-by: Michael Yuan --- docker-compose.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 64995e4..e50c3c3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,12 +4,12 @@ services: ports: - "8000:8000" environment: - - LLM_API_BASE=http://host.docker.internal:8080/v1 - - LLM_MODEL=Qwen2.5-Coder-3B-Instruct-Q5_K_M - - LLM_EMBED_MODEL=local-embed - - LLM_EMBED_SIZE=768 - - LLM_API_KEY= - - SKIP_VECTOR_SEARCH=true + - LLM_API_BASE=${LLM_API_BASE:-http://host.docker.internal:8080/v1} + - LLM_MODEL=${LLM_MODEL:-Qwen2.5-Coder-3B-Instruct-Q5_K_M} + - LLM_EMBED_MODEL=${LLM_EMBED_MODEL:-nomic-embed} + - LLM_EMBED_SIZE=${LLM_EMBED_SIZE:-768} + - LLM_API_KEY=${LLM_API_KEY:-ABCD1234} + - SKIP_VECTOR_SEARCH=${SKIP_VECTOR_SEARCH:-true} - QDRANT_HOST=qdrant - QDRANT_PORT=6333 command: uvicorn app.main:app --host 0.0.0.0 --port 8000 From 76c477ecc8a7ca05c2b4bec2a2710caeb4bb9221 Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Fri, 30 May 2025 22:38:55 -0500 Subject: [PATCH 4/5] Try to fix the Actions flow Signed-off-by: Michael Yuan --- .env | 7 ------- .github/workflows/test-mcp-server.yml | 17 +++++++++-------- 2 files changed, 9 insertions(+), 15 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index f5b208a..0000000 --- a/.env +++ /dev/null @@ -1,7 +0,0 @@ -# Local Gaia node configuration -LLM_API_BASE=http://localhost:8080/v1 -LLM_MODEL=Qwen2.5-Coder-3B-Instruct-Q5_K_M -LLM_EMBED_MODEL=local-embed -LLM_EMBED_SIZE=768 -# No API key needed for local setup -LLM_API_KEY= \ No newline at end of file diff --git a/.github/workflows/test-mcp-server.yml b/.github/workflows/test-mcp-server.yml index 4e9b643..4cb9b1b 100644 --- a/.github/workflows/test-mcp-server.yml +++ b/.github/workflows/test-mcp-server.yml @@ -11,14 +11,6 @@ jobs: test: runs-on: ubuntu-latest - env: - LLM_API_BASE: http://localhost:8080/v1 - LLM_MODEL: Qwen2.5-Coder-3B-Instruct-Q5_K_M - LLM_EMBED_MODEL: local-embed - LLM_EMBED_SIZE: 768 - LLM_API_KEY: "" - SKIP_VECTOR_SEARCH: "true" - steps: - name: Checkout code uses: actions/checkout@v4.2.2 @@ -34,10 +26,19 @@ jobs: - name: Install Python dependencies run: pip install -r requirements.txt + - name: Set up env + run: | + echo "LLM_API_BASE=${{ env.LLM_API_BASE }}" > .env.temp + echo "LLM_API_KEY=${{ secrets.LLM_API_KEY }}" >> .env.temp + echo "LLM_MODEL=${{ env.LLM_MODEL }}" >> .env.temp + echo "LLM_EMBED_MODEL=${{ env.LLM_EMBED_MODEL }}" >> .env.temp + echo "LLM_EMBED_SIZE=${{ env.LLM_EMBED_SIZE }}" >> .env.temp + - name: Run docker compose uses: hoverkraft-tech/compose-action@v2.0.1 with: compose-file: ./docker-compose.yml + compose-flags: "--env-file .env.temp" up-flags: "-d --build" - name: Wait for services to be ready From 323123d00725864db22ae0951f229bee99f7c1d3 Mon Sep 17 00:00:00 2001 From: Michael Yuan Date: Fri, 30 May 2025 23:58:38 -0500 Subject: [PATCH 5/5] Fix actions Signed-off-by: Michael Yuan --- .github/workflows/test-mcp-server.yml | 329 +------------------------- 1 file changed, 3 insertions(+), 326 deletions(-) diff --git a/.github/workflows/test-mcp-server.yml b/.github/workflows/test-mcp-server.yml index 4cb9b1b..0bd6532 100644 --- a/.github/workflows/test-mcp-server.yml +++ b/.github/workflows/test-mcp-server.yml @@ -122,11 +122,12 @@ jobs: # Verify the response format has filename markers if ! echo "$RESPONSE" | grep -q "\[filename:"; then echo "Response does not contain filename markers:" - echo "$RESPONSE" + echo "$RESPONSE" | jq || echo "$RESPONSE" exit 1 fi echo "Compile and fix successful! Response contains code files in text format." + echo "$RESPONSE" | jq || echo "$RESPONSE" fi - name: Test /generate endpoint @@ -265,6 +266,7 @@ jobs: echo "Generate-sync successful! Response contains code files in text format." echo "status=success" >> $GITHUB_OUTPUT + echo "$RESPONSE" | jq || echo "$RESPONSE" - name: "Test workflow: /generate-sync → /compile" run: | @@ -301,328 +303,3 @@ jobs: echo "Workflow test successful! Generated code compiles correctly." echo "$COMPILE_RESPONSE" | jq || echo "$COMPILE_RESPONSE" - - name: Test basic API endpoints (non-LLM) - run: | - # Test /compile endpoint with valid code first - echo "Testing /compile endpoint..." - COMPILE_RESPONSE=$(curl -s -S -f -X POST http://localhost:8000/compile \ - -H "Content-Type: application/json" \ - -d '{ - "code": "[filename: Cargo.toml]\n[package]\nname = \"hello_world\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\n\n[filename: src/main.rs]\nfn main() {\n println!(\"Hello, World!\");\n}" - }' || echo "CURL_FAILED") - - if [ "$COMPILE_RESPONSE" = "CURL_FAILED" ] || ! echo "$COMPILE_RESPONSE" | jq -e '.success == true' > /dev/null; then - echo "Basic compilation failed - API endpoints are not working properly" - exit 1 - fi - - echo "Basic API endpoints are working correctly" - - - name: Check LLM API connectivity - id: check-llm - continue-on-error: true - run: | - # Test LLM connectivity with a simple request - HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST http://localhost:8000/generate-sync \ - -H "Content-Type: application/json" \ - -d '{ - "description": "A simple hello world program", - "requirements": "Print hello" - }') - - if [[ "$HTTP_CODE" == "500" ]]; then - echo "LLM API key is invalid or service is unavailable" - echo "llm_available=false" >> $GITHUB_OUTPUT - else - echo "LLM service is available" - echo "llm_available=true" >> $GITHUB_OUTPUT - fi - - - name: Test LLM-dependent endpoints - if: steps.check-llm.outputs.llm_available == 'true' - run: | - echo "LLM service is available, testing all endpoints..." - # Continue with the rest of the tests for /generate-sync, etc. - - - name: Skip LLM-dependent tests - if: steps.check-llm.outputs.llm_available != 'true' - run: | - echo "NOTICE: Skipping LLM-dependent tests due to invalid API key or unavailable service" - echo "The following endpoints were not tested: /generate-sync, parts of /generate" - echo "Basic endpoints like /compile and /compile-and-fix are working correctly" - - - name: Check Docker logs on failure - if: failure() - run: | - echo "Checking Docker logs for troubleshooting..." - docker compose logs || echo "Failed to get logs" - - # test-with-external-llm: - # runs-on: ubuntu-latest - # needs: test # Run after the local tests complete - - # env: - # LLM_API_BASE: "https://0x9fcf7888963793472bfcb8c14f4b6b47a7462f17.gaia.domains/v1" - # LLM_MODEL: "Qwen2.5-Coder-3B-Instruct" - # LLM_EMBED_MODEL: "gte-Qwen2-1.5B-instruct" - # LLM_EMBED_SIZE: "1536" - # LLM_API_KEY: "" # No API key needed for this public node - # SKIP_VECTOR_SEARCH: "true" - - # services: - # qdrant: - # image: qdrant/qdrant:latest - # ports: - # - 6333:6333 - # - 6334:6334 - - # steps: - # - name: Checkout code - # uses: actions/checkout@v4.2.2 - - # - name: Install Python and dependencies - # uses: actions/setup-python@v4 - # with: - # python-version: '3.10' - - # # Add Rust setup - this was missing - # - name: Set up Rust - # uses: actions-rs/toolchain@v1 - # with: - # toolchain: stable - # profile: minimal - - # - name: Install jq and curl - # run: sudo apt-get install -y jq curl - - # - name: Install Python dependencies - # run: pip install -r requirements.txt - - # - name: Create test directories - # run: | - # mkdir -p output - # mkdir -p data/project_examples - # mkdir -p data/error_examples - - # - name: Start API server with external LLM - # run: | - # echo "Starting API server with external Gaia node..." - # # Set USE_MOCK_LLM=true in environment for this command - # export USE_MOCK_LLM=true - - # # Add a dummy API key for the public node to bypass validation - # export LLM_API_KEY="dummy_key_for_public_node" - - # # Add debug output to see what's being used - # echo "LLM_API_BASE: $LLM_API_BASE" - # echo "USE_MOCK_LLM: $USE_MOCK_LLM" - # echo "LLM_API_KEY is set to a dummy value for the public node" - - # # Redirect output to log file for debugging - # python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 > server.log 2>&1 & - # SERVER_PID=$! - # echo "Server started with PID: $SERVER_PID" - # sleep 10 # Give the server time to start - - # # Check if server process is still running - # if ps -p $SERVER_PID > /dev/null; then - # echo "Server process is running" - # else - # echo "Server process died. Check server logs:" - # cat server.log - # exit 1 - # fi - - # - name: Verify API server is running - # run: | - # HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/docs || echo "CURL_FAILED") - # if [[ "$HTTP_CODE" != "200" ]]; then - # echo "API server is not running properly. Status code: $HTTP_CODE" - # ps aux | grep uvicorn - # echo "Server logs:" - # cat server.log - # exit 1 - # fi - # echo "API server is running correctly" - - # - name: Test /compile endpoint with external LLM - # run: | - # echo "Testing /compile endpoint..." - # RESPONSE=$(curl -s -S -f -X POST http://localhost:8000/compile \ - # -H "Content-Type: application/json" \ - # -d '{ - # "code": "[filename: Cargo.toml]\n[package]\nname = \"hello_world\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\n\n[filename: src/main.rs]\nfn main() {\n println!(\"Hello, World!\");\n}" - # }' || echo "CURL_FAILED") - - # if [ "$RESPONSE" = "CURL_FAILED" ]; then - # echo "Failed to connect to API service" - # exit 1 - # fi - - # # Check for success in response - # if ! echo "$RESPONSE" | jq -e '.success == true' > /dev/null; then - # echo "Compilation failed:" - # echo "$RESPONSE" | jq || echo "$RESPONSE" - # exit 1 - # fi - - # echo "Compilation successful with external LLM!" - # echo "$RESPONSE" | jq || echo "$RESPONSE" - - # - name: Test /compile-and-fix endpoint with external LLM - # run: | - # echo "Testing /compile-and-fix endpoint..." - # RESPONSE=$(curl -s -S -f -X POST http://localhost:8000/compile-and-fix \ - # -H "Content-Type: application/json" \ - # -d '{ - # "code": "[filename: Cargo.toml]\n[package]\nname = \"hello_world\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\n\n[filename: src/main.rs]\nfn main() {\n println!(\"Hello, World!\\\" // Missing closing parenthesis\n}", - # "description": "A simple hello world program", - # "max_attempts": 3 - # }' || echo "CURL_FAILED") - - # if [ "$RESPONSE" = "CURL_FAILED" ]; then - # echo "Failed to connect to API service" - # exit 1 - # fi - - # # Save full response for debugging - # echo "$RESPONSE" > compile_fix_response.txt - - # # Check if response is JSON or text format - # if [[ "$RESPONSE" == {* ]]; then - # # JSON response (likely error) - # echo "Got JSON response (may be expected with external LLM):" - # echo "$RESPONSE" | jq || echo "$RESPONSE" - # else - # # Text response (success case) - # echo "Compile and fix successful with external LLM! Got text response with fixed code." - # fi - - # - name: Test /generate-sync with external LLM - # id: test-generate-sync-external - # continue-on-error: true - # run: | - # echo "Testing /generate-sync endpoint with external LLM..." - # RESPONSE=$(curl -X POST http://localhost:8000/generate-sync \ - # -H "Content-Type: application/json" \ - # -d '{"description": "A simple command-line calculator in Rust", "requirements": "Should support addition, subtraction, multiplication, and division"}') - - # HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST http://localhost:8000/generate-sync \ - # -H "Content-Type: application/json" \ - # -d '{"description": "A simple command-line calculator in Rust", "requirements": "Should support addition, subtraction, multiplication, and division"}') - - # echo "HTTP response code: $HTTP_CODE" - - # if [[ "$HTTP_CODE" != "200" ]]; then - # echo "External LLM generation failed with code $HTTP_CODE" - # echo "Response: $RESPONSE" - # echo "status=error" >> $GITHUB_OUTPUT - # exit 1 - # fi - - # # Save response to file for later use - # echo "$RESPONSE" > external_generate_output.txt - - # # Verify the response format has filename markers - # if ! echo "$RESPONSE" | grep -q "\[filename:"; then - # echo "Response does not contain filename markers:" - # echo "$RESPONSE" | head -20 - # echo "status=error" >> $GITHUB_OUTPUT - # exit 1 - # fi - - # # Check if this is a fallback template - # if echo "$RESPONSE" | grep -q "THIS IS A FALLBACK TEMPLATE - LLM generation failed"; then - # echo "WARNING: Response contains fallback template - external LLM generation failed" - # echo "status=fallback" >> $GITHUB_OUTPUT - # else - # echo "External LLM generate-sync successful! Response contains code files in text format." - # echo "status=success" >> $GITHUB_OUTPUT - # fi - - # - name: "Test workflow with external LLM: /generate-sync → /compile" - # if: steps.test-generate-sync-external.outcome == 'success' - # run: | - # echo "Testing workflow with external LLM: /generate-sync → /compile..." - - # # Check if response contains fallback template - # if grep -q "FALLBACK TEMPLATE" external_generate_output.txt; then - # echo "WARNING: Testing with fallback template code - external LLM generation failed but continuing with tests" - # fi - - # # Get the output from the previous step and remove the build status comment - # GENERATE_OUTPUT=$(cat external_generate_output.txt | sed '/^# Build/,$d') - - # # Pass the cleaned generated code directly to compile - # COMPILE_RESPONSE=$(curl -s -S -f -X POST http://localhost:8000/compile \ - # -H "Content-Type: application/json" \ - # -d "{ - # \"code\": $(echo "$GENERATE_OUTPUT" | jq -Rs .) - # }" || echo "CURL_FAILED") - - # if [ "$COMPILE_RESPONSE" = "CURL_FAILED" ]; then - # echo "Failed to connect to API service" - # exit 1 - # fi - - # # Check for success in response - # if ! echo "$COMPILE_RESPONSE" | jq -e '.success == true' > /dev/null; then - # echo "Compilation failed:" - # echo "$COMPILE_RESPONSE" | jq || echo "$COMPILE_RESPONSE" - # exit 1 - # fi - - # echo "External LLM workflow test successful! Generated code compiles correctly." - # echo "$COMPILE_RESPONSE" | jq || echo "$COMPILE_RESPONSE" - - # - name: Test /generate endpoint with external LLM - # continue-on-error: true - # run: | - # echo "Testing /generate endpoint with external LLM..." - - # # Generate the project - # RESPONSE=$(curl -s -S -f -X POST http://localhost:8000/generate \ - # -H "Content-Type: application/json" \ - # -d '{ - # "description": "A Rust program that converts between different units of measurement", - # "requirements": "Support length (m, cm, inch, feet), weight (kg, g, lb), and temperature (C, F, K)" - # }' || echo "CURL_FAILED") - - # if [ "$RESPONSE" = "CURL_FAILED" ]; then - # echo "Failed to connect to API service" - # exit 1 - # fi - - # # Extract project_id from response - # PROJECT_ID=$(echo "$RESPONSE" | jq -r '.project_id') - # echo "Project ID: $PROJECT_ID" - - # # Poll for project completion (maximum 6 attempts, 20 seconds apart) - # echo "Polling for project completion..." - # for i in {1..6}; do - # echo "Checking project status (attempt $i)..." - # STATUS_RESPONSE=$(curl -s -S -f "http://localhost:8000/project/$PROJECT_ID" || echo "CURL_FAILED") - - # if [ "$STATUS_RESPONSE" = "CURL_FAILED" ]; then - # echo "Failed to get project status" - # continue - # fi - - # STATUS=$(echo "$STATUS_RESPONSE" | jq -r '.status') - # echo "Current status: $STATUS" - - # if [ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ]; then - # echo "Project generation finished with status: $STATUS" - # echo "$STATUS_RESPONSE" | jq - # break - # fi - - # # If still processing, wait and try again - # if [ $i -eq 6 ]; then - # echo "Project generation taking too long, but this is acceptable for testing" - # break - # fi - - # echo "Waiting 20 seconds before next check..." - # sleep 20 - # done