lily_fast_api / docs /API_REFERENCE.md
gbrabbit's picture
Fresh start for HF Spaces deployment
526927a

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 토큰 인증

# 둜그인
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"
}

보호된 μ—”λ“œν¬μΈνŠΈ μ‚¬μš©

curl -X GET "http://localhost:8001/auth/me" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

πŸ€– AI λͺ¨λΈ κ΄€λ ¨

1. λͺ¨λΈ λͺ©λ‘ 쑰회

GET /models

응닡 μ˜ˆμ‹œ:

{
  "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. ν…μŠ€νŠΈ 생성

POST /generate

μš”μ²­ νŒŒλΌλ―Έν„°:

{
  "prompt": "μ•ˆλ…•ν•˜μ„Έμš”, AI에 λŒ€ν•΄ μ„€λͺ…ν•΄μ£Όμ„Έμš”.",
  "model_id": "polyglot-ko-1.3b-chat",
  "max_length": 200,
  "temperature": 0.7,
  "top_p": 0.9,
  "do_sample": true
}

응닡 μ˜ˆμ‹œ:

{
  "generated_text": "μ•ˆλ…•ν•˜μ„Έμš”! AI(인곡지λŠ₯)λŠ” μΈκ°„μ˜ ν•™μŠ΅λŠ₯λ ₯κ³Ό μΆ”λ‘ λŠ₯λ ₯을 인곡적으둜 κ΅¬ν˜„ν•œ 컴퓨터 μ‹œμŠ€ν…œμž…λ‹ˆλ‹€...",
  "model_name": "polyglot-ko-1.3b-chat",
  "processing_time": 2.34,
  "tokens_generated": 45
}

3. λ©€ν‹°λͺ¨λ‹¬ 생성 (이미지 + ν…μŠ€νŠΈ)

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, ...]

응닡 μ˜ˆμ‹œ:

{
  "generated_text": "이 μ΄λ―Έμ§€λŠ” μ•„λ¦„λ‹€μš΄ μžμ—° 풍경을 λ³΄μ—¬μ€λ‹ˆλ‹€...",
  "model_name": "kanana-1.5-v-3b-instruct",
  "processing_time": 15.67,
  "images_processed": 2
}

πŸ“„ λ¬Έμ„œ 처리 (RAG)

1. λ¬Έμ„œ μ—…λ‘œλ“œ

POST /document/upload

μš”μ²­ (multipart/form-data):

file: [PDF/DOC/DOCX/PPTX 파일]
user_id: "user123"

응닡 μ˜ˆμ‹œ:

{
  "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 쿼리

POST /rag/generate

μš”μ²­ νŒŒλΌλ―Έν„°:

{
  "query": "인곡지λŠ₯의 λ―Έλž˜μ— λŒ€ν•΄ μ•Œλ €μ£Όμ„Έμš”",
  "user_id": "user123",
  "max_length": 300,
  "temperature": 0.7
}

응닡 μ˜ˆμ‹œ:

{
  "response": "인곡지λŠ₯의 λ―Έλž˜λŠ” 맀우 λ°μŠ΅λ‹ˆλ‹€. ν˜„μž¬ λ¬Έμ„œμ— λ”°λ₯΄λ©΄...",
  "sources": [
    {
      "document_id": "doc_123456",
      "page": 5,
      "chunk": "AI 기술의 λ°œμ „ λ°©ν–₯..."
    }
  ],
  "confidence": 0.85,
  "processing_time": 3.45
}

3. ν•˜μ΄λΈŒλ¦¬λ“œ RAG (이미지 + λ¬Έμ„œ)

POST /rag/generate-hybrid

μš”μ²­ (multipart/form-data):

query: "이 이미지와 κ΄€λ ¨λœ λ¬Έμ„œ λ‚΄μš©μ„ μ°Ύμ•„μ£Όμ„Έμš”"
user_id: "user123"
image_files: [이미지 νŒŒμΌλ“€]
max_length: 300
temperature: 0.7

πŸ’¬ μ±„νŒ… 및 μ„Έμ…˜ 관리

1. μ‚¬μš©μž 생성

POST /user/create

μš”μ²­ νŒŒλΌλ―Έν„°:

{
  "user_id": "user123",
  "username": "ν…ŒμŠ€νŠΈμ‚¬μš©μž",
  "email": "test@example.com"
}

2. μ±„νŒ… μ„Έμ…˜ 생성

POST /session/create

μš”μ²­ νŒŒλΌλ―Έν„°:

{
  "user_id": "user123",
  "session_name": "AI 상담 μ„Έμ…˜"
}

3. λ©”μ‹œμ§€ μΆ”κ°€

POST /chat/message

μš”μ²­ νŒŒλΌλ―Έν„°:

{
  "session_id": "session_123",
  "user_id": "user123",
  "message_type": "text",
  "content": "μ•ˆλ…•ν•˜μ„Έμš”!"
}

4. μ±„νŒ… 기둝 쑰회

GET /chat/history/{session_id}

πŸ”„ λ°±κ·ΈλΌμš΄λ“œ μž‘μ—…

1. λ¬Έμ„œ 처리 μž‘μ—…

POST /tasks/document/process

μš”μ²­ νŒŒλΌλ―Έν„°:

{
  "file_path": "/uploads/document.pdf",
  "user_id": "user123"
}

응닡 μ˜ˆμ‹œ:

{
  "task_id": "task_123456",
  "status": "PENDING",
  "message": "λ¬Έμ„œ 처리 μž‘μ—…μ΄ μ‹œμž‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€."
}

2. μž‘μ—… μƒνƒœ 확인

GET /tasks/{task_id}

응닡 μ˜ˆμ‹œ:

{
  "task_id": "task_123456",
  "status": "SUCCESS",
  "result": {
    "document_id": "doc_123456",
    "chunks": 45
  },
  "progress": 100
}

πŸ“Š λͺ¨λ‹ˆν„°λ§

1. μ„±λŠ₯ λͺ¨λ‹ˆν„°λ§ μ‹œμž‘

POST /monitoring/start

2. μ„±λŠ₯ μƒνƒœ 쑰회

GET /monitoring/status

응닡 μ˜ˆμ‹œ:

{
  "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. μ‹œμŠ€ν…œ 건강 μƒνƒœ

GET /monitoring/health

πŸ”Œ WebSocket

μ—°κ²°

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);
};

λ©”μ‹œμ§€ 전솑

ws.send(JSON.stringify({
    type: 'chat',
    message: 'μ•ˆλ…•ν•˜μ„Έμš”!',
    session_id: 'session_123'
}));

🚨 였λ₯˜ μ½”λ“œ

μ½”λ“œ 의미 ν•΄κ²° 방법
400 잘λͺ»λœ μš”μ²­ μš”μ²­ νŒŒλΌλ―Έν„° 확인
401 인증 μ‹€νŒ¨ 토큰 확인
403 κΆŒν•œ μ—†μŒ κΆŒν•œ 확인
404 λ¦¬μ†ŒμŠ€ μ—†μŒ URL 확인
422 검증 μ‹€νŒ¨ μš”μ²­ 데이터 ν˜•μ‹ 확인
500 μ„œλ²„ 였λ₯˜ μ„œλ²„ 둜그 확인
503 μ„œλΉ„μŠ€ λΆˆκ°€ μ„œλΉ„μŠ€ μƒνƒœ 확인

πŸ“ 예제 μ½”λ“œ

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 ν΄λΌμ΄μ–ΈνŠΈ

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);
    }
});

πŸ”§ μ„€μ •

ν™˜κ²½ λ³€μˆ˜

# μ„œλ²„ μ„€μ •
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

πŸ“š μΆ”κ°€ λ¦¬μ†ŒμŠ€