File size: 5,913 Bytes
34d557c
 
 
 
16d445c
34d557c
16d445c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c8920fc
34d557c
 
16d445c
34d557c
16d445c
 
 
 
 
34d557c
16d445c
34d557c
16d445c
34d557c
16d445c
 
 
 
34d557c
16d445c
 
 
34d557c
 
16d445c
c8920fc
 
16d445c
 
 
 
 
 
 
 
 
 
 
 
 
c8920fc
16d445c
c8920fc
 
16d445c
 
 
 
c8920fc
16d445c
 
 
 
 
 
 
 
 
 
c8920fc
16d445c
 
c8920fc
16d445c
c8920fc
16d445c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34d557c
16d445c
 
34d557c
 
16d445c
 
 
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import json
import time
import requests
import jwt
import aiohttp


# ─────────────────────────────────────────
#  곡톡: 응닡을 μ„Έ κ°€μ§€λ‘œ μΆ•μ•½ν•΄μ„œ ν‘œμ‹œ
#        "True"    β†’ 정상
#        "exceed"  β†’ μΏΌν„°β€§λ ˆμ΄νŠΈλ¦¬λ°‹ 초과
#        "False"   β†’ κ·Έ μ™Έ μ‹€νŒ¨
# ─────────────────────────────────────────
def _classify_response(resp: requests.Response) -> str:
    if resp.status_code == 200:
        return "True"

    try:
        err = resp.json().get("error", {})
        code = err.get("code", 0)
        msg = str(err.get("message", "")).lower()

        if code == 429 or "quota" in msg or "rate" in msg or "exceed" in msg:
            return "exceed"
    except Exception:
        pass

    return "False"


def _classify_async_status(code: int, text: str) -> str:
    if code == 200:
        return "True"
    if code == 429 or any(kw in text.lower() for kw in ("quota", "rate", "exceed")):
        return "exceed"
    return "False"


# ─────────────────────────────────────────
#  1) μ„œλΉ„μŠ€ 계정 β†’ μ•‘μ„ΈμŠ€ 토큰
# ─────────────────────────────────────────
def get_access_token(client_email, private_key):
    current_time = int(time.time())
    expiration_time = current_time + 600

    claims = {
        "iss": client_email,
        "scope": "https://www.googleapis.com/auth/cloud-platform",
        "aud": "https://oauth2.googleapis.com/token",
        "exp": expiration_time,
        "iat": current_time,
    }

    try:
        signed_jwt = jwt.encode(claims, private_key, algorithm="RS256")
    except Exception as e:
        return "False", f"JWT encode error: {e}"

    resp = requests.post(
        "https://oauth2.googleapis.com/token",
        data={
            "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
            "assertion": signed_jwt,
        },
    )

    return _classify_response(resp), resp.json().get("access_token", resp.text)


# ─────────────────────────────────────────
#  2) refresh_token β†’ μ•‘μ„ΈμŠ€ 토큰
# ─────────────────────────────────────────
def get_access_token_refresh(client_id, client_secret, refresh_token):
    resp = requests.post(
        "https://oauth2.googleapis.com/token",
        data={
            "client_id": client_id,
            "client_secret": client_secret,
            "refresh_token": refresh_token,
            "grant_type": "refresh_token",
        },
    )

    return _classify_response(resp), resp.json().get("access_token", resp.text)


# ─────────────────────────────────────────
#  3) Gemini λͺ¨λΈ λͺ©λ‘ (κ°„μ†Œν™”)
#     성곡 μ—¬λΆ€λ§Œ λ°˜ν™˜
# ─────────────────────────────────────────
def get_gemini_models(key):
    url = f"https://generativelanguage.googleapis.com/v1beta/models?key={key}&pageSize=1"
    resp = requests.get(url)
    return _classify_response(resp)


# ─────────────────────────────────────────
#  4) Gemini 더미 호좜 (μΏΌν„° 체크)
# ─────────────────────────────────────────
def send_fake_gemini_request(key, model="gemini-1.5-flash"):
    url = f"https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent?key={key}"
    payload = {
        "contents": [{"role": "user", "parts": [{"text": ""}]}],
        "generationConfig": {"maxOutputTokens": 0},
    }

    try:
        resp = requests.post(url, headers={"Content-Type": "application/json"}, json=payload)
        return _classify_response(resp)
    except Exception:
        return "False"


# ─────────────────────────────────────────
#  5) Gemini μ‹€μ œ 호좜 (μš”μ•½ μƒνƒœ λ°˜ν™˜)
# ─────────────────────────────────────────
def send_gemini_request(key, payload, model="gemini-1.5-flash"):
    url = f"https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent?key={key}"
    resp = requests.post(url, headers={"Content-Type": "application/json"}, json=payload)
    return _classify_response(resp), resp.json() if resp.status_code == 200 else resp.text


# ─────────────────────────────────────────
#  6) Vertex AI + Anthropic (streamRawPredict)
#     μƒνƒœλ§Œ κ°„μ†Œν™”ν•΄ λ°˜ν™˜
# ─────────────────────────────────────────
async def send_gcp_request(
    session: aiohttp.ClientSession,
    project_id: str,
    access_token: str,
    payload: str,
    region: str = "us-east5",
    model: str = "claude-3-5-sonnet@20240620",
):
    url = f"https://{region}-aiplatform.googleapis.com/v1/projects/{project_id}/locations/{region}/publishers/anthropic/models/{model}:streamRawPredict"
    headers = {
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json; charset=utf-8",
    }

    async with session.post(url, headers=headers, data=payload) as resp:
        text = await resp.text()
        return _classify_async_status(resp.status, text), text