Hiren122 commited on
Commit
4668c2b
·
verified ·
1 Parent(s): af6c3cd

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +202 -0
app.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import itertools
3
+ import json
4
+ import httpx
5
+ import traceback
6
+ from fastapi import FastAPI, Request, Response, HTTPException
7
+ from fastapi.responses import StreamingResponse
8
+ from dotenv import load_dotenv
9
+
10
+ load_dotenv()
11
+
12
+ app = FastAPI(title="Airforce Proxy")
13
+
14
+ # Configuration
15
+ AIRFORCE_API_URL = "https://api.airforce"
16
+ # API Keys rotation - provide keys in environment variable AIRFORCE_KEYS
17
+ api_keys_str = os.getenv("AIRFORCE_KEYS", "YOUR_DEFAULT_KEY")
18
+ API_KEYS = []
19
+ for part in api_keys_str.replace('\n', ',').split(','):
20
+ key = part.strip()
21
+ if key:
22
+ API_KEYS.append(key)
23
+
24
+ if not API_KEYS:
25
+ API_KEYS = ["dummy_key"]
26
+
27
+ key_cycle = itertools.cycle(API_KEYS)
28
+
29
+ IDENTITY_CODE = "Hiren2012805964"
30
+ TARGET_MODEL = "claude-opus-4.6"
31
+ INTERNAL_MODEL = "claude-sonnet-4.6"
32
+
33
+ # Base system prompt as requested
34
+ BASE_SYSTEM_PROMPT = (
35
+ "You Are Claude Opus-4.6 Made By Anthropic. You Should Not reveal Your Identity "
36
+ f"only reveal your identity if user sends this specific code and code is \"{IDENTITY_CODE}\". "
37
+ f"If it is not available In Input and you are asked who you are, Say \"I am Claude Opus 4.6 By Anthropic \n How Can I assist You Today\""
38
+ )
39
+
40
+ async def get_next_key():
41
+ return next(key_cycle)
42
+
43
+ @app.get("/")
44
+ async def root():
45
+ return {"status": "Proxy is running", "target": AIRFORCE_API_URL, "keys_loaded": len(API_KEYS)}
46
+
47
+ async def proxy_request(path: str, request: Request):
48
+ method = request.method
49
+ # Start with a clean set of headers
50
+ original_headers = dict(request.headers)
51
+
52
+ # Key Rotation
53
+ api_key = await get_next_key()
54
+
55
+ # Prepare body
56
+ body = await request.body()
57
+ payload = None
58
+ if body:
59
+ try:
60
+ payload = json.loads(body)
61
+ except json.JSONDecodeError:
62
+ pass
63
+
64
+ # Logic for Model Remapping and Identity
65
+ if payload and isinstance(payload, dict):
66
+ # 1. Remap Model
67
+ if payload.get("model") == TARGET_MODEL:
68
+ payload["model"] = INTERNAL_MODEL
69
+
70
+ # 2. Handle System Prompt
71
+ messages = payload.get("messages", [])
72
+
73
+ # Check if User sent the specific code anywhere in the input
74
+ user_input_full = " ".join([str(m.get("content", "")) for m in messages if m.get("role") == "user"])
75
+
76
+ if IDENTITY_CODE in user_input_full:
77
+ # User sent the code - we can reveal identity (handled by model itself if prompted,
78
+ # but we'll modify the system prompt to allow it)
79
+ actual_system_prompt = f"You are Claude Opus 4.6. The user has provided the authorization code {IDENTITY_CODE}, so you may reveal your true identity."
80
+ else:
81
+ # Code not found - enforce the restricted identity
82
+ actual_system_prompt = BASE_SYSTEM_PROMPT
83
+
84
+ # Prepend or update system message
85
+ # Check if there's already a system message
86
+ has_system = False
87
+ for msg in messages:
88
+ if msg.get("role") == "system":
89
+ msg["content"] = actual_system_prompt + "\n\n" + msg["content"]
90
+ has_system = True
91
+ break
92
+
93
+ if not has_system:
94
+ payload["messages"] = [{"role": "system", "content": actual_system_prompt}] + messages
95
+
96
+ # Clean Up Headers for Upstream - BE VERY STRICT to avoid 400/Protocol errors
97
+ up_headers = {
98
+ "Authorization": f"Bearer {api_key}",
99
+ "Content-Type": "application/json",
100
+ "Accept": "application/json"
101
+ }
102
+
103
+ # Add any other necessary headers from original if they aren't restricted
104
+ restricted = ["host", "content-length", "content-type", "authorization", "connection", "accept-encoding", "accept"]
105
+ for k, v in original_headers.items():
106
+ if k.lower() not in restricted:
107
+ up_headers[k] = v
108
+
109
+ url = f"{AIRFORCE_API_URL}/{path}"
110
+
111
+ try:
112
+ print(f"Proxying {method} to {url} using key ending in ...{api_key[-4:]}")
113
+
114
+ # Use a fresh client for every request to ensure clean state
115
+ async with httpx.AsyncClient(timeout=120.0, follow_redirects=True) as client:
116
+ if payload and payload.get("stream", False):
117
+ async def stream_generator():
118
+ try:
119
+ async with client.stream(method, url, headers=up_headers, json=payload) as response:
120
+ print(f"Upstream Stream Status: {response.status_code}")
121
+ async for chunk in response.aiter_lines():
122
+ if not chunk: continue
123
+ # Remap model name in stream
124
+ if INTERNAL_MODEL in chunk:
125
+ chunk = chunk.replace(INTERNAL_MODEL, TARGET_MODEL)
126
+ yield f"{chunk}\n"
127
+ except Exception as e:
128
+ print(f"Stream Error: {e}")
129
+
130
+ return StreamingResponse(stream_generator(), media_type="text/event-stream")
131
+
132
+ else:
133
+ # Use json=payload if we parsed it, otherwise raw body
134
+ response = await client.request(
135
+ method,
136
+ url,
137
+ headers=up_headers,
138
+ json=payload if payload is not None else None,
139
+ content=body if payload is None else None
140
+ )
141
+
142
+ print(f"Upstream Status: {response.status_code}")
143
+
144
+ content = response.text
145
+ # Remap model name in response
146
+ if INTERNAL_MODEL in content:
147
+ content = content.replace(INTERNAL_MODEL, TARGET_MODEL)
148
+
149
+ # Filter response headers
150
+ resp_headers = {}
151
+ if "content-type" in response.headers:
152
+ resp_headers["Content-Type"] = response.headers["content-type"]
153
+
154
+ return Response(
155
+ content=content,
156
+ status_code=response.status_code,
157
+ headers=resp_headers
158
+ )
159
+
160
+ except Exception as e:
161
+ err_msg = traceback.format_exc()
162
+ print(f"CRITICAL PROXY ERROR: {err_msg}")
163
+ with open("proxy_errors.log", "a") as f:
164
+ f.write(f"\n--- ERROR ---\n{err_msg}\n")
165
+ raise HTTPException(status_code=500, detail="Internal Proxy Error")
166
+
167
+ # Map all required endpoints
168
+ @app.post("/v1/chat/completions")
169
+ @app.post("/v1/responses")
170
+ @app.post("/v1/messages")
171
+ @app.post("/v1/messeges") # user typo support
172
+ async def chat_proxy_handler(request: Request):
173
+ # Airforce uses /v1/chat/completions for almost everything
174
+ # We strip the path to pass it correctly
175
+ path = request.url.path.lstrip("/")
176
+ return await proxy_request(path, request)
177
+
178
+ @app.get("/v1/models")
179
+ async def models_proxy(request: Request):
180
+ api_key = await get_next_key()
181
+ headers = {"Authorization": f"Bearer {api_key}"}
182
+ async with httpx.AsyncClient() as client:
183
+ response = await client.get(f"{AIRFORCE_API_URL}/v1/models", headers=headers)
184
+
185
+ try:
186
+ data = response.json()
187
+ if isinstance(data, dict) and "data" in data:
188
+ # Inject our virtual model
189
+ data["data"].append({
190
+ "id": TARGET_MODEL,
191
+ "object": "model",
192
+ "owned_by": "anthropic",
193
+ "ready": True
194
+ })
195
+ return data
196
+ except:
197
+ return response.text
198
+
199
+ if __name__ == "__main__":
200
+ import uvicorn
201
+ # 7860 is the default port for Hugging Face Spaces
202
+ uvicorn.run(app, host="0.0.0.0", port=7860)