File size: 3,582 Bytes
2446f5f
500ef17
2446f5f
 
56d0fcf
 
6b64125
56d0fcf
 
 
 
 
 
 
2446f5f
 
 
1c864be
500ef17
 
 
 
 
6b64125
 
 
 
 
 
 
2446f5f
 
 
1c864be
 
6b64125
2446f5f
326f1b4
500ef17
2446f5f
500ef17
2446f5f
6b64125
 
 
2446f5f
 
 
 
 
 
6b64125
2446f5f
 
6b64125
 
 
 
 
2446f5f
 
1c864be
 
500ef17
 
1c864be
6b64125
500ef17
 
1c864be
500ef17
56d0fcf
500ef17
 
56d0fcf
500ef17
 
56d0fcf
500ef17
 
56d0fcf
 
 
2446f5f
56d0fcf
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
import httpx
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import StreamingResponse
import json
import random
import logging
import ipaddress

# Configure logging to output to stdout
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)

app = FastAPI()

# List of API URLs to be randomized
API_URLS = [
    "https://api.deepinfra.com/v1/openai/chat/completions",
    "https://stage.api.deepinfra.com/v1/openai/chat/completions",
]

def generate_random_ip() -> str:
    """Generate a random IPv4 address, avoiding reserved ranges."""
    while True:
        ip = ipaddress.IPv4Address(random.getrandbits(32))
        if not (ip.is_private or ip.is_multicast or ip.is_reserved or ip.is_loopback):
            return str(ip)

@app.post("/v1/openai/chat/completions")
async def proxy_deepinfra(request: Request):
    """
    Proxies chat completion requests to the DeepInfra API.
    It randomizes the order of API URLs and uses the next as a fallback.
    Adds spoofed random IP headers.
    """
    try:
        body = await request.json()
    except json.JSONDecodeError:
        raise HTTPException(status_code=400, detail="Invalid JSON in request body")

    # Generate a random spoofed IP
    random_ip = generate_random_ip()

    headers = {
        'sec-ch-ua-platform': request.headers.get('sec-ch-ua-platform', '"Windows"'),
        'Referer': request.headers.get('Referer', 'https://deepinfra.com/'),
        'sec-ch-ua': request.headers.get('sec-ch-ua', '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"'),
        'sec-ch-ua-mobile': request.headers.get('sec-ch-ua-mobile', '?0'),
        'User-Agent': request.headers.get('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'),
        'accept': request.headers.get('accept', 'text/event-stream'),
        'X-Deepinfra-Source': request.headers.get('X-Deepinfra-Source', 'web-embed'),
        'Content-Type': request.headers.get('Content-Type', 'application/json'),

        # Spoofed IP headers
        'X-Forwarded-For': random_ip,
        'X-Real-IP': random_ip,
        'Forwarded': f'for={random_ip};proto=https',
    }

    shuffled_urls = random.sample(API_URLS, len(API_URLS))

    async def stream_generator():
        last_error = None
        for url in shuffled_urls:
            logging.info(f"Attempting to connect to: {url} with spoofed IP {random_ip}")
            try:
                async with httpx.AsyncClient() as client:
                    async with client.stream("POST", url, headers=headers, json=body, timeout=None) as response:
                        response.raise_for_status()
                        logging.info(f"Successfully connected. Streaming from: {url}")
                        async for chunk in response.aiter_bytes():
                            yield chunk
                        return
            except (httpx.RequestError, httpx.HTTPStatusError) as e:
                last_error = e
                logging.warning(f"Failed to connect to {url}: {e}. Trying next URL.")
                continue
        if last_error:
            logging.error(f"All API endpoints failed. Last error: {last_error}")
            # In a streaming response, we can't easily raise an HTTPException after starting.
            # The connection will simply close, which the client will see as a failed request.

    return StreamingResponse(stream_generator(), media_type="text-event-stream")