Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -146,50 +146,115 @@ async def v1_chat(request: Request, authorization: str = Header(None)):
|
|
| 146 |
headers={"Access-Control-Allow-Origin": "*"},
|
| 147 |
)
|
| 148 |
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
@api.post("/v1/images/generations")
|
| 154 |
-
async def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
check_key(authorization)
|
| 156 |
-
if not BYTEZ_AUTH:
|
| 157 |
-
raise HTTPException(status_code=500, detail="Server BYTEZ_API_KEY not configured")
|
| 158 |
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
|
|
|
|
|
|
| 164 |
|
| 165 |
-
|
| 166 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
|
| 168 |
headers = {
|
| 169 |
-
"Authorization":
|
| 170 |
-
"Content-Type": "application/json"
|
| 171 |
}
|
| 172 |
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 189 |
"created": int(time.time()),
|
| 190 |
-
"data":
|
| 191 |
}
|
| 192 |
-
|
|
|
|
| 193 |
|
| 194 |
|
| 195 |
|
|
|
|
| 146 |
headers={"Access-Control-Allow-Origin": "*"},
|
| 147 |
)
|
| 148 |
|
| 149 |
+
# ---------------------------------------------------------------------
|
| 150 |
+
# --------------------- /v1/images/generations (FIXED) ---------------
|
| 151 |
+
# ---------------------------------------------------------------------
|
|
|
|
| 152 |
@api.post("/v1/images/generations")
|
| 153 |
+
async def v1_images_generations(request: Request, authorization: str = Header(None)):
|
| 154 |
+
"""
|
| 155 |
+
Fully OpenAI-compatible DALL·E-3 via Bytez
|
| 156 |
+
→ Accepts `model` field (required by Continue.dev, Cursor, etc.)
|
| 157 |
+
→ Ignores it safely (since Bytez uses URL path, not model name)
|
| 158 |
+
→ Returns proper OpenAI format with url + b64_json
|
| 159 |
+
"""
|
| 160 |
check_key(authorization)
|
|
|
|
|
|
|
| 161 |
|
| 162 |
+
if not BYTEZ_AUTH_2:
|
| 163 |
+
raise HTTPException(status_code=500, detail="Server BYTEZ_API_KEY_2 not configured")
|
| 164 |
+
|
| 165 |
+
try:
|
| 166 |
+
payload = await request.json()
|
| 167 |
+
except json.JSONDecodeError:
|
| 168 |
+
raise HTTPException(status_code=400, detail="Invalid JSON")
|
| 169 |
|
| 170 |
+
prompt = payload.get("prompt")
|
| 171 |
+
if not prompt or not prompt.strip():
|
| 172 |
+
raise HTTPException(status_code=400, detail="Field 'prompt' is required and cannot be empty")
|
| 173 |
+
|
| 174 |
+
# These fields are REQUIRED by UI tools even if we ignore some
|
| 175 |
+
model_name = payload.get("model", "dall-e-3") # just for show — we ignore it
|
| 176 |
+
n = payload.get("n", 1)
|
| 177 |
+
size = payload.get("size", "1024x1024")
|
| 178 |
+
quality = payload.get("quality", "standard")
|
| 179 |
+
style = payload.get("style") # vivid or natural
|
| 180 |
+
response_format = payload.get("response_format", "url") # url or b64_json
|
| 181 |
+
|
| 182 |
+
# Map OpenAI sizes → Bytez accepts the same strings
|
| 183 |
+
if size not in ["1024x1024", "1024x1792", "1792x1024"]:
|
| 184 |
+
size = "1024x1024" # fallback
|
| 185 |
+
|
| 186 |
+
bytez_payload = {
|
| 187 |
+
"text": prompt,
|
| 188 |
+
"num_outputs": n,
|
| 189 |
+
"size": size,
|
| 190 |
+
}
|
| 191 |
+
if quality in ["standard", "hd"]:
|
| 192 |
+
bytez_payload["quality"] = quality
|
| 193 |
+
if style in ["vivid", "natural"]:
|
| 194 |
+
bytez_payload["style"] = style
|
| 195 |
|
| 196 |
headers = {
|
| 197 |
+
"Authorization": BYTEZ_AUTH_2,
|
| 198 |
+
"Content-Type": "application/json",
|
| 199 |
}
|
| 200 |
|
| 201 |
+
async with httpx.AsyncClient(timeout=200) as client:
|
| 202 |
+
try:
|
| 203 |
+
resp = await client.post(
|
| 204 |
+
"https://api.bytez.com/models/v2/openai/dall-e-3",
|
| 205 |
+
json=bytez_payload,
|
| 206 |
+
headers=headers,
|
| 207 |
+
)
|
| 208 |
+
resp.raise_for_status()
|
| 209 |
+
except httpx.HTTPStatusError as e:
|
| 210 |
+
try:
|
| 211 |
+
error_detail = e.response.json()
|
| 212 |
+
except:
|
| 213 |
+
error_detail = e.response.text
|
| 214 |
+
raise HTTPException(status_code=e.response.status_code, detail=error_detail)
|
| 215 |
+
except Exception as e:
|
| 216 |
+
raise HTTPException(status_code=502, detail=f"Bytez unreachable: {str(e)}")
|
| 217 |
+
|
| 218 |
+
try:
|
| 219 |
+
bytez_data = resp.json()
|
| 220 |
+
except json.JSONDecodeError:
|
| 221 |
+
raise HTTPException(status_code=502, detail="Bytez returned invalid JSON")
|
| 222 |
+
|
| 223 |
+
# Handle different possible response shapes from Bytez
|
| 224 |
+
images = bytez_data.get("images") or bytez_data.get("data") or []
|
| 225 |
+
if isinstance(images, str):
|
| 226 |
+
images = [images]
|
| 227 |
+
if not images:
|
| 228 |
+
raise HTTPException(status_code=500, detail="No images returned from Bytez")
|
| 229 |
+
|
| 230 |
+
# Build proper OpenAI response
|
| 231 |
+
openai_images = []
|
| 232 |
+
for img_data in images:
|
| 233 |
+
if img_data.startswith("data:image"):
|
| 234 |
+
b64 = img_data.split("base64,")[-1]
|
| 235 |
+
url = img_data
|
| 236 |
+
else:
|
| 237 |
+
b64 = img_data
|
| 238 |
+
url = f"data:image/png;base64,{img_data}"
|
| 239 |
+
|
| 240 |
+
item = {}
|
| 241 |
+
if response_format == "b64_json" or response_format is None:
|
| 242 |
+
item["b64_json"] = b64
|
| 243 |
+
else:
|
| 244 |
+
item["url"] = url
|
| 245 |
+
|
| 246 |
+
# Optional: include revised_prompt if Bytez returns it
|
| 247 |
+
if "revised_prompt" in bytez_data:
|
| 248 |
+
item["revised_prompt"] = bytez_data["revised_prompt"]
|
| 249 |
+
|
| 250 |
+
openai_images.append(item)
|
| 251 |
+
|
| 252 |
+
final_response = {
|
| 253 |
"created": int(time.time()),
|
| 254 |
+
"data": openai_images
|
| 255 |
}
|
| 256 |
+
|
| 257 |
+
return JSONResponse(final_response, headers={"Access-Control-Allow-Origin": "*"})
|
| 258 |
|
| 259 |
|
| 260 |
|