253 lines
15 KiB
Python
253 lines
15 KiB
Python
"""Offline regressions for cross-provider context migration and native voice."""
|
|
import ast
|
|
import copy
|
|
import hashlib
|
|
import json
|
|
import threading
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
from pathlib import Path
|
|
import unittest
|
|
import urllib.request
|
|
|
|
import gecis_context as gc
|
|
import ozet_kopru
|
|
import bridge
|
|
import test_ayirici as fixtures
|
|
from test_ayirici import duz, zstd, sunucu
|
|
from test_model_switch import env
|
|
|
|
|
|
class History(unittest.TestCase):
|
|
def test_legacy_summary_and_tool_ids_repaired_without_mutating_source(self):
|
|
req = {"input": [ozet_kopru.ozet_ogesi("KARAR-4827 korunacak"),
|
|
{"type": "custom_tool_call", "id": "fc_call_173683", "call_id": "call_1", "input": "abc"},
|
|
{"type": "custom_tool_call_output", "id": "fc_out", "call_id": "call_1", "output": "result"},
|
|
{"type": "compaction", "encrypted_content": "gAAAA_native"}]}
|
|
before = copy.deepcopy(req)
|
|
result = gc.normalize(req)
|
|
self.assertEqual(req, before)
|
|
self.assertIn("KARAR-4827", result["input"][0]["content"][0]["text"])
|
|
self.assertEqual(result["input"][1]["id"], "ctc_call_173683")
|
|
self.assertEqual(result["input"][2]["id"], "ctco_out")
|
|
self.assertEqual(result["input"][3], req["input"][3])
|
|
self.assertEqual(result["input"][1]["call_id"], result["input"][2]["call_id"])
|
|
|
|
def test_already_custom_stream_ids_and_deltas(self):
|
|
adapter = env["SseRewriter"]({"apply_patch"})
|
|
item = {"type": "custom_tool_call", "id": "fc_call_173683", "call_id": "call_1", "name": "apply_patch", "input": "abc"}
|
|
for event in ("response.output_item.added", "response.output_item.done"):
|
|
self.assertEqual(adapter._fix({"type": event, "item": dict(item)})["item"]["id"], "ctc_call_173683")
|
|
self.assertEqual(adapter._fix({"type": "response.custom_tool_call_input.delta", "item_id": item["id"], "delta": "a"})["item_id"], "ctc_call_173683")
|
|
final = adapter._fix({"type": "response.completed", "response": {"output": [dict(item)]}})
|
|
self.assertEqual(final["response"]["output"][0]["id"], "ctc_call_173683")
|
|
|
|
def test_big_history_summarizes_middle_instead_of_cutting_it(self):
|
|
req = {"model": "ide/test", "instructions": "keep instructions", "tools": [], "input": [
|
|
{"role": "user", "content": "A" * 60000 + "KARAR-4827" + "B" * 60000},
|
|
{"role": "assistant", "content": "work complete"},
|
|
{"role": "user", "content": "Son karari uygula"}]}
|
|
before = copy.deepcopy(req)
|
|
calls = []
|
|
def summarize(text, budget):
|
|
self.assertIn("KARAR-4827", text)
|
|
calls.append(budget)
|
|
return "KARAR-4827: Korunacak karar ve acik is. " * 5
|
|
prep = gc.Preparer()
|
|
result = prep.prepare(req, 32768, summarize, "account/thread")
|
|
self.assertEqual(req, before)
|
|
self.assertLessEqual(gc.tokens(result), int(32768 * .7))
|
|
self.assertEqual(result["instructions"], req["instructions"])
|
|
self.assertEqual(result["input"][-1], req["input"][-1])
|
|
self.assertIn("KARAR-4827", json.dumps(result))
|
|
self.assertEqual(prep.prepare(req, 32768, summarize, "account/thread"), result)
|
|
self.assertEqual(len(calls), 1)
|
|
|
|
def test_threshold_includes_tools_and_instructions(self):
|
|
req = {"instructions": "x" * 100000, "tools": [{"type": "function", "name": "p", "description": "y" * 30000}],
|
|
"input": [{"role": "user", "content": "hello"}]}
|
|
with self.assertRaises(gc.TransferError):
|
|
gc.Preparer().prepare(req, 32768, lambda *_: self.fail("must not summarize protected instructions"))
|
|
|
|
def test_failed_summary_never_replaces_history(self):
|
|
req = {"input": [{"role": "user", "content": "a" * 90000}, {"role": "user", "content": "latest"}]}
|
|
before = copy.deepcopy(req)
|
|
for response in ("", "too short", "x" * 100000):
|
|
with self.assertRaises(gc.TransferError):
|
|
gc.Preparer().prepare(req, 32768, lambda *_: response)
|
|
self.assertEqual(req, before)
|
|
|
|
def test_recent_tool_pairs_stay_together(self):
|
|
call = {"type": "custom_tool_call", "call_id": "p", "input": "abc"}
|
|
output = {"type": "custom_tool_call_output", "call_id": "p", "output": "result"}
|
|
protected, old, recent = gc.split_history([
|
|
{"role": "user", "content": "old"}, call, {"role": "user", "content": "latest"}, output], keep_users=1)
|
|
self.assertIn(call, recent)
|
|
self.assertIn(output, recent)
|
|
|
|
|
|
class PreparationService(unittest.TestCase):
|
|
def setUp(self):
|
|
# Extract just the real preparation methods: importing kopru starts its
|
|
# catalog and background threads, which must not happen in offline tests.
|
|
tree = ast.parse(Path(__file__).with_name("kopru.py").read_text(encoding="utf-8"))
|
|
owner = next(x for x in tree.body if isinstance(x, ast.ClassDef) and any(isinstance(m, ast.FunctionDef) and m.name == "_prepare_transfer" for m in x.body))
|
|
methods = [x for x in owner.body if isinstance(x, ast.FunctionDef) and x.name in
|
|
{"_transfer_window", "_transfer_summary", "_prepare_transfer"}]
|
|
self.ns = {"json": json, "hashlib": hashlib, "urllib": __import__("urllib"), "gecis_context": gc,
|
|
"ozet_kopru": ozet_kopru, "pencere": __import__("pencere"), "CFG": {}, "UA": "test",
|
|
"UPSTREAM_IDLE_TIMEOUT": 5, "MODEL_MAP": {"ide/test": ("aiturk", "test", True)},
|
|
"IDE_BILGI": {"ide/test": {"pencere": 32768, "kaynak": "yerel"}},
|
|
"LAST_MODELS": {"acct": {"body": json.dumps({"models": [{"slug": "gpt-6-astra", "context_window": 128000}]})}},
|
|
"ek_model_mu": lambda slug: slug.startswith("ide/"), "yonlendirilen": lambda *_: None}
|
|
def upstream(h, body):
|
|
data = json.loads(body)
|
|
self.assertEqual(data["model"], "test")
|
|
self.assertEqual(data["tools"], [])
|
|
self.assertEqual(h.headers["Authorization"], "Bearer gateway-test")
|
|
self.assertNotIn("chatgpt-account-id", h.headers)
|
|
text = "KARAR-4827: Dosya yolu ve acik is korunacak. " * 3
|
|
events = [{"type": "response.output_item.done", "item": {"type": "message", "content": [{"type": "output_text", "text": text}]}},
|
|
{"type": "response.completed", "response": {"status": "completed", "output": []}}]
|
|
raw = b"".join(b"data: " + json.dumps(e).encode() + b"\n\n" for e in events)
|
|
duz(200, raw, "text/event-stream")(h, body)
|
|
self.server = sunucu(upstream)
|
|
self.ns["ROUTES"] = {"aiturk": {"url": "http://127.0.0.1:%d/responses" % self.server.server_address[1], "key": "gateway-test"}}
|
|
cls = ast.ClassDef(name="Service", bases=[], keywords=[], body=methods, decorator_list=[])
|
|
exec(compile(ast.fix_missing_locations(ast.Module(body=[cls], type_ignores=[])), "preparation", "exec"), self.ns)
|
|
self.service = self.ns["Service"]()
|
|
self.service.headers = {"Authorization": "Bearer native-test", "chatgpt-account-id": "acct"}
|
|
self.service._openai_ozet_coz = lambda *_: None
|
|
|
|
def tearDown(self):
|
|
self.server.shutdown()
|
|
self.server.server_close()
|
|
|
|
def test_large_external_preflight_uses_aiturk_only_and_fits(self):
|
|
req = {"model": "ide/test", "input": [{"role": "user", "content": "x" * 60000 + "KARAR-4827"},
|
|
{"role": "user", "content": "latest"}]}
|
|
result = self.service._prepare_transfer({"request": req})["request"]
|
|
self.assertLessEqual(gc.tokens(result), int(32768 * .7))
|
|
self.assertIn("KARAR-4827", json.dumps(result))
|
|
self.assertGreater(len(self.server.gelen), 1) # bounded chunks, not oversized summarizer request
|
|
|
|
def test_subscription_window_is_read_from_its_own_catalog(self):
|
|
self.assertEqual(self.service._transfer_window("gpt-6-astra"), 128000)
|
|
self.assertEqual(self.service._transfer_window("ide/unknown"), 32768)
|
|
|
|
def test_output_reserve_can_require_preparation_below_seventy_percent(self):
|
|
req = {"model": "ide/test", "max_output_tokens": 24000,
|
|
"input": [{"role": "user", "content": "x" * 20000}, {"role": "user", "content": "latest"}]}
|
|
self.assertLess(gc.tokens(req), int(32768 * .7))
|
|
result = self.service._prepare_transfer({"request": req})["request"]
|
|
self.assertLessEqual(gc.tokens(result), 32768 - 24000)
|
|
self.assertTrue(self.server.gelen)
|
|
|
|
def test_unreadable_native_compaction_never_drops_context(self):
|
|
req = {"model": "ide/test", "input": [{"type": "compaction", "encrypted_content": "gAAAA_unreadable"}]}
|
|
with self.assertRaises(gc.TransferError):
|
|
self.service._prepare_transfer({"request": req})
|
|
self.assertEqual(self.server.gelen, [])
|
|
|
|
def test_external_compaction_uses_external_summary_not_subscription(self):
|
|
req = {"model": "ide/test", "input": [{"role": "user", "content": "KARAR-4827"}, {"type": "compaction_trigger"}]}
|
|
result = self.service._prepare_transfer({"request": req})
|
|
self.assertIn("KARAR-4827", ozet_kopru.ozet_metni(result["compaction"]))
|
|
self.assertEqual(len(self.server.gelen), 1)
|
|
|
|
|
|
class SocketMigration(fixtures.Ayirici):
|
|
def test_fifty_simultaneous_chats_keep_their_provider_routes(self):
|
|
start = threading.Barrier(50)
|
|
def call(index):
|
|
external = index % 2 == 0
|
|
req = {"model": "ide/test" if external else "gpt-6-astra",
|
|
"prompt_cache_key": f"parallel-{index}",
|
|
"input": [{"role": "user", "content": f"marker-{index}"}]}
|
|
start.wait(timeout=5)
|
|
status, body, _ = self.iste('POST', '/backend-api/codex/responses', json.dumps(req).encode())
|
|
self.assertEqual(status, 200)
|
|
self.assertIn(b'"kopru":1' if external else b'"ok":1', body)
|
|
with ThreadPoolExecutor(max_workers=50) as pool:
|
|
list(pool.map(call, range(50)))
|
|
native = [json.loads(x['govde']) for x in self.ozgun.gelen]
|
|
added = [json.loads(x['govde']) for x in self.kopru.gelen if x['yol'].endswith('/responses')]
|
|
self.assertEqual(len(native), 25)
|
|
self.assertEqual(len(added), 25)
|
|
self.assertTrue(all(x['model'] == 'gpt-6-astra' for x in native))
|
|
self.assertTrue(all(x['model'] == 'ide/test' for x in added))
|
|
self.assertEqual(len({x['prompt_cache_key'] for x in native + added}), 50)
|
|
self.assertEqual(bridge.Sunucu.request_queue_size, 128)
|
|
|
|
def test_concurrent_native_chat_is_not_blocked_by_external_preparation(self):
|
|
entered, release = threading.Event(), threading.Event()
|
|
original_worker = self.kopru.cevap
|
|
|
|
def worker(h, body):
|
|
if h.path.endswith('/prepare-transfer'):
|
|
entered.set()
|
|
if not release.wait(5):
|
|
return duz(422, b'{"error":{"message":"test wait expired"}}')(h, body)
|
|
return original_worker(h, body)
|
|
|
|
self.kopru.cevap = worker
|
|
external = {"model": "ide/test", "prompt_cache_key": "external-thread", "input": []}
|
|
native = {"model": "gpt-6-astra", "prompt_cache_key": "native-thread", "input": []}
|
|
with ThreadPoolExecutor(max_workers=2) as pool:
|
|
pending = pool.submit(self.iste, 'POST', '/backend-api/codex/responses', json.dumps(external).encode())
|
|
try:
|
|
self.assertTrue(entered.wait(2))
|
|
result = pool.submit(self.iste, 'POST', '/backend-api/codex/responses', json.dumps(native).encode()).result(timeout=2)
|
|
self.assertEqual(result[0], 200)
|
|
self.assertEqual(json.loads(self.ozgun.gelen[-1]['govde']), native)
|
|
self.assertEqual(len(self.kopru.gelen), 1)
|
|
finally:
|
|
release.set()
|
|
self.assertEqual(pending.result(timeout=3)[0], 200)
|
|
self.assertEqual(len(self.ozgun.gelen), 1)
|
|
|
|
def test_native_voice_never_enters_added_model_or_preparation_path(self):
|
|
for path in ("/backend-api/codex/realtime/calls", "/wham/realtime/calls?intent=quicksilver&architecture=avas"):
|
|
for model in ("ide/GPT-6-Astra", "ide/agy-gemini-3.8-flash-high", "gpt-6-astra"):
|
|
body = json.dumps({"model": model}).encode()
|
|
self.assertEqual(self.iste("POST", path, body, {"Authorization": "Bearer voice-test"})[0], 200)
|
|
self.assertEqual(self.ozgun.gelen[-1]["govde"], body)
|
|
self.assertEqual(self.ozgun.gelen[-1]["baslik"]["Authorization"], "Bearer voice-test")
|
|
self.assertEqual(self.kopru.gelen, [])
|
|
|
|
def test_returning_compressed_history_repaired_but_inference_stays_native(self):
|
|
req = {"model": "gpt-6-astra", "input": [ozet_kopru.ozet_ogesi("KARAR-4827"),
|
|
{"type": "custom_tool_call", "id": "fc_call_173683", "call_id": "p", "name": "probe", "input": "x"}]}
|
|
body = zstd(json.dumps(req).encode())
|
|
self.iste("POST", "/backend-api/codex/responses", body, {"Content-Encoding": "zstd", "Authorization": "Bearer native-test"})
|
|
sent = self.ozgun.gelen[-1]
|
|
parsed = json.loads(sent["govde"])
|
|
self.assertIn("KARAR-4827", json.dumps(parsed))
|
|
self.assertEqual(parsed["input"][1]["id"], "ctc_call_173683")
|
|
self.assertNotIn("Content-Encoding", sent["baslik"])
|
|
self.assertEqual(sent["baslik"]["Authorization"], "Bearer native-test")
|
|
self.assertEqual(self.kopru.gelen, [])
|
|
|
|
def test_large_return_uses_preparation_only_not_aiturk_inference(self):
|
|
req = {"model": "gpt-6-astra", "input": [{"role": "assistant", "id": "msg_chatcmpl-foreign", "content": "x" * 100000},
|
|
{"role": "user", "content": "latest"}]}
|
|
def worker(h, body):
|
|
payload = json.loads(body)
|
|
self.assertEqual(h.path, "/aiturk/codex-baglama/prepare-transfer")
|
|
clean = dict(payload["request"], input=[{"role": "user", "content": "prepared summary"}, req["input"][-1]])
|
|
duz(200, json.dumps({"request": clean}).encode())(h, body)
|
|
self.kopru.cevap = worker
|
|
self.iste("POST", "/backend-api/codex/responses", json.dumps(req).encode())
|
|
self.assertEqual(len(self.kopru.gelen), 1)
|
|
self.assertEqual(json.loads(self.ozgun.gelen[-1]["govde"])["model"], "gpt-6-astra")
|
|
|
|
def test_preparation_failure_never_sends_truncated_request(self):
|
|
self.kopru.cevap = duz(422, b'{"error":{"message":"summary unavailable"}}')
|
|
_, body, _ = self.iste("POST", "/backend-api/codex/responses", b'{"model":"ide/test","input":[]}')
|
|
self.assertIn(b"response.failed", body)
|
|
self.assertEqual(self.ozgun.gelen, [])
|
|
self.assertEqual(len(self.kopru.gelen), 1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|