File size: 5,590 Bytes
cec729c
2bd3b94
e66c037
2bd3b94
e66c037
2bd3b94
cced70b
2bd3b94
 
 
5fa76ab
2bd3b94
5fa76ab
 
 
2bd3b94
 
 
 
 
 
 
 
 
cec729c
2bd3b94
 
c7a8f22
2bd3b94
 
 
 
 
 
 
 
c7a8f22
2bd3b94
 
 
e66c037
 
 
 
c7a8f22
2bd3b94
 
 
 
 
 
 
5fa76ab
2bd3b94
5fa76ab
2bd3b94
 
 
 
 
 
 
 
 
e66c037
2bd3b94
 
5fa76ab
e66c037
2bd3b94
 
 
 
 
 
 
cec729c
e66c037
 
 
 
 
 
 
 
 
c7a8f22
e66c037
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2bd3b94
e66c037
 
2bd3b94
5fa76ab
 
2bd3b94
 
 
e66c037
 
 
2bd3b94
 
e66c037
 
2bd3b94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e66c037
 
 
2bd3b94
 
 
 
 
 
 
 
 
 
e66c037
2bd3b94
 
 
 
e66c037
2bd3b94
 
 
e66c037
 
2bd3b94
5fa76ab
cec729c
2bd3b94
 
c7a8f22
cec729c
 
2bd3b94
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import os
import base64
import json
from io import BytesIO
from typing import List, Optional, Union, Dict, Any

from fastapi import FastAPI, File, UploadFile, HTTPException, Depends, Security, status, Form
from fastapi.security import APIKeyHeader
from pydantic import BaseModel, Field
import requests
import uvicorn
from PIL import Image

app = FastAPI()

# Environment variables for authentication
HF_TOKEN = os.getenv("HF_TOKEN")
API_KEY = os.getenv("API_KEY")

if not HF_TOKEN:
    raise ValueError("Hugging Face API token (HF_TOKEN) is missing!")

if not API_KEY:
    raise ValueError("API key (API_KEY) is missing!")

# API Key security scheme
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=True)

# Authentication dependency
async def get_api_key(api_key: str = Security(api_key_header)):
    if api_key != API_KEY:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid API Key"
        )
    return api_key

# Models
class Message(BaseModel):
    role: str
    content: Union[str, List[Dict[str, Any]]]
    
    def to_dict(self):
        return {"role": self.role, "content": self.content}

class ChatItem(BaseModel):
    messages: List[Message]
    temperature: float = 0.7
    max_tokens: int = 1024
    top_p: float = 0.9
    frequency_penalty: float = 0.0
    presence_penalty: float = 0.0

class ImageUploadRequest(BaseModel):
    prompt: str
    system_prompt: Optional[str] = ""
    temperature: float = 0.7
    max_tokens: int = 1024
    top_p: float = 0.9

def encode_image_to_base64(image_file):
    """Convert an image file to base64 string"""
    img = Image.open(image_file)
    buffer = BytesIO()
    img.save(buffer, format=img.format or "JPEG")
    img_str = base64.b64encode(buffer.getvalue()).decode("utf-8")
    return img_str

def generate_with_phi_vision(messages_data):
    """Generate response using Phi-3.5-Vision model"""
    headers = {
        "Authorization": f"Bearer {HF_TOKEN}",
        "Content-Type": "application/json"
    }
    
    api_url = "https://api-inference.huggingface.co/models/microsoft/Phi-3.5-vision-instruct"
    
    # Convert pydantic models to dictionaries if necessary
    message_dicts = []
    for msg in messages_data:
        if hasattr(msg, 'to_dict'):
            message_dicts.append(msg.to_dict())
        else:
            message_dicts.append(msg)
    
    payload = {"inputs": message_dicts}
    
    try:
        response = requests.post(api_url, json=payload, headers=headers)
        
        if response.status_code != 200:
            # Handle the "model is loading" scenario
            if "is currently loading" in response.text:
                error_data = response.json()
                raise HTTPException(
                    status_code=503,
                    detail=f"Model is currently loading. Estimated time: {error_data.get('estimated_time', 'unknown')} seconds"
                )
            else:
                raise HTTPException(
                    status_code=response.status_code,
                    detail=f"Hugging Face API error: {response.text}"
                )
        
        return response.json()
    except requests.exceptions.RequestException as e:
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail=f"Error connecting to Hugging Face API: {str(e)}"
        )

@app.post("/generate/")
async def generate_text(item: ChatItem, api_key: str = Depends(get_api_key)):
    """Generate text response using Phi-3.5-Vision model"""
    try:
        # Convert Message objects to dictionaries
        messages_dict = [msg.to_dict() for msg in item.messages]
        response = generate_with_phi_vision(messages_dict)
        return {"response": response}
    except Exception as e:
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(status_code=500, detail=str(e))

@app.post("/generate_with_image/")
async def generate_with_image(
    prompt: str = Form(...),
    system_prompt: str = Form(""),
    image: UploadFile = File(...),
    temperature: float = Form(0.7),
    max_tokens: int = Form(1024),
    top_p: float = Form(0.9),
    api_key: str = Depends(get_api_key)
):
    """Generate response from Phi-3.5-Vision based on an image and text prompt"""
    try:
        # Convert image to base64
        img_base64 = encode_image_to_base64(image.file)
        
        # Determine content type
        content_type = image.content_type or "image/jpeg"
        
        # Construct the messages in the format expected by the model
        messages = [
            {
                "role": "system",
                "content": system_prompt if system_prompt else "You are a helpful vision assistant."
            },
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": prompt},
                    {"type": "image_url", "image_url": {"url": f"data:{content_type};base64,{img_base64}"}}
                ]
            }
        ]
        
        # Call the model with retry logic
        response = generate_with_phi_vision(messages)
        return {"response": response}
    except Exception as e:
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/test/")
async def test_api(api_key: str = Depends(get_api_key)):
    """Test if the API is working"""
    return {"message": "API is working!"}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=7860)