kader 0.1.2__tar.gz → 0.1.4__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 (78) hide show
  1. {kader-0.1.2 → kader-0.1.4}/PKG-INFO +2 -1
  2. {kader-0.1.2 → kader-0.1.4}/cli/app.py +128 -2
  3. {kader-0.1.2 → kader-0.1.4}/cli/app.tcss +17 -0
  4. {kader-0.1.2 → kader-0.1.4}/cli/utils.py +1 -0
  5. {kader-0.1.2 → kader-0.1.4}/kader/agent/base.py +10 -5
  6. {kader-0.1.2 → kader-0.1.4}/pyproject.toml +2 -1
  7. {kader-0.1.2 → kader-0.1.4}/uv.lock +132 -1
  8. {kader-0.1.2 → kader-0.1.4}/.github/workflows/ci.yml +0 -0
  9. {kader-0.1.2 → kader-0.1.4}/.github/workflows/release.yml +0 -0
  10. {kader-0.1.2 → kader-0.1.4}/.gitignore +0 -0
  11. {kader-0.1.2 → kader-0.1.4}/.python-version +0 -0
  12. {kader-0.1.2 → kader-0.1.4}/.qwen/QWEN.md +0 -0
  13. {kader-0.1.2 → kader-0.1.4}/.qwen/agents/technical-writer.md +0 -0
  14. {kader-0.1.2 → kader-0.1.4}/.qwen/agents/test-automation-specialist.md +0 -0
  15. {kader-0.1.2 → kader-0.1.4}/README.md +0 -0
  16. {kader-0.1.2 → kader-0.1.4}/cli/README.md +0 -0
  17. {kader-0.1.2 → kader-0.1.4}/cli/__init__.py +0 -0
  18. {kader-0.1.2 → kader-0.1.4}/cli/__main__.py +0 -0
  19. {kader-0.1.2 → kader-0.1.4}/cli/widgets/__init__.py +0 -0
  20. {kader-0.1.2 → kader-0.1.4}/cli/widgets/confirmation.py +0 -0
  21. {kader-0.1.2 → kader-0.1.4}/cli/widgets/conversation.py +0 -0
  22. {kader-0.1.2 → kader-0.1.4}/cli/widgets/loading.py +0 -0
  23. {kader-0.1.2 → kader-0.1.4}/examples/.gitignore +0 -0
  24. {kader-0.1.2 → kader-0.1.4}/examples/README.md +0 -0
  25. {kader-0.1.2 → kader-0.1.4}/examples/memory_example.py +0 -0
  26. {kader-0.1.2 → kader-0.1.4}/examples/ollama_example.py +0 -0
  27. {kader-0.1.2 → kader-0.1.4}/examples/planning_agent_example.py +0 -0
  28. {kader-0.1.2 → kader-0.1.4}/examples/python_developer/main.py +0 -0
  29. {kader-0.1.2 → kader-0.1.4}/examples/python_developer/template.yaml +0 -0
  30. {kader-0.1.2 → kader-0.1.4}/examples/react_agent_example.py +0 -0
  31. {kader-0.1.2 → kader-0.1.4}/examples/simple_agent.py +0 -0
  32. {kader-0.1.2 → kader-0.1.4}/examples/todo_agent/main.py +0 -0
  33. {kader-0.1.2 → kader-0.1.4}/examples/tools_example.py +0 -0
  34. {kader-0.1.2 → kader-0.1.4}/kader/__init__.py +0 -0
  35. {kader-0.1.2 → kader-0.1.4}/kader/agent/__init__.py +0 -0
  36. {kader-0.1.2 → kader-0.1.4}/kader/agent/agents.py +0 -0
  37. {kader-0.1.2 → kader-0.1.4}/kader/agent/logger.py +0 -0
  38. {kader-0.1.2 → kader-0.1.4}/kader/config.py +0 -0
  39. {kader-0.1.2 → kader-0.1.4}/kader/memory/__init__.py +0 -0
  40. {kader-0.1.2 → kader-0.1.4}/kader/memory/conversation.py +0 -0
  41. {kader-0.1.2 → kader-0.1.4}/kader/memory/session.py +0 -0
  42. {kader-0.1.2 → kader-0.1.4}/kader/memory/state.py +0 -0
  43. {kader-0.1.2 → kader-0.1.4}/kader/memory/types.py +0 -0
  44. {kader-0.1.2 → kader-0.1.4}/kader/prompts/__init__.py +0 -0
  45. {kader-0.1.2 → kader-0.1.4}/kader/prompts/agent_prompts.py +0 -0
  46. {kader-0.1.2 → kader-0.1.4}/kader/prompts/base.py +0 -0
  47. {kader-0.1.2 → kader-0.1.4}/kader/prompts/templates/planning_agent.j2 +0 -0
  48. {kader-0.1.2 → kader-0.1.4}/kader/prompts/templates/react_agent.j2 +0 -0
  49. {kader-0.1.2 → kader-0.1.4}/kader/providers/__init__.py +0 -0
  50. {kader-0.1.2 → kader-0.1.4}/kader/providers/base.py +0 -0
  51. {kader-0.1.2 → kader-0.1.4}/kader/providers/mock.py +0 -0
  52. {kader-0.1.2 → kader-0.1.4}/kader/providers/ollama.py +0 -0
  53. {kader-0.1.2 → kader-0.1.4}/kader/tools/README.md +0 -0
  54. {kader-0.1.2 → kader-0.1.4}/kader/tools/__init__.py +0 -0
  55. {kader-0.1.2 → kader-0.1.4}/kader/tools/base.py +0 -0
  56. {kader-0.1.2 → kader-0.1.4}/kader/tools/exec_commands.py +0 -0
  57. {kader-0.1.2 → kader-0.1.4}/kader/tools/filesys.py +0 -0
  58. {kader-0.1.2 → kader-0.1.4}/kader/tools/filesystem.py +0 -0
  59. {kader-0.1.2 → kader-0.1.4}/kader/tools/protocol.py +0 -0
  60. {kader-0.1.2 → kader-0.1.4}/kader/tools/rag.py +0 -0
  61. {kader-0.1.2 → kader-0.1.4}/kader/tools/todo.py +0 -0
  62. {kader-0.1.2 → kader-0.1.4}/kader/tools/utils.py +0 -0
  63. {kader-0.1.2 → kader-0.1.4}/kader/tools/web.py +0 -0
  64. {kader-0.1.2 → kader-0.1.4}/tests/conftest.py +0 -0
  65. {kader-0.1.2 → kader-0.1.4}/tests/providers/test_mock.py +0 -0
  66. {kader-0.1.2 → kader-0.1.4}/tests/providers/test_ollama.py +0 -0
  67. {kader-0.1.2 → kader-0.1.4}/tests/providers/test_providers_base.py +0 -0
  68. {kader-0.1.2 → kader-0.1.4}/tests/test_agent_logger.py +0 -0
  69. {kader-0.1.2 → kader-0.1.4}/tests/test_agent_logger_integration.py +0 -0
  70. {kader-0.1.2 → kader-0.1.4}/tests/test_base_agent.py +0 -0
  71. {kader-0.1.2 → kader-0.1.4}/tests/test_file_memory.py +0 -0
  72. {kader-0.1.2 → kader-0.1.4}/tests/test_todo_tool.py +0 -0
  73. {kader-0.1.2 → kader-0.1.4}/tests/tools/test_exec_commands.py +0 -0
  74. {kader-0.1.2 → kader-0.1.4}/tests/tools/test_filesys_tools.py +0 -0
  75. {kader-0.1.2 → kader-0.1.4}/tests/tools/test_filesystem_tools.py +0 -0
  76. {kader-0.1.2 → kader-0.1.4}/tests/tools/test_rag.py +0 -0
  77. {kader-0.1.2 → kader-0.1.4}/tests/tools/test_tools_base.py +0 -0
  78. {kader-0.1.2 → kader-0.1.4}/tests/tools/test_web.py +0 -0
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kader
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: kader coding agent
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: faiss-cpu>=1.9.0
7
7
  Requires-Dist: jinja2>=3.1.6
8
8
  Requires-Dist: loguru>=0.7.3
9
9
  Requires-Dist: ollama>=0.6.1
10
+ Requires-Dist: outdated>=0.2.2
10
11
  Requires-Dist: pyyaml>=6.0.3
11
12
  Requires-Dist: tenacity>=9.1.2
12
13
  Requires-Dist: textual[syntax]>=6.8.0
@@ -2,6 +2,7 @@
2
2
 
3
3
  import asyncio
4
4
  import threading
5
+ from importlib.metadata import version as get_version
5
6
  from pathlib import Path
6
7
  from typing import Optional
7
8
 
@@ -32,9 +33,19 @@ from .utils import (
32
33
  )
33
34
  from .widgets import ConversationView, InlineSelector, LoadingSpinner, ModelSelector
34
35
 
35
- WELCOME_MESSAGE = """# Welcome to Kader CLI! 🚀
36
+ WELCOME_MESSAGE = """
37
+ <div align="center">
36
38
 
37
- Your **modern AI-powered coding assistant**.
39
+ ```
40
+ ██╗ ██╗ ██╗ █████╗ ██████╗ ███████╗██████╗
41
+ ██╔╝ ██║ ██╔╝██╔══██╗██╔══██╗██╔════╝██╔══██╗
42
+ ██╔╝ █████╔╝ ███████║██║ ██║█████╗ ██████╔╝
43
+ ██╔╝ ██╔═██╗ ██╔══██║██║ ██║██╔══╝ ██╔══██╗
44
+ ██╔╝ ██║ ██╗██║ ██║██████╔╝███████╗██║ ██║
45
+ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═╝
46
+ ```
47
+
48
+ </div>
38
49
 
39
50
  Type a message below to start chatting, or use one of the commands:
40
51
 
@@ -45,10 +56,16 @@ Type a message below to start chatting, or use one of the commands:
45
56
  - `/save` - Save current session
46
57
  - `/load` - Load a saved session
47
58
  - `/sessions` - List saved sessions
59
+ - `/cost` - Show the cost of the conversation
48
60
  - `/exit` - Exit the application
49
61
  """
50
62
 
51
63
 
64
+ # Minimum terminal size to prevent UI breakage
65
+ MIN_WIDTH = 89
66
+ MIN_HEIGHT = 29
67
+
68
+
52
69
  class KaderApp(App):
53
70
  """Main Kader CLI application."""
54
71
 
@@ -81,6 +98,7 @@ class KaderApp(App):
81
98
  self._confirmation_result: tuple[bool, Optional[str]] = (True, None)
82
99
  self._inline_selector: Optional[InlineSelector] = None
83
100
  self._model_selector: Optional[ModelSelector] = None
101
+ self._update_info: Optional[str] = None # Latest version if update available
84
102
 
85
103
  self._agent = self._create_agent(self._current_model)
86
104
 
@@ -153,11 +171,15 @@ class KaderApp(App):
153
171
  self._confirmation_result = (event.confirmed, None)
154
172
 
155
173
  # Remove selector and show result message
174
+ tool_message = None
156
175
  if self._inline_selector:
176
+ tool_message = self._inline_selector.message
157
177
  self._inline_selector.remove()
158
178
  self._inline_selector = None
159
179
 
160
180
  if event.confirmed:
181
+ if tool_message:
182
+ conversation.add_message(tool_message, "assistant")
161
183
  conversation.add_message("✅ Executing tool...", "assistant")
162
184
  # Restart spinner
163
185
  try:
@@ -291,6 +313,71 @@ class KaderApp(App):
291
313
  # Focus the input
292
314
  self.query_one("#prompt-input", Input).focus()
293
315
 
316
+ # Check initial size
317
+ self._check_terminal_size()
318
+
319
+ # Start background update check
320
+ threading.Thread(target=self._check_for_updates, daemon=True).start()
321
+
322
+ def _check_for_updates(self) -> None:
323
+ """Check for package updates in background thread."""
324
+ try:
325
+ from outdated import check_outdated
326
+
327
+ current_version = get_version("kader")
328
+ is_outdated, latest_version = check_outdated("kader", current_version)
329
+
330
+ if is_outdated:
331
+ self._update_info = latest_version
332
+ # Schedule UI update on main thread
333
+ self.call_from_thread(self._show_update_notification)
334
+ except Exception:
335
+ # Silently ignore update check failures
336
+ pass
337
+
338
+ def _show_update_notification(self) -> None:
339
+ """Show update notification as a toast."""
340
+ if not self._update_info:
341
+ return
342
+
343
+ try:
344
+ current = get_version("kader")
345
+ message = (
346
+ f">> Update available! v{current} → v{self._update_info} "
347
+ f"Run: uv tool upgrade kader"
348
+ )
349
+ self.notify(message, severity="information", timeout=10)
350
+ except Exception:
351
+ pass
352
+
353
+ def on_resize(self) -> None:
354
+ """Handle terminal resize events."""
355
+ self._check_terminal_size()
356
+
357
+ def _check_terminal_size(self) -> None:
358
+ """Check if terminal is large enough and show warning if not."""
359
+ width = self.console.size.width
360
+ height = self.console.size.height
361
+
362
+ # Check if we need to show/hide the size warning
363
+ too_small = width < MIN_WIDTH or height < MIN_HEIGHT
364
+
365
+ try:
366
+ warning = self.query_one("#size-warning", Static)
367
+ if not too_small:
368
+ warning.remove()
369
+ except Exception:
370
+ if too_small:
371
+ # Show warning overlay
372
+ warning_text = f"""⚠️ Terminal Too Small
373
+
374
+ Current: {width}x{height}
375
+ Minimum: {MIN_WIDTH}x{MIN_HEIGHT}
376
+
377
+ Please resize your terminal."""
378
+ warning = Static(warning_text, id="size-warning")
379
+ self.mount(warning)
380
+
294
381
  async def on_input_submitted(self, event: Input.Submitted) -> None:
295
382
  """Handle user input submission."""
296
383
  user_input = event.value.strip()
@@ -324,6 +411,7 @@ class KaderApp(App):
324
411
  elif cmd == "/clear":
325
412
  conversation.clear_messages()
326
413
  self._agent.memory.clear()
414
+ self._agent.provider.reset_tracking() # Reset usage/cost tracking
327
415
  self._current_session_id = None
328
416
  self.notify("Conversation cleared!", severity="information")
329
417
  elif cmd == "/save":
@@ -342,6 +430,8 @@ class KaderApp(App):
342
430
  elif cmd == "/refresh":
343
431
  self._refresh_directory_tree()
344
432
  self.notify("Directory tree refreshed!", severity="information")
433
+ elif cmd == "/cost":
434
+ self._handle_cost(conversation)
345
435
  elif cmd == "/exit":
346
436
  self.exit()
347
437
  else:
@@ -536,6 +626,42 @@ class KaderApp(App):
536
626
  conversation.add_message(f"❌ Error listing sessions: {e}", "assistant")
537
627
  self.notify(f"Error: {e}", severity="error")
538
628
 
629
+ def _handle_cost(self, conversation: ConversationView) -> None:
630
+ """Display LLM usage costs."""
631
+ try:
632
+ # Get cost and usage from the provider
633
+ cost = self._agent.provider.total_cost
634
+ usage = self._agent.provider.total_usage
635
+ model = self._agent.provider.model
636
+
637
+ lines = [
638
+ "## Usage Costs 💰\n",
639
+ f"**Model:** `{model}`\n",
640
+ "### Cost Breakdown",
641
+ "| Type | Amount |",
642
+ "|------|--------|",
643
+ f"| Input Cost | ${cost.input_cost:.6f} |",
644
+ f"| Output Cost | ${cost.output_cost:.6f} |",
645
+ f"| **Total Cost** | **${cost.total_cost:.6f}** |",
646
+ "",
647
+ "### Token Usage",
648
+ "| Type | Tokens |",
649
+ "|------|--------|",
650
+ f"| Prompt Tokens | {usage.prompt_tokens:,} |",
651
+ f"| Completion Tokens | {usage.completion_tokens:,} |",
652
+ f"| **Total Tokens** | **{usage.total_tokens:,}** |",
653
+ ]
654
+
655
+ if cost.total_cost == 0.0:
656
+ lines.append(
657
+ "\n> 💡 *Note: Ollama runs locally, so there are no API costs.*"
658
+ )
659
+
660
+ conversation.add_message("\n".join(lines), "assistant")
661
+ except Exception as e:
662
+ conversation.add_message(f"❌ Error getting costs: {e}", "assistant")
663
+ self.notify(f"Error: {e}", severity="error")
664
+
539
665
 
540
666
  def main() -> None:
541
667
  """Run the Kader CLI application."""
@@ -17,6 +17,23 @@ $text-muted: #6c7086;
17
17
 
18
18
  Screen {
19
19
  background: $background;
20
+ min-width: 89;
21
+ min-height: 29;
22
+ }
23
+
24
+ /* ===== Size Warning Overlay ===== */
25
+
26
+ #size-warning {
27
+ dock: top;
28
+ width: 100%;
29
+ height: 100%;
30
+ background: $background 95%;
31
+ color: $warning;
32
+ text-align: center;
33
+ content-align: center middle;
34
+ text-style: bold;
35
+ padding: 2;
36
+ layer: overlay;
20
37
  }
21
38
 
22
39
  /* ===== Header ===== */
@@ -19,6 +19,7 @@ HELP_TEXT = """## Kader CLI Commands 📖
19
19
  | `/save` | Save current session |
20
20
  | `/load <id>` | Load a saved session |
21
21
  | `/sessions` | List saved sessions |
22
+ | `/cost` | Show usage costs |
22
23
  | `/refresh` | Refresh file tree |
23
24
  | `/exit` | Exit the CLI |
24
25
 
@@ -322,7 +322,7 @@ class BaseAgent:
322
322
  return f"execute {tool_name}"
323
323
 
324
324
  def _confirm_tool_execution(
325
- self, tool_call_dict: dict
325
+ self, tool_call_dict: dict, llm_content: Optional[str] = None
326
326
  ) -> tuple[bool, Optional[str]]:
327
327
  """
328
328
  Ask user for confirmation before executing a tool.
@@ -336,6 +336,9 @@ class BaseAgent:
336
336
  """
337
337
  display_str = self._format_tool_call_for_display(tool_call_dict)
338
338
 
339
+ if llm_content and len(llm_content) > 0:
340
+ display_str = f"{llm_content}\n\n{display_str}"
341
+
339
342
  # Use callback if provided (e.g., for GUI/TUI)
340
343
  if self.tool_confirmation_callback:
341
344
  return self.tool_confirmation_callback(display_str)
@@ -357,7 +360,7 @@ class BaseAgent:
357
360
  print("Please enter 'yes' or 'no'.")
358
361
 
359
362
  async def _aconfirm_tool_execution(
360
- self, tool_call_dict: dict
363
+ self, tool_call_dict: dict, llm_content: Optional[str] = None
361
364
  ) -> tuple[bool, Optional[str]]:
362
365
  """
363
366
  Async version - Ask user for confirmation before executing a tool.
@@ -375,7 +378,9 @@ class BaseAgent:
375
378
  # In production, use asyncio.to_thread or aioconsole
376
379
  import asyncio
377
380
 
378
- return await asyncio.to_thread(self._confirm_tool_execution, tool_call_dict)
381
+ return await asyncio.to_thread(
382
+ self._confirm_tool_execution, tool_call_dict, llm_content
383
+ )
379
384
 
380
385
  def _process_tool_calls(
381
386
  self, response: LLMResponse
@@ -396,7 +401,7 @@ class BaseAgent:
396
401
  # Check for interrupt before tool execution
397
402
  if self.interrupt_before_tool:
398
403
  should_execute, user_input = self._confirm_tool_execution(
399
- tool_call_dict
404
+ tool_call_dict, response.content
400
405
  )
401
406
  if not should_execute:
402
407
  # Return the user's elaboration to be processed
@@ -455,7 +460,7 @@ class BaseAgent:
455
460
  # Check for interrupt before tool execution
456
461
  if self.interrupt_before_tool:
457
462
  should_execute, user_input = await self._aconfirm_tool_execution(
458
- tool_call_dict
463
+ tool_call_dict, response.content
459
464
  )
460
465
  if not should_execute:
461
466
  return (False, user_input)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kader"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  description = "kader coding agent"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -14,6 +14,7 @@ dependencies = [
14
14
  "wcmatch>=10.1",
15
15
  "typing-extensions>=4.15.0",
16
16
  "jinja2>=3.1.6",
17
+ "outdated>=0.2.2",
17
18
  ]
18
19
 
19
20
  [project.scripts]
@@ -194,6 +194,79 @@ wheels = [
194
194
  { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" },
195
195
  ]
196
196
 
197
+ [[package]]
198
+ name = "charset-normalizer"
199
+ version = "3.4.4"
200
+ source = { registry = "https://pypi.org/simple" }
201
+ sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
202
+ wheels = [
203
+ { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" },
204
+ { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" },
205
+ { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" },
206
+ { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" },
207
+ { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" },
208
+ { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" },
209
+ { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" },
210
+ { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" },
211
+ { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" },
212
+ { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" },
213
+ { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" },
214
+ { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" },
215
+ { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" },
216
+ { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" },
217
+ { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" },
218
+ { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" },
219
+ { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
220
+ { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
221
+ { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
222
+ { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
223
+ { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
224
+ { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
225
+ { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
226
+ { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
227
+ { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
228
+ { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
229
+ { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
230
+ { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
231
+ { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
232
+ { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
233
+ { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
234
+ { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
235
+ { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
236
+ { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
237
+ { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
238
+ { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
239
+ { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
240
+ { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
241
+ { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
242
+ { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
243
+ { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
244
+ { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
245
+ { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
246
+ { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
247
+ { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
248
+ { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
249
+ { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
250
+ { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
251
+ { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
252
+ { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
253
+ { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
254
+ { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
255
+ { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
256
+ { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
257
+ { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
258
+ { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
259
+ { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
260
+ { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
261
+ { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
262
+ { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
263
+ { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
264
+ { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
265
+ { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
266
+ { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
267
+ { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
268
+ ]
269
+
197
270
  [[package]]
198
271
  name = "click"
199
272
  version = "8.3.1"
@@ -414,13 +487,14 @@ wheels = [
414
487
 
415
488
  [[package]]
416
489
  name = "kader"
417
- version = "0.1.2"
490
+ version = "0.1.4"
418
491
  source = { editable = "." }
419
492
  dependencies = [
420
493
  { name = "faiss-cpu" },
421
494
  { name = "jinja2" },
422
495
  { name = "loguru" },
423
496
  { name = "ollama" },
497
+ { name = "outdated" },
424
498
  { name = "pyyaml" },
425
499
  { name = "tenacity" },
426
500
  { name = "textual", extra = ["syntax"] },
@@ -442,6 +516,7 @@ requires-dist = [
442
516
  { name = "jinja2", specifier = ">=3.1.6" },
443
517
  { name = "loguru", specifier = ">=0.7.3" },
444
518
  { name = "ollama", specifier = ">=0.6.1" },
519
+ { name = "outdated", specifier = ">=0.2.2" },
445
520
  { name = "pyyaml", specifier = ">=6.0.3" },
446
521
  { name = "tenacity", specifier = ">=9.1.2" },
447
522
  { name = "textual", extras = ["syntax"], specifier = ">=6.8.0" },
@@ -469,6 +544,15 @@ wheels = [
469
544
  { url = "https://files.pythonhosted.org/packages/04/1e/b832de447dee8b582cac175871d2f6c3d5077cc56d5575cadba1fd1cccfa/linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79", size = 19820, upload-time = "2024-02-04T14:48:02.496Z" },
470
545
  ]
471
546
 
547
+ [[package]]
548
+ name = "littleutils"
549
+ version = "0.2.4"
550
+ source = { registry = "https://pypi.org/simple" }
551
+ sdist = { url = "https://files.pythonhosted.org/packages/01/f0/ddc72338ed9365ced4b109d8b6233b6689d28f41f1e2a4d97fd8c1eb3da9/littleutils-0.2.4.tar.gz", hash = "sha256:c7835b01020ced42e291118b7d78fb16bc2d9a1b4f3f42f3cb3787bb4da53d19", size = 9526, upload-time = "2024-07-07T22:04:35.844Z" }
552
+ wheels = [
553
+ { url = "https://files.pythonhosted.org/packages/19/ac/a89d28d7421fffc028d68cdfde5e3e056e690cb4b1bbef4a5fea661e16f5/littleutils-0.2.4-py3-none-any.whl", hash = "sha256:d10d5fe2e107c49fe2fc2904a08d6e5a302b41f8405921835ffcc323782d5dbc", size = 8145, upload-time = "2024-07-07T22:04:34.24Z" },
554
+ ]
555
+
472
556
  [[package]]
473
557
  name = "loguru"
474
558
  version = "0.7.3"
@@ -856,6 +940,20 @@ wheels = [
856
940
  { url = "https://files.pythonhosted.org/packages/47/4f/4a617ee93d8208d2bcf26b2d8b9402ceaed03e3853c754940e2290fed063/ollama-0.6.1-py3-none-any.whl", hash = "sha256:fc4c984b345735c5486faeee67d8a265214a31cbb828167782dc642ce0a2bf8c", size = 14354, upload-time = "2025-11-13T23:02:16.292Z" },
857
941
  ]
858
942
 
943
+ [[package]]
944
+ name = "outdated"
945
+ version = "0.2.2"
946
+ source = { registry = "https://pypi.org/simple" }
947
+ dependencies = [
948
+ { name = "littleutils" },
949
+ { name = "requests" },
950
+ { name = "setuptools" },
951
+ ]
952
+ sdist = { url = "https://files.pythonhosted.org/packages/e9/43/ac45b6c53fc62c99f02dbc310939d8693aa76cdf9900afe74a60febc8266/outdated-0.2.2.tar.gz", hash = "sha256:4b7fdec88e36711120d096d485fc4d5035e4e5ffbd907cf3a6ce2af43058b970", size = 9325, upload-time = "2022-10-29T10:14:01.847Z" }
953
+ wheels = [
954
+ { url = "https://files.pythonhosted.org/packages/d3/04/7d2b9a0d1b81e30f39e6f358bac01f4f18b585f35b0ffc5c83fc274f146b/outdated-0.2.2-py2.py3-none-any.whl", hash = "sha256:3e9c2ee6d17e86ae8cc7bb71d70c4172690121cda367155a30994742172678c8", size = 7543, upload-time = "2022-10-29T10:13:59.919Z" },
955
+ ]
956
+
859
957
  [[package]]
860
958
  name = "packaging"
861
959
  version = "25.0"
@@ -1187,6 +1285,21 @@ wheels = [
1187
1285
  { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
1188
1286
  ]
1189
1287
 
1288
+ [[package]]
1289
+ name = "requests"
1290
+ version = "2.32.5"
1291
+ source = { registry = "https://pypi.org/simple" }
1292
+ dependencies = [
1293
+ { name = "certifi" },
1294
+ { name = "charset-normalizer" },
1295
+ { name = "idna" },
1296
+ { name = "urllib3" },
1297
+ ]
1298
+ sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
1299
+ wheels = [
1300
+ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
1301
+ ]
1302
+
1190
1303
  [[package]]
1191
1304
  name = "rich"
1192
1305
  version = "14.2.0"
@@ -1226,6 +1339,15 @@ wheels = [
1226
1339
  { url = "https://files.pythonhosted.org/packages/74/31/b0e29d572670dca3674eeee78e418f20bdf97fa8aa9ea71380885e175ca0/ruff-0.14.10-py3-none-win_arm64.whl", hash = "sha256:e51d046cf6dda98a4633b8a8a771451107413b0f07183b2bef03f075599e44e6", size = 13729839, upload-time = "2025-12-18T19:28:48.636Z" },
1227
1340
  ]
1228
1341
 
1342
+ [[package]]
1343
+ name = "setuptools"
1344
+ version = "80.9.0"
1345
+ source = { registry = "https://pypi.org/simple" }
1346
+ sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
1347
+ wheels = [
1348
+ { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
1349
+ ]
1350
+
1229
1351
  [[package]]
1230
1352
  name = "tenacity"
1231
1353
  version = "9.1.2"
@@ -1605,6 +1727,15 @@ wheels = [
1605
1727
  { url = "https://files.pythonhosted.org/packages/37/87/1f677586e8ac487e29672e4b17455758fce261de06a0d086167bb760361a/uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5", size = 6229, upload-time = "2024-02-09T16:52:00.371Z" },
1606
1728
  ]
1607
1729
 
1730
+ [[package]]
1731
+ name = "urllib3"
1732
+ version = "2.6.3"
1733
+ source = { registry = "https://pypi.org/simple" }
1734
+ sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
1735
+ wheels = [
1736
+ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
1737
+ ]
1738
+
1608
1739
  [[package]]
1609
1740
  name = "wcmatch"
1610
1741
  version = "10.1"
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes