534 lines
23 KiB
Python
534 lines
23 KiB
Python
"""AITURK IDE "Codex Baglama" ayiricisi.
|
||
|
||
13 Ekim 2026 aciklamasi: abonelik ve ek modellerin saglayici yollari ayri
|
||
kalir; gecis gecmisinin hazirlanmasi yasak degildir. Ses Codex'in yerlisik
|
||
ses hattinda kalir. Saf abonelik sohbeti degistirilmez.
|
||
|
||
Codex'te tek adres ayari (`openai_base_url`) oldugu icin butun istekler buraya gelir. Bu dosya YALNIZ ayirir:
|
||
|
||
* Codex'in kendi modeli -> chatgpt.com'a; govde bayt bayt ayni gider, cevap geldigi gibi doner.
|
||
* Bizim ekledigimiz model -> ayri bir surecte calisan kopruye (kopru.py). Codex'in kendi modelinin istegi
|
||
(ide/..., 9r/..., model_map) o surece HIC gitmez.
|
||
* Model listesi -> kopru ayaktaysa ek modelleri eklenmis liste; degilse chatgpt.com'un listesi.
|
||
* Geri kalan her sey -> chatgpt.com'a, oldugu gibi.
|
||
|
||
Karisik gecmiste yerel aktarim hazirligi inference'dan ayridir. Abonelik
|
||
inference'i her zaman CHATGPT'ye; ek model inference'i KOPRU'ye gider.
|
||
Saf abonelik sohbetleri ve ses, ozet servisinin durumundan etkilenmez.
|
||
|
||
Veri klasoru (bridge.json, log/): --home <klasor> ya da AITURK_CODEX_BRIDGE_HOME. Ajan klasoru: --hermes-home.
|
||
Yerel denetim: GET /aiturk/codex-baglama/status, POST .../stop, POST .../refresh, POST .../kopru-yenile.
|
||
"""
|
||
import gzip
|
||
import hashlib
|
||
import http.client
|
||
import http.server
|
||
import json
|
||
import os
|
||
import subprocess
|
||
import sys
|
||
import threading
|
||
import time
|
||
import urllib.error
|
||
import urllib.request
|
||
import gecis_context
|
||
|
||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||
SURUM = "11" # AITURK IDE'nin bekledigi kopru surumu (codex-link-config.ts CODEX_BRIDGE_VERSION)
|
||
try:
|
||
with open(os.path.abspath(__file__), "rb") as _f:
|
||
KOD = hashlib.sha256(_f.read()).hexdigest()[:8]
|
||
except OSError:
|
||
KOD = ""
|
||
|
||
|
||
def _arg(name, env):
|
||
"""--name <deger> komut satirindan, yoksa ortam degiskeninden."""
|
||
if name in sys.argv[1:]:
|
||
k = sys.argv.index(name)
|
||
if k + 1 < len(sys.argv):
|
||
return sys.argv[k + 1]
|
||
return os.environ.get(env) or ""
|
||
|
||
|
||
HOME = _arg("--home", "AITURK_CODEX_BRIDGE_HOME") or HERE
|
||
HERMES = _arg("--hermes-home", "HERMES_HOME")
|
||
try:
|
||
with open(os.path.join(HOME, "bridge.json"), encoding="utf-8") as _f:
|
||
CFG = json.load(_f)
|
||
except (OSError, ValueError):
|
||
CFG = {}
|
||
PORT = int(CFG.get("port", 8790))
|
||
KOPRU_PORT = int(_arg("--kopru-port", "AITURK_CODEX_KOPRU_PORT") or PORT + 1)
|
||
CHATGPT = CFG.get("chatgpt_backend", "https://chatgpt.com").rstrip("/")
|
||
KOPRU = f"http://127.0.0.1:{KOPRU_PORT}"
|
||
BEKLEME = float(CFG.get("upstream_idle_timeout", 3600)) # okuma basina; uzun cevap kesilmez (28 Eylul 2026 karari)
|
||
LOGDIR = os.path.join(HOME, "log")
|
||
|
||
# Codex istek govdesini zstd ile sikistirir. Model adini okumak icin bir KOPYASI acilir; giden govde degismez.
|
||
_VENDOR = os.path.join(HERE, "vendor", "cp%d%d-win_amd64" % sys.version_info[:2])
|
||
if os.name == "nt" and os.path.isdir(_VENDOR):
|
||
sys.path.insert(0, _VENDOR)
|
||
|
||
|
||
def _zstd_ac(govde):
|
||
try:
|
||
from compression import zstd as _z
|
||
return _z.decompress(govde)
|
||
except ImportError:
|
||
import zstandard
|
||
return zstandard.ZstdDecompressor().decompressobj().decompress(govde)
|
||
|
||
|
||
def _zstd_hazir():
|
||
try:
|
||
try:
|
||
from compression import zstd as _z
|
||
ornek = _z.compress(b"aiturk")
|
||
except ImportError:
|
||
import zstandard
|
||
ornek = zstandard.ZstdCompressor().compress(b"aiturk")
|
||
return _zstd_ac(ornek) == b"aiturk"
|
||
except Exception:
|
||
return False
|
||
|
||
|
||
# zstd yoksa model adi okunamaz ve ek modeller ayrilamaz; durum ucu ok=false der, IDE Codex'i buraya yoneltmez.
|
||
ZSTD_OK = _zstd_hazir()
|
||
|
||
kilit = threading.Lock()
|
||
log_kilit = threading.Lock()
|
||
transfer_kilit = threading.Lock()
|
||
sira = [0]
|
||
|
||
# Store only hashed conversation keys and the last external model, not messages
|
||
# or credentials. Survives restarts so returning long chats keep their guard.
|
||
TRANSFER_PATH = os.path.join(HOME, "gecis-sohbetler.json")
|
||
try:
|
||
with open(TRANSFER_PATH, encoding="utf-8") as f:
|
||
TRANSFERS = json.load(f)
|
||
if not isinstance(TRANSFERS, dict):
|
||
TRANSFERS = {}
|
||
except (OSError, ValueError):
|
||
TRANSFERS = {}
|
||
|
||
|
||
def remember_transfer(scope, model):
|
||
with transfer_kilit:
|
||
if TRANSFERS.get(scope) == model:
|
||
return
|
||
TRANSFERS[scope] = model
|
||
while len(TRANSFERS) > 2048:
|
||
del TRANSFERS[next(iter(TRANSFERS))]
|
||
try:
|
||
os.makedirs(HOME, exist_ok=True)
|
||
with open(TRANSFER_PATH + ".tmp", "w", encoding="utf-8") as f:
|
||
json.dump(TRANSFERS, f)
|
||
os.replace(TRANSFER_PATH + ".tmp", TRANSFER_PATH)
|
||
except OSError:
|
||
pass # in-memory guard remains active
|
||
|
||
|
||
def log(msg):
|
||
satir = time.strftime("%Y-%m-%d %H:%M:%S ") + msg
|
||
try:
|
||
os.makedirs(LOGDIR, exist_ok=True)
|
||
with log_kilit:
|
||
with open(os.path.join(LOGDIR, "ayirici.log"), "a", encoding="utf-8") as f:
|
||
f.write(satir + "\n")
|
||
except OSError:
|
||
pass
|
||
|
||
|
||
def ek_model_mu(slug):
|
||
"""Bizim ekledigimiz model mi? Degilse Codex'in kendi modelidir."""
|
||
slug = str(slug or "")
|
||
onek = (CFG.get("nine_router_catalog") or {}).get("prefix", "9r/")
|
||
return bool(slug) and (slug.startswith("ide/") or bool(onek and slug.startswith(onek))
|
||
or slug in (CFG.get("model_map") or {}))
|
||
|
||
|
||
def model_oku(govde, kodlama):
|
||
"""Istekteki model adi; okunamazsa None (o zaman istek Codex'in kendi yoluna gider). Govdeye dokunmaz."""
|
||
try:
|
||
kodlama = (kodlama or "").strip().lower()
|
||
ham = _zstd_ac(govde) if kodlama == "zstd" else gzip.decompress(govde) if kodlama == "gzip" else govde
|
||
veri = json.loads(ham)
|
||
return veri.get("model") if isinstance(veri, dict) else None
|
||
except Exception:
|
||
return None
|
||
|
||
|
||
ISTEK_ATLA = {"host", "content-length", "accept-encoding", "transfer-encoding", "connection"}
|
||
YANIT_ATLA = {"host", "content-length", "accept-encoding", "transfer-encoding", "connection", "content-encoding"}
|
||
|
||
|
||
def kopru_sor(yol, yontem="GET", sure=5.0, govde=None):
|
||
"""Kopru surecinin denetim ucuna sorar; (durum kodu, JSON) ya da ulasilamazsa (None, None)."""
|
||
try:
|
||
istek = urllib.request.Request(KOPRU + yol, data=govde, method=yontem)
|
||
with urllib.request.urlopen(istek, timeout=sure) as yanit:
|
||
return yanit.status, json.loads(yanit.read() or b"{}")
|
||
except Exception:
|
||
return None, None
|
||
|
||
|
||
class Kopru:
|
||
"""Bizim kopruyu (kopru.py) ayri surec olarak calistirir; duserse yeniden baslatir."""
|
||
|
||
def __init__(self):
|
||
self.surec = None
|
||
self.duruyor = False
|
||
self.baslangic = 0.0
|
||
|
||
def komut(self):
|
||
k = [sys.executable, os.path.join(HERE, "kopru.py"), "--home", HOME, "--port", str(KOPRU_PORT)]
|
||
return k + (["--hermes-home", HERMES] if HERMES else [])
|
||
|
||
def baslat(self):
|
||
self.baslangic = time.time()
|
||
self.surec = subprocess.Popen(self.komut(), cwd=HERE, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
|
||
stderr=subprocess.DEVNULL,
|
||
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0))
|
||
log(f"kopru baslatildi (surec {self.surec.pid}, kapi {KOPRU_PORT})")
|
||
|
||
def bekci(self):
|
||
"""Kopru duserse yeniden baslatir: hemen cokuyorsa araligi acar (2, 4, ... en cok 30 sn)."""
|
||
kopru_sor("/aiturk/codex-baglama/stop", "POST", 2.0, b"") # onceki ayiricidan kalmis sahipsiz kopru
|
||
time.sleep(0.5)
|
||
ara = 2.0
|
||
while not self.duruyor:
|
||
try:
|
||
self.baslat()
|
||
kod = self.surec.wait()
|
||
except Exception as e:
|
||
kod = f"{type(e).__name__}: {e}"
|
||
if self.duruyor:
|
||
break
|
||
omur = time.time() - self.baslangic
|
||
ara = 2.0 if omur > 60 else min(ara * 2, 30.0)
|
||
log(f"kopru durdu (cikis {kod}, {int(omur)} sn calisti); {int(ara)} sn sonra yeniden baslatilacak")
|
||
time.sleep(ara)
|
||
|
||
def yenile(self):
|
||
"""Yalniz kopruyu yeniden baslatir; Codex'in kendi modellerinin trafigi kesilmez."""
|
||
if kopru_sor("/aiturk/codex-baglama/stop", "POST", 3.0, b"")[0] is None and self.surec is not None:
|
||
try:
|
||
self.surec.terminate()
|
||
except Exception:
|
||
pass
|
||
|
||
def durdur(self):
|
||
self.duruyor = True
|
||
kopru_sor("/aiturk/codex-baglama/stop", "POST", 3.0, b"")
|
||
try:
|
||
if self.surec is not None:
|
||
self.surec.wait(timeout=3)
|
||
except Exception:
|
||
try:
|
||
self.surec.terminate()
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
KOPRU_SUREC = Kopru()
|
||
|
||
|
||
class H(http.server.BaseHTTPRequestHandler):
|
||
protocol_version = "HTTP/1.1"
|
||
|
||
def log_message(self, *a):
|
||
pass
|
||
|
||
def _oku(self):
|
||
if "chunked" in (self.headers.get("Transfer-Encoding") or "").lower():
|
||
out = bytearray()
|
||
while True:
|
||
ham = self.rfile.readline()
|
||
if not ham:
|
||
break
|
||
satir = ham.strip()
|
||
if not satir:
|
||
continue
|
||
boyut = int(satir.split(b";")[0], 16)
|
||
if boyut == 0:
|
||
while self.rfile.readline().strip():
|
||
pass
|
||
break
|
||
out += self.rfile.read(boyut)
|
||
self.rfile.readline()
|
||
return bytes(out)
|
||
n = int(self.headers.get("Content-Length") or 0)
|
||
return self.rfile.read(n) if n else b""
|
||
|
||
def _json(self, durum, veri):
|
||
b = json.dumps(veri).encode()
|
||
self.send_response(durum)
|
||
self.send_header("Content-Type", "application/json")
|
||
self.send_header("Content-Length", str(len(b)))
|
||
self.end_headers()
|
||
self.wfile.write(b)
|
||
|
||
def _denetim(self, yol, govde):
|
||
son = yol.split("?", 1)[0].rstrip("/")
|
||
if self.command == "POST" and son.endswith("/stop"):
|
||
self._json(200, {"ok": True})
|
||
log("AITURK IDE istegiyle kapatiliyor")
|
||
threading.Thread(target=lambda: (KOPRU_SUREC.durdur(), time.sleep(0.2), os._exit(0)), daemon=True).start()
|
||
return
|
||
if self.command == "POST" and son.endswith("/kopru-yenile"):
|
||
log("kopru yeniden baslatiliyor (istek uzerine); Codex'in kendi modelleri etkilenmez")
|
||
KOPRU_SUREC.yenile()
|
||
return self._json(200, {"ok": True})
|
||
if son.endswith("/status") or (self.command == "POST" and son.endswith("/refresh")):
|
||
kod, veri = kopru_sor(yol, self.command, 40.0 if son.endswith("/refresh") else 5.0,
|
||
govde if self.command == "POST" else None)
|
||
if kod == 200 and isinstance(veri, dict):
|
||
veri.update({"ayirici": True, "ayirici_kod": KOD, "kopru": "calisiyor"})
|
||
# IDE compares the splitter, not the older worker's version.
|
||
veri["surum"] = SURUM
|
||
veri["ok"] = bool(veri.get("ok")) and ZSTD_OK
|
||
return self._json(200, veri)
|
||
return self._json(200, {"ok": ZSTD_OK, "surum": SURUM, "kod": KOD, "zstd": ZSTD_OK, "ide_durum": "kapali",
|
||
"ide_model": 0, "ozet": False, "is_hatti": False, "ayirici": True,
|
||
"ayirici_kod": KOD, "kopru": "kapali"})
|
||
return self._json(404, {"error": {"message": "unknown control path"}})
|
||
|
||
def _ilet(self, n, hedef, yol, govde, etiket, yedekli=False, sessiz=False, rewritten=False, started=False):
|
||
"""Istegi hedefe OLDUGU GIBI iletir, cevabi geldigi gibi (akarak) geri verir.
|
||
yedekli=True: cevap basliklari gelmeden bir sorun olursa hicbir sey gonderilmez ve False doner (cagiran baska
|
||
hedefi dener). Diger durumlarda True doner."""
|
||
tag = f"[{n} {etiket} {self.command} {yol[:70]}]"
|
||
basliklar = {k: v for k, v in self.headers.items() if k.lower() not in ISTEK_ATLA}
|
||
if rewritten:
|
||
basliklar = {k: v for k, v in basliklar.items() if k.lower() != "content-encoding"}
|
||
if self.command == "GET" and "/models" in yol:
|
||
# Liste her seferinde tam gelsin: istemcinin elindeki onbellek damgasi iki tarafin listesini karistirir.
|
||
basliklar = {k: v for k, v in basliklar.items() if k.lower() not in ("if-none-match", "if-modified-since")}
|
||
istek = urllib.request.Request(hedef + yol, data=govde or None, headers=basliklar, method=self.command)
|
||
try:
|
||
yanit = urllib.request.urlopen(istek, timeout=20.0 if yedekli else BEKLEME)
|
||
durum = yanit.status
|
||
except urllib.error.HTTPError as e:
|
||
yanit, durum = e, e.code
|
||
except Exception as e:
|
||
if yedekli:
|
||
return False
|
||
log(f"{tag} HATA {type(e).__name__}: {e}")
|
||
if started:
|
||
self._transfer_fail("Model bağlantısı kurulamadı; sohbet geçmişi korundu.")
|
||
return True
|
||
if hedef == KOPRU:
|
||
self._json(503, {"error": {"message": "AITURK köprüsü şu an çalışmıyor; birkaç saniye sonra yeniden deneyin "
|
||
"ya da ekrandaki seçiciden Codex'in kendi modelini seçin."}})
|
||
else:
|
||
self._json(502, {"error": {"message": f"baglanti kurulamadi: {e}"}})
|
||
return True
|
||
if yedekli and durum != 200:
|
||
try:
|
||
yanit.read()
|
||
except Exception:
|
||
pass
|
||
return False
|
||
if durum >= 400:
|
||
hata = yanit.read()
|
||
log(f"{tag} {durum}: {hata[:300]!r}")
|
||
if started:
|
||
try:
|
||
message = json.loads(hata).get("error", {}).get("message", "Model isteği reddedildi.")
|
||
except (ValueError, AttributeError):
|
||
message = "Model isteği reddedildi (HTTP %d)." % durum
|
||
self._transfer_fail(message)
|
||
return True
|
||
self.send_response(durum)
|
||
for k, v in yanit.headers.items():
|
||
if k.lower() not in YANIT_ATLA:
|
||
self.send_header(k, v)
|
||
self.send_header("Content-Length", str(len(hata)))
|
||
self.end_headers()
|
||
self.wfile.write(hata)
|
||
return True
|
||
if not started:
|
||
self.send_response(durum)
|
||
for k, v in yanit.headers.items():
|
||
if k.lower() not in YANIT_ATLA:
|
||
self.send_header(k, v)
|
||
self.send_header("Transfer-Encoding", "chunked")
|
||
self.end_headers()
|
||
toplam = 0
|
||
try:
|
||
while True:
|
||
try:
|
||
parca = yanit.read1(4096) # gelen kadarini hemen verir; bekletmez
|
||
except (OSError, http.client.HTTPException) as e:
|
||
# Hedef koptu: baglanti kapatilir ki Codex yarim akisi tam sanmasin.
|
||
log(f"{tag} hedef koptu ({toplam} bayt): {type(e).__name__}: {e}")
|
||
self.close_connection = True
|
||
return True
|
||
if not parca:
|
||
break
|
||
toplam += len(parca)
|
||
self.wfile.write(b"%x\r\n" % len(parca) + parca + b"\r\n")
|
||
self.wfile.flush()
|
||
self.wfile.write(b"0\r\n\r\n")
|
||
self.wfile.flush()
|
||
except (ConnectionAbortedError, BrokenPipeError, ConnectionResetError):
|
||
log(f"{tag} istemci baglantiyi kapatti ({toplam} bayt)")
|
||
self.close_connection = True
|
||
return True
|
||
if not sessiz:
|
||
log(f"{tag} bitti status={durum} {toplam} bayt")
|
||
return True
|
||
|
||
def _transfer_begin(self):
|
||
self.send_response(200)
|
||
self.send_header("Content-Type", "text/event-stream")
|
||
self.send_header("Transfer-Encoding", "chunked")
|
||
self.end_headers()
|
||
|
||
def _transfer_chunk(self, body):
|
||
self.wfile.write(b"%x\r\n" % len(body) + body + b"\r\n")
|
||
self.wfile.flush()
|
||
|
||
def _transfer_fail(self, message):
|
||
event = {"type": "response.failed", "response": {"id": "resp_aiturk_transfer", "status": "failed",
|
||
"output": [], "error": {"code": "aiturk_transfer_failed", "message": message}}}
|
||
self._transfer_chunk(b"event: response.failed\ndata: " + json.dumps(event, ensure_ascii=False).encode() + b"\n\n")
|
||
self.wfile.write(b"0\r\n\r\n")
|
||
self.wfile.flush()
|
||
|
||
def _prepare(self, req, source_model):
|
||
"""Preparation is separate from inference. ChatGPT auth never goes to AITURK upstream.
|
||
|
||
The local worker needs subscription auth only to render opaque OpenAI
|
||
summaries through their owner. It returns history, never a task answer.
|
||
"""
|
||
done, result = threading.Event(), {}
|
||
def run():
|
||
try:
|
||
headers = {"Content-Type": "application/json", "X-AITURK-Transfer": "1"}
|
||
for key in ("Authorization", "chatgpt-account-id", "originator"):
|
||
if self.headers.get(key):
|
||
headers[key] = self.headers[key]
|
||
data = json.dumps({"request": req, "source_model": source_model}, ensure_ascii=False).encode()
|
||
request = urllib.request.Request(KOPRU + "/aiturk/codex-baglama/prepare-transfer", data=data, headers=headers)
|
||
with urllib.request.urlopen(request, timeout=BEKLEME) as response:
|
||
result["value"] = json.load(response)
|
||
except urllib.error.HTTPError as e:
|
||
try:
|
||
result["error"] = json.loads(e.read())["error"]["message"]
|
||
except Exception:
|
||
result["error"] = "Geçiş hazırlığı başarısız; geçmiş korundu."
|
||
except Exception:
|
||
result["error"] = "AITURK geçiş hazırlığına ulaşılamadı; geçmiş korundu."
|
||
finally:
|
||
done.set()
|
||
threading.Thread(target=run, daemon=True).start()
|
||
started = not done.wait(0.2)
|
||
if started:
|
||
self._transfer_begin()
|
||
self._transfer_chunk(b": AITURK context preparation\n\n")
|
||
while not done.wait(10):
|
||
self._transfer_chunk(b": AITURK context preparation\n\n")
|
||
if "error" in result:
|
||
if not started:
|
||
self._transfer_begin()
|
||
self._transfer_fail(result["error"])
|
||
return None, True
|
||
return result["value"], started
|
||
|
||
def _isle(self):
|
||
with kilit:
|
||
sira[0] += 1
|
||
n = sira[0]
|
||
if "websocket" in (self.headers.get("Upgrade") or "").lower():
|
||
# Bugunku calisan durum: surekli acik baglanti reddedilir, Codex olagan yola duser.
|
||
self.send_response(426)
|
||
self.send_header("Content-Length", "0")
|
||
self.send_header("Connection", "close")
|
||
self.end_headers()
|
||
return
|
||
govde = self._oku()
|
||
yol = self.path
|
||
if yol.startswith("/aiturk/codex-baglama/"):
|
||
return self._denetim(yol, govde)
|
||
son = yol.split("?", 1)[0].rstrip("/")
|
||
if self.command == "POST" and son.endswith("/responses"):
|
||
model = model_oku(govde, self.headers.get("Content-Encoding"))
|
||
external = ek_model_mu(model)
|
||
try:
|
||
encoding = (self.headers.get("Content-Encoding") or "").lower()
|
||
raw = _zstd_ac(govde) if encoding == "zstd" else gzip.decompress(govde) if encoding == "gzip" else govde
|
||
req = json.loads(raw)
|
||
if not isinstance(req, dict):
|
||
req = {}
|
||
except Exception:
|
||
req = {}
|
||
thread = str(req.get("prompt_cache_key") or "")
|
||
scope = hashlib.sha256((str(self.headers.get("chatgpt-account-id") or "") + "|" + thread).encode()).hexdigest()
|
||
source = TRANSFERS.get(scope) if thread else None
|
||
mixed = bool(source) or gecis_context.foreign(req)
|
||
if external and thread:
|
||
remember_transfer(scope, model)
|
||
if external or mixed:
|
||
try:
|
||
normalized = gecis_context.normalize(req)
|
||
except gecis_context.TransferError as e:
|
||
self._transfer_begin()
|
||
return self._transfer_fail(str(e))
|
||
# Small returning requests need no summarizer or local worker.
|
||
if external or gecis_context.tokens(normalized) > int(32768 * 0.70):
|
||
prepared, started = self._prepare(normalized, source)
|
||
if prepared is None:
|
||
return
|
||
if "compaction" in prepared:
|
||
if not started:
|
||
self._transfer_begin()
|
||
item = prepared["compaction"]
|
||
for event in ({"type": "response.output_item.done", "output_index": 0, "item": item},
|
||
{"type": "response.completed", "response": {"id": "resp_aiturk_compact", "object": "response",
|
||
"status": "completed", "output": [item]}}):
|
||
self._transfer_chunk(b"event: " + event["type"].encode() + b"\ndata: " + json.dumps(event, ensure_ascii=False).encode() + b"\n\n")
|
||
self.wfile.write(b"0\r\n\r\n")
|
||
self.wfile.flush()
|
||
return
|
||
normalized = prepared["request"]
|
||
else:
|
||
started = False
|
||
changed = normalized != req
|
||
body = json.dumps(normalized, ensure_ascii=False).encode() if changed else govde
|
||
return self._ilet(n, KOPRU if external else CHATGPT, yol, body,
|
||
f"{'KOPRU' if external else 'OZGUN'} {model}", rewritten=changed, started=started)
|
||
return self._ilet(n, CHATGPT, yol, govde, f"OZGUN {model or '?'}")
|
||
if self.command == "GET" and "/models" in yol:
|
||
# Ek modeller listede gorunsun diye liste koprüden istenir; kopru cevap vermezse chatgpt.com'un listesi.
|
||
if self._ilet(n, KOPRU, yol, govde, "LISTE kopru", yedekli=True, sessiz=True):
|
||
return
|
||
return self._ilet(n, CHATGPT, yol, govde, "LISTE ozgun", sessiz=True)
|
||
return self._ilet(n, CHATGPT, yol, govde, "OZGUN diger", sessiz=True)
|
||
|
||
do_POST = _isle
|
||
do_GET = _isle
|
||
do_PUT = _isle
|
||
do_DELETE = _isle
|
||
do_PATCH = _isle
|
||
|
||
|
||
class Sunucu(http.server.ThreadingHTTPServer):
|
||
daemon_threads = True
|
||
# Shipped Python 3.11 defaults to only five pending connections. Model
|
||
# discovery, voice and simultaneous chats can arrive in the same burst.
|
||
request_queue_size = 128
|
||
allow_reuse_address = False # kapi doluysa sessizce yan yana dinlemek yerine acikca hata
|
||
|
||
|
||
if __name__ == "__main__":
|
||
try:
|
||
sunucu = Sunucu(("127.0.0.1", PORT), H)
|
||
except OSError as e:
|
||
log(f"HATA: 127.0.0.1:{PORT} acilamadi ({e}); baska bir surec bu kapiyi kullaniyor olabilir.")
|
||
sys.exit(1)
|
||
log(f"ayirici :{PORT} Codex'in kendi modelleri -> {CHATGPT} ek modeller -> kopru :{KOPRU_PORT} (kod {KOD})")
|
||
threading.Thread(target=KOPRU_SUREC.bekci, daemon=True).start()
|
||
sunucu.serve_forever()
|