Merge pull request #55 from pollen-robotics/optional-local-vision
Browse files- .env.example +3 -2
- README.md +14 -7
- pyproject.toml +1 -2
- src/reachy_mini_conversation_demo/main.py +4 -0
- src/reachy_mini_conversation_demo/openai_realtime.py +7 -30
- src/reachy_mini_conversation_demo/utils.py +11 -3
- src/reachy_mini_conversation_demo/vision/processors.py +24 -12
- uv.lock +102 -98
.env.example
CHANGED
|
@@ -1,10 +1,11 @@
|
|
| 1 |
OPENAI_API_KEY=
|
| 2 |
MODEL_NAME="gpt-realtime"
|
| 3 |
|
| 4 |
-
# Local vision model
|
|
|
|
| 5 |
LOCAL_VISION_MODEL=HuggingFaceTB/SmolVLM2-2.2B-Instruct
|
| 6 |
|
| 7 |
-
# Cache for local VLM
|
| 8 |
HF_HOME=./cache
|
| 9 |
|
| 10 |
# Hugging Face token for accessing datasets/models
|
|
|
|
| 1 |
OPENAI_API_KEY=
|
| 2 |
MODEL_NAME="gpt-realtime"
|
| 3 |
|
| 4 |
+
# Local vision model (only used with --local-vision CLI flag)
|
| 5 |
+
# By default, vision is handled by gpt-realtime when the camera tool is used
|
| 6 |
LOCAL_VISION_MODEL=HuggingFaceTB/SmolVLM2-2.2B-Instruct
|
| 7 |
|
| 8 |
+
# Cache for local VLM (only used with --local-vision CLI flag)
|
| 9 |
HF_HOME=./cache
|
| 10 |
|
| 11 |
# Hugging Face token for accessing datasets/models
|
README.md
CHANGED
|
@@ -6,7 +6,7 @@ Conversational demo for the Reachy Mini robot combining OpenAI's realtime APIs,
|
|
| 6 |
|
| 7 |
## Overview
|
| 8 |
- Real-time audio conversation loop powered by the OpenAI realtime API and `fastrtc` for low-latency streaming.
|
| 9 |
-
-
|
| 10 |
- Layered motion system queues primary moves (dances, emotions, goto poses, breathing) while blending speech-reactive wobble and face-tracking.
|
| 11 |
- Async tool dispatch integrates robot motion, camera capture, and optional face-tracking capabilities through a Gradio web UI with live transcripts.
|
| 12 |
|
|
@@ -75,10 +75,10 @@ Some wheels (e.g. PyTorch) are large and require compatible CUDA or CPU builds
|
|
| 75 |
| Variable | Description |
|
| 76 |
|----------|-------------|
|
| 77 |
| `OPENAI_API_KEY` | Required. Grants access to the OpenAI realtime endpoint.
|
| 78 |
-
| `MODEL_NAME` | Override the realtime model (defaults to `gpt-realtime`).
|
| 79 |
-
| `HF_HOME` | Cache directory for local Hugging Face downloads (defaults to `./cache`).
|
| 80 |
-
| `HF_TOKEN` | Optional token for Hugging Face models (falls back to `huggingface-cli login`).
|
| 81 |
-
| `LOCAL_VISION_MODEL` | Hugging Face model path for local vision processing (defaults to `HuggingFaceTB/SmolVLM2-2.2B-Instruct`).
|
| 82 |
|
| 83 |
## Running the demo
|
| 84 |
|
|
@@ -88,7 +88,7 @@ Activate your virtual environment, ensure the Reachy Mini robot (or simulator) i
|
|
| 88 |
reachy-mini-conversation-demo
|
| 89 |
```
|
| 90 |
|
| 91 |
-
By default, the app runs in console mode for direct audio interaction. Use the `--gradio` flag to launch a web UI served locally at http://127.0.0.1:7860/ (required when running in simulation mode). With a camera attached,
|
| 92 |
|
| 93 |
### CLI options
|
| 94 |
|
|
@@ -96,6 +96,7 @@ By default, the app runs in console mode for direct audio interaction. Use the `
|
|
| 96 |
|--------|---------|-------------|
|
| 97 |
| `--head-tracker {yolo,mediapipe}` | `None` | Select a face-tracking backend when a camera is available. YOLO is implemented locally, MediaPipe comes from the `reachy_mini_toolbox` package. Requires the matching optional extra. |
|
| 98 |
| `--no-camera` | `False` | Run without camera capture or face tracking. |
|
|
|
|
| 99 |
| `--gradio` | `False` | Launch the Gradio web UI. Without this flag, runs in console mode. Required when running in simulation mode. |
|
| 100 |
| `--debug` | `False` | Enable verbose logging for troubleshooting. |
|
| 101 |
|
|
@@ -107,6 +108,12 @@ By default, the app runs in console mode for direct audio interaction. Use the `
|
|
| 107 |
reachy-mini-conversation-demo --head-tracker mediapipe
|
| 108 |
```
|
| 109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
- Disable the camera pipeline (audio-only conversation):
|
| 111 |
|
| 112 |
```bash
|
|
@@ -118,7 +125,7 @@ By default, the app runs in console mode for direct audio interaction. Use the `
|
|
| 118 |
| Tool | Action | Dependencies |
|
| 119 |
|------|--------|--------------|
|
| 120 |
| `move_head` | Queue a head pose change (left/right/up/down/front). | Core install only. |
|
| 121 |
-
| `camera` | Capture the latest camera frame and
|
| 122 |
| `head_tracking` | Enable or disable face-tracking offsets (not facial recognition - only detects and tracks face position). | Camera worker with configured head tracker. |
|
| 123 |
| `dance` | Queue a dance from `reachy_mini_dances_library`. | Core install only. |
|
| 124 |
| `stop_dance` | Clear queued dances. | Core install only. |
|
|
|
|
| 6 |
|
| 7 |
## Overview
|
| 8 |
- Real-time audio conversation loop powered by the OpenAI realtime API and `fastrtc` for low-latency streaming.
|
| 9 |
+
- Vision processing uses gpt-realtime by default (when camera tool is used), with optional local vision processing using SmolVLM2 model running on-device (CPU/GPU/MPS) via `--local-vision` flag.
|
| 10 |
- Layered motion system queues primary moves (dances, emotions, goto poses, breathing) while blending speech-reactive wobble and face-tracking.
|
| 11 |
- Async tool dispatch integrates robot motion, camera capture, and optional face-tracking capabilities through a Gradio web UI with live transcripts.
|
| 12 |
|
|
|
|
| 75 |
| Variable | Description |
|
| 76 |
|----------|-------------|
|
| 77 |
| `OPENAI_API_KEY` | Required. Grants access to the OpenAI realtime endpoint.
|
| 78 |
+
| `MODEL_NAME` | Override the realtime model (defaults to `gpt-realtime`). Used for both conversation and vision (unless `--local-vision` flag is used).
|
| 79 |
+
| `HF_HOME` | Cache directory for local Hugging Face downloads (only used with `--local-vision` flag, defaults to `./cache`).
|
| 80 |
+
| `HF_TOKEN` | Optional token for Hugging Face models (only used with `--local-vision` flag, falls back to `huggingface-cli login`).
|
| 81 |
+
| `LOCAL_VISION_MODEL` | Hugging Face model path for local vision processing (only used with `--local-vision` flag, defaults to `HuggingFaceTB/SmolVLM2-2.2B-Instruct`).
|
| 82 |
|
| 83 |
## Running the demo
|
| 84 |
|
|
|
|
| 88 |
reachy-mini-conversation-demo
|
| 89 |
```
|
| 90 |
|
| 91 |
+
By default, the app runs in console mode for direct audio interaction. Use the `--gradio` flag to launch a web UI served locally at http://127.0.0.1:7860/ (required when running in simulation mode). With a camera attached, vision is handled by the gpt-realtime model when the camera tool is used. For local vision processing, use the `--local-vision` flag to process frames periodically using the SmolVLM2 model. Additionally, you can enable face tracking via YOLO or MediaPipe pipelines depending on the extras you installed.
|
| 92 |
|
| 93 |
### CLI options
|
| 94 |
|
|
|
|
| 96 |
|--------|---------|-------------|
|
| 97 |
| `--head-tracker {yolo,mediapipe}` | `None` | Select a face-tracking backend when a camera is available. YOLO is implemented locally, MediaPipe comes from the `reachy_mini_toolbox` package. Requires the matching optional extra. |
|
| 98 |
| `--no-camera` | `False` | Run without camera capture or face tracking. |
|
| 99 |
+
| `--local-vision` | `False` | Use local vision model (SmolVLM2) for periodic image processing instead of gpt-realtime vision. Requires `local_vision` extra to be installed. |
|
| 100 |
| `--gradio` | `False` | Launch the Gradio web UI. Without this flag, runs in console mode. Required when running in simulation mode. |
|
| 101 |
| `--debug` | `False` | Enable verbose logging for troubleshooting. |
|
| 102 |
|
|
|
|
| 108 |
reachy-mini-conversation-demo --head-tracker mediapipe
|
| 109 |
```
|
| 110 |
|
| 111 |
+
- Run with local vision processing (requires `local_vision` extra):
|
| 112 |
+
|
| 113 |
+
```bash
|
| 114 |
+
reachy-mini-conversation-demo --local-vision
|
| 115 |
+
```
|
| 116 |
+
|
| 117 |
- Disable the camera pipeline (audio-only conversation):
|
| 118 |
|
| 119 |
```bash
|
|
|
|
| 125 |
| Tool | Action | Dependencies |
|
| 126 |
|------|--------|--------------|
|
| 127 |
| `move_head` | Queue a head pose change (left/right/up/down/front). | Core install only. |
|
| 128 |
+
| `camera` | Capture the latest camera frame and send it to gpt-realtime for vision analysis. | Requires camera worker; uses gpt-realtime vision by default. |
|
| 129 |
| `head_tracking` | Enable or disable face-tracking offsets (not facial recognition - only detects and tracks face position). | Camera worker with configured head tracker. |
|
| 130 |
| `dance` | Queue a dance from `reachy_mini_dances_library`. | Core install only. |
|
| 131 |
| `stop_dance` | Clear queued dances. | Core install only. |
|
pyproject.toml
CHANGED
|
@@ -16,7 +16,6 @@ dependencies = [
|
|
| 16 |
"gradio>=5.49.0",
|
| 17 |
"huggingface_hub>=0.34.4",
|
| 18 |
"opencv-python>=4.12.0.88",
|
| 19 |
-
"num2words",
|
| 20 |
|
| 21 |
#Environment variables
|
| 22 |
"python-dotenv",
|
|
@@ -31,7 +30,7 @@ dependencies = [
|
|
| 31 |
]
|
| 32 |
|
| 33 |
[project.optional-dependencies]
|
| 34 |
-
local_vision = ["torch", "transformers"]
|
| 35 |
yolo_vision = ["ultralytics", "supervision"]
|
| 36 |
mediapipe_vision = ["mediapipe>=0.10.14"]
|
| 37 |
all_vision = [
|
|
|
|
| 16 |
"gradio>=5.49.0",
|
| 17 |
"huggingface_hub>=0.34.4",
|
| 18 |
"opencv-python>=4.12.0.88",
|
|
|
|
| 19 |
|
| 20 |
#Environment variables
|
| 21 |
"python-dotenv",
|
|
|
|
| 30 |
]
|
| 31 |
|
| 32 |
[project.optional-dependencies]
|
| 33 |
+
local_vision = ["torch", "transformers", "num2words"]
|
| 34 |
yolo_vision = ["ultralytics", "supervision"]
|
| 35 |
mediapipe_vision = ["mediapipe>=0.10.14"]
|
| 36 |
all_vision = [
|
src/reachy_mini_conversation_demo/main.py
CHANGED
|
@@ -99,6 +99,8 @@ def main():
|
|
| 99 |
head_wobbler.start()
|
| 100 |
if camera_worker:
|
| 101 |
camera_worker.start()
|
|
|
|
|
|
|
| 102 |
|
| 103 |
try:
|
| 104 |
stream_manager.launch()
|
|
@@ -113,6 +115,8 @@ def main():
|
|
| 113 |
head_wobbler.stop()
|
| 114 |
if camera_worker:
|
| 115 |
camera_worker.stop()
|
|
|
|
|
|
|
| 116 |
|
| 117 |
# prevent connection to keep alive some threads
|
| 118 |
robot.client.disconnect()
|
|
|
|
| 99 |
head_wobbler.start()
|
| 100 |
if camera_worker:
|
| 101 |
camera_worker.start()
|
| 102 |
+
if vision_manager:
|
| 103 |
+
vision_manager.start()
|
| 104 |
|
| 105 |
try:
|
| 106 |
stream_manager.launch()
|
|
|
|
| 115 |
head_wobbler.stop()
|
| 116 |
if camera_worker:
|
| 117 |
camera_worker.stop()
|
| 118 |
+
if vision_manager:
|
| 119 |
+
vision_manager.stop()
|
| 120 |
|
| 121 |
# prevent connection to keep alive some threads
|
| 122 |
robot.client.disconnect()
|
src/reachy_mini_conversation_demo/openai_realtime.py
CHANGED
|
@@ -36,8 +36,6 @@ class OpenaiRealtimeHandler(AsyncStreamHandler):
|
|
| 36 |
self.connection = None
|
| 37 |
self.output_queue = asyncio.Queue()
|
| 38 |
|
| 39 |
-
self._pending_calls: dict[str, dict] = {}
|
| 40 |
-
|
| 41 |
self.last_activity_time = asyncio.get_event_loop().time()
|
| 42 |
self.start_time = asyncio.get_event_loop().time()
|
| 43 |
self.is_idle_tool_call = False
|
|
@@ -115,33 +113,10 @@ class OpenaiRealtimeHandler(AsyncStreamHandler):
|
|
| 115 |
)
|
| 116 |
|
| 117 |
# ---- tool-calling plumbing ----
|
| 118 |
-
# 1) model announces a function call item; capture name + call_id
|
| 119 |
-
if event.type == "response.output_item.added":
|
| 120 |
-
item = getattr(event, "item", None)
|
| 121 |
-
if item and getattr(item, "type", "") == "function_call":
|
| 122 |
-
call_id = getattr(item, "call_id", None)
|
| 123 |
-
name = getattr(item, "name", None)
|
| 124 |
-
if call_id and name:
|
| 125 |
-
self._pending_calls[call_id] = {
|
| 126 |
-
"name": name,
|
| 127 |
-
"args_buf": "",
|
| 128 |
-
}
|
| 129 |
-
|
| 130 |
-
# 2) model streams JSON arguments; buffer them by call_id
|
| 131 |
-
if event.type == "response.function_call_arguments.delta":
|
| 132 |
-
call_id = getattr(event, "call_id", None)
|
| 133 |
-
delta = getattr(event, "delta", "")
|
| 134 |
-
if call_id in self._pending_calls:
|
| 135 |
-
self._pending_calls[call_id]["args_buf"] += delta
|
| 136 |
-
|
| 137 |
-
# 3) when args done, execute Python tool, send function_call_output, then trigger a new response
|
| 138 |
if event.type == "response.function_call_arguments.done":
|
|
|
|
|
|
|
| 139 |
call_id = getattr(event, "call_id", None)
|
| 140 |
-
tool_call_info = self._pending_calls.get(call_id)
|
| 141 |
-
if not tool_call_info:
|
| 142 |
-
continue
|
| 143 |
-
tool_name = tool_call_info["name"]
|
| 144 |
-
args_json_str = tool_call_info["args_buf"] or "{}"
|
| 145 |
|
| 146 |
try:
|
| 147 |
tool_result = await dispatch_tool_call(tool_name, args_json_str, self.deps)
|
|
@@ -171,7 +146,11 @@ class OpenaiRealtimeHandler(AsyncStreamHandler):
|
|
| 171 |
)
|
| 172 |
|
| 173 |
if tool_name == "camera" and "b64_im" in tool_result:
|
| 174 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
await self.connection.conversation.item.create(
|
| 176 |
item={
|
| 177 |
"type": "message",
|
|
@@ -209,8 +188,6 @@ class OpenaiRealtimeHandler(AsyncStreamHandler):
|
|
| 209 |
|
| 210 |
# re synchronize the head wobble after a tool call that may have taken some time
|
| 211 |
self.deps.head_wobbler.reset()
|
| 212 |
-
# cleanup
|
| 213 |
-
self._pending_calls.pop(call_id, None)
|
| 214 |
|
| 215 |
# server error
|
| 216 |
if event.type == "error":
|
|
|
|
| 36 |
self.connection = None
|
| 37 |
self.output_queue = asyncio.Queue()
|
| 38 |
|
|
|
|
|
|
|
| 39 |
self.last_activity_time = asyncio.get_event_loop().time()
|
| 40 |
self.start_time = asyncio.get_event_loop().time()
|
| 41 |
self.is_idle_tool_call = False
|
|
|
|
| 113 |
)
|
| 114 |
|
| 115 |
# ---- tool-calling plumbing ----
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
if event.type == "response.function_call_arguments.done":
|
| 117 |
+
tool_name = getattr(event, "name", None)
|
| 118 |
+
args_json_str = getattr(event, "arguments", None)
|
| 119 |
call_id = getattr(event, "call_id", None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
|
| 121 |
try:
|
| 122 |
tool_result = await dispatch_tool_call(tool_name, args_json_str, self.deps)
|
|
|
|
| 146 |
)
|
| 147 |
|
| 148 |
if tool_name == "camera" and "b64_im" in tool_result:
|
| 149 |
+
# use raw base64, don't json.dumps (which adds quotes)
|
| 150 |
+
b64_im = tool_result["b64_im"]
|
| 151 |
+
if not isinstance(b64_im, str):
|
| 152 |
+
logger.warning("Unexpected type for b64_im: %s", type(b64_im))
|
| 153 |
+
b64_im = str(b64_im)
|
| 154 |
await self.connection.conversation.item.create(
|
| 155 |
item={
|
| 156 |
"type": "message",
|
|
|
|
| 188 |
|
| 189 |
# re synchronize the head wobble after a tool call that may have taken some time
|
| 190 |
self.deps.head_wobbler.reset()
|
|
|
|
|
|
|
| 191 |
|
| 192 |
# server error
|
| 193 |
if event.type == "error":
|
src/reachy_mini_conversation_demo/utils.py
CHANGED
|
@@ -16,13 +16,18 @@ def parse_args():
|
|
| 16 |
help="Choose head tracker (default: None)",
|
| 17 |
)
|
| 18 |
parser.add_argument("--no-camera", default=False, action="store_true", help="Disable camera usage")
|
|
|
|
| 19 |
parser.add_argument("--gradio", default=False, action="store_true", help="Open gradio interface")
|
| 20 |
parser.add_argument("--debug", default=False, action="store_true", help="Enable debug logging")
|
| 21 |
return parser.parse_args()
|
| 22 |
|
| 23 |
|
| 24 |
def handle_vision_stuff(args, current_robot):
|
| 25 |
-
"""Initialize camera, head tracker, camera worker, and vision manager.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
camera_worker = None
|
| 27 |
head_tracker = None
|
| 28 |
vision_manager = None
|
|
@@ -40,8 +45,11 @@ def handle_vision_stuff(args, current_robot):
|
|
| 40 |
# Initialize camera worker
|
| 41 |
camera_worker = CameraWorker(current_robot, head_tracker)
|
| 42 |
|
| 43 |
-
# Initialize vision manager
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
return camera_worker, head_tracker, vision_manager
|
| 47 |
|
|
|
|
| 16 |
help="Choose head tracker (default: None)",
|
| 17 |
)
|
| 18 |
parser.add_argument("--no-camera", default=False, action="store_true", help="Disable camera usage")
|
| 19 |
+
parser.add_argument("--local-vision", default=False, action="store_true", help="Use local vision model instead of gpt-realtime vision")
|
| 20 |
parser.add_argument("--gradio", default=False, action="store_true", help="Open gradio interface")
|
| 21 |
parser.add_argument("--debug", default=False, action="store_true", help="Enable debug logging")
|
| 22 |
return parser.parse_args()
|
| 23 |
|
| 24 |
|
| 25 |
def handle_vision_stuff(args, current_robot):
|
| 26 |
+
"""Initialize camera, head tracker, camera worker, and vision manager.
|
| 27 |
+
|
| 28 |
+
By default, vision is handled by gpt-realtime model when camera tool is used.
|
| 29 |
+
If --local-vision flag is used, a local vision model will process images periodically.
|
| 30 |
+
"""
|
| 31 |
camera_worker = None
|
| 32 |
head_tracker = None
|
| 33 |
vision_manager = None
|
|
|
|
| 45 |
# Initialize camera worker
|
| 46 |
camera_worker = CameraWorker(current_robot, head_tracker)
|
| 47 |
|
| 48 |
+
# Initialize vision manager only if local vision is requested
|
| 49 |
+
if args.local_vision:
|
| 50 |
+
vision_manager = initialize_vision_manager(camera_worker)
|
| 51 |
+
else:
|
| 52 |
+
logging.getLogger(__name__).info("Using gpt-realtime for vision (default). Use --local-vision for local processing.")
|
| 53 |
|
| 54 |
return camera_worker, head_tracker, vision_manager
|
| 55 |
|
src/reachy_mini_conversation_demo/vision/processors.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
import os
|
| 2 |
import time
|
| 3 |
import base64
|
| 4 |
-
import asyncio
|
| 5 |
import logging
|
| 6 |
import threading
|
| 7 |
from typing import Any, Dict, Optional
|
|
@@ -214,44 +213,57 @@ class VisionManager:
|
|
| 214 |
self.processor = VisionProcessor(self.vision_config)
|
| 215 |
|
| 216 |
self._last_processed_time = 0
|
|
|
|
|
|
|
| 217 |
|
| 218 |
# Initialize processor
|
| 219 |
if not self.processor.initialize():
|
| 220 |
logger.error("Failed to initialize vision processor")
|
| 221 |
raise RuntimeError("Vision processor initialization failed")
|
| 222 |
|
| 223 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
"""Vision processing loop (runs in separate thread)."""
|
| 225 |
-
while not
|
| 226 |
try:
|
| 227 |
current_time = time.time()
|
| 228 |
|
| 229 |
if current_time - self._last_processed_time >= self.vision_interval:
|
| 230 |
frame = self.camera.get_latest_frame()
|
| 231 |
if frame is not None:
|
| 232 |
-
description =
|
| 233 |
-
|
| 234 |
-
frame, "Briefly describe what you see in one sentence."
|
| 235 |
-
)
|
| 236 |
)
|
| 237 |
|
| 238 |
# Only update if we got a valid response
|
| 239 |
if description and not description.startswith(("Vision", "Failed", "Error")):
|
| 240 |
self._last_processed_time = current_time
|
| 241 |
-
|
| 242 |
-
logger.info(f"Vision update: {description}")
|
| 243 |
else:
|
| 244 |
logger.warning(f"Invalid vision response: {description}")
|
| 245 |
|
| 246 |
-
|
| 247 |
|
| 248 |
except Exception:
|
| 249 |
logger.exception("Vision processing loop error")
|
| 250 |
-
|
| 251 |
|
| 252 |
logger.info("Vision loop finished")
|
| 253 |
|
| 254 |
-
|
| 255 |
"""Get comprehensive status information."""
|
| 256 |
return {
|
| 257 |
"last_processed": self._last_processed_time,
|
|
|
|
| 1 |
import os
|
| 2 |
import time
|
| 3 |
import base64
|
|
|
|
| 4 |
import logging
|
| 5 |
import threading
|
| 6 |
from typing import Any, Dict, Optional
|
|
|
|
| 213 |
self.processor = VisionProcessor(self.vision_config)
|
| 214 |
|
| 215 |
self._last_processed_time = 0
|
| 216 |
+
self._stop_event = threading.Event()
|
| 217 |
+
self._thread: Optional[threading.Thread] = None
|
| 218 |
|
| 219 |
# Initialize processor
|
| 220 |
if not self.processor.initialize():
|
| 221 |
logger.error("Failed to initialize vision processor")
|
| 222 |
raise RuntimeError("Vision processor initialization failed")
|
| 223 |
|
| 224 |
+
def start(self) -> None:
|
| 225 |
+
"""Start the vision processing loop in a thread."""
|
| 226 |
+
self._stop_event.clear()
|
| 227 |
+
self._thread = threading.Thread(target=self._working_loop, daemon=True)
|
| 228 |
+
self._thread.start()
|
| 229 |
+
logger.info("Local vision processing started")
|
| 230 |
+
|
| 231 |
+
def stop(self) -> None:
|
| 232 |
+
"""Stop the vision processing loop."""
|
| 233 |
+
self._stop_event.set()
|
| 234 |
+
if self._thread is not None:
|
| 235 |
+
self._thread.join()
|
| 236 |
+
logger.info("Local vision processing stopped")
|
| 237 |
+
|
| 238 |
+
def _working_loop(self) -> None:
|
| 239 |
"""Vision processing loop (runs in separate thread)."""
|
| 240 |
+
while not self._stop_event.is_set():
|
| 241 |
try:
|
| 242 |
current_time = time.time()
|
| 243 |
|
| 244 |
if current_time - self._last_processed_time >= self.vision_interval:
|
| 245 |
frame = self.camera.get_latest_frame()
|
| 246 |
if frame is not None:
|
| 247 |
+
description = self.processor.process_image(
|
| 248 |
+
frame, "Briefly describe what you see in one sentence."
|
|
|
|
|
|
|
| 249 |
)
|
| 250 |
|
| 251 |
# Only update if we got a valid response
|
| 252 |
if description and not description.startswith(("Vision", "Failed", "Error")):
|
| 253 |
self._last_processed_time = current_time
|
| 254 |
+
logger.debug(f"Vision update: {description}")
|
|
|
|
| 255 |
else:
|
| 256 |
logger.warning(f"Invalid vision response: {description}")
|
| 257 |
|
| 258 |
+
time.sleep(1.0) # Check every second
|
| 259 |
|
| 260 |
except Exception:
|
| 261 |
logger.exception("Vision processing loop error")
|
| 262 |
+
time.sleep(5.0) # Longer sleep on error
|
| 263 |
|
| 264 |
logger.info("Vision loop finished")
|
| 265 |
|
| 266 |
+
def get_status(self) -> Dict[str, Any]:
|
| 267 |
"""Get comprehensive status information."""
|
| 268 |
return {
|
| 269 |
"last_processed": self._last_processed_time,
|
uv.lock
CHANGED
|
@@ -3513,7 +3513,7 @@ wheels = [
|
|
| 3513 |
[[package]]
|
| 3514 |
name = "reachy-mini"
|
| 3515 |
version = "0.1.0"
|
| 3516 |
-
source = { git = "ssh://git@github.com/pollen-robotics/reachy_mini?rev=develop#
|
| 3517 |
dependencies = [
|
| 3518 |
{ name = "aiohttp" },
|
| 3519 |
{ name = "asgiref" },
|
|
@@ -3543,7 +3543,6 @@ dependencies = [
|
|
| 3543 |
{ name = "fastrtc" },
|
| 3544 |
{ name = "gradio" },
|
| 3545 |
{ name = "huggingface-hub" },
|
| 3546 |
-
{ name = "num2words" },
|
| 3547 |
{ name = "openai" },
|
| 3548 |
{ name = "opencv-python" },
|
| 3549 |
{ name = "python-dotenv" },
|
|
@@ -3555,12 +3554,14 @@ dependencies = [
|
|
| 3555 |
[package.optional-dependencies]
|
| 3556 |
all-vision = [
|
| 3557 |
{ name = "mediapipe" },
|
|
|
|
| 3558 |
{ name = "supervision" },
|
| 3559 |
{ name = "torch" },
|
| 3560 |
{ name = "transformers" },
|
| 3561 |
{ name = "ultralytics" },
|
| 3562 |
]
|
| 3563 |
local-vision = [
|
|
|
|
| 3564 |
{ name = "torch" },
|
| 3565 |
{ name = "transformers" },
|
| 3566 |
]
|
|
@@ -3585,7 +3586,7 @@ requires-dist = [
|
|
| 3585 |
{ name = "gradio", specifier = ">=5.49.0" },
|
| 3586 |
{ name = "huggingface-hub", specifier = ">=0.34.4" },
|
| 3587 |
{ name = "mediapipe", marker = "extra == 'mediapipe-vision'", specifier = ">=0.10.14" },
|
| 3588 |
-
{ name = "num2words" },
|
| 3589 |
{ name = "openai", specifier = ">=2.1" },
|
| 3590 |
{ name = "opencv-python", specifier = ">=4.12.0.88" },
|
| 3591 |
{ name = "python-dotenv" },
|
|
@@ -4531,7 +4532,7 @@ wheels = [
|
|
| 4531 |
|
| 4532 |
[[package]]
|
| 4533 |
name = "ultralytics"
|
| 4534 |
-
version = "8.3.
|
| 4535 |
source = { registry = "https://pypi.org/simple" }
|
| 4536 |
dependencies = [
|
| 4537 |
{ name = "matplotlib" },
|
|
@@ -4547,9 +4548,9 @@ dependencies = [
|
|
| 4547 |
{ name = "torchvision" },
|
| 4548 |
{ name = "ultralytics-thop" },
|
| 4549 |
]
|
| 4550 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 4551 |
wheels = [
|
| 4552 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4553 |
]
|
| 4554 |
|
| 4555 |
[[package]]
|
|
@@ -4633,102 +4634,105 @@ wheels = [
|
|
| 4633 |
|
| 4634 |
[[package]]
|
| 4635 |
name = "watchfiles"
|
| 4636 |
-
version = "1.1.
|
| 4637 |
source = { registry = "https://pypi.org/simple" }
|
| 4638 |
dependencies = [
|
| 4639 |
{ name = "anyio" },
|
| 4640 |
]
|
| 4641 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 4642 |
-
wheels = [
|
| 4643 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4644 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4645 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4646 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4647 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4648 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4649 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4650 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4651 |
-
{ url = "https://files.pythonhosted.org/packages/a5/
|
| 4652 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4653 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4654 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4655 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4656 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4657 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4658 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4659 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4660 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4661 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4662 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4663 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4664 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4665 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4666 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4667 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4668 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4669 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4670 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4671 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4672 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4673 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4674 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4675 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4676 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4677 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4678 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4679 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4680 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4681 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4682 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4683 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4684 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4685 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4686 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4687 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4688 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4689 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4690 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4691 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4692 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4693 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4694 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4695 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4696 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4697 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4698 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4699 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4700 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4701 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4702 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4703 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4704 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4705 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4706 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4707 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4708 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4709 |
-
{ url = "https://files.pythonhosted.org/packages/84/
|
| 4710 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4711 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4712 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4713 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4714 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4715 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4716 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4717 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4718 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4719 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4720 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4721 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4722 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4723 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4724 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4725 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4726 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4727 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4728 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4729 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4730 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 4731 |
-
{ url = "https://files.pythonhosted.org/packages/
|
|
|
|
|
|
|
|
|
|
| 4732 |
]
|
| 4733 |
|
| 4734 |
[[package]]
|
|
|
|
| 3513 |
[[package]]
|
| 3514 |
name = "reachy-mini"
|
| 3515 |
version = "0.1.0"
|
| 3516 |
+
source = { git = "ssh://git@github.com/pollen-robotics/reachy_mini?rev=develop#2d1e16e4dd3084241c56dc826ccab84ddf58fda4" }
|
| 3517 |
dependencies = [
|
| 3518 |
{ name = "aiohttp" },
|
| 3519 |
{ name = "asgiref" },
|
|
|
|
| 3543 |
{ name = "fastrtc" },
|
| 3544 |
{ name = "gradio" },
|
| 3545 |
{ name = "huggingface-hub" },
|
|
|
|
| 3546 |
{ name = "openai" },
|
| 3547 |
{ name = "opencv-python" },
|
| 3548 |
{ name = "python-dotenv" },
|
|
|
|
| 3554 |
[package.optional-dependencies]
|
| 3555 |
all-vision = [
|
| 3556 |
{ name = "mediapipe" },
|
| 3557 |
+
{ name = "num2words" },
|
| 3558 |
{ name = "supervision" },
|
| 3559 |
{ name = "torch" },
|
| 3560 |
{ name = "transformers" },
|
| 3561 |
{ name = "ultralytics" },
|
| 3562 |
]
|
| 3563 |
local-vision = [
|
| 3564 |
+
{ name = "num2words" },
|
| 3565 |
{ name = "torch" },
|
| 3566 |
{ name = "transformers" },
|
| 3567 |
]
|
|
|
|
| 3586 |
{ name = "gradio", specifier = ">=5.49.0" },
|
| 3587 |
{ name = "huggingface-hub", specifier = ">=0.34.4" },
|
| 3588 |
{ name = "mediapipe", marker = "extra == 'mediapipe-vision'", specifier = ">=0.10.14" },
|
| 3589 |
+
{ name = "num2words", marker = "extra == 'local-vision'" },
|
| 3590 |
{ name = "openai", specifier = ">=2.1" },
|
| 3591 |
{ name = "opencv-python", specifier = ">=4.12.0.88" },
|
| 3592 |
{ name = "python-dotenv" },
|
|
|
|
| 4532 |
|
| 4533 |
[[package]]
|
| 4534 |
name = "ultralytics"
|
| 4535 |
+
version = "8.3.214"
|
| 4536 |
source = { registry = "https://pypi.org/simple" }
|
| 4537 |
dependencies = [
|
| 4538 |
{ name = "matplotlib" },
|
|
|
|
| 4548 |
{ name = "torchvision" },
|
| 4549 |
{ name = "ultralytics-thop" },
|
| 4550 |
]
|
| 4551 |
+
sdist = { url = "https://files.pythonhosted.org/packages/81/86/be70e1e3f30beb794c330ce070f1349f060acbaaf3a8f0345bb70cc8baa2/ultralytics-8.3.214.tar.gz", hash = "sha256:39f01fb0c3e36ba2c47c63b119d8d15139e3537d490c1ed274731b065f711137", size = 921124, upload-time = "2025-10-14T12:51:53.911Z" }
|
| 4552 |
wheels = [
|
| 4553 |
+
{ url = "https://files.pythonhosted.org/packages/10/18/de85d7c454ef6939a850118a5e10b21c9b7adafb38388ecfb104c87c296d/ultralytics-8.3.214-py3-none-any.whl", hash = "sha256:17d6e335b67c52d484d6b96cc73c940d408c7861d998c8051517340954bfcb2d", size = 1074155, upload-time = "2025-10-14T12:51:51.335Z" },
|
| 4554 |
]
|
| 4555 |
|
| 4556 |
[[package]]
|
|
|
|
| 4634 |
|
| 4635 |
[[package]]
|
| 4636 |
name = "watchfiles"
|
| 4637 |
+
version = "1.1.1"
|
| 4638 |
source = { registry = "https://pypi.org/simple" }
|
| 4639 |
dependencies = [
|
| 4640 |
{ name = "anyio" },
|
| 4641 |
]
|
| 4642 |
+
sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" }
|
| 4643 |
+
wheels = [
|
| 4644 |
+
{ url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318, upload-time = "2025-10-14T15:04:18.753Z" },
|
| 4645 |
+
{ url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478, upload-time = "2025-10-14T15:04:20.297Z" },
|
| 4646 |
+
{ url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894, upload-time = "2025-10-14T15:04:21.527Z" },
|
| 4647 |
+
{ url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065, upload-time = "2025-10-14T15:04:22.795Z" },
|
| 4648 |
+
{ url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377, upload-time = "2025-10-14T15:04:24.138Z" },
|
| 4649 |
+
{ url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837, upload-time = "2025-10-14T15:04:25.057Z" },
|
| 4650 |
+
{ url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456, upload-time = "2025-10-14T15:04:26.497Z" },
|
| 4651 |
+
{ url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614, upload-time = "2025-10-14T15:04:27.539Z" },
|
| 4652 |
+
{ url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690, upload-time = "2025-10-14T15:04:28.495Z" },
|
| 4653 |
+
{ url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459, upload-time = "2025-10-14T15:04:29.491Z" },
|
| 4654 |
+
{ url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663, upload-time = "2025-10-14T15:04:30.435Z" },
|
| 4655 |
+
{ url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453, upload-time = "2025-10-14T15:04:31.53Z" },
|
| 4656 |
+
{ url = "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", size = 406529, upload-time = "2025-10-14T15:04:32.899Z" },
|
| 4657 |
+
{ url = "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741", size = 394384, upload-time = "2025-10-14T15:04:33.761Z" },
|
| 4658 |
+
{ url = "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", size = 448789, upload-time = "2025-10-14T15:04:34.679Z" },
|
| 4659 |
+
{ url = "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", size = 460521, upload-time = "2025-10-14T15:04:35.963Z" },
|
| 4660 |
+
{ url = "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", size = 488722, upload-time = "2025-10-14T15:04:37.091Z" },
|
| 4661 |
+
{ url = "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", size = 596088, upload-time = "2025-10-14T15:04:38.39Z" },
|
| 4662 |
+
{ url = "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", size = 472923, upload-time = "2025-10-14T15:04:39.666Z" },
|
| 4663 |
+
{ url = "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", size = 456080, upload-time = "2025-10-14T15:04:40.643Z" },
|
| 4664 |
+
{ url = "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", size = 629432, upload-time = "2025-10-14T15:04:41.789Z" },
|
| 4665 |
+
{ url = "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", size = 623046, upload-time = "2025-10-14T15:04:42.718Z" },
|
| 4666 |
+
{ url = "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl", hash = "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", size = 271473, upload-time = "2025-10-14T15:04:43.624Z" },
|
| 4667 |
+
{ url = "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", size = 287598, upload-time = "2025-10-14T15:04:44.516Z" },
|
| 4668 |
+
{ url = "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", size = 277210, upload-time = "2025-10-14T15:04:45.883Z" },
|
| 4669 |
+
{ url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" },
|
| 4670 |
+
{ url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" },
|
| 4671 |
+
{ url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" },
|
| 4672 |
+
{ url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" },
|
| 4673 |
+
{ url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" },
|
| 4674 |
+
{ url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" },
|
| 4675 |
+
{ url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" },
|
| 4676 |
+
{ url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" },
|
| 4677 |
+
{ url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" },
|
| 4678 |
+
{ url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" },
|
| 4679 |
+
{ url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" },
|
| 4680 |
+
{ url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" },
|
| 4681 |
+
{ url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" },
|
| 4682 |
+
{ url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" },
|
| 4683 |
+
{ url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" },
|
| 4684 |
+
{ url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" },
|
| 4685 |
+
{ url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" },
|
| 4686 |
+
{ url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" },
|
| 4687 |
+
{ url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" },
|
| 4688 |
+
{ url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" },
|
| 4689 |
+
{ url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" },
|
| 4690 |
+
{ url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" },
|
| 4691 |
+
{ url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" },
|
| 4692 |
+
{ url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" },
|
| 4693 |
+
{ url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" },
|
| 4694 |
+
{ url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" },
|
| 4695 |
+
{ url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" },
|
| 4696 |
+
{ url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" },
|
| 4697 |
+
{ url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" },
|
| 4698 |
+
{ url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" },
|
| 4699 |
+
{ url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" },
|
| 4700 |
+
{ url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" },
|
| 4701 |
+
{ url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" },
|
| 4702 |
+
{ url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" },
|
| 4703 |
+
{ url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" },
|
| 4704 |
+
{ url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" },
|
| 4705 |
+
{ url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" },
|
| 4706 |
+
{ url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" },
|
| 4707 |
+
{ url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" },
|
| 4708 |
+
{ url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" },
|
| 4709 |
+
{ url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" },
|
| 4710 |
+
{ url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" },
|
| 4711 |
+
{ url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" },
|
| 4712 |
+
{ url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" },
|
| 4713 |
+
{ url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" },
|
| 4714 |
+
{ url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" },
|
| 4715 |
+
{ url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" },
|
| 4716 |
+
{ url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" },
|
| 4717 |
+
{ url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" },
|
| 4718 |
+
{ url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" },
|
| 4719 |
+
{ url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" },
|
| 4720 |
+
{ url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" },
|
| 4721 |
+
{ url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" },
|
| 4722 |
+
{ url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" },
|
| 4723 |
+
{ url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" },
|
| 4724 |
+
{ url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" },
|
| 4725 |
+
{ url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" },
|
| 4726 |
+
{ url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" },
|
| 4727 |
+
{ url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" },
|
| 4728 |
+
{ url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611, upload-time = "2025-10-14T15:06:05.809Z" },
|
| 4729 |
+
{ url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889, upload-time = "2025-10-14T15:06:07.035Z" },
|
| 4730 |
+
{ url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616, upload-time = "2025-10-14T15:06:08.072Z" },
|
| 4731 |
+
{ url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413, upload-time = "2025-10-14T15:06:09.209Z" },
|
| 4732 |
+
{ url = "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", size = 408250, upload-time = "2025-10-14T15:06:10.264Z" },
|
| 4733 |
+
{ url = "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", size = 396117, upload-time = "2025-10-14T15:06:11.28Z" },
|
| 4734 |
+
{ url = "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", size = 450493, upload-time = "2025-10-14T15:06:12.321Z" },
|
| 4735 |
+
{ url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" },
|
| 4736 |
]
|
| 4737 |
|
| 4738 |
[[package]]
|