mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-07-16 13:08:02 +00:00
Checkpoint Odysseus local update
This commit is contained in:
@@ -186,6 +186,12 @@ _FUNCTION_MODEL_NAME_RE = re.compile(
|
||||
)
|
||||
_FUNCTION_MODEL_PARAMS_OPEN_RE = re.compile(r"<parameters>\s*", re.IGNORECASE)
|
||||
_FUNCTION_MODEL_PARAMS_CLOSE_RE = re.compile(r"</parameters>", re.IGNORECASE)
|
||||
_QWEN_ROLE_MARKER_RE = re.compile(r"</?\|(?:assistant|assistan|user|system|tool)\|>?|</\|end\|>?", re.IGNORECASE)
|
||||
_QWEN_BARE_MARKER_RE = re.compile(
|
||||
r"(?:^|[\t\r\n ])(?:\|?end\|?|/?\|end\|)(?=[\t\r\n ]|$)|"
|
||||
r"(?:^|[\t\r\n ])assistan(?:t)?(?=[\t\r\n ]|$)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
# Pattern 5: DeepSeek DSML markup leaking into content. When deepseek
|
||||
@@ -581,6 +587,205 @@ def _parse_raw_web_json_lookup(text: str) -> Optional[tuple[ToolBlock, tuple[int
|
||||
return block, (start, start + end)
|
||||
return None
|
||||
|
||||
|
||||
def _looks_like_openai_tool_call_blob(value) -> bool:
|
||||
"""Return True for raw OpenAI-style tool-call JSON leaked as text."""
|
||||
if isinstance(value, list):
|
||||
return bool(value) and all(_looks_like_openai_tool_call_blob(item) for item in value)
|
||||
if not isinstance(value, dict):
|
||||
return False
|
||||
fn = value.get("function")
|
||||
if isinstance(fn, dict) and isinstance(fn.get("name"), str):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _raw_openai_tool_call_to_block(value) -> Optional[ToolBlock]:
|
||||
if isinstance(value, list):
|
||||
for item in value:
|
||||
block = _raw_openai_tool_call_to_block(item)
|
||||
if block:
|
||||
return block
|
||||
return None
|
||||
if not isinstance(value, dict):
|
||||
return None
|
||||
fn = value.get("function")
|
||||
if not isinstance(fn, dict):
|
||||
return None
|
||||
name = str(fn.get("name") or "").strip()
|
||||
if not name:
|
||||
return None
|
||||
tool_type = _TOOL_NAME_MAP.get(name, name)
|
||||
raw_args = fn.get("arguments") or {}
|
||||
try:
|
||||
args = json.loads(raw_args) if isinstance(raw_args, str) else raw_args
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
args = {}
|
||||
if not isinstance(args, dict):
|
||||
args = {}
|
||||
# Common local-model typo seen in raw OpenAI JSON leaks.
|
||||
if "text" not in args and "tex" in args:
|
||||
args["text"] = args.get("tex")
|
||||
|
||||
if tool_type.startswith("mcp__"):
|
||||
return ToolBlock(tool_type, json.dumps(args) if args else "{}")
|
||||
if name in BUILTIN_EMAIL_TOOLS:
|
||||
return ToolBlock(f"mcp__email__{name}", json.dumps(args) if args else "{}")
|
||||
if tool_type not in TOOL_TAGS:
|
||||
return None
|
||||
|
||||
if tool_type == "bash":
|
||||
content = args.get("command", "")
|
||||
elif tool_type == "python":
|
||||
content = args.get("code", "")
|
||||
elif tool_type == "web_search":
|
||||
content = args.get("query", "")
|
||||
queries = args.get("queries")
|
||||
if not content and isinstance(queries, list) and queries:
|
||||
content = str(queries[0])
|
||||
elif not content and queries:
|
||||
content = str(queries)
|
||||
tf = args.get("time_filter")
|
||||
if content and isinstance(tf, str) and tf in ("day", "week", "month", "year"):
|
||||
content = json.dumps({"query": content, "time_filter": tf})
|
||||
elif tool_type == "web_fetch":
|
||||
content = args.get("url") or args.get("domain") or ""
|
||||
elif tool_type == "read_file":
|
||||
content = json.dumps(args) if (args.get("offset") or args.get("limit")) else args.get("path", "")
|
||||
elif tool_type in ("grep", "glob", "ls", "edit_file"):
|
||||
content = json.dumps(args) if args else "{}"
|
||||
elif tool_type == "write_file":
|
||||
content = args.get("path", "") + "\n" + args.get("content", "")
|
||||
elif tool_type == "create_document":
|
||||
parts = [args.get("title", "Untitled")]
|
||||
if args.get("language"):
|
||||
parts.append(args["language"])
|
||||
parts.append(args.get("content", ""))
|
||||
content = "\n".join(parts)
|
||||
elif tool_type == "update_document":
|
||||
content = args.get("content", "")
|
||||
elif tool_type in ("edit_document", "suggest_document"):
|
||||
marker = "SUGGEST" if tool_type == "suggest_document" else "REPLACE"
|
||||
blocks = []
|
||||
for edit in args.get("suggestions" if tool_type == "suggest_document" else "edits", []) or []:
|
||||
if not isinstance(edit, dict):
|
||||
continue
|
||||
block = f'<<<FIND>>>\n{edit.get("find", "")}\n<<<{marker}>>>\n{edit.get("replace", "")}'
|
||||
if tool_type == "suggest_document":
|
||||
block += f'\n<<<REASON>>>\n{edit.get("reason", "")}'
|
||||
blocks.append(block + "\n<<<END>>>")
|
||||
content = "\n".join(blocks)
|
||||
elif tool_type == "search_chats":
|
||||
content = args.get("query", "")
|
||||
elif tool_type == "chat_with_model":
|
||||
content = args.get("model", "") + "\n" + args.get("message", "")
|
||||
elif tool_type == "create_session":
|
||||
content = args.get("name", "Untitled") + "\n" + args.get("model", "")
|
||||
elif tool_type == "list_sessions":
|
||||
content = args.get("filter", "")
|
||||
elif tool_type == "send_to_session":
|
||||
content = args.get("session_id", "") + "\n" + args.get("message", "")
|
||||
elif tool_type == "pipeline":
|
||||
content = json.dumps({"steps": args.get("steps", [])})
|
||||
elif tool_type == "manage_session":
|
||||
action = args.get("action", "")
|
||||
if action == "list":
|
||||
keyword = args.get("keyword", "") or args.get("value", "")
|
||||
content = "list" + (("\n" + keyword) if keyword and keyword.lower() != "current" else "")
|
||||
else:
|
||||
content = action + "\n" + args.get("session_id", "current")
|
||||
if args.get("value"):
|
||||
content += "\n" + args["value"]
|
||||
elif tool_type == "manage_memory":
|
||||
action = args.get("action", "")
|
||||
if action == "add":
|
||||
content = "add\n" + str(args.get("text", ""))
|
||||
if args.get("category"):
|
||||
content += "\n" + str(args["category"])
|
||||
elif action == "edit":
|
||||
content = "edit\n" + str(args.get("memory_id", "")) + "\n" + str(args.get("text", ""))
|
||||
elif action == "delete":
|
||||
content = "delete\n" + str(args.get("memory_id", ""))
|
||||
elif action == "search":
|
||||
content = "search\n" + str(args.get("text", ""))
|
||||
elif action == "list":
|
||||
content = "list" + (("\n" + str(args["category"])) if args.get("category") else "")
|
||||
else:
|
||||
content = action
|
||||
elif tool_type == "ui_control":
|
||||
action = args.get("action", "")
|
||||
name_arg = args.get("name", "")
|
||||
value = args.get("value", "")
|
||||
if action == "open_panel":
|
||||
content = f"open_panel {name_arg or value}"
|
||||
elif action == "toggle":
|
||||
content = f"toggle {name_arg} {value}"
|
||||
else:
|
||||
content = action
|
||||
elif tool_type in ("manage_tasks", "manage_skills", "api_call", "manage_endpoints",
|
||||
"manage_mcp", "manage_webhooks", "manage_tokens",
|
||||
"manage_documents", "manage_settings", "manage_notes",
|
||||
"manage_research", "manage_bg_jobs"):
|
||||
content = json.dumps(args)
|
||||
elif tool_type in ("get_workspace", "list_models"):
|
||||
content = args.get("filter", "") if tool_type == "list_models" else ""
|
||||
else:
|
||||
content = json.dumps(args) if args else ""
|
||||
return ToolBlock(tool_type, str(content or ""))
|
||||
|
||||
|
||||
def _parse_raw_openai_tool_call_json(text: str) -> Optional[ToolBlock]:
|
||||
if not isinstance(text, str) or '"function"' not in text:
|
||||
return None
|
||||
decoder = json.JSONDecoder()
|
||||
for match in re.finditer(r"[\[{]", text):
|
||||
try:
|
||||
parsed, _end = decoder.raw_decode(text[match.start():])
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
block = _raw_openai_tool_call_to_block(parsed)
|
||||
if block:
|
||||
return block
|
||||
return None
|
||||
|
||||
|
||||
def _strip_raw_openai_tool_call_json(text: str) -> str:
|
||||
"""Strip raw JSON tool calls such as {"function": {...}, "type": "function"}.
|
||||
|
||||
Some local models emit native tool-call JSON into assistant text. The agent
|
||||
can still parse/execute it through the native path, but the raw payload must
|
||||
not render or persist as prose.
|
||||
"""
|
||||
if not isinstance(text, str) or '"function"' not in text:
|
||||
return text
|
||||
decoder = json.JSONDecoder()
|
||||
pieces = []
|
||||
pos = 0
|
||||
changed = False
|
||||
for match in re.finditer(r"[\[{]", text):
|
||||
start = match.start()
|
||||
if start < pos:
|
||||
continue
|
||||
try:
|
||||
parsed, rel_end = decoder.raw_decode(text[start:])
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
end = start + rel_end
|
||||
if not _looks_like_openai_tool_call_blob(parsed):
|
||||
continue
|
||||
pieces.append(text[pos:start])
|
||||
pos = end
|
||||
changed = True
|
||||
# Common broken local-model suffix: a standalone ] before a role marker.
|
||||
while pos < len(text) and text[pos] in " \t\r\n":
|
||||
pos += 1
|
||||
if pos < len(text) and text[pos] == "]":
|
||||
pos += 1
|
||||
if not changed:
|
||||
return text
|
||||
pieces.append(text[pos:])
|
||||
return "".join(pieces)
|
||||
|
||||
def _parse_tool_call_block(raw: str) -> Optional[ToolBlock]:
|
||||
"""Parse a [TOOL_CALL] block into a ToolBlock.
|
||||
|
||||
@@ -1178,6 +1383,13 @@ def parse_tool_blocks(text: str, skip_fenced: bool = False) -> List[ToolBlock]:
|
||||
if block:
|
||||
blocks.append(block)
|
||||
|
||||
# Pattern 4d: raw OpenAI-style tool-call JSON leaked as assistant text.
|
||||
# Example: {"function":{"arguments":"{\"action\":\"add\"}","name":"manage_memory"},"type":"function"}
|
||||
if not blocks:
|
||||
block = _parse_raw_openai_tool_call_json(text)
|
||||
if block:
|
||||
blocks.append(block)
|
||||
|
||||
# Pattern 6: local text-model web_search call leaked as prose + bare JSON.
|
||||
if not blocks and not skip_fenced:
|
||||
raw_web_json = _parse_raw_web_json_lookup(text)
|
||||
@@ -1225,6 +1437,9 @@ def strip_tool_blocks(text: str, skip_fenced: bool = False) -> str:
|
||||
cleaned = _strip_delimited(cleaned, _TOOL_CODE_OPEN_RE, _TOOL_CODE_CLOSE_RE)
|
||||
cleaned = _GEMMA_TOOL_CALL_RE.sub('', cleaned)
|
||||
cleaned = _strip_delimited(cleaned, _FUNCTION_MODEL_OPEN_RE, _FUNCTION_MODEL_CLOSE_RE)
|
||||
cleaned = _strip_raw_openai_tool_call_json(cleaned)
|
||||
cleaned = _QWEN_ROLE_MARKER_RE.sub('', cleaned)
|
||||
cleaned = _QWEN_BARE_MARKER_RE.sub(' ', cleaned)
|
||||
if not skip_fenced:
|
||||
raw_web_json = _parse_raw_web_json_lookup(cleaned)
|
||||
if raw_web_json:
|
||||
|
||||
Reference in New Issue
Block a user