Skip to content

Commit 5a843b9

Browse files
authored
Merge pull request #54 from sp2935/fix/openai-compatible-tool-args
fix(tool-args): coerce JSON-encoded strings on str_replace_editor numeric args
2 parents 3ed41ee + ef5c397 commit 5a843b9

1 file changed

Lines changed: 27 additions & 3 deletions

File tree

codewiki/src/be/agent_tools/str_replace_editor.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import sys
1111
from collections import defaultdict
1212
from pathlib import Path
13-
from typing import List, Optional, Tuple, Literal
13+
from typing import Annotated, List, Literal, Optional, Tuple
1414
import io
1515

1616
import logging
@@ -19,12 +19,36 @@
1919

2020
logger = logging.getLogger(__name__)
2121

22+
from pydantic import BeforeValidator
2223
from pydantic_ai import RunContext, Tool
2324

2425
from .deps import CodeWikiDeps
2526
from ..utils import validate_mermaid_diagrams
2627

2728

29+
def _coerce_json_string(value):
30+
"""Coerce a JSON encoded string to its parsed Python value before pydantic
31+
strict validation runs. No op on already typed values.
32+
33+
Some local models routed through OpenAI compatible endpoints (LiteLLM,
34+
vLLM, Ollama, etc.) emit list and int tool args as JSON encoded strings
35+
(e.g. `"[1, 50]"` instead of `[1, 50]`) which strict pydantic validation
36+
rejects. This validator parses them so the tool accepts both shapes.
37+
Anthropic native API users are unaffected because they already emit
38+
structured values.
39+
"""
40+
if isinstance(value, str):
41+
try:
42+
return json.loads(value)
43+
except (json.JSONDecodeError, ValueError):
44+
pass
45+
return value
46+
47+
48+
ViewRange = Annotated[Optional[List[int]], BeforeValidator(_coerce_json_string)]
49+
InsertLine = Annotated[Optional[int], BeforeValidator(_coerce_json_string)]
50+
51+
2852
# There are some super strange "ascii can't decode x" errors,
2953
# that can be solved with setting the default encoding for stdout
3054
# (note that python3.6 doesn't have the reconfigure method)
@@ -713,10 +737,10 @@ async def str_replace_editor(
713737
path: Optional[str] = None,
714738
file: Optional[str] = None,
715739
file_text: Optional[str] = None,
716-
view_range: Optional[List[int]] = None,
740+
view_range: ViewRange = None,
717741
old_str: Optional[str] = None,
718742
new_str: Optional[str] = None,
719-
insert_line: Optional[int] = None,
743+
insert_line: InsertLine = None,
720744
) -> str:
721745
"""
722746
Custom editing tool for viewing, creating and editing files

0 commit comments

Comments
 (0)