Spaces:
Sleeping
Sleeping
| """ | |
| KCSC MCP ์๋ฒ ์ ํ๋ฆฌ์ผ์ด์ | |
| ํ๊ตญ๊ฑด์ค๊ธฐ์ค์ผํฐ(KCSC) API์ Model Context Protocol(MCP)์ ์ฐ๋ํ | |
| ์์ฐ์ด ๊ฒ์ ๋ฐ ์ง์์๋ต API ์๋ฒ | |
| """ | |
| import logging | |
| import os | |
| import json | |
| import time | |
| import uuid | |
| from datetime import datetime | |
| from typing import List, Optional, Dict, Any, Union | |
| from fastapi import FastAPI, HTTPException, Depends, Request, BackgroundTasks | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import JSONResponse | |
| from fastapi.openapi.docs import get_swagger_ui_html | |
| from fastapi.openapi.utils import get_openapi | |
| from pydantic import BaseModel, Field, validator | |
| from src import config | |
| from src.kcsc_api_client import KCSCApiClient | |
| from src.vector_db_client import KCSCVectorDB | |
| from src.mcp_processor import MCPProcessor | |
| from src.utils import ( | |
| format_error_response, | |
| KCSCBaseException, | |
| sanitize_string | |
| ) | |
| # ์ค์ ์ด๊ธฐํ | |
| config.init() | |
| logger = config.setup_logger("app") | |
| # FastAPI ์ฑ ์ด๊ธฐํ | |
| app = FastAPI( | |
| title="KCSC MCP API", | |
| description="ํ๊ตญ๊ฑด์ค๊ธฐ์ค์ผํฐ(KCSC) API์ Model Context Protocol(MCP)์ ์ฐ๋ํ ์์ฐ์ด ๊ฒ์ ๋ฐ ์ง์์๋ต API", | |
| version="1.0.1", | |
| docs_url=None, # ๊ธฐ๋ณธ ๋ฌธ์ ๊ฒฝ๋ก ๋นํ์ฑํ (์ปค์คํ ๊ฒฝ๋ก ์ฌ์ฉ) | |
| redoc_url=None, # Redoc ๋นํ์ฑํ | |
| ) | |
| # CORS ์ค์ | |
| origins = [ | |
| "http://localhost", | |
| "http://localhost:8000", | |
| "http://127.0.0.1", | |
| "http://127.0.0.1:8000", | |
| # ํ์์ ๋ฐ๋ผ ์ถ๊ฐ ๋๋ฉ์ธ ํ์ฉ | |
| ] | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=origins, | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # ๋ชจ๋ธ ์ ์ | |
| class SearchQuery(BaseModel): | |
| """๊ฒ์ ์ฟผ๋ฆฌ ๋ชจ๋ธ""" | |
| query: str = Field(..., description="๊ฒ์์ด") | |
| doc_types: Optional[List[str]] = Field(None, description="๋ฌธ์ ์ ํ ๋ชฉ๋ก") | |
| limit: Optional[int] = Field(5, description="๊ฒ์ ๊ฒฐ๊ณผ ์", ge=1, le=20) | |
| def validate_query(cls, v): | |
| if not v or not v.strip(): | |
| raise ValueError('๊ฒ์์ด๋ ๋น์๋ ์ ์์ต๋๋ค.') | |
| return sanitize_string(v.strip()) | |
| class MCPQuery(BaseModel): | |
| """MCP ์ฟผ๋ฆฌ ๋ชจ๋ธ""" | |
| query: str = Field(..., description="์ง์ ๋ด์ฉ") | |
| conversation_id: Optional[str] = Field(None, description="๋ํ ID") | |
| enable_rag: bool = Field(True, description="RAG(๊ฒ์ ์ฆ๊ฐ ์์ฑ) ์ฌ์ฉ ์ฌ๋ถ") | |
| system_prompt: Optional[str] = Field(None, description="์์คํ ํ๋กฌํํธ") | |
| temperature: Optional[float] = Field(0.2, description="์์ฑ ์จ๋(์ฐฝ์์ฑ)", ge=0.0, le=1.0) | |
| max_tokens: Optional[int] = Field(2000, description="์ต๋ ์์ฑ ํ ํฐ ์", ge=1, le=4096) | |
| def validate_query(cls, v): | |
| if not v or not v.strip(): | |
| raise ValueError('์ง์ ๋ด์ฉ์ ๋น์๋ ์ ์์ต๋๋ค.') | |
| return sanitize_string(v.strip()) | |
| class CodeDetailRequest(BaseModel): | |
| """์ฝ๋ ์์ธ ์ ๋ณด ์์ฒญ ๋ชจ๋ธ""" | |
| code: str = Field(..., description="์ฝ๋ ๋ฒํธ") | |
| doc_type: str = Field(..., description="๋ฌธ์ ์ ํ") | |
| def validate_fields(cls, v): | |
| if not v or not v.strip(): | |
| raise ValueError('ํ๋๋ ๋น์๋ ์ ์์ต๋๋ค.') | |
| return v.strip() | |
| class SearchResult(BaseModel): | |
| """๊ฒ์ ๊ฒฐ๊ณผ ๋ชจ๋ธ""" | |
| id: str = Field(..., description="๊ฒฐ๊ณผ ID") | |
| text: str = Field(..., description="๊ฒฐ๊ณผ ํ ์คํธ") | |
| metadata: Dict[str, Any] = Field(..., description="๋ฉํ๋ฐ์ดํฐ") | |
| relevance: float = Field(..., description="๊ด๋ จ๋ ์ ์", ge=0.0, le=1.0) | |
| class SearchResponse(BaseModel): | |
| """๊ฒ์ ์๋ต ๋ชจ๋ธ""" | |
| results: List[SearchResult] = Field(..., description="๊ฒ์ ๊ฒฐ๊ณผ ๋ชฉ๋ก") | |
| query: str = Field(..., description="์๋ณธ ๊ฒ์์ด") | |
| timestamp: str = Field(..., description="ํ์์คํฌํ") | |
| class CollectRequest(BaseModel): | |
| """๋ฐ์ดํฐ ์์ง ์์ฒญ ๋ชจ๋ธ""" | |
| doc_types: Optional[List[str]] = Field(["KDS", "KCS"], description="์์งํ ๋ฌธ์ ์ ํ ๋ชฉ๋ก") | |
| class IndexParams(BaseModel): | |
| """์ธ๋ฑ์ค ๊ตฌ์ถ ํ๋ผ๋ฏธํฐ ๋ชจ๋ธ""" | |
| chunk_size: Optional[int] = Field(None, description="์ฒญํฌ ํฌ๊ธฐ") | |
| overlap: Optional[int] = Field(None, description="์ฒญํฌ ๊ฐ ๊ฒน์น๋ ๋ฌธ์ ์") | |
| reset: Optional[bool] = Field(True, description="๊ธฐ์กด ๋ฐ์ดํฐ ๋ฆฌ์ ์ฌ๋ถ") | |
| # ์ข ์์ฑ ์ฃผ์ - ์ฑ๊ธํด ๊ฐ์ฒด | |
| def get_kcsc_api(): | |
| """KCSC API ํด๋ผ์ด์ธํธ ๋ฐํ""" | |
| return KCSCApiClient() | |
| def get_vector_db(): | |
| """๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํด๋ผ์ด์ธํธ ๋ฐํ""" | |
| return KCSCVectorDB() | |
| def get_mcp_processor(): | |
| """MCP ํ๋ก์ธ์ ๋ฐํ""" | |
| return MCPProcessor() | |
| # ๋ฐฑ๊ทธ๋ผ์ด๋ ์์ | |
| def background_data_collection(api_client, doc_types): | |
| """๋ฐฑ๊ทธ๋ผ์ด๋ ๋ฐ์ดํฐ ์์ง ์์ """ | |
| try: | |
| logger.info(f"๋ฐฑ๊ทธ๋ผ์ด๋ ๋ฐ์ดํฐ ์์ง ์์: {doc_types}") | |
| all_codes = api_client.collect_codes(doc_types) | |
| for doc_type, codes in all_codes.items(): | |
| logger.info(f"{doc_type} ์์ธ ์ ๋ณด ์์ง ์์ (์ด {len(codes)}๊ฐ ์ฝ๋)") | |
| api_client.fetch_code_details(doc_type, codes) | |
| logger.info(f"๋ฐฑ๊ทธ๋ผ์ด๋ ๋ฐ์ดํฐ ์์ง ์๋ฃ") | |
| except Exception as e: | |
| logger.error(f"๋ฐฑ๊ทธ๋ผ์ด๋ ๋ฐ์ดํฐ ์์ง ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| def background_index_building(vector_db, params): | |
| """๋ฐฑ๊ทธ๋ผ์ด๋ ์ธ๋ฑ์ค ๊ตฌ์ถ ์์ """ | |
| try: | |
| logger.info("๋ฐฑ๊ทธ๋ผ์ด๋ ์ธ๋ฑ์ค ๊ตฌ์ถ ์์") | |
| vector_db.build_index_from_files( | |
| chunk_size=params.get("chunk_size"), | |
| overlap=params.get("overlap"), | |
| reset=params.get("reset", True) | |
| ) | |
| logger.info("๋ฐฑ๊ทธ๋ผ์ด๋ ์ธ๋ฑ์ค ๊ตฌ์ถ ์๋ฃ") | |
| except Exception as e: | |
| logger.error(f"๋ฐฑ๊ทธ๋ผ์ด๋ ์ธ๋ฑ์ค ๊ตฌ์ถ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| # ์๋ฌ ํธ๋ค๋ฌ | |
| async def kcsc_exception_handler(request: Request, exc: KCSCBaseException): | |
| """KCSC ์์ธ ์ฒ๋ฆฌ๊ธฐ""" | |
| return JSONResponse( | |
| status_code=500, | |
| content=format_error_response(exc) | |
| ) | |
| async def general_exception_handler(request: Request, exc: Exception): | |
| """์ผ๋ฐ ์์ธ ์ฒ๋ฆฌ๊ธฐ""" | |
| logger.error(f"์์ฒญ ์ฒ๋ฆฌ ์ค ์์ธ ๋ฐ์: {str(exc)}") | |
| return JSONResponse( | |
| status_code=500, | |
| content={ | |
| "status": "error", | |
| "error": { | |
| "type": exc.__class__.__name__, | |
| "message": str(exc), | |
| "timestamp": datetime.now().isoformat() | |
| } | |
| } | |
| ) | |
| # ์๋ฒ ์์ ์ ์ด๊ธฐํ | |
| async def startup_event(): | |
| """์๋ฒ ์์ ์ด๋ฒคํธ ํธ๋ค๋ฌ""" | |
| logger.info("์๋ฒ ์์ ์ค...") | |
| try: | |
| # ํ์ํ ๋๋ ํ ๋ฆฌ ์์ฑ | |
| os.makedirs(config.DATA_DIR, exist_ok=True) | |
| os.makedirs(config.LOGS_DIR, exist_ok=True) | |
| os.makedirs(config.VECTOR_DB_DIR, exist_ok=True) | |
| # ๋ชจ๋ ์ด๊ธฐํ | |
| kcsc_api = get_kcsc_api() | |
| vector_db = get_vector_db() | |
| mcp_processor = get_mcp_processor() | |
| # ๋ฒกํฐ DB ์ด๊ธฐํ ์ํ ํ์ธ | |
| stats = vector_db.get_collection_stats() | |
| has_data = any(count > 0 for count in stats.values()) | |
| if not has_data: | |
| logger.info("๋ฒกํฐ DB์ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค. ๋ฐ์ดํฐ ์์ง ๋ฐ ์ธ๋ฑ์ฑ์ด ํ์ํฉ๋๋ค.") | |
| logger.info("์๋ฒ ์์ ์๋ฃ") | |
| except Exception as e: | |
| logger.error(f"์๋ฒ ์์ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| # ์ค๋ฅ๊ฐ ์๋๋ผ๋ ์๋ฒ๋ ์คํ (ํ์์ ์ผ๋ถ ๊ธฐ๋ฅ๋ง ์ ํ) | |
| # API ์๋ํฌ์ธํธ ๊ตฌํ | |
| async def root(): | |
| """๋ฃจํธ ์๋ํฌ์ธํธ""" | |
| return { | |
| "status": "online", | |
| "message": "KCSC MCP API ์๋ฒ ์คํ ์ค", | |
| "version": app.version, | |
| "timestamp": datetime.now().isoformat() | |
| } | |
| # ๋ฌธ์ ์ปค์คํ ์๋ํฌ์ธํธ | |
| async def custom_swagger_ui_html(): | |
| """์ปค์คํ Swagger UI""" | |
| return get_swagger_ui_html( | |
| openapi_url="/openapi.json", | |
| title=app.title + " - API ๋ฌธ์", | |
| swagger_js_url="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4/swagger-ui-bundle.js", | |
| swagger_css_url="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4/swagger-ui.css", | |
| ) | |
| async def get_open_api_endpoint(): | |
| """OpenAPI ์คํค๋ง""" | |
| return get_openapi( | |
| title=app.title, | |
| version=app.version, | |
| description=app.description, | |
| routes=app.routes, | |
| ) | |
| # MCP ํ๋กํ ์ฝ ๋ฉํ๋ฐ์ดํฐ ์๋ํฌ์ธํธ | |
| async def mcp_metadata(): | |
| """MCP ์๋ฒ ๋ฉํ๋ฐ์ดํฐ - Claude Desktop์์ ์๋ฒ ๊ฒ์ฆ์ ์ฌ์ฉ""" | |
| return { | |
| "name": "KCSC API", | |
| "description": "ํ๊ตญ๊ฑด์ค๊ธฐ์ค์ผํฐ(KCSC) API๋ฅผ ํตํ ๊ฒ์ ๋ฐ ์ปจํ ์คํธ ์ ๊ณต", | |
| "version": app.version, | |
| "actions": [ | |
| { | |
| "name": "search", | |
| "description": "๊ฑด์ค๊ธฐ์ค ์ ๋ณด ๊ฒ์", | |
| "parameters": { | |
| "query": "๊ฒ์์ด", | |
| "doc_types": "๋ฌธ์ ์ ํ (์ ํ์ )", | |
| "limit": "๊ฒฐ๊ณผ ์ (์ ํ์ )" | |
| } | |
| }, | |
| { | |
| "name": "code_detail", | |
| "description": "ํน์ ์ฝ๋์ ์์ธ ์ ๋ณด ์กฐํ", | |
| "parameters": { | |
| "code": "์ฝ๋ ๋ฒํธ", | |
| "doc_type": "๋ฌธ์ ์ ํ" | |
| } | |
| } | |
| ] | |
| } | |
| # MCP ๋ฉ์์ง ์ฒ๋ฆฌ ์๋ํฌ์ธํธ - Claude Desktop๊ณผ ํต์ ํ๋ ํต์ฌ ๋ถ๋ถ | |
| async def handle_mcp_message( | |
| request: Request, | |
| vector_db: KCSCVectorDB = Depends(get_vector_db), | |
| kcsc_api: KCSCApiClient = Depends(get_kcsc_api), | |
| mcp_processor: MCPProcessor = Depends(get_mcp_processor) | |
| ): | |
| """ | |
| Claude Desktop๊ณผ ํธํ๋๋ MCP ๋ฉ์์ง ์ฒ๋ฆฌ | |
| ์ด ์๋ํฌ์ธํธ๋ MCP ํ๋กํ ์ฝ์ ํตํด Claude Desktop๊ณผ ํต์ ํฉ๋๋ค. | |
| """ | |
| try: | |
| # ์์ฒญ ๋ฐ์ดํฐ ํ์ฑ | |
| data = await request.json() | |
| message = data.get("message", "") | |
| conversation_id = data.get("conversation_id") | |
| action = data.get("action") | |
| action_params = data.get("action_params", {}) | |
| logger.info(f"MCP ๋ฉ์์ง ์์ : action={action}, message_prefix={message[:30] if message else 'None'}...") | |
| # ์ก์ ์ด ์์ผ๋ฉด ๊ธฐ๋ณธ ๊ฒ์ ์ํ | |
| if not action: | |
| # ๊ฒ์ ์ฟผ๋ฆฌ๋ก ์ฒ๋ฆฌ | |
| search_results = vector_db.search( | |
| query=message, | |
| limit=3 | |
| ) | |
| # ์ปจํ ์คํธ ํ์์ผ๋ก ๋ณํ | |
| context = [] | |
| for i, result in enumerate(search_results): | |
| context.append({ | |
| "id": f"result-{i}", | |
| "content": result["text"], | |
| "metadata": { | |
| "source": "KCSC", | |
| "code": result["metadata"]["code"], | |
| "name": result["metadata"]["name"], | |
| "doc_type": result["metadata"]["doc_type"], | |
| "relevance": result["relevance"] | |
| } | |
| }) | |
| logger.info(f"๊ธฐ๋ณธ ๊ฒ์ ์๋ฃ: {len(context)}๊ฐ ๊ฒฐ๊ณผ") | |
| # ์๋ต ์์ฑ | |
| return { | |
| "content": "๋ค์์ ํ๊ตญ๊ฑด์ค๊ธฐ์ค์ผํฐ(KCSC)์์ ๊ฒ์ํ ๊ด๋ จ ์ ๋ณด์ ๋๋ค:", | |
| "context": context, | |
| "conversation_id": conversation_id | |
| } | |
| # ์ก์ ๋ณ ์ฒ๋ฆฌ | |
| elif action == "search": | |
| # ๊ฒ์ ํ๋ผ๋ฏธํฐ ์ถ์ถ | |
| query = action_params.get("query", message) | |
| doc_types = action_params.get("doc_types") | |
| limit = int(action_params.get("limit", 5)) | |
| # ๊ฒ์ ์ํ | |
| search_results = vector_db.search( | |
| query=query, | |
| doc_types=doc_types, | |
| limit=limit | |
| ) | |
| # ์ปจํ ์คํธ ํ์์ผ๋ก ๋ณํ | |
| context = [] | |
| for i, result in enumerate(search_results): | |
| context.append({ | |
| "id": f"result-{i}", | |
| "content": result["text"], | |
| "metadata": { | |
| "source": "KCSC", | |
| "code": result["metadata"]["code"], | |
| "name": result["metadata"]["name"], | |
| "doc_type": result["metadata"]["doc_type"], | |
| "relevance": result["relevance"] | |
| } | |
| }) | |
| logger.info(f"๊ฒ์ ์ก์ ์๋ฃ: '{query}'์ ๋ํด {len(context)}๊ฐ ๊ฒฐ๊ณผ") | |
| # ์๋ต ์์ฑ | |
| return { | |
| "content": f"'{query}'์ ๋ํ ๊ฒ์ ๊ฒฐ๊ณผ์ ๋๋ค:", | |
| "context": context, | |
| "conversation_id": conversation_id | |
| } | |
| elif action == "code_detail": | |
| # ์ฝ๋ ํ๋ผ๋ฏธํฐ ์ถ์ถ | |
| code = action_params.get("code") | |
| doc_type = action_params.get("doc_type") | |
| if not code or not doc_type: | |
| return { | |
| "content": "์ฝ๋ ๋ฒํธ์ ๋ฌธ์ ์ ํ์ด ํ์ํฉ๋๋ค.", | |
| "conversation_id": conversation_id | |
| } | |
| # ์ฝ๋ ์์ธ ์ ๋ณด ์กฐํ | |
| detail = kcsc_api.get_code_details(doc_type, code) | |
| if not detail: | |
| return { | |
| "content": f"์ฝ๋ {doc_type}/{code}์ ๋ํ ์ ๋ณด๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.", | |
| "conversation_id": conversation_id | |
| } | |
| # ํฌ๋งทํ ๋ ์์ธ ์ ๋ณด | |
| formatted_detail = f""" | |
| ์ฝ๋: {detail.get('Code')} | |
| ์ด๋ฆ: {detail.get('Name')} | |
| ๋ฒ์ : {detail.get('Version')} | |
| ์ ๋ฐ์ดํธ: {detail.get('UpdateDate')} | |
| ๋ด์ฉ: {detail.get('Contents')} | |
| """ | |
| # ์๋ต ์์ฑ | |
| return { | |
| "content": formatted_detail, | |
| "context": [{ | |
| "id": f"{doc_type}-{code}", | |
| "content": detail.get('Contents', ''), | |
| "metadata": { | |
| "source": "KCSC", | |
| "code": detail.get('Code'), | |
| "name": detail.get('Name'), | |
| "doc_type": doc_type | |
| } | |
| }], | |
| "conversation_id": conversation_id | |
| } | |
| else: | |
| return { | |
| "content": f"์ง์ํ์ง ์๋ ์ก์ ์ ๋๋ค: {action}", | |
| "conversation_id": conversation_id | |
| } | |
| except Exception as e: | |
| logger.error(f"MCP ๋ฉ์์ง ์ฒ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| return { | |
| "content": f"์ค๋ฅ ๋ฐ์: {str(e)}", | |
| "conversation_id": conversation_id | |
| } | |
| async def get_status( | |
| kcsc_api: KCSCApiClient = Depends(get_kcsc_api), | |
| vector_db: KCSCVectorDB = Depends(get_vector_db) | |
| ): | |
| """์๋ฒ ์ํ ํ์ธ""" | |
| try: | |
| # ๋ฒกํฐ DB ์ํ ํ์ธ | |
| db_stats = vector_db.get_collection_stats() | |
| return { | |
| "status": "online", | |
| "vector_db": db_stats, | |
| "timestamp": datetime.now().isoformat(), | |
| "version": app.version | |
| } | |
| except Exception as e: | |
| logger.error(f"์ํ ํ์ธ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def collect_data( | |
| request: CollectRequest, | |
| background_tasks: BackgroundTasks, | |
| kcsc_api: KCSCApiClient = Depends(get_kcsc_api) | |
| ): | |
| """ | |
| KCSC API์์ ๋ฐ์ดํฐ ์์ง ์์ | |
| ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋ฐ์ดํฐ๋ฅผ ์์งํ์ฌ ์๋ฒ ์๋ต์ ์ฆ์ ๋ฐํํฉ๋๋ค. | |
| """ | |
| try: | |
| doc_types = request.doc_types | |
| logger.info(f"๋ฐ์ดํฐ ์์ง ์์ฒญ: {doc_types}") | |
| # ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋ฐ์ดํฐ ์์ง ์์ | |
| background_tasks.add_task(background_data_collection, kcsc_api, doc_types) | |
| return { | |
| "status": "accepted", | |
| "message": "๋ฐ์ดํฐ ์์ง์ด ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์์๋์์ต๋๋ค.", | |
| "doc_types": doc_types, | |
| "timestamp": datetime.now().isoformat() | |
| } | |
| except Exception as e: | |
| logger.error(f"๋ฐ์ดํฐ ์์ง ์์ฒญ ์ฒ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def build_index( | |
| params: IndexParams = None, | |
| background_tasks: BackgroundTasks = None, | |
| vector_db: KCSCVectorDB = Depends(get_vector_db) | |
| ): | |
| """ | |
| ๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ๋ฑ์ค ๊ตฌ์ถ | |
| ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์ธ๋ฑ์ค๋ฅผ ๊ตฌ์ถํ์ฌ ์๋ฒ ์๋ต์ ์ฆ์ ๋ฐํํฉ๋๋ค. | |
| """ | |
| try: | |
| logger.info("๋ฒกํฐ ์ธ๋ฑ์ค ๊ตฌ์ถ ์์ฒญ") | |
| params_dict = params.dict() if params else {} | |
| # ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์ธ๋ฑ์ค ๊ตฌ์ถ ์์ | |
| background_tasks.add_task(background_index_building, vector_db, params_dict) | |
| return { | |
| "status": "accepted", | |
| "message": "๋ฒกํฐ ์ธ๋ฑ์ค ๊ตฌ์ถ์ด ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์์๋์์ต๋๋ค.", | |
| "parameters": params_dict, | |
| "timestamp": datetime.now().isoformat() | |
| } | |
| except Exception as e: | |
| logger.error(f"์ธ๋ฑ์ค ๊ตฌ์ถ ์์ฒญ ์ฒ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def search( | |
| query: SearchQuery, | |
| vector_db: KCSCVectorDB = Depends(get_vector_db) | |
| ): | |
| """ | |
| ์์ฐ์ด ๊ฒ์ ์ํ | |
| ๋ฌธ์ ๋ด์ฉ์ ์์ฐ์ด ์ฟผ๋ฆฌ๋ก ๊ฒ์ํฉ๋๋ค. | |
| """ | |
| try: | |
| logger.info(f"๊ฒ์ ์์ฒญ: {query.query}") | |
| # ๊ฒ์ ์ํ | |
| results = vector_db.search( | |
| query=query.query, | |
| doc_types=query.doc_types, | |
| limit=query.limit | |
| ) | |
| # ์๋ต ํ์ ๋ณํ | |
| formatted_results = [] | |
| for result in results: | |
| formatted_results.append(SearchResult( | |
| id=result["id"], | |
| text=result["text"], | |
| metadata=result["metadata"], | |
| relevance=result["relevance"] | |
| )) | |
| return SearchResponse( | |
| results=formatted_results, | |
| query=query.query, | |
| timestamp=datetime.now().isoformat() | |
| ) | |
| except Exception as e: | |
| logger.error(f"๊ฒ์ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def get_code_detail( | |
| request: CodeDetailRequest, | |
| kcsc_api: KCSCApiClient = Depends(get_kcsc_api) | |
| ): | |
| """ | |
| ํน์ ์ฝ๋์ ์์ธ ์ ๋ณด ์กฐํ | |
| ์ฝ๋ ๋ฒํธ์ ๋ฌธ์ ์ ํ์ผ๋ก ์์ธ ์ ๋ณด๋ฅผ ์กฐํํฉ๋๋ค. | |
| """ | |
| try: | |
| logger.info(f"์ฝ๋ ์์ธ ์ ๋ณด ์์ฒญ: {request.doc_type}/{request.code}") | |
| # ์ฝ๋ ์์ธ ์ ๋ณด ์กฐํ | |
| detail = kcsc_api.get_code_details(request.doc_type, request.code) | |
| if not detail: | |
| raise HTTPException(status_code=404, detail="ํด๋น ์ฝ๋ ์ ๋ณด๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.") | |
| return detail | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"์ฝ๋ ์์ธ ์ ๋ณด ์กฐํ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def process_mcp_query( | |
| query: MCPQuery, | |
| vector_db: KCSCVectorDB = Depends(get_vector_db), | |
| mcp_processor: MCPProcessor = Depends(get_mcp_processor) | |
| ): | |
| """ | |
| MCP๋ฅผ ํตํ ์ฟผ๋ฆฌ ์ฒ๋ฆฌ | |
| RAG(๊ฒ์ ์ฆ๊ฐ ์์ฑ)์ ํ์ฉํ์ฌ ์ง์์ ๋ต๋ณํฉ๋๋ค. | |
| """ | |
| try: | |
| logger.info(f"MCP ์ฟผ๋ฆฌ ์์ฒญ: {query.query[:50]}...") | |
| # ์ปจํ ์คํธ ๋ฐ์ดํฐ ์ค๋น (RAG ์ฌ์ฉํ๋ ๊ฒฝ์ฐ) | |
| context_data = None | |
| if query.enable_rag: | |
| search_results = vector_db.search(query.query, limit=3) | |
| if search_results: | |
| context_data = [ | |
| { | |
| "id": result["id"], | |
| "content": result["text"], | |
| "metadata": { | |
| "title": result["metadata"]["name"], | |
| "code": result["metadata"]["code"], | |
| "doc_type": result["metadata"]["doc_type"], | |
| "relevance": result["relevance"] | |
| } | |
| } | |
| for result in search_results | |
| ] | |
| logger.debug(f"์ปจํ ์คํธ ๋ฐ์ดํฐ ์ค๋น ์๋ฃ: {len(context_data)}๊ฐ ํญ๋ชฉ") | |
| # MCP๋ฅผ ํตํ ์ฒ๋ฆฌ | |
| result = mcp_processor.process_query( | |
| query=query.query, | |
| context_data=context_data, | |
| conversation_id=query.conversation_id, | |
| system_prompt=query.system_prompt, | |
| temperature=query.temperature, | |
| max_tokens=query.max_tokens | |
| ) | |
| return { | |
| "response": result["content"], | |
| "conversation_id": result["conversation_id"], | |
| "used_context": result["used_context"], | |
| "timestamp": datetime.now().isoformat() | |
| } | |
| except Exception as e: | |
| logger.error(f"MCP ์ฟผ๋ฆฌ ์ฒ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def health_check(): | |
| """ | |
| ํฌ์ค ์ฒดํฌ ์๋ํฌ์ธํธ | |
| ์๋ฒ ์ํ์ ๊ฐ๋จํ ํฌ์ค ์ฒดํฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. | |
| """ | |
| return { | |
| "status": "healthy", | |
| "timestamp": datetime.now().isoformat(), | |
| "service": "KCSC MCP API", | |
| "version": app.version | |
| } | |
| # ๋ฉ์ธ ์คํ | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run( | |
| "src.app:app", | |
| host=config.HOST, | |
| port=config.PORT, | |
| reload=config.DEBUG | |
| ) | |