From 3d6dfc864dd8778fbdac7b3a09bbfff8ab86c78f Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Wed, 13 Aug 2025 11:43:35 -0700 Subject: [PATCH 01/72] clip: terminate processes in clip() instead of in main() (#35984) * terminate processes in clip() instead of in main() * context manager for proc --- common/run.py | 15 ++++++++ tools/clip/run.py | 87 ++++++++++++++++++----------------------------- 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/common/run.py b/common/run.py index 06deb6388d..75395ead1f 100644 --- a/common/run.py +++ b/common/run.py @@ -1,4 +1,6 @@ import subprocess +from contextlib import contextmanager +from subprocess import Popen, PIPE, TimeoutExpired def run_cmd(cmd: list[str], cwd=None, env=None) -> str: @@ -11,3 +13,16 @@ def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> st except subprocess.CalledProcessError: return default + +@contextmanager +def managed_proc(cmd: list[str], env: dict[str, str]): + proc = Popen(cmd, env=env, stdout=PIPE, stderr=PIPE) + try: + yield proc + finally: + if proc.poll() is None: + proc.terminate() + try: + proc.wait(timeout=5) + except TimeoutExpired: + proc.kill() diff --git a/tools/clip/run.py b/tools/clip/run.py index 7920751447..8fa0e8eda3 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -import atexit import logging import os import platform @@ -11,13 +10,14 @@ from argparse import ArgumentParser, ArgumentTypeError from collections.abc import Sequence from pathlib import Path from random import randint -from subprocess import Popen, PIPE +from subprocess import Popen from typing import Literal from cereal.messaging import SubMaster from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params, UnknownKeyName from openpilot.common.prefix import OpenpilotPrefix +from openpilot.common.run import managed_proc from openpilot.tools.lib.route import Route from openpilot.tools.lib.logreader import LogReader @@ -38,22 +38,23 @@ UI = str(Path(BASEDIR, 'selfdrive/ui/ui').resolve()) logger = logging.getLogger('clip.py') -def check_for_failure(proc: Popen): - exit_code = proc.poll() - if exit_code is not None and exit_code != 0: - cmd = str(proc.args) - if isinstance(proc.args, str): - cmd = proc.args - elif isinstance(proc.args, Sequence): - cmd = str(proc.args[0]) - msg = f'{cmd} failed, exit code {exit_code}' - logger.error(msg) - stdout, stderr = proc.communicate() - if stdout: - logger.error(stdout.decode()) - if stderr: - logger.error(stderr.decode()) - raise ChildProcessError(msg) +def check_for_failure(procs: list[Popen]): + for proc in procs: + exit_code = proc.poll() + if exit_code is not None and exit_code != 0: + cmd = str(proc.args) + if isinstance(proc.args, str): + cmd = proc.args + elif isinstance(proc.args, Sequence): + cmd = str(proc.args[0]) + msg = f'{cmd} failed, exit code {exit_code}' + logger.error(msg) + stdout, stderr = proc.communicate() + if stdout: + logger.error(stdout.decode()) + if stderr: + logger.error(stderr.decode()) + raise ChildProcessError(msg) def escape_ffmpeg_text(value: str): @@ -137,10 +138,6 @@ def populate_car_params(lr: LogReader): logger.debug('persisted CarParams') -def start_proc(args: list[str], env: dict[str, str]): - return Popen(args, env=env, stdout=PIPE, stderr=PIPE) - - def validate_env(parser: ArgumentParser): if platform.system() not in ['Linux']: parser.exit(1, f'clip.py: error: {platform.system()} is not a supported operating system\n') @@ -176,8 +173,7 @@ def wait_for_frames(procs: list[Popen]): while no_frames_drawn: sm.update() no_frames_drawn = sm['uiDebug'].drawTimeMillis == 0. - for proc in procs: - check_for_failure(proc) + check_for_failure(procs) def clip( @@ -253,35 +249,22 @@ def clip( with OpenpilotPrefix(prefix, shared_download_cache=True): populate_car_params(lr) - env = os.environ.copy() env['DISPLAY'] = display - xvfb_proc = start_proc(xvfb_cmd, env) - atexit.register(lambda: xvfb_proc.terminate()) - ui_proc = start_proc(ui_cmd, env) - atexit.register(lambda: ui_proc.terminate()) - replay_proc = start_proc(replay_cmd, env) - atexit.register(lambda: replay_proc.terminate()) - procs = [replay_proc, ui_proc, xvfb_proc] - - logger.info('waiting for replay to begin (loading segments, may take a while)...') - wait_for_frames(procs) - - logger.debug(f'letting UI warm up ({SECONDS_TO_WARM}s)...') - time.sleep(SECONDS_TO_WARM) - for proc in procs: - check_for_failure(proc) - - ffmpeg_proc = start_proc(ffmpeg_cmd, env) - procs.append(ffmpeg_proc) - atexit.register(lambda: ffmpeg_proc.terminate()) - - logger.info(f'recording in progress ({duration}s)...') - ffmpeg_proc.wait(duration + PROC_WAIT_SECONDS) - for proc in procs: - check_for_failure(proc) - logger.info(f'recording complete: {Path(out).resolve()}') + with managed_proc(xvfb_cmd, env) as xvfb_proc, managed_proc(ui_cmd, env) as ui_proc, managed_proc(replay_cmd, env) as replay_proc: + procs = [xvfb_proc, ui_proc, replay_proc] + logger.info('waiting for replay to begin (loading segments, may take a while)...') + wait_for_frames(procs) + logger.debug(f'letting UI warm up ({SECONDS_TO_WARM}s)...') + time.sleep(SECONDS_TO_WARM) + check_for_failure(procs) + with managed_proc(ffmpeg_cmd, env) as ffmpeg_proc: + procs.append(ffmpeg_proc) + logger.info(f'recording in progress ({duration}s)...') + ffmpeg_proc.wait(duration + PROC_WAIT_SECONDS) + check_for_failure(procs) + logger.info(f'recording complete: {Path(out).resolve()}') def main(): @@ -319,9 +302,7 @@ def main(): logger.exception('interrupted by user', exc_info=e) except Exception as e: logger.exception('encountered error', exc_info=e) - finally: - atexit._run_exitfuncs() - sys.exit(exit_code) + sys.exit(exit_code) if __name__ == '__main__': From be934b3881ba136dbb32a4a9114cc92dfa5d0359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Wed, 13 Aug 2025 11:43:50 -0700 Subject: [PATCH 02/72] fancontroller: remove weird minus (#35983) * fancontroller: remove weird minus * another minus --- system/hardware/fan_controller.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/system/hardware/fan_controller.py b/system/hardware/fan_controller.py index 7d5bec0509..4c7adc0a3e 100755 --- a/system/hardware/fan_controller.py +++ b/system/hardware/fan_controller.py @@ -21,16 +21,16 @@ class TiciFanController(BaseFanController): self.controller = PIDController(k_p=0, k_i=4e-3, k_f=1, rate=(1 / DT_HW)) def update(self, cur_temp: float, ignition: bool) -> int: - self.controller.neg_limit = -(100 if ignition else 30) - self.controller.pos_limit = -(30 if ignition else 0) + self.controller.pos_limit = 100 if ignition else 30 + self.controller.neg_limit = 30 if ignition else 0 if ignition != self.last_ignition: self.controller.reset() - error = 75 - cur_temp - fan_pwr_out = -int(self.controller.update( + error = cur_temp - 75 + fan_pwr_out = int(self.controller.update( error=error, - feedforward=np.interp(cur_temp, [60.0, 100.0], [0, -100]) + feedforward=np.interp(cur_temp, [60.0, 100.0], [0, 100]) )) self.last_ignition = ignition From a2a385336e2e8e2d9ea468e9fd075ea94b1826d2 Mon Sep 17 00:00:00 2001 From: Alexandre Nobuharu Sato <66435071+AlexandreSato@users.noreply.github.com> Date: Wed, 13 Aug 2025 19:20:58 -0300 Subject: [PATCH 03/72] Multilang: update pt-BR translation (#35971) Multilang: update pt-BR translations --- selfdrive/ui/translations/main_pt-BR.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index e16872c758..403faf226f 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -504,7 +504,7 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - + openpilot detectou atuação excessiva de %1 na sua última condução. Entre em contato com o suporte em https://comma.ai/support e compartilhe o Dongle ID do seu dispositivo para solução de problemas. @@ -1145,13 +1145,15 @@ Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo a Record Audio Feedback with LKAS button - + Gravar feedback de áudio com o botão LKAS Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. Note that this feature is only compatible with select cars. - + Pressione o botão LKAS para gravar e compartilhar feedback de direção com a equipe do openpilot. Quando esta opção estiver desativada, o botão funcionará como um botão de marcador. O evento será destacado no comma connect e o segmento será preservado no armazenamento do seu dispositivo. + +Observe que este recurso é compatível apenas com alguns modelos de carros. From 3f830827b2e69fb25054018a5592b5c270ddba08 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 13 Aug 2025 16:07:12 -0700 Subject: [PATCH 04/72] setup: new flow (#35960) * start * remove * path * fix * prepare * url * format * better * better * consist * check * not real * ref * simpler * fix * fix * more * more * path * clean * line * progress * fast * no * ori * flag * remove * install * line * wait time * wait install * Revert "wait time" This reverts commit 14f750971c3d19b93e4609e9344cb3a8ce9175f4. * move * fix * install * universal service resources * fix * safer * this is stupid * time * cleaner * comment --- selfdrive/ui/installer/installer.cc | 46 ++++++++++++++------ system/ui/setup.py | 66 +++++++++++++++++++++++------ 2 files changed, 85 insertions(+), 27 deletions(-) diff --git a/selfdrive/ui/installer/installer.cc b/selfdrive/ui/installer/installer.cc index 7326e089ab..a9b84b5c06 100644 --- a/selfdrive/ui/installer/installer.cc +++ b/selfdrive/ui/installer/installer.cc @@ -24,11 +24,13 @@ const std::string BRANCH_STR = get_str(BRANCH "? #define GIT_SSH_URL "git@github.com:commaai/openpilot.git" #define CONTINUE_PATH "/data/continue.sh" -const std::string CACHE_PATH = "/data/openpilot.cache"; +const std::string INSTALL_PATH = "/data/openpilot"; +const std::string VALID_CACHE_PATH = "/data/.openpilot_cache"; -#define INSTALL_PATH "/data/openpilot" #define TMP_INSTALL_PATH "/data/tmppilot" +const int FONT_SIZE = 120; + extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_start"); extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_end"); extern const uint8_t inter_ttf[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_start"); @@ -41,6 +43,16 @@ void run(const char* cmd) { assert(err == 0); } +void finishInstall() { + BeginDrawing(); + ClearBackground(BLACK); + const char *m = "Finishing install..."; + int text_width = MeasureText(m, FONT_SIZE); + DrawTextEx(font, m, (Vector2){(float)(GetScreenWidth() - text_width)/2 + FONT_SIZE, (float)(GetScreenHeight() - FONT_SIZE)/2}, FONT_SIZE, 0, WHITE); + EndDrawing(); + util::sleep_for(60 * 1000); +} + void renderProgress(int progress) { BeginDrawing(); ClearBackground(BLACK); @@ -62,11 +74,11 @@ int doInstall() { } // cleanup previous install attempts - run("rm -rf " TMP_INSTALL_PATH " " INSTALL_PATH); + run("rm -rf " TMP_INSTALL_PATH); // do the install - if (util::file_exists(CACHE_PATH)) { - return cachedFetch(CACHE_PATH); + if (util::file_exists(INSTALL_PATH) && util::file_exists(VALID_CACHE_PATH)) { + return cachedFetch(INSTALL_PATH); } else { return freshClone(); } @@ -135,7 +147,9 @@ void cloneFinished(int exitCode) { run("git submodule update --init"); // move into place - run("mv " TMP_INSTALL_PATH " " INSTALL_PATH); + run(("rm -f " + VALID_CACHE_PATH).c_str()); + run(("rm -rf " + INSTALL_PATH).c_str()); + run(util::string_format("mv %s %s", TMP_INSTALL_PATH, INSTALL_PATH.c_str()).c_str()); #ifdef INTERNAL run("mkdir -p /data/params/d/"); @@ -153,9 +167,9 @@ void cloneFinished(int exitCode) { param << value; param.close(); } - run("cd " INSTALL_PATH " && " + run(("cd " + INSTALL_PATH + " && " "git remote set-url origin --push " GIT_SSH_URL " && " - "git config --replace-all remote.origin.fetch \"+refs/heads/*:refs/remotes/origin/*\""); + "git config --replace-all remote.origin.fetch \"+refs/heads/*:refs/remotes/origin/*\"").c_str()); #endif // write continue.sh @@ -171,16 +185,22 @@ void cloneFinished(int exitCode) { run("mv /data/continue.sh.new " CONTINUE_PATH); // wait for the installed software's UI to take over - util::sleep_for(60 * 1000); + finishInstall(); } int main(int argc, char *argv[]) { InitWindow(2160, 1080, "Installer"); - font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, 120, NULL, 0); + font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, FONT_SIZE, NULL, 0); SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); - renderProgress(0); - int result = doInstall(); - cloneFinished(result); + + if (util::file_exists(CONTINUE_PATH)) { + finishInstall(); + } else { + renderProgress(0); + int result = doInstall(); + cloneFinished(result); + } + CloseWindow(); UnloadFont(font); return 0; diff --git a/system/ui/setup.py b/system/ui/setup.py index d675e868ff..72718b5d6d 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -6,9 +6,12 @@ import time import urllib.request from urllib.parse import urlparse from enum import IntEnum +import shutil + import pyray as rl from cereal import log +from openpilot.common.run import run_cmd from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import Widget @@ -30,6 +33,19 @@ BUTTON_SPACING = 50 OPENPILOT_URL = "https://openpilot.comma.ai" USER_AGENT = f"AGNOSSetup-{HARDWARE.get_os_version()}" +CONTINUE_PATH = "/data/continue.sh" +TMP_CONTINUE_PATH = "/data/continue.sh.new" +INSTALL_PATH = "/data/openpilot" +VALID_CACHE_PATH = "/data/.openpilot_cache" +INSTALLER_SOURCE_PATH = "/usr/comma/installer" +INSTALLER_DESTINATION_PATH = "/tmp/installer" +INSTALLER_URL_PATH = "/tmp/installer_url" + +CONTINUE = """#!/usr/bin/env bash + +cd /data/openpilot +exec ./launch_openpilot.sh +""" class SetupState(IntEnum): LOW_VOLTAGE = 0 @@ -136,21 +152,19 @@ class Setup(Widget): self.state = SetupState.SOFTWARE_SELECTION def _custom_software_warning_continue_button_callback(self): - self.state = SetupState.CUSTOM_SOFTWARE + self.state = SetupState.NETWORK_SETUP + self.stop_network_check_thread.clear() + self.start_network_check() def _getting_started_button_callback(self): - self.state = SetupState.NETWORK_SETUP - self.stop_network_check_thread.clear() - self.start_network_check() + self.state = SetupState.SOFTWARE_SELECTION def _software_selection_back_button_callback(self): - self.state = SetupState.NETWORK_SETUP - self.stop_network_check_thread.clear() - self.start_network_check() + self.state = SetupState.GETTING_STARTED def _software_selection_continue_button_callback(self): if self._software_selection_openpilot_button.selected: - self.download(OPENPILOT_URL) + self.use_openpilot() else: self.state = SetupState.CUSTOM_SOFTWARE_WARNING @@ -158,11 +172,14 @@ class Setup(Widget): self.state = SetupState.GETTING_STARTED def _network_setup_back_button_callback(self): - self.state = SetupState.GETTING_STARTED + self.state = SetupState.SOFTWARE_SELECTION def _network_setup_continue_button_callback(self): - self.state = SetupState.SOFTWARE_SELECTION self.stop_network_check_thread.set() + if self._software_selection_openpilot_button.selected: + self.download(OPENPILOT_URL) + else: + self.state = SetupState.CUSTOM_SOFTWARE def render_low_voltage(self, rect: rl.Rectangle): rl.draw_texture(self.warning, int(rect.x + 150), int(rect.y + 110), rl.WHITE) @@ -299,6 +316,23 @@ class Setup(Widget): self.keyboard.set_title("Enter URL", "for Custom Software") gui_app.set_modal_overlay(self.keyboard, callback=handle_keyboard_result) + def use_openpilot(self): + if os.path.isdir(INSTALL_PATH) and os.path.isfile(VALID_CACHE_PATH): + os.remove(VALID_CACHE_PATH) + with open(TMP_CONTINUE_PATH, "w") as f: + f.write(CONTINUE) + run_cmd(["chmod", "+x", TMP_CONTINUE_PATH]) + shutil.move(TMP_CONTINUE_PATH, CONTINUE_PATH) + shutil.copyfile(INSTALLER_SOURCE_PATH, INSTALLER_DESTINATION_PATH) + + # give time for installer UI to take over + time.sleep(1) + gui_app.request_close() + else: + self.state = SetupState.NETWORK_SETUP + self.stop_network_check_thread.clear() + self.start_network_check() + def download(self, url: str): # autocomplete incomplete URLs if re.match("^([^/.]+)/([^/]+)$", url): @@ -316,7 +350,7 @@ class Setup(Widget): try: import tempfile - _, tmpfile = tempfile.mkstemp(prefix="installer_") + fd, tmpfile = tempfile.mkstemp(prefix="installer_") headers = {"User-Agent": USER_AGENT, "X-openpilot-serial": HARDWARE.get_serial()} req = urllib.request.Request(self.download_url, headers=headers) @@ -346,12 +380,16 @@ class Setup(Widget): self.download_failed(self.download_url, "No custom software found at this URL.") return - os.rename(tmpfile, "/tmp/installer") - os.chmod("/tmp/installer", 0o755) + # AGNOS might try to execute the installer before this process exits. + # Therefore, important to close the fd before renaming the installer. + os.close(fd) + os.rename(tmpfile, INSTALLER_DESTINATION_PATH) - with open("/tmp/installer_url", "w") as f: + with open(INSTALLER_URL_PATH, "w") as f: f.write(self.download_url) + # give time for installer UI to take over + time.sleep(5) gui_app.request_close() except Exception: From 56a89eb4fb39aed92ecec771b0771d685ff9d42c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 18:32:10 -0700 Subject: [PATCH 05/72] [bot] Update translations (#35975) Update translations Co-authored-by: Vehicle Researcher --- selfdrive/ui/translations/main_ar.ts | 105 ---------------------- selfdrive/ui/translations/main_de.ts | 105 ---------------------- selfdrive/ui/translations/main_es.ts | 107 ----------------------- selfdrive/ui/translations/main_fr.ts | 105 ---------------------- selfdrive/ui/translations/main_ja.ts | 107 ----------------------- selfdrive/ui/translations/main_ko.ts | 107 ----------------------- selfdrive/ui/translations/main_pt-BR.ts | 107 ----------------------- selfdrive/ui/translations/main_th.ts | 105 ---------------------- selfdrive/ui/translations/main_tr.ts | 105 ---------------------- selfdrive/ui/translations/main_zh-CHS.ts | 107 ----------------------- selfdrive/ui/translations/main_zh-CHT.ts | 107 ----------------------- 11 files changed, 1167 deletions(-) diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index eac3b561f7..ff2e7ab05c 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -700,111 +700,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp خرطوم الحريق - - Setup - - WARNING: Low Voltage - تحذير: الجهد منخفض - - - Power your device in a car with a harness or proceed at your own risk. - شغل جهازك في السيارة عن طريق شرطان التوصيل، أو تابع على مسؤوليتك. - - - Power off - إيقاف التشغيل - - - Continue - متابعة - - - Getting Started - البدء - - - Before we get on the road, let’s finish installation and cover some details. - قبل أن ننطلق في الطريق، دعنا ننتهي من التثبيت ونغطي بعض التفاصيل. - - - Connect to Wi-Fi - الاتصال بشبكة الواي فاي - - - Back - السابق - - - Continue without Wi-Fi - المتابعة بدون شبكة الواي فاي - - - Waiting for internet - بانتظار الاتصال بالإنترنت - - - Enter URL - أدخل رابط URL - - - for Custom Software - للبرامج المخصصة - - - Downloading... - يتم الآن التنزيل... - - - Download Failed - فشل التنزيل - - - Ensure the entered URL is valid, and the device’s internet connection is good. - تأكد من أن رابط URL الذي أدخلته صالح، وأن اتصال الجهاز بالإنترنت جيد. - - - Reboot device - إعادة التشغيل - - - Start over - البدء من جديد - - - Something went wrong. Reboot the device. - حدث خطأ ما. أعد التشغيل الجهاز. - - - No custom software found at this URL. - لم يتم العثور على برنامج خاص لعنوان URL ها. - - - Select a language - اختر لغة - - - Choose Software to Install - اختر البرنامج للتثبيت - - - openpilot - openpilot - - - Custom Software - البرمجيات المخصصة - - - WARNING: Custom Software - - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - - - SetupWidget diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index b91c0e23ce..52fe55720d 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -680,111 +680,6 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere Firehose - - Setup - - WARNING: Low Voltage - Warnung: Batteriespannung niedrig - - - Power your device in a car with a harness or proceed at your own risk. - Versorge dein Gerät über einen Kabelbaum im Auto mit Strom, oder fahre auf eigene Gefahr fort. - - - Power off - Ausschalten - - - Continue - Fortsetzen - - - Getting Started - Loslegen - - - Before we get on the road, let’s finish installation and cover some details. - Bevor wir uns auf die Straße begeben, lass uns die Installation fertigstellen und einige Details prüfen. - - - Connect to Wi-Fi - Mit WLAN verbinden - - - Back - Zurück - - - Continue without Wi-Fi - Ohne WLAN fortsetzen - - - Waiting for internet - Auf Internet warten - - - Enter URL - URL eingeben - - - for Custom Software - für spezifische Software - - - Downloading... - Herunterladen... - - - Download Failed - Herunterladen fehlgeschlagen - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Stelle sicher, dass die eingegebene URL korrekt ist und dein Gerät eine stabile Internetverbindung hat. - - - Reboot device - Gerät neustarten - - - Start over - Von neuem beginnen - - - No custom software found at this URL. - Keine benutzerdefinierte Software unter dieser URL gefunden. - - - Something went wrong. Reboot the device. - Etwas ist schiefgelaufen. Starte das Gerät neu. - - - Select a language - Sprache wählen - - - Choose Software to Install - Wähle die zu installierende Software - - - openpilot - openpilot - - - Custom Software - Benutzerdefinierte Software - - - WARNING: Custom Software - - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - - - SetupWidget diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index 79b10f316f..145eb7ae67 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -684,113 +684,6 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para Firehose - - Setup - - Something went wrong. Reboot the device. - Algo ha ido mal. Reinicie el dispositivo. - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Asegúrese de que la URL insertada es válida y que el dispositivo tiene buena conexión. - - - No custom software found at this URL. - No encontramos software personalizado en esta URL. - - - WARNING: Low Voltage - ALERTA: Voltaje bajo - - - Power your device in a car with a harness or proceed at your own risk. - Encienda su dispositivo en un auto con el arnés o proceda bajo su propio riesgo. - - - Power off - Apagar - - - Continue - Continuar - - - Getting Started - Comenzando - - - Before we get on the road, let’s finish installation and cover some details. - Antes de ponernos en marcha, terminemos la instalación y cubramos algunos detalles. - - - Connect to Wi-Fi - Conectarse al Wi-Fi - - - Back - Volver - - - Continue without Wi-Fi - Continuar sin Wi-Fi - - - Waiting for internet - Esperando conexión a internet - - - Choose Software to Install - Elija el software a instalar - - - openpilot - openpilot - - - Custom Software - Software personalizado - - - Enter URL - Insertar URL - - - for Custom Software - para Software personalizado - - - Downloading... - Descargando... - - - Download Failed - Descarga fallida - - - Reboot device - Reiniciar Dispositivo - - - Start over - Comenzar de nuevo - - - Select a language - Seleccione un idioma - - - WARNING: Custom Software - ADVERTENCIA: Software personalizado - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - Tenga cuidado al instalar software de terceros. El software de terceros no ha sido probado por comma y puede causar daños a su dispositivo y/o vehículo. - -Si desea continuar, utilice https://flash.comma.ai para restaurar su dispositivo a un estado de fábrica más tarde. - - SetupWidget diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index a4effb2a45..297d936139 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -678,111 +678,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - - Setup - - Something went wrong. Reboot the device. - Un problème est survenu. Redémarrez l'appareil. - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Assurez-vous que l'URL saisie est valide et que la connexion internet de l'appareil est bonne. - - - No custom software found at this URL. - Aucun logiciel personnalisé trouvé à cette URL. - - - WARNING: Low Voltage - ATTENTION : Tension faible - - - Power your device in a car with a harness or proceed at your own risk. - Alimentez votre appareil dans une voiture avec un harness ou continuez à vos risques et périls. - - - Power off - Éteindre - - - Continue - Continuer - - - Getting Started - Commencer - - - Before we get on the road, let’s finish installation and cover some details. - Avant de prendre la route, terminons l'installation et passons en revue quelques détails. - - - Connect to Wi-Fi - Se connecter au Wi-Fi - - - Back - Retour - - - Enter URL - Entrer l'URL - - - for Custom Software - pour logiciel personnalisé - - - Continue without Wi-Fi - Continuer sans Wi-Fi - - - Waiting for internet - En attente d'internet - - - Downloading... - Téléchargement... - - - Download Failed - Échec du téléchargement - - - Reboot device - Redémarrer l'appareil - - - Start over - Recommencer - - - Select a language - Choisir une langue - - - Choose Software to Install - Choisir le logiciel à installer - - - openpilot - openpilot - - - Custom Software - Logiciel personnalisé - - - WARNING: Custom Software - - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - - - SetupWidget diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 8d0b2cb75b..7bb5c5364f 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -679,113 +679,6 @@ Firehoseモードを有効にすると学習データを最大限アップロー データ学習 - - Setup - - WARNING: Low Voltage - 警告:電圧低下 - - - Power your device in a car with a harness or proceed at your own risk. - ハーネスを使って車でデバイスに電源を供給するか、自己責任でこのまま継続して下さい。 - - - Power off - 電源を切る - - - Continue - 続ける - - - Getting Started - はじめに - - - Before we get on the road, let’s finish installation and cover some details. - 出発する前に、インストールを完了させて少し詳細を確認しましょう。 - - - Connect to Wi-Fi - Wi-Fiに接続 - - - Back - 戻る - - - Continue without Wi-Fi - Wi-Fiに接続せずに続行 - - - Waiting for internet - インターネット接続を待機中 - - - Enter URL - URLの入力 - - - for Custom Software - カスタムソフトウェア - - - Downloading... - ダウンロード中... - - - Download Failed - ダウンロードに失敗しました - - - Ensure the entered URL is valid, and the device’s internet connection is good. - 入力されたURLが正しいかどうか、インターネットに正常に接続できているかを確認してください。 - - - Reboot device - デバイスを再起動 - - - Start over - やり直す - - - No custom software found at this URL. - このURLはカスタムソフトウェアではありません。 - - - Something went wrong. Reboot the device. - 何かの問題が発生しました。デバイスを再起動してください。 - - - Select a language - 言語を選択 - - - Choose Software to Install - インストールするソフトウェアを選択してください。 - - - openpilot - openpilot - - - Custom Software - カスタムソフトウェア - - - WARNING: Custom Software - 警告: カスタムソフトウェア - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - サードパーティ製ソフトウェアをインストールする際は注意してください。サードパーティ製ソフトウェアはcommaによってテストされておらず、あなたのデバイスや車両に損害を与える可能性があります。 - -続行したい場合は、後でデバイスを工場出荷時の状態に戻すために https://flash.comma.ai を使用してください。 - - SetupWidget diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 3cffab10b2..807996f182 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -679,113 +679,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp 파이어호스 - - Setup - - WARNING: Low Voltage - 경고: 전압이 낮습니다 - - - Power your device in a car with a harness or proceed at your own risk. - 장치를 하네스를 통해 차량 전원에 연결하세요. USB 전원에서는 예상치 못한 문제가 생길 수 있습니다. - - - Power off - 전원 끄기 - - - Continue - 계속 - - - Getting Started - 시작하기 - - - Before we get on the road, let’s finish installation and cover some details. - 출발 전 설정을 완료하고 세부 사항을 살펴봅니다. - - - Connect to Wi-Fi - Wi-Fi 연결 - - - Back - 뒤로 - - - Continue without Wi-Fi - Wi-Fi 연결 없이 진행 - - - Waiting for internet - 인터넷 연결 대기 중 - - - Enter URL - URL 입력 - - - for Custom Software - 커스텀 소프트웨어 - - - Downloading... - 다운로드 중... - - - Download Failed - 다운로드 실패 - - - Ensure the entered URL is valid, and the device’s internet connection is good. - 입력된 URL이 유효하고 인터넷 연결이 원활한지 확인하세요. - - - Reboot device - 장치 재부팅 - - - Start over - 다시 시작 - - - Something went wrong. Reboot the device. - 문제가 발생했습니다. 장치를 재부팅하세요. - - - No custom software found at this URL. - 이 URL에서 커스텀 소프트웨어를 찾을 수 없습니다. - - - Select a language - 언어를 선택하세요 - - - Choose Software to Install - 설치할 소프트웨어 선택 - - - openpilot - 오픈파일럿 - - - Custom Software - 커스텀 소프트웨어 - - - WARNING: Custom Software - 경고: 커스텀 소프트웨어 - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - 타사 소프트웨어를 설치할 때는 주의하십시오. 타사 소프트웨어는 comma에 의해 테스트되지 않았으며 장치나 차량에 손상을 줄 수 있습니다. - -진행하려면 https://flash.comma.ai를 사용하여 나중에 장치를 공장 초기화하세요. - - SetupWidget diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 403faf226f..7f41588e8f 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -684,113 +684,6 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar Firehose - - Setup - - WARNING: Low Voltage - ALERTA: Baixa Voltagem - - - Power your device in a car with a harness or proceed at your own risk. - Ligue seu dispositivo em um carro com um chicote ou prossiga por sua conta e risco. - - - Power off - Desligar - - - Continue - Continuar - - - Getting Started - Começando - - - Before we get on the road, let’s finish installation and cover some details. - Antes de pegarmos a estrada, vamos terminar a instalação e cobrir alguns detalhes. - - - Connect to Wi-Fi - Conectar ao Wi-Fi - - - Back - Voltar - - - Continue without Wi-Fi - Continuar sem Wi-Fi - - - Waiting for internet - Esperando pela internet - - - Enter URL - Preencher URL - - - for Custom Software - para o Software Customizado - - - Downloading... - Baixando... - - - Download Failed - Download Falhou - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Garanta que a URL inserida é valida, e uma boa conexão à internet. - - - Reboot device - Reiniciar Dispositivo - - - Start over - Inicializar - - - No custom software found at this URL. - Não há software personalizado nesta URL. - - - Something went wrong. Reboot the device. - Algo deu errado. Reinicie o dispositivo. - - - Select a language - Selecione o Idioma - - - Choose Software to Install - Escolha o Software a ser Instalado - - - openpilot - openpilot - - - Custom Software - Software Customizado - - - WARNING: Custom Software - AVISO: Software Personalizado - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - Tenha cuidado ao instalar software de terceiros. Softwares de terceiros não foram testados pela comma e podem causar danos ao seu dispositivo e/ou veículo. - -Se quiser continuar, use https://flash.comma.ai para restaurar seu dispositivo ao estado de fábrica mais tarde. - - SetupWidget diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index 87cb8bc2e2..34824c2680 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -675,111 +675,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp สายยางดับเพลิง - - Setup - - WARNING: Low Voltage - คำเตือน: แรงดันแบตเตอรี่ต่ำ - - - Power your device in a car with a harness or proceed at your own risk. - โปรดต่ออุปกรณ์ของคุณเข้ากับสายควบคุมในรถยนต์ หรือดำเนินการด้วยความเสี่ยงของคุณเอง - - - Power off - ปิดเครื่อง - - - Continue - ดำเนินการต่อ - - - Getting Started - เริ่มกันเลย - - - Before we get on the road, let’s finish installation and cover some details. - ก่อนออกเดินทาง เรามาทำการติดตั้งซอฟต์แวร์ และตรวจสอบการตั้งค่า - - - Connect to Wi-Fi - เชื่อมต่อ Wi-Fi - - - Back - ย้อนกลับ - - - Continue without Wi-Fi - ดำเนินการต่อโดยไม่ใช้ Wi-Fi - - - Waiting for internet - กำลังรอสัญญาณอินเตอร์เน็ต - - - Enter URL - ป้อน URL - - - for Custom Software - สำหรับซอฟต์แวร์ที่กำหนดเอง - - - Downloading... - กำลังดาวน์โหลด... - - - Download Failed - ดาวน์โหลดล้มเหลว - - - Ensure the entered URL is valid, and the device’s internet connection is good. - ตรวจสอบให้แน่ใจว่า URL ที่ป้อนนั้นถูกต้อง และอุปกรณ์เชื่อมต่ออินเทอร์เน็ตอยู่ - - - Reboot device - รีบูตอุปกรณ์ - - - Start over - เริ่มต้นใหม่ - - - Something went wrong. Reboot the device. - มีบางอย่างผิดพลาด รีบูตอุปกรณ์ - - - No custom software found at this URL. - ไม่พบซอฟต์แวร์ที่กำหนดเองที่ URL นี้ - - - Select a language - เลือกภาษา - - - Choose Software to Install - เลือกซอฟต์แวร์ที่จะติดตั้ง - - - openpilot - openpilot - - - Custom Software - ซอฟต์แวร์ที่กำหนดเอง - - - WARNING: Custom Software - - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - - - SetupWidget diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 6112698e31..db6c4283a8 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -672,111 +672,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - - Setup - - WARNING: Low Voltage - UYARI: Düşük voltaj - - - Power your device in a car with a harness or proceed at your own risk. - Cihazınızı emniyet kemeri olan bir arabada çalıştırın veya riski kabul ederek devam edin. - - - Power off - Sistemi kapat - - - Continue - Devam et - - - Getting Started - Başlarken - - - Before we get on the road, let’s finish installation and cover some details. - Yola çıkmadan önce kurulumu bitirin ve bazı detayları gözden geçirin.. - - - Connect to Wi-Fi - Wi-Fi ile bağlan - - - Back - Geri - - - Continue without Wi-Fi - Wi-Fi bağlantısı olmadan devam edin - - - Waiting for internet - İnternet bağlantısı bekleniyor. - - - Enter URL - URL girin - - - for Custom Software - özel yazılım için - - - Downloading... - İndiriliyor... - - - Download Failed - İndirme başarısız. - - - Ensure the entered URL is valid, and the device’s internet connection is good. - Girilen URL nin geçerli olduğundan ve cihazın internet bağlantısının olduğunu kontrol edin - - - Reboot device - Cihazı yeniden başlat - - - Start over - Zacznij od początku - - - Something went wrong. Reboot the device. - - - - No custom software found at this URL. - - - - Select a language - Dil seçin - - - Choose Software to Install - - - - openpilot - openpilot - - - Custom Software - - - - WARNING: Custom Software - - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - - - SetupWidget diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index a1d1602373..fc4247d6a7 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -679,113 +679,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Firehose - - Setup - - WARNING: Low Voltage - 警告:低电压 - - - Power your device in a car with a harness or proceed at your own risk. - 请使用car harness线束为您的设备供电,或自行承担风险。 - - - Power off - 关机 - - - Continue - 继续 - - - Getting Started - 开始设置 - - - Before we get on the road, let’s finish installation and cover some details. - 开始旅程之前,让我们完成安装并介绍一些细节。 - - - Connect to Wi-Fi - 连接到WiFi - - - Back - 返回 - - - Continue without Wi-Fi - 不连接WiFi并继续 - - - Waiting for internet - 等待网络连接 - - - Enter URL - 输入网址 - - - for Custom Software - 以下载自定义软件 - - - Downloading... - 正在下载…… - - - Download Failed - 下载失败 - - - Ensure the entered URL is valid, and the device’s internet connection is good. - 请确保互联网连接良好且输入的URL有效。 - - - Reboot device - 重启设备 - - - Start over - 重来 - - - No custom software found at this URL. - 在此网址找不到自定义软件。 - - - Something went wrong. Reboot the device. - 发生了一些错误。请重新启动您的设备。 - - - Select a language - 选择语言 - - - Choose Software to Install - 选择要安装的软件 - - - openpilot - openpilot - - - Custom Software - 定制软件 - - - WARNING: Custom Software - 警告:自定义软件 - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - 请谨慎安装第三方软件。第三方软件未经 comma 测试,可能会损害您的设备和车辆。 - -如果您决定继续,后续可通过 https://flash.comma.ai 将设备恢复到出厂状态。 - - SetupWidget diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index a094dd4182..bf1a739313 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -679,113 +679,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Firehose - - Setup - - WARNING: Low Voltage - 警告:電壓過低 - - - Power your device in a car with a harness or proceed at your own risk. - 請使用車上 harness 提供的電源,若繼續的話您需要自擔風險。 - - - Power off - 關機 - - - Continue - 繼續 - - - Getting Started - 入門 - - - Before we get on the road, let’s finish installation and cover some details. - 在我們上路之前,讓我們完成安裝並介紹一些細節。 - - - Connect to Wi-Fi - 連接到無線網路 - - - Back - 回上頁 - - - Continue without Wi-Fi - 在沒有 Wi-Fi 的情況下繼續 - - - Waiting for internet - 連接至網路中 - - - Enter URL - 輸入網址 - - - for Custom Software - 訂製的軟體 - - - Downloading... - 下載中… - - - Download Failed - 下載失敗 - - - Ensure the entered URL is valid, and the device’s internet connection is good. - 請確定您輸入的是有效的安裝網址,並且確定裝置的網路連線狀態良好。 - - - Reboot device - 重新啟動 - - - Start over - 重新開始 - - - No custom software found at this URL. - 在此網址找不到自訂軟體。 - - - Something went wrong. Reboot the device. - 發生了一些錯誤。請重新啟動您的裝置。 - - - Select a language - 選擇語言 - - - Choose Software to Install - 選擇要安裝的軟體 - - - openpilot - openpilot - - - Custom Software - 自訂軟體 - - - WARNING: Custom Software - 警告:自訂軟體 - - - Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle. - -If you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later. - 請謹慎安裝第三方軟體。第三方軟體未經 comma 測試,可能會損壞您的裝置及車輛。 - -若您仍要繼續,日後可使用 https://flash.comma.ai 將您的裝置恢復至出廠狀態。 - - SetupWidget From 741ea44abaaa0824982e9fe522ac207c4c742483 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 13 Aug 2025 23:25:09 -0700 Subject: [PATCH 06/72] AGNOS 12.7 (#35988) * agnos12.7 * prod --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 12 ++++---- system/hardware/tici/all-partitions.json | 36 ++++++++++++------------ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index 0ed1395b37..e1a0da9b67 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="12.6" + export AGNOS_VERSION="12.7" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 7d99793a59..0900c51d10 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -67,17 +67,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img.xz", - "hash": "49faee0e9b084abf0ea46f87722e3366bbd0435fb6b25cce189295c1ff368da1", - "hash_raw": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643", + "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img.xz", + "hash": "e6fbc73b6ef9551f57f123791f94f2f72db8ce59e9fba8ccd44c30685582368b", + "hash_raw": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "db07761be0130e35a9d3ea6bec8df231260d3e767ae770850f18f10e14d0ab3f", + "ondevice_hash": "af2a42284ecfddc9d8aa50fde0e2093ba18cf1dd2242a7a3fbe05f78f6ec0228", "alt": { - "hash": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643", - "url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img", + "hash": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", + "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img", "size": 5368709120 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 8648544991..5d1bcc65a7 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -350,51 +350,51 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img.xz", - "hash": "49faee0e9b084abf0ea46f87722e3366bbd0435fb6b25cce189295c1ff368da1", - "hash_raw": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643", + "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img.xz", + "hash": "e6fbc73b6ef9551f57f123791f94f2f72db8ce59e9fba8ccd44c30685582368b", + "hash_raw": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "db07761be0130e35a9d3ea6bec8df231260d3e767ae770850f18f10e14d0ab3f", + "ondevice_hash": "af2a42284ecfddc9d8aa50fde0e2093ba18cf1dd2242a7a3fbe05f78f6ec0228", "alt": { - "hash": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643", - "url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img", + "hash": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", + "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img", "size": 5368709120 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-02f7abb4b667c04043c0c6950145aaebd704851261f32256d0f7e84a52059dda.img.xz", - "hash": "1eda66d4e31222fc2e792a62ae8e7d322fc643f0b23785e7527bb51a9fee97c7", - "hash_raw": "02f7abb4b667c04043c0c6950145aaebd704851261f32256d0f7e84a52059dda", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-ad38266b508280f22d02c3749322291eb083e08580ff8b7014add6989d290b12.img.xz", + "hash": "59e1bb2606b65293721dd89bcdc8d0c5cae47718c213eb40f235149aa5a408ae", + "hash_raw": "ad38266b508280f22d02c3749322291eb083e08580ff8b7014add6989d290b12", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "679b650ee04b7b1ef610b63fde9b43569fded39ceacf88789b564de99c221ea1" + "ondevice_hash": "40b5e666ec137a863178b51a7947e3bb76fe9259584d84e6a66361fabced3da5" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-bab8399bbe3968f3c496f7bc83c2541b33acc1f47814c4ad95801bf5cb7e7588.img.xz", - "hash": "e63d3277285aae1f04fd7f4f48429ce35010f4843ab755f10d360c3aa788e484", - "hash_raw": "bab8399bbe3968f3c496f7bc83c2541b33acc1f47814c4ad95801bf5cb7e7588", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-2f78f798861e0a3dd55af87e7a0c1982502a9293340d3eee6f3b8c9c88878e33.img.xz", + "hash": "e555a29c3ccb547ed840085c052cd1c6126c32d5c6dafe1712f593ae631be190", + "hash_raw": "2f78f798861e0a3dd55af87e7a0c1982502a9293340d3eee6f3b8c9c88878e33", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "2947374fc5980ffe3c5b94b61cc1c81bc55214f494153ed234164801731f5dc0" + "ondevice_hash": "f5bc368dbe52ac7800634f74b1fd764fc3b6e68337984f40fd59222b1276d9f2" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-22c874b4b66bbc000f3219abede8d62cb307f5786fd526a8473c61422765dea0.img.xz", - "hash": "12d9245711e8c49c51ff2c7b82d7301f2fcb1911edcddb35a105a80911859113", - "hash_raw": "22c874b4b66bbc000f3219abede8d62cb307f5786fd526a8473c61422765dea0", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-6367d73ba6a32823035c9941168ca3af3704c783f6bcd912f70afe1dbdfdd14b.img.xz", + "hash": "f5dfbe1dcba25b9a1920259bc89f52b5e406519539ff0112d5469fff3f1b6dba", + "hash_raw": "6367d73ba6a32823035c9941168ca3af3704c783f6bcd912f70afe1dbdfdd14b", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "03c8b65c945207f887ed6c52d38b53d53d71c8597dcb0b63dfbb11f7cfff8d2b" + "ondevice_hash": "b8f447f0ea40faae7292d3a0dc380d194caab6ec3f5007eda5661f2aa4d2f4ab" } ] \ No newline at end of file From 349c0ec66237dad81176532788558217397d231e Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:30:42 -0400 Subject: [PATCH 07/72] Honda: Add several new cars to release (#35989) * bump opendbc * regen CARS.md * update RELEASES.md --- RELEASES.md | 4 ++++ docs/CARS.md | 9 +++++++-- opendbc_repo | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 388ddae28b..2fadae9238 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -13,6 +13,10 @@ Version 0.10.0 (2025-08-05) * Enable live-learned steering actuation delay * Record driving feedback using LKAS button * Opt-in audio recording for dashcam video +* Acura MDX 2025 support thanks to vanillagorillaa and MVL! +* Honda Accord 2023-25 support thanks to vanillagorillaa and MVL! +* Honda CR-V 2023-25 support thanks to vanillagorillaa and MVL! +* Honda Pilot 2023-25 support thanks to vanillagorillaa and MVL! Version 0.9.9 (2025-05-23) ======================== diff --git a/docs/CARS.md b/docs/CARS.md index a0a4fd6bd7..fcb87236c8 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,12 +4,13 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 314 Supported Cars +# 319 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video|Setup Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| |Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Acura|ILX 2019|All|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Acura|MDX 2025|All except Type S|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Acura|RDX 2019-21|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| @@ -72,8 +73,9 @@ A supported vehicle is one that just works when you install a comma device. All |Genesis|GV80 2023[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| -|Honda|Accord 2023|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Accord 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Accord Hybrid 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[5](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| @@ -84,7 +86,9 @@ A supported vehicle is one that just works when you install a comma device. All |Honda|Civic Hybrid 2025|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|CR-V 2015-16|Touring Trim|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|CR-V 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|15 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|CR-V 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|CR-V Hybrid 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|CR-V Hybrid 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|e 2020|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Fit 2018-20|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Freed 2020|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| @@ -95,6 +99,7 @@ A supported vehicle is one that just works when you install a comma device. All |Honda|Odyssey 2018-20|Honda Sensing|openpilot|26 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Passport 2019-25|All|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Pilot 2016-22|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Pilot 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Ridgeline 2017-25|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|Azera 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|Azera Hybrid 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| diff --git a/opendbc_repo b/opendbc_repo index 040c6123fe..fa210c3fd8 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 040c6123fe524f138ae5490b93e229dd5de5e151 +Subproject commit fa210c3fd81273a06691b2c355814560804a072e From f2c806f8a0e420ba9cee95c0671cf7d78637d64a Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:39:02 -0400 Subject: [PATCH 08/72] bump opendbc for fingerprint updates (#35990) bump opendbc --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index fa210c3fd8..3024c6fccb 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit fa210c3fd81273a06691b2c355814560804a072e +Subproject commit 3024c6fccbaea8f7b4760a3c295e7c8fb8e8b4a6 From a6d0a88b1ebb321fef8c5c6e96290123f60d9d4a Mon Sep 17 00:00:00 2001 From: eFini Date: Fri, 15 Aug 2025 01:27:44 +0800 Subject: [PATCH 09/72] Multilang: update chs/cht translations (#35981) --- selfdrive/ui/translations/main_zh-CHS.ts | 12 +++++++----- selfdrive/ui/translations/main_zh-CHT.ts | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index fc4247d6a7..667d81ddd4 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -494,15 +494,15 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
Acknowledge Excessive Actuation - + 确认过度作动 Snooze Update - 暂停更新 + 推迟更新 openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - + openpilot 在您上一次驾驶中,检测到过度的 %1 作动。请访问 https://comma.ai/support 联系客服,并提供您设备的 Dongle ID 以便进行故障排查。
@@ -1033,13 +1033,15 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Record Audio Feedback with LKAS button - + 使用“车道保持”按钮录制音频反馈 Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. Note that this feature is only compatible with select cars. - + 按下“车道保持”按钮,即可录制并分享驾驶反馈给 openpilot 团队。当此开关禁用时,该按钮将用作书签按钮。该事件将在 comma connect 中高亮显示,且对应的视频片段将被保留在您的设备存储空间中。 + +请注意,此功能仅兼容部分车型。 diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index bf1a739313..a0f1997a00 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -494,15 +494,15 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Acknowledge Excessive Actuation - + 確認過度作動 Snooze Update - 暫停更新 + 延後更新 openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting. - + openpilot 在您上次的駕駛中,偵測到過度的 %1 作動。請至 https://comma.ai/support 聯絡客服,並提供您裝置的 Dongle ID 以進行故障排除。 @@ -1033,13 +1033,15 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Record Audio Feedback with LKAS button - + 使用「車道維持」按鈕錄製音訊回饋 Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. Note that this feature is only compatible with select cars. - + 按下「車道維持」按鈕,即可錄製並分享駕駛回饋給 openpilot 團隊。當此開關停用時,該按鈕的功能將轉為書籤按鈕。該事件將會在 comma connect 中被標註,且對應的路段影像將保留在您的裝置儲存空間中。 + +請注意,此功能僅與特定車款相容。 From aa91a02db8278bb52eba64b47390ed5c341363db Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 14 Aug 2025 18:26:19 -0700 Subject: [PATCH 10/72] LogReader sourcing: check comma API source before CI source (#35992) sort --- tools/lib/logreader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index cc84c8b52e..6b0d9fa527 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -298,7 +298,7 @@ class LogReader: def __init__(self, identifier: str | list[str], default_mode: ReadMode = ReadMode.RLOG, sources: list[Source] = None, sort_by_time=False, only_union_types=False): if sources is None: - sources = [internal_source, openpilotci_source, comma_api_source, comma_car_segments_source] + sources = [internal_source, comma_api_source, openpilotci_source, comma_car_segments_source] self.default_mode = default_mode self.sources = sources From daef43f620edb431428fd8498eaf99be824a0dd1 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 14 Aug 2025 18:54:56 -0700 Subject: [PATCH 11/72] ci: show all tests durations (#35995) show --- .github/workflows/selfdrive_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index fb082ae407..2d94a39260 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -27,7 +27,7 @@ env: RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c - PYTEST: pytest --continue-on-collection-errors --durations=0 --durations-min=5 -n logical + PYTEST: pytest --continue-on-collection-errors --durations=0 -n logical jobs: build_release: From 1eef956cadd30aa4fd5aca154ec7074f16a849df Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 14 Aug 2025 19:19:37 -0700 Subject: [PATCH 12/72] LogReader sourcing: return dict (#35994) * new return type * fix test * why not --- tools/lib/logreader.py | 33 ++++++++++++++++--------------- tools/lib/tests/test_logreader.py | 6 +++--- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 6b0d9fa527..d15e473c41 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -141,7 +141,7 @@ class ReadMode(enum.StrEnum): LogPath = str | None LogFileName = tuple[str, ...] -Source = Callable[[SegmentRange, LogFileName], list[LogPath]] +Source = Callable[[SegmentRange, LogFileName], dict[int, LogPath]] InternalUnavailableException = Exception("Internal source not available") @@ -150,52 +150,53 @@ class LogsUnavailable(Exception): pass -def comma_api_source(sr: SegmentRange, fns: LogFileName) -> list[LogPath]: +def comma_api_source(sr: SegmentRange, fns: LogFileName) -> dict[int, LogPath]: route = Route(sr.route_name) # comma api will have already checked if the file exists if fns == FileName.RLOG: - return [route.log_paths()[seg] for seg in sr.seg_idxs] + return {seg: route.log_paths()[seg] for seg in sr.seg_idxs} else: - return [route.qlog_paths()[seg] for seg in sr.seg_idxs] + return {seg: route.qlog_paths()[seg] for seg in sr.seg_idxs} -def internal_source(sr: SegmentRange, fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> list[LogPath]: +def internal_source(sr: SegmentRange, fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, LogPath]: if not internal_source_available(endpoint_url): raise InternalUnavailableException def get_internal_url(sr: SegmentRange, seg, file): return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" - return eval_source([[get_internal_url(sr, seg, fn) for fn in fns] for seg in sr.seg_idxs]) + return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in sr.seg_idxs}) -def openpilotci_source(sr: SegmentRange, fns: LogFileName) -> list[LogPath]: - return eval_source([[get_url(sr.route_name, seg, fn) for fn in fns] for seg in sr.seg_idxs]) +def openpilotci_source(sr: SegmentRange, fns: LogFileName) -> dict[int, LogPath]: + return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in sr.seg_idxs}) -def comma_car_segments_source(sr: SegmentRange, fns: LogFileName) -> list[LogPath]: - return eval_source([get_comma_segments_url(sr.route_name, seg) for seg in sr.seg_idxs]) +def comma_car_segments_source(sr: SegmentRange, fns: LogFileName) -> dict[int, LogPath]: + return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in sr.seg_idxs}) def direct_source(file_or_url: str) -> list[str]: return [file_or_url] -def eval_source(files: list[list[str] | str]) -> list[LogPath]: +def eval_source(files: dict[int, list[str] | str]) -> dict[int, LogPath]: # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) - valid_files: list[LogPath] = [] + valid_files: dict[int, LogPath] = {} - for urls in files: + for seg_idx, urls in files.items(): if isinstance(urls, str): urls = [urls] + # Add first valid file URL or None for url in urls: if file_exists(url): - valid_files.append(url) + valid_files[seg_idx] = url break else: - valid_files.append(None) + valid_files[seg_idx] = None return valid_files @@ -227,7 +228,7 @@ def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) assert len(files) == len(valid_files) or len(valid_files) == 0, f"Source {source.__name__} returned unexpected number of files" # Build a dict of valid files - for idx, f in enumerate(files): + for idx, f in files.items(): if valid_files.get(idx) is None: valid_files[idx] = f diff --git a/tools/lib/tests/test_logreader.py b/tools/lib/tests/test_logreader.py index 11bdd33ccf..7a70ad6aab 100644 --- a/tools/lib/tests/test_logreader.py +++ b/tools/lib/tests/test_logreader.py @@ -36,12 +36,12 @@ def setup_source_scenario(mocker, is_internal=False): comma_api_source_mock.__name__ = comma_api_source_mock._mock_name if is_internal: - internal_source_mock.return_value = [QLOG_FILE] + internal_source_mock.return_value = {3: QLOG_FILE} else: internal_source_mock.side_effect = InternalUnavailableException - openpilotci_source_mock.return_value = [None] - comma_api_source_mock.return_value = [QLOG_FILE] + openpilotci_source_mock.return_value = {3: None} + comma_api_source_mock.return_value = {3: QLOG_FILE} yield From 8ec61991ee35bb0cda07c02a518c469e6502a31c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 14 Aug 2025 19:28:37 -0700 Subject: [PATCH 13/72] LogReader sourcing: remove redundant file existence checks (#35991) * speed up sourcing but avoiding checking for existence of collected files already from previous sources * clean up * been meaning to make them return dicts * no longer true * no longer true * clean up * more * more * revert --- tools/lib/logreader.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index d15e473c41..bedafb8566 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -141,7 +141,7 @@ class ReadMode(enum.StrEnum): LogPath = str | None LogFileName = tuple[str, ...] -Source = Callable[[SegmentRange, LogFileName], dict[int, LogPath]] +Source = Callable[[SegmentRange, list[int], LogFileName], dict[int, LogPath]] InternalUnavailableException = Exception("Internal source not available") @@ -150,32 +150,32 @@ class LogsUnavailable(Exception): pass -def comma_api_source(sr: SegmentRange, fns: LogFileName) -> dict[int, LogPath]: +def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, LogPath]: route = Route(sr.route_name) # comma api will have already checked if the file exists if fns == FileName.RLOG: - return {seg: route.log_paths()[seg] for seg in sr.seg_idxs} + return {seg: route.log_paths()[seg] for seg in seg_idxs} else: - return {seg: route.qlog_paths()[seg] for seg in sr.seg_idxs} + return {seg: route.qlog_paths()[seg] for seg in seg_idxs} -def internal_source(sr: SegmentRange, fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, LogPath]: +def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, LogPath]: if not internal_source_available(endpoint_url): raise InternalUnavailableException def get_internal_url(sr: SegmentRange, seg, file): return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" - return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in sr.seg_idxs}) + return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) -def openpilotci_source(sr: SegmentRange, fns: LogFileName) -> dict[int, LogPath]: - return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in sr.seg_idxs}) +def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, LogPath]: + return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) -def comma_car_segments_source(sr: SegmentRange, fns: LogFileName) -> dict[int, LogPath]: - return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in sr.seg_idxs}) +def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, LogPath]: + return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) def direct_source(file_or_url: str) -> list[str]: @@ -205,8 +205,9 @@ def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) exceptions = {} sr = SegmentRange(identifier) - mode = default_mode if sr.selector is None else ReadMode(sr.selector) + needed_seg_idxs = sr.seg_idxs + mode = default_mode if sr.selector is None else ReadMode(sr.selector) if mode == ReadMode.QLOG: try_fns = [FileName.QLOG] else: @@ -222,16 +223,16 @@ def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) for fn in try_fns: for source in sources: try: - files = source(sr, fn) - - # Check every source returns an expected number of files - assert len(files) == len(valid_files) or len(valid_files) == 0, f"Source {source.__name__} returned unexpected number of files" + files = source(sr, needed_seg_idxs, fn) # Build a dict of valid files for idx, f in files.items(): if valid_files.get(idx) is None: valid_files[idx] = f + # Don't check for segment files that have already been found + needed_seg_idxs = [idx for idx in needed_seg_idxs if valid_files.get(idx) is None] + # We've found all files, return them if all(f is not None for f in valid_files.values()): return cast(list[str], list(valid_files.values())) From 7c6bc7031205409a8ee93b590b6971f87ffde7e2 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 14 Aug 2025 20:14:12 -0700 Subject: [PATCH 14/72] params: fix default boolean params (#35997) * fix * update test --- system/manager/manager.py | 2 +- system/manager/test/test_manager.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/system/manager/manager.py b/system/manager/manager.py index bd8e552dd3..36055d8635 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -40,7 +40,7 @@ def manager_init() -> None: # set unset params to their default value for k in params.all_keys(): default_value = params.get_default_value(k) - if default_value and params.get(k) is None: + if default_value is not None and params.get(k) is None: params.put(k, default_value) # Create folders needed for msgq diff --git a/system/manager/test/test_manager.py b/system/manager/test/test_manager.py index 5e55648283..34d07c6724 100644 --- a/system/manager/test/test_manager.py +++ b/system/manager/test/test_manager.py @@ -46,9 +46,10 @@ class TestManager: manager.main() for k in params.all_keys(): default_value = params.get_default_value(k) - if default_value: + if default_value is not None: assert params.get(k) == default_value assert params.get("OpenpilotEnabledToggle") + assert params.get("RouteCount") == 0 @pytest.mark.skip("this test is flaky the way it's currently written, should be moved to test_onroad") def test_clean_exit(self, subtests): From 385ad9e8397122da8eff77279db6e84365a35693 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 14 Aug 2025 21:38:27 -0700 Subject: [PATCH 15/72] updated: connectivity check with new setup (#35998) * default * fix --- common/params_keys.h | 4 ++-- system/updated/updated.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index 3d7281bf93..c54600462c 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -73,9 +73,9 @@ inline static std::unordered_map keys = { {"LastOffroadStatusPacket", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON}}, {"LastPowerDropDetected", {CLEAR_ON_MANAGER_START, STRING}}, {"LastUpdateException", {CLEAR_ON_MANAGER_START, STRING}}, - {"LastUpdateRouteCount", {PERSISTENT, INT}}, + {"LastUpdateRouteCount", {PERSISTENT, INT, "0"}}, {"LastUpdateTime", {PERSISTENT, TIME}}, - {"LastUpdateUptimeOnroad", {PERSISTENT, FLOAT}}, + {"LastUpdateUptimeOnroad", {PERSISTENT, FLOAT, "0.0"}}, {"LiveDelay", {PERSISTENT, BYTES}}, {"LiveParameters", {PERSISTENT, JSON}}, {"LiveParametersV2", {PERSISTENT, BYTES}}, diff --git a/system/updated/updated.py b/system/updated/updated.py index 1fd9e8b717..72f9de7d01 100755 --- a/system/updated/updated.py +++ b/system/updated/updated.py @@ -283,8 +283,8 @@ class Updater: self.params.put("LastUpdateUptimeOnroad", last_uptime_onroad) self.params.put("LastUpdateRouteCount", last_route_count) else: - last_uptime_onroad = self.params.get("LastUpdateUptimeOnroad") or last_uptime_onroad - last_route_count = self.params.get("LastUpdateRouteCount") or last_route_count + last_uptime_onroad = self.params.get("LastUpdateUptimeOnroad", return_default=True) + last_route_count = self.params.get("LastUpdateRouteCount", return_default=True) if exception is None: self.params.remove("LastUpdateException") From b54d5997de76a34123f8a0b979f926314564443d Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 14 Aug 2025 21:59:25 -0700 Subject: [PATCH 16/72] Update RELEASES.md --- RELEASES.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 2fadae9238..e1c64efad4 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -5,11 +5,10 @@ Version 0.10.0 (2025-08-05) ======================== * New driving model * New training architecture - * Architecture outlined in CVPR paper: "Learning to Drive from a World Model" - * Longitudinal MPC replaced by E2E planning from worldmodel in experimental mode - * Action from lateral MPC as training objective replaced by E2E planning from worldmodel + * Described in our CVPR paper: "Learning to Drive from a World Model" + * Longitudinal MPC replaced by E2E planning from World Model in Experimental Mode + * Action from lateral MPC as training objective replaced by E2E planning from World Model * Low-speed lead car ground-truth fixes - * Enable live-learned steering actuation delay * Record driving feedback using LKAS button * Opt-in audio recording for dashcam video From 4536719353fc9eeb4b9c57692cd72766a2f86397 Mon Sep 17 00:00:00 2001 From: eFini Date: Sat, 16 Aug 2025 00:02:38 +0800 Subject: [PATCH 17/72] longitudinal_planner: Convert self.mode to a local variable in update() (#35999) Make 'mode' variable local --- selfdrive/controls/lib/longitudinal_planner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 605d956b8e..34fc85f8a5 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -90,7 +90,7 @@ class LongitudinalPlanner: return x, v, a, j, throttle_prob def update(self, sm): - self.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc' + mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc' if len(sm['carControl'].orientationNED) == 3: accel_coast = get_coast_accel(sm['carControl'].orientationNED[1]) @@ -113,7 +113,7 @@ class LongitudinalPlanner: # No change cost when user is controlling the speed, or when standstill prev_accel_constraint = not (reset_state or sm['carState'].standstill) - if self.mode == 'acc': + if mode == 'acc': accel_clip = [ACCEL_MIN, get_max_accel(v_ego)] steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg accel_clip = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_clip, self.CP) @@ -163,7 +163,7 @@ class LongitudinalPlanner: output_a_target_e2e = sm['modelV2'].action.desiredAcceleration output_should_stop_e2e = sm['modelV2'].action.shouldStop - if self.mode == 'acc': + if mode == 'acc': output_a_target = output_a_target_mpc self.output_should_stop = output_should_stop_mpc else: From 5417efaa1dd83c3126176d87d40f32c1e750c44a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 15 Aug 2025 11:12:18 -0700 Subject: [PATCH 18/72] bump opendbc (#36001) --- opendbc_repo | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 3024c6fccb..18f8c0e757 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 3024c6fccbaea8f7b4760a3c295e7c8fb8e8b4a6 +Subproject commit 18f8c0e757906c779a16b70817b2c0fb5020f406 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 54ff189358..dd1f0b6a06 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -543bd2347fa35f8300478a3893fdd0a03a7c1fe6 \ No newline at end of file +4536719353fc9eeb4b9c57692cd72766a2f86397 \ No newline at end of file From 1805a47139238a9e8c7212f740f2d46321536ab6 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 15 Aug 2025 11:13:45 -0700 Subject: [PATCH 19/72] USB takes forever to come up... --- selfdrive/pandad/pandad.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/pandad/pandad.py b/selfdrive/pandad/pandad.py index b33ffb6473..4e49813f5d 100755 --- a/selfdrive/pandad/pandad.py +++ b/selfdrive/pandad/pandad.py @@ -87,7 +87,7 @@ def main() -> None: # TODO: remove this in the next AGNOS # wait until USB is up before counting - if time.monotonic() < 35.: + if time.monotonic() < 60.: no_internal_panda_count = 0 # Handle missing internal panda From ab44c9a4ff905db21c77c5328323653767650275 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 15 Aug 2025 11:39:56 -0700 Subject: [PATCH 20/72] Torque controller: refactor calculations to be in accel space (#35790) * clean up * little confusing but works * clean up * fix * pid outputs torque again, fix windup above max torque * clean up * fix * fix * typo * fix conflicts * fix PID * cleanups * seems correct * updte * inverse * whitespace * move * small cleanup * more cleanup * update ref --------- Co-authored-by: Bruce Wayne --- common/pid.py | 7 +++-- opendbc_repo | 2 +- selfdrive/controls/lib/latcontrol_torque.py | 28 +++++++++++-------- selfdrive/controls/lib/tests/__init__.py | 0 .../{lib => }/tests/test_latcontrol.py | 4 ++- selfdrive/test/process_replay/ref_commit | 2 +- 6 files changed, 26 insertions(+), 17 deletions(-) delete mode 100644 selfdrive/controls/lib/tests/__init__.py rename selfdrive/controls/{lib => }/tests/test_latcontrol.py (88%) diff --git a/common/pid.py b/common/pid.py index 721a7c9d65..99142280ca 100644 --- a/common/pid.py +++ b/common/pid.py @@ -14,8 +14,7 @@ class PIDController: if isinstance(self._k_d, Number): self._k_d = [[0], [self._k_d]] - self.pos_limit = pos_limit - self.neg_limit = neg_limit + self.set_limits(pos_limit, neg_limit) self.i_rate = 1.0 / rate self.speed = 0.0 @@ -41,6 +40,10 @@ class PIDController: self.f = 0.0 self.control = 0 + def set_limits(self, pos_limit, neg_limit): + self.pos_limit = pos_limit + self.neg_limit = neg_limit + def update(self, error, error_rate=0.0, speed=0.0, feedforward=0., freeze_integrator=False): self.speed = speed self.p = float(error) * self.k_p diff --git a/opendbc_repo b/opendbc_repo index 18f8c0e757..74bfaa2c75 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 18f8c0e757906c779a16b70817b2c0fb5020f406 +Subproject commit 74bfaa2c750f2eca584dd6b58c1b862ba59d29d8 diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index c368a5da43..dffe85c473 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -3,7 +3,6 @@ import numpy as np from cereal import log from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction -from opendbc.car.interfaces import LatControlInputs from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY from openpilot.selfdrive.controls.lib.latcontrol import LatControl from openpilot.common.pid import PIDController @@ -27,15 +26,22 @@ class LatControlTorque(LatControl): def __init__(self, CP, CI): super().__init__(CP, CI) self.torque_params = CP.lateralTuning.torque.as_builder() - self.pid = PIDController(self.torque_params.kp, self.torque_params.ki, - k_f=self.torque_params.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max) self.torque_from_lateral_accel = CI.torque_from_lateral_accel() + self.lateral_accel_from_torque = CI.lateral_accel_from_torque() + self.pid = PIDController(self.torque_params.kp, self.torque_params.ki, + k_f=self.torque_params.kf) + self.update_limits() self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction): self.torque_params.latAccelFactor = latAccelFactor self.torque_params.latAccelOffset = latAccelOffset self.torque_params.friction = friction + self.update_limits() + + def update_limits(self): + self.pid.set_limits(self.lateral_accel_from_torque(self.steer_max, self.torque_params), + self.lateral_accel_from_torque(-self.steer_max, self.torque_params)) def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited): pid_log = log.ControlsState.LateralTorqueState.new_message() @@ -57,27 +63,25 @@ class LatControlTorque(LatControl): setpoint = desired_lateral_accel + low_speed_factor * desired_curvature measurement = actual_lateral_accel + low_speed_factor * actual_curvature gravity_adjusted_lateral_accel = desired_lateral_accel - roll_compensation - torque_from_setpoint = self.torque_from_lateral_accel(LatControlInputs(setpoint, roll_compensation, CS.vEgo, CS.aEgo), self.torque_params, - gravity_adjusted=False) - torque_from_measurement = self.torque_from_lateral_accel(LatControlInputs(measurement, roll_compensation, CS.vEgo, CS.aEgo), self.torque_params, - gravity_adjusted=False) - pid_log.error = float(torque_from_setpoint - torque_from_measurement) - ff = self.torque_from_lateral_accel(LatControlInputs(gravity_adjusted_lateral_accel, roll_compensation, CS.vEgo, CS.aEgo), self.torque_params, - gravity_adjusted=True) + + # do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly + pid_log.error = float(setpoint - measurement) + ff = gravity_adjusted_lateral_accel ff += get_friction(desired_lateral_accel - actual_lateral_accel, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params) freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5 - output_torque = self.pid.update(pid_log.error, + output_lataccel = self.pid.update(pid_log.error, feedforward=ff, speed=CS.vEgo, freeze_integrator=freeze_integrator) + output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params) pid_log.active = True pid_log.p = float(self.pid.p) pid_log.i = float(self.pid.i) pid_log.d = float(self.pid.d) pid_log.f = float(self.pid.f) - pid_log.output = float(-output_torque) + pid_log.output = float(-output_torque) # TODO: log lat accel? pid_log.actualLateralAccel = float(actual_lateral_accel) pid_log.desiredLateralAccel = float(desired_lateral_accel) pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited)) diff --git a/selfdrive/controls/lib/tests/__init__.py b/selfdrive/controls/lib/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/selfdrive/controls/lib/tests/test_latcontrol.py b/selfdrive/controls/tests/test_latcontrol.py similarity index 88% rename from selfdrive/controls/lib/tests/test_latcontrol.py rename to selfdrive/controls/tests/test_latcontrol.py index f32e20b1e5..0ce06dc996 100644 --- a/selfdrive/controls/lib/tests/test_latcontrol.py +++ b/selfdrive/controls/tests/test_latcontrol.py @@ -5,6 +5,7 @@ from opendbc.car.car_helpers import interfaces from opendbc.car.honda.values import CAR as HONDA from opendbc.car.toyota.values import CAR as TOYOTA from opendbc.car.nissan.values import CAR as NISSAN +from opendbc.car.gm.values import CAR as GM from opendbc.car.vehicle_model import VehicleModel from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque @@ -13,7 +14,8 @@ from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle class TestLatControl: - @parameterized.expand([(HONDA.HONDA_CIVIC, LatControlPID), (TOYOTA.TOYOTA_RAV4, LatControlTorque), (NISSAN.NISSAN_LEAF, LatControlAngle)]) + @parameterized.expand([(HONDA.HONDA_CIVIC, LatControlPID), (TOYOTA.TOYOTA_RAV4, LatControlTorque), + (NISSAN.NISSAN_LEAF, LatControlAngle), (GM.CHEVROLET_BOLT_EUV, LatControlTorque)]) def test_saturation(self, car_name, controller): CarInterface = interfaces[car_name] CP = CarInterface.get_non_essential_params(car_name) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index dd1f0b6a06..a4297096c0 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -4536719353fc9eeb4b9c57692cd72766a2f86397 \ No newline at end of file +6d3219bca9f66a229b38a5382d301a92b0147edb \ No newline at end of file From 372682d4a90530615368ca68cf2cb5cb2f63d99b Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 15 Aug 2025 14:46:20 -0700 Subject: [PATCH 21/72] updated: branch migration (#35993) * release * Update system/updated/updated.py Co-authored-by: Adeeb Shihadeh --------- Co-authored-by: Adeeb Shihadeh --- Jenkinsfile | 2 +- system/updated/updated.py | 3 +++ system/version.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index a14bf59299..0905abd6da 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -167,7 +167,7 @@ node { env.GIT_COMMIT = checkout(scm).GIT_COMMIT def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging', - 'testing-closet*', 'hotfix-*'] + 'release-tici', 'testing-closet*', 'hotfix-*'] def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*') if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) { diff --git a/system/updated/updated.py b/system/updated/updated.py index 72f9de7d01..11928bc24c 100755 --- a/system/updated/updated.py +++ b/system/updated/updated.py @@ -242,6 +242,9 @@ class Updater: b: str | None = self.params.get("UpdaterTargetBranch") if b is None: b = self.get_branch(BASEDIR) + b = { + ("tici", "release3"): "release-tici" + }.get((HARDWARE.get_device_type(), b), b) return b @property diff --git a/system/version.py b/system/version.py index cd0e2d7f38..5e4fcf5c33 100755 --- a/system/version.py +++ b/system/version.py @@ -10,7 +10,7 @@ from openpilot.common.basedir import BASEDIR from openpilot.common.swaglog import cloudlog from openpilot.common.git import get_commit, get_origin, get_branch, get_short_branch, get_commit_date -RELEASE_BRANCHES = ['release3-staging', 'release3', 'nightly'] +RELEASE_BRANCHES = ['release3-staging', 'release3', 'release-tici', 'nightly'] TESTED_BRANCHES = RELEASE_BRANCHES + ['devel', 'devel-staging', 'nightly-dev'] BUILD_METADATA_FILENAME = "build.json" From 6a67f9e56fd1fbcfbb1ff1701df2a2b02f9805a8 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 15 Aug 2025 23:10:47 -0700 Subject: [PATCH 22/72] setup: custom software warning (#36003) * warn * msg * label * space * Revert "space" This reverts commit ae9b8ad1149612c5741ae3b091740170238473ed. --- system/ui/setup.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/system/ui/setup.py b/system/ui/setup.py index 72718b5d6d..800ca7662c 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -13,6 +13,7 @@ import pyray as rl from cereal import log from openpilot.common.run import run_cmd from openpilot.system.hardware import HARDWARE +from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import Button, ButtonStyle, ButtonRadio @@ -109,14 +110,21 @@ class Setup(Widget): self._network_setup_continue_button.set_enabled(False) self._network_setup_title_label = Label("Connect to Wi-Fi", TITLE_FONT_SIZE, FontWeight.BOLD, TextAlignment.LEFT) - self._custom_software_warning_continue_button = Button("Continue", self._custom_software_warning_continue_button_callback) + self._custom_software_warning_continue_button = Button("Scroll to continue", self._custom_software_warning_continue_button_callback, + button_style=ButtonStyle.PRIMARY) + self._custom_software_warning_continue_button.set_enabled(False) self._custom_software_warning_back_button = Button("Back", self._custom_software_warning_back_button_callback) self._custom_software_warning_title_label = Label("WARNING: Custom Software", 100, FontWeight.BOLD, TextAlignment.LEFT, text_color=rl.Color(255,89,79,255), text_padding=60) - self._custom_software_warning_body_label = Label("Use caution when installing third-party software. Third-party software has not been tested by comma," - + " and may cause damage to your device and/or vehicle.\n\nIf you'd like to proceed, use https://flash.comma.ai " + self._custom_software_warning_body_label = Label("Use caution when installing third-party software.\n\n" + + "⚠️ It has not been tested by comma.\n\n" + + "⚠️ It may not comply with relevant safety standards.\n\n" + + "⚠️ It may cause damage to your device and/or vehicle.\n\n" + + "If you'd like to proceed, use https://flash.comma.ai " + "to restore your device to a factory state later.", 85, text_alignment=TextAlignment.LEFT, text_padding=60) + self._custom_software_warning_body_scroll_panel = GuiScrollPanel() + self._downloading_body_label = Label("Downloading...", TITLE_FONT_SIZE, FontWeight.MEDIUM) try: @@ -291,13 +299,23 @@ class Setup(Widget): self._download_failed_startover_button.render(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT)) def render_custom_software_warning(self, rect: rl.Rectangle): - self._custom_software_warning_title_label.render(rl.Rectangle(rect.x + 50, rect.y + 150, rect.width - 265, TITLE_FONT_SIZE)) - self._custom_software_warning_body_label.render(rl.Rectangle(rect.x + 50, rect.y + 200 , rect.width - 50, BODY_FONT_SIZE * 3)) + warn_rect = rl.Rectangle(rect.x, rect.y, rect.width, 1500) + offset = self._custom_software_warning_body_scroll_panel.handle_scroll(rect, warn_rect) button_width = (rect.width - MARGIN * 3) / 2 button_y = rect.height - MARGIN - BUTTON_HEIGHT + + rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(button_y - BODY_FONT_SIZE)) + y_offset = rect.y + offset.y + self._custom_software_warning_title_label.render(rl.Rectangle(rect.x + 50, y_offset + 150, rect.width - 265, TITLE_FONT_SIZE)) + self._custom_software_warning_body_label.render(rl.Rectangle(rect.x + 50, y_offset + 200 , rect.width - 50, BODY_FONT_SIZE * 3)) + rl.end_scissor_mode() + self._custom_software_warning_back_button.render(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT)) self._custom_software_warning_continue_button.render(rl.Rectangle(rect.x + MARGIN * 2 + button_width, button_y, button_width, BUTTON_HEIGHT)) + if offset.y < (rect.height - warn_rect.height): + self._custom_software_warning_continue_button.set_enabled(True) + self._custom_software_warning_continue_button.set_text("Continue") def render_custom_software(self): def handle_keyboard_result(result): From ceb557058c5a7e7fd4de8c2dc5608e32c8773eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Sun, 17 Aug 2025 10:18:30 -0700 Subject: [PATCH 23/72] Steam Powered model (#36000) * f3e67f3e-6079-48cf-92a4-dee5eebd1d73/360 * f3e67f3e-6079-48cf-92a4-dee5eebd1d73/400 * No more action head: a8f96b93-bde2-4e28-a732-4df21ebba968/400 --- selfdrive/modeld/modeld.py | 11 ----------- selfdrive/modeld/models/driving_policy.onnx | 4 ++-- selfdrive/modeld/models/driving_vision.onnx | 4 ++-- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 707b221bb9..8bc8bf01ab 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -102,15 +102,12 @@ class ModelState: self.full_features_buffer = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32) self.full_desire = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32) - self.full_prev_desired_curv = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32) self.temporal_idxs = slice(-1-(ModelConstants.TEMPORAL_SKIP*(ModelConstants.INPUT_HISTORY_BUFFER_LEN-1)), None, ModelConstants.TEMPORAL_SKIP) # policy inputs self.numpy_inputs = { 'desire': np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32), 'traffic_convention': np.zeros((1, ModelConstants.TRAFFIC_CONVENTION_LEN), dtype=np.float32), - 'lateral_control_params': np.zeros((1, ModelConstants.LATERAL_CONTROL_PARAMS_LEN), dtype=np.float32), - 'prev_desired_curv': np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32), 'features_buffer': np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32), } @@ -143,7 +140,6 @@ class ModelState: self.numpy_inputs['desire'][:] = self.full_desire.reshape((1,ModelConstants.INPUT_HISTORY_BUFFER_LEN,ModelConstants.TEMPORAL_SKIP,-1)).max(axis=2) self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention'] - self.numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params'] imgs_cl = {name: self.frames[name].prepare(bufs[name], transforms[name].flatten()) for name in self.vision_input_names} if TICI and not USBGPU: @@ -169,11 +165,6 @@ class ModelState: self.policy_output = self.policy_run(**self.policy_inputs).contiguous().realize().uop.base.buffer.numpy() policy_outputs_dict = self.parser.parse_policy_outputs(self.slice_outputs(self.policy_output, self.policy_output_slices)) - # TODO model only uses last value now - self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:] - self.full_prev_desired_curv[0,-1,:] = policy_outputs_dict['desired_curvature'][0, :] - self.numpy_inputs['prev_desired_curv'][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs] - combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict} if SEND_RAW_PRED: combined_outputs_dict['raw_pred'] = np.concatenate([self.vision_output.copy(), self.policy_output.copy()]) @@ -292,7 +283,6 @@ def main(demo=False): frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) lat_delay = sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS - lateral_control_params = np.array([v_ego, lat_delay], dtype=np.float32) if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] @@ -325,7 +315,6 @@ def main(demo=False): inputs:dict[str, np.ndarray] = { 'desire': vec_desire, 'traffic_convention': traffic_convention, - 'lateral_control_params': lateral_control_params, } mt1 = time.perf_counter() diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 267fc92a3f..867a0d3b9b 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1af87c38492444521632a0e75839b5684ee46bf255b3474773784bffb9fe4f57 -size 15583374 +oid sha256:04b763fb71efe57a8a4c4168a8043ecd58939015026ded0dc755ded6905ac251 +size 12343523 diff --git a/selfdrive/modeld/models/driving_vision.onnx b/selfdrive/modeld/models/driving_vision.onnx index 18f63358db..ce0dc927e7 100644 --- a/selfdrive/modeld/models/driving_vision.onnx +++ b/selfdrive/modeld/models/driving_vision.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c824f68646a3b94f117f01c70dc8316fb466e05fbd42ccdba440b8a8dc86914b -size 46265993 +oid sha256:e66bb8d53eced3786ed71a59b55ffc6810944cb217f0518621cc76303260a1ef +size 46271991 From 63fa250f291c26ce933969fb821b26e9915d6524 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 17 Aug 2025 11:53:20 -0700 Subject: [PATCH 24/72] Add note around excessive actuation check (#36010) * Add note around excessive actuation check * Update selfdrived.py --- selfdrive/selfdrived/selfdrived.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index 89fa36c906..cf22040e84 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -238,7 +238,10 @@ class SelfdriveD: if self.sm['driverAssistance'].leftLaneDeparture or self.sm['driverAssistance'].rightLaneDeparture: self.events.add(EventName.ldw) - # Check for excessive actuation + # ****************************************************************************************** + # NOTE: To fork maintainers. + # Disabling or nerfing safety features will get you and your users banned from our servers. + # We recommend that you do not change these numbers from the defaults. if self.sm.updated['liveCalibration']: self.pose_calibrator.feed_live_calib(self.sm['liveCalibration']) if self.sm.updated['livePose']: @@ -253,6 +256,7 @@ class SelfdriveD: if self.excessive_actuation: self.events.add(EventName.excessiveActuation) + # ****************************************************************************************** # Handle lane change if self.sm['modelV2'].meta.laneChangeState == LaneChangeState.preLaneChange: From ef9e430992220cc981249c93ba838deb7637b1e4 Mon Sep 17 00:00:00 2001 From: Muhammed Jaseem Pallikkal <64106637+jasseeeem@users.noreply.github.com> Date: Mon, 18 Aug 2025 02:21:12 +0530 Subject: [PATCH 25/72] Fixed development environment link in CONTRIBUTING.md (#36011) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed development environment link in CONTRIBUTING.md This is my first PR to openpilot 🎉 excited to contribute! * Using absolute path for tools --- docs/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 1950db8a05..f8c27b8815 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -6,7 +6,7 @@ Development is coordinated through [Discord](https://discord.comma.ai) and GitHu ### Getting Started -* Setup your [development environment](../tools/) +* Setup your [development environment](/tools/) * Join our [Discord](https://discord.comma.ai) * Docs are at https://docs.comma.ai and https://blog.comma.ai From 3d879dd1ae9e22fd2d1d3a02a7c8a9da7b5e37de Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Sun, 17 Aug 2025 17:53:50 -0700 Subject: [PATCH 26/72] [bot] Update Python packages (#36012) * Update Python packages * add psa --------- Co-authored-by: Vehicle Researcher Co-authored-by: Adeeb Shihadeh --- opendbc_repo | 2 +- .../test/process_replay/test_processes.py | 2 +- tinygrad_repo | 2 +- uv.lock | 226 +++++++++--------- 4 files changed, 116 insertions(+), 116 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 74bfaa2c75..b2f34136c0 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 74bfaa2c750f2eca584dd6b58c1b862ba59d29d8 +Subproject commit b2f34136c0e58a76d4b4197003a852e72e48767e diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index 54e7039a4e..5868ca4c1c 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -61,7 +61,7 @@ segments = [ ] # dashcamOnly makes don't need to be tested until a full port is done -excluded_interfaces = ["mock", "body"] +excluded_interfaces = ["mock", "body", "psa"] BASE_URL = "https://commadataci.blob.core.windows.net/openpilotci/" REF_COMMIT_FN = os.path.join(PROC_REPLAY_DIR, "ref_commit") diff --git a/tinygrad_repo b/tinygrad_repo index d2bb1bcb97..c30a113b2a 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit d2bb1bcb976f106a41928f2d66d354ab7afd6f59 +Subproject commit c30a113b2a876cabaea1049601fea3a0b758c5b1 diff --git a/uv.lock b/uv.lock index 3b1baa3ee3..7cfaceed97 100644 --- a/uv.lock +++ b/uv.lock @@ -415,31 +415,31 @@ wheels = [ [[package]] name = "cython" -version = "3.1.2" +version = "3.1.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/40/7b17cd866158238db704965da1b5849af261dbad393ea3ac966f934b2d39/cython-3.1.2.tar.gz", hash = "sha256:6bbf7a953fa6762dfecdec015e3b054ba51c0121a45ad851fa130f63f5331381", size = 3184825, upload-time = "2025-06-09T07:08:48.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/ab/915337fb39ab4f4539a313df38fc69938df3bf14141b90d61dfd5c2919de/cython-3.1.3.tar.gz", hash = "sha256:10ee785e42328924b78f75a74f66a813cb956b4a9bc91c44816d089d5934c089", size = 3186689, upload-time = "2025-08-13T06:19:13.619Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/de/502ddebaf5fe78f13cd6361acdd74710d3a5b15c22a9edc0ea4c873a59a5/cython-3.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5548573e0912d7dc80579827493315384c462e2f15797b91a8ed177686d31eb9", size = 3007792, upload-time = "2025-06-09T07:09:28.777Z" }, - { url = "https://files.pythonhosted.org/packages/bb/c8/91b00bc68effba9ba1ff5b33988052ac4d98fc1ac3021ade7261661299c6/cython-3.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bf3ea5bc50d80762c490f42846820a868a6406fdb5878ae9e4cc2f11b50228a", size = 2870798, upload-time = "2025-06-09T07:09:30.745Z" }, - { url = "https://files.pythonhosted.org/packages/f4/4b/29d290f14607785112c00a5e1685d766f433531bbd6a11ad229ab61b7a70/cython-3.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20ce53951d06ab2bca39f153d9c5add1d631c2a44d58bf67288c9d631be9724e", size = 3131280, upload-time = "2025-06-09T07:09:32.785Z" }, - { url = "https://files.pythonhosted.org/packages/38/3c/7c61e9ce25377ec7c4aa0b7ceeed34559ebca7b5cfd384672ba64eeaa4ba/cython-3.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e05a36224e3002d48c7c1c695b3771343bd16bc57eab60d6c5d5e08f3cbbafd8", size = 3223898, upload-time = "2025-06-09T07:09:35.345Z" }, - { url = "https://files.pythonhosted.org/packages/10/96/2d3fbe7e50e98b53ac86fefb48b64262b2e1304b3495e8e25b3cd1c3473e/cython-3.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbc0fc0777c7ab82297c01c61a1161093a22a41714f62e8c35188a309bd5db8e", size = 3291527, upload-time = "2025-06-09T07:09:37.502Z" }, - { url = "https://files.pythonhosted.org/packages/bd/e4/4cd3624e250d86f05bdb121a567865b9cca75cdc6dce4eedd68e626ea4f8/cython-3.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:18161ef3dd0e90a944daa2be468dd27696712a5f792d6289e97d2a31298ad688", size = 3184034, upload-time = "2025-06-09T07:09:40.225Z" }, - { url = "https://files.pythonhosted.org/packages/24/de/f8c1243c3e50ec95cb81f3a7936c8cf162f28050db8683e291c3861b46a0/cython-3.1.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ca45020950cd52d82189d6dfb6225737586be6fe7b0b9d3fadd7daca62eff531", size = 3386084, upload-time = "2025-06-09T07:09:42.206Z" }, - { url = "https://files.pythonhosted.org/packages/c8/95/2365937da44741ef0781bb9ecc1f8f52b38b65acb7293b5fc7c3eaee5346/cython-3.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:aaae97d6d07610224be2b73a93e9e3dd85c09aedfd8e47054e3ef5a863387dae", size = 3309974, upload-time = "2025-06-09T07:09:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b8/280eed114110a1a3aa9e2e76bcd06cdd5ef0df7ab77c0be9d5378ca28c57/cython-3.1.2-cp311-cp311-win32.whl", hash = "sha256:3d439d9b19e7e70f6ff745602906d282a853dd5219d8e7abbf355de680c9d120", size = 2482942, upload-time = "2025-06-09T07:09:46.583Z" }, - { url = "https://files.pythonhosted.org/packages/a2/50/0aa65be5a4ab65bde3224b8fd23ed795f699d1e724ac109bb0a32036b82d/cython-3.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:8efa44ee2f1876e40eb5e45f6513a19758077c56bf140623ccab43d31f873b61", size = 2686535, upload-time = "2025-06-09T07:09:48.345Z" }, - { url = "https://files.pythonhosted.org/packages/22/86/9393ab7204d5bb65f415dd271b658c18f57b9345d06002cae069376a5a7a/cython-3.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9c2c4b6f9a941c857b40168b3f3c81d514e509d985c2dcd12e1a4fea9734192e", size = 3015898, upload-time = "2025-06-09T07:09:50.79Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b8/3d10ac37ab7b7ee60bc6bfb48f6682ebee7fddaccf56e1e135f0d46ca79f/cython-3.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bdbc115bbe1b8c1dcbcd1b03748ea87fa967eb8dfc3a1a9bb243d4a382efcff4", size = 2846204, upload-time = "2025-06-09T07:09:52.832Z" }, - { url = "https://files.pythonhosted.org/packages/f8/34/637771d8e10ebabc34a34cdd0d63fe797b66c334e150189955bf6442d710/cython-3.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05111f89db1ca98edc0675cfaa62be47b3ff519a29876eb095532a9f9e052b8", size = 3080671, upload-time = "2025-06-09T07:09:54.924Z" }, - { url = "https://files.pythonhosted.org/packages/6b/c8/383ad1851fb272920a152c5a30bb6f08c3471b5438079d9488fc3074a170/cython-3.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e7188df8709be32cfdfadc7c3782e361c929df9132f95e1bbc90a340dca3c7", size = 3199022, upload-time = "2025-06-09T07:09:56.978Z" }, - { url = "https://files.pythonhosted.org/packages/e6/11/20adc8f2db37a29f245e8fd4b8b8a8245fce4bbbd128185cc9a7b1065e4c/cython-3.1.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c0ecc71e60a051732c2607b8eb8f2a03a5dac09b28e52b8af323c329db9987b", size = 3241337, upload-time = "2025-06-09T07:09:59.156Z" }, - { url = "https://files.pythonhosted.org/packages/6f/0b/491f1fd3e177cccb6bb6d52f9609f78d395edde83ac47ebb06d21717ca29/cython-3.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f27143cf88835c8bcc9bf3304953f23f377d1d991e8942982fe7be344c7cfce3", size = 3131808, upload-time = "2025-06-09T07:10:01.31Z" }, - { url = "https://files.pythonhosted.org/packages/db/d2/5e7053a3214c9baa7ad72940555eb87cf4750e597f10b2bb43db62c3f39f/cython-3.1.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d8c43566701133f53bf13485839d8f3f309095fe0d3b9d0cd5873073394d2edc", size = 3340319, upload-time = "2025-06-09T07:10:03.485Z" }, - { url = "https://files.pythonhosted.org/packages/95/42/4842f8ddac9b36c94ae08b23c7fcde3f930c1dd49ac8992bb5320a4d96b5/cython-3.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a3bb893e85f027a929c1764bb14db4c31cbdf8a96f59a78f608f2ba7cfbbce95", size = 3287370, upload-time = "2025-06-09T07:10:05.637Z" }, - { url = "https://files.pythonhosted.org/packages/03/0d/417745ed75d414176e50310087b43299a3e611e75c379ff998f60f2ca1a8/cython-3.1.2-cp312-cp312-win32.whl", hash = "sha256:12c5902f105e43ca9af7874cdf87a23627f98c15d5a4f6d38bc9d334845145c0", size = 2487734, upload-time = "2025-06-09T07:10:07.591Z" }, - { url = "https://files.pythonhosted.org/packages/8e/82/df61d09ab81979ba171a8252af8fb8a3b26a0f19d1330c2679c11fe41667/cython-3.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:06789eb7bd2e55b38b9dd349e9309f794aee0fed99c26ea5c9562d463877763f", size = 2695542, upload-time = "2025-06-09T07:10:09.545Z" }, - { url = "https://files.pythonhosted.org/packages/25/d6/ef8557d5e75cc57d55df579af4976935ee111a85bbee4a5b72354e257066/cython-3.1.2-py3-none-any.whl", hash = "sha256:d23fd7ffd7457205f08571a42b108a3cf993e83a59fe4d72b42e6fc592cf2639", size = 1224753, upload-time = "2025-06-09T07:08:44.849Z" }, + { url = "https://files.pythonhosted.org/packages/b5/51/54f5d1bed7b7d003d0584996a030542497aeb05b9241782c217ea71061f5/cython-3.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b3b2f6587b42efdece2d174a2aa4234da4524cc6673f3955c2e62b60c6d11fd", size = 3002973, upload-time = "2025-08-13T06:19:50.777Z" }, + { url = "https://files.pythonhosted.org/packages/05/07/b4043fed60070ee21b0d9ff3a877d2ecdc79231e55119ce852b79b690306/cython-3.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:963cf640d049fcca1cefd62d1653f859892d6dc8e4d958eb49a5babc491de6a1", size = 2865389, upload-time = "2025-08-13T06:19:52.675Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e3/67d349b5310a40f281e153e826ca24d9f7ba2997c06800eda781253dccfd/cython-3.1.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2ab05d1bf2d5522ecff35d94ca233b77db2300413597c3ca0b6448377fa4bd7c", size = 3410316, upload-time = "2025-08-13T06:19:55.217Z" }, + { url = "https://files.pythonhosted.org/packages/49/c4/84aae921135174111091547fa726ea5f8bba718cd12b2589a70c2aab8d5c/cython-3.1.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd517e3be052fb49443585b01f02f46080b3408e32c1108a0fdc4cc25b3c9d30", size = 3189568, upload-time = "2025-08-13T06:19:57.503Z" }, + { url = "https://files.pythonhosted.org/packages/e2/85/1bf18883f1a1f656ad83a671e54553caedb1ee2f39a3e792a14aa82557a2/cython-3.1.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a48e2180d74e3c528561d85b48f9a939a429537f9ea8aac7fb16180e7bff47e2", size = 3312649, upload-time = "2025-08-13T06:19:59.894Z" }, + { url = "https://files.pythonhosted.org/packages/68/da/cc1373decc0d14a25f1b434f47de5e27696f3092319aa5e19fcf84157415/cython-3.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e7c9daa90b15f59aa2a0d638ac1b36777a7e80122099952a0295c71190ce14bc", size = 3203821, upload-time = "2025-08-13T06:20:02.124Z" }, + { url = "https://files.pythonhosted.org/packages/0a/32/e10582d6f7b02ee63607f388dbbd34e329c54559c85961ddc5c655266519/cython-3.1.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:08ac646ff42781827f23b7a9b61669cdb92055f52724cd8cbe0e1defc56fce2e", size = 3426853, upload-time = "2025-08-13T06:20:04.474Z" }, + { url = "https://files.pythonhosted.org/packages/e0/da/5b0d1b5a4c7622ec812ad9374c9c6b426d69818f13f51e36f4f25ca01c86/cython-3.1.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bb0e0e7fceaffa22e4dc9600f7f75998eef5cc6ac5a8c0733b482851ba765ef2", size = 3328872, upload-time = "2025-08-13T06:20:06.834Z" }, + { url = "https://files.pythonhosted.org/packages/b0/5f/f102f6c8d27338f0baf094754c67f920828a19612053abc903e66f84506f/cython-3.1.3-cp311-cp311-win32.whl", hash = "sha256:42b1c3ebe36a52e2a8e939c0651e9ca5d30b81d03f800bbf0499deb0503ab565", size = 2480850, upload-time = "2025-08-13T06:20:08.988Z" }, + { url = "https://files.pythonhosted.org/packages/b7/60/415d0f0f7c2e227707baec11c968387d33507d2eb7f26a2950a323c705e5/cython-3.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:34a973844998281951bf54cdd0b6a9946ba03ba94580820738583a00da167d8f", size = 2712560, upload-time = "2025-08-13T06:20:11.877Z" }, + { url = "https://files.pythonhosted.org/packages/79/26/f433fdfd5182b9231819a99acc49a1f856669532306e7a38dce63ba7297e/cython-3.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:849ef3d15d4354e5f74cdb6d3c80d80b03209b3bf1f4ff93315890b19da18944", size = 3014237, upload-time = "2025-08-13T06:20:13.77Z" }, + { url = "https://files.pythonhosted.org/packages/e5/6c/1bebf44f5f177f8c750e608f82c08cd699b8f28cc233e799379bfcf2a2c2/cython-3.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93dd0f62a3f8e93166d8584f8b243180d681ba8fed1f530b55d5f70c348c5797", size = 2844261, upload-time = "2025-08-13T06:20:15.619Z" }, + { url = "https://files.pythonhosted.org/packages/c7/74/983005ce5954f6dc8360b105a561e51a60ea619c53296afac98253d1c9d7/cython-3.1.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ff4a2cb84798faffb3988bd94636c3ad31a95ff44ef017f09121abffc56f84cf", size = 3388846, upload-time = "2025-08-13T06:20:17.679Z" }, + { url = "https://files.pythonhosted.org/packages/68/50/dbe7edefe9b652bc72d49da07785173e89197b9a2d64a2ac45da9795eccf/cython-3.1.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b05319e36f34d5388deea5cc2bcfd65f9ebf76f4ea050829421a69625dbba4a", size = 3167022, upload-time = "2025-08-13T06:20:19.863Z" }, + { url = "https://files.pythonhosted.org/packages/4a/55/b50548b77203e22262002feae23a172f95282b4b8deb5ad104f08e3dc20d/cython-3.1.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ac902a17934a6da46f80755f49413bc4c03a569ae3c834f5d66da7288ba7e6c", size = 3317782, upload-time = "2025-08-13T06:20:21.962Z" }, + { url = "https://files.pythonhosted.org/packages/f6/1b/20a97507d528dc330d67be4fbad6bc767c681d56192bce8f7117a187b2ad/cython-3.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7d7a555a864b1b08576f9e8a67f3789796a065837544f9f683ebf3188012fdbd", size = 3179818, upload-time = "2025-08-13T06:20:24.419Z" }, + { url = "https://files.pythonhosted.org/packages/43/a5/7b32a19c4c6bb0e2cc7ae52269b6b23a42f67f730e4abd4f8dca63660f7a/cython-3.1.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b827ce7d97ef8624adcf2bdda594b3dcb6c9b4f124d8f72001d8aea27d69dc1c", size = 3403206, upload-time = "2025-08-13T06:20:26.846Z" }, + { url = "https://files.pythonhosted.org/packages/ba/e1/08cfd4c5e99f79e62d4a7b0009bbd906748633270935c8a55eee51fead76/cython-3.1.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7851107204085f4f02d0eb6b660ddcad2ce4846e8b7a1eaba724a0bd3cd68a6b", size = 3327837, upload-time = "2025-08-13T06:20:28.946Z" }, + { url = "https://files.pythonhosted.org/packages/23/1b/f3d384be86fa2a6e110b42f42bf97295a513197aaa5532f451c73bf5b48e/cython-3.1.3-cp312-cp312-win32.whl", hash = "sha256:ed20f1b45b2da5a4f8e71a80025bca1cdc96ba35820b0b17658a4a025be920b0", size = 2485990, upload-time = "2025-08-13T06:20:31.517Z" }, + { url = "https://files.pythonhosted.org/packages/de/f0/17cff75e3c141bda73d770f7412632f53e55778d3bfdadfeec4dd3a7d1d6/cython-3.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:dc4ca0f4dec55124cd79ddcfc555be1cbe0092cc99bcf1403621d17b9c6218bb", size = 2704002, upload-time = "2025-08-13T06:20:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/56/c8/46ac27096684f33e27dab749ef43c6b0119c6a0d852971eaefb73256dc4c/cython-3.1.3-py3-none-any.whl", hash = "sha256:d13025b34f72f77bf7f65c1cd628914763e6c285f4deb934314c922b91e6be5a", size = 1225725, upload-time = "2025-08-13T06:19:09.593Z" }, ] [[package]] @@ -501,36 +501,36 @@ wheels = [ [[package]] name = "filelock" -version = "3.18.0" +version = "3.19.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, + { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, ] [[package]] name = "fonttools" -version = "4.59.0" +version = "4.59.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/27/ec3c723bfdf86f34c5c82bf6305df3e0f0d8ea798d2d3a7cb0c0a866d286/fonttools-4.59.0.tar.gz", hash = "sha256:be392ec3529e2f57faa28709d60723a763904f71a2b63aabe14fee6648fe3b14", size = 3532521, upload-time = "2025-07-16T12:04:54.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/11/7f/29c9c3fe4246f6ad96fee52b88d0dc3a863c7563b0afc959e36d78b965dc/fonttools-4.59.1.tar.gz", hash = "sha256:74995b402ad09822a4c8002438e54940d9f1ecda898d2bb057729d7da983e4cb", size = 3534394, upload-time = "2025-08-14T16:28:14.266Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/96/520733d9602fa1bf6592e5354c6721ac6fc9ea72bc98d112d0c38b967199/fonttools-4.59.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:841b2186adce48903c0fef235421ae21549020eca942c1da773ac380b056ab3c", size = 2782387, upload-time = "2025-07-16T12:03:51.424Z" }, - { url = "https://files.pythonhosted.org/packages/87/6a/170fce30b9bce69077d8eec9bea2cfd9f7995e8911c71be905e2eba6368b/fonttools-4.59.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9bcc1e77fbd1609198966ded6b2a9897bd6c6bcbd2287a2fc7d75f1a254179c5", size = 2342194, upload-time = "2025-07-16T12:03:53.295Z" }, - { url = "https://files.pythonhosted.org/packages/b0/b6/7c8166c0066856f1408092f7968ac744060cf72ca53aec9036106f57eeca/fonttools-4.59.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37c377f7cb2ab2eca8a0b319c68146d34a339792f9420fca6cd49cf28d370705", size = 5032333, upload-time = "2025-07-16T12:03:55.177Z" }, - { url = "https://files.pythonhosted.org/packages/eb/0c/707c5a19598eafcafd489b73c4cb1c142102d6197e872f531512d084aa76/fonttools-4.59.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa39475eaccb98f9199eccfda4298abaf35ae0caec676ffc25b3a5e224044464", size = 4974422, upload-time = "2025-07-16T12:03:57.406Z" }, - { url = "https://files.pythonhosted.org/packages/f6/e7/6d33737d9fe632a0f59289b6f9743a86d2a9d0673de2a0c38c0f54729822/fonttools-4.59.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d3972b13148c1d1fbc092b27678a33b3080d1ac0ca305742b0119b75f9e87e38", size = 5010631, upload-time = "2025-07-16T12:03:59.449Z" }, - { url = "https://files.pythonhosted.org/packages/63/e1/a4c3d089ab034a578820c8f2dff21ef60daf9668034a1e4fb38bb1cc3398/fonttools-4.59.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a408c3c51358c89b29cfa5317cf11518b7ce5de1717abb55c5ae2d2921027de6", size = 5122198, upload-time = "2025-07-16T12:04:01.542Z" }, - { url = "https://files.pythonhosted.org/packages/09/77/ca82b9c12fa4de3c520b7760ee61787640cf3fde55ef1b0bfe1de38c8153/fonttools-4.59.0-cp311-cp311-win32.whl", hash = "sha256:6770d7da00f358183d8fd5c4615436189e4f683bdb6affb02cad3d221d7bb757", size = 2214216, upload-time = "2025-07-16T12:04:03.515Z" }, - { url = "https://files.pythonhosted.org/packages/ab/25/5aa7ca24b560b2f00f260acf32c4cf29d7aaf8656e159a336111c18bc345/fonttools-4.59.0-cp311-cp311-win_amd64.whl", hash = "sha256:84fc186980231a287b28560d3123bd255d3c6b6659828c642b4cf961e2b923d0", size = 2261879, upload-time = "2025-07-16T12:04:05.015Z" }, - { url = "https://files.pythonhosted.org/packages/e2/77/b1c8af22f4265e951cd2e5535dbef8859efcef4fb8dee742d368c967cddb/fonttools-4.59.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f9b3a78f69dcbd803cf2fb3f972779875b244c1115481dfbdd567b2c22b31f6b", size = 2767562, upload-time = "2025-07-16T12:04:06.895Z" }, - { url = "https://files.pythonhosted.org/packages/ff/5a/aeb975699588176bb357e8b398dfd27e5d3a2230d92b81ab8cbb6187358d/fonttools-4.59.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:57bb7e26928573ee7c6504f54c05860d867fd35e675769f3ce01b52af38d48e2", size = 2335168, upload-time = "2025-07-16T12:04:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/54/97/c6101a7e60ae138c4ef75b22434373a0da50a707dad523dd19a4889315bf/fonttools-4.59.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4536f2695fe5c1ffb528d84a35a7d3967e5558d2af58b4775e7ab1449d65767b", size = 4909850, upload-time = "2025-07-16T12:04:10.761Z" }, - { url = "https://files.pythonhosted.org/packages/bd/6c/fa4d18d641054f7bff878cbea14aa9433f292b9057cb1700d8e91a4d5f4f/fonttools-4.59.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:885bde7d26e5b40e15c47bd5def48b38cbd50830a65f98122a8fb90962af7cd1", size = 4955131, upload-time = "2025-07-16T12:04:12.846Z" }, - { url = "https://files.pythonhosted.org/packages/20/5c/331947fc1377deb928a69bde49f9003364f5115e5cbe351eea99e39412a2/fonttools-4.59.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6801aeddb6acb2c42eafa45bc1cb98ba236871ae6f33f31e984670b749a8e58e", size = 4899667, upload-time = "2025-07-16T12:04:14.558Z" }, - { url = "https://files.pythonhosted.org/packages/8a/46/b66469dfa26b8ff0baa7654b2cc7851206c6d57fe3abdabbaab22079a119/fonttools-4.59.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:31003b6a10f70742a63126b80863ab48175fb8272a18ca0846c0482968f0588e", size = 5051349, upload-time = "2025-07-16T12:04:16.388Z" }, - { url = "https://files.pythonhosted.org/packages/2e/05/ebfb6b1f3a4328ab69787d106a7d92ccde77ce66e98659df0f9e3f28d93d/fonttools-4.59.0-cp312-cp312-win32.whl", hash = "sha256:fbce6dae41b692a5973d0f2158f782b9ad05babc2c2019a970a1094a23909b1b", size = 2201315, upload-time = "2025-07-16T12:04:18.557Z" }, - { url = "https://files.pythonhosted.org/packages/09/45/d2bdc9ea20bbadec1016fd0db45696d573d7a26d95ab5174ffcb6d74340b/fonttools-4.59.0-cp312-cp312-win_amd64.whl", hash = "sha256:332bfe685d1ac58ca8d62b8d6c71c2e52a6c64bc218dc8f7825c9ea51385aa01", size = 2249408, upload-time = "2025-07-16T12:04:20.489Z" }, - { url = "https://files.pythonhosted.org/packages/d0/9c/df0ef2c51845a13043e5088f7bb988ca6cd5bb82d5d4203d6a158aa58cf2/fonttools-4.59.0-py3-none-any.whl", hash = "sha256:241313683afd3baacb32a6bd124d0bce7404bc5280e12e291bae1b9bba28711d", size = 1128050, upload-time = "2025-07-16T12:04:52.687Z" }, + { url = "https://files.pythonhosted.org/packages/34/62/9667599561f623d4a523cc9eb4f66f3b94b6155464110fa9aebbf90bbec7/fonttools-4.59.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4909cce2e35706f3d18c54d3dcce0414ba5e0fb436a454dffec459c61653b513", size = 2778815, upload-time = "2025-08-14T16:26:28.484Z" }, + { url = "https://files.pythonhosted.org/packages/8f/78/cc25bcb2ce86033a9df243418d175e58f1956a35047c685ef553acae67d6/fonttools-4.59.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:efbec204fa9f877641747f2d9612b2b656071390d7a7ef07a9dbf0ecf9c7195c", size = 2341631, upload-time = "2025-08-14T16:26:30.396Z" }, + { url = "https://files.pythonhosted.org/packages/a4/cc/fcbb606dd6871f457ac32f281c20bcd6cc77d9fce77b5a4e2b2afab1f500/fonttools-4.59.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39dfd42cc2dc647b2c5469bc7a5b234d9a49e72565b96dd14ae6f11c2c59ef15", size = 5022222, upload-time = "2025-08-14T16:26:32.447Z" }, + { url = "https://files.pythonhosted.org/packages/61/96/c0b1cf2b74d08eb616a80dbf5564351fe4686147291a25f7dce8ace51eb3/fonttools-4.59.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b11bc177a0d428b37890825d7d025040d591aa833f85f8d8878ed183354f47df", size = 4966512, upload-time = "2025-08-14T16:26:34.621Z" }, + { url = "https://files.pythonhosted.org/packages/a4/26/51ce2e3e0835ffc2562b1b11d1fb9dafd0aca89c9041b64a9e903790a761/fonttools-4.59.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b9b4c35b3be45e5bc774d3fc9608bbf4f9a8d371103b858c80edbeed31dd5aa", size = 5001645, upload-time = "2025-08-14T16:26:36.876Z" }, + { url = "https://files.pythonhosted.org/packages/36/11/ef0b23f4266349b6d5ccbd1a07b7adc998d5bce925792aa5d1ec33f593e3/fonttools-4.59.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:01158376b8a418a0bae9625c476cebfcfcb5e6761e9d243b219cd58341e7afbb", size = 5113777, upload-time = "2025-08-14T16:26:39.002Z" }, + { url = "https://files.pythonhosted.org/packages/d0/da/b398fe61ef433da0a0472cdb5d4399124f7581ffe1a31b6242c91477d802/fonttools-4.59.1-cp311-cp311-win32.whl", hash = "sha256:cf7c5089d37787387123f1cb8f1793a47c5e1e3d1e4e7bfbc1cc96e0f925eabe", size = 2215076, upload-time = "2025-08-14T16:26:41.196Z" }, + { url = "https://files.pythonhosted.org/packages/94/bd/e2624d06ab94e41c7c77727b2941f1baed7edb647e63503953e6888020c9/fonttools-4.59.1-cp311-cp311-win_amd64.whl", hash = "sha256:c866eef7a0ba320486ade6c32bfc12813d1a5db8567e6904fb56d3d40acc5116", size = 2262779, upload-time = "2025-08-14T16:26:43.483Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fe/6e069cc4cb8881d164a9bd956e9df555bc62d3eb36f6282e43440200009c/fonttools-4.59.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:43ab814bbba5f02a93a152ee61a04182bb5809bd2bc3609f7822e12c53ae2c91", size = 2769172, upload-time = "2025-08-14T16:26:45.729Z" }, + { url = "https://files.pythonhosted.org/packages/b9/98/ec4e03f748fefa0dd72d9d95235aff6fef16601267f4a2340f0e16b9330f/fonttools-4.59.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4f04c3ffbfa0baafcbc550657cf83657034eb63304d27b05cff1653b448ccff6", size = 2337281, upload-time = "2025-08-14T16:26:47.921Z" }, + { url = "https://files.pythonhosted.org/packages/8b/b1/890360a7e3d04a30ba50b267aca2783f4c1364363797e892e78a4f036076/fonttools-4.59.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d601b153e51a5a6221f0d4ec077b6bfc6ac35bfe6c19aeaa233d8990b2b71726", size = 4909215, upload-time = "2025-08-14T16:26:49.682Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ec/2490599550d6c9c97a44c1e36ef4de52d6acf742359eaa385735e30c05c4/fonttools-4.59.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c735e385e30278c54f43a0d056736942023c9043f84ee1021eff9fd616d17693", size = 4951958, upload-time = "2025-08-14T16:26:51.616Z" }, + { url = "https://files.pythonhosted.org/packages/d1/40/bd053f6f7634234a9b9805ff8ae4f32df4f2168bee23cafd1271ba9915a9/fonttools-4.59.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1017413cdc8555dce7ee23720da490282ab7ec1cf022af90a241f33f9a49afc4", size = 4894738, upload-time = "2025-08-14T16:26:53.836Z" }, + { url = "https://files.pythonhosted.org/packages/ac/a1/3cd12a010d288325a7cfcf298a84825f0f9c29b01dee1baba64edfe89257/fonttools-4.59.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5c6d8d773470a5107052874341ed3c487c16ecd179976d81afed89dea5cd7406", size = 5045983, upload-time = "2025-08-14T16:26:56.153Z" }, + { url = "https://files.pythonhosted.org/packages/a2/af/8a2c3f6619cc43cf87951405337cc8460d08a4e717bb05eaa94b335d11dc/fonttools-4.59.1-cp312-cp312-win32.whl", hash = "sha256:2a2d0d33307f6ad3a2086a95dd607c202ea8852fa9fb52af9b48811154d1428a", size = 2203407, upload-time = "2025-08-14T16:26:58.165Z" }, + { url = "https://files.pythonhosted.org/packages/8e/f2/a19b874ddbd3ebcf11d7e25188ef9ac3f68b9219c62263acb34aca8cde05/fonttools-4.59.1-cp312-cp312-win_amd64.whl", hash = "sha256:0b9e4fa7eaf046ed6ac470f6033d52c052481ff7a6e0a92373d14f556f298dc0", size = 2251561, upload-time = "2025-08-14T16:27:00.646Z" }, + { url = "https://files.pythonhosted.org/packages/0f/64/9d606e66d498917cd7a2ff24f558010d42d6fd4576d9dd57f0bd98333f5a/fonttools-4.59.1-py3-none-any.whl", hash = "sha256:647db657073672a8330608970a984d51573557f328030566521bc03415535042", size = 1130094, upload-time = "2025-08-14T16:28:12.048Z" }, ] [[package]] @@ -1536,16 +1536,16 @@ wheels = [ [[package]] name = "protobuf" -version = "6.31.1" +version = "6.32.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797, upload-time = "2025-05-28T19:25:54.947Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/df/fb4a8eeea482eca989b51cffd274aac2ee24e825f0bf3cbce5281fa1567b/protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2", size = 440614, upload-time = "2025-08-14T21:21:25.015Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603, upload-time = "2025-05-28T19:25:41.198Z" }, - { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283, upload-time = "2025-05-28T19:25:44.275Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604, upload-time = "2025-05-28T19:25:45.702Z" }, - { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115, upload-time = "2025-05-28T19:25:47.128Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070, upload-time = "2025-05-28T19:25:50.036Z" }, - { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724, upload-time = "2025-05-28T19:25:53.926Z" }, + { url = "https://files.pythonhosted.org/packages/33/18/df8c87da2e47f4f1dcc5153a81cd6bca4e429803f4069a299e236e4dd510/protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741", size = 424409, upload-time = "2025-08-14T21:21:12.366Z" }, + { url = "https://files.pythonhosted.org/packages/e1/59/0a820b7310f8139bd8d5a9388e6a38e1786d179d6f33998448609296c229/protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e", size = 435735, upload-time = "2025-08-14T21:21:15.046Z" }, + { url = "https://files.pythonhosted.org/packages/cc/5b/0d421533c59c789e9c9894683efac582c06246bf24bb26b753b149bd88e4/protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0", size = 426449, upload-time = "2025-08-14T21:21:16.687Z" }, + { url = "https://files.pythonhosted.org/packages/ec/7b/607764ebe6c7a23dcee06e054fd1de3d5841b7648a90fd6def9a3bb58c5e/protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1", size = 322869, upload-time = "2025-08-14T21:21:18.282Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/2e730bd1c25392fc32e3268e02446f0d77cb51a2c3a8486b1798e34d5805/protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c", size = 322009, upload-time = "2025-08-14T21:21:19.893Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" }, ] [[package]] @@ -4310,7 +4310,7 @@ wheels = [ [[package]] name = "pytest-xdist" -version = "3.7.1.dev24+g2b4372b" +version = "3.7.1.dev24+g2b4372bd6" source = { git = "https://github.com/sshane/pytest-xdist?rev=2b4372bd62699fb412c4fe2f95bf9f01bd2018da#2b4372bd62699fb412c4fe2f95bf9f01bd2018da" } dependencies = [ { name = "execnet" }, @@ -4586,27 +4586,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.12.8" +version = "0.12.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4b/da/5bd7565be729e86e1442dad2c9a364ceeff82227c2dece7c29697a9795eb/ruff-0.12.8.tar.gz", hash = "sha256:4cb3a45525176e1009b2b64126acf5f9444ea59066262791febf55e40493a033", size = 5242373, upload-time = "2025-08-07T19:05:47.268Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/45/2e403fa7007816b5fbb324cb4f8ed3c7402a927a0a0cb2b6279879a8bfdc/ruff-0.12.9.tar.gz", hash = "sha256:fbd94b2e3c623f659962934e52c2bea6fc6da11f667a427a368adaf3af2c866a", size = 5254702, upload-time = "2025-08-14T16:08:55.2Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/1e/c843bfa8ad1114fab3eb2b78235dda76acd66384c663a4e0415ecc13aa1e/ruff-0.12.8-py3-none-linux_armv6l.whl", hash = "sha256:63cb5a5e933fc913e5823a0dfdc3c99add73f52d139d6cd5cc8639d0e0465513", size = 11675315, upload-time = "2025-08-07T19:05:06.15Z" }, - { url = "https://files.pythonhosted.org/packages/24/ee/af6e5c2a8ca3a81676d5480a1025494fd104b8896266502bb4de2a0e8388/ruff-0.12.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a9bbe28f9f551accf84a24c366c1aa8774d6748438b47174f8e8565ab9dedbc", size = 12456653, upload-time = "2025-08-07T19:05:09.759Z" }, - { url = "https://files.pythonhosted.org/packages/99/9d/e91f84dfe3866fa648c10512904991ecc326fd0b66578b324ee6ecb8f725/ruff-0.12.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2fae54e752a3150f7ee0e09bce2e133caf10ce9d971510a9b925392dc98d2fec", size = 11659690, upload-time = "2025-08-07T19:05:12.551Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ac/a363d25ec53040408ebdd4efcee929d48547665858ede0505d1d8041b2e5/ruff-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0acbcf01206df963d9331b5838fb31f3b44fa979ee7fa368b9b9057d89f4a53", size = 11896923, upload-time = "2025-08-07T19:05:14.821Z" }, - { url = "https://files.pythonhosted.org/packages/58/9f/ea356cd87c395f6ade9bb81365bd909ff60860975ca1bc39f0e59de3da37/ruff-0.12.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae3e7504666ad4c62f9ac8eedb52a93f9ebdeb34742b8b71cd3cccd24912719f", size = 11477612, upload-time = "2025-08-07T19:05:16.712Z" }, - { url = "https://files.pythonhosted.org/packages/1a/46/92e8fa3c9dcfd49175225c09053916cb97bb7204f9f899c2f2baca69e450/ruff-0.12.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb82efb5d35d07497813a1c5647867390a7d83304562607f3579602fa3d7d46f", size = 13182745, upload-time = "2025-08-07T19:05:18.709Z" }, - { url = "https://files.pythonhosted.org/packages/5e/c4/f2176a310f26e6160deaf661ef60db6c3bb62b7a35e57ae28f27a09a7d63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dbea798fc0065ad0b84a2947b0aff4233f0cb30f226f00a2c5850ca4393de609", size = 14206885, upload-time = "2025-08-07T19:05:21.025Z" }, - { url = "https://files.pythonhosted.org/packages/87/9d/98e162f3eeeb6689acbedbae5050b4b3220754554526c50c292b611d3a63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49ebcaccc2bdad86fd51b7864e3d808aad404aab8df33d469b6e65584656263a", size = 13639381, upload-time = "2025-08-07T19:05:23.423Z" }, - { url = "https://files.pythonhosted.org/packages/81/4e/1b7478b072fcde5161b48f64774d6edd59d6d198e4ba8918d9f4702b8043/ruff-0.12.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ac9c570634b98c71c88cb17badd90f13fc076a472ba6ef1d113d8ed3df109fb", size = 12613271, upload-time = "2025-08-07T19:05:25.507Z" }, - { url = "https://files.pythonhosted.org/packages/e8/67/0c3c9179a3ad19791ef1b8f7138aa27d4578c78700551c60d9260b2c660d/ruff-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560e0cd641e45591a3e42cb50ef61ce07162b9c233786663fdce2d8557d99818", size = 12847783, upload-time = "2025-08-07T19:05:28.14Z" }, - { url = "https://files.pythonhosted.org/packages/4e/2a/0b6ac3dd045acf8aa229b12c9c17bb35508191b71a14904baf99573a21bd/ruff-0.12.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:71c83121512e7743fba5a8848c261dcc454cafb3ef2934a43f1b7a4eb5a447ea", size = 11702672, upload-time = "2025-08-07T19:05:30.413Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ee/f9fdc9f341b0430110de8b39a6ee5fa68c5706dc7c0aa940817947d6937e/ruff-0.12.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:de4429ef2ba091ecddedd300f4c3f24bca875d3d8b23340728c3cb0da81072c3", size = 11440626, upload-time = "2025-08-07T19:05:32.492Z" }, - { url = "https://files.pythonhosted.org/packages/89/fb/b3aa2d482d05f44e4d197d1de5e3863feb13067b22c571b9561085c999dc/ruff-0.12.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a2cab5f60d5b65b50fba39a8950c8746df1627d54ba1197f970763917184b161", size = 12462162, upload-time = "2025-08-07T19:05:34.449Z" }, - { url = "https://files.pythonhosted.org/packages/18/9f/5c5d93e1d00d854d5013c96e1a92c33b703a0332707a7cdbd0a4880a84fb/ruff-0.12.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:45c32487e14f60b88aad6be9fd5da5093dbefb0e3e1224131cb1d441d7cb7d46", size = 12913212, upload-time = "2025-08-07T19:05:36.541Z" }, - { url = "https://files.pythonhosted.org/packages/71/13/ab9120add1c0e4604c71bfc2e4ef7d63bebece0cfe617013da289539cef8/ruff-0.12.8-py3-none-win32.whl", hash = "sha256:daf3475060a617fd5bc80638aeaf2f5937f10af3ec44464e280a9d2218e720d3", size = 11694382, upload-time = "2025-08-07T19:05:38.468Z" }, - { url = "https://files.pythonhosted.org/packages/f6/dc/a2873b7c5001c62f46266685863bee2888caf469d1edac84bf3242074be2/ruff-0.12.8-py3-none-win_amd64.whl", hash = "sha256:7209531f1a1fcfbe8e46bcd7ab30e2f43604d8ba1c49029bb420b103d0b5f76e", size = 12740482, upload-time = "2025-08-07T19:05:40.391Z" }, - { url = "https://files.pythonhosted.org/packages/cb/5c/799a1efb8b5abab56e8a9f2a0b72d12bd64bb55815e9476c7d0a2887d2f7/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749", size = 11884718, upload-time = "2025-08-07T19:05:42.866Z" }, + { url = "https://files.pythonhosted.org/packages/ad/20/53bf098537adb7b6a97d98fcdebf6e916fcd11b2e21d15f8c171507909cc/ruff-0.12.9-py3-none-linux_armv6l.whl", hash = "sha256:fcebc6c79fcae3f220d05585229463621f5dbf24d79fdc4936d9302e177cfa3e", size = 11759705, upload-time = "2025-08-14T16:08:12.968Z" }, + { url = "https://files.pythonhosted.org/packages/20/4d/c764ee423002aac1ec66b9d541285dd29d2c0640a8086c87de59ebbe80d5/ruff-0.12.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aed9d15f8c5755c0e74467731a007fcad41f19bcce41cd75f768bbd687f8535f", size = 12527042, upload-time = "2025-08-14T16:08:16.54Z" }, + { url = "https://files.pythonhosted.org/packages/8b/45/cfcdf6d3eb5fc78a5b419e7e616d6ccba0013dc5b180522920af2897e1be/ruff-0.12.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5b15ea354c6ff0d7423814ba6d44be2807644d0c05e9ed60caca87e963e93f70", size = 11724457, upload-time = "2025-08-14T16:08:18.686Z" }, + { url = "https://files.pythonhosted.org/packages/72/e6/44615c754b55662200c48bebb02196dbb14111b6e266ab071b7e7297b4ec/ruff-0.12.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d596c2d0393c2502eaabfef723bd74ca35348a8dac4267d18a94910087807c53", size = 11949446, upload-time = "2025-08-14T16:08:21.059Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d1/9b7d46625d617c7df520d40d5ac6cdcdf20cbccb88fad4b5ecd476a6bb8d/ruff-0.12.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b15599931a1a7a03c388b9c5df1bfa62be7ede6eb7ef753b272381f39c3d0ff", size = 11566350, upload-time = "2025-08-14T16:08:23.433Z" }, + { url = "https://files.pythonhosted.org/packages/59/20/b73132f66f2856bc29d2d263c6ca457f8476b0bbbe064dac3ac3337a270f/ruff-0.12.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d02faa2977fb6f3f32ddb7828e212b7dd499c59eb896ae6c03ea5c303575756", size = 13270430, upload-time = "2025-08-14T16:08:25.837Z" }, + { url = "https://files.pythonhosted.org/packages/a2/21/eaf3806f0a3d4c6be0a69d435646fba775b65f3f2097d54898b0fd4bb12e/ruff-0.12.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:17d5b6b0b3a25259b69ebcba87908496e6830e03acfb929ef9fd4c58675fa2ea", size = 14264717, upload-time = "2025-08-14T16:08:27.907Z" }, + { url = "https://files.pythonhosted.org/packages/d2/82/1d0c53bd37dcb582b2c521d352fbf4876b1e28bc0d8894344198f6c9950d/ruff-0.12.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72db7521860e246adbb43f6ef464dd2a532ef2ef1f5dd0d470455b8d9f1773e0", size = 13684331, upload-time = "2025-08-14T16:08:30.352Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2f/1c5cf6d8f656306d42a686f1e207f71d7cebdcbe7b2aa18e4e8a0cb74da3/ruff-0.12.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a03242c1522b4e0885af63320ad754d53983c9599157ee33e77d748363c561ce", size = 12739151, upload-time = "2025-08-14T16:08:32.55Z" }, + { url = "https://files.pythonhosted.org/packages/47/09/25033198bff89b24d734e6479e39b1968e4c992e82262d61cdccaf11afb9/ruff-0.12.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fc83e4e9751e6c13b5046d7162f205d0a7bac5840183c5beebf824b08a27340", size = 12954992, upload-time = "2025-08-14T16:08:34.816Z" }, + { url = "https://files.pythonhosted.org/packages/52/8e/d0dbf2f9dca66c2d7131feefc386523404014968cd6d22f057763935ab32/ruff-0.12.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:881465ed56ba4dd26a691954650de6ad389a2d1fdb130fe51ff18a25639fe4bb", size = 12899569, upload-time = "2025-08-14T16:08:36.852Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b614d7c08515b1428ed4d3f1d4e3d687deffb2479703b90237682586fa66/ruff-0.12.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:43f07a3ccfc62cdb4d3a3348bf0588358a66da756aa113e071b8ca8c3b9826af", size = 11751983, upload-time = "2025-08-14T16:08:39.314Z" }, + { url = "https://files.pythonhosted.org/packages/58/d6/383e9f818a2441b1a0ed898d7875f11273f10882f997388b2b51cb2ae8b5/ruff-0.12.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:07adb221c54b6bba24387911e5734357f042e5669fa5718920ee728aba3cbadc", size = 11538635, upload-time = "2025-08-14T16:08:41.297Z" }, + { url = "https://files.pythonhosted.org/packages/20/9c/56f869d314edaa9fc1f491706d1d8a47747b9d714130368fbd69ce9024e9/ruff-0.12.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f5cd34fabfdea3933ab85d72359f118035882a01bff15bd1d2b15261d85d5f66", size = 12534346, upload-time = "2025-08-14T16:08:43.39Z" }, + { url = "https://files.pythonhosted.org/packages/bd/4b/d8b95c6795a6c93b439bc913ee7a94fda42bb30a79285d47b80074003ee7/ruff-0.12.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f6be1d2ca0686c54564da8e7ee9e25f93bdd6868263805f8c0b8fc6a449db6d7", size = 13017021, upload-time = "2025-08-14T16:08:45.889Z" }, + { url = "https://files.pythonhosted.org/packages/c7/c1/5f9a839a697ce1acd7af44836f7c2181cdae5accd17a5cb85fcbd694075e/ruff-0.12.9-py3-none-win32.whl", hash = "sha256:cc7a37bd2509974379d0115cc5608a1a4a6c4bff1b452ea69db83c8855d53f93", size = 11734785, upload-time = "2025-08-14T16:08:48.062Z" }, + { url = "https://files.pythonhosted.org/packages/fa/66/cdddc2d1d9a9f677520b7cfc490d234336f523d4b429c1298de359a3be08/ruff-0.12.9-py3-none-win_amd64.whl", hash = "sha256:6fb15b1977309741d7d098c8a3cb7a30bc112760a00fb6efb7abc85f00ba5908", size = 12840654, upload-time = "2025-08-14T16:08:50.158Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fd/669816bc6b5b93b9586f3c1d87cd6bc05028470b3ecfebb5938252c47a35/ruff-0.12.9-py3-none-win_arm64.whl", hash = "sha256:63c8c819739d86b96d500cce885956a1a48ab056bbcbc61b747ad494b2485089", size = 11949623, upload-time = "2025-08-14T16:08:52.233Z" }, ] [[package]] @@ -4620,15 +4621,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.34.1" +version = "2.35.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/38/10d6bfe23df1bfc65ac2262ed10b45823f47f810b0057d3feeea1ca5c7ed/sentry_sdk-2.34.1.tar.gz", hash = "sha256:69274eb8c5c38562a544c3e9f68b5be0a43be4b697f5fd385bf98e4fbe672687", size = 336969, upload-time = "2025-07-30T11:13:37.93Z" } +sdist = { url = "https://files.pythonhosted.org/packages/31/83/055dc157b719651ef13db569bb8cf2103df11174478649735c1b2bf3f6bc/sentry_sdk-2.35.0.tar.gz", hash = "sha256:5ea58d352779ce45d17bc2fa71ec7185205295b83a9dbb5707273deb64720092", size = 343014, upload-time = "2025-08-14T17:11:20.223Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/3e/bb34de65a5787f76848a533afbb6610e01fbcdd59e76d8679c254e02255c/sentry_sdk-2.34.1-py2.py3-none-any.whl", hash = "sha256:b7a072e1cdc5abc48101d5146e1ae680fa81fe886d8d95aaa25a0b450c818d32", size = 357743, upload-time = "2025-07-30T11:13:36.145Z" }, + { url = "https://files.pythonhosted.org/packages/36/3d/742617a7c644deb0c1628dcf6bb2d2165ab7c6aab56fe5222758994007f8/sentry_sdk-2.35.0-py2.py3-none-any.whl", hash = "sha256:6e0c29b9a5d34de8575ffb04d289a987ff3053cf2c98ede445bea995e3830263", size = 363806, upload-time = "2025-08-14T17:11:18.29Z" }, ] [[package]] @@ -4963,43 +4964,42 @@ wheels = [ [[package]] name = "zstandard" -version = "0.23.0" +version = "0.24.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation == 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701, upload-time = "2024-07-15T00:18:06.141Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/1b/c20b2ef1d987627765dcd5bf1dadb8ef6564f00a87972635099bb76b7a05/zstandard-0.24.0.tar.gz", hash = "sha256:fe3198b81c00032326342d973e526803f183f97aa9e9a98e3f897ebafe21178f", size = 905681, upload-time = "2025-08-17T18:36:36.352Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/40/f67e7d2c25a0e2dc1744dd781110b0b60306657f8696cafb7ad7579469bd/zstandard-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e", size = 788699, upload-time = "2024-07-15T00:14:04.909Z" }, - { url = "https://files.pythonhosted.org/packages/e8/46/66d5b55f4d737dd6ab75851b224abf0afe5774976fe511a54d2eb9063a41/zstandard-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23", size = 633681, upload-time = "2024-07-15T00:14:13.99Z" }, - { url = "https://files.pythonhosted.org/packages/63/b6/677e65c095d8e12b66b8f862b069bcf1f1d781b9c9c6f12eb55000d57583/zstandard-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a", size = 4944328, upload-time = "2024-07-15T00:14:16.588Z" }, - { url = "https://files.pythonhosted.org/packages/59/cc/e76acb4c42afa05a9d20827116d1f9287e9c32b7ad58cc3af0721ce2b481/zstandard-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db", size = 5311955, upload-time = "2024-07-15T00:14:19.389Z" }, - { url = "https://files.pythonhosted.org/packages/78/e4/644b8075f18fc7f632130c32e8f36f6dc1b93065bf2dd87f03223b187f26/zstandard-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2", size = 5344944, upload-time = "2024-07-15T00:14:22.173Z" }, - { url = "https://files.pythonhosted.org/packages/76/3f/dbafccf19cfeca25bbabf6f2dd81796b7218f768ec400f043edc767015a6/zstandard-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca", size = 5442927, upload-time = "2024-07-15T00:14:24.825Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c3/d24a01a19b6733b9f218e94d1a87c477d523237e07f94899e1c10f6fd06c/zstandard-0.23.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c", size = 4864910, upload-time = "2024-07-15T00:14:26.982Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a9/cf8f78ead4597264f7618d0875be01f9bc23c9d1d11afb6d225b867cb423/zstandard-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e", size = 4935544, upload-time = "2024-07-15T00:14:29.582Z" }, - { url = "https://files.pythonhosted.org/packages/2c/96/8af1e3731b67965fb995a940c04a2c20997a7b3b14826b9d1301cf160879/zstandard-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5", size = 5467094, upload-time = "2024-07-15T00:14:40.126Z" }, - { url = "https://files.pythonhosted.org/packages/ff/57/43ea9df642c636cb79f88a13ab07d92d88d3bfe3e550b55a25a07a26d878/zstandard-0.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48", size = 4860440, upload-time = "2024-07-15T00:14:42.786Z" }, - { url = "https://files.pythonhosted.org/packages/46/37/edb78f33c7f44f806525f27baa300341918fd4c4af9472fbc2c3094be2e8/zstandard-0.23.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c", size = 4700091, upload-time = "2024-07-15T00:14:45.184Z" }, - { url = "https://files.pythonhosted.org/packages/c1/f1/454ac3962671a754f3cb49242472df5c2cced4eb959ae203a377b45b1a3c/zstandard-0.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003", size = 5208682, upload-time = "2024-07-15T00:14:47.407Z" }, - { url = "https://files.pythonhosted.org/packages/85/b2/1734b0fff1634390b1b887202d557d2dd542de84a4c155c258cf75da4773/zstandard-0.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78", size = 5669707, upload-time = "2024-07-15T00:15:03.529Z" }, - { url = "https://files.pythonhosted.org/packages/52/5a/87d6971f0997c4b9b09c495bf92189fb63de86a83cadc4977dc19735f652/zstandard-0.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473", size = 5201792, upload-time = "2024-07-15T00:15:28.372Z" }, - { url = "https://files.pythonhosted.org/packages/79/02/6f6a42cc84459d399bd1a4e1adfc78d4dfe45e56d05b072008d10040e13b/zstandard-0.23.0-cp311-cp311-win32.whl", hash = "sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160", size = 430586, upload-time = "2024-07-15T00:15:32.26Z" }, - { url = "https://files.pythonhosted.org/packages/be/a2/4272175d47c623ff78196f3c10e9dc7045c1b9caf3735bf041e65271eca4/zstandard-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0", size = 495420, upload-time = "2024-07-15T00:15:34.004Z" }, - { url = "https://files.pythonhosted.org/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094", size = 788713, upload-time = "2024-07-15T00:15:35.815Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8", size = 633459, upload-time = "2024-07-15T00:15:37.995Z" }, - { url = "https://files.pythonhosted.org/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1", size = 4945707, upload-time = "2024-07-15T00:15:39.872Z" }, - { url = "https://files.pythonhosted.org/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072", size = 5306545, upload-time = "2024-07-15T00:15:41.75Z" }, - { url = "https://files.pythonhosted.org/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20", size = 5337533, upload-time = "2024-07-15T00:15:44.114Z" }, - { url = "https://files.pythonhosted.org/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373", size = 5436510, upload-time = "2024-07-15T00:15:46.509Z" }, - { url = "https://files.pythonhosted.org/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db", size = 4859973, upload-time = "2024-07-15T00:15:49.939Z" }, - { url = "https://files.pythonhosted.org/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772", size = 4936968, upload-time = "2024-07-15T00:15:52.025Z" }, - { url = "https://files.pythonhosted.org/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105", size = 5467179, upload-time = "2024-07-15T00:15:54.971Z" }, - { url = "https://files.pythonhosted.org/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba", size = 4848577, upload-time = "2024-07-15T00:15:57.634Z" }, - { url = "https://files.pythonhosted.org/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd", size = 4693899, upload-time = "2024-07-15T00:16:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a", size = 5199964, upload-time = "2024-07-15T00:16:03.669Z" }, - { url = "https://files.pythonhosted.org/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90", size = 5655398, upload-time = "2024-07-15T00:16:06.694Z" }, - { url = "https://files.pythonhosted.org/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35", size = 5191313, upload-time = "2024-07-15T00:16:09.758Z" }, - { url = "https://files.pythonhosted.org/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d", size = 430877, upload-time = "2024-07-15T00:16:11.758Z" }, - { url = "https://files.pythonhosted.org/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b", size = 495595, upload-time = "2024-07-15T00:16:13.731Z" }, + { url = "https://files.pythonhosted.org/packages/01/1f/5c72806f76043c0ef9191a2b65281dacdf3b65b0828eb13bb2c987c4fb90/zstandard-0.24.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:addfc23e3bd5f4b6787b9ca95b2d09a1a67ad5a3c318daaa783ff90b2d3a366e", size = 795228, upload-time = "2025-08-17T18:21:46.978Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ba/3059bd5cd834666a789251d14417621b5c61233bd46e7d9023ea8bc1043a/zstandard-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b005bcee4be9c3984b355336283afe77b2defa76ed6b89332eced7b6fa68b68", size = 640520, upload-time = "2025-08-17T18:21:48.162Z" }, + { url = "https://files.pythonhosted.org/packages/57/07/f0e632bf783f915c1fdd0bf68614c4764cae9dd46ba32cbae4dd659592c3/zstandard-0.24.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:3f96a9130171e01dbb6c3d4d9925d604e2131a97f540e223b88ba45daf56d6fb", size = 5347682, upload-time = "2025-08-17T18:21:50.266Z" }, + { url = "https://files.pythonhosted.org/packages/a6/4c/63523169fe84773a7462cd090b0989cb7c7a7f2a8b0a5fbf00009ba7d74d/zstandard-0.24.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd0d3d16e63873253bad22b413ec679cf6586e51b5772eb10733899832efec42", size = 5057650, upload-time = "2025-08-17T18:21:52.634Z" }, + { url = "https://files.pythonhosted.org/packages/c6/16/49013f7ef80293f5cebf4c4229535a9f4c9416bbfd238560edc579815dbe/zstandard-0.24.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b7a8c30d9bf4bd5e4dcfe26900bef0fcd9749acde45cdf0b3c89e2052fda9a13", size = 5404893, upload-time = "2025-08-17T18:21:54.54Z" }, + { url = "https://files.pythonhosted.org/packages/4d/38/78e8bcb5fc32a63b055f2b99e0be49b506f2351d0180173674f516cf8a7a/zstandard-0.24.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:52cd7d9fa0a115c9446abb79b06a47171b7d916c35c10e0c3aa6f01d57561382", size = 5452389, upload-time = "2025-08-17T18:21:56.822Z" }, + { url = "https://files.pythonhosted.org/packages/55/8a/81671f05619edbacd49bd84ce6899a09fc8299be20c09ae92f6618ccb92d/zstandard-0.24.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0f6fc2ea6e07e20df48752e7700e02e1892c61f9a6bfbacaf2c5b24d5ad504b", size = 5558888, upload-time = "2025-08-17T18:21:58.68Z" }, + { url = "https://files.pythonhosted.org/packages/49/cc/e83feb2d7d22d1f88434defbaeb6e5e91f42a4f607b5d4d2d58912b69d67/zstandard-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e46eb6702691b24ddb3e31e88b4a499e31506991db3d3724a85bd1c5fc3cfe4e", size = 5048038, upload-time = "2025-08-17T18:22:00.642Z" }, + { url = "https://files.pythonhosted.org/packages/08/c3/7a5c57ff49ef8943877f85c23368c104c2aea510abb339a2dc31ad0a27c3/zstandard-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5e3b9310fd7f0d12edc75532cd9a56da6293840c84da90070d692e0bb15f186", size = 5573833, upload-time = "2025-08-17T18:22:02.402Z" }, + { url = "https://files.pythonhosted.org/packages/f9/00/64519983cd92535ba4bdd4ac26ac52db00040a52d6c4efb8d1764abcc343/zstandard-0.24.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76cdfe7f920738ea871f035568f82bad3328cbc8d98f1f6988264096b5264efd", size = 4961072, upload-time = "2025-08-17T18:22:04.384Z" }, + { url = "https://files.pythonhosted.org/packages/72/ab/3a08a43067387d22994fc87c3113636aa34ccd2914a4d2d188ce365c5d85/zstandard-0.24.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3f2fe35ec84908dddf0fbf66b35d7c2878dbe349552dd52e005c755d3493d61c", size = 5268462, upload-time = "2025-08-17T18:22:06.095Z" }, + { url = "https://files.pythonhosted.org/packages/49/cf/2abb3a1ad85aebe18c53e7eca73223f1546ddfa3bf4d2fb83fc5a064c5ca/zstandard-0.24.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:aa705beb74ab116563f4ce784fa94771f230c05d09ab5de9c397793e725bb1db", size = 5443319, upload-time = "2025-08-17T18:22:08.572Z" }, + { url = "https://files.pythonhosted.org/packages/40/42/0dd59fc2f68f1664cda11c3b26abdf987f4e57cb6b6b0f329520cd074552/zstandard-0.24.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:aadf32c389bb7f02b8ec5c243c38302b92c006da565e120dfcb7bf0378f4f848", size = 5822355, upload-time = "2025-08-17T18:22:10.537Z" }, + { url = "https://files.pythonhosted.org/packages/99/c0/ea4e640fd4f7d58d6f87a1e7aca11fb886ac24db277fbbb879336c912f63/zstandard-0.24.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e40cd0fc734aa1d4bd0e7ad102fd2a1aefa50ce9ef570005ffc2273c5442ddc3", size = 5365257, upload-time = "2025-08-17T18:22:13.159Z" }, + { url = "https://files.pythonhosted.org/packages/27/a9/92da42a5c4e7e4003271f2e1f0efd1f37cfd565d763ad3604e9597980a1c/zstandard-0.24.0-cp311-cp311-win32.whl", hash = "sha256:cda61c46343809ecda43dc620d1333dd7433a25d0a252f2dcc7667f6331c7b61", size = 435559, upload-time = "2025-08-17T18:22:17.29Z" }, + { url = "https://files.pythonhosted.org/packages/e2/8e/2c8e5c681ae4937c007938f954a060fa7c74f36273b289cabdb5ef0e9a7e/zstandard-0.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:3b95fc06489aa9388400d1aab01a83652bc040c9c087bd732eb214909d7fb0dd", size = 505070, upload-time = "2025-08-17T18:22:14.808Z" }, + { url = "https://files.pythonhosted.org/packages/52/10/a2f27a66bec75e236b575c9f7b0d7d37004a03aa2dcde8e2decbe9ed7b4d/zstandard-0.24.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad9fd176ff6800a0cf52bcf59c71e5de4fa25bf3ba62b58800e0f84885344d34", size = 461507, upload-time = "2025-08-17T18:22:15.964Z" }, + { url = "https://files.pythonhosted.org/packages/26/e9/0bd281d9154bba7fc421a291e263911e1d69d6951aa80955b992a48289f6/zstandard-0.24.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a2bda8f2790add22773ee7a4e43c90ea05598bffc94c21c40ae0a9000b0133c3", size = 795710, upload-time = "2025-08-17T18:22:19.189Z" }, + { url = "https://files.pythonhosted.org/packages/36/26/b250a2eef515caf492e2d86732e75240cdac9d92b04383722b9753590c36/zstandard-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cc76de75300f65b8eb574d855c12518dc25a075dadb41dd18f6322bda3fe15d5", size = 640336, upload-time = "2025-08-17T18:22:20.466Z" }, + { url = "https://files.pythonhosted.org/packages/79/bf/3ba6b522306d9bf097aac8547556b98a4f753dc807a170becaf30dcd6f01/zstandard-0.24.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:d2b3b4bda1a025b10fe0269369475f420177f2cb06e0f9d32c95b4873c9f80b8", size = 5342533, upload-time = "2025-08-17T18:22:22.326Z" }, + { url = "https://files.pythonhosted.org/packages/ea/ec/22bc75bf054e25accdf8e928bc68ab36b4466809729c554ff3a1c1c8bce6/zstandard-0.24.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b84c6c210684286e504022d11ec294d2b7922d66c823e87575d8b23eba7c81f", size = 5062837, upload-time = "2025-08-17T18:22:24.416Z" }, + { url = "https://files.pythonhosted.org/packages/48/cc/33edfc9d286e517fb5b51d9c3210e5bcfce578d02a675f994308ca587ae1/zstandard-0.24.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c59740682a686bf835a1a4d8d0ed1eefe31ac07f1c5a7ed5f2e72cf577692b00", size = 5393855, upload-time = "2025-08-17T18:22:26.786Z" }, + { url = "https://files.pythonhosted.org/packages/73/36/59254e9b29da6215fb3a717812bf87192d89f190f23817d88cb8868c47ac/zstandard-0.24.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6324fde5cf5120fbf6541d5ff3c86011ec056e8d0f915d8e7822926a5377193a", size = 5451058, upload-time = "2025-08-17T18:22:28.885Z" }, + { url = "https://files.pythonhosted.org/packages/9a/c7/31674cb2168b741bbbe71ce37dd397c9c671e73349d88ad3bca9e9fae25b/zstandard-0.24.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51a86bd963de3f36688553926a84e550d45d7f9745bd1947d79472eca27fcc75", size = 5546619, upload-time = "2025-08-17T18:22:31.115Z" }, + { url = "https://files.pythonhosted.org/packages/e6/01/1a9f22239f08c00c156f2266db857545ece66a6fc0303d45c298564bc20b/zstandard-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d82ac87017b734f2fb70ff93818c66f0ad2c3810f61040f077ed38d924e19980", size = 5046676, upload-time = "2025-08-17T18:22:33.077Z" }, + { url = "https://files.pythonhosted.org/packages/a7/91/6c0cf8fa143a4988a0361380ac2ef0d7cb98a374704b389fbc38b5891712/zstandard-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92ea7855d5bcfb386c34557516c73753435fb2d4a014e2c9343b5f5ba148b5d8", size = 5576381, upload-time = "2025-08-17T18:22:35.391Z" }, + { url = "https://files.pythonhosted.org/packages/e2/77/1526080e22e78871e786ccf3c84bf5cec9ed25110a9585507d3c551da3d6/zstandard-0.24.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3adb4b5414febf074800d264ddf69ecade8c658837a83a19e8ab820e924c9933", size = 4953403, upload-time = "2025-08-17T18:22:37.266Z" }, + { url = "https://files.pythonhosted.org/packages/6e/d0/a3a833930bff01eab697eb8abeafb0ab068438771fa066558d96d7dafbf9/zstandard-0.24.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6374feaf347e6b83ec13cc5dcfa70076f06d8f7ecd46cc71d58fac798ff08b76", size = 5267396, upload-time = "2025-08-17T18:22:39.757Z" }, + { url = "https://files.pythonhosted.org/packages/f3/5e/90a0db9a61cd4769c06374297ecfcbbf66654f74cec89392519deba64d76/zstandard-0.24.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:13fc548e214df08d896ee5f29e1f91ee35db14f733fef8eabea8dca6e451d1e2", size = 5433269, upload-time = "2025-08-17T18:22:42.131Z" }, + { url = "https://files.pythonhosted.org/packages/ce/58/fc6a71060dd67c26a9c5566e0d7c99248cbe5abfda6b3b65b8f1a28d59f7/zstandard-0.24.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0a416814608610abf5488889c74e43ffa0343ca6cf43957c6b6ec526212422da", size = 5814203, upload-time = "2025-08-17T18:22:44.017Z" }, + { url = "https://files.pythonhosted.org/packages/5c/6a/89573d4393e3ecbfa425d9a4e391027f58d7810dec5cdb13a26e4cdeef5c/zstandard-0.24.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0d66da2649bb0af4471699aeb7a83d6f59ae30236fb9f6b5d20fb618ef6c6777", size = 5359622, upload-time = "2025-08-17T18:22:45.802Z" }, + { url = "https://files.pythonhosted.org/packages/60/ff/2cbab815d6f02a53a9d8d8703bc727d8408a2e508143ca9af6c3cca2054b/zstandard-0.24.0-cp312-cp312-win32.whl", hash = "sha256:ff19efaa33e7f136fe95f9bbcc90ab7fb60648453b03f95d1de3ab6997de0f32", size = 435968, upload-time = "2025-08-17T18:22:49.493Z" }, + { url = "https://files.pythonhosted.org/packages/ce/a3/8f96b8ddb7ad12344218fbd0fd2805702dafd126ae9f8a1fb91eef7b33da/zstandard-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc05f8a875eb651d1cc62e12a4a0e6afa5cd0cc231381adb830d2e9c196ea895", size = 505195, upload-time = "2025-08-17T18:22:47.193Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4a/bfca20679da63bfc236634ef2e4b1b4254203098b0170e3511fee781351f/zstandard-0.24.0-cp312-cp312-win_arm64.whl", hash = "sha256:b04c94718f7a8ed7cdd01b162b6caa1954b3c9d486f00ecbbd300f149d2b2606", size = 461605, upload-time = "2025-08-17T18:22:48.317Z" }, ] From 31101ecaab71620cfdcc31dc3074117b78c7e10a Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 18 Aug 2025 15:37:44 -0700 Subject: [PATCH 27/72] AGNOS 12.8 (#36008) * staging * prod --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 12 ++++++------ system/hardware/tici/all-partitions.json | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index e1a0da9b67..4c011c6ac0 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="12.7" + export AGNOS_VERSION="12.8" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 0900c51d10..941a4956bf 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -67,17 +67,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img.xz", - "hash": "e6fbc73b6ef9551f57f123791f94f2f72db8ce59e9fba8ccd44c30685582368b", - "hash_raw": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", + "url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img.xz", + "hash": "1468d50b7ad0fda0f04074755d21e786e3b1b6ca5dd5b17eb2608202025e6126", + "hash_raw": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "af2a42284ecfddc9d8aa50fde0e2093ba18cf1dd2242a7a3fbe05f78f6ec0228", + "ondevice_hash": "242aa5adad1c04e1398e00e2440d1babf962022eb12b89adf2e60ee3068946e7", "alt": { - "hash": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", - "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img", + "hash": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087", + "url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img", "size": 5368709120 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 5d1bcc65a7..e49f93a066 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -350,17 +350,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img.xz", - "hash": "e6fbc73b6ef9551f57f123791f94f2f72db8ce59e9fba8ccd44c30685582368b", - "hash_raw": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", + "url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img.xz", + "hash": "1468d50b7ad0fda0f04074755d21e786e3b1b6ca5dd5b17eb2608202025e6126", + "hash_raw": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "af2a42284ecfddc9d8aa50fde0e2093ba18cf1dd2242a7a3fbe05f78f6ec0228", + "ondevice_hash": "242aa5adad1c04e1398e00e2440d1babf962022eb12b89adf2e60ee3068946e7", "alt": { - "hash": "6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63", - "url": "https://commadist.azureedge.net/agnosupdate/system-6bbefd9b5b7719eb4f52c051966cd6ca5c883241e795b4757f795225e459eb63.img", + "hash": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087", + "url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img", "size": 5368709120 } }, From dfc66d780745bd2c393130d26162ed00452e6006 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Mon, 18 Aug 2025 18:52:05 -0700 Subject: [PATCH 28/72] [bot] Update Python packages (#36014) Update Python packages Co-authored-by: Vehicle Researcher --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index b2f34136c0..4b203ff5d1 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit b2f34136c0e58a76d4b4197003a852e72e48767e +Subproject commit 4b203ff5d1ad867de127de6b27382ba73e6e31a7 From 4d55671b17d44c66954ee1f471189fb748b4b8ff Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 18 Aug 2025 18:57:16 -0700 Subject: [PATCH 29/72] feedbackd: temp disable LKAS button as feedback (#36017) * feedbackd: temp disable LKAS button as feedback * disable that * mark --- RELEASES.md | 2 +- selfdrive/ui/feedback/feedbackd.py | 3 ++- selfdrive/ui/tests/test_feedbackd.py | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index e1c64efad4..cb7274e827 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,6 @@ Version 0.10.1 (2025-09-08) ======================== +* Record driving feedback using LKAS button Version 0.10.0 (2025-08-05) ======================== @@ -10,7 +11,6 @@ Version 0.10.0 (2025-08-05) * Action from lateral MPC as training objective replaced by E2E planning from World Model * Low-speed lead car ground-truth fixes * Enable live-learned steering actuation delay -* Record driving feedback using LKAS button * Opt-in audio recording for dashcam video * Acura MDX 2025 support thanks to vanillagorillaa and MVL! * Honda Accord 2023-25 support thanks to vanillagorillaa and MVL! diff --git a/selfdrive/ui/feedback/feedbackd.py b/selfdrive/ui/feedback/feedbackd.py index b02e5d97a7..2d131a0d5e 100755 --- a/selfdrive/ui/feedback/feedbackd.py +++ b/selfdrive/ui/feedback/feedbackd.py @@ -22,7 +22,8 @@ def main(): sm.update() should_send_bookmark = False - if sm.updated['carState'] and sm['carState'].canValid: + # TODO: https://github.com/commaai/openpilot/issues/36015 + if False and sm.updated['carState'] and sm['carState'].canValid: for be in sm['carState'].buttonEvents: if be.type == ButtonType.lkas: if be.pressed: diff --git a/selfdrive/ui/tests/test_feedbackd.py b/selfdrive/ui/tests/test_feedbackd.py index c2d81aef83..6b7ec44863 100644 --- a/selfdrive/ui/tests/test_feedbackd.py +++ b/selfdrive/ui/tests/test_feedbackd.py @@ -5,6 +5,7 @@ from openpilot.common.params import Params from openpilot.system.manager.process_config import managed_processes +@pytest.mark.skip("tmp disabled") class TestFeedbackd: def setup_method(self): self.pm = messaging.PubMaster(['carState', 'rawAudioData']) From f55f3bb7cdda25e2bc7949917a37212d2725d890 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 18 Aug 2025 19:33:34 -0700 Subject: [PATCH 30/72] setup is a noun! --- docs/CONTRIBUTING.md | 2 +- docs/how-to/turn-the-speed-blue.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index f8c27b8815..154734b7fc 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -6,7 +6,7 @@ Development is coordinated through [Discord](https://discord.comma.ai) and GitHu ### Getting Started -* Setup your [development environment](/tools/) +* Set up your [development environment](/tools/) * Join our [Discord](https://discord.comma.ai) * Docs are at https://docs.comma.ai and https://blog.comma.ai diff --git a/docs/how-to/turn-the-speed-blue.md b/docs/how-to/turn-the-speed-blue.md index 64f4475dfa..13b3b03e80 100644 --- a/docs/how-to/turn-the-speed-blue.md +++ b/docs/how-to/turn-the-speed-blue.md @@ -1,11 +1,11 @@ # Turn the speed blue *A getting started guide for openpilot development* -In 30 minutes, we'll get an openpilot development environment setup on your computer and make some changes to openpilot's UI. +In 30 minutes, we'll get an openpilot development environment set up on your computer and make some changes to openpilot's UI. And if you have a comma 3/3X, we'll deploy the change to your device for testing. -## 1. Setup your development environment +## 1. Set up your development environment Run this to clone openpilot and install all the dependencies: ```bash From 2148e2dff2c915f79c93fe4a975bd631e4aa2f1f Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 18 Aug 2025 19:48:08 -0700 Subject: [PATCH 31/72] build_devel: clean submodules --- release/build_devel.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/release/build_devel.sh b/release/build_devel.sh index 05dcaafcaa..c4ef280258 100755 --- a/release/build_devel.sh +++ b/release/build_devel.sh @@ -30,6 +30,7 @@ git reset --hard __nightly git checkout __nightly git reset --hard origin/devel git clean -xdff +git submodule foreach --recursive git clean -xdff git lfs uninstall # remove everything except .git From 2cec2587be926bd4ef42dc8f92177324c05e997a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 19 Aug 2025 09:18:14 -0700 Subject: [PATCH 32/72] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 5b0f1a2eca..3dc2138623 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 5b0f1a2eca710121ef7573f1d183d4bc6c07b6ec +Subproject commit 3dc21386239e3073a623156b75901aa302340d6c From c085b8af19438956c15592828bd082803f43dfaf Mon Sep 17 00:00:00 2001 From: Jimmy <9859727+Quantizr@users.noreply.github.com> Date: Tue, 19 Aug 2025 09:18:32 -0700 Subject: [PATCH 33/72] feedbackd: remove lkas toggle for this release (#36018) remove lkas toggle for this release --- selfdrive/ui/layouts/settings/toggles.py | 10 ---------- selfdrive/ui/qt/offroad/settings.cc | 7 ------- 2 files changed, 17 deletions(-) diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py index ff0564a61a..58afcec5ef 100644 --- a/selfdrive/ui/layouts/settings/toggles.py +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -23,10 +23,6 @@ DESCRIPTIONS = { 'RecordFront': "Upload data from the driver facing camera and help improve the driver monitoring algorithm.", "IsMetric": "Display speed in km/h instead of mph.", "RecordAudio": "Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect.", - "RecordAudioFeedback": ( - "Press the LKAS button to record audio feedback about openpilot. When this toggle is disabled, the button acts as a bookmark button. " + - "The event will be highlighted in comma connect and the segment will be preserved on your device's storage." - ), } @@ -85,12 +81,6 @@ class TogglesLayout(Widget): self._params.get_bool("RecordAudio"), icon="microphone.png", ), - toggle_item( - "Record Audio Feedback with LKAS button", - DESCRIPTIONS["RecordAudioFeedback"], - self._params.get_bool("RecordAudioFeedback"), - icon="microphone.png", - ), toggle_item( "Use Metric System", DESCRIPTIONS["IsMetric"], self._params.get_bool("IsMetric"), icon="metric.png" ), diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 01761295dc..96734d69d9 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -68,13 +68,6 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { "../assets/icons/microphone.png", true, }, - { - "RecordAudioFeedback", - tr("Record Audio Feedback with LKAS button"), - tr("Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage.\n\nNote that this feature is only compatible with select cars."), - "../assets/icons/microphone.png", - false, - }, { "IsMetric", tr("Use Metric System"), From 803b54ebdb7ca851c1b246bb150b5b72ccb3ad39 Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Tue, 19 Aug 2025 10:09:09 -0700 Subject: [PATCH 34/72] model parser: use check missing for mhp checks (#36020) * model parser: use check missing for mhp checks * lint + support re * lint... * no walrus * just remove --- selfdrive/modeld/modeld.py | 2 +- selfdrive/modeld/parse_model_outputs.py | 35 ++++++++++++++----------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 8bc8bf01ab..33da8f02b3 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -116,7 +116,7 @@ class ModelState: self.vision_output = np.zeros(vision_output_size, dtype=np.float32) self.policy_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()} self.policy_output = np.zeros(policy_output_size, dtype=np.float32) - self.parser = Parser() + self.parser = Parser(ignore_missing=('desired_curvature',)) with open(VISION_PKL_PATH, "rb") as f: self.vision_run = pickle.load(f) diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 9e1c048735..5812e0b32e 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -22,9 +22,10 @@ class Parser: self.ignore_missing = ignore_missing def check_missing(self, outs, name): - if name not in outs and not self.ignore_missing: + missing = name not in outs + if missing and not self.ignore_missing: raise ValueError(f"Missing output {name}") - return name not in outs + return missing def parse_categorical_crossentropy(self, name, outs, out_shape=None): if self.check_missing(outs, name): @@ -84,6 +85,13 @@ class Parser: outs[name] = pred_mu_final.reshape(final_shape) outs[name + '_stds'] = pred_std_final.reshape(final_shape) + def is_mhp(self, outs, name, shape): + if self.check_missing(outs, name): + return False + if outs[name].shape[1] == 2 * shape: + return False + return True + def parse_vision_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,)) @@ -94,23 +102,18 @@ class Parser: self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH)) self.parse_binary_crossentropy('meta', outs) self.parse_binary_crossentropy('lead_prob', outs) - if outs['lead'].shape[1] == 2 * ModelConstants.LEAD_MHP_SELECTION *ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH: - self.parse_mdn('lead', outs, in_N=0, out_N=0, - out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) - else: - self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, - out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) + lead_mhp = self.is_mhp(outs, 'lead', ModelConstants.LEAD_MHP_SELECTION * ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH) + lead_in_N, lead_out_N = (ModelConstants.LEAD_MHP_N, ModelConstants.LEAD_MHP_SELECTION) if lead_mhp else (0, 0) + self.parse_mdn( + 'lead', outs, in_N=lead_in_N, out_N=lead_out_N, + out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) + ) return outs def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: - if outs['plan'].shape[1] == 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH: - self.parse_mdn('plan', outs, in_N=0, out_N=0, - out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) - else: - self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION, - out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) - if 'desired_curvature' in outs: - self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,)) + plan_mhp = self.is_mhp(outs, 'plan', ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH) + plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0) + self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,)) return outs From 51314fa9fef0350ed389932c39a30d746e73137f Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Tue, 19 Aug 2025 10:09:59 -0700 Subject: [PATCH 35/72] Revert "model parser: use check missing for mhp checks" (#36022) Revert "model parser: use check missing for mhp checks (#36020)" This reverts commit 803b54ebdb7ca851c1b246bb150b5b72ccb3ad39. --- selfdrive/modeld/modeld.py | 2 +- selfdrive/modeld/parse_model_outputs.py | 35 +++++++++++-------------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 33da8f02b3..8bc8bf01ab 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -116,7 +116,7 @@ class ModelState: self.vision_output = np.zeros(vision_output_size, dtype=np.float32) self.policy_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()} self.policy_output = np.zeros(policy_output_size, dtype=np.float32) - self.parser = Parser(ignore_missing=('desired_curvature',)) + self.parser = Parser() with open(VISION_PKL_PATH, "rb") as f: self.vision_run = pickle.load(f) diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 5812e0b32e..9e1c048735 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -22,10 +22,9 @@ class Parser: self.ignore_missing = ignore_missing def check_missing(self, outs, name): - missing = name not in outs - if missing and not self.ignore_missing: + if name not in outs and not self.ignore_missing: raise ValueError(f"Missing output {name}") - return missing + return name not in outs def parse_categorical_crossentropy(self, name, outs, out_shape=None): if self.check_missing(outs, name): @@ -85,13 +84,6 @@ class Parser: outs[name] = pred_mu_final.reshape(final_shape) outs[name + '_stds'] = pred_std_final.reshape(final_shape) - def is_mhp(self, outs, name, shape): - if self.check_missing(outs, name): - return False - if outs[name].shape[1] == 2 * shape: - return False - return True - def parse_vision_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,)) @@ -102,18 +94,23 @@ class Parser: self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH)) self.parse_binary_crossentropy('meta', outs) self.parse_binary_crossentropy('lead_prob', outs) - lead_mhp = self.is_mhp(outs, 'lead', ModelConstants.LEAD_MHP_SELECTION * ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH) - lead_in_N, lead_out_N = (ModelConstants.LEAD_MHP_N, ModelConstants.LEAD_MHP_SELECTION) if lead_mhp else (0, 0) - self.parse_mdn( - 'lead', outs, in_N=lead_in_N, out_N=lead_out_N, - out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) - ) + if outs['lead'].shape[1] == 2 * ModelConstants.LEAD_MHP_SELECTION *ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH: + self.parse_mdn('lead', outs, in_N=0, out_N=0, + out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) + else: + self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, + out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) return outs def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: - plan_mhp = self.is_mhp(outs, 'plan', ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH) - plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0) - self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) + if outs['plan'].shape[1] == 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH: + self.parse_mdn('plan', outs, in_N=0, out_N=0, + out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) + else: + self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION, + out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) + if 'desired_curvature' in outs: + self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,)) self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,)) return outs From 3d24225cc151732ab8d1501f24e8da3f027b3a64 Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Tue, 19 Aug 2025 10:19:00 -0700 Subject: [PATCH 36/72] model parser: use check missing for mhp checks (#36023) * model parser: use check missing for mhp checks * lint + support re * lint... * no walrus * just remove * forgot this --- selfdrive/modeld/parse_model_outputs.py | 35 ++++++++++++++----------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 9e1c048735..5812e0b32e 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -22,9 +22,10 @@ class Parser: self.ignore_missing = ignore_missing def check_missing(self, outs, name): - if name not in outs and not self.ignore_missing: + missing = name not in outs + if missing and not self.ignore_missing: raise ValueError(f"Missing output {name}") - return name not in outs + return missing def parse_categorical_crossentropy(self, name, outs, out_shape=None): if self.check_missing(outs, name): @@ -84,6 +85,13 @@ class Parser: outs[name] = pred_mu_final.reshape(final_shape) outs[name + '_stds'] = pred_std_final.reshape(final_shape) + def is_mhp(self, outs, name, shape): + if self.check_missing(outs, name): + return False + if outs[name].shape[1] == 2 * shape: + return False + return True + def parse_vision_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,)) @@ -94,23 +102,18 @@ class Parser: self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH)) self.parse_binary_crossentropy('meta', outs) self.parse_binary_crossentropy('lead_prob', outs) - if outs['lead'].shape[1] == 2 * ModelConstants.LEAD_MHP_SELECTION *ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH: - self.parse_mdn('lead', outs, in_N=0, out_N=0, - out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) - else: - self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, - out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) + lead_mhp = self.is_mhp(outs, 'lead', ModelConstants.LEAD_MHP_SELECTION * ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH) + lead_in_N, lead_out_N = (ModelConstants.LEAD_MHP_N, ModelConstants.LEAD_MHP_SELECTION) if lead_mhp else (0, 0) + self.parse_mdn( + 'lead', outs, in_N=lead_in_N, out_N=lead_out_N, + out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) + ) return outs def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: - if outs['plan'].shape[1] == 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH: - self.parse_mdn('plan', outs, in_N=0, out_N=0, - out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) - else: - self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION, - out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) - if 'desired_curvature' in outs: - self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,)) + plan_mhp = self.is_mhp(outs, 'plan', ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH) + plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0) + self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,)) return outs From 560c503871b100fa5b2d5c05369d10c40813518a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 19 Aug 2025 11:19:58 -0700 Subject: [PATCH 37/72] new release flow (#36021) * new release flow * Update README.md --- release/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/release/README.md b/release/README.md index 0ceb765597..266d881d3c 100644 --- a/release/README.md +++ b/release/README.md @@ -3,33 +3,33 @@ ``` ## release checklist -**Go to `devel-staging`** -- [ ] make issue to track release +**Go to staging** +- [ ] make a GitHub issue to track release +- [ ] create release master branch - [ ] update RELEASES.md -- [ ] trigger new nightly build: https://github.com/commaai/openpilot/actions/workflows/release.yaml -- [ ] update `devel-staging`: `git reset --hard origin/__nightly` +- [ ] bump version on master: `common/version.h` and `RELEASES.md` - [ ] build new userdata partition from `release3-staging` -- [ ] open a pull request from `devel-staging` to `devel` - [ ] post on Discord, tag `@release crew` -**Go to `devel`** -- [ ] bump version on master: `common/version.h` and `RELEASES.md` -- [ ] before merging the pull request, test the following: +Updates to staging: +- [ ] either rebase on master or cherry-pick changes +- [ ] run this to update: `BRANCH=devel-staging release/build_devel.sh` + +**Go to release** +- [ ] before going to release, test the following: - [ ] update from previous release -> new release - [ ] update from new release -> previous release - [ ] fresh install with `openpilot-test.comma.ai` - [ ] drive on fresh install - [ ] no submodules or LFS - [ ] check sentry, MTBF, etc. - - [ ] stress test in production - -**Go to `release3`** + - [ ] stress test passes in production - [ ] publish the blog post - [ ] `git reset --hard origin/release3-staging` - [ ] tag the release: `git tag v0.X.X && git push origin v0.X.X` - [ ] create GitHub release - [ ] final test install on `openpilot.comma.ai` - [ ] update factory provisioning -- [ ] close out milestone +- [ ] close out milestone and issue - [ ] post on Discord, X, etc. ``` From d097a0c201d407f61ccdb18058919d05dccea3ac Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Tue, 19 Aug 2025 11:35:22 -0700 Subject: [PATCH 38/72] model parser: fix lead mhp out shape (#36024) * model parser: fix lead mhp out shape * fix for real --- selfdrive/modeld/parse_model_outputs.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 5812e0b32e..038f51ca9c 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -104,16 +104,15 @@ class Parser: self.parse_binary_crossentropy('lead_prob', outs) lead_mhp = self.is_mhp(outs, 'lead', ModelConstants.LEAD_MHP_SELECTION * ModelConstants.LEAD_TRAJ_LEN * ModelConstants.LEAD_WIDTH) lead_in_N, lead_out_N = (ModelConstants.LEAD_MHP_N, ModelConstants.LEAD_MHP_SELECTION) if lead_mhp else (0, 0) - self.parse_mdn( - 'lead', outs, in_N=lead_in_N, out_N=lead_out_N, - out_shape=(ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) - ) + lead_out_shape = (ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) if lead_mhp else \ + (ModelConstants.LEAD_MHP_SELECTION, ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH) + self.parse_mdn('lead', outs, in_N=lead_in_N, out_N=lead_out_N, out_shape=lead_out_shape) return outs def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: plan_mhp = self.is_mhp(outs, 'plan', ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH) plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0) - self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) + self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH)) self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,)) return outs From 09aa21390dda71c49cf82df9fd9455f927f0db3c Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Tue, 19 Aug 2025 15:38:55 -0400 Subject: [PATCH 39/72] Honda: Adding support for Honda City (#36026) * bump opendbc * release notes * regen CARS.md * bump opendbc correctly this time --- RELEASES.md | 1 + docs/CARS.md | 6 ++++-- opendbc_repo | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index cb7274e827..dacf0eaa17 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,7 @@ Version 0.10.1 (2025-09-08) ======================== * Record driving feedback using LKAS button +* Honda City 2023 support thanks to drFritz! Version 0.10.0 (2025-08-05) ======================== diff --git a/docs/CARS.md b/docs/CARS.md index fcb87236c8..7269f25737 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 319 Supported Cars +# 321 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video|Setup Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -76,10 +76,12 @@ A supported vehicle is one that just works when you install a comma device. All |Honda|Accord 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Accord Hybrid 2023-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|City (Brazil only) 2023|All|openpilot available[1](#footnotes)|0 mph|14 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[5](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| -|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Civic Hatchback 2017-18|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Civic Hatchback 2019-21|All|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic Hatchback 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic Hatchback Hybrid 2025|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic Hatchback Hybrid (Europe only) 2023|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| diff --git a/opendbc_repo b/opendbc_repo index 4b203ff5d1..43006b9a41 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 4b203ff5d1ad867de127de6b27382ba73e6e31a7 +Subproject commit 43006b9a41e233325cb7cbcb6ff40de0234217a0 From 6005b12f940b96f14ccf87fb14c37a9c2fca8948 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 15:04:17 -0700 Subject: [PATCH 40/72] format logreader --- tools/lib/logreader.py | 2 ++ tools/lib/route.py | 7 +++---- tools/lib/url_file.py | 18 ++++++++++-------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index bedafb8566..833f9825bb 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -40,6 +40,7 @@ def save_log(dest, log_msgs, compress=True): with open(dest, "wb") as f: f.write(dat) + def decompress_stream(data: bytes): dctx = zstd.ZstdDecompressor() decompressed_data = b"" @@ -353,6 +354,7 @@ class LogReader: def time_series(self): return msgs_to_time_series(self) + if __name__ == "__main__": import codecs diff --git a/tools/lib/route.py b/tools/lib/route.py index 882585a151..1fc26fb996 100644 --- a/tools/lib/route.py +++ b/tools/lib/route.py @@ -231,7 +231,6 @@ class RouteName: def __str__(self) -> str: return self._canonical_name - class SegmentName: # TODO: add constructor that takes dongle_id, time_str, segment_num and then create instances # of this class instead of manually constructing a segment name (use canonical_name prop instead) @@ -252,7 +251,7 @@ class SegmentName: @property def canonical_name(self) -> str: return self._canonical_name - #TODO should only use one name + # TODO should only use one name @property def data_name(self) -> str: return f"{self._route_name.canonical_name}/{self._num}" @@ -283,7 +282,7 @@ class SegmentName: @staticmethod def from_file_name(file_name): # ??????/xxxxxxxxxxxxxxxx|1111-11-11-11--11-11-11/1/rlog.bz2 - dongle_id, route_name, segment_num = file_name.replace('|','/').split('/')[-4:-1] + dongle_id, route_name, segment_num = file_name.replace('|', '/').split('/')[-4:-1] return SegmentName(dongle_id + "|" + route_name + "--" + segment_num) @staticmethod @@ -304,6 +303,7 @@ class SegmentName: dongle_id, route_name, segment_num = prefix.split("/") return SegmentName(dongle_id + "|" + route_name + "--" + segment_num) + @cache def get_max_seg_number_cached(sr: 'SegmentRange') -> int: try: @@ -365,4 +365,3 @@ class SegmentRange: def __repr__(self) -> str: return self.__str__() - diff --git a/tools/lib/url_file.py b/tools/lib/url_file.py index 204726363d..e80ba1399d 100644 --- a/tools/lib/url_file.py +++ b/tools/lib/url_file.py @@ -9,12 +9,14 @@ from urllib3.util import Timeout from openpilot.common.file_helpers import atomic_write_in_dir from openpilot.system.hardware.hw import Paths + # Cache chunk size K = 1000 CHUNK_SIZE = 1000 * K logging.getLogger("urllib3").setLevel(logging.WARNING) + def hash_256(link: str) -> str: return sha256((link.split("?")[0]).encode('utf-8')).hexdigest() @@ -24,7 +26,7 @@ class URLFileException(Exception): class URLFile: - _pool_manager: PoolManager|None = None + _pool_manager: PoolManager | None = None @staticmethod def reset() -> None: @@ -33,16 +35,16 @@ class URLFile: @staticmethod def pool_manager() -> PoolManager: if URLFile._pool_manager is None: - socket_options = [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),] + socket_options = [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)] retries = Retry(total=5, backoff_factor=0.5, status_forcelist=[409, 429, 503, 504]) URLFile._pool_manager = PoolManager(num_pools=10, maxsize=100, socket_options=socket_options, retries=retries) return URLFile._pool_manager - def __init__(self, url: str, timeout: int=10, debug: bool=False, cache: bool|None=None): + def __init__(self, url: str, timeout: int = 10, debug: bool = False, cache: bool | None = None): self._url = url self._timeout = Timeout(connect=timeout, read=timeout) self._pos = 0 - self._length: int|None = None + self._length: int | None = None self._debug = debug # True by default, false if FILEREADER_CACHE is defined, but can be overwritten by the cache input self._force_download = not int(os.environ.get("FILEREADER_CACHE", "0")) @@ -58,7 +60,7 @@ class URLFile: def __exit__(self, exc_type, exc_value, traceback) -> None: pass - def _request(self, method: str, url: str, headers: dict[str, str]|None=None) -> BaseHTTPResponse: + def _request(self, method: str, url: str, headers: dict[str, str] | None = None) -> BaseHTTPResponse: return URLFile.pool_manager().request(method, url, timeout=self._timeout, headers=headers) def get_length_online(self) -> int: @@ -85,7 +87,7 @@ class URLFile: file_length.write(str(self._length)) return self._length - def read(self, ll: int|None=None) -> bytes: + def read(self, ll: int | None = None) -> bytes: if self._force_download: return self.read_aux(ll=ll) @@ -117,7 +119,7 @@ class URLFile: self._pos = file_end return response - def read_aux(self, ll: int|None=None) -> bytes: + def read_aux(self, ll: int | None = None) -> bytes: download_range = False headers = {} if self._pos != 0 or ll is not None: @@ -152,7 +154,7 @@ class URLFile: self._pos += len(ret) return ret - def seek(self, pos:int) -> None: + def seek(self, pos: int) -> None: self._pos = pos @property From 927548621be1be0c2c9063868b93d1f5020904de Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 19 Aug 2025 15:37:39 -0700 Subject: [PATCH 41/72] update to latest userdata partition (#36027) bump --- system/hardware/tici/all-partitions.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index e49f93a066..5891e2748a 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -366,35 +366,35 @@ }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-ad38266b508280f22d02c3749322291eb083e08580ff8b7014add6989d290b12.img.xz", - "hash": "59e1bb2606b65293721dd89bcdc8d0c5cae47718c213eb40f235149aa5a408ae", - "hash_raw": "ad38266b508280f22d02c3749322291eb083e08580ff8b7014add6989d290b12", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-602d5103cba97e1b07f76508d5febb47cfc4463a7e31bd20e461b55c801feb0a.img.xz", + "hash": "6a11d448bac50467791809339051eed2894aae971c37bf6284b3b972a99ba3ac", + "hash_raw": "602d5103cba97e1b07f76508d5febb47cfc4463a7e31bd20e461b55c801feb0a", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "40b5e666ec137a863178b51a7947e3bb76fe9259584d84e6a66361fabced3da5" + "ondevice_hash": "e014d92940a696bf8582807259820ab73948b950656ed83a45da738f26083705" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-2f78f798861e0a3dd55af87e7a0c1982502a9293340d3eee6f3b8c9c88878e33.img.xz", - "hash": "e555a29c3ccb547ed840085c052cd1c6126c32d5c6dafe1712f593ae631be190", - "hash_raw": "2f78f798861e0a3dd55af87e7a0c1982502a9293340d3eee6f3b8c9c88878e33", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-4d7f6d12a5557eb6e3cbff9a4cd595677456fdfddcc879eddcea96a43a9d8b48.img.xz", + "hash": "748e31a5fc01fc256c012e359c3382d1f98cce98feafe8ecc0fca3e47caef116", + "hash_raw": "4d7f6d12a5557eb6e3cbff9a4cd595677456fdfddcc879eddcea96a43a9d8b48", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "f5bc368dbe52ac7800634f74b1fd764fc3b6e68337984f40fd59222b1276d9f2" + "ondevice_hash": "c181b93050787adcfef730c086bcb780f28508d84e6376d9b80d37e5dc02b55e" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-6367d73ba6a32823035c9941168ca3af3704c783f6bcd912f70afe1dbdfdd14b.img.xz", - "hash": "f5dfbe1dcba25b9a1920259bc89f52b5e406519539ff0112d5469fff3f1b6dba", - "hash_raw": "6367d73ba6a32823035c9941168ca3af3704c783f6bcd912f70afe1dbdfdd14b", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-80a76c8e56bbd7536fd5e87e8daa12984e2960db4edeb1f83229b2baeecc4668.img.xz", + "hash": "09ff390e639e4373d772e1688d05a5ac77a573463ed1deeff86390686fa686f9", + "hash_raw": "80a76c8e56bbd7536fd5e87e8daa12984e2960db4edeb1f83229b2baeecc4668", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "b8f447f0ea40faae7292d3a0dc380d194caab6ec3f5007eda5661f2aa4d2f4ab" + "ondevice_hash": "2c01ab470c02121c721ff6afc25582437e821686207f3afef659387afb69c507" } ] \ No newline at end of file From 5ec9aee2166ec2fefd15128360e4ca26180e6afb Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 15:39:44 -0700 Subject: [PATCH 42/72] File sourcing: simplify return type (#36028) * rm str | none pattern * clean up * more clean up * stash * Revert "stash" This reverts commit 3e2472160cc97e9d11922137757d9ef942a0312d. * fix da prints * fix cmt --- tools/lib/logreader.py | 43 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 833f9825bb..50e3b9dc7a 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -140,9 +140,8 @@ class ReadMode(enum.StrEnum): AUTO_INTERACTIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user -LogPath = str | None LogFileName = tuple[str, ...] -Source = Callable[[SegmentRange, list[int], LogFileName], dict[int, LogPath]] +Source = Callable[[SegmentRange, list[int], LogFileName], dict[int, str]] InternalUnavailableException = Exception("Internal source not available") @@ -151,17 +150,17 @@ class LogsUnavailable(Exception): pass -def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, LogPath]: +def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: route = Route(sr.route_name) # comma api will have already checked if the file exists if fns == FileName.RLOG: - return {seg: route.log_paths()[seg] for seg in seg_idxs} + return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} else: - return {seg: route.qlog_paths()[seg] for seg in seg_idxs} + return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} -def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, LogPath]: +def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: if not internal_source_available(endpoint_url): raise InternalUnavailableException @@ -171,11 +170,11 @@ def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, end return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) -def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, LogPath]: +def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) -def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, LogPath]: +def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) @@ -183,21 +182,19 @@ def direct_source(file_or_url: str) -> list[str]: return [file_or_url] -def eval_source(files: dict[int, list[str] | str]) -> dict[int, LogPath]: +def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) - valid_files: dict[int, LogPath] = {} + valid_files: dict[int, str] = {} for seg_idx, urls in files.items(): if isinstance(urls, str): urls = [urls] - # Add first valid file URL or None + # Add first valid file URL for url in urls: if file_exists(url): valid_files[seg_idx] = url break - else: - valid_files[seg_idx] = None return valid_files @@ -220,37 +217,35 @@ def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) # Build a dict of valid files as we evaluate each source. May contain mix of rlogs, qlogs, and None. # This function only returns when we've sourced all files, or throws an exception - valid_files: dict[int, LogPath] = {} + valid_files: dict[int, str] = {} for fn in try_fns: for source in sources: try: files = source(sr, needed_seg_idxs, fn) # Build a dict of valid files - for idx, f in files.items(): - if valid_files.get(idx) is None: - valid_files[idx] = f + valid_files |= files # Don't check for segment files that have already been found - needed_seg_idxs = [idx for idx in needed_seg_idxs if valid_files.get(idx) is None] + needed_seg_idxs = [idx for idx in needed_seg_idxs if idx not in valid_files] # We've found all files, return them - if all(f is not None for f in valid_files.values()): + if len(needed_seg_idxs) == 0: return cast(list[str], list(valid_files.values())) except Exception as e: exceptions[source.__name__] = e if fn == try_fns[0]: - missing_logs = list(valid_files.values()).count(None) + missing_logs = len(needed_seg_idxs) if mode == ReadMode.AUTO: - cloudlog.warning(f"{missing_logs}/{len(valid_files)} rlogs were not found, falling back to qlogs for those segments...") + cloudlog.warning(f"{missing_logs}/{len(sr.seg_idxs)} rlogs were not found, falling back to qlogs for those segments...") elif mode == ReadMode.AUTO_INTERACTIVE: - if input(f"{missing_logs}/{len(valid_files)} rlogs were not found, would you like to fallback to qlogs for those segments? (y/N) ").lower() != "y": + if input(f"{missing_logs}/{len(sr.seg_idxs)} rlogs were not found, would you like to fallback to qlogs for those segments? (y/N) ").lower() != "y": break - missing_logs = list(valid_files.values()).count(None) - raise LogsUnavailable(f"{missing_logs}/{len(valid_files)} logs were not found, please ensure all logs " + + missing_logs = len(needed_seg_idxs) + raise LogsUnavailable(f"{missing_logs}/{len(sr.seg_idxs)} logs were not found, please ensure all logs " + "are uploaded. You can fall back to qlogs with '/a' selector at the end of the route name.\n\n" + "Exceptions for sources:\n - " + "\n - ".join([f"{k}: {repr(v)}" for k, v in exceptions.items()])) From 18b7ddef8fb8e6e0f1dbb2f8a7799e423d0bd208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Tue, 19 Aug 2025 16:25:13 -0700 Subject: [PATCH 43/72] File sourcing: Not all files are logs (#36025) * Not all files are logs * more refactor * linting ok * fix tests * import exception * whoops forgot to git add * fix --------- Co-authored-by: Shane Smiskol --- tools/lib/file_sources.py | 57 +++++++++++++++++++++++++++++ tools/lib/logreader.py | 61 +++---------------------------- tools/lib/tests/test_logreader.py | 5 ++- 3 files changed, 65 insertions(+), 58 deletions(-) create mode 100755 tools/lib/file_sources.py diff --git a/tools/lib/file_sources.py b/tools/lib/file_sources.py new file mode 100755 index 0000000000..cb7bf15114 --- /dev/null +++ b/tools/lib/file_sources.py @@ -0,0 +1,57 @@ +from collections.abc import Callable + +from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url +from openpilot.tools.lib.openpilotci import get_url +from openpilot.tools.lib.filereader import DATA_ENDPOINT, file_exists, internal_source_available +from openpilot.tools.lib.route import Route, SegmentRange, FileName + +# When passed a tuple of file names, each source will return the first that exists (rlog.zst, rlog.bz2) +FileNames = tuple[str, ...] +Source = Callable[[SegmentRange, list[int], FileNames], dict[int, str]] + +InternalUnavailableException = Exception("Internal source not available") + + +def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: + route = Route(sr.route_name) + + # comma api will have already checked if the file exists + if fns == FileName.RLOG: + return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} + else: + return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} + + +def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: + if not internal_source_available(endpoint_url): + raise InternalUnavailableException + + def get_internal_url(sr: SegmentRange, seg, file): + return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" + + return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) + + +def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: + return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) + + +def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: + return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) + + +def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: + # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) + valid_files: dict[int, str] = {} + + for seg_idx, urls in files.items(): + if isinstance(urls, str): + urls = [urls] + + # Add first valid file URL + for url in urls: + if file_exists(url): + valid_files[seg_idx] = url + break + + return valid_files diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 50e3b9dc7a..8d84cdbd5d 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -12,16 +12,15 @@ import urllib.parse import warnings import zstandard as zstd -from collections.abc import Callable, Iterable, Iterator +from collections.abc import Iterable, Iterator from typing import cast from urllib.parse import parse_qs, urlparse from cereal import log as capnp_log from openpilot.common.swaglog import cloudlog -from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url -from openpilot.tools.lib.openpilotci import get_url -from openpilot.tools.lib.filereader import DATA_ENDPOINT, FileReader, file_exists, internal_source_available -from openpilot.tools.lib.route import Route, SegmentRange, FileName +from openpilot.tools.lib.filereader import FileReader +from openpilot.tools.lib.file_sources import comma_api_source, internal_source, openpilotci_source, comma_car_segments_source, Source +from openpilot.tools.lib.route import SegmentRange, FileName from openpilot.tools.lib.log_time_series import msgs_to_time_series LogMessage = type[capnp._DynamicStructReader] @@ -140,65 +139,15 @@ class ReadMode(enum.StrEnum): AUTO_INTERACTIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user -LogFileName = tuple[str, ...] -Source = Callable[[SegmentRange, list[int], LogFileName], dict[int, str]] - -InternalUnavailableException = Exception("Internal source not available") - - class LogsUnavailable(Exception): pass -def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: - route = Route(sr.route_name) - - # comma api will have already checked if the file exists - if fns == FileName.RLOG: - return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} - else: - return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} - - -def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: - if not internal_source_available(endpoint_url): - raise InternalUnavailableException - - def get_internal_url(sr: SegmentRange, seg, file): - return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" - - return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) - - -def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: - return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) - - -def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: - return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) - - def direct_source(file_or_url: str) -> list[str]: return [file_or_url] -def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: - # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) - valid_files: dict[int, str] = {} - - for seg_idx, urls in files.items(): - if isinstance(urls, str): - urls = [urls] - - # Add first valid file URL - for url in urls: - if file_exists(url): - valid_files[seg_idx] = url - break - - return valid_files - - +# TODO this should apply to camera files as well def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) -> list[str]: exceptions = {} diff --git a/tools/lib/tests/test_logreader.py b/tools/lib/tests/test_logreader.py index 7a70ad6aab..6e508967b9 100644 --- a/tools/lib/tests/test_logreader.py +++ b/tools/lib/tests/test_logreader.py @@ -10,7 +10,8 @@ import requests from parameterized import parameterized from cereal import log as capnp_log -from openpilot.tools.lib.logreader import LogsUnavailable, LogIterable, LogReader, comma_api_source, parse_indirect, ReadMode, InternalUnavailableException +from openpilot.tools.lib.logreader import LogsUnavailable, LogIterable, LogReader, parse_indirect, ReadMode +from openpilot.tools.lib.log_sources import comma_api_source, InternalUnavailableException from openpilot.tools.lib.route import SegmentRange from openpilot.tools.lib.url_file import URLFileException @@ -90,7 +91,7 @@ class TestLogReader: @pytest.mark.parametrize("cache_enabled", [True, False]) def test_direct_parsing(self, mocker, cache_enabled): - file_exists_mock = mocker.patch("openpilot.tools.lib.logreader.file_exists") + file_exists_mock = mocker.patch("openpilot.tools.lib.filereader.file_exists") os.environ["FILEREADER_CACHE"] = "1" if cache_enabled else "0" qlog = tempfile.NamedTemporaryFile(mode='wb', delete=False) From dd5f5fdb98fed20d37cde4b9c786146814b00b58 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 19 Aug 2025 17:11:29 -0700 Subject: [PATCH 44/72] ci: show all unit test failures (#36029) * testci * fix * Revert "testci" This reverts commit b62a0aacb604fc0fd39c6e50a726b686979b9880. --- .github/workflows/selfdrive_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 2d94a39260..fa462166b0 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -162,7 +162,7 @@ jobs: timeout-minutes: ${{ contains(runner.name, 'nsc') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }} run: | ${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \ - $PYTEST --collect-only -m 'not slow' &> /dev/null && \ + $PYTEST --collect-only -m 'not slow' && \ MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \ ./selfdrive/ui/tests/create_test_translations.sh && \ QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \ From 3570022b9a7e90c98a0188d5b7f7f2e14710af61 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 19 Aug 2025 17:11:53 -0700 Subject: [PATCH 45/72] Revert "File sourcing: Not all files are logs (#36025)" This reverts commit 18b7ddef8fb8e6e0f1dbb2f8a7799e423d0bd208. --- tools/lib/file_sources.py | 57 ----------------------------- tools/lib/logreader.py | 61 ++++++++++++++++++++++++++++--- tools/lib/tests/test_logreader.py | 5 +-- 3 files changed, 58 insertions(+), 65 deletions(-) delete mode 100755 tools/lib/file_sources.py diff --git a/tools/lib/file_sources.py b/tools/lib/file_sources.py deleted file mode 100755 index cb7bf15114..0000000000 --- a/tools/lib/file_sources.py +++ /dev/null @@ -1,57 +0,0 @@ -from collections.abc import Callable - -from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url -from openpilot.tools.lib.openpilotci import get_url -from openpilot.tools.lib.filereader import DATA_ENDPOINT, file_exists, internal_source_available -from openpilot.tools.lib.route import Route, SegmentRange, FileName - -# When passed a tuple of file names, each source will return the first that exists (rlog.zst, rlog.bz2) -FileNames = tuple[str, ...] -Source = Callable[[SegmentRange, list[int], FileNames], dict[int, str]] - -InternalUnavailableException = Exception("Internal source not available") - - -def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: - route = Route(sr.route_name) - - # comma api will have already checked if the file exists - if fns == FileName.RLOG: - return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} - else: - return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} - - -def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: - if not internal_source_available(endpoint_url): - raise InternalUnavailableException - - def get_internal_url(sr: SegmentRange, seg, file): - return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" - - return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) - - -def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: - return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) - - -def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: - return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) - - -def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: - # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) - valid_files: dict[int, str] = {} - - for seg_idx, urls in files.items(): - if isinstance(urls, str): - urls = [urls] - - # Add first valid file URL - for url in urls: - if file_exists(url): - valid_files[seg_idx] = url - break - - return valid_files diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 8d84cdbd5d..50e3b9dc7a 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -12,15 +12,16 @@ import urllib.parse import warnings import zstandard as zstd -from collections.abc import Iterable, Iterator +from collections.abc import Callable, Iterable, Iterator from typing import cast from urllib.parse import parse_qs, urlparse from cereal import log as capnp_log from openpilot.common.swaglog import cloudlog -from openpilot.tools.lib.filereader import FileReader -from openpilot.tools.lib.file_sources import comma_api_source, internal_source, openpilotci_source, comma_car_segments_source, Source -from openpilot.tools.lib.route import SegmentRange, FileName +from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url +from openpilot.tools.lib.openpilotci import get_url +from openpilot.tools.lib.filereader import DATA_ENDPOINT, FileReader, file_exists, internal_source_available +from openpilot.tools.lib.route import Route, SegmentRange, FileName from openpilot.tools.lib.log_time_series import msgs_to_time_series LogMessage = type[capnp._DynamicStructReader] @@ -139,15 +140,65 @@ class ReadMode(enum.StrEnum): AUTO_INTERACTIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user +LogFileName = tuple[str, ...] +Source = Callable[[SegmentRange, list[int], LogFileName], dict[int, str]] + +InternalUnavailableException = Exception("Internal source not available") + + class LogsUnavailable(Exception): pass +def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: + route = Route(sr.route_name) + + # comma api will have already checked if the file exists + if fns == FileName.RLOG: + return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} + else: + return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} + + +def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: + if not internal_source_available(endpoint_url): + raise InternalUnavailableException + + def get_internal_url(sr: SegmentRange, seg, file): + return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" + + return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) + + +def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: + return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) + + +def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: + return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) + + def direct_source(file_or_url: str) -> list[str]: return [file_or_url] -# TODO this should apply to camera files as well +def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: + # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) + valid_files: dict[int, str] = {} + + for seg_idx, urls in files.items(): + if isinstance(urls, str): + urls = [urls] + + # Add first valid file URL + for url in urls: + if file_exists(url): + valid_files[seg_idx] = url + break + + return valid_files + + def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) -> list[str]: exceptions = {} diff --git a/tools/lib/tests/test_logreader.py b/tools/lib/tests/test_logreader.py index 6e508967b9..7a70ad6aab 100644 --- a/tools/lib/tests/test_logreader.py +++ b/tools/lib/tests/test_logreader.py @@ -10,8 +10,7 @@ import requests from parameterized import parameterized from cereal import log as capnp_log -from openpilot.tools.lib.logreader import LogsUnavailable, LogIterable, LogReader, parse_indirect, ReadMode -from openpilot.tools.lib.log_sources import comma_api_source, InternalUnavailableException +from openpilot.tools.lib.logreader import LogsUnavailable, LogIterable, LogReader, comma_api_source, parse_indirect, ReadMode, InternalUnavailableException from openpilot.tools.lib.route import SegmentRange from openpilot.tools.lib.url_file import URLFileException @@ -91,7 +90,7 @@ class TestLogReader: @pytest.mark.parametrize("cache_enabled", [True, False]) def test_direct_parsing(self, mocker, cache_enabled): - file_exists_mock = mocker.patch("openpilot.tools.lib.filereader.file_exists") + file_exists_mock = mocker.patch("openpilot.tools.lib.logreader.file_exists") os.environ["FILEREADER_CACHE"] = "1" if cache_enabled else "0" qlog = tempfile.NamedTemporaryFile(mode='wb', delete=False) From 22e79479d257bc3fca5692a82dc9b570db23c044 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 19:39:27 -0700 Subject: [PATCH 46/72] unit tests: add comment (#36030) * remove collection * test * back * wtf it actually saves 10s?! * ah that makes sense * rm * ? * ugh * qq * bc --- .github/workflows/selfdrive_tests.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index fa462166b0..aad772757d 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -162,7 +162,8 @@ jobs: timeout-minutes: ${{ contains(runner.name, 'nsc') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }} run: | ${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \ - $PYTEST --collect-only -m 'not slow' && \ + # Pre-compile Python bytecode so each pytest worker doesn't need to + $PYTEST --collect-only -m 'not slow' -qq && \ MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \ ./selfdrive/ui/tests/create_test_translations.sh && \ QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \ From 60c34a083791e96b0a244dc56decb1423e080da0 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 19:58:47 -0700 Subject: [PATCH 47/72] LogReader: run source test (#36031) run "slow" test --- tools/lib/tests/test_logreader.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/lib/tests/test_logreader.py b/tools/lib/tests/test_logreader.py index 7a70ad6aab..a9f7b2352d 100644 --- a/tools/lib/tests/test_logreader.py +++ b/tools/lib/tests/test_logreader.py @@ -40,7 +40,7 @@ def setup_source_scenario(mocker, is_internal=False): else: internal_source_mock.side_effect = InternalUnavailableException - openpilotci_source_mock.return_value = {3: None} + openpilotci_source_mock.return_value = {} comma_api_source_mock.return_value = {3: QLOG_FILE} yield @@ -208,13 +208,12 @@ class TestLogReader: assert qlog_len == log_len @pytest.mark.parametrize("is_internal", [True, False]) - @pytest.mark.slow def test_auto_source_scenarios(self, mocker, is_internal): lr = LogReader(QLOG_FILE) qlog_len = len(list(lr)) with setup_source_scenario(mocker, is_internal=is_internal): - lr = LogReader(f"{TEST_ROUTE}/0/q") + lr = LogReader(f"{TEST_ROUTE}/3/q") log_len = len(list(lr)) assert qlog_len == log_len From 870d19f33de194679e060b49f7a970c3cfaa3836 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 19:59:50 -0700 Subject: [PATCH 48/72] Reapply "File sourcing: Not all files are logs (#36025)" This reverts commit 3570022b9a7e90c98a0188d5b7f7f2e14710af61. Fix test --- tools/lib/file_sources.py | 57 +++++++++++++++++++++++++++++ tools/lib/logreader.py | 61 +++---------------------------- tools/lib/tests/test_logreader.py | 5 ++- 3 files changed, 65 insertions(+), 58 deletions(-) create mode 100755 tools/lib/file_sources.py diff --git a/tools/lib/file_sources.py b/tools/lib/file_sources.py new file mode 100755 index 0000000000..cb7bf15114 --- /dev/null +++ b/tools/lib/file_sources.py @@ -0,0 +1,57 @@ +from collections.abc import Callable + +from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url +from openpilot.tools.lib.openpilotci import get_url +from openpilot.tools.lib.filereader import DATA_ENDPOINT, file_exists, internal_source_available +from openpilot.tools.lib.route import Route, SegmentRange, FileName + +# When passed a tuple of file names, each source will return the first that exists (rlog.zst, rlog.bz2) +FileNames = tuple[str, ...] +Source = Callable[[SegmentRange, list[int], FileNames], dict[int, str]] + +InternalUnavailableException = Exception("Internal source not available") + + +def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: + route = Route(sr.route_name) + + # comma api will have already checked if the file exists + if fns == FileName.RLOG: + return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} + else: + return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} + + +def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: + if not internal_source_available(endpoint_url): + raise InternalUnavailableException + + def get_internal_url(sr: SegmentRange, seg, file): + return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" + + return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) + + +def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: + return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) + + +def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: FileNames) -> dict[int, str]: + return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) + + +def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: + # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) + valid_files: dict[int, str] = {} + + for seg_idx, urls in files.items(): + if isinstance(urls, str): + urls = [urls] + + # Add first valid file URL + for url in urls: + if file_exists(url): + valid_files[seg_idx] = url + break + + return valid_files diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 50e3b9dc7a..8d84cdbd5d 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -12,16 +12,15 @@ import urllib.parse import warnings import zstandard as zstd -from collections.abc import Callable, Iterable, Iterator +from collections.abc import Iterable, Iterator from typing import cast from urllib.parse import parse_qs, urlparse from cereal import log as capnp_log from openpilot.common.swaglog import cloudlog -from openpilot.tools.lib.comma_car_segments import get_url as get_comma_segments_url -from openpilot.tools.lib.openpilotci import get_url -from openpilot.tools.lib.filereader import DATA_ENDPOINT, FileReader, file_exists, internal_source_available -from openpilot.tools.lib.route import Route, SegmentRange, FileName +from openpilot.tools.lib.filereader import FileReader +from openpilot.tools.lib.file_sources import comma_api_source, internal_source, openpilotci_source, comma_car_segments_source, Source +from openpilot.tools.lib.route import SegmentRange, FileName from openpilot.tools.lib.log_time_series import msgs_to_time_series LogMessage = type[capnp._DynamicStructReader] @@ -140,65 +139,15 @@ class ReadMode(enum.StrEnum): AUTO_INTERACTIVE = "i" # default to rlogs, fallback to qlogs with a prompt from the user -LogFileName = tuple[str, ...] -Source = Callable[[SegmentRange, list[int], LogFileName], dict[int, str]] - -InternalUnavailableException = Exception("Internal source not available") - - class LogsUnavailable(Exception): pass -def comma_api_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: - route = Route(sr.route_name) - - # comma api will have already checked if the file exists - if fns == FileName.RLOG: - return {seg: route.log_paths()[seg] for seg in seg_idxs if route.log_paths()[seg] is not None} - else: - return {seg: route.qlog_paths()[seg] for seg in seg_idxs if route.qlog_paths()[seg] is not None} - - -def internal_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName, endpoint_url: str = DATA_ENDPOINT) -> dict[int, str]: - if not internal_source_available(endpoint_url): - raise InternalUnavailableException - - def get_internal_url(sr: SegmentRange, seg, file): - return f"{endpoint_url.rstrip('/')}/{sr.dongle_id}/{sr.log_id}/{seg}/{file}" - - return eval_source({seg: [get_internal_url(sr, seg, fn) for fn in fns] for seg in seg_idxs}) - - -def openpilotci_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: - return eval_source({seg: [get_url(sr.route_name, seg, fn) for fn in fns] for seg in seg_idxs}) - - -def comma_car_segments_source(sr: SegmentRange, seg_idxs: list[int], fns: LogFileName) -> dict[int, str]: - return eval_source({seg: get_comma_segments_url(sr.route_name, seg) for seg in seg_idxs}) - - def direct_source(file_or_url: str) -> list[str]: return [file_or_url] -def eval_source(files: dict[int, list[str] | str]) -> dict[int, str]: - # Returns valid file URLs given a list of possible file URLs for each segment (e.g. rlog.bz2, rlog.zst) - valid_files: dict[int, str] = {} - - for seg_idx, urls in files.items(): - if isinstance(urls, str): - urls = [urls] - - # Add first valid file URL - for url in urls: - if file_exists(url): - valid_files[seg_idx] = url - break - - return valid_files - - +# TODO this should apply to camera files as well def auto_source(identifier: str, sources: list[Source], default_mode: ReadMode) -> list[str]: exceptions = {} diff --git a/tools/lib/tests/test_logreader.py b/tools/lib/tests/test_logreader.py index a9f7b2352d..8d0870171f 100644 --- a/tools/lib/tests/test_logreader.py +++ b/tools/lib/tests/test_logreader.py @@ -10,7 +10,8 @@ import requests from parameterized import parameterized from cereal import log as capnp_log -from openpilot.tools.lib.logreader import LogsUnavailable, LogIterable, LogReader, comma_api_source, parse_indirect, ReadMode, InternalUnavailableException +from openpilot.tools.lib.logreader import LogsUnavailable, LogIterable, LogReader, parse_indirect, ReadMode +from openpilot.tools.lib.file_sources import comma_api_source, InternalUnavailableException from openpilot.tools.lib.route import SegmentRange from openpilot.tools.lib.url_file import URLFileException @@ -90,7 +91,7 @@ class TestLogReader: @pytest.mark.parametrize("cache_enabled", [True, False]) def test_direct_parsing(self, mocker, cache_enabled): - file_exists_mock = mocker.patch("openpilot.tools.lib.logreader.file_exists") + file_exists_mock = mocker.patch("openpilot.tools.lib.filereader.file_exists") os.environ["FILEREADER_CACHE"] = "1" if cache_enabled else "0" qlog = tempfile.NamedTemporaryFile(mode='wb', delete=False) From fa0017fafcf07c9905cd934af1940829dd4fdb1f Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 19 Aug 2025 23:54:07 -0400 Subject: [PATCH 49/72] car: initialize brand-related settings in opendbc (#1146) --- opendbc_repo | 2 +- selfdrive/car/card.py | 4 +++- sunnypilot/selfdrive/car/interfaces.py | 31 ++++++++++---------------- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index cf9b33143f..25d172711a 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit cf9b33143fb7c1a337be9c3f20428a9fb17323e1 +Subproject commit 25d172711a52817075d7aedda090995038ffcb4d diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index 159ab8fb17..820c68e895 100755 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -107,8 +107,10 @@ class Car: cached_params = _cached_params fixed_fingerprint = (self.params.get("CarPlatformBundle") or {}).get("platform", None) + init_params_list_sp = sunnypilot_interfaces.initialize_params(self.params) - self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, is_release, num_pandas, cached_params, fixed_fingerprint) + self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, is_release, num_pandas, cached_params, + fixed_fingerprint, init_params_list_sp) sunnypilot_interfaces.setup_interfaces(self.CI, self.params) self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP, self.CI.CP_SP) self.CP = self.CI.CP diff --git a/sunnypilot/selfdrive/car/interfaces.py b/sunnypilot/selfdrive/car/interfaces.py index 28bae4a0dd..cd7a24b63b 100644 --- a/sunnypilot/selfdrive/car/interfaces.py +++ b/sunnypilot/selfdrive/car/interfaces.py @@ -4,11 +4,10 @@ Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. This file is part of sunnypilot and is licensed under the MIT License. See the LICENSE.md file in the root directory for more details. """ +from typing import Any from opendbc.car import structs from opendbc.car.interfaces import CarInterfaceBase -from opendbc.sunnypilot.car.hyundai.longitudinal.helpers import LongitudinalTuningType -from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog from openpilot.sunnypilot.selfdrive.controls.lib.nnlc.helpers import get_nn_model_path @@ -23,22 +22,6 @@ def log_fingerprint(CP: structs.CarParams) -> None: sentry.capture_fingerprint(CP.carFingerprint, CP.brand) -def _initialize_custom_longitudinal_tuning(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP, - params: Params = None) -> None: - if params is None: - params = Params() - - # Hyundai Custom Longitudinal Tuning - if CP.brand == 'hyundai': - hyundai_longitudinal_tuning = params.get("HyundaiLongitudinalTuning") - if hyundai_longitudinal_tuning == LongitudinalTuningType.DYNAMIC: - CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_DYNAMIC.value - if hyundai_longitudinal_tuning == LongitudinalTuningType.PREDICTIVE: - CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_PREDICTIVE.value - - CP_SP = CI.get_longitudinal_tuning_sp(CP, CP_SP) - - def _initialize_neural_network_lateral_control(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params = None, enabled: bool = False) -> None: if params is None: @@ -64,5 +47,15 @@ def setup_interfaces(CI: CarInterfaceBase, params: Params = None) -> None: CP = CI.CP CP_SP = CI.CP_SP - _initialize_custom_longitudinal_tuning(CI, CP, CP_SP, params) _initialize_neural_network_lateral_control(CI, CP, CP_SP, params) + + +def initialize_params(params) -> list[dict[str, Any]]: + keys: list = [] + + # hyundai + keys.extend([ + "HyundaiLongitudinalTuning" + ]) + + return [{k: params.get(k, return_default=True)} for k in keys] From d0069c136b4302c6978717abc6e3859efeea3a93 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 22:19:56 -0700 Subject: [PATCH 50/72] raylib: fix experimental mode path gradient (#36033) * fix! * this is enough to fix the broken colors * clean up * fix * use last colors -- need this so we don't have to always pass perfect gradient * clean up * clean up * clean up --- selfdrive/ui/onroad/model_renderer.py | 16 ++++++++-------- system/ui/lib/shader_polygon.py | 4 ++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 4439141a40..684f2a8453 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -187,9 +187,9 @@ class ModelRenderer(Widget): self._path.raw_points, 0.9, self._path_offset_z, max_idx, allow_invert=False ) - self._update_experimental_gradient(self._rect.height) + self._update_experimental_gradient() - def _update_experimental_gradient(self, height): + def _update_experimental_gradient(self): """Pre-calculate experimental mode gradient colors""" if not self._experimental_mode: return @@ -201,14 +201,14 @@ class ModelRenderer(Widget): i = 0 while i < max_len: - track_idx = max_len - i - 1 # flip idx to start from bottom right - track_y = self._path.projected_points[track_idx][1] - if track_y < 0 or track_y > height: + # Some points are out of frame + track_y = self._path.projected_points[i][1] + if track_y < self._rect.y or track_y > self._rect.height: i += 1 continue - # Calculate color based on acceleration - lin_grad_point = (height - track_y) / height + # Calculate color based on acceleration (0 is bottom, 1 is top) + lin_grad_point = 1 - (track_y - self._rect.y) / self._rect.height # speed up: 120, slow down: 0 path_hue = max(min(60 + self._acceleration_x[i] * 35, 120), 0) @@ -280,7 +280,7 @@ class ModelRenderer(Widget): if self._experimental_mode: # Draw with acceleration coloring - if len(self._exp_gradient['colors']) > 2: + if len(self._exp_gradient['colors']) > 1: draw_polygon(self._rect, self._path.projected_points, gradient=self._exp_gradient) else: draw_polygon(self._rect, self._path.projected_points, rl.Color(255, 255, 255, 30)) diff --git a/system/ui/lib/shader_polygon.py b/system/ui/lib/shader_polygon.py index cfde81c554..39bc0d5aa4 100644 --- a/system/ui/lib/shader_polygon.py +++ b/system/ui/lib/shader_polygon.py @@ -39,6 +39,10 @@ vec4 getGradientColor(vec2 pos) { float t = clamp(dot(pos - gradientStart, normalizedDir) / gradientLength, 0.0, 1.0); if (gradientColorCount <= 1) return gradientColors[0]; + + // handle t before first / after last stop + if (t <= gradientStops[0]) return gradientColors[0]; + if (t >= gradientStops[gradientColorCount-1]) return gradientColors[gradientColorCount-1]; for (int i = 0; i < gradientColorCount - 1; i++) { if (t >= gradientStops[i] && t <= gradientStops[i+1]) { float segmentT = (t - gradientStops[i]) / (gradientStops[i+1] - gradientStops[i]); From 63441c048cb59178778aa670e17973324d2ed0d9 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 19 Aug 2025 22:30:48 -0700 Subject: [PATCH 51/72] test_onroad: relax first fid assertion (#36032) * fid * test * Revert "test" This reverts commit 38e6635dd0b0b9fb9c08bcc3a74b9283207b0c2f. * r * Revert "r" This reverts commit 4037a321f89af137a645345a0fffb73da6071c72. --- selfdrive/test/test_onroad.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 935da99c10..0149653c84 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -333,20 +333,18 @@ class TestOnroad: assert np.all(eof_sof_diff > 0) assert np.all(eof_sof_diff < 50*1e6) - first_fid = {c: min(self.ts[c]['frameId']) for c in cams} + first_fid = {min(self.ts[c]['frameId']) for c in cams} + assert len(first_fid) == 1, "Cameras don't start on same frame ID" if cam.endswith('CameraState'): # camerad guarantees that all cams start on frame ID 0 # (note loggerd also needs to start up fast enough to catch it) - assert set(first_fid.values()) == {0, }, "Cameras don't start on frame ID 0" - else: - # encoder guarantees all cams start on the same frame ID - assert len(set(first_fid.values())) == 1, "Cameras don't start on same frame ID" + assert next(iter(first_fid)) < 100, "Cameras start on frame ID too high" # we don't do a full segment rotation, so these might not match exactly - last_fid = {c: max(self.ts[c]['frameId']) for c in cams} - assert max(last_fid.values()) - min(last_fid.values()) < 10 + last_fid = {max(self.ts[c]['frameId']) for c in cams} + assert max(last_fid) - min(last_fid) < 10 - start, end = min(first_fid.values()), min(last_fid.values()) + start, end = min(first_fid), min(last_fid) for i in range(end-start): ts = {c: round(self.ts[c]['timestampSof'][i]/1e6, 1) for c in cams} diff = (max(ts.values()) - min(ts.values())) From 8320934d91b78ddea2c4d714dc29e3d4c72dd196 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 22:33:07 -0700 Subject: [PATCH 52/72] raylib: cleanup experimental mode gradient color calculations (#36035) * dfebug * simplify * come on man --- selfdrive/ui/onroad/model_renderer.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 684f2a8453..f81b0be66c 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -211,12 +211,11 @@ class ModelRenderer(Widget): lin_grad_point = 1 - (track_y - self._rect.y) / self._rect.height # speed up: 120, slow down: 0 - path_hue = max(min(60 + self._acceleration_x[i] * 35, 120), 0) - path_hue = int(path_hue * 100 + 0.5) / 100 + path_hue = np.clip(60 + self._acceleration_x[i] * 35, 0, 120) saturation = min(abs(self._acceleration_x[i] * 1.5), 1) - lightness = self._map_val(saturation, 0.0, 1.0, 0.95, 0.62) - alpha = self._map_val(lin_grad_point, 0.75 / 2.0, 0.75, 0.4, 0.0) + lightness = np.interp(saturation, [0.0, 1.0], [0.95, 0.62]) + alpha = np.interp(lin_grad_point, [0.75 / 2.0, 0.75], [0.4, 0.0]) # Use HSL to RGB conversion color = self._hsla_to_color(path_hue / 360.0, saturation, lightness, alpha) @@ -409,13 +408,6 @@ class ModelRenderer(Widget): return np.vstack((left_screen.T, right_screen[:, ::-1].T)).astype(np.float32) - @staticmethod - def _map_val(x, x0, x1, y0, y1): - x = np.clip(x, x0, x1) - ra = x1 - x0 - rb = y1 - y0 - return (x - x0) * rb / ra + y0 if ra != 0 else y0 - @staticmethod def _hsla_to_color(h, s, l, a): rgb = colorsys.hls_to_rgb(h, l, s) From 2ff707d82f53b01811ad184fbfc873c664188cc3 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 19 Aug 2025 22:37:55 -0700 Subject: [PATCH 53/72] Fix gradient point ignore --- selfdrive/ui/onroad/model_renderer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index f81b0be66c..932773755d 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -201,9 +201,9 @@ class ModelRenderer(Widget): i = 0 while i < max_len: - # Some points are out of frame + # Some points (screen space) are out of frame (rect space) track_y = self._path.projected_points[i][1] - if track_y < self._rect.y or track_y > self._rect.height: + if track_y < self._rect.y or track_y > (self._rect.y + self._rect.height): i += 1 continue From 638dfb68bab1925da2baeed355a92f7e811a8c48 Mon Sep 17 00:00:00 2001 From: Warren Togami Date: Wed, 20 Aug 2025 12:18:07 -0500 Subject: [PATCH 54/72] ui: display thousandths place on lagd toggle (#1166) lagd: display thousandth place so typical variation is visible Co-authored-by: Jason Wen --- selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc index 413bd8a373..93e5ac80a8 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc @@ -393,7 +393,7 @@ void ModelsPanel::updateLabels() { auto liveDelay = event.getLiveDelay(); float lateralDelay = liveDelay.getLateralDelay(); desc += QString("

%1 %2 s") - .arg(tr("Live Steer Delay:")).arg(QString::number(lateralDelay, 'f', 2)); + .arg(tr("Live Steer Delay:")).arg(QString::number(lateralDelay, 'f', 3)); } } } else { From 154f65533554e7b55b6d9abdf22d57e5035b1bc5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 20 Aug 2025 15:44:13 -0700 Subject: [PATCH 55/72] update release checklist --- release/README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/release/README.md b/release/README.md index 266d881d3c..170a2d65ca 100644 --- a/release/README.md +++ b/release/README.md @@ -3,7 +3,7 @@ ``` ## release checklist -**Go to staging** +### Go to staging - [ ] make a GitHub issue to track release - [ ] create release master branch - [ ] update RELEASES.md @@ -11,11 +11,12 @@ - [ ] build new userdata partition from `release3-staging` - [ ] post on Discord, tag `@release crew` -Updates to staging: -- [ ] either rebase on master or cherry-pick changes -- [ ] run this to update: `BRANCH=devel-staging release/build_devel.sh` +Updating staging: +1. either rebase on master or cherry-pick changes +2. run this to update: `BRANCH=devel-staging release/build_devel.sh` +3. build new userdata partition from `release3-staging` -**Go to release** +### Go to release - [ ] before going to release, test the following: - [ ] update from previous release -> new release - [ ] update from new release -> previous release From b4cc4ea8e2dc0c7cae0fe5407b868a71dc1c1af9 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 20 Aug 2025 15:44:23 -0700 Subject: [PATCH 56/72] Update README.md --- release/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/README.md b/release/README.md index 170a2d65ca..fb651fa05a 100644 --- a/release/README.md +++ b/release/README.md @@ -14,7 +14,7 @@ Updating staging: 1. either rebase on master or cherry-pick changes 2. run this to update: `BRANCH=devel-staging release/build_devel.sh` -3. build new userdata partition from `release3-staging` +3. build new userdata partition from `release3-staging` ### Go to release - [ ] before going to release, test the following: From cd9ec6b240abd6e2a2aa4afc29840180a332147f Mon Sep 17 00:00:00 2001 From: "kostas.pats" <35031825+kostas1507@users.noreply.github.com> Date: Wed, 20 Aug 2025 22:45:05 +0000 Subject: [PATCH 57/72] Compressed vipc name pick (#36036) * add custom vipc server name argument * Update compressed_vipc.py * add custom vipc server name argument + fixes * Update compressed_vipc.py --- tools/camerastream/compressed_vipc.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/camerastream/compressed_vipc.py b/tools/camerastream/compressed_vipc.py index b25b8b0cb7..4dc74272ea 100755 --- a/tools/camerastream/compressed_vipc.py +++ b/tools/camerastream/compressed_vipc.py @@ -107,7 +107,7 @@ def decoder(addr, vipc_server, vst, nvidia, W, H, debug=False): class CompressedVipc: - def __init__(self, addr, vision_streams, nvidia=False, debug=False): + def __init__(self, addr, vision_streams, server_name, nvidia=False, debug=False): print("getting frame sizes") os.environ["ZMQ"] = "1" messaging.reset_context() @@ -117,7 +117,7 @@ class CompressedVipc: os.environ.pop("ZMQ") messaging.reset_context() - self.vipc_server = VisionIpcServer("camerad") + self.vipc_server = VisionIpcServer(server_name) for vst in vision_streams: ed = sm[ENCODE_SOCKETS[vst]] self.vipc_server.create_buffers(vst, 4, ed.width, ed.height) @@ -144,6 +144,7 @@ if __name__ == "__main__": parser.add_argument("addr", help="Address of comma three") parser.add_argument("--nvidia", action="store_true", help="Use nvidia instead of ffmpeg") parser.add_argument("--cams", default="0,1,2", help="Cameras to decode") + parser.add_argument("--server", default="camerad", help="choose vipc server name") parser.add_argument("--silent", action="store_true", help="Suppress debug output") args = parser.parse_args() @@ -154,7 +155,7 @@ if __name__ == "__main__": ] vsts = [vision_streams[int(x)] for x in args.cams.split(",")] - cvipc = CompressedVipc(args.addr, vsts, args.nvidia, debug=(not args.silent)) + cvipc = CompressedVipc(args.addr, vsts, args.server, args.nvidia, debug=(not args.silent)) # register exit handler signal.signal(signal.SIGINT, lambda sig, frame: cvipc.kill()) From cea3572b7416fd7589a85e9572fb620976e7b163 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 21 Aug 2025 16:54:15 -0700 Subject: [PATCH 58/72] raylib: fix mouse scale for Widgets (#36040) fix mouse scale for mousestate --- system/ui/lib/application.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 718bf036fa..3f433e1fcb 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -67,7 +67,8 @@ class MouseEvent(NamedTuple): class MouseState: - def __init__(self): + def __init__(self, scale: float = 1.0): + self._scale = scale self._events: deque[MouseEvent] = deque(maxlen=MOUSE_THREAD_RATE) # bound event list self._prev_mouse_event: list[MouseEvent | None] = [None] * MAX_TOUCH_SLOTS @@ -102,8 +103,10 @@ class MouseState: def _handle_mouse_event(self): for slot in range(MAX_TOUCH_SLOTS): mouse_pos = rl.get_touch_position(slot) + x = mouse_pos.x / self._scale if self._scale != 1.0 else mouse_pos.x + y = mouse_pos.y / self._scale if self._scale != 1.0 else mouse_pos.y ev = MouseEvent( - MousePos(mouse_pos.x, mouse_pos.y), + MousePos(x, y), slot, rl.is_mouse_button_pressed(slot), rl.is_mouse_button_released(slot), @@ -133,7 +136,7 @@ class GuiApplication: self._trace_log_callback = None self._modal_overlay = ModalOverlay() - self._mouse = MouseState() + self._mouse = MouseState(self._scale) self._mouse_events: list[MouseEvent] = [] # Debug variables From 7f035bae3966c6fb2648ea43c01177c8faa46766 Mon Sep 17 00:00:00 2001 From: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com> Date: Thu, 21 Aug 2025 18:04:57 -0700 Subject: [PATCH 59/72] ui: bugfix visibility for developer panel (#1131) * yes idk move up * patch * updates * Update developer_panel.cc * Update developer_panel.cc * Update developer_panel.cc --------- Co-authored-by: Jason Wen --- .../qt/offroad/settings/developer_panel.cc | 12 ++++++------ .../sunnypilot/qt/offroad/settings/developer_panel.h | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/developer_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/developer_panel.cc index 5c9f0ff99e..58193f9fe8 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/developer_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/developer_panel.cc @@ -47,17 +47,17 @@ DeveloperPanelSP::DeveloperPanelSP(SettingsWindow *parent) : DeveloperPanel(pare addItem(errorLogBtn); QObject::connect(uiState(), &UIState::offroadTransition, this, &DeveloperPanelSP::updateToggles); + + is_release = params.getBool("IsReleaseBranch"); + is_tested = params.getBool("IsTestedBranch"); + is_development = params.getBool("IsDevelopmentBranch"); } void DeveloperPanelSP::updateToggles(bool offroad) { - bool is_release = params.getBool("IsReleaseBranch"); - bool is_tested = params.getBool("IsTestedBranch"); - bool is_development = params.getBool("IsDevelopmentBranch"); bool disable_updates = params.getBool("DisableUpdates"); prebuiltToggle->setVisible(!is_release && !is_tested && !is_development); prebuiltToggle->setEnabled(disable_updates); - params.putBool("QuickBootToggle", QFile::exists("/data/openpilot/prebuilt")); prebuiltToggle->refresh(); @@ -66,6 +66,7 @@ void DeveloperPanelSP::updateToggles(bool offroad) { "it immediately removes the prebuilt file so compilation of locally edited cpp files can be made. " "

To edit C++ files locally on device, you MUST first turn off this toggle so the changes can recompile.") : tr("Quickboot mode requires updates to be disabled.
Enable 'Disable Updates' in the Software panel first.")); + prebuiltToggle->showDescription(); enableGithubRunner->setVisible(!is_release); errorLogBtn->setVisible(!is_release); @@ -74,7 +75,6 @@ void DeveloperPanelSP::updateToggles(bool offroad) { void DeveloperPanelSP::showEvent(QShowEvent *event) { DeveloperPanel::showEvent(event); - updateToggles(!uiState()->scene.started); AbstractControlSP::UpdateAllAdvancedControls(); - prebuiltToggle->showDescription(); + updateToggles(!uiState()->scene.started); } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/developer_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/developer_panel.h index d63fe18d04..42b3bd83b8 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/developer_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/developer_panel.h @@ -21,6 +21,9 @@ private: ParamControlSP *prebuiltToggle; Params params; ParamControlSP *showAdvancedControls; + bool is_development; + bool is_release; + bool is_tested; private slots: void updateToggles(bool offroad); From c0a74f7a20da6375d710b92037bbf6143fe3822a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 22 Aug 2025 01:55:01 -0700 Subject: [PATCH 60/72] raylib: change default tethering password --- system/ui/lib/wifi_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 178bbec43e..4cb741bc95 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -37,7 +37,7 @@ NM_DEVICE_IFACE = "org.freedesktop.NetworkManager.Device" NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT = 8 TETHERING_IP_ADDRESS = "192.168.43.1" -DEFAULT_TETHERING_PASSWORD = "12345678" +DEFAULT_TETHERING_PASSWORD = "swagswagcomma" # NetworkManager device states From ae3b74245f0685e70e5e76c193e99f7f175c1db0 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 22 Aug 2025 09:08:40 -0700 Subject: [PATCH 61/72] sgo is just o now! --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index dcf645ff61..f3af58f85b 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,6 @@ We have detailed instructions for [how to install the harness and device in a ca | `release3-staging` | openpilot-test.comma.ai | This is the staging branch for releases. Use it to get new releases slightly early. | | `nightly` | openpilot-nightly.comma.ai | This is the bleeding edge development branch. Do not expect this to be stable. | | `nightly-dev` | installer.comma.ai/commaai/nightly-dev | Same as nightly, but includes experimental development features for some cars. | -| `secretgoodopenpilot` | installer.comma.ai/commaai/secretgoodopenpilot | This is a preview branch from the autonomy team where new driving models get merged earlier than master. | To start developing openpilot ------ From 5491a6138495e939b983f4c8d5194c90d885ebfd Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 22 Aug 2025 19:46:24 -0400 Subject: [PATCH 62/72] ci: skip tinygrad bump in repo-maintenance (#1173) --- .github/workflows/repo-maintenance.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/repo-maintenance.yaml b/.github/workflows/repo-maintenance.yaml index 72de377086..14b4d904e3 100644 --- a/.github/workflows/repo-maintenance.yaml +++ b/.github/workflows/repo-maintenance.yaml @@ -50,6 +50,7 @@ jobs: - name: bump submodules run: | git config --global --add safe.directory '*' + git config submodule.tinygrad.update none git submodule update --remote git add . - name: update car docs From de56b2110382be3c002304eb017cb65903e89eda Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 19:50:09 -0400 Subject: [PATCH 63/72] [bot] Update Python packages (#1165) Update Python packages Co-authored-by: github-actions[bot] Co-authored-by: Jason Wen --- docs/CARS.md | 4 +- opendbc_repo | 2 +- uv.lock | 253 ++++++++++++++++++++++++++------------------------- 3 files changed, 134 insertions(+), 125 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index e043c3b10f..0ee0615a54 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 323 Supported Cars +# 325 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video|Setup Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -105,6 +105,7 @@ A supported vehicle is one that just works when you install a comma device. All |Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|Elantra GT 2017-20|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Elantra Non-SCC 2022|No Smart Cruise Control (Non-SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai J connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|Ioniq 5 (Southeast Asia and Europe only) 2022-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| @@ -124,6 +125,7 @@ A supported vehicle is one that just works when you install a comma device. All |Hyundai|Kona Electric (with HDA II, Korea only) 2023[6](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai R connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|Kona Electric Non-SCC 2019|No Smart Cruise Control (Non-SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai G connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai I connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Kona Non-SCC 2019|No Smart Cruise Control (Non-SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|Nexo 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|Palisade 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Hyundai|Santa Cruz 2022-24[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| diff --git a/opendbc_repo b/opendbc_repo index 25d172711a..23d37a87ec 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 25d172711a52817075d7aedda090995038ffcb4d +Subproject commit 23d37a87ec73637cc021b2fa65adc060919d3396 diff --git a/uv.lock b/uv.lock index 1b6dd18961..49ae160993 100644 --- a/uv.lock +++ b/uv.lock @@ -763,40 +763,48 @@ wheels = [ [[package]] name = "lxml" -version = "6.0.0" +version = "6.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c5/ed/60eb6fa2923602fba988d9ca7c5cdbd7cf25faa795162ed538b527a35411/lxml-6.0.0.tar.gz", hash = "sha256:032e65120339d44cdc3efc326c9f660f5f7205f3a535c1fdbf898b29ea01fb72", size = 4096938, upload-time = "2025-06-26T16:28:19.373Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8f/bd/f9d01fd4132d81c6f43ab01983caea69ec9614b913c290a26738431a015d/lxml-6.0.1.tar.gz", hash = "sha256:2b3a882ebf27dd026df3801a87cf49ff791336e0f94b0fad195db77e01240690", size = 4070214, upload-time = "2025-08-22T10:37:53.525Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/23/828d4cc7da96c611ec0ce6147bbcea2fdbde023dc995a165afa512399bbf/lxml-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ee56288d0df919e4aac43b539dd0e34bb55d6a12a6562038e8d6f3ed07f9e36", size = 8438217, upload-time = "2025-06-26T16:25:34.349Z" }, - { url = "https://files.pythonhosted.org/packages/f1/33/5ac521212c5bcb097d573145d54b2b4a3c9766cda88af5a0e91f66037c6e/lxml-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8dd6dd0e9c1992613ccda2bcb74fc9d49159dbe0f0ca4753f37527749885c25", size = 4590317, upload-time = "2025-06-26T16:25:38.103Z" }, - { url = "https://files.pythonhosted.org/packages/2b/2e/45b7ca8bee304c07f54933c37afe7dd4d39ff61ba2757f519dcc71bc5d44/lxml-6.0.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:d7ae472f74afcc47320238b5dbfd363aba111a525943c8a34a1b657c6be934c3", size = 5221628, upload-time = "2025-06-26T16:25:40.878Z" }, - { url = "https://files.pythonhosted.org/packages/32/23/526d19f7eb2b85da1f62cffb2556f647b049ebe2a5aa8d4d41b1fb2c7d36/lxml-6.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5592401cdf3dc682194727c1ddaa8aa0f3ddc57ca64fd03226a430b955eab6f6", size = 4949429, upload-time = "2025-06-28T18:47:20.046Z" }, - { url = "https://files.pythonhosted.org/packages/ac/cc/f6be27a5c656a43a5344e064d9ae004d4dcb1d3c9d4f323c8189ddfe4d13/lxml-6.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58ffd35bd5425c3c3b9692d078bf7ab851441434531a7e517c4984d5634cd65b", size = 5087909, upload-time = "2025-06-28T18:47:22.834Z" }, - { url = "https://files.pythonhosted.org/packages/3b/e6/8ec91b5bfbe6972458bc105aeb42088e50e4b23777170404aab5dfb0c62d/lxml-6.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f720a14aa102a38907c6d5030e3d66b3b680c3e6f6bc95473931ea3c00c59967", size = 5031713, upload-time = "2025-06-26T16:25:43.226Z" }, - { url = "https://files.pythonhosted.org/packages/33/cf/05e78e613840a40e5be3e40d892c48ad3e475804db23d4bad751b8cadb9b/lxml-6.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2a5e8d207311a0170aca0eb6b160af91adc29ec121832e4ac151a57743a1e1e", size = 5232417, upload-time = "2025-06-26T16:25:46.111Z" }, - { url = "https://files.pythonhosted.org/packages/ac/8c/6b306b3e35c59d5f0b32e3b9b6b3b0739b32c0dc42a295415ba111e76495/lxml-6.0.0-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:2dd1cc3ea7e60bfb31ff32cafe07e24839df573a5e7c2d33304082a5019bcd58", size = 4681443, upload-time = "2025-06-26T16:25:48.837Z" }, - { url = "https://files.pythonhosted.org/packages/59/43/0bd96bece5f7eea14b7220476835a60d2b27f8e9ca99c175f37c085cb154/lxml-6.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cfcf84f1defed7e5798ef4f88aa25fcc52d279be731ce904789aa7ccfb7e8d2", size = 5074542, upload-time = "2025-06-26T16:25:51.65Z" }, - { url = "https://files.pythonhosted.org/packages/e2/3d/32103036287a8ca012d8518071f8852c68f2b3bfe048cef2a0202eb05910/lxml-6.0.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a52a4704811e2623b0324a18d41ad4b9fabf43ce5ff99b14e40a520e2190c851", size = 4729471, upload-time = "2025-06-26T16:25:54.571Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a8/7be5d17df12d637d81854bd8648cd329f29640a61e9a72a3f77add4a311b/lxml-6.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c16304bba98f48a28ae10e32a8e75c349dd742c45156f297e16eeb1ba9287a1f", size = 5256285, upload-time = "2025-06-26T16:25:56.997Z" }, - { url = "https://files.pythonhosted.org/packages/cd/d0/6cb96174c25e0d749932557c8d51d60c6e292c877b46fae616afa23ed31a/lxml-6.0.0-cp311-cp311-win32.whl", hash = "sha256:f8d19565ae3eb956d84da3ef367aa7def14a2735d05bd275cd54c0301f0d0d6c", size = 3612004, upload-time = "2025-06-26T16:25:59.11Z" }, - { url = "https://files.pythonhosted.org/packages/ca/77/6ad43b165dfc6dead001410adeb45e88597b25185f4479b7ca3b16a5808f/lxml-6.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:b2d71cdefda9424adff9a3607ba5bbfc60ee972d73c21c7e3c19e71037574816", size = 4003470, upload-time = "2025-06-26T16:26:01.655Z" }, - { url = "https://files.pythonhosted.org/packages/a0/bc/4c50ec0eb14f932a18efc34fc86ee936a66c0eb5f2fe065744a2da8a68b2/lxml-6.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:8a2e76efbf8772add72d002d67a4c3d0958638696f541734304c7f28217a9cab", size = 3682477, upload-time = "2025-06-26T16:26:03.808Z" }, - { url = "https://files.pythonhosted.org/packages/89/c3/d01d735c298d7e0ddcedf6f028bf556577e5ab4f4da45175ecd909c79378/lxml-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78718d8454a6e928470d511bf8ac93f469283a45c354995f7d19e77292f26108", size = 8429515, upload-time = "2025-06-26T16:26:06.776Z" }, - { url = "https://files.pythonhosted.org/packages/06/37/0e3eae3043d366b73da55a86274a590bae76dc45aa004b7042e6f97803b1/lxml-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:84ef591495ffd3f9dcabffd6391db7bb70d7230b5c35ef5148354a134f56f2be", size = 4601387, upload-time = "2025-06-26T16:26:09.511Z" }, - { url = "https://files.pythonhosted.org/packages/a3/28/e1a9a881e6d6e29dda13d633885d13acb0058f65e95da67841c8dd02b4a8/lxml-6.0.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:2930aa001a3776c3e2601cb8e0a15d21b8270528d89cc308be4843ade546b9ab", size = 5228928, upload-time = "2025-06-26T16:26:12.337Z" }, - { url = "https://files.pythonhosted.org/packages/9a/55/2cb24ea48aa30c99f805921c1c7860c1f45c0e811e44ee4e6a155668de06/lxml-6.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:219e0431ea8006e15005767f0351e3f7f9143e793e58519dc97fe9e07fae5563", size = 4952289, upload-time = "2025-06-28T18:47:25.602Z" }, - { url = "https://files.pythonhosted.org/packages/31/c0/b25d9528df296b9a3306ba21ff982fc5b698c45ab78b94d18c2d6ae71fd9/lxml-6.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bd5913b4972681ffc9718bc2d4c53cde39ef81415e1671ff93e9aa30b46595e7", size = 5111310, upload-time = "2025-06-28T18:47:28.136Z" }, - { url = "https://files.pythonhosted.org/packages/e9/af/681a8b3e4f668bea6e6514cbcb297beb6de2b641e70f09d3d78655f4f44c/lxml-6.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:390240baeb9f415a82eefc2e13285016f9c8b5ad71ec80574ae8fa9605093cd7", size = 5025457, upload-time = "2025-06-26T16:26:15.068Z" }, - { url = "https://files.pythonhosted.org/packages/99/b6/3a7971aa05b7be7dfebc7ab57262ec527775c2c3c5b2f43675cac0458cad/lxml-6.0.0-cp312-cp312-manylinux_2_27_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d6e200909a119626744dd81bae409fc44134389e03fbf1d68ed2a55a2fb10991", size = 5657016, upload-time = "2025-07-03T19:19:06.008Z" }, - { url = "https://files.pythonhosted.org/packages/69/f8/693b1a10a891197143c0673fcce5b75fc69132afa81a36e4568c12c8faba/lxml-6.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ca50bd612438258a91b5b3788c6621c1f05c8c478e7951899f492be42defc0da", size = 5257565, upload-time = "2025-06-26T16:26:17.906Z" }, - { url = "https://files.pythonhosted.org/packages/a8/96/e08ff98f2c6426c98c8964513c5dab8d6eb81dadcd0af6f0c538ada78d33/lxml-6.0.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:c24b8efd9c0f62bad0439283c2c795ef916c5a6b75f03c17799775c7ae3c0c9e", size = 4713390, upload-time = "2025-06-26T16:26:20.292Z" }, - { url = "https://files.pythonhosted.org/packages/a8/83/6184aba6cc94d7413959f6f8f54807dc318fdcd4985c347fe3ea6937f772/lxml-6.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:afd27d8629ae94c5d863e32ab0e1d5590371d296b87dae0a751fb22bf3685741", size = 5066103, upload-time = "2025-06-26T16:26:22.765Z" }, - { url = "https://files.pythonhosted.org/packages/ee/01/8bf1f4035852d0ff2e36a4d9aacdbcc57e93a6cd35a54e05fa984cdf73ab/lxml-6.0.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:54c4855eabd9fc29707d30141be99e5cd1102e7d2258d2892314cf4c110726c3", size = 4791428, upload-time = "2025-06-26T16:26:26.461Z" }, - { url = "https://files.pythonhosted.org/packages/29/31/c0267d03b16954a85ed6b065116b621d37f559553d9339c7dcc4943a76f1/lxml-6.0.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c907516d49f77f6cd8ead1322198bdfd902003c3c330c77a1c5f3cc32a0e4d16", size = 5678523, upload-time = "2025-07-03T19:19:09.837Z" }, - { url = "https://files.pythonhosted.org/packages/5c/f7/5495829a864bc5f8b0798d2b52a807c89966523140f3d6fa3a58ab6720ea/lxml-6.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36531f81c8214e293097cd2b7873f178997dae33d3667caaae8bdfb9666b76c0", size = 5281290, upload-time = "2025-06-26T16:26:29.406Z" }, - { url = "https://files.pythonhosted.org/packages/79/56/6b8edb79d9ed294ccc4e881f4db1023af56ba451909b9ce79f2a2cd7c532/lxml-6.0.0-cp312-cp312-win32.whl", hash = "sha256:690b20e3388a7ec98e899fd54c924e50ba6693874aa65ef9cb53de7f7de9d64a", size = 3613495, upload-time = "2025-06-26T16:26:31.588Z" }, - { url = "https://files.pythonhosted.org/packages/0b/1e/cc32034b40ad6af80b6fd9b66301fc0f180f300002e5c3eb5a6110a93317/lxml-6.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:310b719b695b3dd442cdfbbe64936b2f2e231bb91d998e99e6f0daf991a3eba3", size = 4014711, upload-time = "2025-06-26T16:26:33.723Z" }, - { url = "https://files.pythonhosted.org/packages/55/10/dc8e5290ae4c94bdc1a4c55865be7e1f31dfd857a88b21cbba68b5fea61b/lxml-6.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:8cb26f51c82d77483cdcd2b4a53cda55bbee29b3c2f3ddeb47182a2a9064e4eb", size = 3674431, upload-time = "2025-06-26T16:26:35.959Z" }, + { url = "https://files.pythonhosted.org/packages/29/c8/262c1d19339ef644cdc9eb5aad2e85bd2d1fa2d7c71cdef3ede1a3eed84d/lxml-6.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c6acde83f7a3d6399e6d83c1892a06ac9b14ea48332a5fbd55d60b9897b9570a", size = 8422719, upload-time = "2025-08-22T10:32:24.848Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d4/1b0afbeb801468a310642c3a6f6704e53c38a4a6eb1ca6faea013333e02f/lxml-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0d21c9cacb6a889cbb8eeb46c77ef2c1dd529cde10443fdeb1de847b3193c541", size = 4575763, upload-time = "2025-08-22T10:32:27.057Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c1/8db9b5402bf52ceb758618313f7423cd54aea85679fcf607013707d854a8/lxml-6.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:847458b7cd0d04004895f1fb2cca8e7c0f8ec923c49c06b7a72ec2d48ea6aca2", size = 4943244, upload-time = "2025-08-22T10:32:28.847Z" }, + { url = "https://files.pythonhosted.org/packages/e7/78/838e115358dd2369c1c5186080dd874a50a691fb5cd80db6afe5e816e2c6/lxml-6.0.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1dc13405bf315d008fe02b1472d2a9d65ee1c73c0a06de5f5a45e6e404d9a1c0", size = 5081725, upload-time = "2025-08-22T10:32:30.666Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b6/bdcb3a3ddd2438c5b1a1915161f34e8c85c96dc574b0ef3be3924f36315c/lxml-6.0.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f540c229a8c0a770dcaf6d5af56a5295e0fc314fc7ef4399d543328054bcea", size = 5021238, upload-time = "2025-08-22T10:32:32.49Z" }, + { url = "https://files.pythonhosted.org/packages/73/e5/1bfb96185dc1a64c7c6fbb7369192bda4461952daa2025207715f9968205/lxml-6.0.1-cp311-cp311-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:d2f73aef768c70e8deb8c4742fca4fd729b132fda68458518851c7735b55297e", size = 5343744, upload-time = "2025-08-22T10:32:34.385Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ae/df3ea9ebc3c493b9c6bdc6bd8c554ac4e147f8d7839993388aab57ec606d/lxml-6.0.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7f4066b85a4fa25ad31b75444bd578c3ebe6b8ed47237896341308e2ce923c3", size = 5223477, upload-time = "2025-08-22T10:32:36.256Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/65e1e33600542c08bc03a4c5c9c306c34696b0966a424a3be6ffec8038ed/lxml-6.0.1-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0cce65db0cd8c750a378639900d56f89f7d6af11cd5eda72fde054d27c54b8ce", size = 4676626, upload-time = "2025-08-22T10:32:38.793Z" }, + { url = "https://files.pythonhosted.org/packages/7a/46/ee3ed8f3a60e9457d7aea46542d419917d81dbfd5700fe64b2a36fb5ef61/lxml-6.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c372d42f3eee5844b69dcab7b8d18b2f449efd54b46ac76970d6e06b8e8d9a66", size = 5066042, upload-time = "2025-08-22T10:32:41.134Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b9/8394538e7cdbeb3bfa36bc74924be1a4383e0bb5af75f32713c2c4aa0479/lxml-6.0.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2e2b0e042e1408bbb1c5f3cfcb0f571ff4ac98d8e73f4bf37c5dd179276beedd", size = 4724714, upload-time = "2025-08-22T10:32:43.94Z" }, + { url = "https://files.pythonhosted.org/packages/b3/21/3ef7da1ea2a73976c1a5a311d7cde5d379234eec0968ee609517714940b4/lxml-6.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cc73bb8640eadd66d25c5a03175de6801f63c535f0f3cf50cac2f06a8211f420", size = 5247376, upload-time = "2025-08-22T10:32:46.263Z" }, + { url = "https://files.pythonhosted.org/packages/26/7d/0980016f124f00c572cba6f4243e13a8e80650843c66271ee692cddf25f3/lxml-6.0.1-cp311-cp311-win32.whl", hash = "sha256:7c23fd8c839708d368e406282d7953cee5134f4592ef4900026d84566d2b4c88", size = 3609499, upload-time = "2025-08-22T10:32:48.156Z" }, + { url = "https://files.pythonhosted.org/packages/b1/08/28440437521f265eff4413eb2a65efac269c4c7db5fd8449b586e75d8de2/lxml-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:2516acc6947ecd3c41a4a4564242a87c6786376989307284ddb115f6a99d927f", size = 4036003, upload-time = "2025-08-22T10:32:50.662Z" }, + { url = "https://files.pythonhosted.org/packages/7b/dc/617e67296d98099213a505d781f04804e7b12923ecd15a781a4ab9181992/lxml-6.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:cb46f8cfa1b0334b074f40c0ff94ce4d9a6755d492e6c116adb5f4a57fb6ad96", size = 3679662, upload-time = "2025-08-22T10:32:52.739Z" }, + { url = "https://files.pythonhosted.org/packages/b0/a9/82b244c8198fcdf709532e39a1751943a36b3e800b420adc739d751e0299/lxml-6.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c03ac546adaabbe0b8e4a15d9ad815a281afc8d36249c246aecf1aaad7d6f200", size = 8422788, upload-time = "2025-08-22T10:32:56.612Z" }, + { url = "https://files.pythonhosted.org/packages/c9/8d/1ed2bc20281b0e7ed3e6c12b0a16e64ae2065d99be075be119ba88486e6d/lxml-6.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33b862c7e3bbeb4ba2c96f3a039f925c640eeba9087a4dc7a572ec0f19d89392", size = 4593547, upload-time = "2025-08-22T10:32:59.016Z" }, + { url = "https://files.pythonhosted.org/packages/76/53/d7fd3af95b72a3493bf7fbe842a01e339d8f41567805cecfecd5c71aa5ee/lxml-6.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7a3ec1373f7d3f519de595032d4dcafae396c29407cfd5073f42d267ba32440d", size = 4948101, upload-time = "2025-08-22T10:33:00.765Z" }, + { url = "https://files.pythonhosted.org/packages/9d/51/4e57cba4d55273c400fb63aefa2f0d08d15eac021432571a7eeefee67bed/lxml-6.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03b12214fb1608f4cffa181ec3d046c72f7e77c345d06222144744c122ded870", size = 5108090, upload-time = "2025-08-22T10:33:03.108Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6e/5f290bc26fcc642bc32942e903e833472271614e24d64ad28aaec09d5dae/lxml-6.0.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:207ae0d5f0f03b30f95e649a6fa22aa73f5825667fee9c7ec6854d30e19f2ed8", size = 5021791, upload-time = "2025-08-22T10:33:06.972Z" }, + { url = "https://files.pythonhosted.org/packages/13/d4/2e7551a86992ece4f9a0f6eebd4fb7e312d30f1e372760e2109e721d4ce6/lxml-6.0.1-cp312-cp312-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:32297b09ed4b17f7b3f448de87a92fb31bb8747496623483788e9f27c98c0f00", size = 5358861, upload-time = "2025-08-22T10:33:08.967Z" }, + { url = "https://files.pythonhosted.org/packages/8a/5f/cb49d727fc388bf5fd37247209bab0da11697ddc5e976ccac4826599939e/lxml-6.0.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e18224ea241b657a157c85e9cac82c2b113ec90876e01e1f127312006233756", size = 5652569, upload-time = "2025-08-22T10:33:10.815Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b8/66c1ef8c87ad0f958b0a23998851e610607c74849e75e83955d5641272e6/lxml-6.0.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a07a994d3c46cd4020c1ea566345cf6815af205b1e948213a4f0f1d392182072", size = 5252262, upload-time = "2025-08-22T10:33:12.673Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ef/131d3d6b9590e64fdbb932fbc576b81fcc686289da19c7cb796257310e82/lxml-6.0.1-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:2287fadaa12418a813b05095485c286c47ea58155930cfbd98c590d25770e225", size = 4710309, upload-time = "2025-08-22T10:33:14.952Z" }, + { url = "https://files.pythonhosted.org/packages/bc/3f/07f48ae422dce44902309aa7ed386c35310929dc592439c403ec16ef9137/lxml-6.0.1-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b4e597efca032ed99f418bd21314745522ab9fa95af33370dcee5533f7f70136", size = 5265786, upload-time = "2025-08-22T10:33:16.721Z" }, + { url = "https://files.pythonhosted.org/packages/11/c7/125315d7b14ab20d9155e8316f7d287a4956098f787c22d47560b74886c4/lxml-6.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9696d491f156226decdd95d9651c6786d43701e49f32bf23715c975539aa2b3b", size = 5062272, upload-time = "2025-08-22T10:33:18.478Z" }, + { url = "https://files.pythonhosted.org/packages/8b/c3/51143c3a5fc5168a7c3ee626418468ff20d30f5a59597e7b156c1e61fba8/lxml-6.0.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e4e3cd3585f3c6f87cdea44cda68e692cc42a012f0131d25957ba4ce755241a7", size = 4786955, upload-time = "2025-08-22T10:33:20.34Z" }, + { url = "https://files.pythonhosted.org/packages/11/86/73102370a420ec4529647b31c4a8ce8c740c77af3a5fae7a7643212d6f6e/lxml-6.0.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:45cbc92f9d22c28cd3b97f8d07fcefa42e569fbd587dfdac76852b16a4924277", size = 5673557, upload-time = "2025-08-22T10:33:22.282Z" }, + { url = "https://files.pythonhosted.org/packages/d7/2d/aad90afaec51029aef26ef773b8fd74a9e8706e5e2f46a57acd11a421c02/lxml-6.0.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:f8c9bcfd2e12299a442fba94459adf0b0d001dbc68f1594439bfa10ad1ecb74b", size = 5254211, upload-time = "2025-08-22T10:33:24.15Z" }, + { url = "https://files.pythonhosted.org/packages/63/01/c9e42c8c2d8b41f4bdefa42ab05448852e439045f112903dd901b8fbea4d/lxml-6.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1e9dc2b9f1586e7cd77753eae81f8d76220eed9b768f337dc83a3f675f2f0cf9", size = 5275817, upload-time = "2025-08-22T10:33:26.007Z" }, + { url = "https://files.pythonhosted.org/packages/bc/1f/962ea2696759abe331c3b0e838bb17e92224f39c638c2068bf0d8345e913/lxml-6.0.1-cp312-cp312-win32.whl", hash = "sha256:987ad5c3941c64031f59c226167f55a04d1272e76b241bfafc968bdb778e07fb", size = 3610889, upload-time = "2025-08-22T10:33:28.169Z" }, + { url = "https://files.pythonhosted.org/packages/41/e2/22c86a990b51b44442b75c43ecb2f77b8daba8c4ba63696921966eac7022/lxml-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:abb05a45394fd76bf4a60c1b7bec0e6d4e8dfc569fc0e0b1f634cd983a006ddc", size = 4010925, upload-time = "2025-08-22T10:33:29.874Z" }, + { url = "https://files.pythonhosted.org/packages/b2/21/dc0c73325e5eb94ef9c9d60dbb5dcdcb2e7114901ea9509735614a74e75a/lxml-6.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:c4be29bce35020d8579d60aa0a4e95effd66fcfce31c46ffddf7e5422f73a299", size = 3671922, upload-time = "2025-08-22T10:33:31.535Z" }, + { url = "https://files.pythonhosted.org/packages/41/37/41961f53f83ded57b37e65e4f47d1c6c6ef5fd02cb1d6ffe028ba0efa7d4/lxml-6.0.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b556aaa6ef393e989dac694b9c95761e32e058d5c4c11ddeef33f790518f7a5e", size = 3903412, upload-time = "2025-08-22T10:37:40.758Z" }, + { url = "https://files.pythonhosted.org/packages/3d/47/8631ea73f3dc776fb6517ccde4d5bd5072f35f9eacbba8c657caa4037a69/lxml-6.0.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:64fac7a05ebb3737b79fd89fe5a5b6c5546aac35cfcfd9208eb6e5d13215771c", size = 4224810, upload-time = "2025-08-22T10:37:42.839Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b8/39ae30ca3b1516729faeef941ed84bf8f12321625f2644492ed8320cb254/lxml-6.0.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:038d3c08babcfce9dc89aaf498e6da205efad5b7106c3b11830a488d4eadf56b", size = 4329221, upload-time = "2025-08-22T10:37:45.223Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ea/048dea6cdfc7a72d40ae8ed7e7d23cf4a6b6a6547b51b492a3be50af0e80/lxml-6.0.1-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:445f2cee71c404ab4259bc21e20339a859f75383ba2d7fb97dfe7c163994287b", size = 4270228, upload-time = "2025-08-22T10:37:47.276Z" }, + { url = "https://files.pythonhosted.org/packages/6b/d4/c2b46e432377c45d611ae2f669aa47971df1586c1a5240675801d0f02bac/lxml-6.0.1-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e352d8578e83822d70bea88f3d08b9912528e4c338f04ab707207ab12f4b7aac", size = 4416077, upload-time = "2025-08-22T10:37:49.822Z" }, + { url = "https://files.pythonhosted.org/packages/b6/db/8f620f1ac62cf32554821b00b768dd5957ac8e3fd051593532be5b40b438/lxml-6.0.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:51bd5d1a9796ca253db6045ab45ca882c09c071deafffc22e06975b7ace36300", size = 3518127, upload-time = "2025-08-22T10:37:51.66Z" }, ] [[package]] @@ -4310,7 +4318,7 @@ wheels = [ [[package]] name = "pytest-xdist" -version = "3.7.1.dev24+g2b4372b" +version = "3.7.1.dev24+g2b4372bd6" source = { git = "https://github.com/sshane/pytest-xdist?rev=2b4372bd62699fb412c4fe2f95bf9f01bd2018da#2b4372bd62699fb412c4fe2f95bf9f01bd2018da" } dependencies = [ { name = "execnet" }, @@ -4452,38 +4460,38 @@ wheels = [ [[package]] name = "pyzmq" -version = "27.0.1" +version = "27.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/30/5f/557d2032a2f471edbcc227da724c24a1c05887b5cda1e3ae53af98b9e0a5/pyzmq-27.0.1.tar.gz", hash = "sha256:45c549204bc20e7484ffd2555f6cf02e572440ecf2f3bdd60d4404b20fddf64b", size = 281158, upload-time = "2025-08-03T05:05:40.352Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/66/159f38d184f08b5f971b467f87b1ab142ab1320d5200825c824b32b84b66/pyzmq-27.0.2.tar.gz", hash = "sha256:b398dd713b18de89730447347e96a0240225e154db56e35b6bb8447ffdb07798", size = 281440, upload-time = "2025-08-21T04:23:26.334Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/18/a8e0da6ababbe9326116fb1c890bf1920eea880e8da621afb6bc0f39a262/pyzmq-27.0.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:9729190bd770314f5fbba42476abf6abe79a746eeda11d1d68fd56dd70e5c296", size = 1332721, upload-time = "2025-08-03T05:03:15.237Z" }, - { url = "https://files.pythonhosted.org/packages/75/a4/9431ba598651d60ebd50dc25755402b770322cf8432adcc07d2906e53a54/pyzmq-27.0.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:696900ef6bc20bef6a242973943574f96c3f97d2183c1bd3da5eea4f559631b1", size = 908249, upload-time = "2025-08-03T05:03:16.933Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/e624e1793689e4e685d2ee21c40277dd4024d9d730af20446d88f69be838/pyzmq-27.0.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f96a63aecec22d3f7fdea3c6c98df9e42973f5856bb6812c3d8d78c262fee808", size = 668649, upload-time = "2025-08-03T05:03:18.49Z" }, - { url = "https://files.pythonhosted.org/packages/6c/29/0652a39d4e876e0d61379047ecf7752685414ad2e253434348246f7a2a39/pyzmq-27.0.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c512824360ea7490390566ce00bee880e19b526b312b25cc0bc30a0fe95cb67f", size = 856601, upload-time = "2025-08-03T05:03:20.194Z" }, - { url = "https://files.pythonhosted.org/packages/36/2d/8d5355d7fc55bb6e9c581dd74f58b64fa78c994079e3a0ea09b1b5627cde/pyzmq-27.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dfb2bb5e0f7198eaacfb6796fb0330afd28f36d985a770745fba554a5903595a", size = 1657750, upload-time = "2025-08-03T05:03:22.055Z" }, - { url = "https://files.pythonhosted.org/packages/ab/f4/cd032352d5d252dc6f5ee272a34b59718ba3af1639a8a4ef4654f9535cf5/pyzmq-27.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4f6886c59ba93ffde09b957d3e857e7950c8fe818bd5494d9b4287bc6d5bc7f1", size = 2034312, upload-time = "2025-08-03T05:03:23.578Z" }, - { url = "https://files.pythonhosted.org/packages/e4/1a/c050d8b6597200e97a4bd29b93c769d002fa0b03083858227e0376ad59bc/pyzmq-27.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b99ea9d330e86ce1ff7f2456b33f1bf81c43862a5590faf4ef4ed3a63504bdab", size = 1893632, upload-time = "2025-08-03T05:03:25.167Z" }, - { url = "https://files.pythonhosted.org/packages/6a/29/173ce21d5097e7fcf284a090e8beb64fc683c6582b1f00fa52b1b7e867ce/pyzmq-27.0.1-cp311-cp311-win32.whl", hash = "sha256:571f762aed89025ba8cdcbe355fea56889715ec06d0264fd8b6a3f3fa38154ed", size = 566587, upload-time = "2025-08-03T05:03:26.769Z" }, - { url = "https://files.pythonhosted.org/packages/53/ab/22bd33e7086f0a2cc03a5adabff4bde414288bb62a21a7820951ef86ec20/pyzmq-27.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee16906c8025fa464bea1e48128c048d02359fb40bebe5333103228528506530", size = 632873, upload-time = "2025-08-03T05:03:28.685Z" }, - { url = "https://files.pythonhosted.org/packages/90/14/3e59b4a28194285ceeff725eba9aa5ba8568d1cb78aed381dec1537c705a/pyzmq-27.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:ba068f28028849da725ff9185c24f832ccf9207a40f9b28ac46ab7c04994bd41", size = 558918, upload-time = "2025-08-03T05:03:30.085Z" }, - { url = "https://files.pythonhosted.org/packages/0e/9b/c0957041067c7724b310f22c398be46399297c12ed834c3bc42200a2756f/pyzmq-27.0.1-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:af7ebce2a1e7caf30c0bb64a845f63a69e76a2fadbc1cac47178f7bb6e657bdd", size = 1305432, upload-time = "2025-08-03T05:03:32.177Z" }, - { url = "https://files.pythonhosted.org/packages/8e/55/bd3a312790858f16b7def3897a0c3eb1804e974711bf7b9dcb5f47e7f82c/pyzmq-27.0.1-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:8f617f60a8b609a13099b313e7e525e67f84ef4524b6acad396d9ff153f6e4cd", size = 895095, upload-time = "2025-08-03T05:03:33.918Z" }, - { url = "https://files.pythonhosted.org/packages/20/50/fc384631d8282809fb1029a4460d2fe90fa0370a0e866a8318ed75c8d3bb/pyzmq-27.0.1-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d59dad4173dc2a111f03e59315c7bd6e73da1a9d20a84a25cf08325b0582b1a", size = 651826, upload-time = "2025-08-03T05:03:35.818Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0a/2356305c423a975000867de56888b79e44ec2192c690ff93c3109fd78081/pyzmq-27.0.1-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5b6133c8d313bde8bd0d123c169d22525300ff164c2189f849de495e1344577", size = 839751, upload-time = "2025-08-03T05:03:37.265Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1b/81e95ad256ca7e7ccd47f5294c1c6da6e2b64fbace65b84fe8a41470342e/pyzmq-27.0.1-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:58cca552567423f04d06a075f4b473e78ab5bdb906febe56bf4797633f54aa4e", size = 1641359, upload-time = "2025-08-03T05:03:38.799Z" }, - { url = "https://files.pythonhosted.org/packages/50/63/9f50ec965285f4e92c265c8f18344e46b12803666d8b73b65d254d441435/pyzmq-27.0.1-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:4b9d8e26fb600d0d69cc9933e20af08552e97cc868a183d38a5c0d661e40dfbb", size = 2020281, upload-time = "2025-08-03T05:03:40.338Z" }, - { url = "https://files.pythonhosted.org/packages/02/4a/19e3398d0dc66ad2b463e4afa1fc541d697d7bc090305f9dfb948d3dfa29/pyzmq-27.0.1-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2329f0c87f0466dce45bba32b63f47018dda5ca40a0085cc5c8558fea7d9fc55", size = 1877112, upload-time = "2025-08-03T05:03:42.012Z" }, - { url = "https://files.pythonhosted.org/packages/bf/42/c562e9151aa90ed1d70aac381ea22a929d6b3a2ce4e1d6e2e135d34fd9c6/pyzmq-27.0.1-cp312-abi3-win32.whl", hash = "sha256:57bb92abdb48467b89c2d21da1ab01a07d0745e536d62afd2e30d5acbd0092eb", size = 558177, upload-time = "2025-08-03T05:03:43.979Z" }, - { url = "https://files.pythonhosted.org/packages/40/96/5c50a7d2d2b05b19994bf7336b97db254299353dd9b49b565bb71b485f03/pyzmq-27.0.1-cp312-abi3-win_amd64.whl", hash = "sha256:ff3f8757570e45da7a5bedaa140489846510014f7a9d5ee9301c61f3f1b8a686", size = 618923, upload-time = "2025-08-03T05:03:45.438Z" }, - { url = "https://files.pythonhosted.org/packages/13/33/1ec89c8f21c89d21a2eaff7def3676e21d8248d2675705e72554fb5a6f3f/pyzmq-27.0.1-cp312-abi3-win_arm64.whl", hash = "sha256:df2c55c958d3766bdb3e9d858b911288acec09a9aab15883f384fc7180df5bed", size = 552358, upload-time = "2025-08-03T05:03:46.887Z" }, - { url = "https://files.pythonhosted.org/packages/b4/1a/49f66fe0bc2b2568dd4280f1f520ac8fafd73f8d762140e278d48aeaf7b9/pyzmq-27.0.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7fb0ee35845bef1e8c4a152d766242164e138c239e3182f558ae15cb4a891f94", size = 835949, upload-time = "2025-08-03T05:05:13.798Z" }, - { url = "https://files.pythonhosted.org/packages/49/94/443c1984b397eab59b14dd7ae8bc2ac7e8f32dbc646474453afcaa6508c4/pyzmq-27.0.1-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f379f11e138dfd56c3f24a04164f871a08281194dd9ddf656a278d7d080c8ad0", size = 799875, upload-time = "2025-08-03T05:05:15.632Z" }, - { url = "https://files.pythonhosted.org/packages/30/f1/fd96138a0f152786a2ba517e9c6a8b1b3516719e412a90bb5d8eea6b660c/pyzmq-27.0.1-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b978c0678cffbe8860ec9edc91200e895c29ae1ac8a7085f947f8e8864c489fb", size = 567403, upload-time = "2025-08-03T05:05:17.326Z" }, - { url = "https://files.pythonhosted.org/packages/16/57/34e53ef2b55b1428dac5aabe3a974a16c8bda3bf20549ba500e3ff6cb426/pyzmq-27.0.1-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ebccf0d760bc92a4a7c751aeb2fef6626144aace76ee8f5a63abeb100cae87f", size = 747032, upload-time = "2025-08-03T05:05:19.074Z" }, - { url = "https://files.pythonhosted.org/packages/81/b7/769598c5ae336fdb657946950465569cf18803140fe89ce466d7f0a57c11/pyzmq-27.0.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:77fed80e30fa65708546c4119840a46691290efc231f6bfb2ac2a39b52e15811", size = 544566, upload-time = "2025-08-03T05:05:20.798Z" }, + { url = "https://files.pythonhosted.org/packages/42/73/034429ab0f4316bf433eb6c20c3f49d1dc13b2ed4e4d951b283d300a0f35/pyzmq-27.0.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:063845960df76599ad4fad69fa4d884b3ba38304272104fdcd7e3af33faeeb1d", size = 1333169, upload-time = "2025-08-21T04:21:12.483Z" }, + { url = "https://files.pythonhosted.org/packages/35/02/c42b3b526eb03a570c889eea85a5602797f800a50ba8b09ddbf7db568b78/pyzmq-27.0.2-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:845a35fb21b88786aeb38af8b271d41ab0967985410f35411a27eebdc578a076", size = 909176, upload-time = "2025-08-21T04:21:13.835Z" }, + { url = "https://files.pythonhosted.org/packages/1b/35/a1c0b988fabbdf2dc5fe94b7c2bcfd61e3533e5109297b8e0daf1d7a8d2d/pyzmq-27.0.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:515d20b5c3c86db95503faa989853a8ab692aab1e5336db011cd6d35626c4cb1", size = 668972, upload-time = "2025-08-21T04:21:15.315Z" }, + { url = "https://files.pythonhosted.org/packages/a0/63/908ac865da32ceaeecea72adceadad28ca25b23a2ca5ff018e5bff30116f/pyzmq-27.0.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:862aedec0b0684a5050cdb5ec13c2da96d2f8dffda48657ed35e312a4e31553b", size = 856962, upload-time = "2025-08-21T04:21:16.652Z" }, + { url = "https://files.pythonhosted.org/packages/2f/5a/90b3cc20b65cdf9391896fcfc15d8db21182eab810b7ea05a2986912fbe2/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cb5bcfc51c7a4fce335d3bc974fd1d6a916abbcdd2b25f6e89d37b8def25f57", size = 1657712, upload-time = "2025-08-21T04:21:18.666Z" }, + { url = "https://files.pythonhosted.org/packages/c4/3c/32a5a80f9be4759325b8d7b22ce674bb87e586b4c80c6a9d77598b60d6f0/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:38ff75b2a36e3a032e9fef29a5871e3e1301a37464e09ba364e3c3193f62982a", size = 2035054, upload-time = "2025-08-21T04:21:20.073Z" }, + { url = "https://files.pythonhosted.org/packages/13/61/71084fe2ff2d7dc5713f8740d735336e87544845dae1207a8e2e16d9af90/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a5709abe8d23ca158a9d0a18c037f4193f5b6afeb53be37173a41e9fb885792", size = 1894010, upload-time = "2025-08-21T04:21:21.96Z" }, + { url = "https://files.pythonhosted.org/packages/cb/6b/77169cfb13b696e50112ca496b2ed23c4b7d8860a1ec0ff3e4b9f9926221/pyzmq-27.0.2-cp311-cp311-win32.whl", hash = "sha256:47c5dda2018c35d87be9b83de0890cb92ac0791fd59498847fc4eca6ff56671d", size = 566819, upload-time = "2025-08-21T04:21:23.31Z" }, + { url = "https://files.pythonhosted.org/packages/37/cd/86c4083e0f811f48f11bc0ddf1e7d13ef37adfd2fd4f78f2445f1cc5dec0/pyzmq-27.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:f54ca3e98f8f4d23e989c7d0edcf9da7a514ff261edaf64d1d8653dd5feb0a8b", size = 633264, upload-time = "2025-08-21T04:21:24.761Z" }, + { url = "https://files.pythonhosted.org/packages/a0/69/5b8bb6a19a36a569fac02153a9e083738785892636270f5f68a915956aea/pyzmq-27.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:2ef3067cb5b51b090fb853f423ad7ed63836ec154374282780a62eb866bf5768", size = 559316, upload-time = "2025-08-21T04:21:26.1Z" }, + { url = "https://files.pythonhosted.org/packages/68/69/b3a729e7b03e412bee2b1823ab8d22e20a92593634f664afd04c6c9d9ac0/pyzmq-27.0.2-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:5da05e3c22c95e23bfc4afeee6ff7d4be9ff2233ad6cb171a0e8257cd46b169a", size = 1305910, upload-time = "2025-08-21T04:21:27.609Z" }, + { url = "https://files.pythonhosted.org/packages/15/b7/f6a6a285193d489b223c340b38ee03a673467cb54914da21c3d7849f1b10/pyzmq-27.0.2-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4e4520577971d01d47e2559bb3175fce1be9103b18621bf0b241abe0a933d040", size = 895507, upload-time = "2025-08-21T04:21:29.005Z" }, + { url = "https://files.pythonhosted.org/packages/17/e6/c4ed2da5ef9182cde1b1f5d0051a986e76339d71720ec1a00be0b49275ad/pyzmq-27.0.2-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d7de7bf73165b90bd25a8668659ccb134dd28449116bf3c7e9bab5cf8a8ec9", size = 652670, upload-time = "2025-08-21T04:21:30.71Z" }, + { url = "https://files.pythonhosted.org/packages/0e/66/d781ab0636570d32c745c4e389b1c6b713115905cca69ab6233508622edd/pyzmq-27.0.2-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:340e7cddc32f147c6c00d116a3f284ab07ee63dbd26c52be13b590520434533c", size = 840581, upload-time = "2025-08-21T04:21:32.008Z" }, + { url = "https://files.pythonhosted.org/packages/a6/df/f24790caf565d72544f5c8d8500960b9562c1dc848d6f22f3c7e122e73d4/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba95693f9df8bb4a9826464fb0fe89033936f35fd4a8ff1edff09a473570afa0", size = 1641931, upload-time = "2025-08-21T04:21:33.371Z" }, + { url = "https://files.pythonhosted.org/packages/65/65/77d27b19fc5e845367f9100db90b9fce924f611b14770db480615944c9c9/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:ca42a6ce2d697537da34f77a1960d21476c6a4af3e539eddb2b114c3cf65a78c", size = 2021226, upload-time = "2025-08-21T04:21:35.301Z" }, + { url = "https://files.pythonhosted.org/packages/5b/65/1ed14421ba27a4207fa694772003a311d1142b7f543179e4d1099b7eb746/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3e44e665d78a07214b2772ccbd4b9bcc6d848d7895f1b2d7653f047b6318a4f6", size = 1878047, upload-time = "2025-08-21T04:21:36.749Z" }, + { url = "https://files.pythonhosted.org/packages/dd/dc/e578549b89b40dc78a387ec471c2a360766690c0a045cd8d1877d401012d/pyzmq-27.0.2-cp312-abi3-win32.whl", hash = "sha256:272d772d116615397d2be2b1417b3b8c8bc8671f93728c2f2c25002a4530e8f6", size = 558757, upload-time = "2025-08-21T04:21:38.2Z" }, + { url = "https://files.pythonhosted.org/packages/b5/89/06600980aefcc535c758414da969f37a5194ea4cdb73b745223f6af3acfb/pyzmq-27.0.2-cp312-abi3-win_amd64.whl", hash = "sha256:734be4f44efba0aa69bf5f015ed13eb69ff29bf0d17ea1e21588b095a3147b8e", size = 619281, upload-time = "2025-08-21T04:21:39.909Z" }, + { url = "https://files.pythonhosted.org/packages/30/84/df8a5c089552d17c9941d1aea4314b606edf1b1622361dae89aacedc6467/pyzmq-27.0.2-cp312-abi3-win_arm64.whl", hash = "sha256:41f0bd56d9279392810950feb2785a419c2920bbf007fdaaa7f4a07332ae492d", size = 552680, upload-time = "2025-08-21T04:21:41.571Z" }, + { url = "https://files.pythonhosted.org/packages/c7/60/027d0032a1e3b1aabcef0e309b9ff8a4099bdd5a60ab38b36a676ff2bd7b/pyzmq-27.0.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e297784aea724294fe95e442e39a4376c2f08aa4fae4161c669f047051e31b02", size = 836007, upload-time = "2025-08-21T04:23:00.447Z" }, + { url = "https://files.pythonhosted.org/packages/25/20/2ed1e6168aaea323df9bb2c451309291f53ba3af372ffc16edd4ce15b9e5/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e3659a79ded9745bc9c2aef5b444ac8805606e7bc50d2d2eb16dc3ab5483d91f", size = 799932, upload-time = "2025-08-21T04:23:02.052Z" }, + { url = "https://files.pythonhosted.org/packages/fd/25/5c147307de546b502c9373688ce5b25dc22288d23a1ebebe5d587bf77610/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3dba49ff037d02373a9306b58d6c1e0be031438f822044e8767afccfdac4c6b", size = 567459, upload-time = "2025-08-21T04:23:03.593Z" }, + { url = "https://files.pythonhosted.org/packages/71/06/0dc56ffc615c8095cd089c9b98ce5c733e990f09ce4e8eea4aaf1041a532/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de84e1694f9507b29e7b263453a2255a73e3d099d258db0f14539bad258abe41", size = 747088, upload-time = "2025-08-21T04:23:05.334Z" }, + { url = "https://files.pythonhosted.org/packages/06/f6/4a50187e023b8848edd3f0a8e197b1a7fb08d261d8c60aae7cb6c3d71612/pyzmq-27.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f0944d65ba2b872b9fcece08411d6347f15a874c775b4c3baae7f278550da0fb", size = 544639, upload-time = "2025-08-21T04:23:07.279Z" }, ] [[package]] @@ -4524,7 +4532,7 @@ wheels = [ [[package]] name = "requests" -version = "2.32.4" +version = "2.32.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -4532,21 +4540,21 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +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" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, + { 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" }, ] [[package]] name = "ruamel-yaml" -version = "0.18.14" +version = "0.18.15" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ruamel-yaml-clib", marker = "platform_python_implementation == 'CPython'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/39/87/6da0df742a4684263261c253f00edd5829e6aca970fff69e75028cccc547/ruamel.yaml-0.18.14.tar.gz", hash = "sha256:7227b76aaec364df15936730efbf7d72b30c0b79b1d578bbb8e3dcb2d81f52b7", size = 145511, upload-time = "2025-06-09T08:51:09.828Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/db/f3950f5e5031b618aae9f423a39bf81a55c148aecd15a34527898e752cf4/ruamel.yaml-0.18.15.tar.gz", hash = "sha256:dbfca74b018c4c3fba0b9cc9ee33e53c371194a9000e694995e620490fd40700", size = 146865, upload-time = "2025-08-19T11:15:10.694Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/6d/6fe4805235e193aad4aaf979160dd1f3c487c57d48b810c816e6e842171b/ruamel.yaml-0.18.14-py3-none-any.whl", hash = "sha256:710ff198bb53da66718c7db27eec4fbcc9aa6ca7204e4c1df2f282b6fe5eb6b2", size = 118570, upload-time = "2025-06-09T08:51:06.348Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e5/f2a0621f1781b76a38194acae72f01e37b1941470407345b6e8653ad7640/ruamel.yaml-0.18.15-py3-none-any.whl", hash = "sha256:148f6488d698b7a5eded5ea793a025308b25eca97208181b6a026037f391f701", size = 119702, upload-time = "2025-08-19T11:15:07.696Z" }, ] [[package]] @@ -4586,28 +4594,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.12.9" +version = "0.12.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4a/45/2e403fa7007816b5fbb324cb4f8ed3c7402a927a0a0cb2b6279879a8bfdc/ruff-0.12.9.tar.gz", hash = "sha256:fbd94b2e3c623f659962934e52c2bea6fc6da11f667a427a368adaf3af2c866a", size = 5254702, upload-time = "2025-08-14T16:08:55.2Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/eb/8c073deb376e46ae767f4961390d17545e8535921d2f65101720ed8bd434/ruff-0.12.10.tar.gz", hash = "sha256:189ab65149d11ea69a2d775343adf5f49bb2426fc4780f65ee33b423ad2e47f9", size = 5310076, upload-time = "2025-08-21T18:23:22.595Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/20/53bf098537adb7b6a97d98fcdebf6e916fcd11b2e21d15f8c171507909cc/ruff-0.12.9-py3-none-linux_armv6l.whl", hash = "sha256:fcebc6c79fcae3f220d05585229463621f5dbf24d79fdc4936d9302e177cfa3e", size = 11759705, upload-time = "2025-08-14T16:08:12.968Z" }, - { url = "https://files.pythonhosted.org/packages/20/4d/c764ee423002aac1ec66b9d541285dd29d2c0640a8086c87de59ebbe80d5/ruff-0.12.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aed9d15f8c5755c0e74467731a007fcad41f19bcce41cd75f768bbd687f8535f", size = 12527042, upload-time = "2025-08-14T16:08:16.54Z" }, - { url = "https://files.pythonhosted.org/packages/8b/45/cfcdf6d3eb5fc78a5b419e7e616d6ccba0013dc5b180522920af2897e1be/ruff-0.12.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5b15ea354c6ff0d7423814ba6d44be2807644d0c05e9ed60caca87e963e93f70", size = 11724457, upload-time = "2025-08-14T16:08:18.686Z" }, - { url = "https://files.pythonhosted.org/packages/72/e6/44615c754b55662200c48bebb02196dbb14111b6e266ab071b7e7297b4ec/ruff-0.12.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d596c2d0393c2502eaabfef723bd74ca35348a8dac4267d18a94910087807c53", size = 11949446, upload-time = "2025-08-14T16:08:21.059Z" }, - { url = "https://files.pythonhosted.org/packages/fd/d1/9b7d46625d617c7df520d40d5ac6cdcdf20cbccb88fad4b5ecd476a6bb8d/ruff-0.12.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b15599931a1a7a03c388b9c5df1bfa62be7ede6eb7ef753b272381f39c3d0ff", size = 11566350, upload-time = "2025-08-14T16:08:23.433Z" }, - { url = "https://files.pythonhosted.org/packages/59/20/b73132f66f2856bc29d2d263c6ca457f8476b0bbbe064dac3ac3337a270f/ruff-0.12.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d02faa2977fb6f3f32ddb7828e212b7dd499c59eb896ae6c03ea5c303575756", size = 13270430, upload-time = "2025-08-14T16:08:25.837Z" }, - { url = "https://files.pythonhosted.org/packages/a2/21/eaf3806f0a3d4c6be0a69d435646fba775b65f3f2097d54898b0fd4bb12e/ruff-0.12.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:17d5b6b0b3a25259b69ebcba87908496e6830e03acfb929ef9fd4c58675fa2ea", size = 14264717, upload-time = "2025-08-14T16:08:27.907Z" }, - { url = "https://files.pythonhosted.org/packages/d2/82/1d0c53bd37dcb582b2c521d352fbf4876b1e28bc0d8894344198f6c9950d/ruff-0.12.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:72db7521860e246adbb43f6ef464dd2a532ef2ef1f5dd0d470455b8d9f1773e0", size = 13684331, upload-time = "2025-08-14T16:08:30.352Z" }, - { url = "https://files.pythonhosted.org/packages/3b/2f/1c5cf6d8f656306d42a686f1e207f71d7cebdcbe7b2aa18e4e8a0cb74da3/ruff-0.12.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a03242c1522b4e0885af63320ad754d53983c9599157ee33e77d748363c561ce", size = 12739151, upload-time = "2025-08-14T16:08:32.55Z" }, - { url = "https://files.pythonhosted.org/packages/47/09/25033198bff89b24d734e6479e39b1968e4c992e82262d61cdccaf11afb9/ruff-0.12.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fc83e4e9751e6c13b5046d7162f205d0a7bac5840183c5beebf824b08a27340", size = 12954992, upload-time = "2025-08-14T16:08:34.816Z" }, - { url = "https://files.pythonhosted.org/packages/52/8e/d0dbf2f9dca66c2d7131feefc386523404014968cd6d22f057763935ab32/ruff-0.12.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:881465ed56ba4dd26a691954650de6ad389a2d1fdb130fe51ff18a25639fe4bb", size = 12899569, upload-time = "2025-08-14T16:08:36.852Z" }, - { url = "https://files.pythonhosted.org/packages/a0/bd/b614d7c08515b1428ed4d3f1d4e3d687deffb2479703b90237682586fa66/ruff-0.12.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:43f07a3ccfc62cdb4d3a3348bf0588358a66da756aa113e071b8ca8c3b9826af", size = 11751983, upload-time = "2025-08-14T16:08:39.314Z" }, - { url = "https://files.pythonhosted.org/packages/58/d6/383e9f818a2441b1a0ed898d7875f11273f10882f997388b2b51cb2ae8b5/ruff-0.12.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:07adb221c54b6bba24387911e5734357f042e5669fa5718920ee728aba3cbadc", size = 11538635, upload-time = "2025-08-14T16:08:41.297Z" }, - { url = "https://files.pythonhosted.org/packages/20/9c/56f869d314edaa9fc1f491706d1d8a47747b9d714130368fbd69ce9024e9/ruff-0.12.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f5cd34fabfdea3933ab85d72359f118035882a01bff15bd1d2b15261d85d5f66", size = 12534346, upload-time = "2025-08-14T16:08:43.39Z" }, - { url = "https://files.pythonhosted.org/packages/bd/4b/d8b95c6795a6c93b439bc913ee7a94fda42bb30a79285d47b80074003ee7/ruff-0.12.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f6be1d2ca0686c54564da8e7ee9e25f93bdd6868263805f8c0b8fc6a449db6d7", size = 13017021, upload-time = "2025-08-14T16:08:45.889Z" }, - { url = "https://files.pythonhosted.org/packages/c7/c1/5f9a839a697ce1acd7af44836f7c2181cdae5accd17a5cb85fcbd694075e/ruff-0.12.9-py3-none-win32.whl", hash = "sha256:cc7a37bd2509974379d0115cc5608a1a4a6c4bff1b452ea69db83c8855d53f93", size = 11734785, upload-time = "2025-08-14T16:08:48.062Z" }, - { url = "https://files.pythonhosted.org/packages/fa/66/cdddc2d1d9a9f677520b7cfc490d234336f523d4b429c1298de359a3be08/ruff-0.12.9-py3-none-win_amd64.whl", hash = "sha256:6fb15b1977309741d7d098c8a3cb7a30bc112760a00fb6efb7abc85f00ba5908", size = 12840654, upload-time = "2025-08-14T16:08:50.158Z" }, - { url = "https://files.pythonhosted.org/packages/ac/fd/669816bc6b5b93b9586f3c1d87cd6bc05028470b3ecfebb5938252c47a35/ruff-0.12.9-py3-none-win_arm64.whl", hash = "sha256:63c8c819739d86b96d500cce885956a1a48ab056bbcbc61b747ad494b2485089", size = 11949623, upload-time = "2025-08-14T16:08:52.233Z" }, + { url = "https://files.pythonhosted.org/packages/24/e7/560d049d15585d6c201f9eeacd2fd130def3741323e5ccf123786e0e3c95/ruff-0.12.10-py3-none-linux_armv6l.whl", hash = "sha256:8b593cb0fb55cc8692dac7b06deb29afda78c721c7ccfed22db941201b7b8f7b", size = 11935161, upload-time = "2025-08-21T18:22:26.965Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b0/ad2464922a1113c365d12b8f80ed70fcfb39764288ac77c995156080488d/ruff-0.12.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ebb7333a45d56efc7c110a46a69a1b32365d5c5161e7244aaf3aa20ce62399c1", size = 12660884, upload-time = "2025-08-21T18:22:30.925Z" }, + { url = "https://files.pythonhosted.org/packages/d7/f1/97f509b4108d7bae16c48389f54f005b62ce86712120fd8b2d8e88a7cb49/ruff-0.12.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d59e58586829f8e4a9920788f6efba97a13d1fa320b047814e8afede381c6839", size = 11872754, upload-time = "2025-08-21T18:22:34.035Z" }, + { url = "https://files.pythonhosted.org/packages/12/ad/44f606d243f744a75adc432275217296095101f83f966842063d78eee2d3/ruff-0.12.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:822d9677b560f1fdeab69b89d1f444bf5459da4aa04e06e766cf0121771ab844", size = 12092276, upload-time = "2025-08-21T18:22:36.764Z" }, + { url = "https://files.pythonhosted.org/packages/06/1f/ed6c265e199568010197909b25c896d66e4ef2c5e1c3808caf461f6f3579/ruff-0.12.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b4a64f4062a50c75019c61c7017ff598cb444984b638511f48539d3a1c98db", size = 11734700, upload-time = "2025-08-21T18:22:39.822Z" }, + { url = "https://files.pythonhosted.org/packages/63/c5/b21cde720f54a1d1db71538c0bc9b73dee4b563a7dd7d2e404914904d7f5/ruff-0.12.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6f4064c69d2542029b2a61d39920c85240c39837599d7f2e32e80d36401d6e", size = 13468783, upload-time = "2025-08-21T18:22:42.559Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/39369e6ac7f2a1848f22fb0b00b690492f20811a1ac5c1fd1d2798329263/ruff-0.12.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:059e863ea3a9ade41407ad71c1de2badfbe01539117f38f763ba42a1206f7559", size = 14436642, upload-time = "2025-08-21T18:22:45.612Z" }, + { url = "https://files.pythonhosted.org/packages/e3/03/5da8cad4b0d5242a936eb203b58318016db44f5c5d351b07e3f5e211bb89/ruff-0.12.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bef6161e297c68908b7218fa6e0e93e99a286e5ed9653d4be71e687dff101cf", size = 13859107, upload-time = "2025-08-21T18:22:48.886Z" }, + { url = "https://files.pythonhosted.org/packages/19/19/dd7273b69bf7f93a070c9cec9494a94048325ad18fdcf50114f07e6bf417/ruff-0.12.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4f1345fbf8fb0531cd722285b5f15af49b2932742fc96b633e883da8d841896b", size = 12886521, upload-time = "2025-08-21T18:22:51.567Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1d/b4207ec35e7babaee62c462769e77457e26eb853fbdc877af29417033333/ruff-0.12.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f68433c4fbc63efbfa3ba5db31727db229fa4e61000f452c540474b03de52a9", size = 13097528, upload-time = "2025-08-21T18:22:54.609Z" }, + { url = "https://files.pythonhosted.org/packages/ff/00/58f7b873b21114456e880b75176af3490d7a2836033779ca42f50de3b47a/ruff-0.12.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:141ce3d88803c625257b8a6debf4a0473eb6eed9643a6189b68838b43e78165a", size = 13080443, upload-time = "2025-08-21T18:22:57.413Z" }, + { url = "https://files.pythonhosted.org/packages/12/8c/9e6660007fb10189ccb78a02b41691288038e51e4788bf49b0a60f740604/ruff-0.12.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f3fc21178cd44c98142ae7590f42ddcb587b8e09a3b849cbc84edb62ee95de60", size = 11896759, upload-time = "2025-08-21T18:23:00.473Z" }, + { url = "https://files.pythonhosted.org/packages/67/4c/6d092bb99ea9ea6ebda817a0e7ad886f42a58b4501a7e27cd97371d0ba54/ruff-0.12.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7d1a4e0bdfafcd2e3e235ecf50bf0176f74dd37902f241588ae1f6c827a36c56", size = 11701463, upload-time = "2025-08-21T18:23:03.211Z" }, + { url = "https://files.pythonhosted.org/packages/59/80/d982c55e91df981f3ab62559371380616c57ffd0172d96850280c2b04fa8/ruff-0.12.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e67d96827854f50b9e3e8327b031647e7bcc090dbe7bb11101a81a3a2cbf1cc9", size = 12691603, upload-time = "2025-08-21T18:23:06.935Z" }, + { url = "https://files.pythonhosted.org/packages/ad/37/63a9c788bbe0b0850611669ec6b8589838faf2f4f959647f2d3e320383ae/ruff-0.12.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ae479e1a18b439c59138f066ae79cc0f3ee250712a873d00dbafadaad9481e5b", size = 13164356, upload-time = "2025-08-21T18:23:10.225Z" }, + { url = "https://files.pythonhosted.org/packages/47/d4/1aaa7fb201a74181989970ebccd12f88c0fc074777027e2a21de5a90657e/ruff-0.12.10-py3-none-win32.whl", hash = "sha256:9de785e95dc2f09846c5e6e1d3a3d32ecd0b283a979898ad427a9be7be22b266", size = 11896089, upload-time = "2025-08-21T18:23:14.232Z" }, + { url = "https://files.pythonhosted.org/packages/ad/14/2ad38fd4037daab9e023456a4a40ed0154e9971f8d6aed41bdea390aabd9/ruff-0.12.10-py3-none-win_amd64.whl", hash = "sha256:7837eca8787f076f67aba2ca559cefd9c5cbc3a9852fd66186f4201b87c1563e", size = 13004616, upload-time = "2025-08-21T18:23:17.422Z" }, + { url = "https://files.pythonhosted.org/packages/24/3c/21cf283d67af33a8e6ed242396863af195a8a6134ec581524fd22b9811b6/ruff-0.12.10-py3-none-win_arm64.whl", hash = "sha256:cc138cc06ed9d4bfa9d667a65af7172b47840e1a98b02ce7011c391e54635ffc", size = 12074225, upload-time = "2025-08-21T18:23:20.137Z" }, ] [[package]] @@ -4964,43 +4972,42 @@ wheels = [ [[package]] name = "zstandard" -version = "0.23.0" +version = "0.24.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation == 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701, upload-time = "2024-07-15T00:18:06.141Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/1b/c20b2ef1d987627765dcd5bf1dadb8ef6564f00a87972635099bb76b7a05/zstandard-0.24.0.tar.gz", hash = "sha256:fe3198b81c00032326342d973e526803f183f97aa9e9a98e3f897ebafe21178f", size = 905681, upload-time = "2025-08-17T18:36:36.352Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/40/f67e7d2c25a0e2dc1744dd781110b0b60306657f8696cafb7ad7579469bd/zstandard-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e", size = 788699, upload-time = "2024-07-15T00:14:04.909Z" }, - { url = "https://files.pythonhosted.org/packages/e8/46/66d5b55f4d737dd6ab75851b224abf0afe5774976fe511a54d2eb9063a41/zstandard-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23", size = 633681, upload-time = "2024-07-15T00:14:13.99Z" }, - { url = "https://files.pythonhosted.org/packages/63/b6/677e65c095d8e12b66b8f862b069bcf1f1d781b9c9c6f12eb55000d57583/zstandard-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a", size = 4944328, upload-time = "2024-07-15T00:14:16.588Z" }, - { url = "https://files.pythonhosted.org/packages/59/cc/e76acb4c42afa05a9d20827116d1f9287e9c32b7ad58cc3af0721ce2b481/zstandard-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db", size = 5311955, upload-time = "2024-07-15T00:14:19.389Z" }, - { url = "https://files.pythonhosted.org/packages/78/e4/644b8075f18fc7f632130c32e8f36f6dc1b93065bf2dd87f03223b187f26/zstandard-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2", size = 5344944, upload-time = "2024-07-15T00:14:22.173Z" }, - { url = "https://files.pythonhosted.org/packages/76/3f/dbafccf19cfeca25bbabf6f2dd81796b7218f768ec400f043edc767015a6/zstandard-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca", size = 5442927, upload-time = "2024-07-15T00:14:24.825Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c3/d24a01a19b6733b9f218e94d1a87c477d523237e07f94899e1c10f6fd06c/zstandard-0.23.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c", size = 4864910, upload-time = "2024-07-15T00:14:26.982Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a9/cf8f78ead4597264f7618d0875be01f9bc23c9d1d11afb6d225b867cb423/zstandard-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e", size = 4935544, upload-time = "2024-07-15T00:14:29.582Z" }, - { url = "https://files.pythonhosted.org/packages/2c/96/8af1e3731b67965fb995a940c04a2c20997a7b3b14826b9d1301cf160879/zstandard-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5", size = 5467094, upload-time = "2024-07-15T00:14:40.126Z" }, - { url = "https://files.pythonhosted.org/packages/ff/57/43ea9df642c636cb79f88a13ab07d92d88d3bfe3e550b55a25a07a26d878/zstandard-0.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48", size = 4860440, upload-time = "2024-07-15T00:14:42.786Z" }, - { url = "https://files.pythonhosted.org/packages/46/37/edb78f33c7f44f806525f27baa300341918fd4c4af9472fbc2c3094be2e8/zstandard-0.23.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c", size = 4700091, upload-time = "2024-07-15T00:14:45.184Z" }, - { url = "https://files.pythonhosted.org/packages/c1/f1/454ac3962671a754f3cb49242472df5c2cced4eb959ae203a377b45b1a3c/zstandard-0.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003", size = 5208682, upload-time = "2024-07-15T00:14:47.407Z" }, - { url = "https://files.pythonhosted.org/packages/85/b2/1734b0fff1634390b1b887202d557d2dd542de84a4c155c258cf75da4773/zstandard-0.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78", size = 5669707, upload-time = "2024-07-15T00:15:03.529Z" }, - { url = "https://files.pythonhosted.org/packages/52/5a/87d6971f0997c4b9b09c495bf92189fb63de86a83cadc4977dc19735f652/zstandard-0.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473", size = 5201792, upload-time = "2024-07-15T00:15:28.372Z" }, - { url = "https://files.pythonhosted.org/packages/79/02/6f6a42cc84459d399bd1a4e1adfc78d4dfe45e56d05b072008d10040e13b/zstandard-0.23.0-cp311-cp311-win32.whl", hash = "sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160", size = 430586, upload-time = "2024-07-15T00:15:32.26Z" }, - { url = "https://files.pythonhosted.org/packages/be/a2/4272175d47c623ff78196f3c10e9dc7045c1b9caf3735bf041e65271eca4/zstandard-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0", size = 495420, upload-time = "2024-07-15T00:15:34.004Z" }, - { url = "https://files.pythonhosted.org/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094", size = 788713, upload-time = "2024-07-15T00:15:35.815Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8", size = 633459, upload-time = "2024-07-15T00:15:37.995Z" }, - { url = "https://files.pythonhosted.org/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1", size = 4945707, upload-time = "2024-07-15T00:15:39.872Z" }, - { url = "https://files.pythonhosted.org/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072", size = 5306545, upload-time = "2024-07-15T00:15:41.75Z" }, - { url = "https://files.pythonhosted.org/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20", size = 5337533, upload-time = "2024-07-15T00:15:44.114Z" }, - { url = "https://files.pythonhosted.org/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373", size = 5436510, upload-time = "2024-07-15T00:15:46.509Z" }, - { url = "https://files.pythonhosted.org/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db", size = 4859973, upload-time = "2024-07-15T00:15:49.939Z" }, - { url = "https://files.pythonhosted.org/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772", size = 4936968, upload-time = "2024-07-15T00:15:52.025Z" }, - { url = "https://files.pythonhosted.org/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105", size = 5467179, upload-time = "2024-07-15T00:15:54.971Z" }, - { url = "https://files.pythonhosted.org/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba", size = 4848577, upload-time = "2024-07-15T00:15:57.634Z" }, - { url = "https://files.pythonhosted.org/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd", size = 4693899, upload-time = "2024-07-15T00:16:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a", size = 5199964, upload-time = "2024-07-15T00:16:03.669Z" }, - { url = "https://files.pythonhosted.org/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90", size = 5655398, upload-time = "2024-07-15T00:16:06.694Z" }, - { url = "https://files.pythonhosted.org/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35", size = 5191313, upload-time = "2024-07-15T00:16:09.758Z" }, - { url = "https://files.pythonhosted.org/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d", size = 430877, upload-time = "2024-07-15T00:16:11.758Z" }, - { url = "https://files.pythonhosted.org/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b", size = 495595, upload-time = "2024-07-15T00:16:13.731Z" }, + { url = "https://files.pythonhosted.org/packages/01/1f/5c72806f76043c0ef9191a2b65281dacdf3b65b0828eb13bb2c987c4fb90/zstandard-0.24.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:addfc23e3bd5f4b6787b9ca95b2d09a1a67ad5a3c318daaa783ff90b2d3a366e", size = 795228, upload-time = "2025-08-17T18:21:46.978Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ba/3059bd5cd834666a789251d14417621b5c61233bd46e7d9023ea8bc1043a/zstandard-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b005bcee4be9c3984b355336283afe77b2defa76ed6b89332eced7b6fa68b68", size = 640520, upload-time = "2025-08-17T18:21:48.162Z" }, + { url = "https://files.pythonhosted.org/packages/57/07/f0e632bf783f915c1fdd0bf68614c4764cae9dd46ba32cbae4dd659592c3/zstandard-0.24.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:3f96a9130171e01dbb6c3d4d9925d604e2131a97f540e223b88ba45daf56d6fb", size = 5347682, upload-time = "2025-08-17T18:21:50.266Z" }, + { url = "https://files.pythonhosted.org/packages/a6/4c/63523169fe84773a7462cd090b0989cb7c7a7f2a8b0a5fbf00009ba7d74d/zstandard-0.24.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd0d3d16e63873253bad22b413ec679cf6586e51b5772eb10733899832efec42", size = 5057650, upload-time = "2025-08-17T18:21:52.634Z" }, + { url = "https://files.pythonhosted.org/packages/c6/16/49013f7ef80293f5cebf4c4229535a9f4c9416bbfd238560edc579815dbe/zstandard-0.24.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b7a8c30d9bf4bd5e4dcfe26900bef0fcd9749acde45cdf0b3c89e2052fda9a13", size = 5404893, upload-time = "2025-08-17T18:21:54.54Z" }, + { url = "https://files.pythonhosted.org/packages/4d/38/78e8bcb5fc32a63b055f2b99e0be49b506f2351d0180173674f516cf8a7a/zstandard-0.24.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:52cd7d9fa0a115c9446abb79b06a47171b7d916c35c10e0c3aa6f01d57561382", size = 5452389, upload-time = "2025-08-17T18:21:56.822Z" }, + { url = "https://files.pythonhosted.org/packages/55/8a/81671f05619edbacd49bd84ce6899a09fc8299be20c09ae92f6618ccb92d/zstandard-0.24.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0f6fc2ea6e07e20df48752e7700e02e1892c61f9a6bfbacaf2c5b24d5ad504b", size = 5558888, upload-time = "2025-08-17T18:21:58.68Z" }, + { url = "https://files.pythonhosted.org/packages/49/cc/e83feb2d7d22d1f88434defbaeb6e5e91f42a4f607b5d4d2d58912b69d67/zstandard-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e46eb6702691b24ddb3e31e88b4a499e31506991db3d3724a85bd1c5fc3cfe4e", size = 5048038, upload-time = "2025-08-17T18:22:00.642Z" }, + { url = "https://files.pythonhosted.org/packages/08/c3/7a5c57ff49ef8943877f85c23368c104c2aea510abb339a2dc31ad0a27c3/zstandard-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5e3b9310fd7f0d12edc75532cd9a56da6293840c84da90070d692e0bb15f186", size = 5573833, upload-time = "2025-08-17T18:22:02.402Z" }, + { url = "https://files.pythonhosted.org/packages/f9/00/64519983cd92535ba4bdd4ac26ac52db00040a52d6c4efb8d1764abcc343/zstandard-0.24.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76cdfe7f920738ea871f035568f82bad3328cbc8d98f1f6988264096b5264efd", size = 4961072, upload-time = "2025-08-17T18:22:04.384Z" }, + { url = "https://files.pythonhosted.org/packages/72/ab/3a08a43067387d22994fc87c3113636aa34ccd2914a4d2d188ce365c5d85/zstandard-0.24.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3f2fe35ec84908dddf0fbf66b35d7c2878dbe349552dd52e005c755d3493d61c", size = 5268462, upload-time = "2025-08-17T18:22:06.095Z" }, + { url = "https://files.pythonhosted.org/packages/49/cf/2abb3a1ad85aebe18c53e7eca73223f1546ddfa3bf4d2fb83fc5a064c5ca/zstandard-0.24.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:aa705beb74ab116563f4ce784fa94771f230c05d09ab5de9c397793e725bb1db", size = 5443319, upload-time = "2025-08-17T18:22:08.572Z" }, + { url = "https://files.pythonhosted.org/packages/40/42/0dd59fc2f68f1664cda11c3b26abdf987f4e57cb6b6b0f329520cd074552/zstandard-0.24.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:aadf32c389bb7f02b8ec5c243c38302b92c006da565e120dfcb7bf0378f4f848", size = 5822355, upload-time = "2025-08-17T18:22:10.537Z" }, + { url = "https://files.pythonhosted.org/packages/99/c0/ea4e640fd4f7d58d6f87a1e7aca11fb886ac24db277fbbb879336c912f63/zstandard-0.24.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e40cd0fc734aa1d4bd0e7ad102fd2a1aefa50ce9ef570005ffc2273c5442ddc3", size = 5365257, upload-time = "2025-08-17T18:22:13.159Z" }, + { url = "https://files.pythonhosted.org/packages/27/a9/92da42a5c4e7e4003271f2e1f0efd1f37cfd565d763ad3604e9597980a1c/zstandard-0.24.0-cp311-cp311-win32.whl", hash = "sha256:cda61c46343809ecda43dc620d1333dd7433a25d0a252f2dcc7667f6331c7b61", size = 435559, upload-time = "2025-08-17T18:22:17.29Z" }, + { url = "https://files.pythonhosted.org/packages/e2/8e/2c8e5c681ae4937c007938f954a060fa7c74f36273b289cabdb5ef0e9a7e/zstandard-0.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:3b95fc06489aa9388400d1aab01a83652bc040c9c087bd732eb214909d7fb0dd", size = 505070, upload-time = "2025-08-17T18:22:14.808Z" }, + { url = "https://files.pythonhosted.org/packages/52/10/a2f27a66bec75e236b575c9f7b0d7d37004a03aa2dcde8e2decbe9ed7b4d/zstandard-0.24.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad9fd176ff6800a0cf52bcf59c71e5de4fa25bf3ba62b58800e0f84885344d34", size = 461507, upload-time = "2025-08-17T18:22:15.964Z" }, + { url = "https://files.pythonhosted.org/packages/26/e9/0bd281d9154bba7fc421a291e263911e1d69d6951aa80955b992a48289f6/zstandard-0.24.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a2bda8f2790add22773ee7a4e43c90ea05598bffc94c21c40ae0a9000b0133c3", size = 795710, upload-time = "2025-08-17T18:22:19.189Z" }, + { url = "https://files.pythonhosted.org/packages/36/26/b250a2eef515caf492e2d86732e75240cdac9d92b04383722b9753590c36/zstandard-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cc76de75300f65b8eb574d855c12518dc25a075dadb41dd18f6322bda3fe15d5", size = 640336, upload-time = "2025-08-17T18:22:20.466Z" }, + { url = "https://files.pythonhosted.org/packages/79/bf/3ba6b522306d9bf097aac8547556b98a4f753dc807a170becaf30dcd6f01/zstandard-0.24.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:d2b3b4bda1a025b10fe0269369475f420177f2cb06e0f9d32c95b4873c9f80b8", size = 5342533, upload-time = "2025-08-17T18:22:22.326Z" }, + { url = "https://files.pythonhosted.org/packages/ea/ec/22bc75bf054e25accdf8e928bc68ab36b4466809729c554ff3a1c1c8bce6/zstandard-0.24.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b84c6c210684286e504022d11ec294d2b7922d66c823e87575d8b23eba7c81f", size = 5062837, upload-time = "2025-08-17T18:22:24.416Z" }, + { url = "https://files.pythonhosted.org/packages/48/cc/33edfc9d286e517fb5b51d9c3210e5bcfce578d02a675f994308ca587ae1/zstandard-0.24.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c59740682a686bf835a1a4d8d0ed1eefe31ac07f1c5a7ed5f2e72cf577692b00", size = 5393855, upload-time = "2025-08-17T18:22:26.786Z" }, + { url = "https://files.pythonhosted.org/packages/73/36/59254e9b29da6215fb3a717812bf87192d89f190f23817d88cb8868c47ac/zstandard-0.24.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6324fde5cf5120fbf6541d5ff3c86011ec056e8d0f915d8e7822926a5377193a", size = 5451058, upload-time = "2025-08-17T18:22:28.885Z" }, + { url = "https://files.pythonhosted.org/packages/9a/c7/31674cb2168b741bbbe71ce37dd397c9c671e73349d88ad3bca9e9fae25b/zstandard-0.24.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51a86bd963de3f36688553926a84e550d45d7f9745bd1947d79472eca27fcc75", size = 5546619, upload-time = "2025-08-17T18:22:31.115Z" }, + { url = "https://files.pythonhosted.org/packages/e6/01/1a9f22239f08c00c156f2266db857545ece66a6fc0303d45c298564bc20b/zstandard-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d82ac87017b734f2fb70ff93818c66f0ad2c3810f61040f077ed38d924e19980", size = 5046676, upload-time = "2025-08-17T18:22:33.077Z" }, + { url = "https://files.pythonhosted.org/packages/a7/91/6c0cf8fa143a4988a0361380ac2ef0d7cb98a374704b389fbc38b5891712/zstandard-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92ea7855d5bcfb386c34557516c73753435fb2d4a014e2c9343b5f5ba148b5d8", size = 5576381, upload-time = "2025-08-17T18:22:35.391Z" }, + { url = "https://files.pythonhosted.org/packages/e2/77/1526080e22e78871e786ccf3c84bf5cec9ed25110a9585507d3c551da3d6/zstandard-0.24.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3adb4b5414febf074800d264ddf69ecade8c658837a83a19e8ab820e924c9933", size = 4953403, upload-time = "2025-08-17T18:22:37.266Z" }, + { url = "https://files.pythonhosted.org/packages/6e/d0/a3a833930bff01eab697eb8abeafb0ab068438771fa066558d96d7dafbf9/zstandard-0.24.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6374feaf347e6b83ec13cc5dcfa70076f06d8f7ecd46cc71d58fac798ff08b76", size = 5267396, upload-time = "2025-08-17T18:22:39.757Z" }, + { url = "https://files.pythonhosted.org/packages/f3/5e/90a0db9a61cd4769c06374297ecfcbbf66654f74cec89392519deba64d76/zstandard-0.24.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:13fc548e214df08d896ee5f29e1f91ee35db14f733fef8eabea8dca6e451d1e2", size = 5433269, upload-time = "2025-08-17T18:22:42.131Z" }, + { url = "https://files.pythonhosted.org/packages/ce/58/fc6a71060dd67c26a9c5566e0d7c99248cbe5abfda6b3b65b8f1a28d59f7/zstandard-0.24.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0a416814608610abf5488889c74e43ffa0343ca6cf43957c6b6ec526212422da", size = 5814203, upload-time = "2025-08-17T18:22:44.017Z" }, + { url = "https://files.pythonhosted.org/packages/5c/6a/89573d4393e3ecbfa425d9a4e391027f58d7810dec5cdb13a26e4cdeef5c/zstandard-0.24.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0d66da2649bb0af4471699aeb7a83d6f59ae30236fb9f6b5d20fb618ef6c6777", size = 5359622, upload-time = "2025-08-17T18:22:45.802Z" }, + { url = "https://files.pythonhosted.org/packages/60/ff/2cbab815d6f02a53a9d8d8703bc727d8408a2e508143ca9af6c3cca2054b/zstandard-0.24.0-cp312-cp312-win32.whl", hash = "sha256:ff19efaa33e7f136fe95f9bbcc90ab7fb60648453b03f95d1de3ab6997de0f32", size = 435968, upload-time = "2025-08-17T18:22:49.493Z" }, + { url = "https://files.pythonhosted.org/packages/ce/a3/8f96b8ddb7ad12344218fbd0fd2805702dafd126ae9f8a1fb91eef7b33da/zstandard-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc05f8a875eb651d1cc62e12a4a0e6afa5cd0cc231381adb830d2e9c196ea895", size = 505195, upload-time = "2025-08-17T18:22:47.193Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4a/bfca20679da63bfc236634ef2e4b1b4254203098b0170e3511fee781351f/zstandard-0.24.0-cp312-cp312-win_arm64.whl", hash = "sha256:b04c94718f7a8ed7cdd01b162b6caa1954b3c9d486f00ecbbd300f149d2b2606", size = 461605, upload-time = "2025-08-17T18:22:48.317Z" }, ] From ef2bb7f2fc07f4b94709cee752386be815eea8aa Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 22 Aug 2025 20:06:12 -0700 Subject: [PATCH 64/72] release: build orphaned branch (#36047) --- .github/workflows/release.yaml | 2 +- .github/workflows/selfdrive_tests.yaml | 2 +- release/{build_devel.sh => build_stripped.sh} | 23 ++++++++----------- 3 files changed, 11 insertions(+), 16 deletions(-) rename release/{build_devel.sh => build_stripped.sh} (83%) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4065b0ef3e..0f4ce6cb3a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -39,4 +39,4 @@ jobs: git config --global --add safe.directory '*' git lfs pull - name: Push master-ci - run: BRANCH=__nightly release/build_devel.sh + run: BRANCH=__nightly release/build_stripped.sh diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index aad772757d..cdafbbfede 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -52,7 +52,7 @@ jobs: command: git lfs pull - name: Build devel timeout-minutes: 1 - run: TARGET_DIR=$STRIPPED_DIR release/build_devel.sh + run: TARGET_DIR=$STRIPPED_DIR release/build_stripped.sh - uses: ./.github/workflows/setup-with-retry - name: Build openpilot and run checks timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 10 || 30) }} # allow more time when we missed the scons cache diff --git a/release/build_devel.sh b/release/build_stripped.sh similarity index 83% rename from release/build_devel.sh rename to release/build_stripped.sh index c4ef280258..6f1a568c25 100755 --- a/release/build_devel.sh +++ b/release/build_stripped.sh @@ -17,29 +17,23 @@ rm -rf $TARGET_DIR mkdir -p $TARGET_DIR cd $TARGET_DIR cp -r $SOURCE_DIR/.git $TARGET_DIR -pre-commit uninstall || true -echo "[-] bringing __nightly and devel in sync T=$SECONDS" +echo "[-] setting up stripped branch sync T=$SECONDS" cd $TARGET_DIR -git fetch --depth 1 origin __nightly -git fetch --depth 1 origin devel - -git checkout -f --track origin/__nightly -git reset --hard __nightly -git checkout __nightly -git reset --hard origin/devel -git clean -xdff -git submodule foreach --recursive git clean -xdff -git lfs uninstall +# tmp branch +git checkout --orphan tmp # remove everything except .git echo "[-] erasing old openpilot T=$SECONDS" +git submodule deinit -f --all +git rm -rf --cached . find . -maxdepth 1 -not -path './.git' -not -name '.' -not -name '..' -exec rm -rf '{}' \; -# reset source tree +# cleanup before the copy cd $SOURCE_DIR git clean -xdff +git submodule foreach --recursive git clean -xdff # do the files copy echo "[-] copying files T=$SECONDS" @@ -48,6 +42,7 @@ cp -pR --parents $(./release/release_files.py) $TARGET_DIR/ # in the directory cd $TARGET_DIR +rm -rf .git/modules/ rm -f panda/board/obj/panda.bin.signed # include source commit hash and build date in commit @@ -86,7 +81,7 @@ fi if [ ! -z "$BRANCH" ]; then echo "[-] Pushing to $BRANCH T=$SECONDS" - git push -f origin __nightly:$BRANCH + git push -f origin tmp:$BRANCH fi echo "[-] done T=$SECONDS, ready at $TARGET_DIR" From 7ed8abb66c9b3fbbf93e35545cfac56f8d4013b5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 22 Aug 2025 20:11:50 -0700 Subject: [PATCH 65/72] camerad: garbage collect CL files (#36046) --- system/camerad/sensors/ar0231_cl.h | 34 ----------------- system/camerad/sensors/os04c10_cl.h | 58 ----------------------------- system/camerad/sensors/ox03c10_cl.h | 47 ----------------------- 3 files changed, 139 deletions(-) delete mode 100644 system/camerad/sensors/ar0231_cl.h delete mode 100644 system/camerad/sensors/os04c10_cl.h delete mode 100644 system/camerad/sensors/ox03c10_cl.h diff --git a/system/camerad/sensors/ar0231_cl.h b/system/camerad/sensors/ar0231_cl.h deleted file mode 100644 index c79242543b..0000000000 --- a/system/camerad/sensors/ar0231_cl.h +++ /dev/null @@ -1,34 +0,0 @@ -#if SENSOR_ID == 1 - -#define VIGNETTE_PROFILE_8DT0MM - -#define BIT_DEPTH 12 -#define PV_MAX 4096 -#define BLACK_LVL 168 - -float4 normalize_pv(int4 parsed, float vignette_factor) { - float4 pv = (convert_float4(parsed) - BLACK_LVL) / (PV_MAX - BLACK_LVL); - return clamp(pv*vignette_factor, 0.0, 1.0); -} - -float3 color_correct(float3 rgb) { - float3 corrected = rgb.x * (float3)(1.82717181, -0.31231438, 0.07307673); - corrected += rgb.y * (float3)(-0.5743977, 1.36858544, -0.53183455); - corrected += rgb.z * (float3)(-0.25277411, -0.05627105, 1.45875782); - return corrected; -} - -float3 apply_gamma(float3 rgb, int expo_time) { - // tone mapping params - const float gamma_k = 0.75; - const float gamma_b = 0.125; - const float mp = 0.01; // ideally midpoint should be adaptive - const float rk = 9 - 100*mp; - - // poly approximation for s curve - return (rgb > mp) ? - ((rk * (rgb-mp) * (1-(gamma_k*mp+gamma_b)) * (1+1/(rk*(1-mp))) / (1+rk*(rgb-mp))) + gamma_k*mp + gamma_b) : - ((rk * (rgb-mp) * (gamma_k*mp+gamma_b) * (1+1/(rk*mp)) / (1-rk*(rgb-mp))) + gamma_k*mp + gamma_b); -} - -#endif diff --git a/system/camerad/sensors/os04c10_cl.h b/system/camerad/sensors/os04c10_cl.h deleted file mode 100644 index 3b5cf88839..0000000000 --- a/system/camerad/sensors/os04c10_cl.h +++ /dev/null @@ -1,58 +0,0 @@ -#if SENSOR_ID == 3 - -#define BGGR -#define VIGNETTE_PROFILE_4DT6MM - -#define BIT_DEPTH 12 -#define PV_MAX10 1023 -#define PV_MAX12 4095 -#define PV_MAX16 65536 // gamma curve is calibrated to 16bit -#define BLACK_LVL 48 - -float combine_dual_pvs(float lv, float sv, int expo_time) { - float svc = fmax(sv * expo_time, (float)(64 * (PV_MAX10 - BLACK_LVL))); - float svd = sv * fmin(expo_time, 8.0) / 8; - - if (expo_time > 64) { - if (lv < PV_MAX10 - BLACK_LVL) { - return lv / (PV_MAX16 - BLACK_LVL); - } else { - return (svc / 64) / (PV_MAX16 - BLACK_LVL); - } - } else { - if (lv > 32) { - return (lv * 64 / fmax(expo_time, 8.0)) / (PV_MAX16 - BLACK_LVL); - } else { - return svd / (PV_MAX16 - BLACK_LVL); - } - } -} - -float4 normalize_pv_hdr(int4 parsed, int4 short_parsed, float vignette_factor, int expo_time) { - float4 pl = convert_float4(parsed - BLACK_LVL); - float4 ps = convert_float4(short_parsed - BLACK_LVL); - float4 pv; - pv.s0 = combine_dual_pvs(pl.s0, ps.s0, expo_time); - pv.s1 = combine_dual_pvs(pl.s1, ps.s1, expo_time); - pv.s2 = combine_dual_pvs(pl.s2, ps.s2, expo_time); - pv.s3 = combine_dual_pvs(pl.s3, ps.s3, expo_time); - return clamp(pv*vignette_factor, 0.0, 1.0); -} - -float4 normalize_pv(int4 parsed, float vignette_factor) { - float4 pv = (convert_float4(parsed) - BLACK_LVL) / (PV_MAX12 - BLACK_LVL); - return clamp(pv*vignette_factor, 0.0, 1.0); -} - -float3 color_correct(float3 rgb) { - float3 corrected = rgb.x * (float3)(1.55361989, -0.268894615, -0.000593219); - corrected += rgb.y * (float3)(-0.421217301, 1.51883144, -0.69760146); - corrected += rgb.z * (float3)(-0.132402589, -0.249936825, 1.69819468); - return corrected; -} - -float3 apply_gamma(float3 rgb, int expo_time) { - return (10 * rgb) / (1 + 9 * rgb); -} - -#endif diff --git a/system/camerad/sensors/ox03c10_cl.h b/system/camerad/sensors/ox03c10_cl.h deleted file mode 100644 index c8cec7cf8a..0000000000 --- a/system/camerad/sensors/ox03c10_cl.h +++ /dev/null @@ -1,47 +0,0 @@ -#if SENSOR_ID == 2 - -#define VIGNETTE_PROFILE_8DT0MM - -#define BIT_DEPTH 12 -#define BLACK_LVL 64 - -float ox_lut_func(int x) { - if (x < 512) { - return x * 5.94873e-8; - } else if (512 <= x && x < 768) { - return 3.0458e-05 + (x-512) * 1.19913e-7; - } else if (768 <= x && x < 1536) { - return 6.1154e-05 + (x-768) * 2.38493e-7; - } else if (1536 <= x && x < 1792) { - return 0.0002448 + (x-1536) * 9.56930e-7; - } else if (1792 <= x && x < 2048) { - return 0.00048977 + (x-1792) * 1.91441e-6; - } else if (2048 <= x && x < 2304) { - return 0.00097984 + (x-2048) * 3.82937e-6; - } else if (2304 <= x && x < 2560) { - return 0.0019601 + (x-2304) * 7.659055e-6; - } else if (2560 <= x && x < 2816) { - return 0.0039207 + (x-2560) * 1.525e-5; - } else { - return 0.0078421 + (exp((x-2816)/273.0) - 1) * 0.0092421; - } -} - -float4 normalize_pv(int4 parsed, float vignette_factor) { - // PWL - float4 pv = {ox_lut_func(parsed.s0), ox_lut_func(parsed.s1), ox_lut_func(parsed.s2), ox_lut_func(parsed.s3)}; - return clamp(pv*vignette_factor*256.0, 0.0, 1.0); -} - -float3 color_correct(float3 rgb) { - float3 corrected = rgb.x * (float3)(1.5664815, -0.29808738, -0.03973474); - corrected += rgb.y * (float3)(-0.48672447, 1.41914433, -0.40295248); - corrected += rgb.z * (float3)(-0.07975703, -0.12105695, 1.44268722); - return corrected; -} - -float3 apply_gamma(float3 rgb, int expo_time) { - return -0.507089*exp(-12.54124638*rgb) + 0.9655*powr(rgb, 0.5) - 0.472597*rgb + 0.507089; -} - -#endif From 2b46e1450ab9ce42aae7f531924c00db7453c7a8 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 23 Aug 2025 00:49:19 -0700 Subject: [PATCH 66/72] raylib: simplify network state (#36049) * wtf * we never disabled unsupported networks * dont be a hero * i hate mypy * fix --- system/ui/widgets/network.py | 126 +++++++++++++++-------------------- 1 file changed, 55 insertions(+), 71 deletions(-) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 4eac1214c2..0bb759a919 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -1,17 +1,17 @@ -from dataclasses import dataclass +from enum import IntEnum from functools import partial from threading import Lock -from typing import Literal +from typing import cast import pyray as rl from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wifi_manager import NetworkInfo, WifiManagerCallbacks, WifiManagerWrapper, SecurityType from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.button import ButtonStyle, Button, TextAlignment +from openpilot.system.ui.widgets.button import ButtonStyle, Button from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.widgets.keyboard import Keyboard -from openpilot.system.ui.widgets.label import gui_label +from openpilot.system.ui.widgets.label import TextAlignment, gui_label NM_DEVICE_STATE_NEED_AUTH = 60 MIN_PASSWORD_LENGTH = 8 @@ -27,43 +27,20 @@ STRENGTH_ICONS = [ ] -@dataclass -class StateIdle: - action: Literal["idle"] = "idle" - - -@dataclass -class StateConnecting: - network: NetworkInfo - action: Literal["connecting"] = "connecting" - - -@dataclass -class StateNeedsAuth: - network: NetworkInfo - retry: bool - action: Literal["needs_auth"] = "needs_auth" - - -@dataclass -class StateShowForgetConfirm: - network: NetworkInfo - action: Literal["show_forget_confirm"] = "show_forget_confirm" - - -@dataclass -class StateForgetting: - network: NetworkInfo - action: Literal["forgetting"] = "forgetting" - - -UIState = StateIdle | StateConnecting | StateNeedsAuth | StateShowForgetConfirm | StateForgetting +class UIState(IntEnum): + IDLE = 0 + CONNECTING = 1 + NEEDS_AUTH = 2 + SHOW_FORGET_CONFIRM = 3 + FORGETTING = 4 class WifiManagerUI(Widget): def __init__(self, wifi_manager: WifiManagerWrapper): super().__init__() - self.state: UIState = StateIdle() + self.state: UIState = UIState.IDLE + self._state_network: NetworkInfo | None = None # for CONNECTING / NEEDS_AUTH / SHOW_FORGET_CONFIRM / FORGETTING + self._password_retry: bool = False # for NEEDS_AUTH self.btn_width: int = 200 self.scroll_panel = GuiScrollPanel() self.keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH, show_password_toggle=True) @@ -93,17 +70,16 @@ class WifiManagerUI(Widget): gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) return - match self.state: - case StateNeedsAuth(network, retry): - self.keyboard.set_title("Wrong password" if retry else "Enter password", f"for {network.ssid}") - self.keyboard.reset() - gui_app.set_modal_overlay(self.keyboard, lambda result: self._on_password_entered(network, result)) - case StateShowForgetConfirm(network): - self._confirm_dialog.set_text(f'Forget Wi-Fi Network "{network.ssid}"?') - self._confirm_dialog.reset() - gui_app.set_modal_overlay(self._confirm_dialog, callback=lambda result: self.on_forgot_confirm_finished(network, result)) - case _: - self._draw_network_list(rect) + if self.state == UIState.NEEDS_AUTH and self._state_network: + self.keyboard.set_title("Wrong password" if self._password_retry else "Enter password", f"for {self._state_network.ssid}") + self.keyboard.reset() + gui_app.set_modal_overlay(self.keyboard, lambda result: self._on_password_entered(cast(NetworkInfo, self._state_network), result)) + elif self.state == UIState.SHOW_FORGET_CONFIRM and self._state_network: + self._confirm_dialog.set_text(f'Forget Wi-Fi Network "{self._state_network.ssid}"?') + self._confirm_dialog.reset() + gui_app.set_modal_overlay(self._confirm_dialog, callback=lambda result: self.on_forgot_confirm_finished(self._state_network, result)) + else: + self._draw_network_list(rect) def _on_password_entered(self, network: NetworkInfo, result: int): if result == 1: @@ -113,13 +89,13 @@ class WifiManagerUI(Widget): if len(password) >= MIN_PASSWORD_LENGTH: self.connect_to_network(network, password) elif result == 0: - self.state = StateIdle() + self.state = UIState.IDLE def on_forgot_confirm_finished(self, network, result: int): if result == 1: self.forget_network(network) elif result == 0: - self.state = StateIdle() + self.state = UIState.IDLE def _draw_network_list(self, rect: rl.Rectangle): content_rect = rl.Rectangle(rect.x, rect.y, rect.width, len(self._networks) * ITEM_HEIGHT) @@ -147,17 +123,18 @@ class WifiManagerUI(Widget): security_icon_rect = rl.Rectangle(signal_icon_rect.x - spacing - ICON_SIZE, rect.y + (ITEM_HEIGHT - ICON_SIZE) / 2, ICON_SIZE, ICON_SIZE) status_text = "" - match self.state: - case StateConnecting(network=connecting): - if connecting.ssid == network.ssid: - self._networks_buttons[network.ssid].set_enabled(False) - status_text = "CONNECTING..." - case StateForgetting(network=forgetting): - if forgetting.ssid == network.ssid: - self._networks_buttons[network.ssid].set_enabled(False) - status_text = "FORGETTING..." - case _: - self._networks_buttons[network.ssid].set_enabled(True) + if self.state == UIState.CONNECTING and self._state_network: + if self._state_network.ssid == network.ssid: + self._networks_buttons[network.ssid].set_enabled(False) + status_text = "CONNECTING..." + elif self.state == UIState.FORGETTING and self._state_network: + if self._state_network.ssid == network.ssid: + self._networks_buttons[network.ssid].set_enabled(False) + status_text = "FORGETTING..." + elif network.security_type == SecurityType.UNSUPPORTED: + self._networks_buttons[network.ssid].set_enabled(False) + else: + self._networks_buttons[network.ssid].set_enabled(True) self._networks_buttons[network.ssid].render(ssid_rect) @@ -181,13 +158,16 @@ class WifiManagerUI(Widget): def _networks_buttons_callback(self, network): if self.scroll_panel.is_touch_valid(): if not network.is_saved and network.security_type != SecurityType.OPEN: - self.state = StateNeedsAuth(network, False) + self.state = UIState.NEEDS_AUTH + self._state_network = network + self._password_retry = False elif not network.is_connected: self.connect_to_network(network) def _forget_networks_buttons_callback(self, network): if self.scroll_panel.is_touch_valid(): - self.state = StateShowForgetConfirm(network) + self.state = UIState.SHOW_FORGET_CONFIRM + self._state_network = network def _draw_status_icon(self, rect, network: NetworkInfo): """Draw the status icon based on network's connection state""" @@ -212,14 +192,16 @@ class WifiManagerUI(Widget): rl.draw_texture_v(gui_app.texture(STRENGTH_ICONS[strength_level], ICON_SIZE, ICON_SIZE), rl.Vector2(rect.x, rect.y), rl.WHITE) def connect_to_network(self, network: NetworkInfo, password=''): - self.state = StateConnecting(network) + self.state = UIState.CONNECTING + self._state_network = network if network.is_saved and not password: self.wifi_manager.activate_connection(network.ssid) else: self.wifi_manager.connect_to_network(network.ssid, password) def forget_network(self, network: NetworkInfo): - self.state = StateForgetting(network) + self.state = UIState.FORGETTING + self._state_network = network network.is_saved = False self.wifi_manager.forget_connection(network.ssid) @@ -236,22 +218,24 @@ class WifiManagerUI(Widget): with self._lock: network = next((n for n in self._networks if n.ssid == ssid), None) if network: - self.state = StateNeedsAuth(network, True) + self.state = UIState.NEEDS_AUTH + self._state_network = network + self._password_retry = True def _on_activated(self): with self._lock: - if isinstance(self.state, StateConnecting): - self.state = StateIdle() + if self.state == UIState.CONNECTING: + self.state = UIState.IDLE def _on_forgotten(self, ssid): with self._lock: - if isinstance(self.state, StateForgetting): - self.state = StateIdle() + if self.state == UIState.FORGETTING: + self.state = UIState.IDLE def _on_connection_failed(self, ssid: str, error: str): with self._lock: - if isinstance(self.state, StateConnecting): - self.state = StateIdle() + if self.state == UIState.CONNECTING: + self.state = UIState.IDLE def main(): From dd7de180ea9ee95b1440788f6e018dc2cb82540f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 23 Aug 2025 04:56:00 -0700 Subject: [PATCH 67/72] raylib: cache API token (#36050) * cache with time * safety * rm * clean up --- selfdrive/ui/lib/prime_state.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/lib/prime_state.py b/selfdrive/ui/lib/prime_state.py index b6c0d88469..da2ff899dd 100644 --- a/selfdrive/ui/lib/prime_state.py +++ b/selfdrive/ui/lib/prime_state.py @@ -2,12 +2,15 @@ from enum import IntEnum import os import threading import time +from functools import lru_cache from openpilot.common.api import Api, api_get from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog from openpilot.system.athena.registration import UNREGISTERED_DONGLE_ID +TOKEN_EXPIRY_HOURS = 2 + class PrimeType(IntEnum): UNKNOWN = -2, @@ -20,6 +23,12 @@ class PrimeType(IntEnum): PURPLE = 5, +@lru_cache(maxsize=1) +def get_token(dongle_id: str, t: int): + print('getting token') + return Api(dongle_id).get_token(expiry_hours=TOKEN_EXPIRY_HOURS) + + class PrimeState: FETCH_INTERVAL = 5.0 # seconds between API calls API_TIMEOUT = 10.0 # seconds for API requests @@ -49,13 +58,15 @@ class PrimeState: return try: - identity_token = Api(dongle_id).get_token() + identity_token = get_token(dongle_id, int(time.monotonic() / (TOKEN_EXPIRY_HOURS / 2 * 60 * 60))) response = api_get(f"v1.1/devices/{dongle_id}", timeout=self.API_TIMEOUT, access_token=identity_token) if response.status_code == 200: data = response.json() is_paired = data.get("is_paired", False) prime_type = data.get("prime_type", 0) self.set_type(PrimeType(prime_type) if is_paired else PrimeType.UNPAIRED) + elif response.status_code == 401: + get_token.cache_clear() except Exception as e: cloudlog.error(f"Failed to fetch prime status: {e}") From 2b86cc13734096765df814520193e7707d7a2cc0 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 23 Aug 2025 09:42:05 -0400 Subject: [PATCH 68/72] param_store: update list every 3 seconds (#1174) --- sunnypilot/selfdrive/controls/lib/param_store.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sunnypilot/selfdrive/controls/lib/param_store.py b/sunnypilot/selfdrive/controls/lib/param_store.py index fae221bb74..2ef3473187 100644 --- a/sunnypilot/selfdrive/controls/lib/param_store.py +++ b/sunnypilot/selfdrive/controls/lib/param_store.py @@ -24,11 +24,16 @@ class ParamStore: self.values = {} self.cached_params_list: list[capnp.lib.capnp._DynamicStructBuilder] | None = None + self.frame = 0 + def update(self, params: Params) -> None: - old_values = dict(self.values) - self.values = {k: params.get(k) or "0" for k in self.keys} - if old_values != self.values: - self.cached_params_list = None + if self.frame % 300 == 0: + old_values = dict(self.values) + self.values = {k: params.get(k) or "0" for k in self.keys} + if old_values != self.values: + self.cached_params_list = None + + self.frame += 1 def publish(self) -> list[capnp.lib.capnp._DynamicStructBuilder]: if self.cached_params_list is None: From 9e50a1b660478d8ce5c9fbcb96e8c6b12b9de54c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 23 Aug 2025 20:34:53 -0400 Subject: [PATCH 69/72] [bot] Update Python packages (#1175) Update Python packages Co-authored-by: github-actions[bot] --- docs/CARS.md | 3 ++- opendbc_repo | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 0ee0615a54..e10dc8ae77 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 325 Supported Cars +# 326 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video|Setup Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -82,6 +82,7 @@ A supported vehicle is one that just works when you install a comma device. All |Honda|Civic Hatchback Hybrid 2025|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic Hatchback Hybrid (Europe only) 2023|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|Civic Hybrid 2025|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Clarity 2018-21|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Nidec connector + Honda Clarity Proxy Board
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|CR-V 2015-16|Touring Trim|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|CR-V 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|15 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| |Honda|CR-V Hybrid 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| diff --git a/opendbc_repo b/opendbc_repo index 23d37a87ec..dec0074043 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 23d37a87ec73637cc021b2fa65adc060919d3396 +Subproject commit dec007404377755457b769e2438a4857e414af02 From d6af554db4f8eac8b46972e8f2d30702ca58b3f9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 23 Aug 2025 20:38:19 -0400 Subject: [PATCH 70/72] [bot] Update translations (#1172) Update translations Co-authored-by: github-actions[bot] Co-authored-by: Jason Wen --- selfdrive/ui/translations/main_ar.ts | 44 ++++++++++++++++++++--- selfdrive/ui/translations/main_de.ts | 44 ++++++++++++++++++++--- selfdrive/ui/translations/main_es.ts | 46 +++++++++++++++++++++--- selfdrive/ui/translations/main_fr.ts | 44 ++++++++++++++++++++--- selfdrive/ui/translations/main_ja.ts | 44 ++++++++++++++++++++--- selfdrive/ui/translations/main_ko.ts | 44 ++++++++++++++++++++--- selfdrive/ui/translations/main_pt-BR.ts | 44 ++++++++++++++++++++--- selfdrive/ui/translations/main_th.ts | 44 ++++++++++++++++++++--- selfdrive/ui/translations/main_tr.ts | 44 ++++++++++++++++++++--- selfdrive/ui/translations/main_zh-CHS.ts | 44 ++++++++++++++++++++--- selfdrive/ui/translations/main_zh-CHT.ts | 44 ++++++++++++++++++++--- 11 files changed, 441 insertions(+), 45 deletions(-) diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index 95fd0ccbb4..bebddf259e 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -972,10 +972,6 @@ The default software delay value is 0.2 Default - - Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. - - Model download has started in the background. @@ -1020,6 +1016,38 @@ The default software delay value is 0.2 Cancel إلغاء + + Refresh Model List + + + + REFRESH + + + + Fetching Latest Models + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. + + + + Live Steer Delay: + + + + Actuator Delay: + + + + Software Delay: + + + + Total Delay: + +
MultiOptionDialog @@ -2067,6 +2095,14 @@ Warning: You are on a metered connection! Not Paired + + Enable sunnylink uploader to allow sunnypilot to upload your driving data to sunnypilot servers. (only for highest tiers, and does NOT bring ANY benefit to you. We are just testing data volume.) + + + + [Don't use] Enable sunnylink uploader + + SunnylinkSponsorPopup diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 1a928b4e49..1e477553e6 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -964,10 +964,6 @@ The default software delay value is 0.2 Default - - Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. - - Model download has started in the background. @@ -1012,6 +1008,38 @@ The default software delay value is 0.2 Cancel Abbrechen + + Refresh Model List + + + + REFRESH + + + + Fetching Latest Models + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. + + + + Live Steer Delay: + + + + Actuator Delay: + + + + Software Delay: + + + + Total Delay: + + MultiOptionDialog @@ -2049,6 +2077,14 @@ Warning: You are on a metered connection! Not Paired + + Enable sunnylink uploader to allow sunnypilot to upload your driving data to sunnypilot servers. (only for highest tiers, and does NOT bring ANY benefit to you. We are just testing data volume.) + + + + [Don't use] Enable sunnylink uploader + + SunnylinkSponsorPopup diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index 3814373c53..e6c22bd143 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -462,7 +462,7 @@ La calibración del retraso de la dirección está completa. Select a language - + Seleccione el idioma Wake-Up Behavior @@ -968,10 +968,6 @@ The default software delay value is 0.2 Default - - Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. - - Model download has started in the background. @@ -1016,6 +1012,38 @@ The default software delay value is 0.2 Cancel Cancelar + + Refresh Model List + + + + REFRESH + + + + Fetching Latest Models + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. + + + + Live Steer Delay: + + + + Actuator Delay: + + + + Software Delay: + + + + Total Delay: + + MultiOptionDialog @@ -2051,6 +2079,14 @@ Warning: You are on a metered connection! Not Paired + + Enable sunnylink uploader to allow sunnypilot to upload your driving data to sunnypilot servers. (only for highest tiers, and does NOT bring ANY benefit to you. We are just testing data volume.) + + + + [Don't use] Enable sunnylink uploader + + SunnylinkSponsorPopup diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index a86ac3ea64..f70c5eeebb 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -964,10 +964,6 @@ The default software delay value is 0.2 Default - - Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. - - Model download has started in the background. @@ -1012,6 +1008,38 @@ The default software delay value is 0.2 Cancel Annuler + + Refresh Model List + + + + REFRESH + + + + Fetching Latest Models + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. + + + + Live Steer Delay: + + + + Actuator Delay: + + + + Software Delay: + + + + Total Delay: + + MultiOptionDialog @@ -2047,6 +2075,14 @@ Warning: You are on a metered connection! Not Paired + + Enable sunnylink uploader to allow sunnypilot to upload your driving data to sunnypilot servers. (only for highest tiers, and does NOT bring ANY benefit to you. We are just testing data volume.) + + + + [Don't use] Enable sunnylink uploader + + SunnylinkSponsorPopup diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 0da704cd47..b67e0a9709 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -966,10 +966,6 @@ The default software delay value is 0.2 Default - - Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. - - Model download has started in the background. @@ -1014,6 +1010,38 @@ The default software delay value is 0.2 Cancel キャンセル + + Refresh Model List + + + + REFRESH + + + + Fetching Latest Models + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. + + + + Live Steer Delay: + + + + Actuator Delay: + + + + Software Delay: + + + + Total Delay: + + MultiOptionDialog @@ -2046,6 +2074,14 @@ Warning: You are on a metered connection! Not Paired + + Enable sunnylink uploader to allow sunnypilot to upload your driving data to sunnypilot servers. (only for highest tiers, and does NOT bring ANY benefit to you. We are just testing data volume.) + + + + [Don't use] Enable sunnylink uploader + + SunnylinkSponsorPopup diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 90bbfcaea3..2fdd601e6a 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -974,10 +974,6 @@ The default software delay value is 0.2 Default 기본값 - - Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. - 이 기능을 켜면 차량이 스스로 핸들 반응 속도를 학습하고 맞춥니다. 끄면 고정된 핸들 반응 속도를 사용합니다. 이 기능을 켜두는 것이 기본 openpilot 경험을 제공합니다. 차량이 주행 중일 때 현재 값이 자동으로 업데이트됩니다. - Model download has started in the background. 모델 다운로드가 백그라운드에서 시작되었습니다. @@ -1022,6 +1018,38 @@ The default software delay value is 0.2 Cancel 취소 + + Refresh Model List + + + + REFRESH + 새로 고침 + + + Fetching Latest Models + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. + + + + Live Steer Delay: + + + + Actuator Delay: + + + + Software Delay: + + + + Total Delay: + + MultiOptionDialog @@ -2060,6 +2088,14 @@ Warning: You are on a metered connection! Not Paired 페어링되지 않음 + + Enable sunnylink uploader to allow sunnypilot to upload your driving data to sunnypilot servers. (only for highest tiers, and does NOT bring ANY benefit to you. We are just testing data volume.) + + + + [Don't use] Enable sunnylink uploader + + SunnylinkSponsorPopup diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 8dde941e81..c78bb9255c 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -968,10 +968,6 @@ The default software delay value is 0.2 Default - - Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. - - Model download has started in the background. @@ -1016,6 +1012,38 @@ The default software delay value is 0.2 Cancel Cancelar + + Refresh Model List + + + + REFRESH + + + + Fetching Latest Models + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. + + + + Live Steer Delay: + + + + Actuator Delay: + + + + Software Delay: + + + + Total Delay: + + MultiOptionDialog @@ -2051,6 +2079,14 @@ Warning: You are on a metered connection! Not Paired + + Enable sunnylink uploader to allow sunnypilot to upload your driving data to sunnypilot servers. (only for highest tiers, and does NOT bring ANY benefit to you. We are just testing data volume.) + + + + [Don't use] Enable sunnylink uploader + + SunnylinkSponsorPopup diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index 1292ef37fd..2dbf3a6a05 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -962,10 +962,6 @@ The default software delay value is 0.2 Default - - Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. - - Model download has started in the background. @@ -1010,6 +1006,38 @@ The default software delay value is 0.2 Cancel ยกเลิก + + Refresh Model List + + + + REFRESH + + + + Fetching Latest Models + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. + + + + Live Steer Delay: + + + + Actuator Delay: + + + + Software Delay: + + + + Total Delay: + + MultiOptionDialog @@ -2042,6 +2070,14 @@ Warning: You are on a metered connection! Not Paired + + Enable sunnylink uploader to allow sunnypilot to upload your driving data to sunnypilot servers. (only for highest tiers, and does NOT bring ANY benefit to you. We are just testing data volume.) + + + + [Don't use] Enable sunnylink uploader + + SunnylinkSponsorPopup diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 6b5a03bc70..5f06ef5cf6 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -962,10 +962,6 @@ The default software delay value is 0.2 Default - - Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. - - Model download has started in the background. @@ -1010,6 +1006,38 @@ The default software delay value is 0.2 Cancel + + Refresh Model List + + + + REFRESH + + + + Fetching Latest Models + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. + + + + Live Steer Delay: + + + + Actuator Delay: + + + + Software Delay: + + + + Total Delay: + + MultiOptionDialog @@ -2041,6 +2069,14 @@ Warning: You are on a metered connection! Not Paired + + Enable sunnylink uploader to allow sunnypilot to upload your driving data to sunnypilot servers. (only for highest tiers, and does NOT bring ANY benefit to you. We are just testing data volume.) + + + + [Don't use] Enable sunnylink uploader + + SunnylinkSponsorPopup diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index ec2cde0f60..a6f3e9bf0c 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -966,10 +966,6 @@ The default software delay value is 0.2 Default - - Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. - - Model download has started in the background. @@ -1014,6 +1010,38 @@ The default software delay value is 0.2 Cancel 取消 + + Refresh Model List + + + + REFRESH + + + + Fetching Latest Models + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. + + + + Live Steer Delay: + + + + Actuator Delay: + + + + Software Delay: + + + + Total Delay: + + MultiOptionDialog @@ -2046,6 +2074,14 @@ Warning: You are on a metered connection! Not Paired + + Enable sunnylink uploader to allow sunnypilot to upload your driving data to sunnypilot servers. (only for highest tiers, and does NOT bring ANY benefit to you. We are just testing data volume.) + + + + [Don't use] Enable sunnylink uploader + + SunnylinkSponsorPopup diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 669d9b2053..a57fecc7de 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -966,10 +966,6 @@ The default software delay value is 0.2 Default - - Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. The Current value is updated automatically when the vehicle is Onroad. - - Model download has started in the background. @@ -1014,6 +1010,38 @@ The default software delay value is 0.2 Cancel 取消 + + Refresh Model List + + + + REFRESH + + + + Fetching Latest Models + + + + Enable this for the car to learn and adapt its steering response time. Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience. + + + + Live Steer Delay: + + + + Actuator Delay: + + + + Software Delay: + + + + Total Delay: + + MultiOptionDialog @@ -2046,6 +2074,14 @@ Warning: You are on a metered connection! Not Paired + + Enable sunnylink uploader to allow sunnypilot to upload your driving data to sunnypilot servers. (only for highest tiers, and does NOT bring ANY benefit to you. We are just testing data volume.) + + + + [Don't use] Enable sunnylink uploader + + SunnylinkSponsorPopup From f89796033c87f44af1aa0bac9613c7067b3f7650 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 15:52:38 -0700 Subject: [PATCH 71/72] [bot] Update translations (#1179) Update translations Co-authored-by: github-actions[bot] --- selfdrive/ui/translations/main_ar.ts | 10 ---------- selfdrive/ui/translations/main_de.ts | 10 ---------- selfdrive/ui/translations/main_es.ts | 10 ---------- selfdrive/ui/translations/main_fr.ts | 10 ---------- selfdrive/ui/translations/main_ja.ts | 10 ---------- selfdrive/ui/translations/main_ko.ts | 12 ------------ selfdrive/ui/translations/main_pt-BR.ts | 10 ---------- selfdrive/ui/translations/main_th.ts | 10 ---------- selfdrive/ui/translations/main_tr.ts | 10 ---------- selfdrive/ui/translations/main_zh-CHS.ts | 10 ---------- selfdrive/ui/translations/main_zh-CHT.ts | 10 ---------- 11 files changed, 112 deletions(-) diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index bebddf259e..f72ea8ee83 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -2248,16 +2248,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 1e477553e6..193e2fd895 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -2230,16 +2230,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index e6c22bd143..1d1723cde7 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -2232,16 +2232,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. Graba y almacena el audio del micrófono mientras conduces. El audio se incluirá en el video de la cámara del tablero en comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index f70c5eeebb..959321bb4b 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -2228,16 +2228,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index b67e0a9709..28b5b00e51 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -2227,16 +2227,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. 運転中にマイク音声を録音・保存します。音声は comma connect のドライブレコーダー映像に含まれます。 - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 2fdd601e6a..d69fda88b0 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -2241,18 +2241,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. 운전 중 마이크 오디오를 녹음하고 저장합니다. 이 오디오는 comma connect의 대시캠 영상에 포함됩니다. - - Record Audio Feedback with LKAS button - LKAS 버튼으로 오디오 피드백 녹음 - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - LKAS 버튼을 눌러 openpilot 팀과 주행 피드백을 녹음하고 공유하세요. 이 기능을 비활성화하면, 해당 버튼은 북마크 버튼 역할을 합니다. 이 이벤트는 comma connect에서 강조되며, 해당 구간 영상은 기기 저장소에 보존됩니다. - -참고: 이 기능은 일부 차량에서만 호환됩니다. - Enable sunnypilot sunnypilot 사용 diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index c78bb9255c..c5171b19f7 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -2232,16 +2232,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. Grave e armazene o áudio do microfone enquanto estiver dirigindo. O áudio será incluído ao vídeo dashcam no comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index 2dbf3a6a05..3bd2d84a92 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -2223,16 +2223,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 5f06ef5cf6..db9c15580a 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -2222,16 +2222,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index a6f3e9bf0c..2316360891 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -2227,16 +2227,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. 在驾驶时录制并存储麦克风音频。该音频将会包含在 comma connect 的行车记录仪视频中。 - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index a57fecc7de..16bb393dea 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -2227,16 +2227,6 @@ Warning: You are on a metered connection! Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect. 在駕駛時錄製並儲存麥克風音訊。此音訊將會收錄在 comma connect 的行車記錄器影片中。 - - Record Audio Feedback with LKAS button - - - - Press the LKAS button to record and share driving feedback with the openpilot team. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage. - -Note that this feature is only compatible with select cars. - - Enable sunnypilot From 7c6d887187497c2090b38bd356e2943b56874875 Mon Sep 17 00:00:00 2001 From: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com> Date: Sun, 24 Aug 2025 19:08:55 -0700 Subject: [PATCH 72/72] modeld_v2: infer model shapes from inputs (#1162) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * modeld_v2: dynamify temporal buffer management. * skip redundant reshaping and flattening. * simplify MHP checks for lead and plan * modeld_v2: add unit tests for buffer logic and refactor index mapping * Let’s possibly fail a test :) * Update test_buffer_logic_inspect.py * Update test_buffer_logic_inspect.py * modeld_v2: better temporal mapping for non-split * Bump to 10 I guess * Downgrade CURRENT_SELECTOR_VERSION to 9 * red diff ya know? * add dynamic buffer update tests and compare against legacy logic. Cover modelState.init and modelState.run * send * Revert "send" This reverts commit 9e6c95fbfde134eeba952da6eef012baa0396fa0. * format --------- Co-authored-by: Jason Wen --- sunnypilot/modeld_v2/modeld.py | 86 +++--- .../modeld_v2/parse_model_outputs_split.py | 35 ++- .../tests/test_buffer_logic_inspect.py | 256 ++++++++++++++++++ .../runners/tinygrad/tinygrad_runner.py | 2 +- 4 files changed, 308 insertions(+), 71 deletions(-) create mode 100644 sunnypilot/modeld_v2/tests/test_buffer_logic_inspect.py diff --git a/sunnypilot/modeld_v2/modeld.py b/sunnypilot/modeld_v2/modeld.py index 2dc1026c01..1a420e6f1a 100755 --- a/sunnypilot/modeld_v2/modeld.py +++ b/sunnypilot/modeld_v2/modeld.py @@ -69,24 +69,26 @@ class ModelState(ModelStateBase): # img buffers are managed in openCL transform code self.numpy_inputs = {} + self.temporal_buffers = {} + self.temporal_idxs_map = {} for key, shape in self.model_runner.input_shapes.items(): if key not in self.frames: # Managed by opencl self.numpy_inputs[key] = np.zeros(shape, dtype=np.float32) - - if self.model_runner.is_20hz_3d: # split models - self.full_features_buffer = np.zeros((1, self.constants.FULL_HISTORY_BUFFER_LEN, self.constants.FEATURE_LEN), dtype=np.float32) - self.full_desire = np.zeros((1, self.constants.FULL_HISTORY_BUFFER_LEN, self.constants.DESIRE_LEN), dtype=np.float32) - self.full_prev_desired_curv = np.zeros((1, self.constants.FULL_HISTORY_BUFFER_LEN, self.constants.PREV_DESIRED_CURV_LEN), dtype=np.float32) - self.temporal_idxs = slice(-1-(self.constants.TEMPORAL_SKIP*(self.constants.INPUT_HISTORY_BUFFER_LEN-1)), None, self.constants.TEMPORAL_SKIP) - elif self.model_runner.is_20hz and not self.model_runner.is_20hz_3d: - self.full_features_buffer = np.zeros((self.constants.FULL_HISTORY_BUFFER_LEN + 1, self.constants.FEATURE_LEN), dtype=np.float32) - self.full_desire = np.zeros((self.constants.FULL_HISTORY_BUFFER_LEN + 1, self.constants.DESIRE_LEN), dtype=np.float32) - num_elements = self.numpy_inputs['features_buffer'].shape[1] - step_size = int(-100 / num_elements) - self.temporal_idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1] - self.desire_reshape_dims = (self.numpy_inputs['desire'].shape[0], self.numpy_inputs['desire'].shape[1], -1, - self.numpy_inputs['desire'].shape[2]) + # Temporal input: shape is [batch, history, features] + if len(shape) == 3 and shape[1] > 1: + buffer_history_len = max(100, (shape[1] * 4 if shape[1] < 100 else shape[1])) # Allow for higher history buffers in the future + feature_len = shape[2] + self.temporal_buffers[key] = np.zeros((1, buffer_history_len, feature_len), dtype=np.float32) + features_buffer_shape = self.model_runner.input_shapes.get('features_buffer') + if shape[1] in (24, 25) and features_buffer_shape is not None and features_buffer_shape[1] == 24: # 20Hz + step = int(-buffer_history_len / shape[1]) + self.temporal_idxs_map[key] = np.arange(step, step * (shape[1] + 1), step)[::-1] + elif shape[1] == 25: # Split + skip = buffer_history_len // shape[1] + self.temporal_idxs_map[key] = np.arange(buffer_history_len)[-1 - (skip * (shape[1] - 1))::skip] + elif shape[1] == buffer_history_len: # non20hz + self.temporal_idxs_map[key] = np.arange(buffer_history_len) @property def mlsim(self) -> bool: @@ -98,19 +100,16 @@ class ModelState(ModelStateBase): inputs['desire'][0] = 0 new_desire = np.where(inputs['desire'] - self.prev_desire > .99, inputs['desire'], 0) self.prev_desire[:] = inputs['desire'] + self.temporal_buffers['desire'][0,:-1] = self.temporal_buffers['desire'][0,1:] + self.temporal_buffers['desire'][0,-1] = new_desire - if self.model_runner.is_20hz_3d: # split models - self.full_desire[0,:-1] = self.full_desire[0,1:] - self.full_desire[0,-1] = new_desire - self.numpy_inputs['desire'][:] = self.full_desire.reshape((1, self.constants.INPUT_HISTORY_BUFFER_LEN, self.constants.TEMPORAL_SKIP, -1)).max(axis=2) - elif self.model_runner.is_20hz and not self.model_runner.is_20hz_3d: # 20hz supercombo - self.full_desire[:-1] = self.full_desire[1:] - self.full_desire[-1] = new_desire - self.numpy_inputs['desire'][:] = self.full_desire.reshape(self.desire_reshape_dims).max(axis=2) - else: # not 20hz - length = inputs['desire'].shape[0] - self.numpy_inputs['desire'][0, :-1] = self.numpy_inputs['desire'][0, 1:] - self.numpy_inputs['desire'][0, -1, :length] = new_desire[:length] + # Roll buffer and assign based on desire.shape[1] value + if self.temporal_buffers['desire'].shape[1] > self.numpy_inputs['desire'].shape[1]: + skip = self.temporal_buffers['desire'].shape[1] // self.numpy_inputs['desire'].shape[1] + self.numpy_inputs['desire'][:] = ( + self.temporal_buffers['desire'][0].reshape(self.numpy_inputs['desire'].shape[0], self.numpy_inputs['desire'].shape[1], skip, -1).max(axis=2)) + else: + self.numpy_inputs['desire'][:] = self.temporal_buffers['desire'][0, self.temporal_idxs_map['desire']] for key in self.numpy_inputs: if key in inputs and key not in ['desire']: @@ -127,42 +126,27 @@ class ModelState(ModelStateBase): # Run model inference outputs = self.model_runner.run_model() - if self.model_runner.is_20hz_3d: # split models - self.full_features_buffer[0, :-1] = self.full_features_buffer[0, 1:] - self.full_features_buffer[0, -1] = outputs['hidden_state'][0, :] - self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[0, self.temporal_idxs] - elif self.model_runner.is_20hz and not self.model_runner.is_20hz_3d: # 20hz supercombo - self.full_features_buffer[:-1] = self.full_features_buffer[1:] - self.full_features_buffer[-1] = outputs['hidden_state'][0, :] - self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[self.temporal_idxs] - else: # not 20hz - feature_len = outputs['hidden_state'].shape[1] - self.numpy_inputs['features_buffer'][0, :-1] = self.numpy_inputs['features_buffer'][0, 1:] - self.numpy_inputs['features_buffer'][0, -1, :feature_len] = outputs['hidden_state'][0, :feature_len] + # Update features_buffer + self.temporal_buffers['features_buffer'][0, :-1] = self.temporal_buffers['features_buffer'][0, 1:] + self.temporal_buffers['features_buffer'][0, -1] = outputs['hidden_state'][0, :] + self.numpy_inputs['features_buffer'][:] = self.temporal_buffers['features_buffer'][0, self.temporal_idxs_map['features_buffer']] if "desired_curvature" in outputs: input_name_prev = None - if "prev_desired_curvs" in self.numpy_inputs.keys(): input_name_prev = 'prev_desired_curvs' elif "prev_desired_curv" in self.numpy_inputs.keys(): input_name_prev = 'prev_desired_curv' - - if input_name_prev is not None: + if input_name_prev and input_name_prev in self.temporal_buffers: self.process_desired_curvature(outputs, input_name_prev) return outputs def process_desired_curvature(self, outputs, input_name_prev): - if self.model_runner.is_20hz_3d: # split models - self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:] - self.full_prev_desired_curv[0,-1,:] = outputs['desired_curvature'][0, :] - self.numpy_inputs[input_name_prev][:] = self.full_prev_desired_curv[0, self.temporal_idxs] - if self.mlsim: - self.numpy_inputs[input_name_prev][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs] - else: - length = outputs['desired_curvature'][0].size - self.numpy_inputs[input_name_prev][0, :-length, 0] = self.numpy_inputs[input_name_prev][0, length:, 0] - self.numpy_inputs[input_name_prev][0, -length:, 0] = outputs['desired_curvature'][0] + self.temporal_buffers[input_name_prev][0,:-1] = self.temporal_buffers[input_name_prev][0,1:] + self.temporal_buffers[input_name_prev][0,-1,:] = outputs['desired_curvature'][0, :] + self.numpy_inputs[input_name_prev][:] = self.temporal_buffers[input_name_prev][0, self.temporal_idxs_map[input_name_prev]] + if self.mlsim: + self.numpy_inputs[input_name_prev][:] = 0*self.temporal_buffers[input_name_prev][0, self.temporal_idxs_map[input_name_prev]] def get_action_from_model(self, model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action, lat_action_t: float, long_action_t: float, v_ego: float) -> log.ModelDataV2.Action: diff --git a/sunnypilot/modeld_v2/parse_model_outputs_split.py b/sunnypilot/modeld_v2/parse_model_outputs_split.py index ba8592ed18..9cf321a1b6 100644 --- a/sunnypilot/modeld_v2/parse_model_outputs_split.py +++ b/sunnypilot/modeld_v2/parse_model_outputs_split.py @@ -1,6 +1,5 @@ import numpy as np from openpilot.sunnypilot.models.split_model_constants import SplitModelConstants -from openpilot.sunnypilot.models.helpers import get_active_bundle def safe_exp(x, out=None): @@ -25,8 +24,6 @@ def softmax(x, axis=-1): class Parser: def __init__(self, ignore_missing=False): self.ignore_missing = ignore_missing - model_bundle = get_active_bundle() - self.generation = model_bundle.generation if model_bundle is not None else None def check_missing(self, outs, name): if name not in outs and not self.ignore_missing: @@ -91,26 +88,26 @@ class Parser: outs[name] = pred_mu_final.reshape(final_shape) outs[name + '_stds'] = pred_std_final.reshape(final_shape) - def _parse_plan_mhp(self, outs): - self.parse_mdn('plan', outs, in_N=SplitModelConstants.PLAN_MHP_N, out_N=SplitModelConstants.PLAN_MHP_SELECTION, - out_shape=(SplitModelConstants.IDX_N,SplitModelConstants.PLAN_WIDTH)) + def is_mhp(self, outs, name, shape): + if self.check_missing(outs, name): + return False + if outs[name].shape[1] == 2 * shape: + return False + return True def parse_dynamic_outputs(self, outs: dict[str, np.ndarray]) -> None: if 'lead' in outs: - if self.generation >= 12 and \ - outs['lead'].shape[1] == 2 * SplitModelConstants.LEAD_MHP_SELECTION * SplitModelConstants.LEAD_TRAJ_LEN * SplitModelConstants.LEAD_WIDTH: - self.parse_mdn('lead', outs, in_N=0, out_N=0, - out_shape=(SplitModelConstants.LEAD_MHP_SELECTION, SplitModelConstants.LEAD_TRAJ_LEN, SplitModelConstants.LEAD_WIDTH)) - else: - self.parse_mdn('lead', outs, in_N=SplitModelConstants.LEAD_MHP_N, out_N=SplitModelConstants.LEAD_MHP_SELECTION, - out_shape=(SplitModelConstants.LEAD_TRAJ_LEN, SplitModelConstants.LEAD_WIDTH)) + lead_mhp = self.is_mhp(outs, 'lead', + SplitModelConstants.LEAD_MHP_SELECTION * SplitModelConstants.LEAD_TRAJ_LEN * SplitModelConstants.LEAD_WIDTH) + lead_in_N, lead_out_N = (SplitModelConstants.LEAD_MHP_N, SplitModelConstants.LEAD_MHP_SELECTION) if lead_mhp else (0, 0) + lead_out_shape = (SplitModelConstants.LEAD_TRAJ_LEN, SplitModelConstants.LEAD_WIDTH) if lead_mhp else \ + (SplitModelConstants.LEAD_MHP_SELECTION, SplitModelConstants.LEAD_TRAJ_LEN, SplitModelConstants.LEAD_WIDTH) + self.parse_mdn('lead', outs, in_N=lead_in_N, out_N=lead_out_N, out_shape=lead_out_shape) if 'plan' in outs: - if self.generation >= 12 and \ - outs['plan'].shape[1] == 2 * SplitModelConstants.IDX_N * SplitModelConstants.PLAN_WIDTH: - self.parse_mdn('plan', outs, in_N=0, out_N=0, - out_shape=(SplitModelConstants.IDX_N, SplitModelConstants.PLAN_WIDTH)) - else: - self._parse_plan_mhp(outs) + plan_mhp = self.is_mhp(outs, 'plan', SplitModelConstants.IDX_N * SplitModelConstants.PLAN_WIDTH) + plan_in_N, plan_out_N = (SplitModelConstants.PLAN_MHP_N, SplitModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0) + self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, + out_shape=(SplitModelConstants.IDX_N, SplitModelConstants.PLAN_WIDTH)) def split_outputs(self, outs: dict[str, np.ndarray]) -> None: if 'desired_curvature' in outs: diff --git a/sunnypilot/modeld_v2/tests/test_buffer_logic_inspect.py b/sunnypilot/modeld_v2/tests/test_buffer_logic_inspect.py new file mode 100644 index 0000000000..8a0cfd97c8 --- /dev/null +++ b/sunnypilot/modeld_v2/tests/test_buffer_logic_inspect.py @@ -0,0 +1,256 @@ +import numpy as np +import pytest +from typing import Any + +import openpilot.sunnypilot.models.helpers as helpers +import openpilot.sunnypilot.models.runners.helpers as runner_helpers +import openpilot.sunnypilot.modeld_v2.modeld as modeld_module + +ModelState = modeld_module.ModelState + + +# These are the shapes extracted/loaded from the model onnx +SHAPE_MODE_PARAMS = [ + ({'desire': (1, 25, 8), 'features_buffer': (1, 25, 512), 'prev_desired_curv': (1, 25, 1)}, 'split'), + ({'desire': (1, 25, 8), 'features_buffer': (1, 24, 512), 'prev_desired_curv': (1, 25, 1)}, '20hz'), + ({'desire': (1, 100, 8), 'features_buffer': (1, 99, 512), 'prev_desired_curv': (1, 100, 1)}, 'non20hz'), +] + + +# This creates a dummy runner, override, and bundle instance for the tests to run, without actually trying to load a physical model. +class DummyOverride: + def __init__(self, key: str, value: str) -> None: + self.key = key + self.value = value + + +class DummyBundle: + def __init__(self) -> None: + self.overrides = [DummyOverride('lat', '.1'), DummyOverride('long', '.3')] + self.generation = 10 # default to non-mlsim for buffer-update tests, as raising to 11 here will zero curvature buffer + + +class DummyModelRunner: + def __init__(self, input_shapes: dict[str, tuple[int, int, int]], constants: Any = None) -> None: + self.input_shapes = input_shapes + self.constants = constants or type('C', (), { + 'FULL_HISTORY_BUFFER_LEN': 100, + 'FEATURE_LEN': 512, + 'DESIRE_LEN': 8, + 'PREV_DESIRED_CURV_LEN': 1, + 'INPUT_HISTORY_BUFFER_LEN': 25, + 'TEMPORAL_SKIP': 4, + })() + self.vision_input_names: list[str] = [] + shape = input_shapes.get('desire', (1, 0, 0)) # [batch, history, features] + if shape[1] == 25: + self.is_20hz = True + else: + self.is_20hz = False + + # Minimal prepare/run methods so ModelState can be run without actually running the model + def prepare_inputs(self, imgs_cl, numpy_inputs, frames): + return None + + def run_model(self): + return { + 'hidden_state': np.zeros((1, self.constants.FEATURE_LEN), dtype=np.float32), + 'desired_curvature': np.zeros((1, 1), dtype=np.float32), + } + + +@pytest.fixture +def shapes(request): + return request.param + + +@pytest.fixture +def bundle() -> DummyBundle: + return DummyBundle() + + +@pytest.fixture +def runner(shapes) -> DummyModelRunner: + return DummyModelRunner(shapes) + + +@pytest.fixture +def apply_patches(monkeypatch: pytest.MonkeyPatch, bundle: DummyBundle, runner: DummyModelRunner): + monkeypatch.setattr(helpers, 'get_active_bundle', lambda params=None: bundle, raising=False) + monkeypatch.setattr(runner_helpers, 'get_model_runner', lambda: runner, raising=False) + monkeypatch.setattr(modeld_module, 'get_model_runner', lambda: runner, raising=False) + monkeypatch.setattr(modeld_module, 'get_active_bundle', lambda params=None: bundle, raising=False) + + +# These are expected shapes and indices based on the time the model was presented +def get_expected_indices(shape, constants, mode, key=None): + if mode == 'split': + start = -1 - (constants.TEMPORAL_SKIP * (constants.INPUT_HISTORY_BUFFER_LEN - 1)) + arr = np.arange(constants.FULL_HISTORY_BUFFER_LEN) + idxs = arr[start::constants.TEMPORAL_SKIP] + return idxs + elif mode == '20hz': + num_elements = shape[1] + step_size = int(-100 / num_elements) + idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1] + return idxs + elif mode == 'non20hz': + if key and shape[1] == constants.FULL_HISTORY_BUFFER_LEN: + return np.arange(constants.FULL_HISTORY_BUFFER_LEN) + return None + return None + + +@pytest.mark.parametrize("shapes,mode", SHAPE_MODE_PARAMS, indirect=["shapes"]) +def test_buffer_shapes_and_indices(shapes, mode, apply_patches): + state = ModelState(None) + constants = DummyModelRunner(shapes).constants + for key in shapes: + buf = state.temporal_buffers.get(key, None) + idxs = state.temporal_idxs_map.get(key, None) + # Buffer shape logic + if mode == 'split': + expected_shape = (1, constants.FULL_HISTORY_BUFFER_LEN, shapes[key][2]) + expected_idxs = get_expected_indices(shapes[key], constants, 'split', key) + elif mode == '20hz': + expected_shape = (1, constants.FULL_HISTORY_BUFFER_LEN, shapes[key][2]) + expected_idxs = get_expected_indices(shapes[key], constants, '20hz', key) + elif mode == 'non20hz': + if key == 'features_buffer': + expected_shape = (1, shapes[key][1]*4, shapes[key][2]) + else: + expected_shape = (1, shapes[key][1], shapes[key][2]) + expected_idxs = get_expected_indices(shapes[key], constants, 'non20hz', key) + + assert buf is not None, f"{key}: buffer not found" + assert buf.shape == expected_shape, f"{key}: buffer shape {buf.shape} != expected {expected_shape}" + if expected_idxs is not None: + assert np.all(idxs == expected_idxs), f"{key}: buffer idxs {idxs} != expected {expected_idxs}" + else: + assert idxs is None or idxs.size == 0, f"{key}: buffer idxs should be None or empty" + + +def legacy_buffer_update(buf, new_val, mode, key, constants, idxs): + # This is what we compare the new dynamic logic to, to ensure it does the same thing + if mode == 'split': + if key == 'desire': + buf[0,:-1] = buf[0,1:] + buf[0,-1] = new_val + return buf.reshape((1, constants.INPUT_HISTORY_BUFFER_LEN, constants.TEMPORAL_SKIP, -1)).max(axis=2) + elif key == 'features_buffer': + buf[0,:-1] = buf[0,1:] + buf[0,-1] = new_val + return buf[0, idxs] + elif key == 'prev_desired_curv': + buf[0,:-1] = buf[0,1:] + buf[0,-1,:] = new_val + return buf[0, idxs] + elif mode == '20hz': + if key == 'desire': + buf[:-1] = buf[1:] + buf[-1] = new_val + reshape_dims = (1, buf.shape[1], -1, buf.shape[2]) + reshaped = buf.reshape(reshape_dims).max(axis=2) + # Slice to last shape[1] elements to match model input shape + input_len = reshaped.shape[1] + model_input_len = 25 # For 20hz mode, desire shape[1] is 25 + if input_len > model_input_len: + reshaped = reshaped[:, -model_input_len:, :] + return reshaped + elif key == 'features_buffer': + buffer_history_len = buf.shape[1] + legacy_buf = np.zeros((buffer_history_len, buf.shape[2]), dtype=np.float32) + legacy_buf[:] = buf[0] + legacy_buf[:-1] = legacy_buf[1:] + legacy_buf[-1] = new_val + return legacy_buf[idxs] + elif key == 'prev_desired_curv': + buffer_history_len = buf.shape[1] + legacy_buf = np.zeros((buffer_history_len, buf.shape[2]), dtype=np.float32) + legacy_buf[:] = buf[0] + legacy_buf[:-1] = legacy_buf[1:] + legacy_buf[-1,:] = new_val + return legacy_buf[idxs] + elif mode == 'non20hz': + if key == 'desire': + length = new_val.shape[0] + buf[0,:-1,:length] = buf[0,1:,:length] + buf[0,-1,:length] = new_val[:length] + return buf[0] + elif key == 'features_buffer': + feature_len = new_val.shape[0] + buf[0,:-1,:feature_len] = buf[0,1:,:feature_len] + buf[0,-1,:feature_len] = new_val[:feature_len] + return buf[0] + elif key == 'prev_desired_curv': + length = new_val.shape[0] + buf[0,:-length,0] = buf[0,length:,0] + buf[0,-length:,0] = new_val[:length] + return buf[0] + return None + + +def dynamic_buffer_update(state, key, new_val, mode): + if key == 'desire': + state.temporal_buffers['desire'][0,:-1] = state.temporal_buffers['desire'][0,1:] + state.temporal_buffers['desire'][0,-1] = new_val + if state.temporal_buffers['desire'].shape[1] > state.numpy_inputs['desire'].shape[1]: + skip = state.temporal_buffers['desire'].shape[1] // state.numpy_inputs['desire'].shape[1] + return state.temporal_buffers['desire'][0].reshape( + state.numpy_inputs['desire'].shape[0], state.numpy_inputs['desire'].shape[1], skip, -1 + ).max(axis=2) + else: + return state.temporal_buffers['desire'][0, state.temporal_idxs_map['desire']] + + inputs = {'desire': np.zeros((1, state.constants.DESIRE_LEN), dtype=np.float32)} + for k, tb in state.temporal_buffers.items(): + if k in state.temporal_idxs_map: + continue + buf_len = tb.shape[1] + if k in state.numpy_inputs: + out_len = state.numpy_inputs[k].shape[1] + if out_len <= buf_len: + state.temporal_idxs_map[k] = np.arange(buf_len)[-out_len:] + else: + state.temporal_idxs_map[k] = np.arange(buf_len) + else: + state.temporal_idxs_map[k] = np.arange(buf_len) + + if key == 'features_buffer': + def run_model_stub(): + return { + 'hidden_state': np.asarray(new_val, dtype=np.float32).reshape(1, -1), + } + state.model_runner.run_model = run_model_stub + state.run({}, {}, inputs, prepare_only=False) + return state.numpy_inputs['features_buffer'][0] + + if key == 'prev_desired_curv': + def run_model_stub(): + return { + 'hidden_state': np.zeros((1, state.constants.FEATURE_LEN), dtype=np.float32), + 'desired_curvature': np.asarray(new_val, dtype=np.float32).reshape(1, -1), + } + state.model_runner.run_model = run_model_stub + state.run({}, {}, inputs, prepare_only=False) + return state.numpy_inputs['prev_desired_curv'][0] + return None + + +@pytest.mark.parametrize("shapes,mode", SHAPE_MODE_PARAMS, indirect=["shapes"]) +@pytest.mark.parametrize("key", ["desire", "features_buffer", "prev_desired_curv"]) +def test_buffer_update_equivalence(shapes, mode, key, apply_patches): + state = ModelState(None) + constants = DummyModelRunner(shapes).constants + buf = state.temporal_buffers.get(key, None) + idxs = state.temporal_idxs_map.get(key, None) + input_shape = shapes[key] + for step in range(20): # multiple steps to ensure history is built up + new_val = np.full((input_shape[2],), step, dtype=np.float32) + expected = legacy_buffer_update(buf, new_val, mode, key, constants, idxs) + actual = dynamic_buffer_update(state, key, new_val, mode) + # Model returns the reduced numpy_inputs history, compare the last n entries so the test is checking the same slices. + if expected is not None and actual is not None and expected.shape != actual.shape: + if expected.ndim == 2 and actual.ndim == 2 and expected.shape[1] == actual.shape[1]: + expected = expected[-actual.shape[0]:] + assert np.allclose(actual, expected), f"{mode} {key}: dynamic buffer update does not match legacy logic" diff --git a/sunnypilot/models/runners/tinygrad/tinygrad_runner.py b/sunnypilot/models/runners/tinygrad/tinygrad_runner.py index 270890cace..2800179fb2 100644 --- a/sunnypilot/models/runners/tinygrad/tinygrad_runner.py +++ b/sunnypilot/models/runners/tinygrad/tinygrad_runner.py @@ -83,7 +83,7 @@ class TinygradRunner(ModelRunner, SupercomboTinygrad, PolicyTinygrad, VisionTiny def _run_model(self) -> NumpyDict: """Runs the Tinygrad model inference and parses the outputs.""" - outputs = self.model_run(**self.inputs).numpy().flatten() + outputs = self.model_run(**self.inputs).contiguous().realize().uop.base.buffer.numpy() return self._parse_outputs(outputs) def _parse_outputs(self, model_outputs: np.ndarray) -> NumpyDict: