From 3ef9552e744d8eb1c0f8b1d8aaa134fe9e12e20c Mon Sep 17 00:00:00 2001 From: Niko Kivel Date: Mon, 9 Jun 2025 13:16:30 -0600 Subject: [PATCH 1/6] update lock to work on latest macOS --- pixi.lock | 909 +++++++++++++++++++++++++++--------------------------- 1 file changed, 452 insertions(+), 457 deletions(-) diff --git a/pixi.lock b/pixi.lock index 9b1274e..7662e8d 100644 --- a/pixi.lock +++ b/pixi.lock @@ -10,164 +10,162 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.9.0-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/async-timeout-5.0.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2025.1.31-hbcca054_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.1.31-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.8-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.4.26-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.2.1-pyh707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.11-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.12-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-ws-0.7.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-31_h59b9bed_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-31_he106b2a_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h767d61c_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hf1ad2bd_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h767d61c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.1.0-h69a702a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.1.0-hcea5267_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.1.0-h767d61c_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-31_h7ac8fdf_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.4-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.29-pthreads_h94d23a6_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.1-hee588c1_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-h8f9b012_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.1-hee588c1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.50.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.3-py312h72c5963_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.1-h7b32b05_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.6-pyh3cfb1c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.2-py312h12e396e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.8.1-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.0-py312h6cf2f7f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.0-h7b32b05_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.4-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.33.2-py312h680f630_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.9.1-pyh3cfb1c2_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.9-h9e4cc4f_1_cpython.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.11-h9e4cc4f_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.1.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-7_cp312.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h178313f_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/redis-py-5.2.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/redis-py-6.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.0.0-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.46.1-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.2-pyhff008b6_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.2-pyh29332c3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.2-h801b22e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025a-h78e105d_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.46.2-pyh81abbef_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.16.0-pyh167b9f4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.16.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.16.0-hf964461_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.14.0-h32cad80_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.3-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.3-h31011fe_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.4-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.5-py312h12e396e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-15.0.1-py312h66e93f0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wsproto-1.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda - pypi: https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.8.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.9.0-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/async-timeout-5.0.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2025.1.31-hf0a4a13_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.1.31-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.8-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.4.26-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.2.1-pyh707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.11-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.12-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py313h90d716c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-ws-0.7.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-31_h10e41b3_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-31_hb3479ef_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.7-ha82da77_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-20.1.6-ha82da77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.0-h286801f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.6-h1da3d7d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-14_2_0_h6c33f7e_103.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-14.2.0-h6c33f7e_103.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-31_hc9a63f6_openblas.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.4-h39f12f2_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h99b78c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.29-openmp_hf332438_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.49.1-h3f77e49_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.50.0-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.50.1-h3f77e49_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h5505292_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.7-hdb05f8b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-20.1.6-hdb05f8b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py313ha9b7d5b_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.2.3-py313h41a2e72_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.1-h81ee809_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.6-pyh3cfb1c2_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.2-py313hdde674f_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.8.1-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.0-py313h41a2e72_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.5.0-h81ee809_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.4-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.33.2-py313hf3ab51e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.9.1-pyh3cfb1c2_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.2-h81fe080_101_cp313.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.3-h81fe080_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.1.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.13-5_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-7_cp313.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py313ha9b7d5b_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h1d1bf99_2.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/redis-py-5.2.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/redis-py-6.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.0.0-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.46.1-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.2-pyhff008b6_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.2-pyh29332c3_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.2-h801b22e_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025a-h78e105d_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.46.2-pyh81abbef_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.16.0-pyh167b9f4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.16.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.16.0-hf964461_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.14.0-h32cad80_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.3-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.3-h31011fe_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py313h63a2874_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.4-py313hdde674f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.5-py313hdde674f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-15.0.1-py313h90d716c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wsproto-1.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 - - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda - pypi: https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl packages: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 @@ -203,15 +201,16 @@ packages: - pkg:pypi/annotated-types?source=hash-mapping size: 18074 timestamp: 1733247158254 -- conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.8.0-pyhd8ed1ab_0.conda - sha256: f1455d2953e3eb6d71bc49881c8558d8e01888469dfd21061dd48afb6183e836 - md5: 848d25bfbadf020ee4d4ba90e5668252 +- conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.9.0-pyh29332c3_0.conda + sha256: b28e0f78bb0c7962630001e63af25a89224ff504e135a02e50d4d80b6155d386 + md5: 9749a2c77a7c40d432ea0927662d7e52 depends: - exceptiongroup >=1.0.2 - idna >=2.8 - python >=3.9 - sniffio >=1.1 - typing_extensions >=4.5 + - python constrains: - trio >=0.26.1 - uvloop >=0.21 @@ -219,8 +218,8 @@ packages: license_family: MIT purls: - pkg:pypi/anyio?source=hash-mapping - size: 115305 - timestamp: 1736174485476 + size: 126346 + timestamp: 1742243108743 - conda: https://conda.anaconda.org/conda-forge/noarch/async-timeout-5.0.1-pyhd8ed1ab_1.conda sha256: 33d12250c870e06c9a313c6663cfbf1c50380b73dfbbb6006688c3134b29b45a md5: 5d842988b11a8c3ab57fb70840c83d24 @@ -253,42 +252,37 @@ packages: purls: [] size: 122909 timestamp: 1720974522888 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2025.1.31-hbcca054_0.conda - sha256: bf832198976d559ab44d6cdb315642655547e26d826e34da67cbee6624cda189 - md5: 19f3a56f68d2fd06c516076bff482c52 - license: ISC - purls: [] - size: 158144 - timestamp: 1738298224464 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2025.1.31-hf0a4a13_0.conda - sha256: 7e12816618173fe70f5c638b72adf4bfd4ddabf27794369bb17871c5bb75b9f9 - md5: 3569d6a9141adc64d2fe4797f3289e06 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda + sha256: 2a70ed95ace8a3f8a29e6cd1476a943df294a7111dfb3e152e3478c4c889b7ac + md5: 95db94f75ba080a22eb623590993167b + depends: + - __unix license: ISC purls: [] - size: 158425 - timestamp: 1738298167688 -- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.1.31-pyhd8ed1ab_0.conda - sha256: 42a78446da06a2568cb13e69be3355169fbd0ea424b00fc80b7d840f5baaacf3 - md5: c207fa5ac7ea99b149344385a9c0880d + size: 152283 + timestamp: 1745653616541 +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.4.26-pyhd8ed1ab_0.conda + sha256: 52aa837642fd851b3f7ad3b1f66afc5366d133c1d452323f786b0378a391915c + md5: c33eeaaa33f45031be34cda513df39b6 depends: - python >=3.9 license: ISC purls: - - pkg:pypi/certifi?source=compressed-mapping - size: 162721 - timestamp: 1739515973129 -- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.8-pyh707e725_0.conda - sha256: c920d23cd1fcf565031c679adb62d848af60d6fbb0edc2d50ba475cea4f0d8ab - md5: f22f4d4970e09d68a10b922cbb0408d3 + - pkg:pypi/certifi?source=hash-mapping + size: 157200 + timestamp: 1746569627830 +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.2.1-pyh707e725_0.conda + sha256: 8aee789c82d8fdd997840c952a586db63c6890b00e88c4fb6e80a38edd5f51c0 + md5: 94b550b8d3a614dbd326af798c7dfb40 depends: - __unix - - python >=3.9 + - python >=3.10 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/click?source=hash-mapping - size: 84705 - timestamp: 1734858922844 + size: 87749 + timestamp: 1747811451319 - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda sha256: 3ec40ccf63f2450c5e6c7dd579e42fc2e97caf0d8cd4ba24aa434e6fc264eda0 md5: 5fbd60d61d21b4bd2f9d7a48fe100418 @@ -331,19 +325,20 @@ packages: purls: [] size: 6552 timestamp: 1733300828176 -- conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda - sha256: cbde2c64ec317118fc06b223c5fd87c8a680255e7348dd60e7b292d2e103e701 - md5: a16662747cdeb9abbac74d0057cc976e +- conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda + sha256: ce61f4f99401a4bd455b89909153b40b9c823276aefcbb06f2044618696009ca + md5: 72e42d28960d875c7654614f8b50939a depends: - python >=3.9 + - typing_extensions >=4.6.0 license: MIT and PSF-2.0 purls: - - pkg:pypi/exceptiongroup?source=hash-mapping - size: 20486 - timestamp: 1733208916977 -- conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.11-pyh29332c3_0.conda - sha256: 06b93154b2412fd55a235b44580db144334e90cabfec0e57c69155ce35f40962 - md5: a3085f0ade4096d261ecb15208fa5148 + - pkg:pypi/exceptiongroup?source=compressed-mapping + size: 21284 + timestamp: 1746947398083 +- conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.12-pyh29332c3_0.conda + sha256: d72da6ea523d80968f0cca4ba4fb6c31fc27450d07e419f039da9b99654a56e6 + md5: 4bc12ece07c8c717e19fd790bfec100d depends: - python >=3.9 - starlette >=0.40.0,<0.47.0 @@ -359,9 +354,9 @@ packages: license: MIT license_family: MIT purls: - - pkg:pypi/fastapi?source=hash-mapping - size: 78175 - timestamp: 1740930998506 + - pkg:pypi/fastapi?source=compressed-mapping + size: 78350 + timestamp: 1742822207154 - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda sha256: 300683731013b7221922339cd40430bb3c2ddeeb658fd7e37f5099ffe64e4db0 md5: d960e0ea9e1c561aa928f6c4439f04c7 @@ -376,9 +371,9 @@ packages: - pkg:pypi/fastapi-cli?source=hash-mapping size: 15546 timestamp: 1734302408607 -- conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.14.0-pyhd8ed1ab_1.conda - sha256: 622516185a7c740d5c7f27016d0c15b45782c1501e5611deec63fd70344ce7c8 - md5: 7ee49e89531c0dcbba9466f6d115d585 +- conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda + sha256: f64b68148c478c3bfc8f8d519541de7d2616bf59d44485a5271041d40c061887 + md5: 4b69232755285701bc86a5afe4d9933a depends: - python >=3.9 - typing_extensions @@ -386,8 +381,8 @@ packages: license_family: MIT purls: - pkg:pypi/h11?source=hash-mapping - size: 51846 - timestamp: 1733327599467 + size: 37697 + timestamp: 1745526482242 - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda sha256: 0aa1cdc67a9fe75ea95b5644b734a756200d6ec9d0dff66530aec3d1c1e9df75 md5: b4754fb1bdcb70c8fd54f918301582c6 @@ -412,22 +407,23 @@ packages: - pkg:pypi/hpack?source=hash-mapping size: 30731 timestamp: 1737618390337 -- conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.7-pyh29332c3_1.conda - sha256: c84d012a245171f3ed666a8bf9319580c269b7843ffa79f26468842da3abd5df - md5: 2ca8e6dbc86525c8b95e3c0ffa26442e +- conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + sha256: 04d49cb3c42714ce533a8553986e1642d0549a05dc5cc48e0d43ff5be6679a5b + md5: 4f14640d58e2cc0aa0819d9d8ba125bb depends: - - python >=3.8 - - h11 >=0.13,<0.15 + - python >=3.9 + - h11 >=0.16 - h2 >=3,<5 - sniffio 1.* - - anyio >=3.0,<5.0 + - anyio >=4.0,<5.0 - certifi + - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/httpcore?source=hash-mapping - size: 48959 - timestamp: 1731707562362 + size: 49483 + timestamp: 1745602916758 - conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda sha256: 621e7e050b888e5239d33e37ea72d6419f8367e5babcad38b755586f20264796 md5: 8b1160b32557290b64d5be68db3d996d @@ -508,18 +504,6 @@ packages: - pkg:pypi/idna?source=hash-mapping size: 49765 timestamp: 1733211921194 -- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda - sha256: 598951ebdb23e25e4cec4bbff0ae369cec65ead80b50bc08b441d8e54de5cf03 - md5: f4b39bf00c69f56ac01e020ebfac066c - depends: - - python >=3.9 - - zipp >=0.5 - license: Apache-2.0 - license_family: APACHE - purls: - - pkg:pypi/importlib-metadata?source=hash-mapping - size: 29141 - timestamp: 1737420302391 - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda sha256: f1ac18b11637ddadc05642e8185a851c7fab5998c6f5470d716812fae943b2af md5: 446bd6c8cb26050d528881df495ce646 @@ -610,141 +594,143 @@ packages: purls: [] size: 17032 timestamp: 1740088127097 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.7-ha82da77_0.conda - sha256: 776092346da87a2a23502e14d91eb0c32699c4a1522b7331537bd1c3751dcff5 - md5: 5b3e1610ff8bd5443476b91d618f5b77 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-20.1.6-ha82da77_0.conda + sha256: b74ec832ec05571f8747c9bd5f96b93d76489909b4f6f37d99d576dc955f21e9 + md5: 95c1830841844ef54e07efed1654b47f depends: - __osx >=11.0 license: Apache-2.0 WITH LLVM-exception license_family: Apache purls: [] - size: 523505 - timestamp: 1736877862502 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.4-h5888daf_0.conda - sha256: 56541b98447b58e52d824bd59d6382d609e11de1f8adf20b23143e353d2b8d26 - md5: db833e03127376d461e1e13e76f09b6c + size: 567539 + timestamp: 1748495055530 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda + sha256: 33ab03438aee65d6aa667cf7d90c91e5e7d734c19a67aa4c7040742c0a13d505 + md5: db0bfbe7dd197b68ad5f30333bae6ce0 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 constrains: - - expat 2.6.4.* + - expat 2.7.0.* license: MIT license_family: MIT purls: [] - size: 73304 - timestamp: 1730967041968 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda - sha256: e42ab5ace927ee7c84e3f0f7d813671e1cf3529f5f06ee5899606630498c2745 - md5: 38d2656dd914feb0cab8c629370768bf + size: 74427 + timestamp: 1743431794976 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.0-h286801f_0.conda + sha256: ee550e44765a7bbcb2a0216c063dcd53ac914a7be5386dd0554bd06e6be61840 + md5: 6934bbb74380e045741eb8637641a65b depends: - __osx >=11.0 constrains: - - expat 2.6.4.* + - expat 2.7.0.* license: MIT license_family: MIT purls: [] - size: 64693 - timestamp: 1730967175868 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_0.conda - sha256: 67a6c95e33ebc763c1adc3455b9a9ecde901850eb2fceb8e646cc05ef3a663da - md5: e3eb7806380bc8bcecba6d749ad5f026 + size: 65714 + timestamp: 1743431789879 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda + sha256: 764432d32db45466e87f10621db5b74363a9f847d2b8b1f9743746cd160f06ab + md5: ede4673863426c0883c0063d853bbd85 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: MIT license_family: MIT purls: [] - size: 53415 - timestamp: 1739260413716 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 - sha256: 41b3d13efb775e340e4dba549ab5c029611ea6918703096b2eaa9c015c0750ca - md5: 086914b672be056eb70fd4285b6783b6 + size: 57433 + timestamp: 1743434498161 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.6-h1da3d7d_1.conda + sha256: c6a530924a9b14e193ea9adfe92843de2a806d1b7dbfd341546ece9653129e60 + md5: c215a60c2935b517dcda8cad4705734d + depends: + - __osx >=11.0 license: MIT license_family: MIT purls: [] - size: 39020 - timestamp: 1636488587153 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h767d61c_2.conda - sha256: 3a572d031cb86deb541d15c1875aaa097baefc0c580b54dc61f5edab99215792 - md5: ef504d1acbd74b7cc6849ef8af47dd03 + size: 39839 + timestamp: 1743434670405 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_2.conda + sha256: 0024f9ab34c09629621aefd8603ef77bf9d708129b0dd79029e502c39ffc2195 + md5: ea8ac52380885ed41c1baa8f1d6d2b93 depends: - __glibc >=2.17,<3.0.a0 - _openmp_mutex >=4.5 constrains: - - libgomp 14.2.0 h767d61c_2 - - libgcc-ng ==14.2.0=*_2 + - libgcc-ng ==15.1.0=*_2 + - libgomp 15.1.0 h767d61c_2 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] - size: 847885 - timestamp: 1740240653082 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_2.conda - sha256: fb7558c328b38b2f9d2e412c48da7890e7721ba018d733ebdfea57280df01904 - md5: a2222a6ada71fb478682efe483ce0f92 + size: 829108 + timestamp: 1746642191935 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_2.conda + sha256: 0ab5421a89f090f3aa33841036bb3af4ed85e1f91315b528a9d75fab9aad51ae + md5: ddca86c7040dd0e73b2b69bd7833d225 depends: - - libgcc 14.2.0 h767d61c_2 + - libgcc 15.1.0 h767d61c_2 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] - size: 53758 - timestamp: 1740240660904 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_2.conda - sha256: e05263e8960da03c341650f2a3ffa4ccae4e111cb198e8933a2908125459e5a6 - md5: fb54c4ea68b460c278d26eea89cfbcc3 + size: 34586 + timestamp: 1746642200749 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.1.0-h69a702a_2.conda + sha256: 914daa4f632b786827ea71b5e07cd00d25fc6e67789db2f830dc481eec660342 + md5: f92e6e0a3c0c0c85561ef61aa59d555d depends: - - libgfortran5 14.2.0 hf1ad2bd_2 + - libgfortran5 15.1.0 hcea5267_2 constrains: - - libgfortran-ng ==14.2.0=*_2 + - libgfortran-ng ==15.1.0=*_2 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] - size: 53733 - timestamp: 1740240690977 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda - sha256: 44e541b4821c96b28b27fef5630883a60ce4fee91fd9c79f25a199f8f73f337b - md5: 4a55d9e169114b2b90d3ec4604cd7bbf + size: 34541 + timestamp: 1746642233221 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-14_2_0_h6c33f7e_103.conda + sha256: 8628746a8ecd311f1c0d14bb4f527c18686251538f7164982ccbe3b772de58b5 + md5: 044a210bc1d5b8367857755665157413 depends: - - libgfortran5 13.2.0 hf226fd6_3 + - libgfortran5 14.2.0 h6c33f7e_103 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] - size: 110233 - timestamp: 1707330749033 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hf1ad2bd_2.conda - sha256: c17b7cf3073a1f4e1f34d50872934fa326346e104d3c445abc1e62481ad6085c - md5: 556a4fdfac7287d349b8f09aba899693 + size: 156291 + timestamp: 1743863532821 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.1.0-hcea5267_2.conda + sha256: be23750f3ca1a5cb3ada858c4f633effe777487d1ea35fddca04c0965c073350 + md5: 01de444988ed960031dbe84cf4f9b1fc depends: - __glibc >=2.17,<3.0.a0 - - libgcc >=14.2.0 + - libgcc >=15.1.0 constrains: - - libgfortran 14.2.0 + - libgfortran 15.1.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] - size: 1461978 - timestamp: 1740240671964 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda - sha256: bafc679eedb468a86aa4636061c55966186399ee0a04b605920d208d97ac579a - md5: 66ac81d54e95c534ae488726c1f698ea + size: 1569986 + timestamp: 1746642212331 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-14.2.0-h6c33f7e_103.conda + sha256: 8599453990bd3a449013f5fa3d72302f1c68f0680622d419c3f751ff49f01f17 + md5: 69806c1e957069f1d515830dcc9f6cbb depends: - llvm-openmp >=8.0.0 constrains: - - libgfortran 5.0.0 13_2_0_*_3 + - libgfortran 5.0.0 14_2_0_*_103 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] - size: 997381 - timestamp: 1707330687590 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h767d61c_2.conda - sha256: 1a3130e0b9267e781b89399580f3163632d59fe5b0142900d63052ab1a53490e - md5: 06d02030237f4d5b3d9a7e7d348fe3c6 + size: 806566 + timestamp: 1743863491726 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.1.0-h767d61c_2.conda + sha256: 05fff3dc7e80579bc28de13b511baec281c4343d703c406aefd54389959154fb + md5: fbe7d535ff9d3a168c148e07358cd5b1 depends: - __glibc >=2.17,<3.0.a0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] - size: 459862 - timestamp: 1740240588123 + size: 452635 + timestamp: 1746642113092 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-31_h7ac8fdf_openblas.conda build_number: 31 sha256: f583661921456e798aba10972a8abbd9d33571c655c1f66eff450edc9cbefcf3 @@ -775,35 +761,39 @@ packages: purls: [] size: 17033 timestamp: 1740088134988 -- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.6.4-hb9d3cd8_0.conda - sha256: cad52e10319ca4585bc37f0bc7cce99ec7c15dc9168e42ccb96b741b0a27db3f - md5: 42d5b6a0f30d3c10cd88cb8584fda1cb +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + sha256: f2591c0069447bbe28d4d696b7fcb0c5bd0b4ac582769b89addbcf26fb3430d8 + md5: 1a580f7796c7bf6393fddb8bbbde58dc depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 + constrains: + - xz 5.8.1.* license: 0BSD purls: [] - size: 111357 - timestamp: 1738525339684 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.4-h39f12f2_0.conda - sha256: 560c59d3834cc652a84fb45531bd335ad06e271b34ebc216e380a89798fe8e2c - md5: e3fd1f8320a100f2b210e690a57cd615 + size: 112894 + timestamp: 1749230047870 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda + sha256: 0cb92a9e026e7bd4842f410a5c5c665c89b2eb97794ffddba519a626b8ce7285 + md5: d6df911d4564d77c4374b02552cb17d1 depends: - __osx >=11.0 + constrains: + - xz 5.8.1.* license: 0BSD purls: [] - size: 98945 - timestamp: 1738525462560 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h99b78c6_0.conda - sha256: f7917de9117d3a5fe12a39e185c7ce424f8d5010a6f97b4333e8a1dcb2889d16 - md5: 7476305c35dd9acef48da8f754eedb40 + size: 92286 + timestamp: 1749230283517 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda + sha256: 0a1875fc1642324ebd6c4ac864604f3f18f57fbcf558a8264f6ced028a3c75b2 + md5: 85ccccb47823dd9f7a99d2c7f530342f depends: - __osx >=11.0 license: BSD-2-Clause license_family: BSD purls: [] - size: 69263 - timestamp: 1723817629767 + size: 71829 + timestamp: 1748393749336 - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda sha256: 26d77a3bb4dceeedc2a41bd688564fe71bf2d149fdcf117049970bc02ff1add6 md5: 30fd6e37fe21f86f4bd26d6ee73eeec7 @@ -844,38 +834,38 @@ packages: purls: [] size: 4168442 timestamp: 1739825514918 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.1-hee588c1_1.conda - sha256: 7a09eef804ef7cf4d88215c2297eabb72af8ad0bd5b012060111c289f14bbe7d - md5: 73cea06049cc4174578b432320a003b8 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.1-hee588c1_0.conda + sha256: cd15ab1b9f0d53507e7ad7a01e52f6756ab3080bf623ab0e438973b6e4dba3c0 + md5: 96a7e36bff29f1d0ddf5b771e0da373a depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libzlib >=1.3.1,<2.0a0 license: Unlicense purls: [] - size: 915956 - timestamp: 1739953155793 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.49.1-h3f77e49_1.conda - sha256: 266639fb10ca92287961574b0b4d6031fa40dd9d723d64a0fcb08513a24dab03 - md5: c83357a21092bd952933c36c5cb4f4d6 + size: 919819 + timestamp: 1749232795476 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.50.1-h3f77e49_0.conda + sha256: f39e22a00396c048dcfcb5d8c9dbedb2d69f06edcd8dba98b87f263eeb6d2049 + md5: 73df23998b27dd6774d03db626d031d3 depends: - __osx >=11.0 - libzlib >=1.3.1,<2.0a0 license: Unlicense purls: [] - size: 898767 - timestamp: 1739953312379 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-h8f9b012_2.conda - sha256: 8f5bd92e4a24e1d35ba015c5252e8f818898478cb3bc50bd8b12ab54707dc4da - md5: a78c856b6dc6bf4ea8daeb9beaaa3fb0 + size: 901258 + timestamp: 1749232800279 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_2.conda + sha256: 6ae3d153e78f6069d503d9309f2cac6de5b93d067fc6433160a4c05226a5dad4 + md5: 1cb1c67961f6dd257eae9e9691b341aa depends: - __glibc >=2.17,<3.0.a0 - - libgcc 14.2.0 h767d61c_2 + - libgcc 15.1.0 h767d61c_2 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] - size: 3884556 - timestamp: 1740240685253 + size: 3902355 + timestamp: 1746642227493 - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda sha256: 787eb542f055a2b3de553614b25f09eefb0a0931b0c87dbcce6efdfd92f04f18 md5: 40b61aab5c7ba9ff276c41cfffe6b80b @@ -886,27 +876,27 @@ packages: purls: [] size: 33601 timestamp: 1680112270483 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.50.0-hb9d3cd8_0.conda - sha256: b4a8890023902aef9f1f33e3e35603ad9c2f16c21fdb58e968fa6c1bd3e94c0b - md5: 771ee65e13bc599b0b62af5359d80169 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb9d3cd8_0.conda + sha256: 770ca175d64323976c9fe4303042126b2b01c1bd54c8c96cafeaba81bdb481b8 + md5: 1349c022c92c5efd3fd705a79a5804d8 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: MIT license_family: MIT purls: [] - size: 891272 - timestamp: 1737016632446 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.50.0-h5505292_0.conda - sha256: d13fb49d4c8262bf2c44ffb2c77bb2b5d0f85fc6de76bdb75208efeccb29fce6 - md5: 20717343fb30798ab7c23c2e92b748c1 + size: 890145 + timestamp: 1748304699136 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h5505292_0.conda + sha256: 41c1230a3f4e0d265e5053c671f112a16be4405b9047d3da5581e03e9d53de65 + md5: 230a885fe67a3e945a4586b944b6020a depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] - size: 418890 - timestamp: 1737016751326 + size: 420654 + timestamp: 1748304893204 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c md5: 5aa797f8787fe7a17d1b0821485b5adc @@ -941,18 +931,18 @@ packages: purls: [] size: 46438 timestamp: 1727963202283 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.7-hdb05f8b_0.conda - sha256: b92a669f2059874ebdcb69041b6c243d68ffc3fb356ac1339cec44aeb27245d7 - md5: c4d54bfd3817313ce758aa76283b118d +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-20.1.6-hdb05f8b_0.conda + sha256: 99c8aa89a77870d6ee16d62b858be67e30f2ad4fe13555570c7660cc38f9557b + md5: 7a3b28d59940a28e761e0a623241a832 depends: - __osx >=11.0 constrains: - - openmp 19.1.7|19.1.7.* + - openmp 20.1.6|20.1.6.* license: Apache-2.0 WITH LLVM-exception license_family: APACHE purls: [] - size: 280830 - timestamp: 1736986295869 + size: 282698 + timestamp: 1748570308073 - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda sha256: 0fbacdfb31e55964152b24d5567e9a9996e1e7902fb08eb7d91b5fd6ce60803a md5: fee3164ac23dfca50cfcc8b85ddefb81 @@ -1037,9 +1027,9 @@ packages: purls: [] size: 797030 timestamp: 1738196177597 -- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.3-py312h72c5963_0.conda - sha256: e05f689807e11a11871b9072b7443d6f2bf2bae3871d5258d787c6e182a7eaec - md5: d117e16d71afa469ea26c3d6896291da +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.0-py312h6cf2f7f_0.conda + sha256: 59da92a150737e830c75e8de56c149d6dc4e42c9d38ba30d2f0d4787a0c43342 + md5: 8b4095ed29d1072f7e4badfbaf9e5851 depends: - __glibc >=2.17,<3.0.a0 - libblas >=3.9.0,<4.0a0 @@ -1055,11 +1045,11 @@ packages: license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping - size: 8458232 - timestamp: 1739426128240 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.2.3-py313h41a2e72_0.conda - sha256: a60c012a3b87e5e4cb002a41aa164b8f0e6cd8a35d63eadf0b025c259129a580 - md5: 67d7e7f829d06baf1a53dd27b0e8b01d + size: 8417476 + timestamp: 1749430957684 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.0-py313h41a2e72_0.conda + sha256: d473005786a27cf4e1430d45a99a61626c2fbf61eb25b4d021cee8d217b973d2 + md5: 0dc3aa075f3e64bdda6e779e2cbf5aa9 depends: - __osx >=11.0 - libblas >=3.9.0,<4.0a0 @@ -1074,12 +1064,12 @@ packages: license: BSD-3-Clause license_family: BSD purls: - - pkg:pypi/numpy?source=hash-mapping - size: 6533531 - timestamp: 1739426241828 -- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.4.1-h7b32b05_0.conda - sha256: cbf62df3c79a5c2d113247ddea5658e9ff3697b6e741c210656e239ecaf1768f - md5: 41adf927e746dc75ecf0ef841c454e48 + - pkg:pypi/numpy?source=compressed-mapping + size: 6525213 + timestamp: 1749430964570 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.0-h7b32b05_1.conda + sha256: b4491077c494dbf0b5eaa6d87738c22f2154e9277e5293175ec187634bd808a0 + md5: de356753cfdbffcde5bb1e86e3aa6cd0 depends: - __glibc >=2.17,<3.0.a0 - ca-certificates @@ -1087,81 +1077,83 @@ packages: license: Apache-2.0 license_family: Apache purls: [] - size: 2939306 - timestamp: 1739301879343 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.1-h81ee809_0.conda - sha256: 4f8e2389e1b711b44182a075516d02c80fa7a3a7e25a71ff1b5ace9eae57a17a - md5: 75f9f0c7b1740017e2db83a53ab9a28e + size: 3117410 + timestamp: 1746223723843 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.5.0-h81ee809_1.conda + sha256: 73d366c1597a10bcd5f3604b5f0734b31c23225536e03782c6a13f9be9d01bff + md5: 5c7aef00ef60738a14e0e612cfc5bcde depends: - __osx >=11.0 - ca-certificates license: Apache-2.0 license_family: Apache purls: [] - size: 2934522 - timestamp: 1739301896733 -- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.10.6-pyh3cfb1c2_0.conda - sha256: 9a78801a28959edeb945e8270a4e666577b52fac0cf4e35f88cf122f73d83e75 - md5: c69f87041cf24dfc8cb6bf64ca7133c7 + size: 3064197 + timestamp: 1746223530698 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.4-pyh3cfb1c2_0.conda + sha256: a522473505ac6a9c10bb304d7338459a406ba22a6d3bb1a355c1b5283553a372 + md5: 8ad3ad8db5ce2ba470c9facc37af00a9 depends: - annotated-types >=0.6.0 - - pydantic-core 2.27.2 + - pydantic-core 2.33.2 - python >=3.9 - typing-extensions >=4.6.1 + - typing-inspection >=0.4.0 - typing_extensions >=4.12.2 license: MIT license_family: MIT purls: - - pkg:pypi/pydantic?source=hash-mapping - size: 296841 - timestamp: 1737761472006 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.27.2-py312h12e396e_0.conda - sha256: 81602a4592ad2ac1a1cb57372fd25214e63b1c477d5818b0c21cde0f1f85c001 - md5: bae01b2563030c085f5158c518b84e86 + - pkg:pypi/pydantic?source=compressed-mapping + size: 306304 + timestamp: 1746632069456 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.33.2-py312h680f630_0.conda + sha256: 4d14d7634c8f351ff1e63d733f6bb15cba9a0ec77e468b0de9102014a4ddc103 + md5: cfbd96e5a0182dfb4110fc42dda63e57 depends: + - python + - typing-extensions >=4.6.0,!=4.7.0 - __glibc >=2.17,<3.0.a0 - libgcc >=13 - - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - - typing-extensions >=4.6.0,!=4.7.0 constrains: - __glibc >=2.17 license: MIT license_family: MIT purls: - pkg:pypi/pydantic-core?source=hash-mapping - size: 1641402 - timestamp: 1734571789895 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.27.2-py313hdde674f_0.conda - sha256: e59662375a64c65cdbf51d0457e6d4b45c27854b5915355ec9c789c140bf9464 - md5: 3b2f1a63943ac3c2ac6da3c8e777d78c + size: 1890081 + timestamp: 1746625309715 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.33.2-py313hf3ab51e_0.conda + sha256: a70d31e04b81df4c98821668d87089279284d2dbcc70413f791eaa60b28f42fd + md5: 0d5685f410c4234af909cde6fac63cb0 depends: + - python + - typing-extensions >=4.6.0,!=4.7.0 + - python 3.13.* *_cp313 - __osx >=11.0 - - python >=3.13,<3.14.0a0 - - python >=3.13,<3.14.0a0 *_cp313 - python_abi 3.13.* *_cp313 - - typing-extensions >=4.6.0,!=4.7.0 constrains: - __osx >=11.0 license: MIT license_family: MIT purls: - pkg:pypi/pydantic-core?source=hash-mapping - size: 1595692 - timestamp: 1734572306619 -- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.8.1-pyh3cfb1c2_0.conda - sha256: 84b78dcdc75d7dacd8c85df9a7fef42ff5684897217b46beef6c516afb2550dc - md5: 88715188749bfac9fa92aec9c747d62c + size: 1720344 + timestamp: 1746625313921 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.9.1-pyh3cfb1c2_0.conda + sha256: ea2f1027218e83e484fd581933e0ce60b9194486c56c98053b4277b0fb291364 + md5: 29dd5c4ece2497b75b4050ec3c8d4044 depends: - pydantic >=2.7.0 - python >=3.9 - python-dotenv >=0.21.0 + - typing-inspection >=0.4.0 license: MIT license_family: MIT purls: - - pkg:pypi/pydantic-settings?source=compressed-mapping - size: 32632 - timestamp: 1740672054181 + - pkg:pypi/pydantic-settings?source=hash-mapping + size: 38135 + timestamp: 1745015303766 - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda sha256: 28a3e3161390a9d23bc02b4419448f8d27679d9e2c250e29849e37749c8de86b md5: 232fb4577b6687b2d503ef8e254270c9 @@ -1173,25 +1165,24 @@ packages: - pkg:pypi/pygments?source=hash-mapping size: 888600 timestamp: 1736243563082 -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.9-h9e4cc4f_1_cpython.conda - build_number: 1 - sha256: 77f2073889d4c91a57bc0da73a0466d9164dbcf6191ea9c3a7be6872f784d625 - md5: d82342192dfc9145185190e651065aa9 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.11-h9e4cc4f_0_cpython.conda + sha256: 6cca004806ceceea9585d4d655059e951152fc774a471593d4f5138e6a54c81d + md5: 94206474a5608243a10c92cefbe0908f depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-64 >=2.36.1 - - libexpat >=2.6.4,<3.0a0 - - libffi >=3.4,<4.0a0 + - libexpat >=2.7.0,<3.0a0 + - libffi >=3.4.6,<3.5.0a0 - libgcc >=13 - - liblzma >=5.6.4,<6.0a0 + - liblzma >=5.8.1,<6.0a0 - libnsl >=2.0.1,<2.1.0a0 - - libsqlite >=3.49.1,<4.0a0 + - libsqlite >=3.50.0,<4.0a0 - libuuid >=2.38.1,<3.0a0 - libxcrypt >=4.4.36 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - - openssl >=3.4.1,<4.0a0 + - openssl >=3.5.0,<4.0a0 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata @@ -1199,43 +1190,44 @@ packages: - python_abi 3.12.* *_cp312 license: Python-2.0 purls: [] - size: 31670716 - timestamp: 1741130026152 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.2-h81fe080_101_cp313.conda + size: 31445023 + timestamp: 1749050216615 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.3-h81fe080_101_cp313.conda build_number: 101 - sha256: 6239a14c39a9902d6b617d57efe3eefbab23cf30cdc67122fdab81d04da193cd - md5: 71a76067a1cac1a2f03b43a08646a63e + sha256: f96468ab1e6f27bda92157bfc7f272d1fbf2ba2f85697bdc5bb106bccba1befb + md5: b3240ae8c42a3230e0b7f831e1c72e9f depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - - libexpat >=2.6.4,<3.0a0 - - libffi >=3.4,<4.0a0 - - liblzma >=5.6.4,<6.0a0 + - libexpat >=2.7.0,<3.0a0 + - libffi >=3.4.6,<3.5.0a0 + - liblzma >=5.8.1,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - - libsqlite >=3.48.0,<4.0a0 + - libsqlite >=3.49.1,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - - openssl >=3.4.1,<4.0a0 + - openssl >=3.5.0,<4.0a0 - python_abi 3.13.* *_cp313 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata license: Python-2.0 purls: [] - size: 11682568 - timestamp: 1739801342527 + size: 12136505 + timestamp: 1744663807953 python_site_packages_path: lib/python3.13/site-packages -- conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.0.1-pyhd8ed1ab_1.conda - sha256: 99713f6b534fef94995c6c16fd21d59f3548784e9111775d692bdc7c44678f02 - md5: e5c6ed218664802d305e79cc2d4491de +- conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.1.0-pyh29332c3_1.conda + sha256: 7d927317003544049c97e7108e8ca5f2be5ff0ea954f5c84c8bbeb243b663fc8 + md5: 27d816c6981a8d50090537b761de80f4 depends: - python >=3.9 + - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/python-dotenv?source=hash-mapping - size: 24215 - timestamp: 1733243277223 + size: 25557 + timestamp: 1742948348635 - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda sha256: 1b03678d145b1675b757cba165a0d9803885807792f7eb4495e48a38858c3cca md5: a28c984e0429aff3ab7386f7de56de6f @@ -1247,28 +1239,28 @@ packages: - pkg:pypi/python-multipart?source=hash-mapping size: 27913 timestamp: 1734420869885 -- conda: https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda - build_number: 5 - sha256: d10e93d759931ffb6372b45d65ff34d95c6000c61a07e298d162a3bc2accebb0 - md5: 0424ae29b104430108f5218a66db7260 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-7_cp312.conda + build_number: 7 + sha256: a1bbced35e0df66cc713105344263570e835625c28d1bdee8f748f482b2d7793 + md5: 0dfcdc155cf23812a0c9deada86fb723 constrains: - python 3.12.* *_cpython license: BSD-3-Clause license_family: BSD purls: [] - size: 6238 - timestamp: 1723823388266 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.13-5_cp313.conda - build_number: 5 - sha256: 4437198eae80310f40b23ae2f8a9e0a7e5c2b9ae411a8621eb03d87273666199 - md5: b8e82d0a5c1664638f87f63cc5d241fb + size: 6971 + timestamp: 1745258861359 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-7_cp313.conda + build_number: 7 + sha256: 0595134584589064f56e67d3de1d8fcbb673a972946bce25fb593fb092fdcd97 + md5: e84b44e6300f1703cb25d29120c5b1d8 constrains: - python 3.13.* *_cp313 license: BSD-3-Clause license_family: BSD purls: [] - size: 6322 - timestamp: 1723823058879 + size: 6988 + timestamp: 1745258852285 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h178313f_2.conda sha256: 159cba13a93b3fe084a1eb9bda0a07afc9148147647f0d437c3c3da60980503b md5: cf2485f39740de96e2a7f2bb18ed2fee @@ -1320,34 +1312,33 @@ packages: purls: [] size: 252359 timestamp: 1740379663071 -- conda: https://conda.anaconda.org/conda-forge/noarch/redis-py-5.2.1-pyhd8ed1ab_1.conda - sha256: f56ca8804bfc214f2dec5bb4386d75112ad1121d0a0dd31fb5aca3d6c9660fb7 - md5: c7b2e4fccd34b5d3ec19cf2febec7e48 +- conda: https://conda.anaconda.org/conda-forge/noarch/redis-py-6.2.0-pyhd8ed1ab_0.conda + sha256: b48458cc79b4b4232a0c2cfbc2adeab3d8bba1a6964061069f0de3cc83fd9da3 + md5: ecfe17a73de08148d2d388f5c686ba3f depends: - async-timeout >=4.0.3 - - importlib-metadata >=1.0 - python >=3.9 - - typing-extensions license: MIT license_family: MIT purls: - pkg:pypi/redis?source=hash-mapping - size: 179621 - timestamp: 1733605038857 -- conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_1.conda - sha256: 06a760c5ae572e72e865d5a87e9fe3cc171e1a9c996e63daf3db52ff1a0b4457 - md5: 7aed65d4ff222bfb7335997aa40b7da5 + size: 186669 + timestamp: 1748446619157 +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.0.0-pyh29332c3_0.conda + sha256: d10e2b66a557ec6296844e04686db87818b0df87d73c06388f2332fda3f7d2d5 + md5: 202f08242192ce3ed8bdb439ba40c0fe depends: - markdown-it-py >=2.2.0 - pygments >=2.13.0,<3.0.0 - python >=3.9 - typing_extensions >=4.0.0,<5.0.0 + - python license: MIT license_family: MIT purls: - pkg:pypi/rich?source=hash-mapping - size: 185646 - timestamp: 1733342347277 + size: 200323 + timestamp: 1743371105291 - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda sha256: e558f8c254a9ff9164d069110da162fc79497d70c60f2c09a5d3d0d7101c5628 md5: 4ba15ae9388b67d09782798347481f69 @@ -1385,115 +1376,130 @@ packages: - pkg:pypi/sniffio?source=hash-mapping size: 15019 timestamp: 1733244175724 -- conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.46.1-pyha770c72_0.conda - sha256: 821c9e23e9dffaa1519faac109c60f02ebccc6436efd58ea60b85dd7b7f6e9ec - md5: db2f992eed837d11aed1dab97af9e408 +- conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.46.2-pyh81abbef_0.conda + sha256: d41b9b2719a2a0176930df21d7fec7b758058e7fafd53dc900b5706cd627fa3a + md5: 36ec80c2b37e52760ab41be7c2bd1fd3 depends: - anyio >=3.6.2,<5 - python >=3.9 - typing_extensions >=3.10.0 + - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/starlette?source=hash-mapping - size: 58328 - timestamp: 1741450719441 -- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda - sha256: e0569c9caa68bf476bead1bed3d79650bb080b532c64a4af7d8ca286c08dea4e - md5: d453b98d9c83e71da0741bb0ff4d76bc + size: 62335 + timestamp: 1744661396275 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda + sha256: a84ff687119e6d8752346d1d408d5cf360dee0badd487a472aa8ddedfdc219e1 + md5: a0116df4f4ed05c303811a837d5b39d8 depends: - - libgcc-ng >=12 - - libzlib >=1.2.13,<2.0.0a0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 license: TCL license_family: BSD purls: [] - size: 3318875 - timestamp: 1699202167581 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda - sha256: 72457ad031b4c048e5891f3f6cb27a53cb479db68a52d965f796910e71a403a8 - md5: b50a57ba89c32b62428b71a875291c9b + size: 3285204 + timestamp: 1748387766691 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_2.conda + sha256: cb86c522576fa95c6db4c878849af0bccfd3264daf0cc40dd18e7f4a7bfced0e + md5: 7362396c170252e7b7b0c8fb37fe9c78 depends: - - libzlib >=1.2.13,<2.0.0a0 + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 license: TCL license_family: BSD purls: [] - size: 3145523 - timestamp: 1699202432999 -- conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.15.2-pyhff008b6_0.conda - sha256: fa6eeb42e3bddff74126dd61b01b21a3f4f4791368e93bc5a5775563542b2d4e - md5: 1152565b06e3dc27794c3c11f1050005 + size: 3125538 + timestamp: 1748388189063 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.16.0-pyh167b9f4_0.conda + sha256: 1ca70f0c0188598f9425a947afb74914a068bee4b7c4586eabb1c3b02fbf669f + md5: 985cc086b73bda52b2f8d66dcda460a1 depends: - - typer-slim-standard ==0.15.2 h801b22e_0 + - typer-slim-standard ==0.16.0 hf964461_0 - python >=3.9 - python license: MIT license_family: MIT purls: - - pkg:pypi/typer?source=hash-mapping - size: 76158 - timestamp: 1740697495168 -- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.15.2-pyh29332c3_0.conda - sha256: c094713560bfacab0539c863010a5223171d9980cbd419cc799e474ae15aca08 - md5: 7c8d9609e2cfe08dd7672e10fe7e7de9 + - pkg:pypi/typer?source=compressed-mapping + size: 77232 + timestamp: 1748304246569 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.16.0-pyhe01879c_0.conda + sha256: 54f859ddf5d3216fb602f54990c3ccefc65a30d1d98c400b998e520310630df3 + md5: 0d0a6c08daccb968c8c8fa93070658e2 depends: - python >=3.9 - click >=8.0.0 - typing_extensions >=3.7.4.3 - python constrains: - - typer 0.15.2.* + - typer 0.16.0.* - rich >=10.11.0 - shellingham >=1.3.0 license: MIT license_family: MIT purls: - - pkg:pypi/typer-slim?source=hash-mapping - size: 45866 - timestamp: 1740697495167 -- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.15.2-h801b22e_0.conda - sha256: 79b6b34e90e50e041908939d53053f69285714b0082a0370fba6ab3b38315c8d - md5: ea164fc4e03f61f7ff3c1166001969af - depends: - - typer-slim ==0.15.2 pyh29332c3_0 + - pkg:pypi/typer-slim?source=compressed-mapping + size: 46798 + timestamp: 1748304246569 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.16.0-hf964461_0.conda + sha256: c35a0b232e9751ac871b733d4236eee887f64c3b1539ba86aecf175c3ac3dc02 + md5: c8fb6ddb4f5eb567d049f85b3f0c8019 + depends: + - typer-slim ==0.16.0 pyhe01879c_0 - rich - shellingham license: MIT license_family: MIT purls: [] - size: 5409 - timestamp: 1740697495168 -- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.12.2-hd8ed1ab_1.conda - noarch: python - sha256: c8e9c1c467b5f960b627d7adc1c65fece8e929a3de89967e91ef0f726422fd32 - md5: b6a408c64b78ec7b779a3e5c7a902433 - depends: - - typing_extensions 4.12.2 pyha770c72_1 + size: 5271 + timestamp: 1748304246569 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.14.0-h32cad80_0.conda + sha256: b8cabfa54432b0f124c0af6b6facdf8110892914fa841ac2e80ab65ac52c1ba4 + md5: a1cdd40fc962e2f7944bc19e01c7e584 + depends: + - typing_extensions ==4.14.0 pyhe01879c_0 license: PSF-2.0 license_family: PSF purls: [] - size: 10075 - timestamp: 1733188758872 -- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_1.conda - sha256: 337be7af5af8b2817f115b3b68870208b30c31d3439bec07bfb2d8f4823e3568 - md5: d17f13df8b65464ca316cbc000a3cb64 + size: 90310 + timestamp: 1748959427551 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.1-pyhd8ed1ab_0.conda + sha256: 4259a7502aea516c762ca8f3b8291b0d4114e094bdb3baae3171ccc0900e722f + md5: e0c3cd765dc15751ee2f0b03cd015712 + depends: + - python >=3.9 + - typing_extensions >=4.12.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/typing-inspection?source=compressed-mapping + size: 18809 + timestamp: 1747870776989 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.0-pyhe01879c_0.conda + sha256: 8561db52f278c5716b436da6d4ee5521712a49e8f3c70fcae5350f5ebb4be41c + md5: 2adcd9bb86f656d3d43bf84af59a1faf depends: - python >=3.9 + - python license: PSF-2.0 license_family: PSF purls: - - pkg:pypi/typing-extensions?source=hash-mapping - size: 39637 - timestamp: 1733188758212 -- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025a-h78e105d_0.conda - sha256: c4b1ae8a2931fe9b274c44af29c5475a85b37693999f8c792dad0f8c6734b1de - md5: dbcace4706afdfb7eb891f7b37d07c04 + - pkg:pypi/typing-extensions?source=compressed-mapping + size: 50978 + timestamp: 1748959427551 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + sha256: 5aaa366385d716557e365f0a4e9c3fca43ba196872abbbe3d56bb610d131e192 + md5: 4222072737ccff51314b5ece9c7d6f5a license: LicenseRef-Public-Domain purls: [] - size: 122921 - timestamp: 1737119101255 -- conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.0-pyh31011fe_0.conda - sha256: 55c160b0cf9274e2b98bc0f7fcce548bffa8d788bc86aa02801877457040f6fa - md5: 5d448feee86e4740498ec8f8eb40e052 + size: 122968 + timestamp: 1742727099393 +- conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.3-pyh31011fe_0.conda + sha256: 4edebc7b6b96ebf92db8b5488c4b39594982eab79db44b267d8a3502e12b051b + md5: 1520c1396715d45d02f5aa045854a65c depends: - __unix - click >=7.0 @@ -1503,26 +1509,26 @@ packages: license: BSD-3-Clause license_family: BSD purls: - - pkg:pypi/uvicorn?source=hash-mapping - size: 48643 - timestamp: 1734293057914 -- conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.0-h31011fe_0.conda - sha256: 87e1531e175e75122f9f37608eb953af4c977465ab0ae11283cc01fef954e4ec - md5: 32a94143a7f65d76d2d5da37dcb4ed79 + - pkg:pypi/uvicorn?source=compressed-mapping + size: 49042 + timestamp: 1748779747595 +- conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.3-h31011fe_0.conda + sha256: 7de45d9cc878424f65b6ae936f35314e8abc031b60c464258c0a7ef65778c548 + md5: 6d80b382cafd45723e75dccef6496c67 depends: - __unix - httptools >=0.6.3 - python-dotenv >=0.13 - pyyaml >=5.1 - - uvicorn 0.34.0 pyh31011fe_0 + - uvicorn 0.34.3 pyh31011fe_0 - uvloop >=0.14.0,!=0.15.0,!=0.15.1 - watchfiles >=0.13 - websockets >=10.4 license: BSD-3-Clause license_family: BSD purls: [] - size: 7203 - timestamp: 1734293058849 + size: 7613 + timestamp: 1748779748439 - conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda sha256: 9337a80165fcf70b06b9d6ba920dad702260ca966419ae77560a15540e41ab72 md5: 998e481e17c1b6a74572e73b06f2df08 @@ -1551,9 +1557,9 @@ packages: - pkg:pypi/uvloop?source=hash-mapping size: 543651 timestamp: 1730214744398 -- conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.4-py312h12e396e_0.conda - sha256: b728f525dcae2c10524f9942255346eba62aee9c820ff269d7dd4f7caffb7ffb - md5: df87129c4cb7afc4a3cbad71a1b9e223 +- conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.5-py312h12e396e_0.conda + sha256: b97b961190e2f0d4bc4e26d6a58cce847aa353a292f1fec194e9ecca25793101 + md5: 731f9ed51e7198ec91c4b9d7899e881c depends: - __glibc >=2.17,<3.0.a0 - anyio >=3.0.0 @@ -1566,11 +1572,11 @@ packages: license_family: MIT purls: - pkg:pypi/watchfiles?source=hash-mapping - size: 410192 - timestamp: 1736550568524 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.4-py313hdde674f_0.conda - sha256: 1665894163e5c264ea69e3c5c33040bac10be8daafdbc58440600e791e8490fd - md5: aa44bc87687836c24c48cb32cb936acf + size: 421844 + timestamp: 1744451587768 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.5-py313hdde674f_0.conda + sha256: ccd64c90954a9f11b7681969e2c5fc6f0a35811ec7a88a5958fcdd2da2a0117d + md5: 0da7f4997a02d0d281ce13af23a0cf2b depends: - __osx >=11.0 - anyio >=3.0.0 @@ -1583,8 +1589,8 @@ packages: license_family: MIT purls: - pkg:pypi/watchfiles?source=hash-mapping - size: 364347 - timestamp: 1736551041302 + size: 367527 + timestamp: 1744451856378 - conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-15.0.1-py312h66e93f0_0.conda sha256: d55c82992553720a4c2f49d383ce8260a4ce1fa39df0125edb71f78ff2ee3682 md5: b986da7551224417af6b7da4021d8050 @@ -1643,14 +1649,3 @@ packages: purls: [] size: 88016 timestamp: 1641347076660 -- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda - sha256: 567c04f124525c97a096b65769834b7acb047db24b15a56888a322bf3966c3e1 - md5: 0c3cc595284c5e8f0f9900a9b228a332 - depends: - - python >=3.9 - license: MIT - license_family: MIT - purls: - - pkg:pypi/zipp?source=hash-mapping - size: 21809 - timestamp: 1732827613585 From 7c95c5a9cbd9fc8a3a9c86e8e5b3386f0df4045f Mon Sep 17 00:00:00 2001 From: Niko Kivel Date: Mon, 9 Jun 2025 13:31:53 -0600 Subject: [PATCH 2/6] deps and env for nats --- pixi.lock | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ pixi.toml | 7 +++ 2 files changed, 187 insertions(+) diff --git a/pixi.lock b/pixi.lock index 7662e8d..939f14f 100644 --- a/pixi.lock +++ b/pixi.lock @@ -167,6 +167,175 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/wsproto-1.2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 - pypi: https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl + nats: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.9.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/async-timeout-5.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.4.26-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.2.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.12-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.6.4-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-ws-0.7.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-31_h59b9bed_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-31_he106b2a_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.1.0-h69a702a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.1.0-hcea5267_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.1.0-h767d61c_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-31_h7ac8fdf_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.29-pthreads_h94d23a6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.1-hee588c1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nats-py-2.10.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.0-py312h6cf2f7f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.0-h7b32b05_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.4-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.33.2-py312h680f630_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.9.1-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.11-h9e4cc4f_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.1.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-7_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h178313f_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/redis-py-6.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.0.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.46.2-pyh81abbef_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.16.0-pyh167b9f4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.16.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.16.0-hf964461_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.14.0-h32cad80_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.3-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.3-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.21.0-py312h66e93f0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.0.5-py312h12e396e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-15.0.1-py312h66e93f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wsproto-1.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2 + - pypi: https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.9.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/async-timeout-5.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.4.26-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.2.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.7.0-pyhff2d567_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.2.0-hd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.115.12-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.6.4-py313h90d716c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-ws-0.7.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-31_h10e41b3_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-31_hb3479ef_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-20.1.6-ha82da77_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.0-h286801f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.6-h1da3d7d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-14_2_0_h6c33f7e_103.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-14.2.0-h6c33f7e_103.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-31_hc9a63f6_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.29-openmp_hf332438_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.50.1-h3f77e49_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-20.1.6-hdb05f8b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.2-py313ha9b7d5b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/nats-py-2.10.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.0-py313h41a2e72_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.5.0-h81ee809_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.11.4-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.33.2-py313hf3ab51e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.9.1-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.3-h81fe080_101_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.1.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-7_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.2-py313ha9b7d5b_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h1d1bf99_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/redis-py-6.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.0.0-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.11.3-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.46.2-pyh81abbef_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.16.0-pyh167b9f4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.16.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.16.0-hf964461_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.14.0-h32cad80_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.0-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.34.3-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.34.3-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.21.0-py313h63a2874_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.0.5-py313hdde674f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-15.0.1-py313h90d716c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wsproto-1.2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h3422bc3_2.tar.bz2 + - pypi: https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl packages: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 @@ -1008,6 +1177,17 @@ packages: version: 1.1.0 sha256: 17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39 requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/nats-py-2.10.0-pyhd8ed1ab_0.conda + sha256: 27b355b7993bb528d5cf86ee3604dc743bc36d917da3ccd5c6e1a271deab3694 + md5: f869c9cb826f4cc8995cf5093438cd1e + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/nats-py?source=hash-mapping + size: 66706 + timestamp: 1742902120175 - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586 md5: 47e340acb35de30501a76c7c799c41d7 diff --git a/pixi.toml b/pixi.toml index 6564aaf..eb80351 100644 --- a/pixi.toml +++ b/pixi.toml @@ -7,6 +7,7 @@ version = "0.1.0" [tasks] serve = "python server.py" +serve-nats = "python server_nats.py" stream = "python streaming_client.py" write = "python writing_client.py" @@ -22,3 +23,9 @@ httpx-ws = "*" [pypi-dependencies] msgpack = "*" + +[feature.nats_kv.dependencies] +nats-py = "*" + +[environments] +nats = ["nats_kv"] From c829382007c7e2023b717f3af10b63c13c188cce Mon Sep 17 00:00:00 2001 From: Niko Kivel Date: Mon, 9 Jun 2025 13:38:47 -0600 Subject: [PATCH 3/6] NATS kv, quick and dirty --- README.md | 24 ++++++ server_nats.py | 228 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 server_nats.py diff --git a/README.md b/README.md index 37d9e60..3c365a2 100644 --- a/README.md +++ b/README.md @@ -40,3 +40,27 @@ To verify load balancing is working, run this command to see requests distribute ```sh for i in {1..20}; do curl -s -D - http://localhost:8000/stream/live 2>/dev/null | grep X-Server-Host; done | sort ``` + +## NATS + +Aka the better MQ, because it also has kv, and ... object store :D + +### NATS server with jetstream + +```shell +docker run -d --name nats-js -p 4222:4222 -p 8222:8222 nats:latest -js +``` + +### usage + +Use the same writer and stream clients as with the Redis example. + +Note: This is done quick and dirty and is a pure proof of concept! +It's failing hard and there are more uncovered edge cases than actually working cases. + +Follow these steps: + +1. start the NATS kv +2. start the "server" --> `pixi run -e nats serve-nats` +3. `pixi run write` +4. `pixi run stream` diff --git a/server_nats.py b/server_nats.py new file mode 100644 index 0000000..3fd9859 --- /dev/null +++ b/server_nats.py @@ -0,0 +1,228 @@ +import json +import numpy as np +import uvicorn +from pydantic_settings import BaseSettings +from fastapi import FastAPI, WebSocket, Request, WebSocketDisconnect +from pydantic import BaseModel +from datetime import datetime +import msgpack +import asyncio +from typing import Optional +import socket + +from nats.aio.client import Client as NATS +from nats.js.kv import KeyValue +from nats.js.errors import KeyNotFoundError + +class Settings(BaseSettings): + nats_url: str = "nats://localhost:4222" + kv_bucket: str = "datasets" + ttl: int = 60 * 60 # 1 hour + +async def get_nats_kv(settings: Settings): + nc = NATS() + await nc.connect(servers=[settings.nats_url]) + js = nc.jetstream() + try: + kv = await js.key_value(settings.kv_bucket) + except Exception: + # Create the bucket if it doesn't exist + await js.create_key_value(bucket=settings.kv_bucket, history=1, ttl=settings.ttl) + kv = await js.key_value(settings.kv_bucket) + return nc, kv + +def build_app(settings: Settings): + app = FastAPI() + nats_ready = asyncio.Event() + nats_state = {} + + @app.on_event("startup") + async def startup_event(): + nc, kv = await get_nats_kv(settings) + nats_state["nc"] = nc + nats_state["kv"] = kv + nats_ready.set() + + @app.on_event("shutdown") + async def shutdown_event(): + nc = nats_state.get("nc") + if nc: + await nc.close() + + @app.middleware("http") + async def add_server_header(request: Request, call_next): + response = await call_next(request) + response.headers["X-Server-Host"] = socket.gethostname() + return response + + @app.post("/upload") + async def create(): + await nats_ready.wait() + kv: KeyValue = nats_state["kv"] + # Generate a random node_id. + # (In Tiled, PostgreSQL will give us a unique ID.) + node_id = np.random.randint(1_000_000) + await kv.create(f"seq_num:{node_id}", b"0") + return {"node_id": node_id} + + @app.delete("/upload/{node_id}", status_code=204) + async def close(node_id): + await nats_ready.wait() + kv: KeyValue = nats_state["kv"] + await kv.delete(f"seq_num:{node_id}") + return None + + @app.post("/upload/{node_id}") + async def append(node_id, request: Request): + await nats_ready.wait() + kv: KeyValue = nats_state["kv"] + binary_data = await request.body() + headers = request.headers + metadata = { + "timestamp": datetime.now().isoformat(), + } + metadata.setdefault("Content-Type", headers.get("Content-Type")) + + # Increment the counter for this node. + seq_key = f"seq_num:{node_id}" + try: + seq_num = int((await kv.get(seq_key)).value.decode()) + except KeyNotFoundError: + seq_num = 0 # If the key doesn't exist, start from 0 + seq_num += 1 + await kv.put(seq_key, str(seq_num).encode()) + + # Store data in KV + data_key = f"data:{node_id}:{seq_num}" + value = { + "metadata": metadata, + "payload": binary_data.hex(), # Store as hex string + } + await kv.put(data_key, json.dumps(value).encode()) + + # Publish notification + nc = nats_state["nc"] + await nc.publish(f"notify.{node_id}", str(seq_num).encode()) + + @app.post("/close/{node_id}") + async def close_connection(node_id: str, request: Request): + await nats_ready.wait() + kv: KeyValue = nats_state["kv"] + nc = nats_state["nc"] + + body = await request.json() + headers = request.headers + reason = body.get("reason", None) + metadata = { + "timestamp": datetime.now().isoformat(), + "reason": reason + } + metadata.setdefault("Content-Type", headers.get("Content-Type")) + + seq_key = f"seq_num:{node_id}" + seq_num = int((await kv.get(seq_key)).value.decode()) + seq_num += 1 + await kv.put(seq_key, str(seq_num).encode()) + + data_key = f"data:{node_id}:{seq_num}" + value = { + "metadata": metadata, + "payload": None, + } + await kv.put(data_key, json.dumps(value).encode()) + await nc.publish(f"notify.{node_id}", str(seq_num).encode()) + + return { + "status": f"Connection for node {node_id} is now closed.", + "reason": reason + } + + @app.websocket("/stream/single/{node_id}") + async def websocket_endpoint(websocket: WebSocket, + node_id: str, + envelope_format: str = "json", + seq_num: Optional[int] = None): + await nats_ready.wait() + kv: KeyValue = nats_state["kv"] + nc = nats_state["nc"] + + await websocket.accept(headers=[ + (b"x-server-host", socket.gethostname().encode()) + ]) + end_stream = asyncio.Event() + + async def stream_data(seq_num): + key = f"data:{node_id}:{seq_num}" + try: + entry = await kv.get(key) + value = json.loads(entry.value.decode()) + metadata = value["metadata"] + payload = value["payload"] + if payload is not None: + try: + payload = np.frombuffer(bytes.fromhex(payload), dtype=np.float64).tolist() + except Exception: + payload = json.loads(payload) + data = { + "sequence": seq_num, + "metadata": metadata, + "payload": payload, + "server_host": socket.gethostname() + } + if envelope_format == "msgpack": + await websocket.send_bytes(msgpack.packb(data)) + else: + await websocket.send_text(json.dumps(data)) + if payload is None and metadata is not None: + end_stream.set() + except Exception: + return + + stream_buffer = asyncio.Queue() + + async def buffer_live_events(): + sub = await nc.subscribe(f"notify.{node_id}") + try: + async for msg in sub.messages: + try: + live_seq = int(msg.data.decode()) + await stream_buffer.put(live_seq) + except Exception as e: + print(f"Error parsing live message: {e}") + except Exception as e: + print(f"Live subscription error: {e}") + finally: + await sub.unsubscribe() + + live_task = asyncio.create_task(buffer_live_events()) + + if seq_num is not None: + current_seq = int((await kv.get(f"seq_num:{node_id}")).value.decode()) + for s in range(seq_num, current_seq + 1): + await stream_data(s) + try: + while not end_stream.is_set(): + live_seq = await stream_buffer.get() + await stream_data(live_seq) + else: + await websocket.close(code=1000, reason="Producer ended stream") + except WebSocketDisconnect: + print(f"Client disconnected from node {node_id}") + finally: + live_task.cancel() + + @app.get("/stream/live") + async def list_live_streams(): + await nats_ready.wait() + kv: KeyValue = nats_state["kv"] + keys = await kv.keys() + nodes = [key.split(":")[1] for key in keys if key.startswith("seq_num:")] + return nodes + + return app + +settings = Settings() +app = build_app(settings) + +if __name__ == "__main__": + uvicorn.run(app) \ No newline at end of file From 7c6a4ec839cdd8780a5003cd27ad28610e74da68 Mon Sep 17 00:00:00 2001 From: Niko Kivel Date: Mon, 9 Jun 2025 13:44:46 -0600 Subject: [PATCH 4/6] dot notation for key name separator to make the nats cli work --- server_nats.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server_nats.py b/server_nats.py index 3fd9859..4808a1a 100644 --- a/server_nats.py +++ b/server_nats.py @@ -62,14 +62,14 @@ async def create(): # Generate a random node_id. # (In Tiled, PostgreSQL will give us a unique ID.) node_id = np.random.randint(1_000_000) - await kv.create(f"seq_num:{node_id}", b"0") + await kv.create(f"seq_num.{node_id}", b"0") return {"node_id": node_id} @app.delete("/upload/{node_id}", status_code=204) async def close(node_id): await nats_ready.wait() kv: KeyValue = nats_state["kv"] - await kv.delete(f"seq_num:{node_id}") + await kv.delete(f"seq_num.{node_id}") return None @app.post("/upload/{node_id}") @@ -84,7 +84,7 @@ async def append(node_id, request: Request): metadata.setdefault("Content-Type", headers.get("Content-Type")) # Increment the counter for this node. - seq_key = f"seq_num:{node_id}" + seq_key = f"seq_num.{node_id}" try: seq_num = int((await kv.get(seq_key)).value.decode()) except KeyNotFoundError: @@ -93,7 +93,7 @@ async def append(node_id, request: Request): await kv.put(seq_key, str(seq_num).encode()) # Store data in KV - data_key = f"data:{node_id}:{seq_num}" + data_key = f"data.{node_id}.{seq_num}" value = { "metadata": metadata, "payload": binary_data.hex(), # Store as hex string @@ -119,7 +119,7 @@ async def close_connection(node_id: str, request: Request): } metadata.setdefault("Content-Type", headers.get("Content-Type")) - seq_key = f"seq_num:{node_id}" + seq_key = f"seq_num.{node_id}" seq_num = int((await kv.get(seq_key)).value.decode()) seq_num += 1 await kv.put(seq_key, str(seq_num).encode()) @@ -197,7 +197,7 @@ async def buffer_live_events(): live_task = asyncio.create_task(buffer_live_events()) if seq_num is not None: - current_seq = int((await kv.get(f"seq_num:{node_id}")).value.decode()) + current_seq = int((await kv.get(f"seq_num.{node_id}")).value.decode()) for s in range(seq_num, current_seq + 1): await stream_data(s) try: @@ -216,7 +216,7 @@ async def list_live_streams(): await nats_ready.wait() kv: KeyValue = nats_state["kv"] keys = await kv.keys() - nodes = [key.split(":")[1] for key in keys if key.startswith("seq_num:")] + nodes = [key.split(".")[1] for key in keys if key.startswith("seq_num.")] return nodes return app From d8139e9e36271e5cfae6dcfb3997342342afacd7 Mon Sep 17 00:00:00 2001 From: Niko Kivel Date: Mon, 9 Jun 2025 14:06:55 -0600 Subject: [PATCH 5/6] go version --- golang/.gitignore | 1 + golang/Makefile | 52 +++++++++ golang/go.mod | 38 +++++++ golang/go.sum | 89 +++++++++++++++ golang/server_nats.go | 244 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 424 insertions(+) create mode 100644 golang/.gitignore create mode 100644 golang/Makefile create mode 100644 golang/go.mod create mode 100644 golang/go.sum create mode 100644 golang/server_nats.go diff --git a/golang/.gitignore b/golang/.gitignore new file mode 100644 index 0000000..bbde675 --- /dev/null +++ b/golang/.gitignore @@ -0,0 +1 @@ +product \ No newline at end of file diff --git a/golang/Makefile b/golang/Makefile new file mode 100644 index 0000000..4110aad --- /dev/null +++ b/golang/Makefile @@ -0,0 +1,52 @@ +# Define the binary name +BINARY_NAME=nats-ws + +# Define the artifact path with a default value +ARTIFACT_PATH ?= ./product + +# Define the full binary path +BINARY_PATH=$(ARTIFACT_PATH)/$(BINARY_NAME) + +# Ensure the artifact path exists +$(ARTIFACT_PATH): + mkdir -p $(ARTIFACT_PATH) + +# Define the main package path, should really be main.go with proper package structure +MAIN_PACKAGE=./server_nats.go + +# Default target +all: darwin-arm64 linux + +# Helper function to build for a specific OS and architecture +define build + GOOS=$(1) GOARCH=$(2) go build $(LDFLAGS) -o $(BINARY_PATH)-$(1)-$(2) $(MAIN_PACKAGE) +endef + +# Build for Linux (Intel) +linux: $(ARTIFACT_PATH) + $(call build,linux,amd64) + +# Build for Linux (ARM) +linux-arm64: $(ARTIFACT_PATH) + $(call build,linux,arm64) + +# Build for macOS (Intel) +darwin: $(ARTIFACT_PATH) + $(call build,darwin,amd64) + +# Build for macOS (Apple Silicon) +darwin-arm64: $(ARTIFACT_PATH) + $(call build,darwin,arm64) + +# Build for Windows (64-bit) +windows: $(ARTIFACT_PATH) + $(call build,windows,amd64) + +# Clean up build artifacts +clean: + rm -rf $(ARTIFACT_PATH) + +tidy: + go mod tidy + +.PHONY: all linux darwin darwin-arm64 windows clean tidy diff --git a/golang/go.mod b/golang/go.mod new file mode 100644 index 0000000..f30e973 --- /dev/null +++ b/golang/go.mod @@ -0,0 +1,38 @@ +module nats-ws + +go 1.24.3 + +require ( + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.10.1 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nats-io/nats.go v1.43.0 // indirect + github.com/nats-io/nkeys v0.4.11 // indirect + github.com/nats-io/nuid v1.0.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.32.0 // indirect + golang.org/x/text v0.24.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/golang/go.sum b/golang/go.sum new file mode 100644 index 0000000..fc52190 --- /dev/null +++ b/golang/go.sum @@ -0,0 +1,89 @@ +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/nats-io/nats.go v1.43.0 h1:uRFZ2FEoRvP64+UUhaTokyS18XBCR/xM2vQZKO4i8ug= +github.com/nats-io/nats.go v1.43.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g= +github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0= +github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/golang/server_nats.go b/golang/server_nats.go new file mode 100644 index 0000000..87a1574 --- /dev/null +++ b/golang/server_nats.go @@ -0,0 +1,244 @@ +package main + +import ( + "context" + "encoding/hex" + "encoding/json" + "fmt" + "math/rand" + "net/http" + "os" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "github.com/nats-io/nats.go" +) + +type Settings struct { + NatsURL string + KvBucket string + TTL time.Duration +} + +type AppState struct { + Nc *nats.Conn + Js nats.JetStreamContext + Kv nats.KeyValue +} + +type Metadata struct { + Timestamp string `json:"timestamp"` + ContentType string `json:"Content-Type,omitempty"` + Reason string `json:"reason,omitempty"` +} + +type DataValue struct { + Metadata Metadata `json:"metadata"` + Payload string `json:"payload"` // hex string +} + +var upgrader = websocket.Upgrader{} + +func main() { + settings := Settings{ + NatsURL: "nats://localhost:4222", + KvBucket: "datasets", + TTL: time.Hour, + } + + nc, err := nats.Connect(settings.NatsURL) + if err != nil { + panic(err) + } + js, err := nc.JetStream() + if err != nil { + panic(err) + } + kv, err := js.KeyValue(settings.KvBucket) + if err != nil { + _, err = js.CreateKeyValue(&nats.KeyValueConfig{ + Bucket: settings.KvBucket, + History: 1, + TTL: settings.TTL, + }) + if err != nil { + panic(err) + } + kv, err = js.KeyValue(settings.KvBucket) + if err != nil { + panic(err) + } + } + + state := &AppState{Nc: nc, Js: js, Kv: kv} + + r := gin.Default() + + r.Use(func(c *gin.Context) { + c.Writer.Header().Set("X-Server-Host", hostname()) + c.Next() + }) + + r.POST("/upload", func(c *gin.Context) { + nodeID := rand.Intn(1_000_000) + seqKey := fmt.Sprintf("seq_num.%d", nodeID) + _, err := state.Kv.Create(seqKey, []byte("0")) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"node_id": nodeID}) + }) + + r.DELETE("/upload/:node_id", func(c *gin.Context) { + nodeID := c.Param("node_id") + seqKey := fmt.Sprintf("seq_num.%s", nodeID) + err := state.Kv.Delete(seqKey) + if err != nil { + c.Status(http.StatusInternalServerError) + return + } + c.Status(http.StatusNoContent) + }) + + r.POST("/upload/:node_id", func(c *gin.Context) { + nodeID := c.Param("node_id") + seqKey := fmt.Sprintf("seq_num.%s", nodeID) + entry, err := state.Kv.Get(seqKey) + seqNum := 0 + if err == nil { + seqNum, _ = strconv.Atoi(string(entry.Value())) + } + seqNum++ + state.Kv.Put(seqKey, []byte(strconv.Itoa(seqNum))) + + binaryData, _ := c.GetRawData() + metadata := Metadata{ + Timestamp: time.Now().Format(time.RFC3339), + ContentType: c.GetHeader("Content-Type"), + } + dataKey := fmt.Sprintf("data.%s.%d", nodeID, seqNum) + value := DataValue{ + Metadata: metadata, + Payload: hex.EncodeToString(binaryData), + } + valBytes, _ := json.Marshal(value) + state.Kv.Put(dataKey, valBytes) + state.Nc.Publish(fmt.Sprintf("notify.%s", nodeID), []byte(strconv.Itoa(seqNum))) + c.Status(http.StatusOK) + }) + + r.POST("/close/:node_id", func(c *gin.Context) { + nodeID := c.Param("node_id") + seqKey := fmt.Sprintf("seq_num.%s", nodeID) + entry, err := state.Kv.Get(seqKey) + seqNum := 0 + if err == nil { + seqNum, _ = strconv.Atoi(string(entry.Value())) + } + seqNum++ + state.Kv.Put(seqKey, []byte(strconv.Itoa(seqNum))) + + var body map[string]interface{} + c.BindJSON(&body) + reason := "" + if r, ok := body["reason"].(string); ok { + reason = r + } + metadata := Metadata{ + Timestamp: time.Now().Format(time.RFC3339), + ContentType: c.GetHeader("Content-Type"), + Reason: reason, + } + dataKey := fmt.Sprintf("data.%s.%d", nodeID, seqNum) + value := DataValue{ + Metadata: metadata, + Payload: "", + } + valBytes, _ := json.Marshal(value) + state.Kv.Put(dataKey, valBytes) + state.Nc.Publish(fmt.Sprintf("notify.%s", nodeID), []byte(strconv.Itoa(seqNum))) + c.JSON(http.StatusOK, gin.H{ + "status": fmt.Sprintf("Connection for node %s is now closed.", nodeID), + "reason": reason, + }) + }) + + r.GET("/stream/live", func(c *gin.Context) { + keys, _ := state.Kv.Keys() + nodes := []string{} + for _, key := range keys { + if strings.HasPrefix(key, "seq_num.") { + parts := strings.Split(key, ".") + if len(parts) > 1 { + nodes = append(nodes, parts[1]) + } + } + } + c.JSON(http.StatusOK, nodes) + }) + + r.GET("/stream/single/:node_id", func(c *gin.Context) { + nodeID := c.Param("node_id") + conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + return + } + defer conn.Close() + + seqKey := fmt.Sprintf("seq_num.%s", nodeID) + entry, err := state.Kv.Get(seqKey) + currentSeq := 0 + if err == nil { + currentSeq, _ = strconv.Atoi(string(entry.Value())) + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + sub, _ := state.Nc.SubscribeSync(fmt.Sprintf("notify.%s", nodeID)) + defer sub.Unsubscribe() + + // Replay old data + for s := 1; s <= currentSeq; s++ { + sendDataOverWS(state, conn, nodeID, s) + } + + // Listen for new data + for { + msg, err := sub.NextMsgWithContext(ctx) + if err != nil { + break + } + seq, _ := strconv.Atoi(string(msg.Data)) + sendDataOverWS(state, conn, nodeID, seq) + } + }) + + r.Run(":8000") +} + +func sendDataOverWS(state *AppState, conn *websocket.Conn, nodeID string, seqNum int) { + dataKey := fmt.Sprintf("data.%s.%d", nodeID, seqNum) + entry, err := state.Kv.Get(dataKey) + if err != nil { + return + } + var value DataValue + json.Unmarshal(entry.Value(), &value) + resp := map[string]interface{}{ + "sequence": seqNum, + "metadata": value.Metadata, + "payload": value.Payload, + "server_host": hostname(), + } + conn.WriteJSON(resp) +} + +func hostname() string { + h, _ := os.Hostname() + return h +} \ No newline at end of file From 1f2d0ca404c642ca3e0ff1673f503c65a1bafd96 Mon Sep 17 00:00:00 2001 From: Niko Kivel Date: Mon, 9 Jun 2025 14:28:10 -0600 Subject: [PATCH 6/6] msgpacking in golang --- golang/go.mod | 2 + golang/go.sum | 4 + golang/server_nats.go | 475 +++++++++++++++++++++++------------------- 3 files changed, 262 insertions(+), 219 deletions(-) diff --git a/golang/go.mod b/golang/go.mod index f30e973..d5e9136 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -28,6 +28,8 @@ require ( github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.37.0 // indirect golang.org/x/net v0.25.0 // indirect diff --git a/golang/go.sum b/golang/go.sum index fc52190..f1963eb 100644 --- a/golang/go.sum +++ b/golang/go.sum @@ -66,6 +66,10 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= diff --git a/golang/server_nats.go b/golang/server_nats.go index 87a1574..f315d6a 100644 --- a/golang/server_nats.go +++ b/golang/server_nats.go @@ -1,244 +1,281 @@ package main import ( - "context" - "encoding/hex" - "encoding/json" - "fmt" - "math/rand" - "net/http" - "os" - "strconv" - "strings" - "time" - - "github.com/gin-gonic/gin" - "github.com/gorilla/websocket" - "github.com/nats-io/nats.go" + "context" + "encoding/binary" + "encoding/hex" + "encoding/json" + "fmt" + "math" + "math/rand" + "net/http" + "os" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "github.com/nats-io/nats.go" + "github.com/vmihailenco/msgpack/v5" ) type Settings struct { - NatsURL string - KvBucket string - TTL time.Duration + NatsURL string + KvBucket string + TTL time.Duration } type AppState struct { - Nc *nats.Conn - Js nats.JetStreamContext - Kv nats.KeyValue + Nc *nats.Conn + Js nats.JetStreamContext + Kv nats.KeyValue } type Metadata struct { - Timestamp string `json:"timestamp"` - ContentType string `json:"Content-Type,omitempty"` - Reason string `json:"reason,omitempty"` + Timestamp string `json:"timestamp"` + ContentType string `json:"Content-Type,omitempty"` + Reason string `json:"reason,omitempty"` } type DataValue struct { - Metadata Metadata `json:"metadata"` - Payload string `json:"payload"` // hex string + Metadata Metadata `json:"metadata"` + Payload string `json:"payload"` // hex string } var upgrader = websocket.Upgrader{} func main() { - settings := Settings{ - NatsURL: "nats://localhost:4222", - KvBucket: "datasets", - TTL: time.Hour, - } - - nc, err := nats.Connect(settings.NatsURL) - if err != nil { - panic(err) - } - js, err := nc.JetStream() - if err != nil { - panic(err) - } - kv, err := js.KeyValue(settings.KvBucket) - if err != nil { - _, err = js.CreateKeyValue(&nats.KeyValueConfig{ - Bucket: settings.KvBucket, - History: 1, - TTL: settings.TTL, - }) - if err != nil { - panic(err) - } - kv, err = js.KeyValue(settings.KvBucket) - if err != nil { - panic(err) - } - } - - state := &AppState{Nc: nc, Js: js, Kv: kv} - - r := gin.Default() - - r.Use(func(c *gin.Context) { - c.Writer.Header().Set("X-Server-Host", hostname()) - c.Next() - }) - - r.POST("/upload", func(c *gin.Context) { - nodeID := rand.Intn(1_000_000) - seqKey := fmt.Sprintf("seq_num.%d", nodeID) - _, err := state.Kv.Create(seqKey, []byte("0")) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - c.JSON(http.StatusOK, gin.H{"node_id": nodeID}) - }) - - r.DELETE("/upload/:node_id", func(c *gin.Context) { - nodeID := c.Param("node_id") - seqKey := fmt.Sprintf("seq_num.%s", nodeID) - err := state.Kv.Delete(seqKey) - if err != nil { - c.Status(http.StatusInternalServerError) - return - } - c.Status(http.StatusNoContent) - }) - - r.POST("/upload/:node_id", func(c *gin.Context) { - nodeID := c.Param("node_id") - seqKey := fmt.Sprintf("seq_num.%s", nodeID) - entry, err := state.Kv.Get(seqKey) - seqNum := 0 - if err == nil { - seqNum, _ = strconv.Atoi(string(entry.Value())) - } - seqNum++ - state.Kv.Put(seqKey, []byte(strconv.Itoa(seqNum))) - - binaryData, _ := c.GetRawData() - metadata := Metadata{ - Timestamp: time.Now().Format(time.RFC3339), - ContentType: c.GetHeader("Content-Type"), - } - dataKey := fmt.Sprintf("data.%s.%d", nodeID, seqNum) - value := DataValue{ - Metadata: metadata, - Payload: hex.EncodeToString(binaryData), - } - valBytes, _ := json.Marshal(value) - state.Kv.Put(dataKey, valBytes) - state.Nc.Publish(fmt.Sprintf("notify.%s", nodeID), []byte(strconv.Itoa(seqNum))) - c.Status(http.StatusOK) - }) - - r.POST("/close/:node_id", func(c *gin.Context) { - nodeID := c.Param("node_id") - seqKey := fmt.Sprintf("seq_num.%s", nodeID) - entry, err := state.Kv.Get(seqKey) - seqNum := 0 - if err == nil { - seqNum, _ = strconv.Atoi(string(entry.Value())) - } - seqNum++ - state.Kv.Put(seqKey, []byte(strconv.Itoa(seqNum))) - - var body map[string]interface{} - c.BindJSON(&body) - reason := "" - if r, ok := body["reason"].(string); ok { - reason = r - } - metadata := Metadata{ - Timestamp: time.Now().Format(time.RFC3339), - ContentType: c.GetHeader("Content-Type"), - Reason: reason, - } - dataKey := fmt.Sprintf("data.%s.%d", nodeID, seqNum) - value := DataValue{ - Metadata: metadata, - Payload: "", - } - valBytes, _ := json.Marshal(value) - state.Kv.Put(dataKey, valBytes) - state.Nc.Publish(fmt.Sprintf("notify.%s", nodeID), []byte(strconv.Itoa(seqNum))) - c.JSON(http.StatusOK, gin.H{ - "status": fmt.Sprintf("Connection for node %s is now closed.", nodeID), - "reason": reason, - }) - }) - - r.GET("/stream/live", func(c *gin.Context) { - keys, _ := state.Kv.Keys() - nodes := []string{} - for _, key := range keys { - if strings.HasPrefix(key, "seq_num.") { - parts := strings.Split(key, ".") - if len(parts) > 1 { - nodes = append(nodes, parts[1]) - } - } - } - c.JSON(http.StatusOK, nodes) - }) - - r.GET("/stream/single/:node_id", func(c *gin.Context) { - nodeID := c.Param("node_id") - conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) - if err != nil { - return - } - defer conn.Close() - - seqKey := fmt.Sprintf("seq_num.%s", nodeID) - entry, err := state.Kv.Get(seqKey) - currentSeq := 0 - if err == nil { - currentSeq, _ = strconv.Atoi(string(entry.Value())) - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - sub, _ := state.Nc.SubscribeSync(fmt.Sprintf("notify.%s", nodeID)) - defer sub.Unsubscribe() - - // Replay old data - for s := 1; s <= currentSeq; s++ { - sendDataOverWS(state, conn, nodeID, s) - } - - // Listen for new data - for { - msg, err := sub.NextMsgWithContext(ctx) - if err != nil { - break - } - seq, _ := strconv.Atoi(string(msg.Data)) - sendDataOverWS(state, conn, nodeID, seq) - } - }) - - r.Run(":8000") + settings := Settings{ + NatsURL: "nats://localhost:4222", + KvBucket: "datasets", + TTL: time.Hour, + } + + nc, err := nats.Connect(settings.NatsURL) + if err != nil { + panic(err) + } + js, err := nc.JetStream() + if err != nil { + panic(err) + } + kv, err := js.KeyValue(settings.KvBucket) + if err != nil { + _, err = js.CreateKeyValue(&nats.KeyValueConfig{ + Bucket: settings.KvBucket, + History: 1, + TTL: settings.TTL, + }) + if err != nil { + panic(err) + } + kv, err = js.KeyValue(settings.KvBucket) + if err != nil { + panic(err) + } + } + + state := &AppState{Nc: nc, Js: js, Kv: kv} + + r := gin.Default() + + r.Use(func(c *gin.Context) { + c.Writer.Header().Set("X-Server-Host", hostname()) + c.Next() + }) + + r.POST("/upload", func(c *gin.Context) { + nodeID := rand.Intn(1_000_000) + seqKey := fmt.Sprintf("seq_num.%d", nodeID) + _, err := state.Kv.Create(seqKey, []byte("0")) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"node_id": nodeID}) + }) + + r.DELETE("/upload/:node_id", func(c *gin.Context) { + nodeID := c.Param("node_id") + seqKey := fmt.Sprintf("seq_num.%s", nodeID) + err := state.Kv.Delete(seqKey) + if err != nil { + c.Status(http.StatusInternalServerError) + return + } + c.Status(http.StatusNoContent) + }) + + r.POST("/upload/:node_id", func(c *gin.Context) { + nodeID := c.Param("node_id") + seqKey := fmt.Sprintf("seq_num.%s", nodeID) + entry, err := state.Kv.Get(seqKey) + seqNum := 0 + if err == nil { + seqNum, _ = strconv.Atoi(string(entry.Value())) + } + seqNum++ + state.Kv.Put(seqKey, []byte(strconv.Itoa(seqNum))) + + binaryData, _ := c.GetRawData() + metadata := Metadata{ + Timestamp: time.Now().Format(time.RFC3339), + ContentType: c.GetHeader("Content-Type"), + } + dataKey := fmt.Sprintf("data.%s.%d", nodeID, seqNum) + value := DataValue{ + Metadata: metadata, + Payload: hex.EncodeToString(binaryData), + } + valBytes, _ := json.Marshal(value) + state.Kv.Put(dataKey, valBytes) + state.Nc.Publish(fmt.Sprintf("notify.%s", nodeID), []byte(strconv.Itoa(seqNum))) + c.Status(http.StatusOK) + }) + + r.POST("/close/:node_id", func(c *gin.Context) { + nodeID := c.Param("node_id") + seqKey := fmt.Sprintf("seq_num.%s", nodeID) + entry, err := state.Kv.Get(seqKey) + seqNum := 0 + if err == nil { + seqNum, _ = strconv.Atoi(string(entry.Value())) + } + seqNum++ + state.Kv.Put(seqKey, []byte(strconv.Itoa(seqNum))) + + var body map[string]interface{} + c.BindJSON(&body) + reason := "" + if r, ok := body["reason"].(string); ok { + reason = r + } + metadata := Metadata{ + Timestamp: time.Now().Format(time.RFC3339), + ContentType: c.GetHeader("Content-Type"), + Reason: reason, + } + dataKey := fmt.Sprintf("data.%s.%d", nodeID, seqNum) + value := DataValue{ + Metadata: metadata, + Payload: "", + } + valBytes, _ := json.Marshal(value) + state.Kv.Put(dataKey, valBytes) + state.Nc.Publish(fmt.Sprintf("notify.%s", nodeID), []byte(strconv.Itoa(seqNum))) + c.JSON(http.StatusOK, gin.H{ + "status": fmt.Sprintf("Connection for node %s is now closed.", nodeID), + "reason": reason, + }) + }) + + r.GET("/stream/live", func(c *gin.Context) { + keys, _ := state.Kv.Keys() + nodes := []string{} + for _, key := range keys { + if strings.HasPrefix(key, "seq_num.") { + parts := strings.Split(key, ".") + if len(parts) > 1 { + nodes = append(nodes, parts[1]) + } + } + } + c.JSON(http.StatusOK, nodes) + }) + + r.GET("/stream/single/:node_id", func(c *gin.Context) { + nodeID := c.Param("node_id") + envelopeFormat := c.DefaultQuery("envelope_format", "json") // get from query, default to json + conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + return + } + defer conn.Close() + + seqKey := fmt.Sprintf("seq_num.%s", nodeID) + entry, err := state.Kv.Get(seqKey) + currentSeq := 0 + if err == nil { + currentSeq, _ = strconv.Atoi(string(entry.Value())) + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + sub, _ := state.Nc.SubscribeSync(fmt.Sprintf("notify.%s", nodeID)) + defer sub.Unsubscribe() + + // Replay old data + for s := 1; s <= currentSeq; s++ { + sendDataOverWS(state, conn, nodeID, s, envelopeFormat) + } + + // Listen for new data + for { + msg, err := sub.NextMsgWithContext(ctx) + if err != nil { + break + } + seq, _ := strconv.Atoi(string(msg.Data)) + sendDataOverWS(state, conn, nodeID, seq, envelopeFormat) + } + }) + + r.Run(":8000") } -func sendDataOverWS(state *AppState, conn *websocket.Conn, nodeID string, seqNum int) { - dataKey := fmt.Sprintf("data.%s.%d", nodeID, seqNum) - entry, err := state.Kv.Get(dataKey) - if err != nil { - return - } - var value DataValue - json.Unmarshal(entry.Value(), &value) - resp := map[string]interface{}{ - "sequence": seqNum, - "metadata": value.Metadata, - "payload": value.Payload, - "server_host": hostname(), - } - conn.WriteJSON(resp) +func sendDataOverWS(state *AppState, conn *websocket.Conn, nodeID string, seqNum int, envelopeFormat string) { + dataKey := fmt.Sprintf("data.%s.%d", nodeID, seqNum) + entry, err := state.Kv.Get(dataKey) + if err != nil { + return + } + var value DataValue + json.Unmarshal(entry.Value(), &value) + + // Try to decode payload as float64 array (like Python) + var payload interface{} + if value.Payload != "" { + raw, err := hex.DecodeString(value.Payload) + if err == nil { + // Try float64 array + if len(raw)%8 == 0 { + floats := make([]float64, len(raw)/8) + for i := 0; i < len(floats); i++ { + floats[i] = math.Float64frombits(binary.LittleEndian.Uint64(raw[i*8 : (i+1)*8])) + } + payload = floats + } else { + // Try JSON + var obj interface{} + if err := json.Unmarshal(raw, &obj); err == nil { + payload = obj + } else { + payload = value.Payload // fallback to hex string + } + } + } else { + payload = value.Payload // fallback to hex string + } + } + + resp := map[string]interface{}{ + "sequence": seqNum, + "metadata": value.Metadata, + "payload": payload, + "server_host": hostname(), + } + + if envelopeFormat == "msgpack" { + packed, _ := msgpack.Marshal(resp) + conn.WriteMessage(websocket.BinaryMessage, packed) + } else { + conn.WriteJSON(resp) + } } func hostname() string { - h, _ := os.Hostname() - return h -} \ No newline at end of file + h, _ := os.Hostname() + return h +}