imbecome commited on
Commit
468074f
·
verified ·
1 Parent(s): cfbbf02

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +46 -30
app.py CHANGED
@@ -1,30 +1,27 @@
1
  import os, asyncio, hashlib, json, httpx, uuid, time, uvicorn, random, shutil, tempfile
2
  from contextlib import asynccontextmanager
3
  from fastapi import FastAPI, Request, UploadFile, File
4
- from fastapi.responses import JSONResponse, StreamingResponse
5
  from fastapi.middleware.cors import CORSMiddleware
6
  from faster_whisper import WhisperModel
7
  from gtts import gTTS
8
 
9
- # ====================================
10
- # CONFIGURASI MODEL (Gampang di-Manage)
11
- # ====================================
12
  MODEL_STRATEGY = {
13
  "deepseek": [
14
  {"provider": "ollama", "model": "deepseek-r1:7b"},
15
- {"provider": "openrouter", "model": "deepseek/deepseek-r1"},
16
- {"provider": "cloudflare", "model": "@cf/meta/llama-3-8b-instruct"}
17
  ],
18
- "gpt": [{"provider": "openrouter", "model": "openai/gpt-4o-mini"}]
 
19
  }
20
-
21
- TIMEOUT = 120
22
  QUEUE = asyncio.Queue()
 
 
23
  whisper_model = WhisperModel("tiny", device="cpu", compute_type="int8", download_root="./models_cache")
24
 
25
- # ====================================
26
- # CORE AI ROUTER
27
- # ====================================
28
  async def call_ollama(messages, model):
29
  try:
30
  async with httpx.AsyncClient(timeout=TIMEOUT) as client:
@@ -35,7 +32,7 @@ async def call_ollama(messages, model):
35
 
36
  async def call_openrouter(messages, model):
37
  keys = os.getenv("OPENROUTER_KEYS", "").split(",")
38
- if not keys[0]: return None
39
  try:
40
  async with httpx.AsyncClient(timeout=TIMEOUT) as client:
41
  res = await client.post("https://openrouter.ai/api/v1/chat/completions",
@@ -48,17 +45,10 @@ async def smart_router(messages, alias):
48
  strategies = MODEL_STRATEGY.get(alias, MODEL_STRATEGY["deepseek"])
49
  for attempt in strategies:
50
  prov, mod = attempt["provider"], attempt["model"]
51
- print(f"[*] Mencoba {prov} ({mod})...")
52
- res = None
53
- if prov == "ollama": res = await call_ollama(messages, mod)
54
- elif prov == "openrouter": res = await call_openrouter(messages, mod)
55
-
56
  if res: return res
57
  return None
58
 
59
- # ====================================
60
- # WORKER & FASTAPI
61
- # ====================================
62
  async def worker():
63
  while True:
64
  messages, alias, future = await QUEUE.get()
@@ -70,30 +60,46 @@ async def worker():
70
 
71
  @asynccontextmanager
72
  async def lifespan(app: FastAPI):
73
- # Jalankan 4 worker paralel
74
  workers = [asyncio.create_task(worker()) for _ in range(4)]
75
  yield
76
  for w in workers: w.cancel()
77
 
78
- app = FastAPI(lifespan=lifespan)
79
  app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
80
 
81
- @app.get("/")
82
- async def root():
83
- return {"status": "AI Gateway Online", "engine": "Ollama + Fallback API"}
 
 
 
 
 
 
 
 
 
 
84
 
85
  @app.post("/v1/chat/completions")
86
  async def chat(request: Request):
87
  body = await request.json()
 
88
  future = asyncio.get_event_loop().create_future()
89
- await QUEUE.put((body.get("messages", []), body.get("model", "deepseek"), future))
90
  result = await future
91
- if not result: return JSONResponse({"error": "Semua model gagal"}, status_code=500)
92
- return {"id": str(uuid.uuid4()), "choices": [{"message": {"role": "assistant", "content": result}}]}
 
 
 
 
 
 
93
 
94
  @app.post("/v1/audio/transcriptions")
95
  async def transcribe(file: UploadFile = File(...)):
96
- with tempfile.NamedTemporaryFile(delete=False, suffix=".tmp") as tmp:
97
  shutil.copyfileobj(file.file, tmp)
98
  path = tmp.name
99
  try:
@@ -102,5 +108,15 @@ async def transcribe(file: UploadFile = File(...)):
102
  finally:
103
  if os.path.exists(path): os.remove(path)
104
 
 
 
 
 
 
 
 
 
 
 
105
  if __name__ == "__main__":
106
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
1
  import os, asyncio, hashlib, json, httpx, uuid, time, uvicorn, random, shutil, tempfile
2
  from contextlib import asynccontextmanager
3
  from fastapi import FastAPI, Request, UploadFile, File
4
+ from fastapi.responses import JSONResponse, StreamingResponse, HTMLResponse
5
  from fastapi.middleware.cors import CORSMiddleware
6
  from faster_whisper import WhisperModel
7
  from gtts import gTTS
8
 
9
+ # --- CONFIG ---
 
 
10
  MODEL_STRATEGY = {
11
  "deepseek": [
12
  {"provider": "ollama", "model": "deepseek-r1:7b"},
13
+ {"provider": "openrouter", "model": "deepseek/deepseek-r1"}
 
14
  ],
15
+ "gpt": [{"provider": "openrouter", "model": "openai/gpt-4o-mini"}],
16
+ "gemini": [{"provider": "openrouter", "model": "google/gemini-2.0-flash"}]
17
  }
18
+ TIMEOUT = 180
 
19
  QUEUE = asyncio.Queue()
20
+
21
+ # Load Whisper
22
  whisper_model = WhisperModel("tiny", device="cpu", compute_type="int8", download_root="./models_cache")
23
 
24
+ # --- PROVIDERS ---
 
 
25
  async def call_ollama(messages, model):
26
  try:
27
  async with httpx.AsyncClient(timeout=TIMEOUT) as client:
 
32
 
33
  async def call_openrouter(messages, model):
34
  keys = os.getenv("OPENROUTER_KEYS", "").split(",")
35
+ if not keys or not keys[0]: return None
36
  try:
37
  async with httpx.AsyncClient(timeout=TIMEOUT) as client:
38
  res = await client.post("https://openrouter.ai/api/v1/chat/completions",
 
45
  strategies = MODEL_STRATEGY.get(alias, MODEL_STRATEGY["deepseek"])
46
  for attempt in strategies:
47
  prov, mod = attempt["provider"], attempt["model"]
48
+ res = await call_ollama(messages, mod) if prov == "ollama" else await call_openrouter(messages, mod)
 
 
 
 
49
  if res: return res
50
  return None
51
 
 
 
 
52
  async def worker():
53
  while True:
54
  messages, alias, future = await QUEUE.get()
 
60
 
61
  @asynccontextmanager
62
  async def lifespan(app: FastAPI):
 
63
  workers = [asyncio.create_task(worker()) for _ in range(4)]
64
  yield
65
  for w in workers: w.cancel()
66
 
67
+ app = FastAPI(title="AI Gateway", lifespan=lifespan)
68
  app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
69
 
70
+ # --- UI & ENDPOINTS ---
71
+ @app.get("/", response_class=HTMLResponse)
72
+ async def home():
73
+ return """
74
+ <body style="background:#0f172a; color:white; font-family:sans-serif; display:flex; flex-direction:column; align-items:center; justify-content:center; height:100vh;">
75
+ <h1 style="color:#38bdf8">AI Gateway Running</h1>
76
+ <p style="color:#94a3b8">OpenAI-Compatible API for YT-Short-Clipper</p>
77
+ <div style="background:#1e293b; padding:20px; border-radius:10px; border:1px solid #334155">
78
+ <code>Endpoint: /v1/chat/completions</code><br>
79
+ <code>Model: deepseek, gpt, gemini</code>
80
+ </div>
81
+ </body>
82
+ """
83
 
84
  @app.post("/v1/chat/completions")
85
  async def chat(request: Request):
86
  body = await request.json()
87
+ alias = body.get("model", "deepseek")
88
  future = asyncio.get_event_loop().create_future()
89
+ await QUEUE.put((body.get("messages", []), alias, future))
90
  result = await future
91
+ if not result: return JSONResponse({"error": "Failed"}, status_code=500)
92
+ return {
93
+ "id": f"chatcmpl-{uuid.uuid4()}",
94
+ "object": "chat.completion",
95
+ "created": int(time.time()),
96
+ "model": alias,
97
+ "choices": [{"message": {"role": "assistant", "content": result}, "finish_reason": "stop"}]
98
+ }
99
 
100
  @app.post("/v1/audio/transcriptions")
101
  async def transcribe(file: UploadFile = File(...)):
102
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp:
103
  shutil.copyfileobj(file.file, tmp)
104
  path = tmp.name
105
  try:
 
108
  finally:
109
  if os.path.exists(path): os.remove(path)
110
 
111
+ @app.post("/v1/audio/speech")
112
+ async def tts(request: Request):
113
+ body = await request.json()
114
+ tmp_path = f"/tmp/{uuid.uuid4()}.mp3"
115
+ gTTS(text=body.get("input", ""), lang=body.get("voice", "id")).save(tmp_path)
116
+ def iter():
117
+ with open(tmp_path, "rb") as f: yield from f
118
+ os.remove(tmp_path)
119
+ return StreamingResponse(iter(), media_type="audio/mpeg")
120
+
121
  if __name__ == "__main__":
122
  uvicorn.run(app, host="0.0.0.0", port=7860)