From cb6381afe4d10fa5e30597453ad3af66e7653d53 Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 19 May 2026 20:38:39 +0400 Subject: [PATCH] fix: add previous_response_id support for multi-turn tool calls (Crof fix) Codex Desktop uses previous_response_id to chain conversation turns. Without storing and resolving these, the proxy sent only the new function_call_output to upstream providers, missing the original user message and assistant tool call. This caused Crof.ai (and any provider using tool calls) to stop after the first response. - Add in-memory response store (50 entry LRU) keyed by response ID - resolve_previous_response() reconstructs full input chain on multi-turn - Fix orphan message output item when response has only tool calls - Applies to all backends: openai-compat, anthropic, command-code - v2.1.2 --- CHANGELOG.md | 9 ++++ README.md | 1 + codex-launcher_2.1.1_all.deb | Bin 22624 -> 0 bytes codex-launcher_2.1.2_all.deb | Bin 0 -> 23240 bytes src/codex-launcher-gui | 6 +++ src/translate-proxy.py | 86 +++++++++++++++++++++++++++++++---- 6 files changed, 93 insertions(+), 9 deletions(-) delete mode 100644 codex-launcher_2.1.1_all.deb create mode 100644 codex-launcher_2.1.2_all.deb diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a63faf..7c3c5d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## v2.1.2 (2026-05-19) + +- **Fixed Crof.ai and other providers stopping after first tool call (root cause)** +- Proxy now stores responses and resolves `previous_response_id` for multi-turn conversations +- Codex Desktop uses `previous_response_id` to chain turns — proxy reconstructs full conversation context +- Without this fix, the proxy sent only the new `function_call_output` to upstream without the original user message or assistant tool call, causing the upstream model to return incomplete responses +- Fixed orphan message output item when response is only tool calls (no text content) +- Response store capped at 50 entries (LRU eviction) + ## v2.1.1 (2026-05-19) - Added Command Code backend to translation proxy (proprietary `/alpha/generate` API) diff --git a/README.md b/README.md index 09bcd5f..785eb9d 100644 --- a/README.md +++ b/README.md @@ -390,6 +390,7 @@ README.md # This file | Proxy stops when terminal closes | SIGHUP to subprocess | Launcher uses `os.setsid` process groups | | Models not showing in picker | Wrong model catalog format | Must have both `slug` + `model` fields | | Codex hangs in "thinking" | Missing `response.completed` | Proxy emits full SSE event sequence | +| Stops after first tool call (Crof) | `previous_response_id` not resolved | V2.1.2 stores and chains responses for multi-turn | --- diff --git a/codex-launcher_2.1.1_all.deb b/codex-launcher_2.1.1_all.deb deleted file mode 100644 index 0e252306d23426da2c1736196830894997463f03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22624 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 b=pO+54!ohci)jJ!4**dE13&-(Y*i>@dK3<~ diff --git a/codex-launcher_2.1.2_all.deb b/codex-launcher_2.1.2_all.deb new file mode 100644 index 0000000000000000000000000000000000000000..7a98692809b251da3905ed8ee803523923cf0651 GIT binary patch literal 23240 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 UrSUD2i)9oT13&-(G=wK)3NEgjlmGw# literal 0 HcmV?d00001 diff --git a/src/codex-launcher-gui b/src/codex-launcher-gui index 8f60432..3f12e86 100755 --- a/src/codex-launcher-gui +++ b/src/codex-launcher-gui @@ -24,6 +24,12 @@ model_catalog_json = "" """ CHANGELOG = [ + ("2.1.2", "2026-05-19", [ + "Fixed Crof.ai and other providers stopping after first tool call", + "Proxy now stores and resolves previous_response_id for multi-turn conversations", + "Codex Desktop uses previous_response_id to chain turns — proxy reconstructs full context", + "Fixed orphan message output item when response is only tool calls (no text)", + ]), ("2.1.1", "2026-05-19", [ "Fixed proxy: map 'developer' role to 'system' for Chat Completions providers", "Fixed proxy: map 'developer' role to 'user' for Anthropic providers", diff --git a/src/translate-proxy.py b/src/translate-proxy.py index b167441..f2cefa4 100755 --- a/src/translate-proxy.py +++ b/src/translate-proxy.py @@ -88,6 +88,32 @@ CC_VERSION = CONFIG.get("cc_version", "") _pool = uuid.uuid4().hex[:8] +_response_store = {} +_MAX_STORED = 50 + +def store_response(resp_id, input_data, output_items): + if not resp_id: + return + _response_store[resp_id] = {"input": input_data, "output": output_items} + if len(_response_store) > _MAX_STORED: + oldest = list(_response_store.keys())[0] + del _response_store[oldest] + +def resolve_previous_response(body): + prev_id = body.get("previous_response_id") + input_data = body.get("input", "") + if not prev_id or prev_id not in _response_store: + return input_data + stored = _response_store[prev_id] + prev_input = stored["input"] + prev_output = stored["output"] + new_input = input_data if isinstance(input_data, list) else [] + if isinstance(prev_input, list): + combined = list(prev_input) + list(prev_output) + new_input + else: + combined = [{"type": "message", "role": "user", "content": [{"type": "input_text", "text": str(prev_input)}]}] + list(prev_output) + new_input + return combined + _HOP_BY_HOP_HEADERS = { "connection", "keep-alive", @@ -236,15 +262,12 @@ def oa_stream_to_sse(chat_stream, model, req_id): text_buf = "" tc_buf = {} fr = None + msg_opened = False yield emit("response.created", {"type": "response.created", "response": {"id": resp_id, "object": "response", "model": model, "status": "in_progress", "created": int(time.time()), "output": []}}) yield emit("response.in_progress", {"type": "response.in_progress", "response": {"id": resp_id}}) - yield emit("response.output_item.added", {"type": "response.output_item.added", - "item": {"type": "message", "id": msg_id, "role": "assistant", "status": "in_progress", "content": []}}) - yield emit("response.content_part.added", {"type": "response.content_part.added", - "part": {"type": "output_text", "text": "", "annotations": []}, "item_id": msg_id}) for line in chat_stream: line = line.decode("utf-8", errors="replace").strip() @@ -264,6 +287,13 @@ def oa_stream_to_sse(chat_stream, model, req_id): content = delta.get("content") if content: + if not msg_opened: + msg_id = uid("msg") + yield emit("response.output_item.added", {"type": "response.output_item.added", + "item": {"type": "message", "id": msg_id, "role": "assistant", "status": "in_progress", "content": []}}) + yield emit("response.content_part.added", {"type": "response.content_part.added", + "part": {"type": "output_text", "text": "", "annotations": []}, "item_id": msg_id}) + msg_opened = True text_buf += content yield emit("response.output_text.delta", {"type": "response.output_text.delta", "delta": content, "item_id": msg_id, "content_index": 0}) @@ -288,7 +318,7 @@ def oa_stream_to_sse(chat_stream, model, req_id): if rc: yield emit("response.reasoning.delta", {"type": "response.reasoning.delta", "delta": rc}) - if text_buf: + if msg_opened: yield emit("response.output_text.done", {"type": "response.output_text.done", "text": text_buf, "item_id": msg_id, "content_index": 0}) yield emit("response.content_part.done", {"type": "response.content_part.done", @@ -308,7 +338,7 @@ def oa_stream_to_sse(chat_stream, model, req_id): fm = {"stop": "completed", "length": "incomplete", "tool_calls": "completed", "content_filter": "incomplete"} status = fm.get(fr, "incomplete") final_out = [] - if text_buf: + if msg_opened: final_out.append({"type": "message", "id": msg_id, "role": "assistant", "status": "completed", "content": [{"type": "output_text", "text": text_buf, "annotations": []}]}) for idx in sorted(tc_buf): @@ -646,6 +676,12 @@ class Handler(http.server.BaseHTTPRequestHandler): except Exception as e: return self.send_json(400, {"error": {"message": f"Bad request: {e}"}}) + input_data = resolve_previous_response(body) + body["input"] = input_data + prev_id = body.get("previous_response_id") + input_types = [i.get("type") for i in input_data] if isinstance(input_data, list) else str(type(input_data)) + print(f"[REQUEST] prev_id={prev_id} resolved_input_types={input_types}", file=sys.stderr) + model = body.get("model", MODELS[0]["id"] if MODELS else "unknown") stream = body.get("stream", False) @@ -686,7 +722,8 @@ class Handler(http.server.BaseHTTPRequestHandler): ) self._forward(req, stream, model, lambda r: oa_resp_to_responses(json.loads(r.read()), model), - lambda s: oa_stream_to_sse(s, model, body.get("request_id") or body.get("id"))) + lambda s: oa_stream_to_sse(s, model, body.get("request_id") or body.get("id")), + input_data=body.get("input", "")) def _handle_anthropic(self, body, model, stream): input_data = body.get("input", "") @@ -721,7 +758,8 @@ class Handler(http.server.BaseHTTPRequestHandler): ) self._forward(req, stream, model, lambda r: an_resp_to_responses(json.loads(r.read()), model), - lambda s: an_stream_to_sse(s, model, body.get("request_id") or body.get("id"))) + lambda s: an_stream_to_sse(s, model, body.get("request_id") or body.get("id")), + input_data=body.get("input", "")) def _handle_command_code(self, body, model, stream): input_data = body.get("input", "") @@ -800,9 +838,21 @@ class Handler(http.server.BaseHTTPRequestHandler): self.send_header("Cache-Control", "no-cache") self.send_header("Connection", "keep-alive") self.end_headers() + last_resp_id = None + last_output = None for event in cc_stream_to_sse(upstream, model, body.get("request_id") or body.get("id")): self.wfile.write(event.encode("utf-8")) self.wfile.flush() + for line in event.strip().split("\n"): + if line.startswith("data: "): + try: + d = json.loads(line[6:]) + if d.get("type") == "response.completed": + last_resp_id = d.get("response", {}).get("id") + last_output = d.get("response", {}).get("output", []) + except: pass + if last_resp_id: + store_response(last_resp_id, body.get("input", ""), last_output) else: try: upstream = urllib.request.urlopen(req) @@ -816,8 +866,11 @@ class Handler(http.server.BaseHTTPRequestHandler): lines = raw.strip().split("\n") result = cc_resp_to_responses(lines, model) self.send_json(200, result) + rid = result.get("id") + if rid: + store_response(rid, body.get("input", ""), result.get("output", [])) - def _forward(self, req, stream, model, nonstream_fn, stream_fn): + def _forward(self, req, stream, model, nonstream_fn, stream_fn, input_data=None): try: upstream = urllib.request.urlopen(req) except urllib.error.HTTPError as e: @@ -832,12 +885,27 @@ class Handler(http.server.BaseHTTPRequestHandler): self.send_header("Cache-Control", "no-cache") self.send_header("Connection", "keep-alive") self.end_headers() + last_resp_id = None + last_output = None for event in stream_fn(upstream): self.wfile.write(event.encode("utf-8")) self.wfile.flush() + for line in event.strip().split("\n"): + if line.startswith("data: "): + try: + d = json.loads(line[6:]) + if d.get("type") == "response.completed": + last_resp_id = d.get("response", {}).get("id") + last_output = d.get("response", {}).get("output", []) + except: pass + if last_resp_id and input_data is not None: + store_response(last_resp_id, input_data, last_output) else: result = nonstream_fn(upstream) self.send_json(200, result) + rid = result.get("id") + if rid and input_data is not None: + store_response(rid, input_data, result.get("output", [])) def send_json(self, status, data): body = json.dumps(data).encode()