models + refactor

This commit is contained in:
xds
2026-03-17 16:46:32 +03:00
parent e011805186
commit 14f9e7b7e9
15 changed files with 979 additions and 83 deletions

View File

@@ -0,0 +1,100 @@
import logging
import io
import httpx
import hashlib
import time
from typing import List, Tuple, Dict, Any, Optional
from datetime import datetime
from models.enums import AspectRatios, Quality
from config import settings
logger = logging.getLogger(__name__)
class AIProxyAdapter:
def __init__(self, base_url: str = "http://82.22.174.14:8001", salt: str = None):
self.base_url = base_url.rstrip("/")
self.salt = salt or settings.PROXY_SECRET_SALT
def _generate_headers(self) -> Dict[str, str]:
timestamp = int(time.time())
hash_input = f"{timestamp}{self.salt}".encode()
signature = hashlib.sha256(hash_input).hexdigest()
return {
"X-Timestamp": str(timestamp),
"X-Signature": signature
}
async def generate_text(self, prompt: str, model: str = "gemini-3.1-pro-preview", asset_urls: List[str] | None = None) -> str:
"""
Generates text using the AI Proxy with signature verification.
"""
url = f"{self.base_url}/generate_text"
messages = [{"role": "user", "content": prompt}]
payload = {
"messages": messages,
"asset_urls": asset_urls
}
headers = self._generate_headers()
async with httpx.AsyncClient() as client:
try:
response = await client.post(url, json=payload, headers=headers, timeout=60.0)
response.raise_for_status()
data = response.json()
if data.get("finish_reason") != "STOP":
logger.warning(f"AI Proxy generation finished with reason: {data.get('finish_reason')}")
return data.get("response") or ""
except Exception as e:
logger.error(f"AI Proxy Text Error: {e}")
raise Exception(f"AI Proxy Text Error: {e}")
async def generate_image(
self,
prompt: str,
aspect_ratio: AspectRatios,
quality: Quality,
model: str = "gemini-3-pro-image-preview",
asset_urls: List[str] | None = None
) -> Tuple[List[io.BytesIO], Dict[str, Any]]:
"""
Generates image using the AI Proxy with signature verification.
"""
url = f"{self.base_url}/generate_image"
payload = {
"prompt": prompt,
"asset_urls": asset_urls
}
headers = self._generate_headers()
start_time = datetime.now()
async with httpx.AsyncClient() as client:
try:
response = await client.post(url, json=payload, headers=headers, timeout=120.0)
response.raise_for_status()
image_bytes = response.content
byte_arr = io.BytesIO(image_bytes)
byte_arr.name = f"{datetime.now().timestamp()}.png"
byte_arr.seek(0)
end_time = datetime.now()
api_duration = (end_time - start_time).total_seconds()
metrics = {
"api_execution_time_seconds": api_duration,
"token_usage": 0,
"input_token_usage": 0,
"output_token_usage": 0
}
return [byte_arr], metrics
except Exception as e:
logger.error(f"AI Proxy Image Error: {e}")
raise Exception(f"AI Proxy Image Error: {e}")

88
adapters/meta_adapter.py Normal file
View File

@@ -0,0 +1,88 @@
import logging
from typing import Optional
import httpx
logger = logging.getLogger(__name__)
META_GRAPH_VERSION = "v18.0"
META_GRAPH_BASE = f"https://graph.facebook.com/{META_GRAPH_VERSION}"
class MetaAdapter:
"""Adapter for Meta Platform API (Instagram Graph API).
Requires:
- access_token: long-lived Page or Instagram access token
- instagram_account_id: Instagram Business Account ID
"""
def __init__(self, access_token: str, instagram_account_id: str):
self.access_token = access_token
self.instagram_account_id = instagram_account_id
async def post_to_feed(self, image_url: str, caption: str) -> Optional[str]:
"""Upload image and publish to Instagram feed.
Returns the post ID on success, raises on failure.
"""
async with httpx.AsyncClient(timeout=30.0) as client:
# Step 1: create media container
resp = await client.post(
f"{META_GRAPH_BASE}/{self.instagram_account_id}/media",
data={
"image_url": image_url,
"caption": caption,
"access_token": self.access_token,
},
)
resp.raise_for_status()
creation_id = resp.json().get("id")
if not creation_id:
raise ValueError(f"No creation_id from Meta API: {resp.text}")
# Step 2: publish
resp2 = await client.post(
f"{META_GRAPH_BASE}/{self.instagram_account_id}/media_publish",
data={
"creation_id": creation_id,
"access_token": self.access_token,
},
)
resp2.raise_for_status()
post_id = resp2.json().get("id")
logger.info(f"Published to Instagram feed: {post_id}")
return post_id
async def post_to_story(self, image_url: str) -> Optional[str]:
"""Upload image and publish to Instagram story.
Returns the story ID on success, raises on failure.
"""
async with httpx.AsyncClient(timeout=30.0) as client:
# Step 1: create story container
resp = await client.post(
f"{META_GRAPH_BASE}/{self.instagram_account_id}/media",
data={
"image_url": image_url,
"media_type": "STORIES",
"access_token": self.access_token,
},
)
resp.raise_for_status()
creation_id = resp.json().get("id")
if not creation_id:
raise ValueError(f"No creation_id from Meta API: {resp.text}")
# Step 2: publish
resp2 = await client.post(
f"{META_GRAPH_BASE}/{self.instagram_account_id}/media_publish",
data={
"creation_id": creation_id,
"access_token": self.access_token,
},
)
resp2.raise_for_status()
story_id = resp2.json().get("id")
logger.info(f"Published to Instagram story: {story_id}")
return story_id