mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-07-15 12:58:04 +00:00
Repair document tool args and metrics cleanup
This commit is contained in:
+74
-4
@@ -1212,18 +1212,88 @@ FUNCTION_TOOL_SCHEMAS = [
|
|||||||
# Converter: native function call -> ToolBlock
|
# Converter: native function call -> ToolBlock
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _decode_loose_json_string(value: str) -> str:
|
||||||
|
"""Decode common JSON string escapes without requiring inner quotes to be escaped."""
|
||||||
|
out = []
|
||||||
|
i = 0
|
||||||
|
while i < len(value):
|
||||||
|
ch = value[i]
|
||||||
|
if ch != "\\" or i + 1 >= len(value):
|
||||||
|
out.append(ch)
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
nxt = value[i + 1]
|
||||||
|
if nxt == "n":
|
||||||
|
out.append("\n")
|
||||||
|
elif nxt == "r":
|
||||||
|
out.append("\r")
|
||||||
|
elif nxt == "t":
|
||||||
|
out.append("\t")
|
||||||
|
elif nxt == "b":
|
||||||
|
out.append("\b")
|
||||||
|
elif nxt == "f":
|
||||||
|
out.append("\f")
|
||||||
|
elif nxt in ('"', "\\", "/"):
|
||||||
|
out.append(nxt)
|
||||||
|
elif nxt == "u" and i + 5 < len(value):
|
||||||
|
try:
|
||||||
|
out.append(chr(int(value[i + 2:i + 6], 16)))
|
||||||
|
i += 4
|
||||||
|
except ValueError:
|
||||||
|
out.append("\\" + nxt)
|
||||||
|
else:
|
||||||
|
out.append("\\" + nxt)
|
||||||
|
i += 2
|
||||||
|
return "".join(out)
|
||||||
|
|
||||||
|
|
||||||
|
def _repair_document_function_args(tool_type: str, arguments: str) -> Optional[dict]:
|
||||||
|
"""Salvage obvious malformed document tool args from local model wrappers.
|
||||||
|
|
||||||
|
The doc LoRA sometimes emits the right native tool call but puts raw quotes
|
||||||
|
inside the document text, making the surrounding JSON invalid. Treat that as
|
||||||
|
a wrapper parse failure, not a semantic tool-choice failure.
|
||||||
|
"""
|
||||||
|
if tool_type != "update_document" or not isinstance(arguments, str):
|
||||||
|
return None
|
||||||
|
raw = arguments.strip()
|
||||||
|
if not raw.startswith("{") or not raw.endswith("}"):
|
||||||
|
return None
|
||||||
|
for key in ("content", "conten"):
|
||||||
|
marker = f'"{key}"'
|
||||||
|
key_pos = raw.find(marker)
|
||||||
|
if key_pos < 0:
|
||||||
|
continue
|
||||||
|
colon_pos = raw.find(":", key_pos + len(marker))
|
||||||
|
if colon_pos < 0:
|
||||||
|
continue
|
||||||
|
first_quote = raw.find('"', colon_pos + 1)
|
||||||
|
if first_quote < 0:
|
||||||
|
continue
|
||||||
|
close_brace = raw.rfind("}")
|
||||||
|
last_quote = raw.rfind('"', first_quote + 1, close_brace)
|
||||||
|
if last_quote <= first_quote:
|
||||||
|
continue
|
||||||
|
content = _decode_loose_json_string(raw[first_quote + 1:last_quote])
|
||||||
|
return {"content": content}
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def function_call_to_tool_block(name: str, arguments: str) -> Optional[ToolBlock]:
|
def function_call_to_tool_block(name: str, arguments: str) -> Optional[ToolBlock]:
|
||||||
"""Convert a native function call into a ToolBlock for the existing execution pipeline."""
|
"""Convert a native function call into a ToolBlock for the existing execution pipeline."""
|
||||||
|
tool_type = _TOOL_NAME_MAP.get(name, name)
|
||||||
try:
|
try:
|
||||||
if not arguments or (isinstance(arguments, str) and not arguments.strip()):
|
if not arguments or (isinstance(arguments, str) and not arguments.strip()):
|
||||||
args = {}
|
args = {}
|
||||||
else:
|
else:
|
||||||
args = json.loads(arguments) if isinstance(arguments, str) else arguments
|
args = json.loads(arguments) if isinstance(arguments, str) else arguments
|
||||||
except (json.JSONDecodeError, TypeError):
|
except (json.JSONDecodeError, TypeError):
|
||||||
logger.error(f"Failed to parse function call arguments for {name}: {arguments}")
|
args = _repair_document_function_args(tool_type, arguments)
|
||||||
return None
|
if args is not None:
|
||||||
|
logger.warning(f"Repaired malformed document function call arguments for {name}")
|
||||||
tool_type = _TOOL_NAME_MAP.get(name, name)
|
else:
|
||||||
|
logger.error(f"Failed to parse function call arguments for {name}: {arguments}")
|
||||||
|
return None
|
||||||
|
|
||||||
# Some models emit valid JSON that isn't an object (e.g. a bare array
|
# Some models emit valid JSON that isn't an object (e.g. a bare array
|
||||||
# ["ls -la"], string, or number) as function arguments. Most local tools keep
|
# ["ls -la"], string, or number) as function arguments. Most local tools keep
|
||||||
|
|||||||
@@ -1757,8 +1757,9 @@ export function createUserMsgFooter(msgElement) {
|
|||||||
* Display performance metrics for a message.
|
* Display performance metrics for a message.
|
||||||
*/
|
*/
|
||||||
export function displayMetrics(messageElement, metrics) {
|
export function displayMetrics(messageElement, metrics) {
|
||||||
const existingMetrics = messageElement.querySelector('.response-metrics');
|
messageElement
|
||||||
if (existingMetrics) existingMetrics.remove();
|
.querySelectorAll('.response-metrics, .metrics-divider, .ctx-divider, .ctx-ring')
|
||||||
|
.forEach((el) => el.remove());
|
||||||
|
|
||||||
const metricsContainer = document.createElement('span');
|
const metricsContainer = document.createElement('span');
|
||||||
metricsContainer.className = 'response-metrics';
|
metricsContainer.className = 'response-metrics';
|
||||||
@@ -1806,6 +1807,7 @@ export function displayMetrics(messageElement, metrics) {
|
|||||||
metricsContainer.style.cursor = 'pointer';
|
metricsContainer.style.cursor = 'pointer';
|
||||||
metricsContainer.title = 'Click for details';
|
metricsContainer.title = 'Click for details';
|
||||||
const metricsDivider = document.createElement('span');
|
const metricsDivider = document.createElement('span');
|
||||||
|
metricsDivider.className = 'metrics-divider';
|
||||||
metricsDivider.textContent = ' | ';
|
metricsDivider.textContent = ' | ';
|
||||||
metricsDivider.style.color = 'var(--color-muted-alt)';
|
metricsDivider.style.color = 'var(--color-muted-alt)';
|
||||||
metricsDivider.style.pointerEvents = 'none';
|
metricsDivider.style.pointerEvents = 'none';
|
||||||
|
|||||||
Reference in New Issue
Block a user