renderers 0.1.9.dev7__tar.gz → 0.1.9.dev9__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 (76) hide show
  1. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/PKG-INFO +1 -1
  2. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/_version.py +2 -2
  3. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/base.py +10 -2
  4. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/parsing.py +44 -1
  5. renderers-0.1.9.dev9/tests/test_glm_tool_name_validation.py +183 -0
  6. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/uv.lock +3 -3
  7. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/.github/workflows/publish-dev.yml +0 -0
  8. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/.github/workflows/publish.yml +0 -0
  9. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/.github/workflows/style.yml +0 -0
  10. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/.github/workflows/test.yml +0 -0
  11. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/.gitignore +0 -0
  12. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/.pre-commit-config.yaml +0 -0
  13. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/LICENSE +0 -0
  14. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/README.md +0 -0
  15. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/docs/renderer-config.md +0 -0
  16. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/examples/README.md +0 -0
  17. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/examples/sglang/multiturn_generate_sglang.py +0 -0
  18. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/examples/sglang/online_multiturn_sglang.py +0 -0
  19. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/examples/tinker/multiturn_generate_tinker.py +0 -0
  20. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/examples/transformers/multiturn_generate_transformers.py +0 -0
  21. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/examples/vllm/multiturn_generate_vllm.py +0 -0
  22. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/pyproject.toml +0 -0
  23. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/__init__.py +0 -0
  24. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/client.py +0 -0
  25. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/configs.py +0 -0
  26. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/deepseek_r1.py +0 -0
  27. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/deepseek_v3.py +0 -0
  28. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/default.py +0 -0
  29. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/glm45.py +0 -0
  30. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/glm5.py +0 -0
  31. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/gpt_oss.py +0 -0
  32. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/hy3.py +0 -0
  33. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/kimi_k2.py +0 -0
  34. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/kimi_k25.py +0 -0
  35. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/laguna_xs2.py +0 -0
  36. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/llama_3.py +0 -0
  37. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/minimax_m2.py +0 -0
  38. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/nemotron3.py +0 -0
  39. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/parsers.py +0 -0
  40. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/prime_qwen3.py +0 -0
  41. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/qwen3.py +0 -0
  42. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/qwen35.py +0 -0
  43. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/qwen36.py +0 -0
  44. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/renderers/qwen3_vl.py +0 -0
  45. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/conftest.py +0 -0
  46. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_bridge.py +0 -0
  47. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_build_helpers.py +0 -0
  48. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_client.py +0 -0
  49. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_deepseek_r1.py +0 -0
  50. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_disabled_thinking_stability.py +0 -0
  51. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_gpt_oss_harmony_parity.py +0 -0
  52. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_hy3.py +0 -0
  53. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_incremental.py +0 -0
  54. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_is_content.py +0 -0
  55. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_kimi_k25_tool_schema.py +0 -0
  56. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_laguna_xs21.py +0 -0
  57. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_llama_3.py +0 -0
  58. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_load_tokenizer.py +0 -0
  59. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_message_indices.py +0 -0
  60. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_message_tool_names.py +0 -0
  61. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_multimodal.py +0 -0
  62. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_nemotron3_parity.py +0 -0
  63. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_nemotron3_ultra.py +0 -0
  64. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_parse_response.py +0 -0
  65. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_parse_response_robustness.py +0 -0
  66. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_parsers.py +0 -0
  67. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_preserve_thinking.py +0 -0
  68. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_prime_qwen3_parity.py +0 -0
  69. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_qwen35_size_coverage.py +0 -0
  70. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_render_ids.py +0 -0
  71. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_renderer_config.py +0 -0
  72. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_renderer_config_parity.py +0 -0
  73. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_roundtrip.py +0 -0
  74. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_sampled_mask.py +0 -0
  75. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_tokens_per_message.py +0 -0
  76. {renderers-0.1.9.dev7 → renderers-0.1.9.dev9}/tests/test_tool_arg_type_preservation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: renderers
3
- Version: 0.1.9.dev7
3
+ Version: 0.1.9.dev9
4
4
  Summary: Chat template renderers — deterministic message-to-token conversion for LLM training
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.1.9.dev7'
22
- __version_tuple__ = version_tuple = (0, 1, 9, 'dev7')
21
+ __version__ = version = '0.1.9.dev9'
22
+ __version_tuple__ = version_tuple = (0, 1, 9, 'dev9')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -558,8 +558,15 @@ class ToolCallParseStatus(str, enum.Enum):
558
558
  """Per-attempt outcome of parsing a single ``<tool_call>`` block.
559
559
 
560
560
  The renderer parser's job is JSON-syntax → ``dict`` (the parser-level
561
- contract). Schema validation — required fields, argument types, tool
562
- name lookup — is the *tool*'s job and is intentionally not done here.
561
+ contract). Schema validation — required fields, argument types — is
562
+ the *tool*'s job and is intentionally not done here. Tool-*name*
563
+ lookup is the one exception, and only where the reference inference
564
+ parser does it: vLLM ≥ 0.24 aliases ``glm45``/``glm47`` to a parser
565
+ with ``validate_tool_names=True`` that silently drops any call whose
566
+ name isn't in the request's tool list. ``parse_glm`` mirrors that as
567
+ ``UNKNOWN_TOOL`` (when ``tools`` is passed) so train-side parsing
568
+ agrees with what an eval client sees from the engine — but keeps the
569
+ attempt visible instead of swallowing it.
563
570
  See ``ParsedToolCall.status`` for what each value means.
564
571
 
565
572
  Diverges from vLLM/SGLang on purpose. Both engines collapse parse
@@ -576,6 +583,7 @@ class ToolCallParseStatus(str, enum.Enum):
576
583
  UNCLOSED_BLOCK = "unclosed_block" # opening delim hit EOS / stop
577
584
  MISSING_NAME = "missing_name" # parsed structurally, but no function name
578
585
  MALFORMED_STRUCTURE = "malformed_structure" # format-specific shape error
586
+ UNKNOWN_TOOL = "unknown_tool" # name not in the provided tools list
579
587
 
580
588
 
581
589
  @dataclass
@@ -59,6 +59,26 @@ def _build_param_type_index(
59
59
  return index
60
60
 
61
61
 
62
+ def _extract_tool_names(tools: list[ToolSpec] | None) -> set[str] | None:
63
+ """Set of declared tool names, or ``None`` when ``tools`` is empty.
64
+
65
+ ``None`` disables name validation — mirroring vLLM's
66
+ ``ParserEngine._is_valid_tool_name``, which returns ``True`` whenever
67
+ the request carries no tools. Accepts both flat ``ToolSpec`` and the
68
+ OpenAI ``{"type": "function", "function": {...}}`` envelope, like
69
+ ``_build_param_type_index`` (but independent of it: a tool with no
70
+ ``parameters.properties`` still counts as a known name).
71
+ """
72
+ if not tools:
73
+ return None
74
+ names: set[str] = set()
75
+ for tool in tools:
76
+ spec = tool.get("function", tool) if isinstance(tool, dict) else None
77
+ if isinstance(spec, dict) and isinstance(spec.get("name"), str):
78
+ names.add(spec["name"])
79
+ return names
80
+
81
+
62
82
  def _coerce_arg_value(
63
83
  text: str, param_schema: dict[str, Any] | None
64
84
  ) -> tuple[Any, bool]:
@@ -434,7 +454,23 @@ def parse_glm(
434
454
  arg_value_end_id: int,
435
455
  tools: list[ToolSpec] | None = None,
436
456
  ) -> ParsedResponse:
437
- """Parse GLM completion tokens. Token-level thinking + arg_key/arg_value tool calls."""
457
+ """Parse GLM completion tokens. Token-level thinking + arg_key/arg_value tool calls.
458
+
459
+ When ``tools`` is passed, tool names are validated against it: a call
460
+ whose name isn't declared gets ``status=UNKNOWN_TOOL`` instead of
461
+ ``OK``. This mirrors vLLM ≥ 0.24, where the ``glm45``/``glm47`` tool
462
+ parsers run with ``validate_tool_names=True`` and silently drop
463
+ unknown-name calls (``vllm/parser/glm47_moe.py``,
464
+ ``ParserEngine._is_valid_tool_name``) — so a completion that yields no
465
+ tool call from the engine also yields no ``OK`` call here, and
466
+ downstream finish-reason promotion (``renderers/client.py``) agrees
467
+ with an OpenAI chat-completions client talking to the same engine.
468
+ Notably this covers the missing-``<arg_key>`` shape
469
+ (``<tool_call>bash\\n<arg_value>...</arg_value></tool_call>``): both
470
+ this parser and vLLM's resolve the whole block as the name, which then
471
+ fails validation. Without ``tools``, no validation happens (vLLM
472
+ behaves the same when the request carries no tools).
473
+ """
438
474
  ids = _strip_stop_tokens(token_ids, stop_ids)
439
475
 
440
476
  reasoning = None
@@ -468,6 +504,7 @@ def parse_glm(
468
504
  arg_value_end_id,
469
505
  section_offset=parse_offset + tc_start,
470
506
  param_index=_build_param_type_index(tools),
507
+ known_names=_extract_tool_names(tools),
471
508
  )
472
509
  else:
473
510
  content_text = _decode(tokenizer, ids).strip()
@@ -491,6 +528,7 @@ def _parse_glm_tool_calls(
491
528
  *,
492
529
  section_offset: int,
493
530
  param_index: dict[str, dict[str, dict[str, Any]]],
531
+ known_names: set[str] | None = None,
494
532
  ) -> list[ParsedToolCall]:
495
533
  """Parse GLM-style tool calls: name + arg_key/arg_value pairs, all by token ID."""
496
534
  tool_calls: list[ParsedToolCall] = []
@@ -548,6 +586,11 @@ def _parse_glm_tool_calls(
548
586
  j += 1
549
587
  if not name:
550
588
  status = ToolCallParseStatus.MISSING_NAME
589
+ elif known_names is not None and name not in known_names:
590
+ # vLLM ≥ 0.24 drops the call entirely here; we keep the
591
+ # attempt visible but deny it OK so consumers agree with
592
+ # the engine on "no tool was called."
593
+ status = ToolCallParseStatus.UNKNOWN_TOOL
551
594
  elif structure_broke:
552
595
  status = ToolCallParseStatus.MALFORMED_STRUCTURE
553
596
  elif any_json_fallback:
@@ -0,0 +1,183 @@
1
+ """GLM tool-name validation against the provided tools list.
2
+
3
+ vLLM 0.24 re-aliased ``glm45`` from the lenient ``Glm4MoeModelToolParser``
4
+ to ``Glm47MoeModelToolParser``, whose engine config sets
5
+ ``validate_tool_names=True`` (``vllm/parser/glm47_moe.py``): a completed
6
+ ``<tool_call>`` block whose name isn't in the request's tool list is
7
+ silently dropped — no tool call is emitted and the response reads as a
8
+ voluntary stop. Before this, unknown-name calls were surfaced to the
9
+ harness, which answered with a recoverable "unknown tool" error.
10
+
11
+ ``parse_glm`` mirrors the ≥ 0.24 behavior when ``tools`` is passed:
12
+ unknown names get ``status=UNKNOWN_TOOL`` — never ``OK`` — so the
13
+ client-side stop→tool_calls finish-reason promotion
14
+ (``renderers/client.py``) agrees with what an OpenAI chat-completions
15
+ client sees from the engine, while the attempt itself stays visible for
16
+ verifier / RL-loss code. Without ``tools`` there is no validation, also
17
+ matching vLLM (``ParserEngine._is_valid_tool_name`` returns ``True``
18
+ when the request has no tools).
19
+
20
+ Cross-validated end-to-end against the real vLLM v0.23.0 and v0.24.0
21
+ parsers (vendored from the release tags) on identical completions.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from functools import lru_cache
27
+
28
+ from renderers.base import ToolCallParseStatus
29
+
30
+ # Both GLM renderers share ``parse_glm`` and are served by the same
31
+ # strict vLLM parser (the ``glm45`` and ``glm47`` aliases both resolve
32
+ # to ``Glm47MoeModelToolParser`` in vLLM ≥ 0.24).
33
+ _MODELS = [
34
+ ("THUDM/GLM-4.5-Air", "auto"),
35
+ ("zai-org/GLM-5", "auto"),
36
+ ]
37
+
38
+ _TOOLS = [
39
+ {
40
+ "name": "bash",
41
+ "description": "Run a shell command.",
42
+ "parameters": {
43
+ "type": "object",
44
+ "properties": {
45
+ "command": {"type": "string"},
46
+ "timeout": {"type": "integer"},
47
+ },
48
+ "required": ["command"],
49
+ },
50
+ },
51
+ # A declared tool with no parameters must still count as a known
52
+ # name (name lookup is independent of the param-type index).
53
+ {"name": "submit", "description": "Finish the episode."},
54
+ ]
55
+
56
+
57
+ @lru_cache(maxsize=None)
58
+ def _load(model: str, renderer_name: str):
59
+ from renderers import config_from_name, create_renderer
60
+ from renderers.base import load_tokenizer
61
+
62
+ tok = load_tokenizer(model)
63
+ return tok, create_renderer(tok, config_from_name(renderer_name))
64
+
65
+
66
+ def pytest_generate_tests(metafunc):
67
+ if "model" in metafunc.fixturenames:
68
+ metafunc.parametrize(
69
+ "model,renderer_name",
70
+ _MODELS,
71
+ ids=[m for m, _ in _MODELS],
72
+ )
73
+
74
+
75
+ def _parse(model: str, renderer_name: str, text: str, tools):
76
+ tok, renderer = _load(model, renderer_name)
77
+ ids = tok.encode(text, add_special_tokens=False)
78
+ return renderer.parse_response(ids, tools=tools)
79
+
80
+
81
+ def _statuses(parsed):
82
+ return [tc.status for tc in parsed.tool_calls]
83
+
84
+
85
+ def test_known_name_is_ok(model, renderer_name):
86
+ parsed = _parse(
87
+ model,
88
+ renderer_name,
89
+ "<tool_call>bash\n"
90
+ "<arg_key>command</arg_key>\n<arg_value>pwd</arg_value>\n"
91
+ "</tool_call>",
92
+ _TOOLS,
93
+ )
94
+ assert _statuses(parsed) == [ToolCallParseStatus.OK]
95
+ assert parsed.tool_calls[0].name == "bash"
96
+ assert parsed.tool_calls[0].arguments == {"command": "pwd"}
97
+
98
+
99
+ def test_known_name_without_parameters_is_ok(model, renderer_name):
100
+ parsed = _parse(model, renderer_name, "<tool_call>submit\n</tool_call>", _TOOLS)
101
+ assert _statuses(parsed) == [ToolCallParseStatus.OK]
102
+ assert parsed.tool_calls[0].name == "submit"
103
+
104
+
105
+ def test_unknown_name_is_flagged(model, renderer_name):
106
+ parsed = _parse(
107
+ model,
108
+ renderer_name,
109
+ "<tool_call>read\n"
110
+ "<arg_key>lines</arg_key>\n<arg_value>10</arg_value>\n"
111
+ "</tool_call>",
112
+ _TOOLS,
113
+ )
114
+ assert _statuses(parsed) == [ToolCallParseStatus.UNKNOWN_TOOL]
115
+ # The attempt stays visible (unlike vLLM's silent drop): name and
116
+ # parsed arguments are preserved for verifier / RL-loss consumers.
117
+ assert parsed.tool_calls[0].name == "read"
118
+ assert parsed.tool_calls[0].arguments == {"lines": 10}
119
+
120
+
121
+ def test_unknown_name_without_args_is_flagged(model, renderer_name):
122
+ parsed = _parse(model, renderer_name, "<tool_call>finish\n</tool_call>", _TOOLS)
123
+ assert _statuses(parsed) == [ToolCallParseStatus.UNKNOWN_TOOL]
124
+
125
+
126
+ def test_missing_arg_key_block_is_flagged(model, renderer_name):
127
+ # No <arg_key> token ⇒ the whole block resolves as the name — both
128
+ # here and in vLLM's engine (unmatched terminals in TOOL_NAME state
129
+ # accumulate into the name) — and then fails validation.
130
+ parsed = _parse(
131
+ model,
132
+ renderer_name,
133
+ "<tool_call>bash\n<arg_value>pwd</arg_value>\n</tool_call>",
134
+ _TOOLS,
135
+ )
136
+ assert _statuses(parsed) == [ToolCallParseStatus.UNKNOWN_TOOL]
137
+ assert "bash" in (parsed.tool_calls[0].name or "")
138
+
139
+
140
+ def test_mixed_calls_flag_only_unknown(model, renderer_name):
141
+ parsed = _parse(
142
+ model,
143
+ renderer_name,
144
+ "<tool_call>bash\n"
145
+ "<arg_key>command</arg_key>\n<arg_value>ls</arg_value>\n"
146
+ "</tool_call>"
147
+ "<tool_call>read\n"
148
+ "<arg_key>path</arg_key>\n<arg_value>x</arg_value>\n"
149
+ "</tool_call>",
150
+ _TOOLS,
151
+ )
152
+ assert _statuses(parsed) == [
153
+ ToolCallParseStatus.OK,
154
+ ToolCallParseStatus.UNKNOWN_TOOL,
155
+ ]
156
+
157
+
158
+ def test_no_tools_means_no_validation(model, renderer_name):
159
+ # vLLM skips name validation when the request carries no tools; so
160
+ # do we — this also keeps tools-less parse_response calls (the
161
+ # common test / SFT path) byte-for-byte backward compatible.
162
+ parsed = _parse(
163
+ model,
164
+ renderer_name,
165
+ "<tool_call>read\n"
166
+ "<arg_key>lines</arg_key>\n<arg_value>10</arg_value>\n"
167
+ "</tool_call>",
168
+ None,
169
+ )
170
+ assert _statuses(parsed) == [ToolCallParseStatus.OK]
171
+
172
+
173
+ def test_openai_envelope_tools_are_recognized(model, renderer_name):
174
+ wrapped = [{"type": "function", "function": t} for t in _TOOLS]
175
+ parsed = _parse(
176
+ model,
177
+ renderer_name,
178
+ "<tool_call>bash\n"
179
+ "<arg_key>command</arg_key>\n<arg_value>pwd</arg_value>\n"
180
+ "</tool_call>",
181
+ wrapped,
182
+ )
183
+ assert _statuses(parsed) == [ToolCallParseStatus.OK]
@@ -1464,11 +1464,11 @@ wheels = [
1464
1464
 
1465
1465
  [[package]]
1466
1466
  name = "setuptools"
1467
- version = "81.0.0"
1467
+ version = "83.0.0"
1468
1468
  source = { registry = "https://pypi.org/simple" }
1469
- sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" }
1469
+ sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" }
1470
1470
  wheels = [
1471
- { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" },
1471
+ { url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" },
1472
1472
  ]
1473
1473
 
1474
1474
  [[package]]
File without changes
File without changes