# Lily LLM API μ°Έμ‘° λ¬Έμ„œ ## πŸ“‹ κ°œμš” Lily LLM APIλŠ” λ‹€μ–‘ν•œ μ–Έμ–΄ λͺ¨λΈμ„ μ§€μ›ν•˜λŠ” RESTful API μ„œλ²„μž…λ‹ˆλ‹€. ν…μŠ€νŠΈ 생성, λ©€ν‹°λͺ¨λ‹¬ 처리, RAG(Retrieval-Augmented Generation) κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€. ## πŸ”— κΈ°λ³Έ 정보 - **Base URL**: `http://localhost:8001` - **API λ¬Έμ„œ**: `http://localhost:8001/docs` - **ReDoc λ¬Έμ„œ**: `http://localhost:8001/redoc` ## πŸ” 인증 ### JWT 토큰 인증 ```bash # 둜그인 curl -X POST "http://localhost:8001/auth/login" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=your_username&password=your_password" # 응닡 { "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", "token_type": "bearer" } ``` ### 보호된 μ—”λ“œν¬μΈνŠΈ μ‚¬μš© ```bash curl -X GET "http://localhost:8001/auth/me" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` ## πŸ€– AI λͺ¨λΈ κ΄€λ ¨ ### 1. λͺ¨λΈ λͺ©λ‘ 쑰회 ```http GET /models ``` **응닡 μ˜ˆμ‹œ:** ```json { "available_models": [ { "model_id": "polyglot-ko-1.3b-chat", "display_name": "Polyglot Korean 1.3B Chat", "model_type": "text", "description": "ν•œκ΅­μ–΄ νŠΉν™” ν…μŠ€νŠΈ 생성 λͺ¨λΈ" }, { "model_id": "kanana-1.5-v-3b-instruct", "display_name": "Kanana 1.5 v3B Instruct", "model_type": "multimodal", "description": "λ©€ν‹°λͺ¨λ‹¬ 이미지+ν…μŠ€νŠΈ 처리 λͺ¨λΈ" } ], "current_model": "polyglot-ko-1.3b-chat" } ``` ### 2. ν…μŠ€νŠΈ 생성 ```http POST /generate ``` **μš”μ²­ νŒŒλΌλ―Έν„°:** ```json { "prompt": "μ•ˆλ…•ν•˜μ„Έμš”, AI에 λŒ€ν•΄ μ„€λͺ…ν•΄μ£Όμ„Έμš”.", "model_id": "polyglot-ko-1.3b-chat", "max_length": 200, "temperature": 0.7, "top_p": 0.9, "do_sample": true } ``` **응닡 μ˜ˆμ‹œ:** ```json { "generated_text": "μ•ˆλ…•ν•˜μ„Έμš”! AI(인곡지λŠ₯)λŠ” μΈκ°„μ˜ ν•™μŠ΅λŠ₯λ ₯κ³Ό μΆ”λ‘ λŠ₯λ ₯을 인곡적으둜 κ΅¬ν˜„ν•œ 컴퓨터 μ‹œμŠ€ν…œμž…λ‹ˆλ‹€...", "model_name": "polyglot-ko-1.3b-chat", "processing_time": 2.34, "tokens_generated": 45 } ``` ### 3. λ©€ν‹°λͺ¨λ‹¬ 생성 (이미지 + ν…μŠ€νŠΈ) ```http POST /generate-multimodal ``` **μš”μ²­ (multipart/form-data):** ``` prompt: "이 이미지에 λŒ€ν•΄ μ„€λͺ…ν•΄μ£Όμ„Έμš”" model_id: "kanana-1.5-v-3b-instruct" max_length: 200 temperature: 0.7 image_files: [파일1, 파일2, ...] ``` **응닡 μ˜ˆμ‹œ:** ```json { "generated_text": "이 μ΄λ―Έμ§€λŠ” μ•„λ¦„λ‹€μš΄ μžμ—° 풍경을 λ³΄μ—¬μ€λ‹ˆλ‹€...", "model_name": "kanana-1.5-v-3b-instruct", "processing_time": 15.67, "images_processed": 2 } ``` ## πŸ“„ λ¬Έμ„œ 처리 (RAG) ### 1. λ¬Έμ„œ μ—…λ‘œλ“œ ```http POST /document/upload ``` **μš”μ²­ (multipart/form-data):** ``` file: [PDF/DOC/DOCX/PPTX 파일] user_id: "user123" ``` **응닡 μ˜ˆμ‹œ:** ```json { "document_id": "doc_123456", "filename": "sample.pdf", "file_type": "pdf", "file_size": 1024000, "pages": 15, "chunks": 45, "upload_time": "2025-08-04T10:30:00Z" } ``` ### 2. RAG 쿼리 ```http POST /rag/generate ``` **μš”μ²­ νŒŒλΌλ―Έν„°:** ```json { "query": "인곡지λŠ₯의 λ―Έλž˜μ— λŒ€ν•΄ μ•Œλ €μ£Όμ„Έμš”", "user_id": "user123", "max_length": 300, "temperature": 0.7 } ``` **응닡 μ˜ˆμ‹œ:** ```json { "response": "인곡지λŠ₯의 λ―Έλž˜λŠ” 맀우 λ°μŠ΅λ‹ˆλ‹€. ν˜„μž¬ λ¬Έμ„œμ— λ”°λ₯΄λ©΄...", "sources": [ { "document_id": "doc_123456", "page": 5, "chunk": "AI 기술의 λ°œμ „ λ°©ν–₯..." } ], "confidence": 0.85, "processing_time": 3.45 } ``` ### 3. ν•˜μ΄λΈŒλ¦¬λ“œ RAG (이미지 + λ¬Έμ„œ) ```http POST /rag/generate-hybrid ``` **μš”μ²­ (multipart/form-data):** ``` query: "이 이미지와 κ΄€λ ¨λœ λ¬Έμ„œ λ‚΄μš©μ„ μ°Ύμ•„μ£Όμ„Έμš”" user_id: "user123" image_files: [이미지 νŒŒμΌλ“€] max_length: 300 temperature: 0.7 ``` ## πŸ’¬ μ±„νŒ… 및 μ„Έμ…˜ 관리 ### 1. μ‚¬μš©μž 생성 ```http POST /user/create ``` **μš”μ²­ νŒŒλΌλ―Έν„°:** ```json { "user_id": "user123", "username": "ν…ŒμŠ€νŠΈμ‚¬μš©μž", "email": "test@example.com" } ``` ### 2. μ±„νŒ… μ„Έμ…˜ 생성 ```http POST /session/create ``` **μš”μ²­ νŒŒλΌλ―Έν„°:** ```json { "user_id": "user123", "session_name": "AI 상담 μ„Έμ…˜" } ``` ### 3. λ©”μ‹œμ§€ μΆ”κ°€ ```http POST /chat/message ``` **μš”μ²­ νŒŒλΌλ―Έν„°:** ```json { "session_id": "session_123", "user_id": "user123", "message_type": "text", "content": "μ•ˆλ…•ν•˜μ„Έμš”!" } ``` ### 4. μ±„νŒ… 기둝 쑰회 ```http GET /chat/history/{session_id} ``` ## πŸ”„ λ°±κ·ΈλΌμš΄λ“œ μž‘μ—… ### 1. λ¬Έμ„œ 처리 μž‘μ—… ```http POST /tasks/document/process ``` **μš”μ²­ νŒŒλΌλ―Έν„°:** ```json { "file_path": "/uploads/document.pdf", "user_id": "user123" } ``` **응닡 μ˜ˆμ‹œ:** ```json { "task_id": "task_123456", "status": "PENDING", "message": "λ¬Έμ„œ 처리 μž‘μ—…μ΄ μ‹œμž‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€." } ``` ### 2. μž‘μ—… μƒνƒœ 확인 ```http GET /tasks/{task_id} ``` **응닡 μ˜ˆμ‹œ:** ```json { "task_id": "task_123456", "status": "SUCCESS", "result": { "document_id": "doc_123456", "chunks": 45 }, "progress": 100 } ``` ## πŸ“Š λͺ¨λ‹ˆν„°λ§ ### 1. μ„±λŠ₯ λͺ¨λ‹ˆν„°λ§ μ‹œμž‘ ```http POST /monitoring/start ``` ### 2. μ„±λŠ₯ μƒνƒœ 쑰회 ```http GET /monitoring/status ``` **응닡 μ˜ˆμ‹œ:** ```json { "current_metrics": { "cpu_percent": 25.5, "memory_percent": 68.2, "memory_used_mb": 8192.0, "disk_usage_percent": 45.0 }, "performance_stats": { "avg_response_time": 1.23, "avg_inference_time": 2.45, "total_requests": 1250, "success_rate": 98.5 }, "system_health": { "status": "healthy", "recommendations": [] } } ``` ### 3. μ‹œμŠ€ν…œ 건강 μƒνƒœ ```http GET /monitoring/health ``` ## πŸ”Œ WebSocket ### μ—°κ²° ```javascript const ws = new WebSocket('ws://localhost:8001/ws/user123'); ws.onopen = function() { console.log('WebSocket 연결됨'); }; ws.onmessage = function(event) { const data = JSON.parse(event.data); console.log('λ©”μ‹œμ§€ μˆ˜μ‹ :', data); }; ``` ### λ©”μ‹œμ§€ 전솑 ```javascript ws.send(JSON.stringify({ type: 'chat', message: 'μ•ˆλ…•ν•˜μ„Έμš”!', session_id: 'session_123' })); ``` ## 🚨 였λ₯˜ μ½”λ“œ | μ½”λ“œ | 의미 | ν•΄κ²° 방법 | |------|------|-----------| | 400 | 잘λͺ»λœ μš”μ²­ | μš”μ²­ νŒŒλΌλ―Έν„° 확인 | | 401 | 인증 μ‹€νŒ¨ | 토큰 확인 | | 403 | κΆŒν•œ μ—†μŒ | κΆŒν•œ 확인 | | 404 | λ¦¬μ†ŒμŠ€ μ—†μŒ | URL 확인 | | 422 | 검증 μ‹€νŒ¨ | μš”μ²­ 데이터 ν˜•μ‹ 확인 | | 500 | μ„œλ²„ 였λ₯˜ | μ„œλ²„ 둜그 확인 | | 503 | μ„œλΉ„μŠ€ λΆˆκ°€ | μ„œλΉ„μŠ€ μƒνƒœ 확인 | ## πŸ“ 예제 μ½”λ“œ ### Python ν΄λΌμ΄μ–ΈνŠΈ ```python import requests import json class LilyLLMClient: def __init__(self, base_url="http://localhost:8001"): self.base_url = base_url self.token = None def login(self, username, password): response = requests.post(f"{self.base_url}/auth/login", data={"username": username, "password": password}) if response.status_code == 200: self.token = response.json()["access_token"] return True return False def generate_text(self, prompt, model_id="polyglot-ko-1.3b-chat"): headers = {"Authorization": f"Bearer {self.token}"} if self.token else {} data = { "prompt": prompt, "model_id": model_id, "max_length": 200, "temperature": 0.7 } response = requests.post(f"{self.base_url}/generate", data=data, headers=headers) return response.json() def upload_document(self, file_path, user_id): headers = {"Authorization": f"Bearer {self.token}"} if self.token else {} with open(file_path, 'rb') as f: files = {'file': f} data = {'user_id': user_id} response = requests.post(f"{self.base_url}/document/upload", files=files, data=data, headers=headers) return response.json() # μ‚¬μš© 예제 client = LilyLLMClient() if client.login("username", "password"): result = client.generate_text("μ•ˆλ…•ν•˜μ„Έμš”!") print(result["generated_text"]) ``` ### JavaScript ν΄λΌμ΄μ–ΈνŠΈ ```javascript class LilyLLMClient { constructor(baseUrl = 'http://localhost:8001') { this.baseUrl = baseUrl; this.token = null; } async login(username, password) { const response = await fetch(`${this.baseUrl}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: `username=${username}&password=${password}` }); if (response.ok) { const data = await response.json(); this.token = data.access_token; return true; } return false; } async generateText(prompt, modelId = 'polyglot-ko-1.3b-chat') { const headers = this.token ? {'Authorization': `Bearer ${this.token}`} : {}; const formData = new FormData(); formData.append('prompt', prompt); formData.append('model_id', modelId); formData.append('max_length', '200'); formData.append('temperature', '0.7'); const response = await fetch(`${this.baseUrl}/generate`, { method: 'POST', headers, body: formData }); return await response.json(); } } // μ‚¬μš© 예제 const client = new LilyLLMClient(); client.login('username', 'password').then(async (success) => { if (success) { const result = await client.generateText('μ•ˆλ…•ν•˜μ„Έμš”!'); console.log(result.generated_text); } }); ``` ## πŸ”§ μ„€μ • ### ν™˜κ²½ λ³€μˆ˜ ```bash # μ„œλ²„ μ„€μ • HOST=0.0.0.0 PORT=8001 LOG_LEVEL=INFO # λ°μ΄ν„°λ² μ΄μŠ€ DATABASE_URL=sqlite:///app/data/lily_llm.db # Redis REDIS_URL=redis://localhost:6379 # Celery CELERY_BROKER_URL=redis://localhost:6379/0 CELERY_RESULT_BACKEND=redis://localhost:6379/0 # λ³΄μ•ˆ SECRET_KEY=your-secret-key JWT_SECRET_KEY=your-jwt-secret-key ``` ## πŸ“š μΆ”κ°€ λ¦¬μ†ŒμŠ€ - [FastAPI λ¬Έμ„œ](https://fastapi.tiangolo.com/) - [Celery λ¬Έμ„œ](https://docs.celeryproject.org/) - [Redis λ¬Έμ„œ](https://redis.io/documentation) - [LangChain λ¬Έμ„œ](https://python.langchain.com/)