v2.2.1: fix compaction orphaning tool outputs causing Crof incomplete
- Fix compaction cutting between function_call and function_call_output pairs - Orphaned tool results confused Crof models causing finish_reason=length - reasoning_effort=none now always sends enable_thinking=false too - Added Crof upstream debug logging
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v2.2.1 (2026-05-20)
|
||||||
|
|
||||||
|
- **Fixed compaction orphaning function_call_output items** — root cause of Crof `incomplete` responses
|
||||||
|
- Compaction cut between function_call and its function_call_output, creating dangling tool results
|
||||||
|
- Crof model received orphaned `tool` messages with empty `tool_call_id`, causing confusion and token exhaustion
|
||||||
|
- Compaction now expands tail boundary to include matching function_call/function_call_output pairs
|
||||||
|
- **Fixed reasoning control**: `reasoning_effort=none` now always sends both `enable_thinking=false` AND `reasoning_effort=none`
|
||||||
|
- Crof API testing confirmed `reasoning_effort=none` is what actually suppresses reasoning, not `enable_thinking=false`
|
||||||
|
- Added upstream debug logging to `~/.cache/codex-proxy/crof-upstream.jsonl`
|
||||||
|
|
||||||
## v2.2.0 (2026-05-20)
|
## v2.2.0 (2026-05-20)
|
||||||
|
|
||||||
- **Added per-provider Reasoning controls in endpoint editor**
|
- **Added per-provider Reasoning controls in endpoint editor**
|
||||||
|
|||||||
Binary file not shown.
BIN
codex-launcher_2.2.1_all.deb
Normal file
BIN
codex-launcher_2.2.1_all.deb
Normal file
Binary file not shown.
@@ -24,6 +24,11 @@ model_catalog_json = ""
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
CHANGELOG = [
|
CHANGELOG = [
|
||||||
|
("2.2.1", "2026-05-20", [
|
||||||
|
"Fixed compaction orphaning function_call_output items — root cause of Crof incomplete responses",
|
||||||
|
"Compaction now respects function_call/function_call_output pairs — no more dangling tool results",
|
||||||
|
"Fixed reasoning control: reasoning_effort=none now always sends enable_thinking=false too",
|
||||||
|
]),
|
||||||
("2.2.0", "2026-05-20", [
|
("2.2.0", "2026-05-20", [
|
||||||
"Added per-provider Reasoning On/Off toggle in endpoint editor",
|
"Added per-provider Reasoning On/Off toggle in endpoint editor",
|
||||||
"Added Reasoning Effort level per provider: None, Minimal, Low, Medium, High, Max",
|
"Added Reasoning Effort level per provider: None, Minimal, Low, Medium, High, Max",
|
||||||
@@ -543,7 +548,7 @@ class LauncherWin(Gtk.Window):
|
|||||||
# header row
|
# header row
|
||||||
hdr = Gtk.Box(spacing=8)
|
hdr = Gtk.Box(spacing=8)
|
||||||
vbox.pack_start(hdr, False, False, 0)
|
vbox.pack_start(hdr, False, False, 0)
|
||||||
lbl = Gtk.Label(label="<b>Codex Launcher v2.2.0</b>")
|
lbl = Gtk.Label(label="<b>Codex Launcher v2.2.1</b>")
|
||||||
lbl.set_use_markup(True)
|
lbl.set_use_markup(True)
|
||||||
hdr.pack_start(lbl, False, False, 0)
|
hdr.pack_start(lbl, False, False, 0)
|
||||||
changelog_btn = Gtk.Button(label="Changelog")
|
changelog_btn = Gtk.Button(label="Changelog")
|
||||||
|
|||||||
@@ -238,8 +238,16 @@ def _compact_input(input_data):
|
|||||||
break
|
break
|
||||||
|
|
||||||
head = input_data[:head_end]
|
head = input_data[:head_end]
|
||||||
tail = input_data[-_COMPACT_KEEP_RECENT:]
|
tail_start = len(input_data) - _COMPACT_KEEP_RECENT
|
||||||
body = input_data[head_end:-_COMPACT_KEEP_RECENT]
|
while tail_start > head_end:
|
||||||
|
if input_data[tail_start].get("type") == "function_call_output":
|
||||||
|
tail_start -= 1
|
||||||
|
elif input_data[tail_start].get("type") == "message" and input_data[tail_start].get("role") == "assistant":
|
||||||
|
tail_start -= 1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
tail = input_data[tail_start:]
|
||||||
|
body = input_data[head_end:tail_start]
|
||||||
|
|
||||||
if not body:
|
if not body:
|
||||||
return head + tail
|
return head + tail
|
||||||
@@ -891,9 +899,11 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
|||||||
if body.get("tool_choice"):
|
if body.get("tool_choice"):
|
||||||
chat_body["tool_choice"] = body["tool_choice"]
|
chat_body["tool_choice"] = body["tool_choice"]
|
||||||
chat_body["stream"] = stream
|
chat_body["stream"] = stream
|
||||||
if not REASONING_ENABLED:
|
if not REASONING_ENABLED or REASONING_EFFORT == "none":
|
||||||
chat_body["enable_thinking"] = False
|
chat_body["enable_thinking"] = False
|
||||||
chat_body["reasoning_effort"] = REASONING_EFFORT if REASONING_ENABLED else "none"
|
chat_body["reasoning_effort"] = "none"
|
||||||
|
else:
|
||||||
|
chat_body["reasoning_effort"] = REASONING_EFFORT
|
||||||
|
|
||||||
target = upstream_target(TARGET_URL, "/chat/completions")
|
target = upstream_target(TARGET_URL, "/chat/completions")
|
||||||
fwd = forwarded_headers(self.headers, {
|
fwd = forwarded_headers(self.headers, {
|
||||||
@@ -901,6 +911,16 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
|||||||
"Authorization": f"Bearer {API_KEY}",
|
"Authorization": f"Bearer {API_KEY}",
|
||||||
}, browser_ua=True)
|
}, browser_ua=True)
|
||||||
print(f"[translate-proxy] POST {target} model={model} stream={stream} ua={fwd.get('User-Agent','')[:50]}", file=sys.stderr)
|
print(f"[translate-proxy] POST {target} model={model} stream={stream} ua={fwd.get('User-Agent','')[:50]}", file=sys.stderr)
|
||||||
|
_crof_debug_path = os.path.join(_LOG_DIR, "crof-upstream.jsonl")
|
||||||
|
with open(_crof_debug_path, "a") as _cdf:
|
||||||
|
_cdf.write(json.dumps({
|
||||||
|
"ts": _ts, "model": model, "max_tokens": chat_body.get("max_tokens"),
|
||||||
|
"reasoning_enabled": REASONING_ENABLED, "reasoning_effort": chat_body.get("reasoning_effort"),
|
||||||
|
"enable_thinking": chat_body.get("enable_thinking", "NOT_SENT"),
|
||||||
|
"n_messages": len(chat_body.get("messages", [])),
|
||||||
|
"has_tools": bool(chat_body.get("tools")),
|
||||||
|
"messages_summary": [{"role": m.get("role"), "tc": len(m.get("tool_calls", [])), "content_len": len(str(m.get("content", "")))[:6], "tool_call_id": m.get("tool_call_id")} for m in chat_body.get("messages", [])],
|
||||||
|
}) + "\n")
|
||||||
req = urllib.request.Request(
|
req = urllib.request.Request(
|
||||||
target,
|
target,
|
||||||
data=json.dumps(chat_body).encode(),
|
data=json.dumps(chat_body).encode(),
|
||||||
|
|||||||
Reference in New Issue
Block a user