263 lines
8.0 KiB
Python
263 lines
8.0 KiB
Python
# HF_api.py
|
|
import aiohttp
|
|
import base64
|
|
import logging
|
|
import json
|
|
from typing import List, Union, Optional, Dict, Any
|
|
from huggingface_hub import InferenceClient
|
|
from io import BytesIO
|
|
import requests
|
|
from PIL import Image
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
async def send_huggingface_request(
|
|
base_ip: str,
|
|
base64_images: Optional[List[str]],
|
|
model: str,
|
|
system_message: str,
|
|
user_message: str,
|
|
messages: List[Dict[str, Any]],
|
|
api_key: str,
|
|
seed: Optional[int] = None,
|
|
temperature: float = 0.7,
|
|
max_tokens: int = 1024,
|
|
top_p: float = 0.95,
|
|
strategy: str = "normal",
|
|
batch_count: int = 1,
|
|
mask: Optional[str] = None,
|
|
**kwargs
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Send request to HuggingFace Inference API with support for different strategies
|
|
"""
|
|
try:
|
|
if not api_key:
|
|
raise ValueError("HuggingFace API key is required")
|
|
|
|
if strategy == "create":
|
|
# Handle text-to-image generation
|
|
return await generate_images(
|
|
model=model,
|
|
prompt=user_message,
|
|
api_key=api_key,
|
|
num_images=batch_count,
|
|
seed=seed,
|
|
negative_prompt=kwargs.get('neg_content', '')
|
|
)
|
|
|
|
elif strategy == "edit":
|
|
# Handle image-to-image editing
|
|
if not base64_images:
|
|
raise ValueError("Image required for edit strategy")
|
|
|
|
return await edit_images(
|
|
model=model,
|
|
image=base64_images[0],
|
|
mask=mask,
|
|
prompt=user_message,
|
|
api_key=api_key,
|
|
num_images=batch_count,
|
|
negative_prompt=kwargs.get('neg_content', '')
|
|
)
|
|
|
|
else:
|
|
# Handle regular chat/vision requests
|
|
client = InferenceClient(api_key=api_key)
|
|
|
|
# Prepare messages for VLM
|
|
formatted_messages = prepare_messages(
|
|
system_message=system_message,
|
|
user_message=user_message,
|
|
messages=messages,
|
|
base64_images=base64_images
|
|
)
|
|
|
|
response = await run_inference(
|
|
client=client,
|
|
model=model,
|
|
messages=formatted_messages,
|
|
max_tokens=max_tokens,
|
|
temperature=temperature,
|
|
top_p=top_p,
|
|
seed=seed
|
|
)
|
|
|
|
return format_response(response)
|
|
|
|
except Exception as e:
|
|
error_msg = f"Error in HuggingFace API request: {str(e)}"
|
|
logger.error(error_msg)
|
|
return {"choices": [{"message": {"content": error_msg}}]}
|
|
|
|
async def generate_images(
|
|
model: str,
|
|
prompt: str,
|
|
api_key: str,
|
|
num_images: int = 1,
|
|
seed: Optional[int] = None,
|
|
negative_prompt: Optional[str] = None
|
|
) -> Dict[str, Any]:
|
|
"""Generate images using HuggingFace text-to-image models"""
|
|
api_url = f"https://api-inference.huggingface.co/models/{model}"
|
|
headers = {"Authorization": f"Bearer {api_key}"}
|
|
|
|
payload = {
|
|
"inputs": prompt,
|
|
"parameters": {
|
|
"num_inference_steps": 50,
|
|
"guidance_scale": 7.5,
|
|
"negative_prompt": negative_prompt if negative_prompt else None,
|
|
"num_images_per_prompt": num_images,
|
|
}
|
|
}
|
|
|
|
if seed is not None:
|
|
payload["parameters"]["seed"] = seed
|
|
|
|
try:
|
|
async with aiohttp.ClientSession() as session:
|
|
async with session.post(api_url, headers=headers, json=payload) as response:
|
|
response.raise_for_status()
|
|
|
|
# Handle both single image and batch responses
|
|
images = []
|
|
if response.content_type == 'application/json':
|
|
data = await response.json()
|
|
images = [d.get("image", "") for d in data]
|
|
else:
|
|
# Single image as bytes
|
|
image_bytes = await response.read()
|
|
images = [base64.b64encode(image_bytes).decode('utf-8')]
|
|
|
|
return {
|
|
"images": images
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error generating images: {str(e)}")
|
|
raise
|
|
|
|
async def edit_images(
|
|
model: str,
|
|
image: str,
|
|
mask: Optional[str],
|
|
prompt: str,
|
|
api_key: str,
|
|
num_images: int = 1,
|
|
negative_prompt: Optional[str] = None
|
|
) -> Dict[str, Any]:
|
|
"""Edit images using HuggingFace image-to-image models"""
|
|
api_url = f"https://api-inference.huggingface.co/models/{model}"
|
|
headers = {"Authorization": f"Bearer {api_key}"}
|
|
|
|
# Prepare payload
|
|
payload = {
|
|
"inputs": {
|
|
"image": image,
|
|
"prompt": prompt,
|
|
"negative_prompt": negative_prompt if negative_prompt else None,
|
|
"num_images": num_images,
|
|
}
|
|
}
|
|
|
|
if mask is not None:
|
|
payload["inputs"]["mask"] = mask
|
|
|
|
try:
|
|
async with aiohttp.ClientSession() as session:
|
|
async with session.post(api_url, headers=headers, json=payload) as response:
|
|
response.raise_for_status()
|
|
|
|
images = []
|
|
if response.content_type == 'application/json':
|
|
data = await response.json()
|
|
images = [d.get("image", "") for d in data]
|
|
else:
|
|
image_bytes = await response.read()
|
|
images = [base64.b64encode(image_bytes).decode('utf-8')]
|
|
|
|
return {
|
|
"images": images
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error editing images: {str(e)}")
|
|
raise
|
|
|
|
def prepare_messages(
|
|
system_message: str,
|
|
user_message: str,
|
|
messages: List[Dict[str, Any]],
|
|
base64_images: Optional[List[str]] = None
|
|
) -> List[Dict[str, Any]]:
|
|
"""Prepare messages for HuggingFace VLM models"""
|
|
prepared_messages = []
|
|
|
|
if system_message:
|
|
prepared_messages.append({
|
|
"role": "system",
|
|
"content": system_message
|
|
})
|
|
|
|
# Add previous messages
|
|
prepared_messages.extend(messages)
|
|
|
|
# Add current message with images if present
|
|
if base64_images:
|
|
content = [{"type": "text", "text": user_message}]
|
|
for img in base64_images:
|
|
content.append({
|
|
"type": "image_url",
|
|
"image_url": {
|
|
"url": f"data:image/jpeg;base64,{img}"
|
|
}
|
|
})
|
|
prepared_messages.append({"role": "user", "content": content})
|
|
else:
|
|
prepared_messages.append({"role": "user", "content": user_message})
|
|
|
|
return prepared_messages
|
|
|
|
async def run_inference(
|
|
client: InferenceClient,
|
|
model: str,
|
|
messages: List[Dict[str, Any]],
|
|
max_tokens: int,
|
|
temperature: float,
|
|
top_p: float,
|
|
seed: Optional[int] = None
|
|
) -> Any:
|
|
"""Run inference using HuggingFace client"""
|
|
params = {
|
|
"model": model,
|
|
"messages": messages,
|
|
"max_tokens": max_tokens,
|
|
"temperature": temperature,
|
|
"top_p": top_p,
|
|
"stream": False
|
|
}
|
|
|
|
if seed is not None:
|
|
params["seed"] = seed
|
|
|
|
return await client.chat.completions.create(**params)
|
|
|
|
def format_response(response: Any) -> Dict[str, Any]:
|
|
"""Format HuggingFace response to match expected structure"""
|
|
if hasattr(response, 'choices'):
|
|
return {
|
|
"choices": [{
|
|
"message": {
|
|
"content": choice.message.content
|
|
}
|
|
} for choice in response.choices]
|
|
}
|
|
else:
|
|
return {
|
|
"choices": [{
|
|
"message": {
|
|
"content": str(response)
|
|
}
|
|
}]
|
|
} |