|
import requests
|
|
import json
|
|
import os
|
|
import random
|
|
import time
|
|
from typing import Any, Dict, Optional, Generator, Union, List
|
|
import warnings
|
|
from fake_useragent import UserAgent
|
|
from requests_ip_rotator import ApiGateway
|
|
from proxy_finder import ProxyFinder
|
|
|
|
class IPRotator:
|
|
"""Manages AWS API Gateway rotation for multiple regions"""
|
|
|
|
def __init__(self, target_url="deepinfra.com", regions=None):
|
|
"""Initialize with target URL and regions"""
|
|
self.target_url = target_url
|
|
self.regions = regions or ["us-east-1", "us-west-1", "eu-west-1", "ap-southeast-1"]
|
|
self.gateways = {}
|
|
|
|
def setup(self):
|
|
"""Set up API gateways for each region"""
|
|
for region in self.regions:
|
|
try:
|
|
gateway = ApiGateway(self.target_url, region=region)
|
|
gateway.start()
|
|
self.gateways[region] = gateway
|
|
except Exception as e:
|
|
print(f"Failed to set up gateway in {region}: {str(e)}")
|
|
|
|
if not self.gateways:
|
|
raise Exception("Failed to set up any API gateways for IP rotation")
|
|
|
|
def get_session(self):
|
|
"""Get a random session from a gateway"""
|
|
if not self.gateways:
|
|
return requests.Session()
|
|
|
|
|
|
region = random.choice(list(self.gateways.keys()))
|
|
gateway = self.gateways[region]
|
|
return gateway.get_session()
|
|
|
|
def shutdown(self):
|
|
"""Clean up all gateways"""
|
|
for gateway in self.gateways.values():
|
|
try:
|
|
gateway.shutdown()
|
|
except:
|
|
pass
|
|
|
|
class ProxyManager:
|
|
"""Manages proxy rotation for HTTP requests"""
|
|
|
|
def __init__(self, proxies=None):
|
|
"""Initialize with a list of proxies or an empty list"""
|
|
self.proxies = proxies or []
|
|
|
|
def add_proxy(self, proxy):
|
|
"""Add a proxy to the list"""
|
|
self.proxies.append(proxy)
|
|
|
|
def get_random(self):
|
|
"""Return a random proxy if available, otherwise None"""
|
|
if not self.proxies:
|
|
return None
|
|
return random.choice(self.proxies)
|
|
|
|
class DeepInfraClient:
|
|
"""
|
|
A client for DeepInfra API with OpenAI-compatible interface and enhanced features
|
|
"""
|
|
|
|
AVAILABLE_MODELS = [
|
|
"deepseek-ai/DeepSeek-R1-Turbo",
|
|
"deepseek-ai/DeepSeek-R1",
|
|
"deepseek-ai/DeepSeek-R1-Distill-Llama-70B",
|
|
"deepseek-ai/DeepSeek-V3",
|
|
"meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
|
"mistralai/Mistral-Small-24B-Instruct-2501",
|
|
"deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
|
|
"microsoft/phi-4",
|
|
"meta-llama/Meta-Llama-3.1-70B-Instruct",
|
|
"meta-llama/Meta-Llama-3.1-8B-Instruct",
|
|
"meta-llama/Meta-Llama-3.1-405B-Instruct",
|
|
"meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
|
|
"meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
|
|
"Qwen/Qwen2.5-Coder-32B-Instruct",
|
|
"nvidia/Llama-3.1-Nemotron-70B-Instruct",
|
|
"Qwen/Qwen2.5-72B-Instruct",
|
|
"meta-llama/Llama-3.2-90B-Vision-Instruct",
|
|
"meta-llama/Llama-3.2-11B-Vision-Instruct",
|
|
"Gryphe/MythoMax-L2-13b",
|
|
"NousResearch/Hermes-3-Llama-3.1-405B",
|
|
"NovaSky-AI/Sky-T1-32B-Preview",
|
|
"Qwen/Qwen2.5-7B-Instruct",
|
|
"Sao10K/L3.1-70B-Euryale-v2.2",
|
|
"Sao10K/L3.3-70B-Euryale-v2.3",
|
|
"google/gemma-2-27b-it",
|
|
"google/gemma-2-9b-it",
|
|
"meta-llama/Llama-3.2-1B-Instruct",
|
|
"meta-llama/Llama-3.2-3B-Instruct",
|
|
"meta-llama/Meta-Llama-3-70B-Instruct",
|
|
"meta-llama/Meta-Llama-3-8B-Instruct",
|
|
"mistralai/Mistral-Nemo-Instruct-2407",
|
|
"mistralai/Mistral-7B-Instruct-v0.3",
|
|
"mistralai/Mixtral-8x7B-Instruct-v0.1"
|
|
]
|
|
|
|
def __init__(
|
|
self,
|
|
api_key: Optional[str] = None,
|
|
base_url: str = "https://api.deepinfra.com/v1",
|
|
timeout: int = 30,
|
|
max_tokens: int = 2049,
|
|
model: str = "meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
|
use_random_user_agent: bool = True,
|
|
use_proxy_rotation: bool = True,
|
|
use_ip_rotation: bool = True,
|
|
proxy_types: List[str] = None
|
|
):
|
|
"""Initialize the DeepInfraClient"""
|
|
self.base_url = base_url
|
|
self.api_key = api_key
|
|
self.model = model
|
|
self.timeout = timeout
|
|
self.max_tokens = max_tokens
|
|
self.use_random_user_agent = use_random_user_agent
|
|
self.use_ip_rotation = use_ip_rotation
|
|
self.use_proxy_rotation = use_proxy_rotation
|
|
self.proxy_types = proxy_types or ['http', 'socks5']
|
|
|
|
|
|
self.user_agent = UserAgent()
|
|
|
|
|
|
self.proxy_finder = None
|
|
if self.use_proxy_rotation:
|
|
self.proxy_finder = ProxyFinder(verbose=False)
|
|
self.proxy_finder.get_proxies(self.proxy_types)
|
|
|
|
|
|
self.ip_rotator = None
|
|
if use_ip_rotation:
|
|
try:
|
|
self.ip_rotator = IPRotator(target_url="deepinfra.com")
|
|
self.ip_rotator.setup()
|
|
except Exception as e:
|
|
print(f"Failed to set up IP rotation: {e}. Continuing without IP rotation.")
|
|
self.ip_rotator = None
|
|
|
|
|
|
self.headers = self._create_headers()
|
|
|
|
|
|
if self.use_ip_rotation and self.ip_rotator:
|
|
self.session = self.ip_rotator.get_session()
|
|
else:
|
|
self.session = requests.Session()
|
|
|
|
self.session.headers.update(self.headers)
|
|
|
|
|
|
if self.use_proxy_rotation and self.proxy_finder:
|
|
self._apply_random_proxy()
|
|
|
|
|
|
self.models = Models(self)
|
|
self.chat = ChatCompletions(self)
|
|
self.completions = Completions(self)
|
|
|
|
def _create_headers(self) -> Dict[str, str]:
|
|
"""Create headers for the HTTP request, optionally with a random user agent"""
|
|
user_agent = self.user_agent.random if self.use_random_user_agent else \
|
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
|
|
|
|
headers = {
|
|
'User-Agent': user_agent,
|
|
'Accept-Language': 'en-US,en;q=0.9',
|
|
'Cache-Control': 'no-cache',
|
|
'Connection': 'keep-alive',
|
|
'Content-Type': 'application/json',
|
|
'Origin': 'https://deepinfra.com',
|
|
'Referer': 'https://deepinfra.com/',
|
|
'Sec-Fetch-Dest': 'empty',
|
|
'Sec-Fetch-Mode': 'cors',
|
|
'Sec-Fetch-Site': 'same-site',
|
|
'X-Deepinfra-Source': 'web-embed',
|
|
'accept': 'text/event-stream',
|
|
'sec-ch-ua-mobile': '?0',
|
|
'sec-ch-ua-platform': '"macOS"'
|
|
}
|
|
|
|
if self.api_key:
|
|
headers['Authorization'] = f"Bearer {self.api_key}"
|
|
|
|
return headers
|
|
|
|
def _apply_random_proxy(self):
|
|
"""Apply a random proxy to the current session"""
|
|
if not self.proxy_finder:
|
|
return False
|
|
|
|
|
|
for proxy_type in self.proxy_types:
|
|
proxy = self.proxy_finder.get_random_proxy(proxy_type)
|
|
if proxy:
|
|
if proxy_type in ['http', 'https']:
|
|
self.session.proxies.update({
|
|
"http": f"http://{proxy}",
|
|
"https": f"http://{proxy}"
|
|
})
|
|
return True
|
|
elif proxy_type == 'socks4':
|
|
self.session.proxies.update({
|
|
"http": f"socks4://{proxy}",
|
|
"https": f"socks4://{proxy}"
|
|
})
|
|
return True
|
|
elif proxy_type == 'socks5':
|
|
self.session.proxies.update({
|
|
"http": f"socks5://{proxy}",
|
|
"https": f"socks5://{proxy}"
|
|
})
|
|
return True
|
|
|
|
|
|
return False
|
|
|
|
def refresh_session(self):
|
|
"""Refresh the session with new headers and possibly a new proxy or IP"""
|
|
if self.use_random_user_agent:
|
|
self.headers['User-Agent'] = self.user_agent.random
|
|
|
|
|
|
if self.use_proxy_rotation and self.proxy_finder:
|
|
proxy_applied = self._apply_random_proxy()
|
|
|
|
if not proxy_applied:
|
|
self.proxy_finder.get_proxies(self.proxy_types)
|
|
self._apply_random_proxy()
|
|
|
|
|
|
if self.use_ip_rotation and self.ip_rotator:
|
|
self.session = self.ip_rotator.get_session()
|
|
|
|
self.session.headers.update(self.headers)
|
|
|
|
def _request(self, method: str, endpoint: str, **kwargs) -> requests.Response:
|
|
"""Make an HTTP request with automatic retry and proxy/user-agent rotation"""
|
|
url = f"{self.base_url}/{endpoint.lstrip('/')}"
|
|
max_retries = 3
|
|
retry_delay = 1
|
|
|
|
for attempt in range(max_retries):
|
|
try:
|
|
response = self.session.request(method, url, **kwargs)
|
|
response.raise_for_status()
|
|
return response
|
|
except requests.RequestException as e:
|
|
if attempt < max_retries - 1:
|
|
self.refresh_session()
|
|
time.sleep(retry_delay * (attempt + 1))
|
|
continue
|
|
raise e
|
|
|
|
def refresh_proxies(self):
|
|
"""Refresh all proxies by fetching new ones"""
|
|
if self.proxy_finder:
|
|
self.proxy_finder.get_proxies(self.proxy_types)
|
|
self._apply_random_proxy()
|
|
return True
|
|
return False
|
|
|
|
def __del__(self):
|
|
"""Clean up resources on deletion"""
|
|
if self.ip_rotator:
|
|
try:
|
|
self.ip_rotator.shutdown()
|
|
except:
|
|
pass
|
|
|
|
class Models:
|
|
def __init__(self, client: DeepInfraClient):
|
|
self.client = client
|
|
|
|
def list(self) -> Dict[str, Any]:
|
|
"""Get available models, similar to OpenAI's /v1/models endpoint"""
|
|
model_data = []
|
|
for model_id in self.client.AVAILABLE_MODELS:
|
|
model_data.append({
|
|
"id": model_id,
|
|
"object": "model",
|
|
"created": 1677610602,
|
|
"owned_by": "deepinfra"
|
|
})
|
|
|
|
return {
|
|
"object": "list",
|
|
"data": model_data
|
|
}
|
|
|
|
class ChatCompletions:
|
|
def __init__(self, client: DeepInfraClient):
|
|
self.client = client
|
|
|
|
def create(
|
|
self,
|
|
messages: List[Dict[str, str]],
|
|
model: str = None,
|
|
temperature: float = 0.7,
|
|
max_tokens: int = None,
|
|
stream: bool = False,
|
|
**kwargs
|
|
) -> Union[Dict[str, Any], Generator[Dict[str, Any], None, None]]:
|
|
"""Create a chat completion, similar to OpenAI's chat/completions endpoint"""
|
|
model = model or self.client.model
|
|
max_tokens = max_tokens or self.client.max_tokens
|
|
|
|
url = "openai/chat/completions"
|
|
|
|
|
|
payload = {
|
|
"model": model,
|
|
"messages": messages,
|
|
"temperature": temperature,
|
|
"max_tokens": max_tokens,
|
|
"stream": stream
|
|
}
|
|
|
|
|
|
payload.update({k: v for k, v in kwargs.items() if v is not None})
|
|
|
|
if stream:
|
|
return self._handle_stream(url, payload)
|
|
else:
|
|
return self._handle_request(url, payload)
|
|
|
|
def _handle_request(self, url: str, payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Handle non-streaming requests"""
|
|
try:
|
|
response = self.client._request(
|
|
"POST",
|
|
url,
|
|
json=payload,
|
|
timeout=self.client.timeout
|
|
)
|
|
return response.json()
|
|
except requests.RequestException as e:
|
|
error_message = f"Request failed: {str(e)}"
|
|
if hasattr(e, 'response') and e.response is not None:
|
|
try:
|
|
error_data = e.response.json()
|
|
if 'error' in error_data:
|
|
error_message = f"API error: {error_data['error']}"
|
|
except:
|
|
error_message = f"API error: {e.response.text}"
|
|
|
|
raise Exception(error_message)
|
|
|
|
def _handle_stream(self, url: str, payload: Dict[str, Any]) -> Generator[Dict[str, Any], None, None]:
|
|
"""Handle streaming requests"""
|
|
try:
|
|
response = self.client._request(
|
|
"POST",
|
|
url,
|
|
json=payload,
|
|
stream=True,
|
|
timeout=self.client.timeout
|
|
)
|
|
|
|
for line in response.iter_lines(decode_unicode=True):
|
|
if line:
|
|
line = line.strip()
|
|
if line.startswith("data: "):
|
|
json_str = line[6:]
|
|
if json_str == "[DONE]":
|
|
break
|
|
try:
|
|
json_data = json.loads(json_str)
|
|
yield json_data
|
|
except json.JSONDecodeError:
|
|
continue
|
|
except requests.RequestException as e:
|
|
error_message = f"Stream request failed: {str(e)}"
|
|
if hasattr(e, 'response') and e.response is not None:
|
|
try:
|
|
error_data = e.response.json()
|
|
if 'error' in error_data:
|
|
error_message = f"API error: {error_data['error']}"
|
|
except:
|
|
error_message = f"API error: {e.response.text}"
|
|
|
|
raise Exception(error_message)
|
|
|
|
class Completions:
|
|
def __init__(self, client: DeepInfraClient):
|
|
self.client = client
|
|
|
|
def create(
|
|
self,
|
|
prompt: str,
|
|
model: str = None,
|
|
temperature: float = 0.7,
|
|
max_tokens: int = None,
|
|
stream: bool = False,
|
|
**kwargs
|
|
) -> Union[Dict[str, Any], Generator[Dict[str, Any], None, None]]:
|
|
"""Create a completion, similar to OpenAI's completions endpoint"""
|
|
|
|
messages = [{"role": "user", "content": prompt}]
|
|
|
|
return self.client.chat.create(
|
|
messages=messages,
|
|
model=model,
|
|
temperature=temperature,
|
|
max_tokens=max_tokens,
|
|
stream=stream,
|
|
**kwargs
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
import time
|
|
from rich import print
|
|
|
|
|
|
client = DeepInfraClient(
|
|
use_random_user_agent=True,
|
|
use_ip_rotation=True,
|
|
use_proxy_rotation=True,
|
|
proxy_types=['http', 'socks5']
|
|
)
|
|
|
|
|
|
models_response = client.models.list()
|
|
print("Available models:")
|
|
for model in models_response["data"][:5]:
|
|
print(f"- {model['id']}")
|
|
print("...")
|
|
|
|
|
|
chat_response = client.chat.create(
|
|
messages=[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Write a short poem about AI"}
|
|
]
|
|
)
|
|
print("\nNon-streaming response:")
|
|
print(chat_response["choices"][0]["message"]["content"])
|
|
|
|
|
|
print("\nRefreshing proxies and making another request...")
|
|
client.refresh_proxies()
|
|
client.refresh_session()
|
|
|
|
streaming_response = client.chat.create(
|
|
messages=[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Tell me about the future of AI in 3 sentences."}
|
|
],
|
|
stream=True
|
|
)
|
|
|
|
print("\nStreaming response:")
|
|
full_response = ""
|
|
for chunk in streaming_response:
|
|
if 'choices' in chunk and len(chunk['choices']) > 0:
|
|
delta = chunk['choices'][0].get('delta', {})
|
|
if 'content' in delta:
|
|
content = delta['content']
|
|
print(content, end='', flush=True)
|
|
full_response += content
|
|
print("\n")
|
|
|