agent-tool-parser 0.1.5__tar.gz → 0.1.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.
Files changed (33) hide show
  1. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/Cargo.lock +4 -4
  2. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/Cargo.toml +1 -1
  3. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/PKG-INFO +3 -3
  4. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/README.md +2 -2
  5. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/README.md +2 -2
  6. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/src/parser.rs +14 -1
  7. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/src/streaming.rs +5 -3
  8. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/tests/degenerate_tests.rs +13 -0
  9. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/pyproject.toml +1 -1
  10. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/src/agent_tool_parser/__init__.py +1 -1
  11. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/LICENSE +0 -0
  12. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/Cargo.toml +0 -0
  13. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/src/canonical.rs +0 -0
  14. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/src/cleaners.rs +0 -0
  15. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/src/lib.rs +0 -0
  16. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/src/models.rs +0 -0
  17. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/src/python_calls.rs +0 -0
  18. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/src/raw.rs +0 -0
  19. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/src/semantics.rs +0 -0
  20. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/src/vector.rs +0 -0
  21. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-core/tests/benchmark_tests.rs +0 -0
  22. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-py/Cargo.toml +0 -0
  23. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/crates/agent-tool-parser-py/src/lib.rs +0 -0
  24. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/src/agent_tool_parser/canonical.py +0 -0
  25. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/src/agent_tool_parser/cleaners.py +0 -0
  26. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/src/agent_tool_parser/models.py +0 -0
  27. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/src/agent_tool_parser/parser.py +0 -0
  28. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/src/agent_tool_parser/py.typed +0 -0
  29. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/src/agent_tool_parser/python_calls.py +0 -0
  30. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/src/agent_tool_parser/raw.py +0 -0
  31. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/src/agent_tool_parser/semantics.py +0 -0
  32. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/src/agent_tool_parser/streaming.py +0 -0
  33. {agent_tool_parser-0.1.5 → agent_tool_parser-0.1.6}/src/agent_tool_parser/vector.py +0 -0
@@ -4,7 +4,7 @@ version = 4
4
4
 
5
5
  [[package]]
6
6
  name = "agent-tool-parser-c"
7
- version = "0.1.5"
7
+ version = "0.1.6"
8
8
  dependencies = [
9
9
  "agent-tool-parser-core",
10
10
  "serde_json",
@@ -12,7 +12,7 @@ dependencies = [
12
12
 
13
13
  [[package]]
14
14
  name = "agent-tool-parser-core"
15
- version = "0.1.5"
15
+ version = "0.1.6"
16
16
  dependencies = [
17
17
  "html-escape",
18
18
  "memchr",
@@ -24,7 +24,7 @@ dependencies = [
24
24
 
25
25
  [[package]]
26
26
  name = "agent-tool-parser-py"
27
- version = "0.1.5"
27
+ version = "0.1.6"
28
28
  dependencies = [
29
29
  "agent-tool-parser-core",
30
30
  "pyo3",
@@ -34,7 +34,7 @@ dependencies = [
34
34
 
35
35
  [[package]]
36
36
  name = "agent-tool-parser-wasm"
37
- version = "0.1.5"
37
+ version = "0.1.6"
38
38
  dependencies = [
39
39
  "agent-tool-parser-core",
40
40
  "serde-wasm-bindgen",
@@ -3,7 +3,7 @@ resolver = "2"
3
3
  members = ["crates/agent-tool-parser-core", "crates/agent-tool-parser-py"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.1.5"
6
+ version = "0.1.6"
7
7
  edition = "2021"
8
8
  authors = ["InBoost Team"]
9
9
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-tool-parser
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -41,9 +41,9 @@ Project-URL: Repository, https://github.com/inboost-dev/agent-tool-parser.git
41
41
  # agent-tool-parser ⚡
42
42
 
43
43
  [![Documentation](https://img.shields.io/badge/docs-interactive%20playground-blueviolet.svg)](https://inboost-dev.github.io/agent-tool-parser/)
44
- [![PyPI version](https://img.shields.io/badge/pypi-v0.1.5-blue.svg)](https://pypi.org/project/agent-tool-parser/)
44
+ [![PyPI version](https://img.shields.io/badge/pypi-v0.1.6-blue.svg)](https://pypi.org/project/agent-tool-parser/)
45
45
  [![Python](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](https://pypi.org/)
46
- [![Crates.io](https://img.shields.io/badge/crates.io-v0.1.5-orange.svg)](https://crates.io/crates/agent-tool-parser-core)
46
+ [![Crates.io](https://img.shields.io/badge/crates.io-v0.1.6-orange.svg)](https://crates.io/crates/agent-tool-parser-core)
47
47
  [![Rust](https://img.shields.io/badge/rust-1.75%2B-red.svg)](https://www.rust-lang.org/)
48
48
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
49
49
  [![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)]()
@@ -1,9 +1,9 @@
1
1
  # agent-tool-parser ⚡
2
2
 
3
3
  [![Documentation](https://img.shields.io/badge/docs-interactive%20playground-blueviolet.svg)](https://inboost-dev.github.io/agent-tool-parser/)
4
- [![PyPI version](https://img.shields.io/badge/pypi-v0.1.5-blue.svg)](https://pypi.org/project/agent-tool-parser/)
4
+ [![PyPI version](https://img.shields.io/badge/pypi-v0.1.6-blue.svg)](https://pypi.org/project/agent-tool-parser/)
5
5
  [![Python](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](https://pypi.org/)
6
- [![Crates.io](https://img.shields.io/badge/crates.io-v0.1.5-orange.svg)](https://crates.io/crates/agent-tool-parser-core)
6
+ [![Crates.io](https://img.shields.io/badge/crates.io-v0.1.6-orange.svg)](https://crates.io/crates/agent-tool-parser-core)
7
7
  [![Rust](https://img.shields.io/badge/rust-1.75%2B-red.svg)](https://www.rust-lang.org/)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
9
9
  [![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)]()
@@ -1,9 +1,9 @@
1
1
  # agent-tool-parser ⚡
2
2
 
3
3
  [![Documentation](https://img.shields.io/badge/docs-interactive%20playground-blueviolet.svg)](https://inboost-dev.github.io/agent-tool-parser/)
4
- [![PyPI version](https://img.shields.io/badge/pypi-v0.1.5-blue.svg)](https://pypi.org/project/agent-tool-parser/)
4
+ [![PyPI version](https://img.shields.io/badge/pypi-v0.1.6-blue.svg)](https://pypi.org/project/agent-tool-parser/)
5
5
  [![Python](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](https://pypi.org/)
6
- [![Crates.io](https://img.shields.io/badge/crates.io-v0.1.5-orange.svg)](https://crates.io/crates/agent-tool-parser-core)
6
+ [![Crates.io](https://img.shields.io/badge/crates.io-v0.1.6-orange.svg)](https://crates.io/crates/agent-tool-parser-core)
7
7
  [![Rust](https://img.shields.io/badge/rust-1.75%2B-red.svg)](https://www.rust-lang.org/)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
9
9
  [![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)]()
@@ -13,6 +13,18 @@ static JSON_BLOCK_RE: Lazy<Regex> =
13
13
  static JSON_ARRAY_BLOCK_RE: Lazy<Regex> =
14
14
  Lazy::new(|| Regex::new(r"(?s)```(?:json)?\s*(\[.*?\])\s*```").unwrap());
15
15
 
16
+ /// Safely find the closest char boundary at or below `index` (MSRV 1.75+ compatible).
17
+ #[inline]
18
+ pub(crate) fn floor_char_boundary(s: &str, mut index: usize) -> usize {
19
+ if index >= s.len() {
20
+ return s.len();
21
+ }
22
+ while !s.is_char_boundary(index) {
23
+ index -= 1;
24
+ }
25
+ index
26
+ }
27
+
16
28
  pub(crate) static INVOKE_RE: Lazy<Regex> = Lazy::new(|| {
17
29
  Regex::new(
18
30
  r#"(?is)<[||]*(?:dsml[||]*)?(?P<tag>tool_invoke|invoke|tool_call|call|tool|invocation|function_call|function|func|action|tool_use|ant_tool_use|function_use|действие|вызов_функции|функция|инструмент)(?:(?::|=)\s*['"]?(?P<colon_tool>[\w.:-]+)['"]?)?\b(?P<attrs>[^>]*)>(?P<body>.*?)(?:</[||]*(?:dsml[||]*)?(?:tool_invoke|invoke|tool_call|call|tool|invocation|function_call|function|func|action|tool_use|ant_tool_use|function_use|действие|вызов_функции|функция|инструмент)(?:(?::|=)\s*[\w.:-]+)?\s*>|<[||]*(?:dsml[||]*)?(?:tool_invoke|invoke|tool_call|call|tool|invocation|function_call|function|func|action|tool_use|ant_tool_use|function_use|действие|вызов_функции|функция|инструмент)\b|</[||]*(?:dsml[||]*)?(?:tool_call|tool_calls|function_calls|calls|tools)\s*>|$)"#
@@ -553,7 +565,8 @@ impl ToolParser {
553
565
  Err(ToolError::new(format!(
554
566
  "No valid tool call found in response: {:?}",
555
567
  if text.len() > 100 {
556
- &text[..100]
568
+ let end = floor_char_boundary(&text, 100);
569
+ &text[..end]
557
570
  } else {
558
571
  &text
559
572
  }
@@ -179,7 +179,8 @@ impl StreamingToolParser {
179
179
  .unwrap_or_default();
180
180
  if tag_lower == "tool_call" || tag_lower == "tool_calls" {
181
181
  let after_tag = &self.buffer[end..];
182
- let check_len = after_tag.len().min(300);
182
+ let check_len =
183
+ crate::parser::floor_char_boundary(after_tag, 300);
183
184
  JSON_NAME_RE.captures(&after_tag[..check_len]).map(|c| {
184
185
  c.name("name").unwrap().as_str().trim().to_string()
185
186
  })
@@ -343,8 +344,9 @@ impl StreamingToolParser {
343
344
  }
344
345
 
345
346
  if self.buffer.len() > 500 {
346
- if let Some(nl) = self.buffer[100..].find('\n') {
347
- let cut = 100 + nl + 1;
347
+ let check_start = crate::parser::floor_char_boundary(&self.buffer, 100);
348
+ if let Some(nl) = self.buffer[check_start..].find('\n') {
349
+ let cut = check_start + nl + 1;
348
350
  events.push(StreamEvent::Text(self.buffer[..cut].to_string()));
349
351
  self.buffer = self.buffer[cut..].to_string();
350
352
  continue;
@@ -442,3 +442,16 @@ fn test_degenerate_massive_string_without_catastrophic_backtracking() {
442
442
  elapsed
443
443
  );
444
444
  }
445
+
446
+ #[test]
447
+ fn test_degenerate_utf8_char_boundary_error_preview() {
448
+ // 98 ASCII bytes + 3-byte CJK character '钮' (bytes 98..101).
449
+ // Slicing at byte 100 without boundary check panics with non-char-boundary.
450
+ let input = format!("{}钮", "a".repeat(98));
451
+ let err = parse_tool_call(&input).unwrap_err();
452
+ assert!(err.to_string().contains("No valid tool call found"));
453
+
454
+ // Also verify non-raising try_parse variants return empty / None without panic
455
+ assert!(try_parse_tool_call(&input).is_none());
456
+ assert!(try_parse_tool_calls(&input).is_empty());
457
+ }
@@ -11,7 +11,7 @@ include = ["LICENSE"]
11
11
 
12
12
  [project]
13
13
  name = "agent-tool-parser"
14
- version = "0.1.5"
14
+ version = "0.1.6"
15
15
  description = "High-performance dual-engine (Rust + Python) tool call parser for DeepSeek R1/V3, Claude 3.7, Qwen 2.5, Llama 3, OpenAI & ReAct formats with auto-repair and zero dependencies."
16
16
  readme = "README.md"
17
17
  requires-python = ">=3.9"
@@ -149,7 +149,7 @@ if not hasattr(ToolCall, "is_search"):
149
149
  if not hasattr(ToolCall, "is_submit"):
150
150
  ToolCall.is_submit = property(lambda self: is_submit_tool(self.name))
151
151
 
152
- __version__ = "0.1.5"
152
+ __version__ = "0.1.6"
153
153
  __all__ = [
154
154
  "ACCELERATED",
155
155
  "ARG_ALIASES",