nimcode 0.8.3__tar.gz → 0.8.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nimcode-0.8.3 → nimcode-0.8.6}/PKG-INFO +9 -7
- {nimcode-0.8.3 → nimcode-0.8.6}/README.md +8 -6
- nimcode-0.8.6/src/nimcode/__version__.py +1 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/agent.py +2 -2
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/agents/qa_agent.py +1 -1
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/cli.py +1 -1
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/config.py +1 -1
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/lenient_parser.py +22 -12
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/nim_client.py +1 -1
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode.egg-info/PKG-INFO +9 -7
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_config.py +2 -2
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_lenient_parser.py +19 -4
- nimcode-0.8.3/src/nimcode/__version__.py +0 -1
- {nimcode-0.8.3 → nimcode-0.8.6}/setup.cfg +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/setup.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/__init__.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/agents/__init__.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/mcp_client.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/memory.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/model_registry.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/permissions.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/plugin_manager.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/rag.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/repl.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/repo_map.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/task_manager.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/tools.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/updater.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode/watcher.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode.egg-info/SOURCES.txt +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode.egg-info/dependency_links.txt +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode.egg-info/entry_points.txt +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode.egg-info/requires.txt +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/src/nimcode.egg-info/top_level.txt +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_advanced_blockers.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_agent.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_bash_blockers.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_cli.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_lazy_blockers.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_mcp_client.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_memory.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_model_registry.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_nim_client.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_permissions.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_plan_injection.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_plugin_manager.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_qa_agent.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_repl.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_repl_extra.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_repl_fixes.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_repl_trust.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_repo_map.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_task_manager.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_tools.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_updater.py +0 -0
- {nimcode-0.8.3 → nimcode-0.8.6}/tests/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nimcode
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.6
|
|
4
4
|
Summary: A standalone, robust coding agent for NVIDIA NIM models.
|
|
5
5
|
Home-page: https://github.com/Batunash/nimcode
|
|
6
6
|
Author: Autonomous Agent
|
|
@@ -193,12 +193,14 @@ NimCode's behavior can be fully customized through `~/.nimcode/settings.json` (g
|
|
|
193
193
|
|
|
194
194
|
## 📋 Changelog
|
|
195
195
|
|
|
196
|
-
### v0.8.
|
|
197
|
-
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
-
|
|
201
|
-
- 🧪 **
|
|
196
|
+
### v0.8.6 (Latest)
|
|
197
|
+
- 🚀 **Default Model Bump**: Made `deepseek-ai/deepseek-v4-pro` the default model across the entire agent for superior reasoning and tool performance.
|
|
198
|
+
|
|
199
|
+
### v0.8.4
|
|
200
|
+
- 🧠 **Native DeepSeek XML Support**: Re-engineered the internal parser (`lenient_parser.py`) into a hybrid parser that seamlessly handles both strict JSON (`<tool_call>`) and fine-tuned XML formats (`<tool_call name=\"...\">`). DeepSeek V4 Pro, Mistral Large 2, and others now work flawlessly out of the box!
|
|
201
|
+
- 🧪 **224 tests passing**
|
|
202
|
+
|
|
203
|
+
### v0.8.0
|
|
202
204
|
|
|
203
205
|
### v0.7.0
|
|
204
206
|
- 🛡️ **Strict Markdown Validator**: `/plan` mode now physically blocks tasks that are under 150 characters or missing required Markdown headers (`Target Files`, `Implementation Details`, `Checklist`).
|
|
@@ -172,12 +172,14 @@ NimCode's behavior can be fully customized through `~/.nimcode/settings.json` (g
|
|
|
172
172
|
|
|
173
173
|
## 📋 Changelog
|
|
174
174
|
|
|
175
|
-
### v0.8.
|
|
176
|
-
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
-
|
|
180
|
-
- 🧪 **
|
|
175
|
+
### v0.8.6 (Latest)
|
|
176
|
+
- 🚀 **Default Model Bump**: Made `deepseek-ai/deepseek-v4-pro` the default model across the entire agent for superior reasoning and tool performance.
|
|
177
|
+
|
|
178
|
+
### v0.8.4
|
|
179
|
+
- 🧠 **Native DeepSeek XML Support**: Re-engineered the internal parser (`lenient_parser.py`) into a hybrid parser that seamlessly handles both strict JSON (`<tool_call>`) and fine-tuned XML formats (`<tool_call name=\"...\">`). DeepSeek V4 Pro, Mistral Large 2, and others now work flawlessly out of the box!
|
|
180
|
+
- 🧪 **224 tests passing**
|
|
181
|
+
|
|
182
|
+
### v0.8.0
|
|
181
183
|
|
|
182
184
|
### v0.7.0
|
|
183
185
|
- 🛡️ **Strict Markdown Validator**: `/plan` mode now physically blocks tasks that are under 150 characters or missing required Markdown headers (`Target Files`, `Implementation Details`, `Checklist`).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.8.6"
|
|
@@ -188,7 +188,7 @@ class Agent:
|
|
|
188
188
|
def __init__(self, api_key: str, model: str = None, max_turns: int = None, permission_mode: PermissionMode = PermissionMode.DEFAULT, max_tokens: int = None):
|
|
189
189
|
# Load global settings
|
|
190
190
|
self.settings = load_settings()
|
|
191
|
-
self.model = model or self.settings.get("model", "
|
|
191
|
+
self.model = model or self.settings.get("model", "deepseek-ai/deepseek-v4-pro")
|
|
192
192
|
api_base_url = self.settings.get("api_base_url", "https://integrate.api.nvidia.com/v1")
|
|
193
193
|
self.client = NimClient(
|
|
194
194
|
api_key=api_key,
|
|
@@ -373,7 +373,7 @@ class Agent:
|
|
|
373
373
|
else:
|
|
374
374
|
display_text = parts[0]
|
|
375
375
|
|
|
376
|
-
tool_tag_idx = display_text.find("<tool_call
|
|
376
|
+
tool_tag_idx = display_text.find("<tool_call")
|
|
377
377
|
if tool_tag_idx != -1:
|
|
378
378
|
display_text = display_text[:tool_tag_idx] + "\n\n*[dim]⚙️ Preparing tool execution...[/dim]*"
|
|
379
379
|
|
|
@@ -19,7 +19,7 @@ class QAAgent:
|
|
|
19
19
|
import json
|
|
20
20
|
settings_path = os.path.join(self.cwd, ".nimcode", "settings.json")
|
|
21
21
|
api_key = os.environ.get("NVIDIA_API_KEY")
|
|
22
|
-
model = "
|
|
22
|
+
model = "deepseek-ai/deepseek-v4-pro"
|
|
23
23
|
if os.path.exists(settings_path):
|
|
24
24
|
try:
|
|
25
25
|
with open(settings_path, "r") as f:
|
|
@@ -101,7 +101,7 @@ def main():
|
|
|
101
101
|
# Main CLI arguments
|
|
102
102
|
parser.add_argument("prompt", nargs="?", default=None, help="The task you want NimCode to accomplish. If omitted, starts interactive REPL.")
|
|
103
103
|
parser.add_argument("--api-key", "-k", default=None, help="NVIDIA NIM API Key. Can also be set via NIM_API_KEY environment variable.")
|
|
104
|
-
parser.add_argument("--model", "-m", default="
|
|
104
|
+
parser.add_argument("--model", "-m", default="deepseek-ai/deepseek-v4-pro", help="Model ID to use from NIM.")
|
|
105
105
|
parser.add_argument("--max-turns", "-t", type=int, default=None, help="Maximum number of turns the agent is allowed to run. If omitted, uses settings.json max_turns (default 200; 0 = unlimited).")
|
|
106
106
|
parser.add_argument("--permission-mode", "-p", type=PermissionMode, choices=list(PermissionMode), default=PermissionMode.DEFAULT, help="Permission mode for mutating tools.")
|
|
107
107
|
parser.add_argument("--resume", "-r", action="store_true", help="Resume from the last session stored in .nimcode/sessions/session.json.")
|
|
@@ -8,7 +8,7 @@ logger = logging.getLogger(__name__)
|
|
|
8
8
|
def load_settings() -> Dict[str, Any]:
|
|
9
9
|
"""Loads configuration from ~/.nimcode/settings.json and .nimcode/settings.json"""
|
|
10
10
|
settings = {
|
|
11
|
-
"model": "
|
|
11
|
+
"model": "deepseek-ai/deepseek-v4-pro",
|
|
12
12
|
"api_base_url": "https://integrate.api.nvidia.com/v1",
|
|
13
13
|
"mcp_servers": {},
|
|
14
14
|
# Timeouts (0 = disabled/infinite)
|
|
@@ -7,17 +7,22 @@ logger = logging.getLogger(__name__)
|
|
|
7
7
|
|
|
8
8
|
class LenientParser:
|
|
9
9
|
@staticmethod
|
|
10
|
-
def extract_tool_calls(text: str) -> List[str]:
|
|
11
|
-
"""Extracts JSON strings from <tool_call> fenced blocks."""
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
pattern = re.compile(r"<tool_call>\s*(.*?)(?:</tool_call>|\Z)", re.DOTALL | re.IGNORECASE)
|
|
10
|
+
def extract_tool_calls(text: str) -> List[Tuple[str, str]]:
|
|
11
|
+
"""Extracts tool names and JSON strings from <tool_call> fenced blocks."""
|
|
12
|
+
# We look for <tool_call> or <tool_call name="..."> and extract everything until </tool_call> or EOF.
|
|
13
|
+
pattern = re.compile(r"<tool_call([^>]*)>\s*(.*?)(?:</tool_call>|\Z)", re.DOTALL | re.IGNORECASE)
|
|
15
14
|
matches = pattern.findall(text)
|
|
16
15
|
|
|
17
16
|
# Clean up markdown code blocks if the model wrapped the JSON in them
|
|
18
17
|
cleaned_matches = []
|
|
19
|
-
for
|
|
20
|
-
|
|
18
|
+
for attr_str, inner_text in matches:
|
|
19
|
+
# Parse name from attributes if it exists (e.g., name="Read")
|
|
20
|
+
name = ""
|
|
21
|
+
name_match = re.search(r'name=["\'](.*?)["\']', attr_str, re.IGNORECASE)
|
|
22
|
+
if name_match:
|
|
23
|
+
name = name_match.group(1).strip()
|
|
24
|
+
|
|
25
|
+
match = inner_text.strip()
|
|
21
26
|
if match.startswith("```json"):
|
|
22
27
|
match = match[7:]
|
|
23
28
|
elif match.startswith("```"):
|
|
@@ -26,7 +31,7 @@ class LenientParser:
|
|
|
26
31
|
if match.endswith("```"):
|
|
27
32
|
match = match[:-3]
|
|
28
33
|
|
|
29
|
-
cleaned_matches.append(match.strip())
|
|
34
|
+
cleaned_matches.append((name, match.strip()))
|
|
30
35
|
|
|
31
36
|
return cleaned_matches
|
|
32
37
|
|
|
@@ -86,14 +91,19 @@ class LenientParser:
|
|
|
86
91
|
Returns (plain_text_message, list_of_tool_calls).
|
|
87
92
|
"""
|
|
88
93
|
# First, extract the tool calls
|
|
89
|
-
|
|
94
|
+
tool_call_tuples = cls.extract_tool_calls(text)
|
|
90
95
|
|
|
91
96
|
tool_calls = []
|
|
92
|
-
for tc_str in
|
|
97
|
+
for name, tc_str in tool_call_tuples:
|
|
93
98
|
if not tc_str:
|
|
94
99
|
continue
|
|
95
100
|
try:
|
|
96
|
-
|
|
101
|
+
tc_json = cls.parse_tool_call(tc_str)
|
|
102
|
+
# If model used <tool_call name="..."> and provided raw arguments in JSON
|
|
103
|
+
if isinstance(tc_json, dict) and "tool" not in tc_json and name:
|
|
104
|
+
tc = {"tool": name, "args": tc_json}
|
|
105
|
+
else:
|
|
106
|
+
tc = tc_json
|
|
97
107
|
tool_calls.append(tc)
|
|
98
108
|
except ValueError:
|
|
99
109
|
# We could append an error message or raise. We'll raise to let the agent loop handle it and re-prompt.
|
|
@@ -101,7 +111,7 @@ class LenientParser:
|
|
|
101
111
|
|
|
102
112
|
# The plain text is whatever is not in the tool_call blocks.
|
|
103
113
|
# We remove the tool call blocks from the original text to get the assistant's prose.
|
|
104
|
-
plain_text = re.sub(r"<tool_call
|
|
114
|
+
plain_text = re.sub(r"<tool_call[^>]*>\s*(.*?)(?:</tool_call>|\Z)", "", text, flags=re.DOTALL | re.IGNORECASE)
|
|
105
115
|
plain_text = plain_text.strip()
|
|
106
116
|
|
|
107
117
|
return plain_text, tool_calls
|
|
@@ -8,7 +8,7 @@ from typing import List, Dict, Any, Optional, AsyncGenerator
|
|
|
8
8
|
logger = logging.getLogger(__name__)
|
|
9
9
|
|
|
10
10
|
class NimClient:
|
|
11
|
-
def __init__(self, api_key: str, base_url: str = "https://integrate.api.nvidia.com/v1", model: str = "
|
|
11
|
+
def __init__(self, api_key: str, base_url: str = "https://integrate.api.nvidia.com/v1", model: str = "deepseek-ai/deepseek-v4-pro", timeout: float = 120.0, max_retries: int = 15, retry_base_delay: float = 2.0, retry_max_delay: float = 60.0):
|
|
12
12
|
self.api_key = api_key
|
|
13
13
|
self.base_url = base_url.rstrip("/")
|
|
14
14
|
self.model = model
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nimcode
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.6
|
|
4
4
|
Summary: A standalone, robust coding agent for NVIDIA NIM models.
|
|
5
5
|
Home-page: https://github.com/Batunash/nimcode
|
|
6
6
|
Author: Autonomous Agent
|
|
@@ -193,12 +193,14 @@ NimCode's behavior can be fully customized through `~/.nimcode/settings.json` (g
|
|
|
193
193
|
|
|
194
194
|
## 📋 Changelog
|
|
195
195
|
|
|
196
|
-
### v0.8.
|
|
197
|
-
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
-
|
|
201
|
-
- 🧪 **
|
|
196
|
+
### v0.8.6 (Latest)
|
|
197
|
+
- 🚀 **Default Model Bump**: Made `deepseek-ai/deepseek-v4-pro` the default model across the entire agent for superior reasoning and tool performance.
|
|
198
|
+
|
|
199
|
+
### v0.8.4
|
|
200
|
+
- 🧠 **Native DeepSeek XML Support**: Re-engineered the internal parser (`lenient_parser.py`) into a hybrid parser that seamlessly handles both strict JSON (`<tool_call>`) and fine-tuned XML formats (`<tool_call name=\"...\">`). DeepSeek V4 Pro, Mistral Large 2, and others now work flawlessly out of the box!
|
|
201
|
+
- 🧪 **224 tests passing**
|
|
202
|
+
|
|
203
|
+
### v0.8.0
|
|
202
204
|
|
|
203
205
|
### v0.7.0
|
|
204
206
|
- 🛡️ **Strict Markdown Validator**: `/plan` mode now physically blocks tasks that are under 150 characters or missing required Markdown headers (`Target Files`, `Implementation Details`, `Checklist`).
|
|
@@ -7,7 +7,7 @@ from nimcode.config import load_settings, save_global_setting
|
|
|
7
7
|
def test_load_settings_default():
|
|
8
8
|
with patch("os.path.exists", return_value=False):
|
|
9
9
|
settings = load_settings()
|
|
10
|
-
assert settings["model"] == "
|
|
10
|
+
assert settings["model"] == "deepseek-ai/deepseek-v4-pro"
|
|
11
11
|
assert settings["mcp_servers"] == {}
|
|
12
12
|
# New configurable settings
|
|
13
13
|
assert settings["max_turns"] == 200
|
|
@@ -74,4 +74,4 @@ def test_load_settings_error_handling():
|
|
|
74
74
|
with patch("os.path.exists", return_value=True):
|
|
75
75
|
with patch("builtins.open", side_effect=Exception("Read error")):
|
|
76
76
|
settings = load_settings()
|
|
77
|
-
assert settings["model"] == "
|
|
77
|
+
assert settings["model"] == "deepseek-ai/deepseek-v4-pro"
|
|
@@ -5,7 +5,7 @@ def test_extract_tool_calls_clean():
|
|
|
5
5
|
text = "Here is my tool call:\n<tool_call>\n{\"tool\": \"Read\", \"args\": {}}\n</tool_call>\nAnd some prose."
|
|
6
6
|
calls = LenientParser.extract_tool_calls(text)
|
|
7
7
|
assert len(calls) == 1
|
|
8
|
-
assert calls[0] == "{\"tool\": \"Read\", \"args\": {}}"
|
|
8
|
+
assert calls[0] == ("", "{\"tool\": \"Read\", \"args\": {}}")
|
|
9
9
|
|
|
10
10
|
def test_repair_truncated_json():
|
|
11
11
|
# Test unclosed quote
|
|
@@ -19,19 +19,19 @@ def test_extract_tool_calls_unclosed():
|
|
|
19
19
|
text = "Here is my tool call:\n<tool_call>\n{\"tool\": \"Read\"}"
|
|
20
20
|
calls = LenientParser.extract_tool_calls(text)
|
|
21
21
|
assert len(calls) == 1
|
|
22
|
-
assert calls[0] == "{\"tool\": \"Read\"}"
|
|
22
|
+
assert calls[0] == ("", "{\"tool\": \"Read\"}")
|
|
23
23
|
|
|
24
24
|
def test_extract_tool_calls_markdown_wrapped():
|
|
25
25
|
text = "<tool_call>\n```json\n{\"tool\": \"Read\"}\n```\n</tool_call>"
|
|
26
26
|
calls = LenientParser.extract_tool_calls(text)
|
|
27
27
|
assert len(calls) == 1
|
|
28
|
-
assert calls[0] == "{\"tool\": \"Read\"}"
|
|
28
|
+
assert calls[0] == ("", "{\"tool\": \"Read\"}")
|
|
29
29
|
|
|
30
30
|
def test_extract_tool_calls_markdown_wrapped_no_lang():
|
|
31
31
|
text = "<tool_call>\n```\n{\"tool\": \"Read\"}\n```\n</tool_call>"
|
|
32
32
|
calls = LenientParser.extract_tool_calls(text)
|
|
33
33
|
assert len(calls) == 1
|
|
34
|
-
assert calls[0] == "{\"tool\": \"Read\"}"
|
|
34
|
+
assert calls[0] == ("", "{\"tool\": \"Read\"}")
|
|
35
35
|
|
|
36
36
|
def test_repair_trailing_comma():
|
|
37
37
|
bad_json = '{"tool": "Read", "args": {"file": "a.txt",},}'
|
|
@@ -65,3 +65,18 @@ def test_process_model_response_raises_on_invalid():
|
|
|
65
65
|
text = "<tool_call>\n{invalid}\n</tool_call>"
|
|
66
66
|
with pytest.raises(ValueError, match="Malformed tool call JSON"):
|
|
67
67
|
LenientParser.process_model_response(text)
|
|
68
|
+
|
|
69
|
+
def test_extract_tool_calls_with_name_attr():
|
|
70
|
+
text = "Here is DeepSeek output:\n<tool_call name=\"Read\">\n{\"file_path\": \"a.txt\"}\n</tool_call>"
|
|
71
|
+
calls = LenientParser.extract_tool_calls(text)
|
|
72
|
+
assert len(calls) == 1
|
|
73
|
+
assert calls[0] == ("Read", "{\"file_path\": \"a.txt\"}")
|
|
74
|
+
|
|
75
|
+
def test_process_model_response_deepseek_format():
|
|
76
|
+
text = "Thinking... <tool_call name=\"Write\">\n{\"file_path\": \"a.txt\", \"content\": \"hello\"}\n</tool_call>"
|
|
77
|
+
prose, calls = LenientParser.process_model_response(text)
|
|
78
|
+
assert prose == "Thinking..."
|
|
79
|
+
assert len(calls) == 1
|
|
80
|
+
assert calls[0]["tool"] == "Write"
|
|
81
|
+
assert calls[0]["args"]["file_path"] == "a.txt"
|
|
82
|
+
assert calls[0]["args"]["content"] == "hello"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.8.3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|