Files
if-ai-ComfyUI-IF_LLM/HF_api.py
T
2024-12-31 17:28:59 +00:00

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