Added mcp server and agent
This commit is contained in:
parent
fdf566b52f
commit
91df929dad
8 changed files with 506 additions and 7 deletions
|
|
@ -3,7 +3,7 @@ services:
|
||||||
|
|
||||||
fyp-redis:
|
fyp-redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
container_name: fyp-redis
|
container_name: ${REDIS_CONTAINER_NAME:-fyp-redis}
|
||||||
ports:
|
ports:
|
||||||
- "0.0.0.0:6379:6379"
|
- "0.0.0.0:6379:6379"
|
||||||
volumes:
|
volumes:
|
||||||
|
|
@ -36,31 +36,64 @@ services:
|
||||||
build:
|
build:
|
||||||
context: ../..
|
context: ../..
|
||||||
dockerfile: compose/dev/python/Dockerfile
|
dockerfile: compose/dev/python/Dockerfile
|
||||||
|
container_name: dynavera-api
|
||||||
ports:
|
ports:
|
||||||
- "0.0.0.0:8000:8000"
|
- "0.0.0.0:8000:8000"
|
||||||
volumes:
|
volumes:
|
||||||
- ../../:/app
|
- ../../:/app
|
||||||
- venv:/venv
|
- venv:/venv
|
||||||
environment:
|
environment:
|
||||||
CELERY_BROKER_URL: redis://fyp-redis:6379/0
|
DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:-dev-secret-key-change-in-production}
|
||||||
|
DJANGO_DEBUG: "true"
|
||||||
|
DJANGO_ALLOWED_HOSTS: "*"
|
||||||
|
DJANGO_CELERY_BROKER_URL: redis://${REDIS_CONTAINER_NAME:-fyp-redis}:6379/0
|
||||||
|
DJANGO_CORS_ALLOWED_ORIGINS: http://localhost:5173,http://127.0.0.1:5173
|
||||||
DJANGO_SETTINGS_MODULE: config.settings
|
DJANGO_SETTINGS_MODULE: config.settings
|
||||||
|
MCP_AGENT_URL: http://mcp-agent-server:8001
|
||||||
depends_on:
|
depends_on:
|
||||||
fyp-redis:
|
fyp-redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
web:
|
web:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
|
mcp-agent-server:
|
||||||
|
condition: service_started
|
||||||
|
|
||||||
celery:
|
celery:
|
||||||
build:
|
build:
|
||||||
context: ../..
|
context: ../..
|
||||||
dockerfile: compose/dev/celery/Dockerfile
|
dockerfile: compose/dev/celery/Dockerfile
|
||||||
|
container_name: dynavera-celery
|
||||||
volumes:
|
volumes:
|
||||||
- ../../:/app
|
- ../../:/app
|
||||||
- venv:/venv
|
- venv:/venv
|
||||||
- ${USERPROFILE}/.cache/gpt4all:/root/.cache/gpt4all:ro
|
- ${USERPROFILE}/.cache/gpt4all:/root/.cache/gpt4all:rw
|
||||||
environment:
|
environment:
|
||||||
CELERY_BROKER_URL: redis://fyp-redis:6379/0
|
DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:-dev-secret-key-change-in-production}
|
||||||
|
DJANGO_CELERY_BROKER_URL: redis://${REDIS_CONTAINER_NAME:-fyp-redis}:6379/0
|
||||||
DJANGO_SETTINGS_MODULE: config.settings
|
DJANGO_SETTINGS_MODULE: config.settings
|
||||||
|
MCP_AGENT_URL: http://mcp-agent-server:8001
|
||||||
|
depends_on:
|
||||||
|
fyp-redis:
|
||||||
|
condition: service_healthy
|
||||||
|
mcp-agent-server:
|
||||||
|
condition: service_started
|
||||||
|
|
||||||
|
mcp-agent-server:
|
||||||
|
build:
|
||||||
|
context: ../..
|
||||||
|
dockerfile: compose/dev/mcp/Dockerfile
|
||||||
|
container_name: dynavera-mcp-agent
|
||||||
|
ports:
|
||||||
|
- "0.0.0.0:8001:8001"
|
||||||
|
volumes:
|
||||||
|
- ../../:/app
|
||||||
|
- ${USERPROFILE}/.cache/gpt4all:/root/.cache/gpt4all:rw
|
||||||
|
- ../../build/rag_db:/app/build/rag_db:ro
|
||||||
|
environment:
|
||||||
|
DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:-dev-secret-key-change-in-production}
|
||||||
|
DJANGO_SETTINGS_MODULE: config.settings
|
||||||
|
PYTHONUNBUFFERED: "1"
|
||||||
|
HOME: /root
|
||||||
depends_on:
|
depends_on:
|
||||||
fyp-redis:
|
fyp-redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
|
||||||
24
compose/dev/mcp/Dockerfile
Normal file
24
compose/dev/mcp/Dockerfile
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
git \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY requirements/base.txt requirements/base.txt
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir -r requirements/base.txt && \
|
||||||
|
pip install --no-cache-dir mcp gpt4all sentence-transformers chromadb
|
||||||
|
|
||||||
|
COPY apps /app/apps
|
||||||
|
COPY config /app/config
|
||||||
|
COPY mcp_agent /app/mcp_agent
|
||||||
|
COPY manage.py /app/
|
||||||
|
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
ENV DJANGO_SETTINGS_MODULE=config.settings
|
||||||
|
EXPOSE 8001
|
||||||
|
|
||||||
|
CMD ["python", "-m", "mcp_agent.mcp_server"]
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -65,11 +65,13 @@ ROOT_URLCONF = f'{PARENT_NAME}.urls'
|
||||||
WSGI_APPLICATION = f'{PARENT_NAME}.wsgi.application'
|
WSGI_APPLICATION = f'{PARENT_NAME}.wsgi.application'
|
||||||
ASGI_APPLICATION = f'{PARENT_NAME}.asgi.application'
|
ASGI_APPLICATION = f'{PARENT_NAME}.asgi.application'
|
||||||
|
|
||||||
|
DJANGO_CELERY_BROKER_URL = os.getenv('DJANGO_CELERY_BROKER_URL', 'redis://localhost:6379/0')
|
||||||
|
|
||||||
CHANNEL_LAYERS = {
|
CHANNEL_LAYERS = {
|
||||||
'default': {
|
'default': {
|
||||||
'BACKEND': 'channels_redis.core.RedisChannelLayer',
|
'BACKEND': 'channels_redis.core.RedisChannelLayer',
|
||||||
'CONFIG': {
|
'CONFIG': {
|
||||||
'hosts': [os.getenv('CELERY_BROKER_URL')],
|
'hosts': [DJANGO_CELERY_BROKER_URL],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +154,7 @@ REST_FRAMEWORK = {
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
CELERY_BROKER_URL = os.getenv('DJANGO_CELERY_BROKER_URL')
|
CELERY_BROKER_URL = DJANGO_CELERY_BROKER_URL
|
||||||
CELERY_RESULT_BACKEND = 'django-db'
|
CELERY_RESULT_BACKEND = 'django-db'
|
||||||
CELERY_CACHE_BACKEND = 'django-cache'
|
CELERY_CACHE_BACKEND = 'django-cache'
|
||||||
CELERY_ACCEPT_CONTENT = ['json']
|
CELERY_ACCEPT_CONTENT = ['json']
|
||||||
|
|
@ -162,7 +164,10 @@ CELERY_TIMEZONE = 'UTC'
|
||||||
CELERY_TASK_TRACK_STARTED = True
|
CELERY_TASK_TRACK_STARTED = True
|
||||||
CELERY_TASK_TIME_LIMIT = 30 * 60
|
CELERY_TASK_TIME_LIMIT = 30 * 60
|
||||||
|
|
||||||
CORS_ALLOWED_ORIGINS = os.getenv('DJANGO_CORS_ALLOWED_ORIGINS').split(',') if os.getenv('DJANGO_CORS_ALLOWED_ORIGINS') else []
|
MCP_AGENT_URL = os.getenv('MCP_AGENT_URL', 'http://localhost:8001')
|
||||||
|
|
||||||
|
if origins:=os.getenv('DJANGO_CORS_ALLOWED_ORIGINS'):
|
||||||
|
CORS_ALLOWED_ORIGINS = origins.split(',')
|
||||||
CORS_ALLOW_CREDENTIALS = True
|
CORS_ALLOW_CREDENTIALS = True
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
|
|
|
||||||
0
mcp_agent/__init__.py
Normal file
0
mcp_agent/__init__.py
Normal file
129
mcp_agent/mcp_client.py
Normal file
129
mcp_agent/mcp_client.py
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
import httpx
|
||||||
|
import json
|
||||||
|
from typing import Optional, Dict, Any, List
|
||||||
|
from django.conf import settings
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class MCPAgentClient:
|
||||||
|
|
||||||
|
def __init__(self, server_url: Optional[str] = None):
|
||||||
|
self.server_url = server_url or getattr(settings, 'MCP_AGENT_URL', 'http://localhost:8001')
|
||||||
|
self.http_client = httpx.AsyncClient(
|
||||||
|
timeout=httpx.Timeout(300.0),
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
|
||||||
|
async def __aenter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
if self.http_client:
|
||||||
|
await self.http_client.aclose()
|
||||||
|
|
||||||
|
async def execute_agent(
|
||||||
|
self,
|
||||||
|
agent_id: str,
|
||||||
|
agent_name: str,
|
||||||
|
execution_id: str,
|
||||||
|
query: str,
|
||||||
|
input_data: Optional[Dict[str, Any]] = None
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
arguments = {
|
||||||
|
"agent_id": agent_id,
|
||||||
|
"agent_name": agent_name,
|
||||||
|
"execution_id": execution_id,
|
||||||
|
"query": query,
|
||||||
|
"input_data": input_data or {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await self._execute_via_http(arguments)
|
||||||
|
|
||||||
|
async def _execute_via_http(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
if not self.http_client:
|
||||||
|
raise RuntimeError("HTTP client not initialized")
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = await self.http_client.post(
|
||||||
|
f"{self.server_url}/execute",
|
||||||
|
json={
|
||||||
|
"tool": "execute_agent",
|
||||||
|
"arguments": arguments
|
||||||
|
},
|
||||||
|
headers={"Content-Type": "application/json"}
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
except httpx.HTTPStatusError as e:
|
||||||
|
logger.error(f"HTTP error from MCP server: {e.response.status_code} - {e.response.text}")
|
||||||
|
return {
|
||||||
|
"status": "failed",
|
||||||
|
"error": f"Server returned {e.response.status_code}",
|
||||||
|
"error_type": "HTTPError",
|
||||||
|
"details": e.response.text
|
||||||
|
}
|
||||||
|
except httpx.RequestError as e:
|
||||||
|
logger.error(f"Request error to MCP server: {e}")
|
||||||
|
return {
|
||||||
|
"status": "failed",
|
||||||
|
"error": f"Failed to connect to MCP server at {self.server_url}",
|
||||||
|
"error_type": "ConnectionError"
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Unexpected error in HTTP execution: {e}")
|
||||||
|
return {
|
||||||
|
"status": "failed",
|
||||||
|
"error": str(e),
|
||||||
|
"error_type": type(e).__name__
|
||||||
|
}
|
||||||
|
|
||||||
|
async def health_check(self) -> Dict[str, Any]:
|
||||||
|
try:
|
||||||
|
response = await self.http_client.get(f"{self.server_url}/health")
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except Exception as e:
|
||||||
|
return {"status": "unhealthy", "error": str(e)}
|
||||||
|
|
||||||
|
async def list_tools(self) -> List[Dict[str, Any]]:
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"name": "execute_agent",
|
||||||
|
"description": "Execute an AI agent with given query and input data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "health_check",
|
||||||
|
"description": "Check if the agent server is healthy"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
if self.http_client:
|
||||||
|
await self.http_client.aclose()
|
||||||
|
|
||||||
|
_mcp_client_instance: Optional[MCPAgentClient] = None
|
||||||
|
_client_lock = asyncio.Lock()
|
||||||
|
|
||||||
|
|
||||||
|
async def get_mcp_client() -> MCPAgentClient:
|
||||||
|
global _mcp_client_instance
|
||||||
|
|
||||||
|
async with _client_lock:
|
||||||
|
if _mcp_client_instance is None:
|
||||||
|
server_url = getattr(settings, 'MCP_AGENT_URL', 'http://localhost:8001')
|
||||||
|
_mcp_client_instance = MCPAgentClient(server_url=server_url)
|
||||||
|
|
||||||
|
return _mcp_client_instance
|
||||||
|
|
||||||
|
|
||||||
|
async def close_mcp_client():
|
||||||
|
global _mcp_client_instance
|
||||||
|
|
||||||
|
async with _client_lock:
|
||||||
|
if _mcp_client_instance is not None:
|
||||||
|
await _mcp_client_instance.close()
|
||||||
|
_mcp_client_instance = None
|
||||||
308
mcp_agent/mcp_server.py
Normal file
308
mcp_agent/mcp_server.py
Normal file
|
|
@ -0,0 +1,308 @@
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
project_root = Path(__file__).resolve().parent.parent
|
||||||
|
sys.path.insert(0, str(project_root))
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
||||||
|
import django
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from mcp.server import Server
|
||||||
|
from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
|
||||||
|
from django.utils import timezone
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
|
||||||
|
app = Server("dynavera-agent-runtime")
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_health(request: web.Request) -> web.Response:
|
||||||
|
try:
|
||||||
|
result = await check_health()
|
||||||
|
return web.json_response(result)
|
||||||
|
except Exception as e:
|
||||||
|
return web.json_response({"status": "unhealthy", "error": str(e)}, status=500)
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_execute(request: web.Request) -> web.Response:
|
||||||
|
try:
|
||||||
|
payload = await request.json()
|
||||||
|
tool = payload.get("tool")
|
||||||
|
arguments = payload.get("arguments", {}) or {}
|
||||||
|
if tool not in {"execute_agent", "health_check"}:
|
||||||
|
return web.json_response({"status": "failed", "error": f"Unknown tool: {tool}"}, status=400)
|
||||||
|
|
||||||
|
if tool == "execute_agent":
|
||||||
|
result = await run_agent_execution(arguments)
|
||||||
|
else:
|
||||||
|
result = await check_health()
|
||||||
|
|
||||||
|
return web.json_response(result)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return web.json_response({"status": "failed", "error": "Invalid JSON payload"}, status=400)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[MCP Server] HTTP execute error: {e}")
|
||||||
|
return web.json_response({"status": "failed", "error": str(e)}, status=500)
|
||||||
|
|
||||||
|
|
||||||
|
@app.list_tools()
|
||||||
|
async def list_tools() -> list[Tool]:
|
||||||
|
return [
|
||||||
|
Tool(
|
||||||
|
name="execute_agent",
|
||||||
|
description="Execute an AI agent with given query and input data. Supports RAG-enabled responses using local knowledge base.",
|
||||||
|
inputSchema={
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"agent_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "UUID of the agent to execute"
|
||||||
|
},
|
||||||
|
"agent_name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Name of the agent"
|
||||||
|
},
|
||||||
|
"execution_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "UUID of the execution record"
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "User query to process"
|
||||||
|
},
|
||||||
|
"input_data": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Additional input parameters"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["agent_id", "agent_name", "execution_id", "query"]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Tool(
|
||||||
|
name="health_check",
|
||||||
|
description="Check if the agent server is healthy and ready to process requests",
|
||||||
|
inputSchema={
|
||||||
|
"type": "object",
|
||||||
|
"properties": {}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@app.call_tool()
|
||||||
|
async def call_tool(name: str, arguments: dict) -> list[TextContent | ImageContent | EmbeddedResource]:
|
||||||
|
if name == "execute_agent":
|
||||||
|
result = await run_agent_execution(arguments)
|
||||||
|
return [TextContent(
|
||||||
|
type="text",
|
||||||
|
text=json.dumps(result, indent=2)
|
||||||
|
)]
|
||||||
|
|
||||||
|
elif name == "health_check":
|
||||||
|
health_info = await check_health()
|
||||||
|
return [TextContent(
|
||||||
|
type="text",
|
||||||
|
text=json.dumps(health_info, indent=2)
|
||||||
|
)]
|
||||||
|
|
||||||
|
raise ValueError(f"Unknown tool: {name}")
|
||||||
|
|
||||||
|
|
||||||
|
async def check_health() -> Dict[str, Any]:
|
||||||
|
import platform
|
||||||
|
|
||||||
|
MODEL_NAME = "Meta-Llama-3-8B-Instruct.Q4_0.gguf"
|
||||||
|
DEFAULT_MODEL_DIR = os.path.join(os.path.expanduser("~"), ".cache", "gpt4all")
|
||||||
|
MODEL_PATH = os.path.join(DEFAULT_MODEL_DIR, MODEL_NAME)
|
||||||
|
RAG_PATH = "./build/rag_db"
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "healthy",
|
||||||
|
"timestamp": timezone.now().isoformat(),
|
||||||
|
"platform": platform.platform(),
|
||||||
|
"python_version": platform.python_version(),
|
||||||
|
"model_available": os.path.exists(MODEL_PATH),
|
||||||
|
"model_path": MODEL_PATH,
|
||||||
|
"rag_available": os.path.exists(RAG_PATH),
|
||||||
|
"rag_path": RAG_PATH
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def run_agent_execution(arguments: dict) -> Dict[str, Any]:
|
||||||
|
agent_id = arguments["agent_id"]
|
||||||
|
agent_name = arguments["agent_name"]
|
||||||
|
execution_id = arguments["execution_id"]
|
||||||
|
query = arguments.get("query", "")
|
||||||
|
input_data = arguments.get("input_data", {})
|
||||||
|
|
||||||
|
print(f"[MCP Server] Executing agent {agent_name} (ID: {agent_id})")
|
||||||
|
print(f"[MCP Server] Execution ID: {execution_id}")
|
||||||
|
print(f"[MCP Server] Query: {query}")
|
||||||
|
|
||||||
|
if not query:
|
||||||
|
return {
|
||||||
|
"status": "error",
|
||||||
|
"message": "No query provided",
|
||||||
|
"execution_id": execution_id,
|
||||||
|
"timestamp": timezone.now().isoformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
from gpt4all import GPT4All
|
||||||
|
from sentence_transformers import SentenceTransformer
|
||||||
|
from chromadb import PersistentClient
|
||||||
|
|
||||||
|
MODEL_NAME = "Meta-Llama-3-8B-Instruct.Q4_0.gguf"
|
||||||
|
EMBEDDER_NAME = "all-MiniLM-L6-v2"
|
||||||
|
RAG_PATH = "./build/rag_db"
|
||||||
|
CONTEXT_SIZE = 8192
|
||||||
|
DEFAULT_MODEL_DIR = os.path.join(os.path.expanduser("~"), ".cache", "gpt4all")
|
||||||
|
MODEL_PATH = os.path.join(DEFAULT_MODEL_DIR, MODEL_NAME)
|
||||||
|
|
||||||
|
print(f"[MCP Server] MODEL_PATH={MODEL_PATH}")
|
||||||
|
|
||||||
|
# Check if model exists, fail if not
|
||||||
|
if not os.path.exists(MODEL_PATH):
|
||||||
|
error_msg = f"Model not found at {MODEL_PATH}"
|
||||||
|
print(f"[MCP Server] {error_msg}")
|
||||||
|
return {
|
||||||
|
"status": "failed",
|
||||||
|
"error": error_msg,
|
||||||
|
"error_type": "ModelNotFound",
|
||||||
|
"execution_id": execution_id,
|
||||||
|
"timestamp": timezone.now().isoformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
print("[MCP Server] Full pipeline - loading models")
|
||||||
|
events = []
|
||||||
|
|
||||||
|
# Initialize AI model
|
||||||
|
events.append({
|
||||||
|
"type": "progress",
|
||||||
|
"stage": "initializing",
|
||||||
|
"message": "Initializing AI model...",
|
||||||
|
"timestamp": timezone.now().isoformat()
|
||||||
|
})
|
||||||
|
|
||||||
|
# RAG retrieval if available
|
||||||
|
if os.path.exists(RAG_PATH):
|
||||||
|
print(f"[MCP Server] RAG path found at {RAG_PATH}")
|
||||||
|
try:
|
||||||
|
embedder = SentenceTransformer(EMBEDDER_NAME)
|
||||||
|
client = PersistentClient(path=RAG_PATH)
|
||||||
|
collection = client.get_collection("documents")
|
||||||
|
|
||||||
|
events.append({
|
||||||
|
"type": "progress",
|
||||||
|
"stage": "retrieval",
|
||||||
|
"message": "Retrieving relevant context...",
|
||||||
|
"timestamp": timezone.now().isoformat()
|
||||||
|
})
|
||||||
|
|
||||||
|
query_embedding = embedder.encode(query).tolist()
|
||||||
|
results = collection.query(query_embeddings=[query_embedding], n_results=3)
|
||||||
|
|
||||||
|
retrieved_docs = []
|
||||||
|
if results and results.get('documents'):
|
||||||
|
retrieved_docs = results['documents'][0]
|
||||||
|
|
||||||
|
context = "\n\n".join(retrieved_docs) if retrieved_docs else ""
|
||||||
|
|
||||||
|
events.append({
|
||||||
|
"type": "progress",
|
||||||
|
"stage": "retrieved",
|
||||||
|
"message": f"Retrieved {len(retrieved_docs)} relevant documents",
|
||||||
|
"timestamp": timezone.now().isoformat()
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as rag_error:
|
||||||
|
print(f"[MCP Server] RAG error: {rag_error}")
|
||||||
|
context = ""
|
||||||
|
events.append({
|
||||||
|
"type": "warning",
|
||||||
|
"message": f"RAG retrieval failed: {str(rag_error)}",
|
||||||
|
"timestamp": timezone.now().isoformat()
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
context = ""
|
||||||
|
|
||||||
|
# Load and run LLM
|
||||||
|
events.append({
|
||||||
|
"type": "progress",
|
||||||
|
"stage": "generating",
|
||||||
|
"message": "Generating response...",
|
||||||
|
"timestamp": timezone.now().isoformat()
|
||||||
|
})
|
||||||
|
|
||||||
|
model = GPT4All(MODEL_NAME, model_path=DEFAULT_MODEL_DIR, allow_download=False)
|
||||||
|
|
||||||
|
if context:
|
||||||
|
prompt = f"Context:\n{context}\n\nQuestion: {query}\n\nAnswer:"
|
||||||
|
else:
|
||||||
|
prompt = f"Question: {query}\n\nAnswer:"
|
||||||
|
|
||||||
|
print(f"[MCP Server] Running model inference...")
|
||||||
|
response = model.generate(prompt, max_tokens=512, temp=0.7)
|
||||||
|
|
||||||
|
print(f"[MCP Server] Generated response: {response[:100]}...")
|
||||||
|
|
||||||
|
events.append({
|
||||||
|
"type": "progress",
|
||||||
|
"stage": "completed",
|
||||||
|
"message": "Response generated successfully",
|
||||||
|
"timestamp": timezone.now().isoformat()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "completed",
|
||||||
|
"query": query,
|
||||||
|
"response": response,
|
||||||
|
"method": "rag" if context else "direct",
|
||||||
|
"context_used": bool(context),
|
||||||
|
"timestamp": timezone.now().isoformat(),
|
||||||
|
"agent_name": agent_name,
|
||||||
|
"execution_id": execution_id,
|
||||||
|
"events": events
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[MCP Server] Error during execution: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "failed",
|
||||||
|
"error": str(e),
|
||||||
|
"error_type": type(e).__name__,
|
||||||
|
"execution_id": execution_id,
|
||||||
|
"timestamp": timezone.now().isoformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def run_http_server():
|
||||||
|
host = os.getenv("MCP_HTTP_HOST", "0.0.0.0")
|
||||||
|
port = int(os.getenv("MCP_HTTP_PORT", "8001"))
|
||||||
|
app_http = web.Application()
|
||||||
|
app_http.router.add_post("/execute", handle_execute)
|
||||||
|
app_http.router.add_get("/health", handle_health)
|
||||||
|
|
||||||
|
runner = web.AppRunner(app_http)
|
||||||
|
await runner.setup()
|
||||||
|
site = web.TCPSite(runner, host=host, port=port)
|
||||||
|
await site.start()
|
||||||
|
print(f"[MCP Server] HTTP server listening on {host}:{port}", file=sys.stderr)
|
||||||
|
|
||||||
|
await asyncio.Event().wait()
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
await run_http_server()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
Loading…
Reference in a new issue