diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..0affda01f8d1d60d6c28e7442dc14777d642fba1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.git +.jackailocal +.jackailocal-builder +diagnostics +dist +target +workspace +backends/ollama/windows +bin +models/ollama +saas/gradio/__pycache__ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..23036c19eaabdd0c0ec29602052594f3545265ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,75 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## See https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# Outils locaux (Claude Code, insights) +.claude/ +.out-of-code-insights/ + +# Python - environnements et artefacts generes +__pycache__/ +*.py[cod] +*$py.class + +# Extensions natives Python +*.so + +# Distribution / packaging +.Python +build/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Environnements virtuels +.venv/ +venv/ +env/ +ENV/ + +# Outils et caches +.mypy_cache/ +.pytest_cache/ +.ruff_cache/ +.pyre/ +.hypothesis/ +.tox/ +.nox/ +.coverage +.coverage.* +htmlcov/ + +# Notebooks +.ipynb_checkpoints/ + +.jackaiagent/ +__pycache__/ +.jackailocal-builder/ +reports/ +target/ +saas/gradio/__pycache__/ +bin/ +data/ +.gitignore +logs/ +saas/gradio/__pycache__/app.cpython-313.pyc +models/ +workspace/threads/ + +# Cles privees de signature (licences clients, mises a jour) - ne jamais versionner +license-keys/ +config/update-private-key.xml +license/license.json + diff --git a/.out-of-code-insights/annotations.json b/.out-of-code-insights/annotations.json new file mode 100644 index 0000000000000000000000000000000000000000..f3884d7a3ab2048f4978acc0c99b4a5e0f1392ee --- /dev/null +++ b/.out-of-code-insights/annotations.json @@ -0,0 +1,4 @@ +{ + "schemaVersion": 2, + "annotations": [] +} \ No newline at end of file diff --git a/BUILD-LOCAL-MAC.command b/BUILD-LOCAL-MAC.command new file mode 100644 index 0000000000000000000000000000000000000000..103541241edee46be44488c83ebb6d360ccfda67 --- /dev/null +++ b/BUILD-LOCAL-MAC.command @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")" +chmod +x ./macos/JackAILocal-USB-Builder.sh +./macos/JackAILocal-USB-Builder.sh --mode=local +read -r -p "Press Enter to close..." _ diff --git a/BUILD-LOCAL.cmd b/BUILD-LOCAL.cmd new file mode 100644 index 0000000000000000000000000000000000000000..f541f4ce1e95020b209aa175a1436bb16ff82838 --- /dev/null +++ b/BUILD-LOCAL.cmd @@ -0,0 +1,33 @@ +@echo off +setlocal EnableExtensions +cd /d "%~dp0" +set "ROOT=%~dp0" +set "BUILDER=%ROOT%windows\JackAILocal-USB-Builder.ps1" +set "TARGET=%LOCALAPPDATA%\JackAILocal" + +echo JackAILocal Local Folder Builder +echo Package root: %ROOT% +echo Target folder: %TARGET% +echo. + +if not exist "%BUILDER%" ( + echo ERROR: Missing Windows builder script: + echo %BUILDER% + pause + exit /b 1 +) + +powershell -NoProfile -ExecutionPolicy Bypass -File "%BUILDER%" -TargetMode LocalFolder -TargetDrive "%TARGET%" +if errorlevel 1 ( + echo. + echo JackAILocal local folder build failed. See .jackailocal-builder\logs for details. + pause + exit /b 1 +) + +echo. +echo JackAILocal was installed to: +echo %TARGET% +echo. +echo Launching the real local runtime... +call "%TARGET%\START-HERE.cmd" diff --git a/BUILD-SSD-MAC.command b/BUILD-SSD-MAC.command new file mode 100644 index 0000000000000000000000000000000000000000..2dfbb93b76a0ba2c870c977a20c82dd842617962 --- /dev/null +++ b/BUILD-SSD-MAC.command @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")" +chmod +x ./macos/JackAILocal-USB-Builder.sh +./macos/JackAILocal-USB-Builder.sh --mode=ssd +read -r -p "Press Enter to close..." _ diff --git a/BUILD-SSD.cmd b/BUILD-SSD.cmd new file mode 100644 index 0000000000000000000000000000000000000000..866819176a850bb23e7f9e5b60371dde8a6c14de --- /dev/null +++ b/BUILD-SSD.cmd @@ -0,0 +1,28 @@ +@echo off +setlocal EnableExtensions +cd /d "%~dp0" +set "ROOT=%~dp0" +set "BUILDER=%ROOT%windows\JackAILocal-USB-Builder.ps1" + +echo JackAILocal External SSD Builder +echo Package root: %ROOT% +echo. + +if not exist "%BUILDER%" ( + echo ERROR: Missing Windows builder script: + echo %BUILDER% + pause + exit /b 1 +) + +powershell -NoProfile -ExecutionPolicy Bypass -File "%BUILDER%" -TargetMode SSD +if errorlevel 1 ( + echo. + echo JackAILocal SSD build failed. See .jackailocal-builder\logs for details. + pause + exit /b 1 +) + +echo. +echo JackAILocal SSD Builder finished. +pause diff --git a/BUILD-USB-MAC.command b/BUILD-USB-MAC.command new file mode 100644 index 0000000000000000000000000000000000000000..403fe44e2e82e59d89e7a4fec3df3a0ba4f4ae41 --- /dev/null +++ b/BUILD-USB-MAC.command @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")" +chmod +x ./macos/JackAILocal-USB-Builder.sh +./macos/JackAILocal-USB-Builder.sh --mode=usb +read -r -p "Press Enter to close..." _ diff --git a/BUILD-USB.cmd b/BUILD-USB.cmd new file mode 100644 index 0000000000000000000000000000000000000000..3c50292bf01ac7cca887f9a407594d576de75cdc --- /dev/null +++ b/BUILD-USB.cmd @@ -0,0 +1,34 @@ +@echo off +setlocal EnableExtensions +cd /d "%~dp0" +set "ROOT=%~dp0" +set "BUILDER=%ROOT%windows\JackAILocal-USB-Builder.ps1" + +echo JackAILocal USB Builder +echo Package root: %ROOT% + +if not exist "%BUILDER%" ( + echo. + echo ERROR: Missing Windows builder script: + echo %BUILDER% + echo Do not copy BUILD-USB.cmd alone. Extract the full ZIP first. + echo. + pause + exit /b 1 +) + +echo Using builder script: +echo %BUILDER% +echo. + +powershell -NoProfile -ExecutionPolicy Bypass -File "%BUILDER%" -TargetMode USB +if errorlevel 1 ( + echo. + echo JackAILocal USB Builder failed. See .jackailocal-builder\logs for details. + pause + exit /b 1 +) + +echo. +echo JackAILocal USB Builder finished. +pause diff --git a/CHECK-PACKAGE.cmd b/CHECK-PACKAGE.cmd new file mode 100644 index 0000000000000000000000000000000000000000..47b0fa1967e21ed95970fcb8b223ab57985eda07 --- /dev/null +++ b/CHECK-PACKAGE.cmd @@ -0,0 +1,38 @@ +@echo off +setlocal EnableExtensions +cd /d "%~dp0" +set "ROOT=%~dp0" + +echo JackAILocal package structure check +echo Package root: %ROOT% +echo. + +set "FOUND=0" +if exist "%ROOT%client-builder\windows\JackAILocal-USB-Builder.ps1" ( + echo FOUND: client-builder\windows\JackAILocal-USB-Builder.ps1 + set "FOUND=1" +) +if exist "%ROOT%windows\JackAILocal-USB-Builder.ps1" ( + echo FOUND: windows\JackAILocal-USB-Builder.ps1 + set "FOUND=1" +) +if exist "%ROOT%client-builder\windows\Build-JackAILocalUSB.ps1" ( + echo FOUND: client-builder\windows\Build-JackAILocalUSB.ps1 + set "FOUND=1" +) +if exist "%ROOT%factory\powershell\Prepare-JackAILocalUSB.ps1" ( + echo FOUND: factory\powershell\Prepare-JackAILocalUSB.ps1 + set "FOUND=1" +) + +echo. +if "%FOUND%"=="0" ( + echo FAIL: No builder script found. + echo Extract the full ZIP again. Do not move only the .cmd files. + pause + exit /b 1 +) + +echo PASS: At least one compatible builder script exists. +echo. +pause diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000000000000000000000000000000000..0be1b2722d337dce065397df6284a865b161c5d6 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2657 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitflags" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core 0.6.4", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jackailocald" +version = "0.3.0" +dependencies = [ + "aes-gcm", + "anyhow", + "axum", + "base64", + "chrono", + "clap", + "ed25519-dalek", + "pbkdf2", + "qrcode", + "rand 0.8.6", + "reqwest", + "serde", + "serde_json", + "sha2", + "tokio", + "toml", + "tower-http 0.5.2", + "tracing", + "tracing-subscriber", + "uuid", + "zip", +] + +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "log" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "113b30b4cd05f7c06868fdb2854f66a7b9fece9a48425351cd532e810d74024f" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pxfm" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" + +[[package]] +name = "qrcode" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68782463e408eb1e668cf6152704bd856c78c5b6417adaee3203d8f4c1fc9ec" +dependencies = [ + "image", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.4", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http 0.6.11", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" +dependencies = [ + "getrandom 0.4.2", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "flate2", + "indexmap", + "memchr", + "thiserror", + "zopfli", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..880c39b1eec94cd99d5c930709122250e957bad1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "jackailocald" +version = "0.3.0" +edition = "2021" + +[dependencies] +aes-gcm = "0.10" +anyhow = "1" +axum = "0.7" +base64 = "0.22" +chrono = { version = "0.4", features = ["serde"] } +clap = { version = "4", features = ["derive"] } +ed25519-dalek = { version = "2", features = ["rand_core"] } +pbkdf2 = "0.12" +qrcode = "0.14" +rand = "0.8" +reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +sha2 = "0.10" +tokio = { version = "1", features = ["macros", "rt-multi-thread", "net"] } +toml = "0.8" +tower-http = { version = "0.5", features = ["fs", "cors"] } +tracing = "0.1" +tracing-subscriber = "0.3" +uuid = { version = "1", features = ["v4", "serde"] } +zip = { version = "2", default-features = false, features = ["deflate"] } diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 0000000000000000000000000000000000000000..324a06520d80a63af120348ceb23c4016a0d57ef --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,31 @@ + + + + + + + + + + + + + $(MSBuildThisFileDirectory)Zzz\Zzz.App.Core.dll + + + $(MSBuildThisFileDirectory)Zzz\Zzz.App.Service.Core.dll + + + $(MSBuildThisFileDirectory)Zzz\Zzz.App.Web.Core.dll + + + $(MSBuildThisFileDirectory)Zzz\Zzz.App.Core.AccesDonnees.Oracle.Sql.dll + + + $(MSBuildThisFileDirectory)Zzz\Zzz.App.Core.AccesDonnees.Sql.dll + + + + + + diff --git a/PRODUCTION_CHECKLIST.md b/PRODUCTION_CHECKLIST.md new file mode 100644 index 0000000000000000000000000000000000000000..93e9af5ad308743dd52d1a1fd0b6492cb3f656a1 --- /dev/null +++ b/PRODUCTION_CHECKLIST.md @@ -0,0 +1,44 @@ +# Production Checklist + +## Factory build + +- [ ] Install Rust toolchain on factory PC +- [ ] Run `factory/powershell/Build-RustBinaries.ps1` +- [ ] Confirm `bin/JackAILocald.exe` exists +- [ ] Install or copy Ollama binary +- [ ] Confirm `backends/ollama/windows/ollama.exe` exists +- [ ] Install or copy llama.cpp binary +- [ ] Confirm `backends/llama.cpp/windows/llama-server.exe` exists +- [ ] Set `OLLAMA_MODELS` to USB `models/ollama` +- [ ] Pull required Ollama models into USB +- [ ] Add GGUF models if llama.cpp fallback is enabled +- [ ] Generate manifest +- [ ] Run Windows preflight +- [ ] Run first-launch from USB on clean Windows machine +- [ ] Run airplane-mode test +- [ ] Run benchmark demo +- [ ] Confirm no model download occurs during customer launch + +## Security + +- [ ] Remove placeholder files before release +- [ ] Replace update warning hook with signature verification +- [ ] Sign release artifacts +- [ ] Scan binaries with Microsoft Defender and VirusTotal-compatible workflow +- [ ] Document realistic host traces +- [ ] Avoid "leaves no trace" claim +- [ ] Bind all local services to 127.0.0.1 only +- [ ] Add process crash recovery +- [ ] Add request size limit +- [ ] Add timeout limit + +## Linux boot edition + +- [ ] Flash Linux image to SSD/USB +- [ ] Install Linux runtime +- [ ] Install Linux backends +- [ ] Configure kiosk auto-start +- [ ] Test boot on Dell/Lenovo/HP/ASUS at minimum +- [ ] Test Secure Boot behavior +- [ ] Test no-network mode +- [ ] Test GPU/CPU fallback diff --git a/README-FIRST.txt b/README-FIRST.txt new file mode 100644 index 0000000000000000000000000000000000000000..cc498946cf8cc2ec38a2e968a21e811ca176b1d8 --- /dev/null +++ b/README-FIRST.txt @@ -0,0 +1,23 @@ +JackAILocal v15 + +Windows local folder: + BUILD-LOCAL.cmd + +Windows USB: + BUILD-USB.cmd + +Windows external SSD: + BUILD-SSD.cmd + +Start an already prepared Windows target: + START-HERE.cmd + +Start in a desktop-style browser window: + START-DESKTOP.cmd + +macOS builders: + BUILD-LOCAL-MAC.command + BUILD-USB-MAC.command + BUILD-SSD-MAC.command + +Read README.md and docs/SYSTEM_BEHAVIOR.md for the complete behavior. diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e9477ae0b0c638f40d5715ab03d49fba8d4a4c19 --- /dev/null +++ b/README.md @@ -0,0 +1,209 @@ +--- +title: JackAILocal +emoji: 🚀 +colorFrom: green +colorTo: red +sdk: gradio +sdk_version: 6.16.0 +python_version: '3.13' +app_file: app.py +pinned: false +hf_oauth: true +hf_oauth_scopes: + - inference-api +--- + +# JackAILocal v15 + +JackAILocal is a local-first AI runtime, WebUI, and package builder. It can run from a local folder, USB drive, external SSD, desktop installer, Linux boot disk, or LAN appliance. + +The normal runtime path is: + +```text +WebUI -> jackailocald -> local Ollama model +``` + +No cloud inference fallback is used. A capability is shown as unavailable when its required local binary or model is absent. + +## Implemented Capabilities + +- Local chat through installed Ollama models +- Optional `llama.cpp` OpenAI-compatible server fallback when explicitly provided +- Persistent conversation threads with filter, rename, and individual delete +- SCOUT image analysis and text extraction through an installed vision-capable Ollama model +- LLM-in-the-loop configuration review through an installed local model, with Gemma 4 12B (`gemma4:12b`) as the preferred config agent +- Offline speech-to-text through whisper.cpp +- Offline text-to-speech through Piper +- Local documents and bilingual Field Manual cards +- AES-256-GCM encrypted import/export packs for threads, documents, and safe settings +- Opt-in Phone Access on the local network with pairing token and QR code +- Hardware status, model availability, benchmark, settings, and privacy-safe support ZIP +- English and French WebUI + +## Runtime Requirements + +A usable Windows payload contains at least: + +```text +bin/jackailocald.exe +backends/ollama/windows/ollama.exe +models/ollama/ one or more real Ollama models +backends/whisper.cpp/windows/whisper-cli.exe +backends/piper/windows/piper.exe +models/whisper/ggml-base.bin +models/piper/en_US-libritts_r-medium.onnx +webui/ +config/ +content/ +content-packs/ +``` + +The builders validate these files before reporting success. + +## Start Locally + +Windows: + +```cmd +START-HERE.cmd +``` + +Windows desktop-style browser window: + +```cmd +START-DESKTOP.cmd +``` + +macOS: + +```bash +./START-HERE.command +``` + +The default bind is `127.0.0.1:4891`. Phone Access is disabled by default and requires an explicit enable plus runtime restart. + +## Build Targets + +Windows launchers: + +```cmd +BUILD-LOCAL.cmd +BUILD-USB.cmd +BUILD-SSD.cmd +``` + +macOS launchers: + +```bash +./BUILD-LOCAL-MAC.command +./BUILD-USB-MAC.command +./BUILD-SSD-MAC.command +``` + +`TEST-WITHOUT-USB.cmd` and `TEST-WITHOUT-USB-MAC.command` build a real local-folder edition for acceptance testing. + +The builders: + +1. Read an embedded or detected build manifest. +2. Validate that the target does not overlap the source package. +3. Copy the runtime payload (including `legal/`, `licenses/`, and the PC analyzer in `profiler/`). +4. Install selected Ollama models into the target model store. +5. Install optional LoRA adapters from `manifest/lora-adapters.json` (see `docs/LORA_ADAPTERS.md`). +6. Install and verify Voice assets. +7. Generate a SHA-256 file manifest. +8. Refuse to report success if required runtime assets or legal documents are missing. + +## Customer hardware analysis + +For remote customers, the builder exports a standalone PC analyzer +(`profiler/`): the customer double-clicks `SCAN-MY-PC.cmd` (Windows) or +`SCAN-MY-MAC.command` (macOS), gets `JackAILocal-hardware-profile.json` on the +Desktop, and sends it back. The file is uploaded in the Gradio builder, and +the configuration agent (Gemma 4 12B, `gemma4:12b`) reviews the plan. + +## Offline updates + +Signed update packages are produced with `updates/Create-UpdatePackage.ps1` +and applied on the customer machine with `UPDATE-FROM-USB.cmd` (auto-detects +the update package on removable drives, verifies the RSA signature and all +SHA-256 checksums, asks for confirmation, keeps a rollback backup). See also +`updates/JackAILocal-Update-Offline.ps1 -Rollback`. + +## Licensing + +Customer licenses are Ed25519-signed files verified fully offline. Vendor +tooling: `jackailocald license keygen | issue | verify`. The runtime exposes +`/api/license`, and the WebUI shows the license state under Settings. Model +sources and backends (Ollama, Hugging Face GGUF/safetensors, NVIDIA NIM) are +documented in `docs/MODEL_SOURCES.md`. + +## Gradio Builder + +The Hugging Face Space entrypoint is `app.py`. The full application is in `saas/gradio/app.py`. + +```bash +pip install -r requirements.txt +python app.py +``` + +The Gradio application now starts with a simple flow for non-technical users: choose the intended use, choose a target, then prepare or publish. It can install to this computer, write a USB key or external SSD when running locally on Windows, or publish Windows, macOS, and Linux/Docker appliance ZIPs. Hosted Gradio exports packages only; it does not impersonate a client runtime. + +## Desktop Installers + +Windows installer source: + +```powershell +powershell -ExecutionPolicy Bypass -File installer/windows/Build-Installer.ps1 +``` + +macOS DMG source: + +```bash +./installer/macos/build-dmg.sh +``` + +Both installer builders require a complete payload with at least one installed Ollama model. + +## LAN Appliance + +```bash +cd deploy/appliance +JACKAILOCAL_MODEL=qwen3.5:4b docker compose up --build -d +./show-url.sh +``` + +The appliance runs the Rust runtime and Ollama, preloads the selected model into a persistent volume, and protects remote API requests with the pairing token. + +## Linux Boot Edition + +Run the destructive boot-disk preparation only from Linux and only with the correct target device: + +```bash +sudo linux/factory/prepare-linux-boot-disk.sh /dev/sdX +``` + +The script builds the runtime, stages Ollama, builds whisper.cpp, installs Voice assets, preloads the selected model, creates a Debian UEFI disk, and enables the JackAILocal services. + +## Security Boundaries + +- Loopback bind by default +- No shell tools exposed to the model +- No private update key in exported packages +- Pairing token required for non-loopback API access +- AES transfer pack passphrases are never stored +- Support ZIP excludes conversations and documents +- SHA-256 package manifest and signed-manifest hooks + +JackAILocal does not claim protection from a compromised host operating system. + +## Verification + +```powershell +cargo check +cargo test --no-run +python -m py_compile app.py saas/gradio/app.py +node --check webui/app-v15.js +python qa/static_audit.py +``` + +See `docs/SYSTEM_BEHAVIOR.md` for the API and behavior summary. diff --git a/START-DESKTOP.cmd b/START-DESKTOP.cmd new file mode 100644 index 0000000000000000000000000000000000000000..eb683aecf40a9f0ebe1f709f184190171544317f --- /dev/null +++ b/START-DESKTOP.cmd @@ -0,0 +1,17 @@ +@echo off +setlocal +cd /d "%~dp0" +where powershell.exe >nul 2>nul +if errorlevel 1 ( + echo PowerShell was not found. JackAILocal cannot start. + pause + exit /b 1 +) +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0windows\Start-JackAILocal.ps1" -AppMode +if errorlevel 1 ( + echo. + echo JackAILocal Desktop did not start correctly. + echo Check .jackailocal\logs or run windows\Preflight-Windows.ps1 + pause + exit /b 1 +) diff --git a/START-HERE.cmd b/START-HERE.cmd new file mode 100644 index 0000000000000000000000000000000000000000..1ff185755ea82e389539a7620a53aa2cafc94b42 --- /dev/null +++ b/START-HERE.cmd @@ -0,0 +1,17 @@ +@echo off +setlocal +cd /d "%~dp0" +where powershell.exe >nul 2>nul +if errorlevel 1 ( + echo PowerShell was not found. JackAILocal cannot start. + pause + exit /b 1 +) +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0windows\Start-JackAILocal.ps1" +if errorlevel 1 ( + echo. + echo JackAILocal did not start correctly. + echo Check .jackailocal\logs or run windows\Preflight-Windows.ps1 + pause + exit /b 1 +) diff --git a/START-HERE.command b/START-HERE.command new file mode 100644 index 0000000000000000000000000000000000000000..1ede9a4ddd2121443d5310347b8ae84c731e02a2 --- /dev/null +++ b/START-HERE.command @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")" +chmod +x ./macos/Start-JackAILocal.sh +./macos/Start-JackAILocal.sh +read -r -p "Press Enter to close..." _ diff --git a/STOP-JACKAILOCAL.cmd b/STOP-JACKAILOCAL.cmd new file mode 100644 index 0000000000000000000000000000000000000000..8d5dce3e49f37bbb9346b76f0030ff258b20a5a6 --- /dev/null +++ b/STOP-JACKAILOCAL.cmd @@ -0,0 +1,5 @@ +@echo off +setlocal +cd /d "%~dp0" +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0windows\Stop-JackAILocal.ps1" +pause diff --git a/STOP-JACKAILOCAL.command b/STOP-JACKAILOCAL.command new file mode 100644 index 0000000000000000000000000000000000000000..2a472135364880325acc2187abf140fcc37a01ff --- /dev/null +++ b/STOP-JACKAILOCAL.command @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")" +chmod +x ./macos/Stop-JackAILocal.sh +./macos/Stop-JackAILocal.sh diff --git a/TEST-WITHOUT-USB-MAC.command b/TEST-WITHOUT-USB-MAC.command new file mode 100644 index 0000000000000000000000000000000000000000..12367ab1ce5bc7a49323f35921e6daf25e2e0f0c --- /dev/null +++ b/TEST-WITHOUT-USB-MAC.command @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")" +TARGET="$HOME/JackAILocal-Test" + +echo "JackAILocal real local-folder test" +echo "This creates a real local runtime folder at:" +echo " $TARGET" +echo +chmod +x ./macos/JackAILocal-USB-Builder.sh +./macos/JackAILocal-USB-Builder.sh --mode=local --target="$TARGET" + +echo +echo "Real local-folder build complete." +echo "Location: $TARGET" +chmod +x "$TARGET/START-HERE.command" +"$TARGET/START-HERE.command" +read -r -p "Press Enter to close..." _ diff --git a/TEST-WITHOUT-USB.cmd b/TEST-WITHOUT-USB.cmd new file mode 100644 index 0000000000000000000000000000000000000000..59cb4041d24963688d8ebf26804cfec1e29affe3 --- /dev/null +++ b/TEST-WITHOUT-USB.cmd @@ -0,0 +1,34 @@ +@echo off +setlocal EnableExtensions +cd /d "%~dp0" +set "ROOT=%~dp0" +set "BUILDER=%ROOT%windows\JackAILocal-USB-Builder.ps1" +set "TARGET=%LOCALAPPDATA%\JackAILocal-Test" + +echo JackAILocal real local-folder test +echo Package root: %ROOT% +echo Target folder: +echo %TARGET% +echo. + +if not exist "%BUILDER%" ( + echo ERROR: Missing Windows builder script: + echo %BUILDER% + echo Do not copy TEST-WITHOUT-USB.cmd alone. Extract the full ZIP first. + pause + exit /b 1 +) + +set AUTO_CONFIRM=true +powershell -NoProfile -ExecutionPolicy Bypass -File "%BUILDER%" -TargetMode LocalFolder -TargetDrive "%TARGET%" -CleanTarget +if errorlevel 1 ( + echo. + echo Local-folder build failed. See .jackailocal-builder\logs for details. + pause + exit /b 1 +) + +echo. +echo Real local-folder build complete. +echo Location: %TARGET% +call "%TARGET%\START-HERE.cmd" diff --git a/UPDATE-FROM-USB.cmd b/UPDATE-FROM-USB.cmd new file mode 100644 index 0000000000000000000000000000000000000000..acf05bbe570491df4be87890f4477b6756538fdd --- /dev/null +++ b/UPDATE-FROM-USB.cmd @@ -0,0 +1,5 @@ +@echo off +rem JackAILocal - Mise a jour hors ligne / Offline update +cd /d "%~dp0" +powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0updates\Find-And-Apply-Update.ps1" +pause diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..aa6c8f5c67450911ade197543e9bb6f0f2aa7e63 --- /dev/null +++ b/app.py @@ -0,0 +1,17 @@ +import os + +from saas.gradio.app import build_ui, find_free_port, gradio_launch_kwargs + +# Build the main demo interface +demo = build_ui() + +if __name__ == "__main__": + requested_port = int(os.environ.get("GRADIO_SERVER_PORT", os.environ.get("PORT", "7860"))) + port = find_free_port(requested_port) + if port != requested_port: + print(f"Port {requested_port} is busy; using {port} instead -> http://127.0.0.1:{port}") + demo.launch( + server_name=os.environ.get("GRADIO_SERVER_NAME", "127.0.0.1"), + server_port=port, + **gradio_launch_kwargs(), + ) diff --git a/autorun.inf b/autorun.inf new file mode 100644 index 0000000000000000000000000000000000000000..80e432eec7d6404286a3c172a835798dfcca0875 --- /dev/null +++ b/autorun.inf @@ -0,0 +1,5 @@ +[AutoRun] +label=JackAILocal +icon=branding\jackailocal.ico +open=START-HERE.cmd +action=Start JackAILocal diff --git a/backends/vllm/linux/start-vllm.sh b/backends/vllm/linux/start-vllm.sh new file mode 100644 index 0000000000000000000000000000000000000000..5e3cdf07ff2888eb9a9aee96a2ec01e826ad6998 --- /dev/null +++ b/backends/vllm/linux/start-vllm.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail +MODEL="${1:-/opt/JackAILocal/models/hf/qwen3.5-9b}" +PORT="${PORT:-8000}" +source /opt/JackAILocal/backends/vllm/linux/venv/bin/activate +exec python -m vllm.entrypoints.openai.api_server --host 127.0.0.1 --port "$PORT" --model "$MODEL" diff --git a/cache/projects.json b/cache/projects.json new file mode 100644 index 0000000000000000000000000000000000000000..a838971ed9ecf5b55258ded72f9c3da98a3b2418 --- /dev/null +++ b/cache/projects.json @@ -0,0 +1,3 @@ +{ + "E:\\sources\\jackailocal": "a4a798bc-c6f0-454e-be2e-6b9fe99873b9" +} \ No newline at end of file diff --git a/client-builder/windows/Build-JackAILocalUSB.ps1 b/client-builder/windows/Build-JackAILocalUSB.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..6a5ed495d28bac7fe7d266088ba9933feb485a27 --- /dev/null +++ b/client-builder/windows/Build-JackAILocalUSB.ps1 @@ -0,0 +1,41 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$BuildManifestUrl, + [Parameter(Mandatory=$true)][string]$TargetDrive, + [switch]$SkipModelDownloads +) +$ErrorActionPreference = "Stop" +if ($TargetDrive.Length -eq 2 -and $TargetDrive[1] -eq ':') { $TargetDrive = "$TargetDrive\" } +if (!(Test-Path $TargetDrive)) { throw "Target drive not found: $TargetDrive" } +$manifest = Invoke-RestMethod -Uri $BuildManifestUrl +if ($manifest.product -ne "JackAILocal") { throw "Wrong product manifest." } +$Root = (Resolve-Path $TargetDrive).Path +New-Item -ItemType Directory -Force -Path "$Root\models\ollama", "$Root\models\gguf", "$Root\backends", "$Root\bin", "$Root\manifest" | Out-Null + +# Copy seed runtime from the builder folder if present. +$SeedRoot = Resolve-Path (Join-Path $PSScriptRoot "..\..") +robocopy $SeedRoot $Root /E /XD ".git" ".jackailocal" "target" | Out-Null + +foreach ($asset in $manifest.assets) { + $dest = Join-Path $Root $asset.path + New-Item -ItemType Directory -Force -Path (Split-Path $dest) | Out-Null + if ($asset.url -and !(Test-Path $dest)) { + Invoke-WebRequest -Uri $asset.url -OutFile $dest + } + if ($asset.sha256) { + $h = (Get-FileHash -Algorithm SHA256 $dest).Hash.ToLowerInvariant() + if ($h -ne $asset.sha256.ToLowerInvariant()) { throw "Checksum failed for $($asset.path)" } + } +} + +if (-not $SkipModelDownloads) { + $OllamaExe = Join-Path $Root "backends\ollama\windows\ollama.exe" + if (Test-Path $OllamaExe) { + $env:OLLAMA_MODELS = Join-Path $Root "models\ollama" + foreach ($m in $manifest.ollama_models) { & $OllamaExe pull $m } + } +} + +$manifest | ConvertTo-Json -Depth 20 | Set-Content -Encoding UTF8 (Join-Path $Root "manifest\build-manifest.json") +Write-Host "JackAILocal key built at $Root" +Write-Host "Use START-HERE.cmd on the key." diff --git a/client-profiler/windows/Collect-JackAILocalProfile.ps1 b/client-profiler/windows/Collect-JackAILocalProfile.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..d324ed8464a7e5bd0b32be215d5585cda83d80a3 --- /dev/null +++ b/client-profiler/windows/Collect-JackAILocalProfile.ps1 @@ -0,0 +1,32 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$ApiBaseUrl, + [string]$Email="", + [string]$OutputPath="jackailocal-hardware-profile.json" +) +$ErrorActionPreference = "Stop" +$cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 +$memBytes = (Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory +$gpus = Get-CimInstance Win32_VideoController | ForEach-Object { + [pscustomobject]@{ name=$_.Name; vram_gb=[math]::Round(($_.AdapterRAM / 1GB),1); driver=$_.DriverVersion } +} +$which = $null +if (Get-Command whichllm -ErrorAction SilentlyContinue) { + try { $which = (whichllm --top 10 --json | ConvertFrom-Json) } catch { $which = @{error=$_.Exception.Message} } +} +$profile = [ordered]@{ + product="JackAILocal" + customer_email=$Email + collected_at=(Get-Date).ToString("o") + os="windows" + cpu_name=$cpu.Name + cpu_threads=$cpu.NumberOfLogicalProcessors + ram_gb=[math]::Round($memBytes / 1GB,1) + gpus=$gpus + vram_gb=[double](($gpus | Measure-Object -Property vram_gb -Maximum).Maximum) + whichllm=$which +} +$json = $profile | ConvertTo-Json -Depth 30 +$json | Set-Content -Encoding UTF8 $OutputPath +$response = Invoke-RestMethod -Method Post -Uri "$ApiBaseUrl/api/v1/hardware/submit" -ContentType "application/json" -Body $json +$response | ConvertTo-Json -Depth 10 diff --git a/config/content-packs.json b/config/content-packs.json new file mode 100644 index 0000000000000000000000000000000000000000..70a218fb436ff35a822cee5b4218f18d1427623c --- /dev/null +++ b/config/content-packs.json @@ -0,0 +1,49 @@ +{ + "catalog_version": "2026-06-03-v6", + "product": "JackAILocal", + "download_policy": "Content packs are selected during package build. Normal USB insertion must not download packs.", + "packs": [ + { + "id": "field_manual_core", + "label": "Field Manual Core", + "type": "reference_cards", + "included_by_default": true, + "categories": [ + "water", + "fire", + "first_aid", + "shelter", + "signal", + "food" + ], + "ai_escalation": true, + "storage_target": "content/field-manual/" + }, + { + "id": "starter_prompt_packs", + "label": "Starter Prompt Packs", + "type": "prompt_packs", + "included_by_default": true, + "packs": [ + "general_assistant", + "travel", + "privacy", + "outage", + "study" + ] + }, + { + "id": "professional_prompt_packs", + "label": "Professional Prompt Packs", + "type": "prompt_packs", + "included_by_default": false, + "packs": [ + "business_operator", + "creator_studio", + "rapid_learning", + "translation", + "research" + ] + } + ] +} diff --git a/config/jackailocal.appliance.toml b/config/jackailocal.appliance.toml new file mode 100644 index 0000000000000000000000000000000000000000..4e63a58374231cecfea37f80b10dc076c6bae23c --- /dev/null +++ b/config/jackailocal.appliance.toml @@ -0,0 +1,22 @@ +product = "JackAILocal" +bind = "0.0.0.0:4891" +default_profile = "auto" +local_only = false +allow_remote_network = true +phone_access_available = true + +[paths] +models_ollama = "models/ollama" +models_gguf = "models/gguf" +cache = ".jackailocal/cache" +logs = ".jackailocal/logs" + +[backends.ollama] +enabled = true +# url = "http://ollama:11434" +priority = 10 + +[backends.llamacpp] +enabled = false +# url = "http://127.0.0.1:8080" +priority = 20 diff --git a/config/jackailocal.linux.toml b/config/jackailocal.linux.toml new file mode 100644 index 0000000000000000000000000000000000000000..e2c092650ffa97d17e006ee67c2ae07720c7035c --- /dev/null +++ b/config/jackailocal.linux.toml @@ -0,0 +1,22 @@ +product = "JackAILocal" +bind = "127.0.0.1:4891" +default_profile = "auto" +local_only = true +allow_remote_network = false +phone_access_available = true + +[paths] +models_ollama = "models/ollama" +models_gguf = "models/gguf" +cache = ".jackailocal/cache" +logs = ".jackailocal/logs" + +[backends.ollama] +enabled = true +# url = "http://127.0.0.1:11434" +priority = 10 + +[backends.llamacpp] +enabled = true +# url = "http://127.0.0.1:8080" +priority = 20 diff --git a/config/jackailocal.macos.toml b/config/jackailocal.macos.toml new file mode 100644 index 0000000000000000000000000000000000000000..2ebc81a92ea1bcb011562b2754da8cd703eebf23 --- /dev/null +++ b/config/jackailocal.macos.toml @@ -0,0 +1,24 @@ +[server] +host = "127.0.0.1" +port = 4891 + +[paths] +models = "models" +webui = "webui" +workspace = "workspace" +logs = ".jackailocal/logs" + +[backends.ollama] +enabled = true +# base_url = "http://127.0.0.1:11436" +models_dir = "models/ollama" + +[backends.llamacpp] +enabled = true +# base_url = "http://127.0.0.1:8080" + +[security] +local_only = true +allow_lan = false +phone_access_available = true +allow_shell_tools = false diff --git a/config/jackailocal.windows.toml b/config/jackailocal.windows.toml new file mode 100644 index 0000000000000000000000000000000000000000..e2c092650ffa97d17e006ee67c2ae07720c7035c --- /dev/null +++ b/config/jackailocal.windows.toml @@ -0,0 +1,22 @@ +product = "JackAILocal" +bind = "127.0.0.1:4891" +default_profile = "auto" +local_only = true +allow_remote_network = false +phone_access_available = true + +[paths] +models_ollama = "models/ollama" +models_gguf = "models/gguf" +cache = ".jackailocal/cache" +logs = ".jackailocal/logs" + +[backends.ollama] +enabled = true +# url = "http://127.0.0.1:11434" +priority = 10 + +[backends.llamacpp] +enabled = true +# url = "http://127.0.0.1:8080" +priority = 20 diff --git a/config/model-alias-map.json b/config/model-alias-map.json new file mode 100644 index 0000000000000000000000000000000000000000..73eed4fce30c7d1970cfd2be26891f711ef2f15f --- /dev/null +++ b/config/model-alias-map.json @@ -0,0 +1,31 @@ +{ + "aliases": [ +{ + "hf_pattern": "Qwen/Qwen3.5-2B", + "ollama": "qwen3.5:2b", + "profile": "lite" + }, + { + "hf_pattern": "Qwen/Qwen3.5-4B", + "ollama": "qwen3.5:4b", + "profile": "balanced" + }, + { + "hf_pattern": "Qwen/Qwen3.5-9B", + "ollama": "qwen3.5:9b", + "profile": "smart" + }, + { + "hf_pattern": "Qwen/Qwen3-VL", + "ollama": "qwen3-vl:4b", + "profile": "vision" + }, + { + "hf_pattern": "google/gemma-4-12B-it", + "ollama": "gemma4:12b", + "profile": "config_agent" + } + ], + "note": "whichllm returns Hugging Face model ids; JackAILocal maps compatible entries to local Ollama model references." +} + diff --git a/config/model-catalog.json b/config/model-catalog.json new file mode 100644 index 0000000000000000000000000000000000000000..db54f14ef8fbea99ce6e1611d9992b2bf62c94ba --- /dev/null +++ b/config/model-catalog.json @@ -0,0 +1,290 @@ +{ + "catalog_version": "2026-06-04-v8-gemma-agent", + "product": "JackAILocal", + "policy": { + "default_pack": "field_default", + "normal_use_downloads_models": false, + "factory_or_builder_preloads_models": true, + "user_can_add_models": true, + "hackathon_max_params_b": 32, + "message": "The bundled model set is a sensible offline default, not a hard ceiling. JackAILocal can prepare Qwen3.5 models for normal laptops, Gemma 4 12B as a local configuration agent, and Qwen3.6-27B / other <=32B power models when the customer machine can run them.", + "primary_model_family": [ + "Qwen3.5", + "Qwen3.6", + "Gemma 4" + ] + }, + "profiles": [ +{ + "id": "field_default", + "label": "Field Default", + "description": "Default offline pack sized for normal laptops. Balanced for privacy, travel, outage, and general assistance.", + "included_models": [ + "qwen_fast", + "qwen_balanced", + "qwen_smart", + "qwen_vision" + ], + "recommended_default": "qwen_smart", + "min_ram_gb": 16, + "min_vram_gb": 0, + "drive_class": "standard_usb3_or_better", + "content_packs": [ + "field_manual_core", + "starter_prompt_packs" + ] + }, + { + "id": "core_low_spec", + "label": "Core Low-Spec", + "description": "Entry pack for low-spec laptops and CPU-only machines.", + "included_models": [ + "qwen_fast", + "qwen_balanced" + ], + "recommended_default": "qwen_balanced", + "min_ram_gb": 8, + "min_vram_gb": 0, + "drive_class": "standard_usb3", + "content_packs": [ + "field_manual_core", + "starter_prompt_packs" + ] + }, + { + "id": "power_24gb_vram", + "label": "Power 24GB VRAM", + "description": "Advanced pack for RTX 3090/4090-class machines. It can include Qwen3.6-27B while staying inside the hackathon small-model limit.", + "included_models": [ + "qwen_fast", + "qwen_balanced", + "qwen_smart", + "gemma_config_agent_12b", + "qwen_power_27b", + "qwen36_power_27b", + "qwen_power_30b", + "qwen_vision", + "qwen_vision_pro_30b" + ], + "recommended_default": "qwen36_power_27b", + "min_ram_gb": 32, + "min_vram_gb": 24, + "drive_class": "max_speed_ssd_or_nvme", + "content_packs": [ + "field_manual_core", + "professional_prompt_packs" + ] + } + ], + "models": [ + { + "id": "qwen_fast", + "label": "Fast", + "ollama": "qwen3.5:2b", + "gguf": "models/gguf/qwen3.5-2b-q4_k_m.gguf", + "params_b": 2, + "min_ram_gb": 6, + "min_vram_gb": 0, + "features": [ + "chat", + "vision" + ], + "default_context": 4096, + "allowed_backends": [ + "ollama", + "llama.cpp" + ] + }, + { + "id": "qwen_balanced", + "label": "Balanced", + "ollama": "qwen3.5:4b", + "gguf": "models/gguf/qwen3.5-4b-q4_k_m.gguf", + "params_b": 4, + "min_ram_gb": 8, + "min_vram_gb": 0, + "features": [ + "chat", + "vision", + "voice" + ], + "default_context": 8192, + "allowed_backends": [ + "ollama", + "llama.cpp" + ] + }, + { + "id": "qwen_smart", + "label": "Smart", + "ollama": "qwen3.5:9b", + "gguf": "models/gguf/qwen3.5-9b-q4_k_m.gguf", + "params_b": 9, + "min_ram_gb": 16, + "min_vram_gb": 0, + "features": [ + "chat", + "vision", + "thinking" + ], + "default_context": 8192, + "allowed_backends": [ + "ollama", + "llama.cpp" + ] + }, + { + "id": "gemma_config_agent_12b", + "label": "Gemma 4 12B Config Agent", + "hf": "google/gemma-4-12B-it", + "ollama": "gemma4:12b", + "gguf": null, + "params_b": 11.95, + "min_ram_gb": 24, + "min_vram_gb": 0, + "features": [ + "chat", + "vision", + "thinking", + "audio", + "tools", + "agent", + "config_agent", + "model_selection" + ], + "default_context": 8192, + "max_native_context": 262144, + "recommended_usb_context": 8192, + "allowed_backends": [ + "ollama" + ], + "hackathon_eligible": true, + "agent_role": "LLM-in-the-loop assistant for hardware analysis, client model selection, and safe local configuration review.", + "notes": "Preferred local config-agent model when installed. It must be pulled by the builder or preloaded; the runtime never falls back to cloud." + }, + { + "id": "qwen_power_27b", + "label": "Power 27B", + "ollama": "qwen3.5:27b", + "gguf": "models/gguf/qwen3.5-27b-q4_k_m.gguf", + "params_b": 27, + "min_ram_gb": 32, + "min_vram_gb": 16, + "features": [ + "chat", + "vision", + "thinking", + "longer_answers" + ], + "default_context": 8192, + "allowed_backends": [ + "ollama", + "llama.cpp" + ] + }, + { + "id": "qwen36_power_27b", + "label": "Qwen3.6 Power 27B", + "hf": "Qwen/Qwen3.6-27B", + "ollama": "qwen3.6:27b", + "gguf": "models/gguf/qwen3.6-27b-q4_k_m.gguf", + "params_b": 27, + "min_ram_gb": 32, + "min_vram_gb": 24, + "features": [ + "chat", + "vision", + "thinking", + "coding", + "agentic_coding", + "power_user" + ], + "default_context": 8192, + "max_native_context": 262144, + "recommended_usb_context": 8192, + "allowed_backends": [ + "ollama", + "llama.cpp" + ], + "hackathon_eligible": true, + "notes": "Use as Power profile for RTX 3090-class or better machines. Keep USB default context low; the native 262K context is not a default USB setting." + }, + { + "id": "qwen_power_30b", + "label": "Power 30B", + "ollama": "qwen3:30b", + "gguf": "models/gguf/qwen3-30b-q4_k_m.gguf", + "params_b": 30, + "min_ram_gb": 48, + "min_vram_gb": 24, + "features": [ + "chat", + "thinking", + "power_user" + ], + "default_context": 8192, + "allowed_backends": [ + "ollama", + "llama.cpp" + ] + }, + { + "id": "qwen_vision", + "label": "Vision", + "ollama": "qwen3-vl:8b", + "gguf": null, + "params_b": 8, + "min_ram_gb": 16, + "min_vram_gb": 6, + "features": [ + "vision", + "image", + "document" + ], + "default_context": 4096, + "allowed_backends": [ + "ollama" + ] + }, + { + "id": "qwen_vision_pro_30b", + "label": "Vision Pro 30B", + "ollama": "qwen3-vl:30b", + "gguf": null, + "params_b": 30, + "min_ram_gb": 48, + "min_vram_gb": 24, + "features": [ + "vision", + "document", + "power_user" + ], + "default_context": 4096, + "allowed_backends": [ + "ollama" + ] + } + ], + "selection_rules": { + "ram_below_8": "qwen_fast", + "ram_8_to_15": "qwen_balanced", + "ram_16_plus": "qwen_smart", + "config_agent": "gemma_config_agent_12b if installed and RAM is sufficient; otherwise use another installed local chat model through Auto", + "vram_16_plus": "qwen_power_27b optional", + "vram_24_plus": "qwen_power_30b optional if package size and latency are acceptable", + "vision": "only enable if hardware supports the selected vision profile" + }, + "ui_features": { + "workspace": true, + "model_cookbook": true, + "documents": true, + "benchmark": true, + "settings": true, + "support": true, + "content_packs": true, + "field_manual": true, + "phone_access_optional": true, + "llm_config_agent": true + } +} + diff --git a/config/models.json b/config/models.json new file mode 100644 index 0000000000000000000000000000000000000000..db54f14ef8fbea99ce6e1611d9992b2bf62c94ba --- /dev/null +++ b/config/models.json @@ -0,0 +1,290 @@ +{ + "catalog_version": "2026-06-04-v8-gemma-agent", + "product": "JackAILocal", + "policy": { + "default_pack": "field_default", + "normal_use_downloads_models": false, + "factory_or_builder_preloads_models": true, + "user_can_add_models": true, + "hackathon_max_params_b": 32, + "message": "The bundled model set is a sensible offline default, not a hard ceiling. JackAILocal can prepare Qwen3.5 models for normal laptops, Gemma 4 12B as a local configuration agent, and Qwen3.6-27B / other <=32B power models when the customer machine can run them.", + "primary_model_family": [ + "Qwen3.5", + "Qwen3.6", + "Gemma 4" + ] + }, + "profiles": [ +{ + "id": "field_default", + "label": "Field Default", + "description": "Default offline pack sized for normal laptops. Balanced for privacy, travel, outage, and general assistance.", + "included_models": [ + "qwen_fast", + "qwen_balanced", + "qwen_smart", + "qwen_vision" + ], + "recommended_default": "qwen_smart", + "min_ram_gb": 16, + "min_vram_gb": 0, + "drive_class": "standard_usb3_or_better", + "content_packs": [ + "field_manual_core", + "starter_prompt_packs" + ] + }, + { + "id": "core_low_spec", + "label": "Core Low-Spec", + "description": "Entry pack for low-spec laptops and CPU-only machines.", + "included_models": [ + "qwen_fast", + "qwen_balanced" + ], + "recommended_default": "qwen_balanced", + "min_ram_gb": 8, + "min_vram_gb": 0, + "drive_class": "standard_usb3", + "content_packs": [ + "field_manual_core", + "starter_prompt_packs" + ] + }, + { + "id": "power_24gb_vram", + "label": "Power 24GB VRAM", + "description": "Advanced pack for RTX 3090/4090-class machines. It can include Qwen3.6-27B while staying inside the hackathon small-model limit.", + "included_models": [ + "qwen_fast", + "qwen_balanced", + "qwen_smart", + "gemma_config_agent_12b", + "qwen_power_27b", + "qwen36_power_27b", + "qwen_power_30b", + "qwen_vision", + "qwen_vision_pro_30b" + ], + "recommended_default": "qwen36_power_27b", + "min_ram_gb": 32, + "min_vram_gb": 24, + "drive_class": "max_speed_ssd_or_nvme", + "content_packs": [ + "field_manual_core", + "professional_prompt_packs" + ] + } + ], + "models": [ + { + "id": "qwen_fast", + "label": "Fast", + "ollama": "qwen3.5:2b", + "gguf": "models/gguf/qwen3.5-2b-q4_k_m.gguf", + "params_b": 2, + "min_ram_gb": 6, + "min_vram_gb": 0, + "features": [ + "chat", + "vision" + ], + "default_context": 4096, + "allowed_backends": [ + "ollama", + "llama.cpp" + ] + }, + { + "id": "qwen_balanced", + "label": "Balanced", + "ollama": "qwen3.5:4b", + "gguf": "models/gguf/qwen3.5-4b-q4_k_m.gguf", + "params_b": 4, + "min_ram_gb": 8, + "min_vram_gb": 0, + "features": [ + "chat", + "vision", + "voice" + ], + "default_context": 8192, + "allowed_backends": [ + "ollama", + "llama.cpp" + ] + }, + { + "id": "qwen_smart", + "label": "Smart", + "ollama": "qwen3.5:9b", + "gguf": "models/gguf/qwen3.5-9b-q4_k_m.gguf", + "params_b": 9, + "min_ram_gb": 16, + "min_vram_gb": 0, + "features": [ + "chat", + "vision", + "thinking" + ], + "default_context": 8192, + "allowed_backends": [ + "ollama", + "llama.cpp" + ] + }, + { + "id": "gemma_config_agent_12b", + "label": "Gemma 4 12B Config Agent", + "hf": "google/gemma-4-12B-it", + "ollama": "gemma4:12b", + "gguf": null, + "params_b": 11.95, + "min_ram_gb": 24, + "min_vram_gb": 0, + "features": [ + "chat", + "vision", + "thinking", + "audio", + "tools", + "agent", + "config_agent", + "model_selection" + ], + "default_context": 8192, + "max_native_context": 262144, + "recommended_usb_context": 8192, + "allowed_backends": [ + "ollama" + ], + "hackathon_eligible": true, + "agent_role": "LLM-in-the-loop assistant for hardware analysis, client model selection, and safe local configuration review.", + "notes": "Preferred local config-agent model when installed. It must be pulled by the builder or preloaded; the runtime never falls back to cloud." + }, + { + "id": "qwen_power_27b", + "label": "Power 27B", + "ollama": "qwen3.5:27b", + "gguf": "models/gguf/qwen3.5-27b-q4_k_m.gguf", + "params_b": 27, + "min_ram_gb": 32, + "min_vram_gb": 16, + "features": [ + "chat", + "vision", + "thinking", + "longer_answers" + ], + "default_context": 8192, + "allowed_backends": [ + "ollama", + "llama.cpp" + ] + }, + { + "id": "qwen36_power_27b", + "label": "Qwen3.6 Power 27B", + "hf": "Qwen/Qwen3.6-27B", + "ollama": "qwen3.6:27b", + "gguf": "models/gguf/qwen3.6-27b-q4_k_m.gguf", + "params_b": 27, + "min_ram_gb": 32, + "min_vram_gb": 24, + "features": [ + "chat", + "vision", + "thinking", + "coding", + "agentic_coding", + "power_user" + ], + "default_context": 8192, + "max_native_context": 262144, + "recommended_usb_context": 8192, + "allowed_backends": [ + "ollama", + "llama.cpp" + ], + "hackathon_eligible": true, + "notes": "Use as Power profile for RTX 3090-class or better machines. Keep USB default context low; the native 262K context is not a default USB setting." + }, + { + "id": "qwen_power_30b", + "label": "Power 30B", + "ollama": "qwen3:30b", + "gguf": "models/gguf/qwen3-30b-q4_k_m.gguf", + "params_b": 30, + "min_ram_gb": 48, + "min_vram_gb": 24, + "features": [ + "chat", + "thinking", + "power_user" + ], + "default_context": 8192, + "allowed_backends": [ + "ollama", + "llama.cpp" + ] + }, + { + "id": "qwen_vision", + "label": "Vision", + "ollama": "qwen3-vl:8b", + "gguf": null, + "params_b": 8, + "min_ram_gb": 16, + "min_vram_gb": 6, + "features": [ + "vision", + "image", + "document" + ], + "default_context": 4096, + "allowed_backends": [ + "ollama" + ] + }, + { + "id": "qwen_vision_pro_30b", + "label": "Vision Pro 30B", + "ollama": "qwen3-vl:30b", + "gguf": null, + "params_b": 30, + "min_ram_gb": 48, + "min_vram_gb": 24, + "features": [ + "vision", + "document", + "power_user" + ], + "default_context": 4096, + "allowed_backends": [ + "ollama" + ] + } + ], + "selection_rules": { + "ram_below_8": "qwen_fast", + "ram_8_to_15": "qwen_balanced", + "ram_16_plus": "qwen_smart", + "config_agent": "gemma_config_agent_12b if installed and RAM is sufficient; otherwise use another installed local chat model through Auto", + "vram_16_plus": "qwen_power_27b optional", + "vram_24_plus": "qwen_power_30b optional if package size and latency are acceptable", + "vision": "only enable if hardware supports the selected vision profile" + }, + "ui_features": { + "workspace": true, + "model_cookbook": true, + "documents": true, + "benchmark": true, + "settings": true, + "support": true, + "content_packs": true, + "field_manual": true, + "phone_access_optional": true, + "llm_config_agent": true + } +} + diff --git a/config/update-public-key.xml b/config/update-public-key.xml new file mode 100644 index 0000000000000000000000000000000000000000..ee4eda0378e2098c25231e0e01036bd2f13273de --- /dev/null +++ b/config/update-public-key.xml @@ -0,0 +1 @@ +vaeUBc0J1nCohjd9i9tyujmOQu3qLBP1B4K6o/cNSeF6BhetS2AaFxm/7UpjHlILo/Vzj1Z0ldyU6mGegb0rB26T4EcIoTEXam0bTYj8n4J8vCjIP6wd3w/a/2e8x4y7mpeygGmSvUhqxGJuR9py0U7XfIGOLHBeL/luO8wFjNIJqniNmKQcA7af40ej9yM5xo0Q8juFS8+ekvZCs0cnPA4YtrOUiY8mounP1e8yM7JdsdwDIpcoHUmen6usHuXMc1QLMvUqjoE+6VJkoT8r+EZhndst80w1MkLX3AG36hyN1SGKeDXEeftKj/qWkLoMFKlHliGPkqyLhzka+DQl7Q==AQAB diff --git a/config/voice-assets.json b/config/voice-assets.json new file mode 100644 index 0000000000000000000000000000000000000000..cb86f79a5991a5bacf3f64f2eaef50f13ed98f70 --- /dev/null +++ b/config/voice-assets.json @@ -0,0 +1,49 @@ +{ + "schema_version": 2, + "shared": { + "comment": "Model files are platform-independent (GGML/ONNX); only the binary archives below are per-platform.", + "whisper": { + "model_url": "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin", + "model_sha256": "60ed5bc3dd14eea856493d334349b405782ddcaf0028d4b5df4088345fba2efe" + }, + "piper": { + "voice_url": "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx", + "voice_sha256": "10bb85e071d616fcf4071f369f1799d0491492ab3c5d552ec19fb548fac13195", + "voice_config_url": "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/libritts_r/medium/en_US-libritts_r-medium.onnx.json", + "voice_config_sha256": "b471dc60d2d8335e819c393d196d6fbf792817f40051257b269878505bc9afb3" + } + }, + "windows_x64": { + "whisper": { + "version": "v1.8.6", + "archive_url": "https://github.com/ggml-org/whisper.cpp/releases/download/v1.8.6/whisper-bin-x64.zip", + "archive_sha256": "b07ea0b1b4115a38e1a7b07debf581f0b77d999925f8acb8f39d322b0ba0a822" + }, + "piper": { + "version": "2023.11.14-2", + "archive_url": "https://github.com/rhasspy/piper/releases/download/2023.11.14-2/piper_windows_amd64.zip", + "archive_sha256": "f3c58906402b24f3a96d92145f58acba6d86c9b5db896d207f78dc80811efcea" + } + }, + "linux_x86_64": { + "piper": { + "version": "2023.11.14-2", + "archive_url": "https://github.com/rhasspy/piper/releases/download/2023.11.14-2/piper_linux_x86_64.tar.gz", + "archive_sha256": "a50cb45f355b7af1f6d758c1b360717877ba0a398cc8cbe6d2a7a3a26e225992" + } + }, + "macos_aarch64": { + "piper": { + "version": "2023.11.14-2", + "archive_url": "https://github.com/rhasspy/piper/releases/download/2023.11.14-2/piper_macos_aarch64.tar.gz", + "archive_sha256": "6b1eb03b3735946cb35216e063e7eebcc33a6bbf5dd96ec0217959bf1cdcb0cc" + } + }, + "macos_x86_64": { + "piper": { + "version": "2023.11.14-2", + "archive_url": "https://github.com/rhasspy/piper/releases/download/2023.11.14-2/piper_macos_x64.tar.gz", + "archive_sha256": "ced85c0a3df13945b1e623b878a48fdc2854d5c485b4b67f62857cf551deaf8b" + } + } +} diff --git a/content-packs/prompts/business-operator.json b/content-packs/prompts/business-operator.json new file mode 100644 index 0000000000000000000000000000000000000000..bef9c1421b7511a3cdac0ba51517fc79da0c0eb6 --- /dev/null +++ b/content-packs/prompts/business-operator.json @@ -0,0 +1,9 @@ +{ + "id": "business_operator", + "label": "Business Operator", + "prompts": [ + "Turn these notes into a decision memo.", + "Draft a concise operating procedure from this process.", + "Identify risks, owners, and due dates in this plan." + ] +} diff --git a/content-packs/prompts/creator-studio.json b/content-packs/prompts/creator-studio.json new file mode 100644 index 0000000000000000000000000000000000000000..6a98534ed887e9574a4933741b690c5cd9ba5d94 --- /dev/null +++ b/content-packs/prompts/creator-studio.json @@ -0,0 +1,9 @@ +{ + "id": "creator_studio", + "label": "Creator Studio", + "prompts": [ + "Create three outlines for this idea.", + "Rewrite this draft for clarity without changing its meaning.", + "Build a publication checklist for this content." + ] +} diff --git a/content-packs/prompts/general-assistant.json b/content-packs/prompts/general-assistant.json new file mode 100644 index 0000000000000000000000000000000000000000..7adeab70f3a9dae56d190153de55125c2a66e42c --- /dev/null +++ b/content-packs/prompts/general-assistant.json @@ -0,0 +1,9 @@ +{ + "id": "general_assistant", + "label": "General Assistant", + "prompts": [ + "Summarize the following text and list the next actions.", + "Explain this topic in simple terms, then give a more technical explanation.", + "Create a checklist from these notes." + ] +} diff --git a/content-packs/prompts/outage.json b/content-packs/prompts/outage.json new file mode 100644 index 0000000000000000000000000000000000000000..9a80dad7d0c2835ac8bda51000bf8dd24668613e --- /dev/null +++ b/content-packs/prompts/outage.json @@ -0,0 +1,9 @@ +{ + "id": "outage", + "label": "Outage", + "prompts": [ + "Create a communications plan for a temporary power outage.", + "Prioritize these available supplies for the next 24 hours.", + "Turn this situation report into a concise action list." + ] +} diff --git a/content-packs/prompts/privacy.json b/content-packs/prompts/privacy.json new file mode 100644 index 0000000000000000000000000000000000000000..b20d85a3ad9935d979d9a1054ca9da8c796431e6 --- /dev/null +++ b/content-packs/prompts/privacy.json @@ -0,0 +1,9 @@ +{ + "id": "privacy", + "label": "Privacy", + "prompts": [ + "Identify sensitive information in this text before I share it.", + "Rewrite this note while removing personal identifiers.", + "Create a local-only workflow for this task." + ] +} diff --git a/content-packs/prompts/rapid-learning.json b/content-packs/prompts/rapid-learning.json new file mode 100644 index 0000000000000000000000000000000000000000..1b3e94af29fc0886b791d478dfd62374b2b9f94a --- /dev/null +++ b/content-packs/prompts/rapid-learning.json @@ -0,0 +1,9 @@ +{ + "id": "rapid_learning", + "label": "Rapid Learning", + "prompts": [ + "Build a one-hour learning plan for this topic.", + "List the prerequisite concepts I need first.", + "Create exercises that increase in difficulty." + ] +} diff --git a/content-packs/prompts/research.json b/content-packs/prompts/research.json new file mode 100644 index 0000000000000000000000000000000000000000..a4e3308badd90f12f7fef0d66edae73e395b5735 --- /dev/null +++ b/content-packs/prompts/research.json @@ -0,0 +1,9 @@ +{ + "id": "research", + "label": "Research", + "prompts": [ + "Extract claims, evidence, and unanswered questions from this text.", + "Compare these sources and identify disagreements.", + "Create a research log from these notes." + ] +} diff --git a/content-packs/prompts/study.json b/content-packs/prompts/study.json new file mode 100644 index 0000000000000000000000000000000000000000..c73251440eac8f57b504c3a208c5586f9f1dd588 --- /dev/null +++ b/content-packs/prompts/study.json @@ -0,0 +1,9 @@ +{ + "id": "study", + "label": "Study", + "prompts": [ + "Create flashcards from this material.", + "Quiz me on this topic one question at a time.", + "Explain where my reasoning is incomplete." + ] +} diff --git a/content-packs/prompts/survival-mode.json b/content-packs/prompts/survival-mode.json new file mode 100644 index 0000000000000000000000000000000000000000..b36e0c29e49f593bf4df33c8b36362eb1c9a5293 --- /dev/null +++ b/content-packs/prompts/survival-mode.json @@ -0,0 +1,6 @@ +{ + "name": "Survival quick reference", + "warning": "Educational reference only. Does not replace emergency services or professional medical advice.", + "categories": ["water", "fire", "first aid", "shelter", "signal", "food"], + "system_prompt": "You are an offline emergency reference assistant. Give concise, cautious, practical steps. Avoid dangerous overconfidence. Tell the user when professional help is required." +} diff --git a/content-packs/prompts/translation.json b/content-packs/prompts/translation.json new file mode 100644 index 0000000000000000000000000000000000000000..b4e5e1505cbe0034dd835873104f176ea38db5af --- /dev/null +++ b/content-packs/prompts/translation.json @@ -0,0 +1,9 @@ +{ + "id": "translation", + "label": "Translation", + "prompts": [ + "Translate this text while preserving formatting.", + "Explain ambiguous phrases before translating them.", + "Provide a literal translation and a natural translation." + ] +} diff --git a/content-packs/prompts/travel.json b/content-packs/prompts/travel.json new file mode 100644 index 0000000000000000000000000000000000000000..0acf5bb0eaf5723846bcc538788c61495794931f --- /dev/null +++ b/content-packs/prompts/travel.json @@ -0,0 +1,9 @@ +{ + "id": "travel", + "label": "Travel", + "prompts": [ + "Create a compact packing checklist for this trip.", + "Translate this message and keep the tone polite.", + "Turn these travel notes into a day-by-day plan." + ] +} diff --git a/content/field-manual/README.md b/content/field-manual/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c9e6e28f7dcae915500441aa77f31ef0ab328a2d --- /dev/null +++ b/content/field-manual/README.md @@ -0,0 +1,5 @@ +# Field Manual Core + +`cards.json` contains the bilingual offline reference cards served by the JackAILocal runtime. The cards are intentionally concise and include safety limits where professional help is required. + +Add offline reference cards here. diff --git a/content/field-manual/cards.json b/content/field-manual/cards.json new file mode 100644 index 0000000000000000000000000000000000000000..dc6e0dea0cf99b6705d7eee5746419ea9ca0887a --- /dev/null +++ b/content/field-manual/cards.json @@ -0,0 +1,172 @@ +{ + "product": "JackAILocal", + "version": 1, + "warning_en": "Educational emergency reference only. Contact local emergency services and qualified professionals whenever possible.", + "warning_fr": "Reference educative d'urgence seulement. Contactez les services d'urgence locaux et des professionnels qualifies lorsque possible.", + "cards": [ + { + "id": "water", + "title_en": "Water", + "title_fr": "Eau", + "summary_en": "Prioritize a safe water source, then remove particles and disinfect before drinking.", + "summary_fr": "Priorisez une source d'eau sure, puis retirez les particules et desinfectez avant de boire.", + "steps_en": [ + "Prefer treated municipal water, sealed containers, or a known safe source.", + "Let cloudy water settle and filter it through a clean cloth before disinfection.", + "Use a certified filter, boiling, or an approved disinfectant according to its label.", + "Store treated water in a clean, covered container." + ], + "steps_fr": [ + "Preferez l'eau municipale traitee, les contenants scelles ou une source reconnue sure.", + "Laissez decanter l'eau trouble et filtrez-la avec un tissu propre avant la desinfection.", + "Utilisez un filtre certifie, l'ebullition ou un desinfectant approuve selon son etiquette.", + "Conservez l'eau traitee dans un contenant propre et couvert." + ], + "warnings_en": [ + "Do not drink seawater, fuel-contaminated water, or water with a chemical odor.", + "Clear-looking water can still contain pathogens." + ], + "warnings_fr": [ + "Ne buvez pas d'eau de mer, d'eau contaminee par du carburant ou d'eau ayant une odeur chimique.", + "Une eau claire peut quand meme contenir des agents pathogenes." + ] + }, + { + "id": "fire", + "title_en": "Fire Safety", + "title_fr": "Securite incendie", + "summary_en": "Use fire only when conditions allow it and extinguish it completely.", + "summary_fr": "Utilisez le feu seulement lorsque les conditions le permettent et eteignez-le completement.", + "steps_en": [ + "Check local fire restrictions and avoid fire during dry, windy conditions.", + "Use an existing fire ring or clear a small area away from vegetation and structures.", + "Keep water or another extinguishing method within reach.", + "Drown, stir, and cool the remains until they are cold to the touch." + ], + "steps_fr": [ + "Verifiez les restrictions locales et evitez le feu par temps sec ou venteux.", + "Utilisez un foyer existant ou degagez une petite zone loin de la vegetation et des structures.", + "Gardez de l'eau ou un autre moyen d'extinction a portee.", + "Arrosez, remuez et refroidissez les restes jusqu'a ce qu'ils soient froids au toucher." + ], + "warnings_en": [ + "Never use a flame in an enclosed space without proper ventilation.", + "Never leave a fire unattended." + ], + "warnings_fr": [ + "N'utilisez jamais une flamme dans un espace ferme sans ventilation adequate.", + "Ne laissez jamais un feu sans surveillance." + ] + }, + { + "id": "first-aid", + "title_en": "First Aid Priorities", + "title_fr": "Priorites de premiers soins", + "summary_en": "Protect yourself, call for help, and address immediate life threats.", + "summary_fr": "Protegez-vous, appelez de l'aide et traitez les menaces immediates a la vie.", + "steps_en": [ + "Check that the scene is safe before approaching.", + "Call emergency services or send someone for help as early as possible.", + "Check responsiveness and normal breathing.", + "Control severe external bleeding with firm direct pressure and clean material.", + "Keep the person warm and monitor them until help arrives." + ], + "steps_fr": [ + "Verifiez que les lieux sont securitaires avant d'approcher.", + "Appelez les services d'urgence ou envoyez quelqu'un chercher de l'aide des que possible.", + "Verifiez la reaction et la respiration normale.", + "Controlez un saignement externe important avec une pression directe ferme et un materiau propre.", + "Gardez la personne au chaud et surveillez-la jusqu'a l'arrivee des secours." + ], + "warnings_en": [ + "This card does not replace certified first-aid training.", + "Do not give food or drink to an unconscious or seriously injured person." + ], + "warnings_fr": [ + "Cette carte ne remplace pas une formation certifiee en premiers soins.", + "Ne donnez pas de nourriture ni de boisson a une personne inconsciente ou gravement blessee." + ] + }, + { + "id": "shelter", + "title_en": "Shelter", + "title_fr": "Abri", + "summary_en": "Reduce exposure to wind, rain, cold, heat, and ground moisture.", + "summary_fr": "Reduisez l'exposition au vent, a la pluie, au froid, a la chaleur et a l'humidite du sol.", + "steps_en": [ + "Choose stable ground away from flood channels, dead trees, cliffs, and avalanche paths.", + "Use available clothing, tarps, or natural cover to block wind and precipitation.", + "Insulate yourself from the ground with dry material.", + "Ventilate enclosed shelters and keep combustion outside." + ], + "steps_fr": [ + "Choisissez un sol stable loin des chenaux d'inondation, arbres morts, falaises et couloirs d'avalanche.", + "Utilisez des vetements, baches ou protections naturelles pour bloquer le vent et les precipitations.", + "Isolez-vous du sol avec un materiau sec.", + "Ventilez les abris fermes et gardez toute combustion a l'exterieur." + ], + "warnings_en": [ + "Carbon monoxide is odorless and can be fatal.", + "Do not shelter in an area that may flood." + ], + "warnings_fr": [ + "Le monoxyde de carbone est inodore et peut etre mortel.", + "Ne vous abritez pas dans une zone pouvant etre inondee." + ] + }, + { + "id": "signal", + "title_en": "Signal for Help", + "title_fr": "Signaler sa position", + "summary_en": "Make your location easy to find while conserving energy and battery.", + "summary_fr": "Rendez votre position facile a trouver tout en economisant energie et batterie.", + "steps_en": [ + "Call or text emergency services when a network is available and share your location.", + "Use a whistle, bright material, mirror, or flashlight to attract attention.", + "Three repeated signals are commonly recognized as a distress pattern.", + "If it is safe, remain near a known route or your last known location." + ], + "steps_fr": [ + "Appelez ou textez les services d'urgence lorsqu'un reseau est disponible et partagez votre position.", + "Utilisez un sifflet, un materiau voyant, un miroir ou une lampe pour attirer l'attention.", + "Trois signaux repetes sont souvent reconnus comme un motif de detresse.", + "Si c'est securitaire, restez pres d'un itineraire connu ou de votre derniere position connue." + ], + "warnings_en": [ + "Do not create a signal fire when fire conditions are unsafe.", + "Avoid unnecessary travel if rescuers know your last location." + ], + "warnings_fr": [ + "Ne creez pas de feu de signalisation lorsque les conditions sont dangereuses.", + "Evitez les deplacements inutiles si les secouristes connaissent votre derniere position." + ] + }, + { + "id": "food", + "title_en": "Food Safety", + "title_fr": "Securite alimentaire", + "summary_en": "Prevent contamination and avoid unknown plants, fungi, and animals.", + "summary_fr": "Evitez la contamination et les plantes, champignons et animaux inconnus.", + "steps_en": [ + "Use sealed, known food before attempting to forage.", + "Keep raw foods separate from ready-to-eat foods.", + "Cook food thoroughly and keep utensils and hands clean.", + "Discard food with unusual odor, swelling, leakage, or visible spoilage." + ], + "steps_fr": [ + "Utilisez d'abord des aliments scelles et connus avant de tenter la cueillette.", + "Separez les aliments crus des aliments prets a manger.", + "Cuisez les aliments completement et gardez les ustensiles et les mains propres.", + "Jetez les aliments ayant une odeur inhabituelle, un gonflement, une fuite ou une deterioration visible." + ], + "warnings_en": [ + "Do not eat a wild plant or mushroom unless it has been positively identified by a qualified expert.", + "Food is usually less urgent than water, shelter, and medical needs." + ], + "warnings_fr": [ + "Ne mangez pas une plante ou un champignon sauvage sans identification certaine par un expert qualifie.", + "La nourriture est generalement moins urgente que l'eau, l'abri et les besoins medicaux." + ] + } + ] +} diff --git a/data/models-cache.json b/data/models-cache.json new file mode 100644 index 0000000000000000000000000000000000000000..c7bdda48a981f83d1ce25a358b8a717957724fc6 --- /dev/null +++ b/data/models-cache.json @@ -0,0 +1,34000 @@ +{ + "timestamp": 1780544259814, + "models": [ + { + "id": "qwen/qwen3.7-plus", + "name": "Qwen: Qwen3.7 Plus", + "provider": "qwen", + "ctx": 1000000, + "maxOut": 65536, + "pIn": 0.39999999999999997, + "pOut": 1.5999999999999999, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "minimax/minimax-m3", + "name": "MiniMax: MiniMax M3", + "provider": "minimax", + "ctx": 1048576, + "maxOut": 512000, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "stepfun/step-3.7-flash", + "name": "StepFun: Step 3.7 Flash", + "provider": "stepfun", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 1.15, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-opus-4.8-fast", + "name": "Anthropic: Claude Opus 4.8 (Fast)", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 10, + "pOut": 50, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-opus-4.8", + "name": "Anthropic: Claude Opus 4.8", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.7-max", + "name": "Qwen: Qwen3.7 Max", + "provider": "qwen", + "ctx": 1000000, + "maxOut": 65536, + "pIn": 1.25, + "pOut": 3.75, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "x-ai/grok-build-0.1", + "name": "xAI: Grok Build 0.1", + "provider": "x-ai", + "ctx": 256000, + "maxOut": null, + "pIn": 1, + "pOut": 2, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-3.5-flash", + "name": "Google: Gemini 3.5 Flash", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 1.5, + "pOut": 9, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-opus-4.7-fast", + "name": "Anthropic: Claude Opus 4.7 (Fast)", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 30, + "pOut": 150, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openrouter/fusion", + "name": "OpenRouter: Fusion", + "provider": "openrouter", + "ctx": 128000, + "maxOut": null, + "pIn": -1000000, + "pOut": -1000000, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "perceptron/perceptron-mk1", + "name": "Perceptron: Perceptron Mk1", + "provider": "perceptron", + "ctx": 32768, + "maxOut": 8192, + "pIn": 0.15, + "pOut": 1.5, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "inclusionai/ring-2.6-1t", + "name": "inclusionAI: Ring-2.6-1T", + "provider": "inclusionai", + "ctx": 262144, + "maxOut": 65536, + "pIn": 0.075, + "pOut": 0.625, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-3.1-flash-lite", + "name": "Google: Gemini 3.1 Flash Lite", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.25, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-chat-latest", + "name": "OpenAI: GPT Chat Latest", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 5, + "pOut": 30, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "x-ai/grok-4.3", + "name": "xAI: Grok 4.3", + "provider": "x-ai", + "ctx": 1000000, + "maxOut": null, + "pIn": 1.25, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "ibm-granite/granite-4.1-8b", + "name": "IBM: Granite 4.1 8B", + "provider": "ibm-granite", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.049999999999999996, + "pOut": 0.09999999999999999, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/mistral-medium-3-5", + "name": "Mistral: Mistral Medium 3.5", + "provider": "mistral", + "ctx": 262144, + "maxOut": null, + "pIn": 1.5, + "pOut": 7.5, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openrouter/owl-alpha", + "name": "Owl Alpha", + "provider": "openrouter", + "ctx": 1048756, + "maxOut": 262144, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free", + "name": "NVIDIA: Nemotron 3 Nano Omni (free)", + "provider": "nvidia", + "ctx": 256000, + "maxOut": 65536, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "poolside/laguna-xs.2:free", + "name": "Poolside: Laguna XS.2 (free)", + "provider": "poolside", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "poolside/laguna-m.1:free", + "name": "Poolside: Laguna M.1 (free)", + "provider": "poolside", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "~anthropic/claude-haiku-latest", + "name": "Anthropic Claude Haiku Latest", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "~openai/gpt-mini-latest", + "name": "OpenAI GPT Mini Latest", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 0.75, + "pOut": 4.5, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "~google/gemini-pro-latest", + "name": "Google Gemini Pro Latest", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "~moonshotai/kimi-latest", + "name": "MoonshotAI Kimi Latest", + "provider": "~moonshotai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.684, + "pOut": 3.42, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "~google/gemini-flash-latest", + "name": "Google Gemini Flash Latest", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 1.5, + "pOut": 9, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "~anthropic/claude-sonnet-latest", + "name": "Anthropic Claude Sonnet Latest", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "~openai/gpt-latest", + "name": "OpenAI GPT Latest", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 5, + "pOut": 30, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.5-plus-20260420", + "name": "Qwen: Qwen3.5 Plus 2026-04-20", + "provider": "qwen", + "ctx": 1000000, + "maxOut": 65536, + "pIn": 0.3, + "pOut": 1.7999999999999998, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.6-flash", + "name": "Qwen: Qwen3.6 Flash", + "provider": "qwen", + "ctx": 1000000, + "maxOut": 65536, + "pIn": 0.1875, + "pOut": 1.125, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.6-35b-a3b", + "name": "Qwen: Qwen3.6 35B A3B", + "provider": "qwen", + "ctx": 262144, + "maxOut": 262140, + "pIn": 0.14, + "pOut": 1, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.6-max-preview", + "name": "Qwen: Qwen3.6 Max Preview", + "provider": "qwen", + "ctx": 262144, + "maxOut": 65536, + "pIn": 1.04, + "pOut": 6.24, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.6-27b", + "name": "Qwen: Qwen3.6 27B", + "provider": "qwen", + "ctx": 262144, + "maxOut": 262140, + "pIn": 0.29, + "pOut": 3.1999999999999997, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.5-pro", + "name": "OpenAI: GPT-5.5 Pro", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 30, + "pOut": 180, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.5", + "name": "OpenAI: GPT-5.5", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 5, + "pOut": 30, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "deepseek/deepseek-v4-pro", + "name": "DeepSeek: DeepSeek V4 Pro", + "provider": "deepseek", + "ctx": 1048576, + "maxOut": 384000, + "pIn": 0.435, + "pOut": 0.87, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "deepseek/deepseek-v4-flash", + "name": "DeepSeek: DeepSeek V4 Flash", + "provider": "deepseek", + "ctx": 1048576, + "maxOut": 131072, + "pIn": 0.0983, + "pOut": 0.1966, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "inclusionai/ling-2.6-1t", + "name": "inclusionAI: Ling-2.6-1T", + "provider": "inclusionai", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0.075, + "pOut": 0.625, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "tencent/hy3-preview", + "name": "Tencent: Hy3 preview", + "provider": "tencent", + "ctx": 262144, + "maxOut": null, + "pIn": 0.063, + "pOut": 0.21, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "xiaomi/mimo-v2.5-pro", + "name": "Xiaomi: MiMo-V2.5-Pro", + "provider": "xiaomi", + "ctx": 1048576, + "maxOut": 131072, + "pIn": 0.435, + "pOut": 0.87, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "xiaomi/mimo-v2.5", + "name": "Xiaomi: MiMo-V2.5", + "provider": "xiaomi", + "ctx": 1048576, + "maxOut": 131072, + "pIn": 0.14, + "pOut": 0.28, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.4-image-2", + "name": "OpenAI: GPT-5.4 Image 2", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 8, + "pOut": 15, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "inclusionai/ling-2.6-flash", + "name": "inclusionAI: Ling-2.6-flash", + "provider": "inclusionai", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0.01, + "pOut": 0.03, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "~anthropic/claude-opus-latest", + "name": "Anthropic: Claude Opus Latest", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openrouter/pareto-code", + "name": "Pareto Code Router", + "provider": "openrouter", + "ctx": 2000000, + "maxOut": null, + "pIn": -1000000, + "pOut": -1000000, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "moonshotai/kimi-k2.6:free", + "name": "MoonshotAI: Kimi K2.6 (free)", + "provider": "moonshotai", + "ctx": 262144, + "maxOut": null, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "moonshotai/kimi-k2.6", + "name": "MoonshotAI: Kimi K2.6", + "provider": "moonshotai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.684, + "pOut": 3.42, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-opus-4.7", + "name": "Anthropic: Claude Opus 4.7", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-opus-4.6-fast", + "name": "Anthropic: Claude Opus 4.6 (Fast)", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 30, + "pOut": 150, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "z-ai/glm-5.1", + "name": "Z.ai: GLM 5.1", + "provider": "z-ai", + "ctx": 202752, + "maxOut": null, + "pIn": 0.98, + "pOut": 3.08, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemma-4-26b-a4b-it:free", + "name": "Google: Gemma 4 26B A4B (free)", + "provider": "gemini", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemma-4-26b-a4b-it", + "name": "Google: Gemma 4 26B A4B ", + "provider": "gemini", + "ctx": 262144, + "maxOut": null, + "pIn": 0.06, + "pOut": 0.33, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemma-4-31b-it:free", + "name": "Google: Gemma 4 31B (free)", + "provider": "gemini", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemma-4-31b-it", + "name": "Google: Gemma 4 31B", + "provider": "gemini", + "ctx": 262144, + "maxOut": 16384, + "pIn": 0.12, + "pOut": 0.37, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.6-plus", + "name": "Qwen: Qwen3.6 Plus", + "provider": "qwen", + "ctx": 1000000, + "maxOut": 65536, + "pIn": 0.325, + "pOut": 1.95, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "z-ai/glm-5v-turbo", + "name": "Z.ai: GLM 5V Turbo", + "provider": "z-ai", + "ctx": 202752, + "maxOut": 131072, + "pIn": 1.2, + "pOut": 4, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "arcee-ai/trinity-large-thinking", + "name": "Arcee AI: Trinity Large Thinking", + "provider": "arcee-ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.22, + "pOut": 0.85, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "x-ai/grok-4.20-multi-agent", + "name": "xAI: Grok 4.20 Multi-Agent", + "provider": "x-ai", + "ctx": 2000000, + "maxOut": null, + "pIn": 2, + "pOut": 6, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "x-ai/grok-4.20", + "name": "xAI: Grok 4.20", + "provider": "x-ai", + "ctx": 2000000, + "maxOut": null, + "pIn": 1.25, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/lyria-3-pro-preview", + "name": "Google: Lyria 3 Pro Preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0, + "pOut": 0, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "google/lyria-3-clip-preview", + "name": "Google: Lyria 3 Clip Preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0, + "pOut": 0, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "kwaipilot/kat-coder-pro-v2", + "name": "Kwaipilot: KAT-Coder-Pro V2", + "provider": "kwaipilot", + "ctx": 256000, + "maxOut": 80000, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "rekaai/reka-edge", + "name": "Reka Edge", + "provider": "rekaai", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "minimax/minimax-m2.7", + "name": "MiniMax: MiniMax M2.7", + "provider": "minimax", + "ctx": 204800, + "maxOut": 131072, + "pIn": 0.27899999999999997, + "pOut": 1.2, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.4-nano", + "name": "OpenAI: GPT-5.4 Nano", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 1.25, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.4-mini", + "name": "OpenAI: GPT-5.4 Mini", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 0.75, + "pOut": 4.5, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/mistral-small-2603", + "name": "Mistral: Mistral Small 4", + "provider": "mistral", + "ctx": 262144, + "maxOut": null, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "z-ai/glm-5-turbo", + "name": "Z.ai: GLM 5 Turbo", + "provider": "z-ai", + "ctx": 202752, + "maxOut": 131072, + "pIn": 1.2, + "pOut": 4, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "nvidia/nemotron-3-super-120b-a12b:free", + "name": "NVIDIA: Nemotron 3 Super (free)", + "provider": "nvidia", + "ctx": 1000000, + "maxOut": 262144, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "nvidia/nemotron-3-super-120b-a12b", + "name": "NVIDIA: Nemotron 3 Super", + "provider": "nvidia", + "ctx": 1000000, + "maxOut": null, + "pIn": 0.09, + "pOut": 0.44999999999999996, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "bytedance-seed/seed-2.0-lite", + "name": "ByteDance Seed: Seed-2.0-Lite", + "provider": "bytedance-seed", + "ctx": 262144, + "maxOut": 131072, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.5-9b", + "name": "Qwen: Qwen3.5-9B", + "provider": "qwen", + "ctx": 262144, + "maxOut": 81920, + "pIn": 0.04, + "pOut": 0.15, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.4-pro", + "name": "OpenAI: GPT-5.4 Pro", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 30, + "pOut": 180, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.4", + "name": "OpenAI: GPT-5.4", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 2.5, + "pOut": 15, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "inception/mercury-2", + "name": "Inception: Mercury 2", + "provider": "inception", + "ctx": 128000, + "maxOut": 50000, + "pIn": 0.25, + "pOut": 0.75, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.3-chat", + "name": "OpenAI: GPT-5.3 Chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-3.1-flash-lite-preview", + "name": "Google: Gemini 3.1 Flash Lite Preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.25, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "bytedance-seed/seed-2.0-mini", + "name": "ByteDance Seed: Seed-2.0-Mini", + "provider": "bytedance-seed", + "ctx": 262144, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-3.1-flash-image-preview", + "name": "Google: Nano Banana 2 (Gemini 3.1 Flash Image Preview)", + "provider": "gemini", + "ctx": 131072, + "maxOut": 65536, + "pIn": 0.5, + "pOut": 3, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.5-35b-a3b", + "name": "Qwen: Qwen3.5-35B-A3B", + "provider": "qwen", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.14, + "pOut": 1, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.5-27b", + "name": "Qwen: Qwen3.5-27B", + "provider": "qwen", + "ctx": 262144, + "maxOut": 65536, + "pIn": 0.195, + "pOut": 1.56, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.5-122b-a10b", + "name": "Qwen: Qwen3.5-122B-A10B", + "provider": "qwen", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.26, + "pOut": 2.08, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.5-flash-02-23", + "name": "Qwen: Qwen3.5-Flash", + "provider": "qwen", + "ctx": 1000000, + "maxOut": 65536, + "pIn": 0.065, + "pOut": 0.26, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "liquid/lfm-2-24b-a2b", + "name": "LiquidAI: LFM2-24B-A2B", + "provider": "liquid", + "ctx": 128000, + "maxOut": null, + "pIn": 0.03, + "pOut": 0.12, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-3.1-pro-preview-customtools", + "name": "Google: Gemini 3.1 Pro Preview Custom Tools", + "provider": "gemini", + "ctx": 1048756, + "maxOut": 65536, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.3-codex", + "name": "OpenAI: GPT-5.3-Codex", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "aion-labs/aion-2.0", + "name": "AionLabs: Aion-2.0", + "provider": "aion-labs", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.7999999999999999, + "pOut": 1.5999999999999999, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-3.1-pro-preview", + "name": "Google: Gemini 3.1 Pro Preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-sonnet-4.6", + "name": "Anthropic: Claude Sonnet 4.6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.5-plus-02-15", + "name": "Qwen: Qwen3.5 Plus 2026-02-15", + "provider": "qwen", + "ctx": 1000000, + "maxOut": 65536, + "pIn": 0.26, + "pOut": 1.56, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3.5-397b-a17b", + "name": "Qwen: Qwen3.5 397B A17B", + "provider": "qwen", + "ctx": 262144, + "maxOut": 65536, + "pIn": 0.39, + "pOut": 2.34, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "minimax/minimax-m2.5", + "name": "MiniMax: MiniMax M2.5", + "provider": "minimax", + "ctx": 204800, + "maxOut": 196608, + "pIn": 0.15, + "pOut": 1.15, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "z-ai/glm-5", + "name": "Z.ai: GLM 5", + "provider": "z-ai", + "ctx": 202752, + "maxOut": null, + "pIn": 0.6, + "pOut": 1.92, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-max-thinking", + "name": "Qwen: Qwen3 Max Thinking", + "provider": "qwen", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0.78, + "pOut": 3.9, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-opus-4.6", + "name": "Anthropic: Claude Opus 4.6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-coder-next", + "name": "Qwen: Qwen3 Coder Next", + "provider": "qwen", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.11, + "pOut": 0.7999999999999999, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openrouter/free", + "name": "Free Models Router", + "provider": "openrouter", + "ctx": 200000, + "maxOut": null, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "stepfun/step-3.5-flash", + "name": "StepFun: Step 3.5 Flash", + "provider": "stepfun", + "ctx": 262144, + "maxOut": 16384, + "pIn": 0.09, + "pOut": 0.3, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "moonshotai/kimi-k2.5", + "name": "MoonshotAI: Kimi K2.5", + "provider": "moonshotai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.39999999999999997, + "pOut": 1.9, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "upstage/solar-pro-3", + "name": "Upstage: Solar Pro 3", + "provider": "upstage", + "ctx": 128000, + "maxOut": null, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "minimax/minimax-m2-her", + "name": "MiniMax: MiniMax M2-her", + "provider": "minimax", + "ctx": 65536, + "maxOut": 2048, + "pIn": 0.3, + "pOut": 1.2, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "writer/palmyra-x5", + "name": "Writer: Palmyra X5", + "provider": "writer", + "ctx": 1040000, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 6, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "liquid/lfm-2.5-1.2b-thinking:free", + "name": "LiquidAI: LFM2.5-1.2B-Thinking (free)", + "provider": "liquid", + "ctx": 32768, + "maxOut": null, + "pIn": 0, + "pOut": 0, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "liquid/lfm-2.5-1.2b-instruct:free", + "name": "LiquidAI: LFM2.5-1.2B-Instruct (free)", + "provider": "liquid", + "ctx": 32768, + "maxOut": null, + "pIn": 0, + "pOut": 0, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-audio", + "name": "OpenAI: GPT Audio", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-audio-mini", + "name": "OpenAI: GPT Audio Mini", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.6, + "pOut": 2.4, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "z-ai/glm-4.7-flash", + "name": "Z.ai: GLM 4.7 Flash", + "provider": "z-ai", + "ctx": 202752, + "maxOut": 16384, + "pIn": 0.06, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.2-codex", + "name": "OpenAI: GPT-5.2-Codex", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "bytedance-seed/seed-1.6-flash", + "name": "ByteDance Seed: Seed 1.6 Flash", + "provider": "bytedance-seed", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0.075, + "pOut": 0.3, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "bytedance-seed/seed-1.6", + "name": "ByteDance Seed: Seed 1.6", + "provider": "bytedance-seed", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "minimax/minimax-m2.1", + "name": "MiniMax: MiniMax M2.1", + "provider": "minimax", + "ctx": 204800, + "maxOut": 196608, + "pIn": 0.29, + "pOut": 0.95, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "z-ai/glm-4.7", + "name": "Z.ai: GLM 4.7", + "provider": "z-ai", + "ctx": 202752, + "maxOut": 131072, + "pIn": 0.39999999999999997, + "pOut": 1.75, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-3-flash-preview", + "name": "Google: Gemini 3 Flash Preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.5, + "pOut": 3, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "xiaomi/mimo-v2-flash", + "name": "Xiaomi: MiMo-V2-Flash", + "provider": "xiaomi", + "ctx": 262144, + "maxOut": 65536, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "nvidia/nemotron-3-nano-30b-a3b:free", + "name": "NVIDIA: Nemotron 3 Nano 30B A3B (free)", + "provider": "nvidia", + "ctx": 256000, + "maxOut": null, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "nvidia/nemotron-3-nano-30b-a3b", + "name": "NVIDIA: Nemotron 3 Nano 30B A3B", + "provider": "nvidia", + "ctx": 262144, + "maxOut": 228000, + "pIn": 0.049999999999999996, + "pOut": 0.19999999999999998, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.2-chat", + "name": "OpenAI: GPT-5.2 Chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.2-pro", + "name": "OpenAI: GPT-5.2 Pro", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 21, + "pOut": 168, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.2", + "name": "OpenAI: GPT-5.2", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/devstral-2512", + "name": "Mistral: Devstral 2 2512", + "provider": "mistral", + "ctx": 262144, + "maxOut": null, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "relace/relace-search", + "name": "Relace: Relace Search", + "provider": "relace", + "ctx": 256000, + "maxOut": 128000, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "z-ai/glm-4.6v", + "name": "Z.ai: GLM 4.6V", + "provider": "z-ai", + "ctx": 131072, + "maxOut": 24000, + "pIn": 0.3, + "pOut": 0.8999999999999999, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "nex-agi/deepseek-v3.1-nex-n1", + "name": "Nex AGI: DeepSeek V3.1 Nex N1", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 163840, + "pIn": 0.135, + "pOut": 0.5, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "essentialai/rnj-1-instruct", + "name": "EssentialAI: Rnj 1 Instruct", + "provider": "essentialai", + "ctx": 32768, + "maxOut": null, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openrouter/bodybuilder", + "name": "Body Builder (beta)", + "provider": "openrouter", + "ctx": 128000, + "maxOut": null, + "pIn": -1000000, + "pOut": -1000000, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.1-codex-max", + "name": "OpenAI: GPT-5.1-Codex-Max", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "amazon/nova-2-lite-v1", + "name": "Amazon: Nova 2 Lite", + "provider": "amazon", + "ctx": 1000000, + "maxOut": 65535, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/ministral-14b-2512", + "name": "Mistral: Ministral 3 14B 2512", + "provider": "mistral", + "ctx": 262144, + "maxOut": null, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/ministral-8b-2512", + "name": "Mistral: Ministral 3 8B 2512", + "provider": "mistral", + "ctx": 262144, + "maxOut": null, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/ministral-3b-2512", + "name": "Mistral: Ministral 3 3B 2512", + "provider": "mistral", + "ctx": 131072, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/mistral-large-2512", + "name": "Mistral: Mistral Large 3 2512", + "provider": "mistral", + "ctx": 262144, + "maxOut": null, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "arcee-ai/trinity-mini", + "name": "Arcee AI: Trinity Mini", + "provider": "arcee-ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.045, + "pOut": 0.15, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "deepseek/deepseek-v3.2", + "name": "DeepSeek: DeepSeek V3.2", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 64000, + "pIn": 0.2288, + "pOut": 0.3432, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "prime-intellect/intellect-3", + "name": "Prime Intellect: INTELLECT-3", + "provider": "prime-intellect", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 1.1, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-opus-4.5", + "name": "Anthropic: Claude Opus 4.5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "allenai/olmo-3-32b-think", + "name": "AllenAI: Olmo 3 32B Think", + "provider": "allenai", + "ctx": 65536, + "maxOut": 65536, + "pIn": 0.15, + "pOut": 0.5, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-3-pro-image-preview", + "name": "Google: Nano Banana Pro (Gemini 3 Pro Image Preview)", + "provider": "gemini", + "ctx": 65536, + "maxOut": 32768, + "pIn": 2, + "pOut": 12, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "deepcogito/cogito-v2.1-671b", + "name": "Deep Cogito: Cogito v2.1 671B", + "provider": "deepcogito", + "ctx": 128000, + "maxOut": null, + "pIn": 1.25, + "pOut": 1.25, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.1", + "name": "OpenAI: GPT-5.1", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.1-chat", + "name": "OpenAI: GPT-5.1 Chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 32000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.1-codex", + "name": "OpenAI: GPT-5.1-Codex", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5.1-codex-mini", + "name": "OpenAI: GPT-5.1-Codex-Mini", + "provider": "openai", + "ctx": 400000, + "maxOut": 100000, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "moonshotai/kimi-k2-thinking", + "name": "MoonshotAI: Kimi K2 Thinking", + "provider": "moonshotai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "amazon/nova-premier-v1", + "name": "Amazon: Nova Premier 1.0", + "provider": "amazon", + "ctx": 1000000, + "maxOut": 32000, + "pIn": 2.5, + "pOut": 12.5, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "perplexity/sonar-pro-search", + "name": "Perplexity: Sonar Pro Search", + "provider": "perplexity", + "ctx": 200000, + "maxOut": 8000, + "pIn": 3, + "pOut": 15, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/voxtral-small-24b-2507", + "name": "Mistral: Voxtral Small 24B 2507", + "provider": "mistral", + "ctx": 32000, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-oss-safeguard-20b", + "name": "OpenAI: gpt-oss-safeguard-20b", + "provider": "openai", + "ctx": 131072, + "maxOut": 65536, + "pIn": 0.075, + "pOut": 0.3, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "nvidia/nemotron-nano-12b-v2-vl:free", + "name": "NVIDIA: Nemotron Nano 12B 2 VL (free)", + "provider": "nvidia", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "minimax/minimax-m2", + "name": "MiniMax: MiniMax M2", + "provider": "minimax", + "ctx": 204800, + "maxOut": 196608, + "pIn": 0.255, + "pOut": 1, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-vl-32b-instruct", + "name": "Qwen: Qwen3 VL 32B Instruct", + "provider": "qwen", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0.10400000000000001, + "pOut": 0.41600000000000004, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "ibm-granite/granite-4.0-h-micro", + "name": "IBM: Granite 4.0 Micro", + "provider": "ibm-granite", + "ctx": 131000, + "maxOut": 131000, + "pIn": 0.017, + "pOut": 0.112, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "microsoft/phi-4-mini-instruct", + "name": "Microsoft: Phi 4 Mini Instruct", + "provider": "microsoft", + "ctx": 131072, + "maxOut": 128000, + "pIn": 0.08, + "pOut": 0.35, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5-image-mini", + "name": "OpenAI: GPT-5 Image Mini", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 2.5, + "pOut": 2, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-haiku-4.5", + "name": "Anthropic: Claude Haiku 4.5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-vl-8b-thinking", + "name": "Qwen: Qwen3 VL 8B Thinking", + "provider": "qwen", + "ctx": 256000, + "maxOut": 32768, + "pIn": 0.117, + "pOut": 1.365, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-vl-8b-instruct", + "name": "Qwen: Qwen3 VL 8B Instruct", + "provider": "qwen", + "ctx": 256000, + "maxOut": 32768, + "pIn": 0.08, + "pOut": 0.5, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5-image", + "name": "OpenAI: GPT-5 Image", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 10, + "pOut": 10, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/o3-deep-research", + "name": "OpenAI: o3 Deep Research", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 10, + "pOut": 40, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/o4-mini-deep-research", + "name": "OpenAI: o4 Mini Deep Research", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "nvidia/llama-3.3-nemotron-super-49b-v1.5", + "name": "NVIDIA: Llama 3.3 Nemotron Super 49B V1.5", + "provider": "meta", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-2.5-flash-image", + "name": "Google: Nano Banana (Gemini 2.5 Flash Image)", + "provider": "gemini", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.3, + "pOut": 2.5, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-vl-30b-a3b-thinking", + "name": "Qwen: Qwen3 VL 30B A3B Thinking", + "provider": "qwen", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.13, + "pOut": 1.56, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-vl-30b-a3b-instruct", + "name": "Qwen: Qwen3 VL 30B A3B Instruct", + "provider": "qwen", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0.13, + "pOut": 0.52, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5-pro", + "name": "OpenAI: GPT-5 Pro", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 15, + "pOut": 120, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "z-ai/glm-4.6", + "name": "Z.ai: GLM 4.6", + "provider": "z-ai", + "ctx": 202752, + "maxOut": 131072, + "pIn": 0.43, + "pOut": 1.74, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-sonnet-4.5", + "name": "Anthropic: Claude Sonnet 4.5", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "deepseek/deepseek-v3.2-exp", + "name": "DeepSeek: DeepSeek V3.2 Exp", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 65536, + "pIn": 0.27, + "pOut": 0.41, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "thedrummer/cydonia-24b-v4.1", + "name": "TheDrummer: Cydonia 24B V4.1", + "provider": "thedrummer", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.3, + "pOut": 0.5, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "relace/relace-apply-3", + "name": "Relace: Relace Apply 3", + "provider": "relace", + "ctx": 256000, + "maxOut": 128000, + "pIn": 0.85, + "pOut": 1.25, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-2.5-flash-lite-preview-09-2025", + "name": "Google: Gemini 2.5 Flash Lite Preview 09-2025", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-vl-235b-a22b-thinking", + "name": "Qwen: Qwen3 VL 235B A22B Thinking", + "provider": "qwen", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.26, + "pOut": 2.6, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-vl-235b-a22b-instruct", + "name": "Qwen: Qwen3 VL 235B A22B Instruct", + "provider": "qwen", + "ctx": 262144, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.88, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-max", + "name": "Qwen: Qwen3 Max", + "provider": "qwen", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0.78, + "pOut": 3.9, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-coder-plus", + "name": "Qwen: Qwen3 Coder Plus", + "provider": "qwen", + "ctx": 1000000, + "maxOut": 65536, + "pIn": 0.65, + "pOut": 3.25, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5-codex", + "name": "OpenAI: GPT-5 Codex", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "deepseek/deepseek-v3.1-terminus", + "name": "DeepSeek: DeepSeek V3.1 Terminus", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 32768, + "pIn": 0.27, + "pOut": 0.95, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-coder-flash", + "name": "Qwen: Qwen3 Coder Flash", + "provider": "qwen", + "ctx": 1000000, + "maxOut": 65536, + "pIn": 0.195, + "pOut": 0.975, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-next-80b-a3b-thinking", + "name": "Qwen: Qwen3 Next 80B A3B Thinking", + "provider": "qwen", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0.0975, + "pOut": 0.78, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-next-80b-a3b-instruct:free", + "name": "Qwen: Qwen3 Next 80B A3B Instruct (free)", + "provider": "qwen", + "ctx": 262144, + "maxOut": null, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-next-80b-a3b-instruct", + "name": "Qwen: Qwen3 Next 80B A3B Instruct", + "provider": "qwen", + "ctx": 262144, + "maxOut": 16384, + "pIn": 0.09, + "pOut": 1.1, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen-plus-2025-07-28:thinking", + "name": "Qwen: Qwen Plus 0728 (thinking)", + "provider": "qwen", + "ctx": 1000000, + "maxOut": 32768, + "pIn": 0.26, + "pOut": 0.78, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen-plus-2025-07-28", + "name": "Qwen: Qwen Plus 0728", + "provider": "qwen", + "ctx": 1000000, + "maxOut": 32768, + "pIn": 0.26, + "pOut": 0.78, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "nvidia/nemotron-nano-9b-v2:free", + "name": "NVIDIA: Nemotron Nano 9B V2 (free)", + "provider": "nvidia", + "ctx": 128000, + "maxOut": null, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "nvidia/nemotron-nano-9b-v2", + "name": "NVIDIA: Nemotron Nano 9B V2", + "provider": "nvidia", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.04, + "pOut": 0.16, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "moonshotai/kimi-k2-0905", + "name": "MoonshotAI: Kimi K2 0905", + "provider": "moonshotai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-30b-a3b-thinking-2507", + "name": "Qwen: Qwen3 30B A3B Thinking 2507", + "provider": "qwen", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.08, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "nousresearch/hermes-4-70b", + "name": "Nous: Hermes 4 70B", + "provider": "nousresearch", + "ctx": 131072, + "maxOut": null, + "pIn": 0.13, + "pOut": 0.39999999999999997, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "nousresearch/hermes-4-405b", + "name": "Nous: Hermes 4 405B", + "provider": "nousresearch", + "ctx": 131072, + "maxOut": null, + "pIn": 1, + "pOut": 3, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "deepseek/deepseek-chat-v3.1", + "name": "DeepSeek: DeepSeek V3.1", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 32768, + "pIn": 0.21, + "pOut": 0.7899999999999999, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/mistral-medium-3.1", + "name": "Mistral: Mistral Medium 3.1", + "provider": "mistral", + "ctx": 131072, + "maxOut": null, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "baidu/ernie-4.5-vl-28b-a3b", + "name": "Baidu: ERNIE 4.5 VL 28B A3B", + "provider": "baidu", + "ctx": 131072, + "maxOut": 8000, + "pIn": 0.14, + "pOut": 0.56, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "z-ai/glm-4.5v", + "name": "Z.ai: GLM 4.5V", + "provider": "z-ai", + "ctx": 65536, + "maxOut": 16384, + "pIn": 0.6, + "pOut": 1.7999999999999998, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "ai21/jamba-large-1.7", + "name": "AI21: Jamba Large 1.7", + "provider": "ai21", + "ctx": 256000, + "maxOut": 4096, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5-chat", + "name": "OpenAI: GPT-5 Chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.25, + "pOut": 10, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5", + "name": "OpenAI: GPT-5", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5-mini", + "name": "OpenAI: GPT-5 Mini", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-5-nano", + "name": "OpenAI: GPT-5 Nano", + "provider": "openai", + "ctx": 400000, + "maxOut": null, + "pIn": 0.049999999999999996, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-oss-120b:free", + "name": "OpenAI: gpt-oss-120b (free)", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-oss-120b", + "name": "OpenAI: gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": null, + "pIn": 0.039, + "pOut": 0.18, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-oss-20b:free", + "name": "OpenAI: gpt-oss-20b (free)", + "provider": "openai", + "ctx": 131072, + "maxOut": 8192, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-oss-20b", + "name": "OpenAI: gpt-oss-20b", + "provider": "openai", + "ctx": 131072, + "maxOut": null, + "pIn": 0.029, + "pOut": 0.14, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-opus-4.1", + "name": "Anthropic: Claude Opus 4.1", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/codestral-2508", + "name": "Mistral: Codestral 2508", + "provider": "mistral", + "ctx": 256000, + "maxOut": null, + "pIn": 0.3, + "pOut": 0.8999999999999999, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-coder-30b-a3b-instruct", + "name": "Qwen: Qwen3 Coder 30B A3B Instruct", + "provider": "qwen", + "ctx": 160000, + "maxOut": 32768, + "pIn": 0.07, + "pOut": 0.27, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-30b-a3b-instruct-2507", + "name": "Qwen: Qwen3 30B A3B Instruct 2507", + "provider": "qwen", + "ctx": 131072, + "maxOut": 32000, + "pIn": 0.0428, + "pOut": 0.1716, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "z-ai/glm-4.5", + "name": "Z.ai: GLM 4.5", + "provider": "z-ai", + "ctx": 131072, + "maxOut": 98304, + "pIn": 0.6, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "z-ai/glm-4.5-air:free", + "name": "Z.ai: GLM 4.5 Air (free)", + "provider": "z-ai", + "ctx": 131072, + "maxOut": 96000, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "z-ai/glm-4.5-air", + "name": "Z.ai: GLM 4.5 Air", + "provider": "z-ai", + "ctx": 131072, + "maxOut": 131070, + "pIn": 0.125, + "pOut": 0.85, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-235b-a22b-thinking-2507", + "name": "Qwen: Qwen3 235B A22B Thinking 2507", + "provider": "qwen", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "z-ai/glm-4-32b", + "name": "Z.ai: GLM 4 32B ", + "provider": "z-ai", + "ctx": 128000, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-coder:free", + "name": "Qwen: Qwen3 Coder 480B A35B (free)", + "provider": "qwen", + "ctx": 1048576, + "maxOut": 262000, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-coder", + "name": "Qwen: Qwen3 Coder 480B A35B", + "provider": "qwen", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.22, + "pOut": 1.7999999999999998, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "bytedance/ui-tars-1.5-7b", + "name": "ByteDance: UI-TARS 7B ", + "provider": "bytedance", + "ctx": 128000, + "maxOut": 2048, + "pIn": 0.09999999999999999, + "pOut": 0.19999999999999998, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-2.5-flash-lite", + "name": "Google: Gemini 2.5 Flash Lite", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-235b-a22b-2507", + "name": "Qwen: Qwen3 235B A22B Instruct 2507", + "provider": "qwen", + "ctx": 262144, + "maxOut": 16384, + "pIn": 0.071, + "pOut": 0.09999999999999999, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "switchpoint/router", + "name": "Switchpoint Router", + "provider": "switchpoint", + "ctx": 131072, + "maxOut": null, + "pIn": 0.85, + "pOut": 3.4, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "moonshotai/kimi-k2", + "name": "MoonshotAI: Kimi K2 0711", + "provider": "moonshotai", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.5700000000000001, + "pOut": 2.3, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "cognitivecomputations/dolphin-mistral-24b-venice-edition:free", + "name": "Venice: Uncensored (free)", + "provider": "mistral", + "ctx": 32768, + "maxOut": null, + "pIn": 0, + "pOut": 0, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "tencent/hunyuan-a13b-instruct", + "name": "Tencent: Hunyuan A13B Instruct", + "provider": "tencent", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.14, + "pOut": 0.5700000000000001, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "morph/morph-v3-large", + "name": "Morph: Morph V3 Large", + "provider": "morph", + "ctx": 262144, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 1.9, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "morph/morph-v3-fast", + "name": "Morph: Morph V3 Fast", + "provider": "morph", + "ctx": 81920, + "maxOut": 38000, + "pIn": 0.7999999999999999, + "pOut": 1.2, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "baidu/ernie-4.5-vl-424b-a47b", + "name": "Baidu: ERNIE 4.5 VL 424B A47B ", + "provider": "baidu", + "ctx": 131072, + "maxOut": 16000, + "pIn": 0.42, + "pOut": 1.25, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/mistral-small-3.2-24b-instruct", + "name": "Mistral: Mistral Small 3.2 24B", + "provider": "mistral", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.075, + "pOut": 0.19999999999999998, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "minimax/minimax-m1", + "name": "MiniMax: MiniMax M1", + "provider": "minimax", + "ctx": 1000000, + "maxOut": 40000, + "pIn": 0.39999999999999997, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-2.5-flash", + "name": "Google: Gemini 2.5 Flash", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-2.5-pro", + "name": "Google: Gemini 2.5 Pro", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/o3-pro", + "name": "OpenAI: o3 Pro", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 20, + "pOut": 80, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-2.5-pro-preview", + "name": "Google: Gemini 2.5 Pro Preview 06-05", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "deepseek/deepseek-r1-0528", + "name": "DeepSeek: R1 0528", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 32768, + "pIn": 0.5, + "pOut": 2.1500000000000004, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-opus-4", + "name": "Anthropic: Claude Opus 4", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-sonnet-4", + "name": "Anthropic: Claude Sonnet 4", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemma-3n-e4b-it", + "name": "Google: Gemma 3n 4B", + "provider": "gemini", + "ctx": 32768, + "maxOut": null, + "pIn": 0.06, + "pOut": 0.12, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/mistral-medium-3", + "name": "Mistral: Mistral Medium 3", + "provider": "mistral", + "ctx": 131072, + "maxOut": null, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemini-2.5-pro-preview-05-06", + "name": "Google: Gemini 2.5 Pro Preview 05-06", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "arcee-ai/spotlight", + "name": "Arcee AI: Spotlight", + "provider": "arcee-ai", + "ctx": 131072, + "maxOut": 65537, + "pIn": 0.18, + "pOut": 0.18, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "arcee-ai/maestro-reasoning", + "name": "Arcee AI: Maestro Reasoning", + "provider": "arcee-ai", + "ctx": 131072, + "maxOut": 32000, + "pIn": 0.8999999999999999, + "pOut": 3.3000000000000003, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "arcee-ai/virtuoso-large", + "name": "Arcee AI: Virtuoso Large", + "provider": "arcee-ai", + "ctx": 131072, + "maxOut": 64000, + "pIn": 0.75, + "pOut": 1.2, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "arcee-ai/coder-large", + "name": "Arcee AI: Coder Large", + "provider": "arcee-ai", + "ctx": 32768, + "maxOut": null, + "pIn": 0.5, + "pOut": 0.7999999999999999, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-guard-4-12b", + "name": "Meta: Llama Guard 4 12B", + "provider": "meta", + "ctx": 163840, + "maxOut": 16384, + "pIn": 0.18, + "pOut": 0.18, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-30b-a3b", + "name": "Qwen: Qwen3 30B A3B", + "provider": "qwen", + "ctx": 131072, + "maxOut": 20000, + "pIn": 0.09, + "pOut": 0.44999999999999996, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-8b", + "name": "Qwen: Qwen3 8B", + "provider": "qwen", + "ctx": 131072, + "maxOut": 8192, + "pIn": 0.049999999999999996, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-14b", + "name": "Qwen: Qwen3 14B", + "provider": "qwen", + "ctx": 131702, + "maxOut": 40960, + "pIn": 0.09999999999999999, + "pOut": 0.24, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-32b", + "name": "Qwen: Qwen3 32B", + "provider": "qwen", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.08, + "pOut": 0.28, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen3-235b-a22b", + "name": "Qwen: Qwen3 235B A22B", + "provider": "qwen", + "ctx": 131072, + "maxOut": 8192, + "pIn": 0.45499999999999996, + "pOut": 1.8199999999999998, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/o4-mini-high", + "name": "OpenAI: o4 Mini High", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/o3", + "name": "OpenAI: o3", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/o4-mini", + "name": "OpenAI: o4 Mini", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4.1", + "name": "OpenAI: GPT-4.1", + "provider": "openai", + "ctx": 1047576, + "maxOut": null, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4.1-mini", + "name": "OpenAI: GPT-4.1 Mini", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.39999999999999997, + "pOut": 1.5999999999999999, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4.1-nano", + "name": "OpenAI: GPT-4.1 Nano", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-4-maverick", + "name": "Meta: Llama 4 Maverick", + "provider": "meta", + "ctx": 1048576, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-4-scout", + "name": "Meta: Llama 4 Scout", + "provider": "meta", + "ctx": 10000000, + "maxOut": 16384, + "pIn": 0.08, + "pOut": 0.3, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "deepseek/deepseek-chat-v3-0324", + "name": "DeepSeek: DeepSeek V3 0324", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.77, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/o1-pro", + "name": "OpenAI: o1-pro", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 150, + "pOut": 600, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/mistral-small-3.1-24b-instruct", + "name": "Mistral: Mistral Small 3.1 24B", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.351, + "pOut": 0.5549999999999999, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemma-3-4b-it", + "name": "Google: Gemma 3 4B", + "provider": "gemini", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.04, + "pOut": 0.08, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemma-3-12b-it", + "name": "Google: Gemma 3 12B", + "provider": "gemini", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.04, + "pOut": 0.13, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "cohere/command-a", + "name": "Cohere: Command A", + "provider": "cohere", + "ctx": 256000, + "maxOut": 8192, + "pIn": 2.5, + "pOut": 10, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4o-mini-search-preview", + "name": "OpenAI: GPT-4o-mini Search Preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4o-search-preview", + "name": "OpenAI: GPT-4o Search Preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "rekaai/reka-flash-3", + "name": "Reka Flash 3", + "provider": "rekaai", + "ctx": 65536, + "maxOut": 65536, + "pIn": 0.09999999999999999, + "pOut": 0.19999999999999998, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemma-3-27b-it", + "name": "Google: Gemma 3 27B", + "provider": "gemini", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.08, + "pOut": 0.16, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "thedrummer/skyfall-36b-v2", + "name": "TheDrummer: Skyfall 36B V2", + "provider": "thedrummer", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.55, + "pOut": 0.7999999999999999, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "perplexity/sonar-reasoning-pro", + "name": "Perplexity: Sonar Reasoning Pro", + "provider": "perplexity", + "ctx": 128000, + "maxOut": null, + "pIn": 2, + "pOut": 8, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "perplexity/sonar-pro", + "name": "Perplexity: Sonar Pro", + "provider": "perplexity", + "ctx": 200000, + "maxOut": 8000, + "pIn": 3, + "pOut": 15, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "perplexity/sonar-deep-research", + "name": "Perplexity: Sonar Deep Research", + "provider": "perplexity", + "ctx": 128000, + "maxOut": null, + "pIn": 2, + "pOut": 8, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/mistral-saba", + "name": "Mistral: Saba", + "provider": "mistral", + "ctx": 32768, + "maxOut": null, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-guard-3-8b", + "name": "Llama Guard 3 8B", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.48400000000000004, + "pOut": 0.03, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/o3-mini-high", + "name": "OpenAI: o3 Mini High", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "aion-labs/aion-1.0", + "name": "AionLabs: Aion-1.0", + "provider": "aion-labs", + "ctx": 131072, + "maxOut": 32768, + "pIn": 4, + "pOut": 8, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "aion-labs/aion-1.0-mini", + "name": "AionLabs: Aion-1.0-Mini", + "provider": "aion-labs", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.7, + "pOut": 1.4, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "aion-labs/aion-rp-llama-3.1-8b", + "name": "AionLabs: Aion-RP 1.0 (8B)", + "provider": "meta", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.7999999999999999, + "pOut": 1.5999999999999999, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen2.5-vl-72b-instruct", + "name": "Qwen: Qwen2.5 VL 72B Instruct", + "provider": "qwen", + "ctx": 131072, + "maxOut": null, + "pIn": 0.25, + "pOut": 0.75, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen-plus", + "name": "Qwen: Qwen-Plus", + "provider": "qwen", + "ctx": 1000000, + "maxOut": 32768, + "pIn": 0.26, + "pOut": 0.78, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/o3-mini", + "name": "OpenAI: o3 Mini", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/mistral-small-24b-instruct-2501", + "name": "Mistral: Mistral Small 3", + "provider": "mistral", + "ctx": 32768, + "maxOut": 16384, + "pIn": 0.049999999999999996, + "pOut": 0.08, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "deepseek/deepseek-r1-distill-qwen-32b", + "name": "DeepSeek: R1 Distill Qwen 32B", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 32768, + "pIn": 0.29, + "pOut": 0.29, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "perplexity/sonar", + "name": "Perplexity: Sonar", + "provider": "perplexity", + "ctx": 127072, + "maxOut": null, + "pIn": 1, + "pOut": 1, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "deepseek/deepseek-r1-distill-llama-70b", + "name": "DeepSeek: R1 Distill Llama 70B", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.7, + "pOut": 0.7999999999999999, + "tools": false, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "deepseek/deepseek-r1", + "name": "DeepSeek: R1", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 16000, + "pIn": 0.7, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "minimax/minimax-01", + "name": "MiniMax: MiniMax-01", + "provider": "minimax", + "ctx": 1000192, + "maxOut": 1000192, + "pIn": 0.19999999999999998, + "pOut": 1.1, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "microsoft/phi-4", + "name": "Microsoft: Phi 4", + "provider": "microsoft", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.065, + "pOut": 0.14, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "sao10k/l3.1-70b-hanami-x1", + "name": "Sao10K: Llama 3.1 70B Hanami x1", + "provider": "sao10k", + "ctx": 16000, + "maxOut": null, + "pIn": 3, + "pOut": 3, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "deepseek/deepseek-chat", + "name": "DeepSeek: DeepSeek V3", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 16000, + "pIn": 0.20020000000000002, + "pOut": 0.8000999999999999, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "sao10k/l3.3-euryale-70b", + "name": "Sao10K: Llama 3.3 Euryale 70B", + "provider": "sao10k", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.65, + "pOut": 0.75, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/o1", + "name": "OpenAI: o1", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 15, + "pOut": 60, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "cohere/command-r7b-12-2024", + "name": "Cohere: Command R7B (12-2024)", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4000, + "pIn": 0.0375, + "pOut": 0.15, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-3.3-70b-instruct:free", + "name": "Meta: Llama 3.3 70B Instruct (free)", + "provider": "meta", + "ctx": 131072, + "maxOut": null, + "pIn": 0, + "pOut": 0, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-3.3-70b-instruct", + "name": "Meta: Llama 3.3 70B Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.09999999999999999, + "pOut": 0.32, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "amazon/nova-lite-v1", + "name": "Amazon: Nova Lite 1.0", + "provider": "amazon", + "ctx": 300000, + "maxOut": 5120, + "pIn": 0.06, + "pOut": 0.24, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "amazon/nova-micro-v1", + "name": "Amazon: Nova Micro 1.0", + "provider": "amazon", + "ctx": 128000, + "maxOut": 5120, + "pIn": 0.035, + "pOut": 0.14, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "amazon/nova-pro-v1", + "name": "Amazon: Nova Pro 1.0", + "provider": "amazon", + "ctx": 300000, + "maxOut": 5120, + "pIn": 0.7999999999999999, + "pOut": 3.1999999999999997, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4o-2024-11-20", + "name": "OpenAI: GPT-4o (2024-11-20)", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/mistral-large-2407", + "name": "Mistral Large 2407", + "provider": "mistral", + "ctx": 131072, + "maxOut": null, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen-2.5-coder-32b-instruct", + "name": "Qwen2.5 Coder 32B Instruct", + "provider": "qwen", + "ctx": 128000, + "maxOut": 32768, + "pIn": 0.66, + "pOut": 1, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "thedrummer/unslopnemo-12b", + "name": "TheDrummer: UnslopNemo 12B", + "provider": "thedrummer", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.39999999999999997, + "pOut": 0.39999999999999997, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-3.5-haiku", + "name": "Anthropic: Claude 3.5 Haiku", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 0.7999999999999999, + "pOut": 4, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "anthracite-org/magnum-v4-72b", + "name": "Magnum v4 72B", + "provider": "anthracite-org", + "ctx": 32768, + "maxOut": 2048, + "pIn": 3, + "pOut": 5, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen-2.5-7b-instruct", + "name": "Qwen: Qwen2.5 7B Instruct", + "provider": "qwen", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.04, + "pOut": 0.09999999999999999, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "inflection/inflection-3-pi", + "name": "Inflection: Inflection 3 Pi", + "provider": "inflection", + "ctx": 8000, + "maxOut": 1024, + "pIn": 2.5, + "pOut": 10, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "inflection/inflection-3-productivity", + "name": "Inflection: Inflection 3 Productivity", + "provider": "inflection", + "ctx": 8000, + "maxOut": 1024, + "pIn": 2.5, + "pOut": 10, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "thedrummer/rocinante-12b", + "name": "TheDrummer: Rocinante 12B", + "provider": "thedrummer", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.16999999999999998, + "pOut": 0.43, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-3.2-1b-instruct", + "name": "Meta: Llama 3.2 1B Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 60000, + "pIn": 0.027, + "pOut": 0.201, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-3.2-3b-instruct:free", + "name": "Meta: Llama 3.2 3B Instruct (free)", + "provider": "meta", + "ctx": 131072, + "maxOut": null, + "pIn": 0, + "pOut": 0, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-3.2-3b-instruct", + "name": "Meta: Llama 3.2 3B Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 80000, + "pIn": 0.0509, + "pOut": 0.335, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-3.2-11b-vision-instruct", + "name": "Meta: Llama 3.2 11B Vision Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.245, + "pOut": 0.245, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "qwen/qwen-2.5-72b-instruct", + "name": "Qwen2.5 72B Instruct", + "provider": "qwen", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.36, + "pOut": 0.39999999999999997, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "cohere/command-r-08-2024", + "name": "Cohere: Command R (08-2024)", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4000, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "cohere/command-r-plus-08-2024", + "name": "Cohere: Command R+ (08-2024)", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4000, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "sao10k/l3.1-euryale-70b", + "name": "Sao10K: Llama 3.1 Euryale 70B v2.2", + "provider": "sao10k", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.85, + "pOut": 0.85, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "nousresearch/hermes-3-llama-3.1-70b", + "name": "Nous: Hermes 3 70B Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.3, + "pOut": 0.3, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "nousresearch/hermes-3-llama-3.1-405b:free", + "name": "Nous: Hermes 3 405B Instruct (free)", + "provider": "meta", + "ctx": 131072, + "maxOut": null, + "pIn": 0, + "pOut": 0, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "nousresearch/hermes-3-llama-3.1-405b", + "name": "Nous: Hermes 3 405B Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 16384, + "pIn": 1, + "pOut": 1, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "sao10k/l3-lunaris-8b", + "name": "Sao10K: Llama 3 8B Lunaris", + "provider": "sao10k", + "ctx": 8192, + "maxOut": 16384, + "pIn": 0.04, + "pOut": 0.049999999999999996, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4o-2024-08-06", + "name": "OpenAI: GPT-4o (2024-08-06)", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-3.1-8b-instruct", + "name": "Meta: Llama 3.1 8B Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.02, + "pOut": 0.049999999999999996, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-3.1-70b-instruct", + "name": "Meta: Llama 3.1 70B Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.39999999999999997, + "pOut": 0.39999999999999997, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/mistral-nemo", + "name": "Mistral: Mistral Nemo", + "provider": "mistral", + "ctx": 131072, + "maxOut": null, + "pIn": 0.02, + "pOut": 0.03, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4o-mini-2024-07-18", + "name": "OpenAI: GPT-4o-mini (2024-07-18)", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4o-mini", + "name": "OpenAI: GPT-4o-mini", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "google/gemma-2-27b-it", + "name": "Google: Gemma 2 27B", + "provider": "gemini", + "ctx": 8192, + "maxOut": 2048, + "pIn": 0.65, + "pOut": 0.65, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "sao10k/l3-euryale-70b", + "name": "Sao10k: Llama 3 Euryale 70B v2.1", + "provider": "sao10k", + "ctx": 8192, + "maxOut": 8192, + "pIn": 1.48, + "pOut": 1.48, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "nousresearch/hermes-2-pro-llama-3-8b", + "name": "NousResearch: Hermes 2 Pro - Llama-3 8B", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.14, + "pOut": 0.14, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4o-2024-05-13", + "name": "OpenAI: GPT-4o (2024-05-13)", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5, + "pOut": 15, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4o", + "name": "OpenAI: GPT-4o", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-3-8b-instruct", + "name": "Meta: Llama 3 8B Instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.04, + "pOut": 0.04, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "meta-llama/llama-3-70b-instruct", + "name": "Meta: Llama 3 70B Instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 8000, + "pIn": 0.51, + "pOut": 0.74, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/mixtral-8x22b-instruct", + "name": "Mistral: Mixtral 8x22B Instruct", + "provider": "mistral", + "ctx": 65536, + "maxOut": null, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "microsoft/wizardlm-2-8x22b", + "name": "WizardLM-2 8x22B", + "provider": "microsoft", + "ctx": 65536, + "maxOut": 8000, + "pIn": 0.62, + "pOut": 0.62, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4-turbo", + "name": "OpenAI: GPT-4 Turbo", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "anthropic/claude-3-haiku", + "name": "Anthropic: Claude 3 Haiku", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 0.25, + "pOut": 1.25, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "mistralai/mistral-large", + "name": "Mistral Large", + "provider": "mistral", + "ctx": 128000, + "maxOut": null, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-3.5-turbo-0613", + "name": "OpenAI: GPT-3.5 Turbo (older v0613)", + "provider": "openai", + "ctx": 4095, + "maxOut": 4096, + "pIn": 1, + "pOut": 2, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4-turbo-preview", + "name": "OpenAI: GPT-4 Turbo Preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openrouter/auto", + "name": "Auto Router", + "provider": "openrouter", + "ctx": 2000000, + "maxOut": null, + "pIn": -1000000, + "pOut": -1000000, + "tools": true, + "cot": true, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4-1106-preview", + "name": "OpenAI: GPT-4 Turbo (older v1106)", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-3.5-turbo-instruct", + "name": "OpenAI: GPT-3.5 Turbo Instruct", + "provider": "openai", + "ctx": 4095, + "maxOut": 4096, + "pIn": 1.5, + "pOut": 2, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-3.5-turbo-16k", + "name": "OpenAI: GPT-3.5 Turbo 16k", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 3, + "pOut": 4, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "mancer/weaver", + "name": "Mancer: Weaver (alpha)", + "provider": "mancer", + "ctx": 8000, + "maxOut": 2000, + "pIn": 0.75, + "pOut": 1, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "undi95/remm-slerp-l2-13b", + "name": "ReMM SLERP 13B", + "provider": "undi95", + "ctx": 6144, + "maxOut": 4096, + "pIn": 0.44999999999999996, + "pOut": 0.65, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "gryphe/mythomax-l2-13b", + "name": "MythoMax 13B", + "provider": "gryphe", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.06, + "pOut": 0.06, + "tools": false, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-3.5-turbo", + "name": "OpenAI: GPT-3.5 Turbo", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "openai/gpt-4", + "name": "OpenAI: GPT-4", + "provider": "openai", + "ctx": 8191, + "maxOut": 4096, + "pIn": 30, + "pOut": 60, + "tools": true, + "cot": false, + "json": false, + "src": "openrouter" + }, + { + "id": "sample_spec", + "name": "sample_spec", + "provider": "sample_spec", + "ctx": "max input tokens, if the provider specifies it. if not default to max_tokens", + "maxOut": "max output tokens, if the provider specifies it. if not default to max_tokens", + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "1024-x-1024/50-steps/stability.stable-diffusion-xl-v1", + "name": "1024-x-1024/50-steps/stability.stable-diffusion-xl-v1", + "provider": "1024-x-1024", + "ctx": 77, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "1024-x-1024/max-steps/stability.stable-diffusion-xl-v1", + "name": "1024-x-1024/max-steps/stability.stable-diffusion-xl-v1", + "provider": "1024-x-1024", + "ctx": 77, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "512-x-512/50-steps/stability.stable-diffusion-xl-v0", + "name": "512-x-512/50-steps/stability.stable-diffusion-xl-v0", + "provider": "512-x-512", + "ctx": 77, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "512-x-512/max-steps/stability.stable-diffusion-xl-v0", + "name": "512-x-512/max-steps/stability.stable-diffusion-xl-v0", + "provider": "512-x-512", + "ctx": 77, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ai21.j2-mid-v1", + "name": "ai21.j2-mid-v1", + "provider": "ai21.j2-mid-v1", + "ctx": 8191, + "maxOut": 8191, + "pIn": 12.5, + "pOut": 12.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ai21.j2-ultra-v1", + "name": "ai21.j2-ultra-v1", + "provider": "ai21.j2-ultra-v1", + "ctx": 8191, + "maxOut": 8191, + "pIn": 18.8, + "pOut": 18.8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ai21.jamba-1-5-large-v1:0", + "name": "ai21.jamba-1-5-large-v1:0", + "provider": "ai21.jamba-1-5-large-v1:0", + "ctx": 256000, + "maxOut": 256000, + "pIn": 2, + "pOut": 8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ai21.jamba-1-5-mini-v1:0", + "name": "ai21.jamba-1-5-mini-v1:0", + "provider": "ai21.jamba-1-5-mini-v1:0", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 0.39999999999999997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ai21.jamba-instruct-v1:0", + "name": "ai21.jamba-instruct-v1:0", + "provider": "ai21.jamba-instruct-v1:0", + "ctx": 70000, + "maxOut": 4096, + "pIn": 0.5, + "pOut": 0.7, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "us.writer.palmyra-x4-v1:0", + "name": "us.writer.palmyra-x4-v1:0", + "provider": "us.writer.palmyra-x4-v1:0", + "ctx": 128000, + "maxOut": 8192, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "us.writer.palmyra-x5-v1:0", + "name": "us.writer.palmyra-x5-v1:0", + "provider": "us.writer.palmyra-x5-v1:0", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "writer.palmyra-x4-v1:0", + "name": "writer.palmyra-x4-v1:0", + "provider": "writer.palmyra-x4-v1:0", + "ctx": 128000, + "maxOut": 8192, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "writer.palmyra-x5-v1:0", + "name": "writer.palmyra-x5-v1:0", + "provider": "writer.palmyra-x5-v1:0", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "amazon.nova-lite-v1:0", + "name": "amazon.nova-lite-v1:0", + "provider": "amazon.nova-lite-v1:0", + "ctx": 300000, + "maxOut": 10000, + "pIn": 0.06, + "pOut": 0.24, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "amazon.nova-2-lite-v1:0", + "name": "amazon.nova-2-lite-v1:0", + "provider": "amazon.nova-2-lite-v1:0", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "amazon.nova-2-pro-preview-20251202-v1:0", + "name": "amazon.nova-2-pro-preview-20251202-v1:0", + "provider": "amazon.nova-2-pro-preview-20251202-v1:0", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 2.1875, + "pOut": 17.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "apac.amazon.nova-2-lite-v1:0", + "name": "apac.amazon.nova-2-lite-v1:0", + "provider": "apac.amazon.nova-2-lite-v1:0", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 0.33, + "pOut": 2.75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "apac.amazon.nova-2-pro-preview-20251202-v1:0", + "name": "apac.amazon.nova-2-pro-preview-20251202-v1:0", + "provider": "apac.amazon.nova-2-pro-preview-20251202-v1:0", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 2.1875, + "pOut": 17.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "eu.amazon.nova-2-lite-v1:0", + "name": "eu.amazon.nova-2-lite-v1:0", + "provider": "eu.amazon.nova-2-lite-v1:0", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 0.33, + "pOut": 2.75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "eu.amazon.nova-2-pro-preview-20251202-v1:0", + "name": "eu.amazon.nova-2-pro-preview-20251202-v1:0", + "provider": "eu.amazon.nova-2-pro-preview-20251202-v1:0", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 2.1875, + "pOut": 17.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us.amazon.nova-2-lite-v1:0", + "name": "us.amazon.nova-2-lite-v1:0", + "provider": "us.amazon.nova-2-lite-v1:0", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 0.33, + "pOut": 2.75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us.amazon.nova-2-pro-preview-20251202-v1:0", + "name": "us.amazon.nova-2-pro-preview-20251202-v1:0", + "provider": "us.amazon.nova-2-pro-preview-20251202-v1:0", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 2.1875, + "pOut": 17.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "amazon.nova-2-multimodal-embeddings-v1:0", + "name": "amazon.nova-2-multimodal-embeddings-v1:0", + "provider": "amazon.nova-2-multimodal-embeddings-v1:0", + "ctx": 8172, + "maxOut": null, + "pIn": 0.135, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "amazon.nova-micro-v1:0", + "name": "amazon.nova-micro-v1:0", + "provider": "amazon.nova-micro-v1:0", + "ctx": 128000, + "maxOut": 10000, + "pIn": 0.035, + "pOut": 0.14, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "amazon.nova-pro-v1:0", + "name": "amazon.nova-pro-v1:0", + "provider": "amazon.nova-pro-v1:0", + "ctx": 300000, + "maxOut": 10000, + "pIn": 0.7999999999999999, + "pOut": 3.1999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "amazon.rerank-v1:0", + "name": "amazon.rerank-v1:0", + "provider": "amazon.rerank-v1:0", + "ctx": 32000, + "maxOut": 32000, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "amazon.titan-embed-image-v1", + "name": "amazon.titan-embed-image-v1", + "provider": "amazon.titan-embed-image-v1", + "ctx": 128, + "maxOut": null, + "pIn": 0.7999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "amazon.titan-embed-text-v1", + "name": "amazon.titan-embed-text-v1", + "provider": "amazon.titan-embed-text-v1", + "ctx": 8192, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "amazon.titan-embed-text-v2:0", + "name": "amazon.titan-embed-text-v2:0", + "provider": "amazon.titan-embed-text-v2:0", + "ctx": 8192, + "maxOut": null, + "pIn": 0.19999999999999998, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "twelvelabs.marengo-embed-2-7-v1:0", + "name": "twelvelabs.marengo-embed-2-7-v1:0", + "provider": "twelvelabs.marengo-embed-2-7-v1:0", + "ctx": 77, + "maxOut": null, + "pIn": 70, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "us.twelvelabs.marengo-embed-2-7-v1:0", + "name": "us.twelvelabs.marengo-embed-2-7-v1:0", + "provider": "us.twelvelabs.marengo-embed-2-7-v1:0", + "ctx": 77, + "maxOut": null, + "pIn": 70, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "eu.twelvelabs.marengo-embed-2-7-v1:0", + "name": "eu.twelvelabs.marengo-embed-2-7-v1:0", + "provider": "eu.twelvelabs.marengo-embed-2-7-v1:0", + "ctx": 77, + "maxOut": null, + "pIn": 70, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "amazon.titan-text-express-v1", + "name": "amazon.titan-text-express-v1", + "provider": "amazon.titan-text-express-v1", + "ctx": 42000, + "maxOut": 8000, + "pIn": 1.3, + "pOut": 1.7, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "amazon.titan-text-lite-v1", + "name": "amazon.titan-text-lite-v1", + "provider": "amazon.titan-text-lite-v1", + "ctx": 42000, + "maxOut": 4000, + "pIn": 0.3, + "pOut": 0.39999999999999997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "amazon.titan-text-premier-v1:0", + "name": "amazon.titan-text-premier-v1:0", + "provider": "amazon.titan-text-premier-v1:0", + "ctx": 42000, + "maxOut": 32000, + "pIn": 0.5, + "pOut": 1.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anthropic.claude-3-5-haiku-20241022-v1:0", + "name": "anthropic.claude-3-5-haiku-20241022-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 0.7999999999999999, + "pOut": 4, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "anthropic.claude-haiku-4-5-20251001-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-haiku-4-5@20251001", + "name": "anthropic.claude-haiku-4-5@20251001", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-3-5-sonnet-20240620-v1:0", + "name": "anthropic.claude-3-5-sonnet-20240620-v1:0", + "provider": "claude", + "ctx": 1000000, + "maxOut": 4096, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-3-5-sonnet-20241022-v2:0", + "name": "anthropic.claude-3-5-sonnet-20241022-v2:0", + "provider": "claude", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-3-7-sonnet-20240620-v1:0", + "name": "anthropic.claude-3-7-sonnet-20240620-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3.5999999999999996, + "pOut": 18, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-3-7-sonnet-20250219-v1:0", + "name": "anthropic.claude-3-7-sonnet-20250219-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-3-haiku-20240307-v1:0", + "name": "anthropic.claude-3-haiku-20240307-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 0.25, + "pOut": 1.25, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-3-opus-20240229-v1:0", + "name": "anthropic.claude-3-opus-20240229-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-3-sonnet-20240229-v1:0", + "name": "anthropic.claude-3-sonnet-20240229-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-instant-v1", + "name": "anthropic.claude-instant-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 0.7999999999999999, + "pOut": 2.4, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anthropic.claude-opus-4-1-20250805-v1:0", + "name": "anthropic.claude-opus-4-1-20250805-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-opus-4-20250514-v1:0", + "name": "anthropic.claude-opus-4-20250514-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-opus-4-5-20251101-v1:0", + "name": "anthropic.claude-opus-4-5-20251101-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-opus-4-6-v1", + "name": "anthropic.claude-opus-4-6-v1", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "global.anthropic.claude-opus-4-6-v1", + "name": "global.anthropic.claude-opus-4-6-v1", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-opus-4-6-v1", + "name": "us.anthropic.claude-opus-4-6-v1", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5.5, + "pOut": 27.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-opus-4-6-v1", + "name": "eu.anthropic.claude-opus-4-6-v1", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5.5, + "pOut": 27.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "au.anthropic.claude-opus-4-6-v1", + "name": "au.anthropic.claude-opus-4-6-v1", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5.5, + "pOut": 27.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-opus-4-7", + "name": "anthropic.claude-opus-4-7", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-mythos-preview", + "name": "anthropic.claude-mythos-preview", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "global.anthropic.claude-opus-4-7", + "name": "global.anthropic.claude-opus-4-7", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-opus-4-7", + "name": "us.anthropic.claude-opus-4-7", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5.5, + "pOut": 27.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-opus-4-7", + "name": "eu.anthropic.claude-opus-4-7", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5.5, + "pOut": 27.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "au.anthropic.claude-opus-4-7", + "name": "au.anthropic.claude-opus-4-7", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5.5, + "pOut": 27.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-opus-4-8", + "name": "anthropic.claude-opus-4-8", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "global.anthropic.claude-opus-4-8", + "name": "global.anthropic.claude-opus-4-8", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-opus-4-8", + "name": "us.anthropic.claude-opus-4-8", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5.5, + "pOut": 27.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-opus-4-8", + "name": "eu.anthropic.claude-opus-4-8", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5.5, + "pOut": 27.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "au.anthropic.claude-opus-4-8", + "name": "au.anthropic.claude-opus-4-8", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5.5, + "pOut": 27.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-sonnet-4-6", + "name": "anthropic.claude-sonnet-4-6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "global.anthropic.claude-sonnet-4-6", + "name": "global.anthropic.claude-sonnet-4-6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-sonnet-4-6", + "name": "us.anthropic.claude-sonnet-4-6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-sonnet-4-6", + "name": "eu.anthropic.claude-sonnet-4-6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "au.anthropic.claude-sonnet-4-6", + "name": "au.anthropic.claude-sonnet-4-6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "jp.anthropic.claude-sonnet-4-6", + "name": "jp.anthropic.claude-sonnet-4-6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-sonnet-4-20250514-v1:0", + "name": "anthropic.claude-sonnet-4-20250514-v1:0", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "anthropic.claude-sonnet-4-5-20250929-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "anthropic.claude-v1", + "name": "anthropic.claude-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anthropic.claude-v2:1", + "name": "anthropic.claude-v2:1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anyscale/HuggingFaceH4/zephyr-7b-beta", + "name": "anyscale/HuggingFaceH4/zephyr-7b-beta", + "provider": "anyscale", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.15, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anyscale/codellama/CodeLlama-34b-Instruct-hf", + "name": "anyscale/codellama/CodeLlama-34b-Instruct-hf", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 1, + "pOut": 1, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anyscale/codellama/CodeLlama-70b-Instruct-hf", + "name": "anyscale/codellama/CodeLlama-70b-Instruct-hf", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 1, + "pOut": 1, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anyscale/google/gemma-7b-it", + "name": "anyscale/google/gemma-7b-it", + "provider": "gemini", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.15, + "pOut": 0.15, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anyscale/meta-llama/Llama-2-13b-chat-hf", + "name": "anyscale/meta-llama/Llama-2-13b-chat-hf", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.25, + "pOut": 0.25, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anyscale/meta-llama/Llama-2-70b-chat-hf", + "name": "anyscale/meta-llama/Llama-2-70b-chat-hf", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 1, + "pOut": 1, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anyscale/meta-llama/Llama-2-7b-chat-hf", + "name": "anyscale/meta-llama/Llama-2-7b-chat-hf", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.15, + "pOut": 0.15, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anyscale/meta-llama/Meta-Llama-3-70B-Instruct", + "name": "anyscale/meta-llama/Meta-Llama-3-70B-Instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 1, + "pOut": 1, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anyscale/meta-llama/Meta-Llama-3-8B-Instruct", + "name": "anyscale/meta-llama/Meta-Llama-3-8B-Instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.15, + "pOut": 0.15, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anyscale/mistralai/Mistral-7B-Instruct-v0.1", + "name": "anyscale/mistralai/Mistral-7B-Instruct-v0.1", + "provider": "mistral", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anyscale/mistralai/Mixtral-8x22B-Instruct-v0.1", + "name": "anyscale/mistralai/Mixtral-8x22B-Instruct-v0.1", + "provider": "mistral", + "ctx": 65536, + "maxOut": 65536, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "anyscale/mistralai/Mixtral-8x7B-Instruct-v0.1", + "name": "anyscale/mistralai/Mixtral-8x7B-Instruct-v0.1", + "provider": "mistral", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "apac.amazon.nova-lite-v1:0", + "name": "apac.amazon.nova-lite-v1:0", + "provider": "apac.amazon.nova-lite-v1:0", + "ctx": 300000, + "maxOut": 10000, + "pIn": 0.063, + "pOut": 0.252, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "apac.amazon.nova-micro-v1:0", + "name": "apac.amazon.nova-micro-v1:0", + "provider": "apac.amazon.nova-micro-v1:0", + "ctx": 128000, + "maxOut": 10000, + "pIn": 0.037, + "pOut": 0.148, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "apac.amazon.nova-pro-v1:0", + "name": "apac.amazon.nova-pro-v1:0", + "provider": "apac.amazon.nova-pro-v1:0", + "ctx": 300000, + "maxOut": 10000, + "pIn": 0.84, + "pOut": 3.36, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "apac.anthropic.claude-3-5-sonnet-20240620-v1:0", + "name": "apac.anthropic.claude-3-5-sonnet-20240620-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "apac.anthropic.claude-3-5-sonnet-20241022-v2:0", + "name": "apac.anthropic.claude-3-5-sonnet-20241022-v2:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "apac.anthropic.claude-3-haiku-20240307-v1:0", + "name": "apac.anthropic.claude-3-haiku-20240307-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 0.25, + "pOut": 1.25, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "apac.anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "apac.anthropic.claude-haiku-4-5-20251001-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1.1, + "pOut": 5.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "apac.anthropic.claude-3-sonnet-20240229-v1:0", + "name": "apac.anthropic.claude-3-sonnet-20240229-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "apac.anthropic.claude-sonnet-4-20250514-v1:0", + "name": "apac.anthropic.claude-sonnet-4-20250514-v1:0", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "au.anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "au.anthropic.claude-sonnet-4-5-20250929-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/ada", + "name": "azure/ada", + "provider": "azure", + "ctx": 8191, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/codex-mini", + "name": "azure/codex-mini", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.5, + "pOut": 6, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/command-r-plus", + "name": "azure/command-r-plus", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4096, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/claude-haiku-4-5", + "name": "azure_ai/claude-haiku-4-5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/claude-opus-4-5", + "name": "azure_ai/claude-opus-4-5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/claude-opus-4-6", + "name": "azure_ai/claude-opus-4-6", + "provider": "claude", + "ctx": 200000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/claude-opus-4-7", + "name": "azure_ai/claude-opus-4-7", + "provider": "claude", + "ctx": 200000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/claude-opus-4-8", + "name": "azure_ai/claude-opus-4-8", + "provider": "claude", + "ctx": 200000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/claude-opus-4-1", + "name": "azure_ai/claude-opus-4-1", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/claude-sonnet-4-5", + "name": "azure_ai/claude-sonnet-4-5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/claude-sonnet-4-6", + "name": "azure_ai/claude-sonnet-4-6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/computer-use-preview", + "name": "azure/computer-use-preview", + "provider": "azure", + "ctx": 8192, + "maxOut": 1024, + "pIn": 3, + "pOut": 12, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/gpt-oss-120b", + "name": "azure_ai/gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/gpt-5.4", + "name": "azure_ai/gpt-5.4", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 2.5, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/gpt-5.4-2026-03-05", + "name": "azure_ai/gpt-5.4-2026-03-05", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 2.5, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/gpt-5.4-pro", + "name": "azure_ai/gpt-5.4-pro", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 30, + "pOut": 180, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "azure_ai/gpt-5.4-pro-2026-03-05", + "name": "azure_ai/gpt-5.4-pro-2026-03-05", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 30, + "pOut": 180, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "azure_ai/gpt-5.4-mini", + "name": "azure_ai/gpt-5.4-mini", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 0.75, + "pOut": 4.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/gpt-5.4-mini-2026-03-17", + "name": "azure_ai/gpt-5.4-mini-2026-03-17", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 0.75, + "pOut": 4.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/gpt-5.4-nano", + "name": "azure_ai/gpt-5.4-nano", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 1.25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/gpt-5.4-nano-2026-03-17", + "name": "azure_ai/gpt-5.4-nano-2026-03-17", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 1.25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/eu/gpt-4o-2024-08-06", + "name": "azure/eu/gpt-4o-2024-08-06", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.75, + "pOut": 11, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/eu/gpt-4o-2024-11-20", + "name": "azure/eu/gpt-4o-2024-11-20", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.75, + "pOut": 11, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/eu/gpt-4o-mini-2024-07-18", + "name": "azure/eu/gpt-4o-mini-2024-07-18", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.165, + "pOut": 0.66, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/eu/gpt-4o-mini-realtime-preview-2024-12-17", + "name": "azure/eu/gpt-4o-mini-realtime-preview-2024-12-17", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.66, + "pOut": 2.64, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/eu/gpt-4o-realtime-preview-2024-10-01", + "name": "azure/eu/gpt-4o-realtime-preview-2024-10-01", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5.5, + "pOut": 22, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/eu/gpt-4o-realtime-preview-2024-12-17", + "name": "azure/eu/gpt-4o-realtime-preview-2024-12-17", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5.5, + "pOut": 22, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/eu/gpt-5-2025-08-07", + "name": "azure/eu/gpt-5-2025-08-07", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.375, + "pOut": 11, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/eu/gpt-5-mini-2025-08-07", + "name": "azure/eu/gpt-5-mini-2025-08-07", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.275, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/eu/gpt-5.1", + "name": "azure/eu/gpt-5.1", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.38, + "pOut": 11, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/eu/gpt-5.1-chat", + "name": "azure/eu/gpt-5.1-chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 1.38, + "pOut": 11, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/eu/gpt-5.1-codex", + "name": "azure/eu/gpt-5.1-codex", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.38, + "pOut": 11, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/eu/gpt-5.1-codex-mini", + "name": "azure/eu/gpt-5.1-codex-mini", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.275, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/eu/gpt-5-nano-2025-08-07", + "name": "azure/eu/gpt-5-nano-2025-08-07", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.055, + "pOut": 0.44, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/eu/o1-2024-12-17", + "name": "azure/eu/o1-2024-12-17", + "provider": "azure", + "ctx": 200000, + "maxOut": 100000, + "pIn": 16.5, + "pOut": 66, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/eu/o1-mini-2024-09-12", + "name": "azure/eu/o1-mini-2024-09-12", + "provider": "azure", + "ctx": 128000, + "maxOut": 65536, + "pIn": 1.21, + "pOut": 4.84, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/eu/o1-preview-2024-09-12", + "name": "azure/eu/o1-preview-2024-09-12", + "provider": "azure", + "ctx": 128000, + "maxOut": 32768, + "pIn": 16.5, + "pOut": 66, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/eu/o3-mini-2025-01-31", + "name": "azure/eu/o3-mini-2025-01-31", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.21, + "pOut": 4.84, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure/global-standard/gpt-4o-2024-08-06", + "name": "azure/global-standard/gpt-4o-2024-08-06", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/global-standard/gpt-4o-2024-11-20", + "name": "azure/global-standard/gpt-4o-2024-11-20", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/global-standard/gpt-4o-mini", + "name": "azure/global-standard/gpt-4o-mini", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/global/gpt-4o-2024-08-06", + "name": "azure/global/gpt-4o-2024-08-06", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/global/gpt-4o-2024-11-20", + "name": "azure/global/gpt-4o-2024-11-20", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/global/gpt-5.1", + "name": "azure/global/gpt-5.1", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/global/gpt-5.1-chat", + "name": "azure/global/gpt-5.1-chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/global/gpt-5.1-codex", + "name": "azure/global/gpt-5.1-codex", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/global/gpt-5.1-codex-mini", + "name": "azure/global/gpt-5.1-codex-mini", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-3.5-turbo", + "name": "azure/gpt-3.5-turbo", + "provider": "openai", + "ctx": 4097, + "maxOut": 4096, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-3.5-turbo-0125", + "name": "azure/gpt-3.5-turbo-0125", + "provider": "openai", + "ctx": 16384, + "maxOut": 4096, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-3.5-turbo-instruct-0914", + "name": "azure/gpt-3.5-turbo-instruct-0914", + "provider": "openai", + "ctx": 4097, + "maxOut": null, + "pIn": 1.5, + "pOut": 2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-35-turbo", + "name": "azure/gpt-35-turbo", + "provider": "openai", + "ctx": 4097, + "maxOut": 4096, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-35-turbo-0125", + "name": "azure/gpt-35-turbo-0125", + "provider": "openai", + "ctx": 16384, + "maxOut": 4096, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-35-turbo-1106", + "name": "azure/gpt-35-turbo-1106", + "provider": "openai", + "ctx": 16384, + "maxOut": 4096, + "pIn": 1, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-35-turbo-16k", + "name": "azure/gpt-35-turbo-16k", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 3, + "pOut": 4, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-35-turbo-16k-0613", + "name": "azure/gpt-35-turbo-16k-0613", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 3, + "pOut": 4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-35-turbo-instruct", + "name": "azure/gpt-35-turbo-instruct", + "provider": "openai", + "ctx": 4097, + "maxOut": null, + "pIn": 1.5, + "pOut": 2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-35-turbo-instruct-0914", + "name": "azure/gpt-35-turbo-instruct-0914", + "provider": "openai", + "ctx": 4097, + "maxOut": null, + "pIn": 1.5, + "pOut": 2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-4", + "name": "azure/gpt-4", + "provider": "openai", + "ctx": 8192, + "maxOut": 4096, + "pIn": 30, + "pOut": 60, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-4-0125-preview", + "name": "azure/gpt-4-0125-preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-4-0613", + "name": "azure/gpt-4-0613", + "provider": "openai", + "ctx": 8192, + "maxOut": 4096, + "pIn": 30, + "pOut": 60, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-4-1106-preview", + "name": "azure/gpt-4-1106-preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-4-32k", + "name": "azure/gpt-4-32k", + "provider": "openai", + "ctx": 32768, + "maxOut": 4096, + "pIn": 60, + "pOut": 120, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-4-32k-0613", + "name": "azure/gpt-4-32k-0613", + "provider": "openai", + "ctx": 32768, + "maxOut": 4096, + "pIn": 60, + "pOut": 120, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-4-turbo", + "name": "azure/gpt-4-turbo", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-4-turbo-2024-04-09", + "name": "azure/gpt-4-turbo-2024-04-09", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-4-turbo-vision-preview", + "name": "azure/gpt-4-turbo-vision-preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-4.1", + "name": "azure/gpt-4.1", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-4.1-2025-04-14", + "name": "azure/gpt-4.1-2025-04-14", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-4.1-mini", + "name": "azure/gpt-4.1-mini", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.39999999999999997, + "pOut": 1.5999999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-4.1-mini-2025-04-14", + "name": "azure/gpt-4.1-mini-2025-04-14", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.39999999999999997, + "pOut": 1.5999999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-4.1-nano", + "name": "azure/gpt-4.1-nano", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-4.1-nano-2025-04-14", + "name": "azure/gpt-4.1-nano-2025-04-14", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-4.5-preview", + "name": "azure/gpt-4.5-preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 75, + "pOut": 150, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-4o", + "name": "azure/gpt-4o", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-4o-2024-05-13", + "name": "azure/gpt-4o-2024-05-13", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-4o-2024-08-06", + "name": "azure/gpt-4o-2024-08-06", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-4o-2024-11-20", + "name": "azure/gpt-4o-2024-11-20", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.75, + "pOut": 11, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-audio-2025-08-28", + "name": "azure/gpt-audio-2025-08-28", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": false, + "json": false, + "src": "litellm" + }, + { + "id": "azure/gpt-audio-1.5-2026-02-23", + "name": "azure/gpt-audio-1.5-2026-02-23", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": false, + "json": false, + "src": "litellm" + }, + { + "id": "azure/gpt-audio-mini-2025-10-06", + "name": "azure/gpt-audio-mini-2025-10-06", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.6, + "pOut": 2.4, + "tools": true, + "cot": false, + "json": false, + "src": "litellm" + }, + { + "id": "azure/gpt-4o-audio-preview-2024-12-17", + "name": "azure/gpt-4o-audio-preview-2024-12-17", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": false, + "json": false, + "src": "litellm" + }, + { + "id": "azure/gpt-4o-mini", + "name": "azure/gpt-4o-mini", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.165, + "pOut": 0.66, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-4o-mini-2024-07-18", + "name": "azure/gpt-4o-mini-2024-07-18", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.165, + "pOut": 0.66, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-4o-mini-audio-preview-2024-12-17", + "name": "azure/gpt-4o-mini-audio-preview-2024-12-17", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": false, + "json": false, + "src": "litellm" + }, + { + "id": "azure/gpt-4o-mini-realtime-preview-2024-12-17", + "name": "azure/gpt-4o-mini-realtime-preview-2024-12-17", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.6, + "pOut": 2.4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-realtime-2025-08-28", + "name": "azure/gpt-realtime-2025-08-28", + "provider": "openai", + "ctx": 32000, + "maxOut": 4096, + "pIn": 4, + "pOut": 16, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-realtime-1.5-2026-02-23", + "name": "azure/gpt-realtime-1.5-2026-02-23", + "provider": "openai", + "ctx": 32000, + "maxOut": 4096, + "pIn": 4, + "pOut": 16, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-realtime-mini-2025-10-06", + "name": "azure/gpt-realtime-mini-2025-10-06", + "provider": "openai", + "ctx": 32000, + "maxOut": 4096, + "pIn": 0.6, + "pOut": 2.4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-4o-realtime-preview-2024-10-01", + "name": "azure/gpt-4o-realtime-preview-2024-10-01", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5, + "pOut": 20, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-4o-realtime-preview-2024-12-17", + "name": "azure/gpt-4o-realtime-preview-2024-12-17", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5, + "pOut": 20, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/gpt-5.1-2025-11-13", + "name": "azure/gpt-5.1-2025-11-13", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.1-chat-2025-11-13", + "name": "azure/gpt-5.1-chat-2025-11-13", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.25, + "pOut": 10, + "tools": false, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.1-codex-2025-11-13", + "name": "azure/gpt-5.1-codex-2025-11-13", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.1-codex-mini-2025-11-13", + "name": "azure/gpt-5.1-codex-mini-2025-11-13", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5", + "name": "azure/gpt-5", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5-2025-08-07", + "name": "azure/gpt-5-2025-08-07", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5-chat", + "name": "azure/gpt-5-chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5-chat-latest", + "name": "azure/gpt-5-chat-latest", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5-codex", + "name": "azure/gpt-5-codex", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5-mini", + "name": "azure/gpt-5-mini", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5-mini-2025-08-07", + "name": "azure/gpt-5-mini-2025-08-07", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5-nano", + "name": "azure/gpt-5-nano", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.049999999999999996, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5-nano-2025-08-07", + "name": "azure/gpt-5-nano-2025-08-07", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.049999999999999996, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5-pro", + "name": "azure/gpt-5-pro", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 15, + "pOut": 120, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.1", + "name": "azure/gpt-5.1", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.1-chat", + "name": "azure/gpt-5.1-chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.1-codex", + "name": "azure/gpt-5.1-codex", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.1-codex-max", + "name": "azure/gpt-5.1-codex-max", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.1-codex-mini", + "name": "azure/gpt-5.1-codex-mini", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.2", + "name": "azure/gpt-5.2", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.2-2025-12-11", + "name": "azure/gpt-5.2-2025-12-11", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.2-chat", + "name": "azure/gpt-5.2-chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.2-chat-2025-12-11", + "name": "azure/gpt-5.2-chat-2025-12-11", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.2-codex", + "name": "azure/gpt-5.2-codex", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.3-chat", + "name": "azure/gpt-5.3-chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.3-codex", + "name": "azure/gpt-5.3-codex", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.2-pro", + "name": "azure/gpt-5.2-pro", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 21, + "pOut": 168, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.2-pro-2025-12-11", + "name": "azure/gpt-5.2-pro-2025-12-11", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 21, + "pOut": 168, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.4", + "name": "azure/gpt-5.4", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 2.5, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.4-2026-03-05", + "name": "azure/gpt-5.4-2026-03-05", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 2.5, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.4-pro", + "name": "azure/gpt-5.4-pro", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 30, + "pOut": 180, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "azure/gpt-5.4-pro-2026-03-05", + "name": "azure/gpt-5.4-pro-2026-03-05", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 30, + "pOut": 180, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "azure/gpt-5.5", + "name": "azure/gpt-5.5", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 5, + "pOut": 30, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.5-2026-04-23", + "name": "azure/gpt-5.5-2026-04-23", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 5, + "pOut": 30, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.5-pro", + "name": "azure/gpt-5.5-pro", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 30, + "pOut": 180, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "azure/gpt-5.5-pro-2026-04-23", + "name": "azure/gpt-5.5-pro-2026-04-23", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 30, + "pOut": 180, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "azure/gpt-5.4-mini", + "name": "azure/gpt-5.4-mini", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 0.75, + "pOut": 4.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.4-mini-2026-03-17", + "name": "azure/gpt-5.4-mini-2026-03-17", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 0.75, + "pOut": 4.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.4-nano", + "name": "azure/gpt-5.4-nano", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 1.25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/gpt-5.4-nano-2026-03-17", + "name": "azure/gpt-5.4-nano-2026-03-17", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 1.25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/mistral-large-2402", + "name": "azure/mistral-large-2402", + "provider": "mistral", + "ctx": 32000, + "maxOut": null, + "pIn": 8, + "pOut": 24, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/mistral-large-latest", + "name": "azure/mistral-large-latest", + "provider": "mistral", + "ctx": 32000, + "maxOut": null, + "pIn": 8, + "pOut": 24, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/o1", + "name": "azure/o1", + "provider": "azure", + "ctx": 200000, + "maxOut": 100000, + "pIn": 15, + "pOut": 60, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure/o1-2024-12-17", + "name": "azure/o1-2024-12-17", + "provider": "azure", + "ctx": 200000, + "maxOut": 100000, + "pIn": 15, + "pOut": 60, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure/o1-mini", + "name": "azure/o1-mini", + "provider": "azure", + "ctx": 128000, + "maxOut": 65536, + "pIn": 1.21, + "pOut": 4.84, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure/o1-mini-2024-09-12", + "name": "azure/o1-mini-2024-09-12", + "provider": "azure", + "ctx": 128000, + "maxOut": 65536, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure/o1-preview", + "name": "azure/o1-preview", + "provider": "azure", + "ctx": 128000, + "maxOut": 32768, + "pIn": 15, + "pOut": 60, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure/o1-preview-2024-09-12", + "name": "azure/o1-preview-2024-09-12", + "provider": "azure", + "ctx": 128000, + "maxOut": 32768, + "pIn": 15, + "pOut": 60, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure/o3", + "name": "azure/o3", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/o3-2025-04-16", + "name": "azure/o3-2025-04-16", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/o3-deep-research", + "name": "azure/o3-deep-research", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 10, + "pOut": 40, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/o3-mini", + "name": "azure/o3-mini", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": null, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/o3-mini-2025-01-31", + "name": "azure/o3-mini-2025-01-31", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure/o3-pro", + "name": "azure/o3-pro", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 20, + "pOut": 80, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/o3-pro-2025-06-10", + "name": "azure/o3-pro-2025-06-10", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 20, + "pOut": 80, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/o4-mini", + "name": "azure/o4-mini", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/o4-mini-2025-04-16", + "name": "azure/o4-mini-2025-04-16", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/text-embedding-3-large", + "name": "azure/text-embedding-3-large", + "provider": "azure", + "ctx": 8191, + "maxOut": null, + "pIn": 0.13, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/text-embedding-3-small", + "name": "azure/text-embedding-3-small", + "provider": "azure", + "ctx": 8191, + "maxOut": null, + "pIn": 0.02, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/text-embedding-ada-002", + "name": "azure/text-embedding-ada-002", + "provider": "azure", + "ctx": 8191, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/us/gpt-4.1-2025-04-14", + "name": "azure/us/gpt-4.1-2025-04-14", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 2.2, + "pOut": 8.8, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/gpt-4.1-mini-2025-04-14", + "name": "azure/us/gpt-4.1-mini-2025-04-14", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.44, + "pOut": 1.76, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/gpt-4.1-nano-2025-04-14", + "name": "azure/us/gpt-4.1-nano-2025-04-14", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.11, + "pOut": 0.44, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/gpt-4o-2024-08-06", + "name": "azure/us/gpt-4o-2024-08-06", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.75, + "pOut": 11, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/gpt-4o-2024-11-20", + "name": "azure/us/gpt-4o-2024-11-20", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.75, + "pOut": 11, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/gpt-4o-mini-2024-07-18", + "name": "azure/us/gpt-4o-mini-2024-07-18", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.165, + "pOut": 0.66, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/gpt-4o-mini-realtime-preview-2024-12-17", + "name": "azure/us/gpt-4o-mini-realtime-preview-2024-12-17", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.66, + "pOut": 2.64, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/us/gpt-4o-realtime-preview-2024-10-01", + "name": "azure/us/gpt-4o-realtime-preview-2024-10-01", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5.5, + "pOut": 22, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/us/gpt-4o-realtime-preview-2024-12-17", + "name": "azure/us/gpt-4o-realtime-preview-2024-12-17", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5.5, + "pOut": 22, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/us/gpt-5-2025-08-07", + "name": "azure/us/gpt-5-2025-08-07", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.375, + "pOut": 11, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/gpt-5-mini-2025-08-07", + "name": "azure/us/gpt-5-mini-2025-08-07", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.275, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/gpt-5-nano-2025-08-07", + "name": "azure/us/gpt-5-nano-2025-08-07", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.055, + "pOut": 0.44, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/gpt-5.1", + "name": "azure/us/gpt-5.1", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.38, + "pOut": 11, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/gpt-5.1-chat", + "name": "azure/us/gpt-5.1-chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 1.38, + "pOut": 11, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/gpt-5.1-codex", + "name": "azure/us/gpt-5.1-codex", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.38, + "pOut": 11, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/gpt-5.1-codex-mini", + "name": "azure/us/gpt-5.1-codex-mini", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.275, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/o1-2024-12-17", + "name": "azure/us/o1-2024-12-17", + "provider": "azure", + "ctx": 200000, + "maxOut": 100000, + "pIn": 16.5, + "pOut": 66, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/us/o1-mini-2024-09-12", + "name": "azure/us/o1-mini-2024-09-12", + "provider": "azure", + "ctx": 128000, + "maxOut": 65536, + "pIn": 1.21, + "pOut": 4.84, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/us/o1-preview-2024-09-12", + "name": "azure/us/o1-preview-2024-09-12", + "provider": "azure", + "ctx": 128000, + "maxOut": 32768, + "pIn": 16.5, + "pOut": 66, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure/us/o3-2025-04-16", + "name": "azure/us/o3-2025-04-16", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 2.2, + "pOut": 8.8, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure/us/o3-mini-2025-01-31", + "name": "azure/us/o3-mini-2025-01-31", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.21, + "pOut": 4.84, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure/us/o4-mini-2025-04-16", + "name": "azure/us/o4-mini-2025-04-16", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.21, + "pOut": 4.84, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/Cohere-embed-v3-english", + "name": "azure_ai/Cohere-embed-v3-english", + "provider": "cohere", + "ctx": 512, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Cohere-embed-v3-multilingual", + "name": "azure_ai/Cohere-embed-v3-multilingual", + "provider": "cohere", + "ctx": 512, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Llama-3.2-11B-Vision-Instruct", + "name": "azure_ai/Llama-3.2-11B-Vision-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": 0.37, + "pOut": 0.37, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Llama-3.2-90B-Vision-Instruct", + "name": "azure_ai/Llama-3.2-90B-Vision-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": 2.04, + "pOut": 2.04, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Llama-3.3-70B-Instruct", + "name": "azure_ai/Llama-3.3-70B-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": 0.71, + "pOut": 0.71, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Llama-4-Maverick-17B-128E-Instruct-FP8", + "name": "azure_ai/Llama-4-Maverick-17B-128E-Instruct-FP8", + "provider": "meta", + "ctx": 1000000, + "maxOut": 16384, + "pIn": 1.4100000000000001, + "pOut": 0.35, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Llama-4-Scout-17B-16E-Instruct", + "name": "azure_ai/Llama-4-Scout-17B-16E-Instruct", + "provider": "meta", + "ctx": 10000000, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.78, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Meta-Llama-3-70B-Instruct", + "name": "azure_ai/Meta-Llama-3-70B-Instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 2048, + "pIn": 1.1, + "pOut": 0.37, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Meta-Llama-3.1-405B-Instruct", + "name": "azure_ai/Meta-Llama-3.1-405B-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": 5.33, + "pOut": 16, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Meta-Llama-3.1-70B-Instruct", + "name": "azure_ai/Meta-Llama-3.1-70B-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": 2.68, + "pOut": 3.54, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Meta-Llama-3.1-8B-Instruct", + "name": "azure_ai/Meta-Llama-3.1-8B-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": 0.3, + "pOut": 0.61, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-3-medium-128k-instruct", + "name": "azure_ai/Phi-3-medium-128k-instruct", + "provider": "azure_ai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.16999999999999998, + "pOut": 0.6799999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-3-medium-4k-instruct", + "name": "azure_ai/Phi-3-medium-4k-instruct", + "provider": "azure_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.16999999999999998, + "pOut": 0.6799999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-3-mini-128k-instruct", + "name": "azure_ai/Phi-3-mini-128k-instruct", + "provider": "azure_ai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.13, + "pOut": 0.52, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-3-mini-4k-instruct", + "name": "azure_ai/Phi-3-mini-4k-instruct", + "provider": "azure_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.13, + "pOut": 0.52, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-3-small-128k-instruct", + "name": "azure_ai/Phi-3-small-128k-instruct", + "provider": "azure_ai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.15, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-3-small-8k-instruct", + "name": "azure_ai/Phi-3-small-8k-instruct", + "provider": "azure_ai", + "ctx": 8192, + "maxOut": 4096, + "pIn": 0.15, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-3.5-MoE-instruct", + "name": "azure_ai/Phi-3.5-MoE-instruct", + "provider": "azure_ai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.16, + "pOut": 0.64, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-3.5-mini-instruct", + "name": "azure_ai/Phi-3.5-mini-instruct", + "provider": "azure_ai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.13, + "pOut": 0.52, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-3.5-vision-instruct", + "name": "azure_ai/Phi-3.5-vision-instruct", + "provider": "azure_ai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.13, + "pOut": 0.52, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-4", + "name": "azure_ai/Phi-4", + "provider": "azure_ai", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.125, + "pOut": 0.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-4-mini-instruct", + "name": "azure_ai/Phi-4-mini-instruct", + "provider": "azure_ai", + "ctx": 131072, + "maxOut": 4096, + "pIn": 0.075, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-4-multimodal-instruct", + "name": "azure_ai/Phi-4-multimodal-instruct", + "provider": "azure_ai", + "ctx": 131072, + "maxOut": 4096, + "pIn": 0.08, + "pOut": 0.32, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-4-mini-reasoning", + "name": "azure_ai/Phi-4-mini-reasoning", + "provider": "azure_ai", + "ctx": 131072, + "maxOut": 4096, + "pIn": 0.08, + "pOut": 0.32, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/Phi-4-reasoning", + "name": "azure_ai/Phi-4-reasoning", + "provider": "azure_ai", + "ctx": 32768, + "maxOut": 4096, + "pIn": 0.125, + "pOut": 0.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/MAI-DS-R1", + "name": "azure_ai/MAI-DS-R1", + "provider": "azure_ai", + "ctx": 128000, + "maxOut": 8192, + "pIn": 1.35, + "pOut": 5.4, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/cohere-rerank-v3-english", + "name": "azure_ai/cohere-rerank-v3-english", + "provider": "cohere", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/cohere-rerank-v3-multilingual", + "name": "azure_ai/cohere-rerank-v3-multilingual", + "provider": "cohere", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/cohere-rerank-v3.5", + "name": "azure_ai/cohere-rerank-v3.5", + "provider": "cohere", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/cohere-rerank-v4.0-pro", + "name": "azure_ai/cohere-rerank-v4.0-pro", + "provider": "cohere", + "ctx": 32768, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/cohere-rerank-v4.0-fast", + "name": "azure_ai/cohere-rerank-v4.0-fast", + "provider": "cohere", + "ctx": 32768, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/deepseek-v3.2", + "name": "azure_ai/deepseek-v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.58, + "pOut": 1.68, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/deepseek-v3.2-speciale", + "name": "azure_ai/deepseek-v3.2-speciale", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.58, + "pOut": 1.68, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/deepseek-r1", + "name": "azure_ai/deepseek-r1", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 8192, + "pIn": 1.35, + "pOut": 5.4, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/deepseek-v3", + "name": "azure_ai/deepseek-v3", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 8192, + "pIn": 1.1400000000000001, + "pOut": 4.5600000000000005, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/deepseek-v3-0324", + "name": "azure_ai/deepseek-v3-0324", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 8192, + "pIn": 1.1400000000000001, + "pOut": 4.5600000000000005, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/embed-v-4-0", + "name": "azure_ai/embed-v-4-0", + "provider": "azure_ai", + "ctx": 128000, + "maxOut": null, + "pIn": 0.12, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/global/grok-3", + "name": "azure_ai/global/grok-3", + "provider": "azure_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "azure_ai/global/grok-3-mini", + "name": "azure_ai/global/grok-3-mini", + "provider": "azure_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.25, + "pOut": 1.27, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "azure_ai/grok-3", + "name": "azure_ai/grok-3", + "provider": "azure_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "azure_ai/grok-3-mini", + "name": "azure_ai/grok-3-mini", + "provider": "azure_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.25, + "pOut": 1.27, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "azure_ai/grok-4", + "name": "azure_ai/grok-4", + "provider": "azure_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/grok-4-fast-non-reasoning", + "name": "azure_ai/grok-4-fast-non-reasoning", + "provider": "azure_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/grok-4-fast-reasoning", + "name": "azure_ai/grok-4-fast-reasoning", + "provider": "azure_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/grok-4-1-fast-non-reasoning", + "name": "azure_ai/grok-4-1-fast-non-reasoning", + "provider": "azure_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/grok-4-1-fast-reasoning", + "name": "azure_ai/grok-4-1-fast-reasoning", + "provider": "azure_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/grok-code-fast-1", + "name": "azure_ai/grok-code-fast-1", + "provider": "azure_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "azure_ai/jais-30b-chat", + "name": "azure_ai/jais-30b-chat", + "provider": "azure_ai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 3200, + "pOut": 9710, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/jamba-instruct", + "name": "azure_ai/jamba-instruct", + "provider": "azure_ai", + "ctx": 70000, + "maxOut": 4096, + "pIn": 0.5, + "pOut": 0.7, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/kimi-k2.5", + "name": "azure_ai/kimi-k2.5", + "provider": "azure_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/ministral-3b", + "name": "azure_ai/ministral-3b", + "provider": "azure_ai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.04, + "pOut": 0.04, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/mistral-large", + "name": "azure_ai/mistral-large", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 4, + "pOut": 12, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/mistral-large-2407", + "name": "azure_ai/mistral-large-2407", + "provider": "mistral", + "ctx": 128000, + "maxOut": 4096, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/mistral-large-latest", + "name": "azure_ai/mistral-large-latest", + "provider": "mistral", + "ctx": 128000, + "maxOut": 4096, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/mistral-large-3", + "name": "azure_ai/mistral-large-3", + "provider": "mistral", + "ctx": 256000, + "maxOut": 8191, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/mistral-medium-2505", + "name": "azure_ai/mistral-medium-2505", + "provider": "mistral", + "ctx": 131072, + "maxOut": 8191, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/mistral-nemo", + "name": "azure_ai/mistral-nemo", + "provider": "mistral", + "ctx": 131072, + "maxOut": 4096, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/mistral-small", + "name": "azure_ai/mistral-small", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "azure_ai/mistral-small-2503", + "name": "azure_ai/mistral-small-2503", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "babbage-002", + "name": "babbage-002", + "provider": "babbage-002", + "ctx": 16384, + "maxOut": 4096, + "pIn": 0.39999999999999997, + "pOut": 0.39999999999999997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/*/1-month-commitment/cohere.command-light-text-v14", + "name": "bedrock/*/1-month-commitment/cohere.command-light-text-v14", + "provider": "cohere", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/*/1-month-commitment/cohere.command-text-v14", + "name": "bedrock/*/1-month-commitment/cohere.command-text-v14", + "provider": "cohere", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/*/6-month-commitment/cohere.command-light-text-v14", + "name": "bedrock/*/6-month-commitment/cohere.command-light-text-v14", + "provider": "cohere", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/*/6-month-commitment/cohere.command-text-v14", + "name": "bedrock/*/6-month-commitment/cohere.command-text-v14", + "provider": "cohere", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/1-month-commitment/anthropic.claude-instant-v1", + "name": "bedrock/ap-northeast-1/1-month-commitment/anthropic.claude-instant-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/1-month-commitment/anthropic.claude-v1", + "name": "bedrock/ap-northeast-1/1-month-commitment/anthropic.claude-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/1-month-commitment/anthropic.claude-v2:1", + "name": "bedrock/ap-northeast-1/1-month-commitment/anthropic.claude-v2:1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/6-month-commitment/anthropic.claude-instant-v1", + "name": "bedrock/ap-northeast-1/6-month-commitment/anthropic.claude-instant-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/6-month-commitment/anthropic.claude-v1", + "name": "bedrock/ap-northeast-1/6-month-commitment/anthropic.claude-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/6-month-commitment/anthropic.claude-v2:1", + "name": "bedrock/ap-northeast-1/6-month-commitment/anthropic.claude-v2:1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/anthropic.claude-instant-v1", + "name": "bedrock/ap-northeast-1/anthropic.claude-instant-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 2.23, + "pOut": 7.55, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/anthropic.claude-v1", + "name": "bedrock/ap-northeast-1/anthropic.claude-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/anthropic.claude-v2:1", + "name": "bedrock/ap-northeast-1/anthropic.claude-v2:1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/deepseek.v3.2", + "name": "bedrock/ap-northeast-1/deepseek.v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.74, + "pOut": 2.2199999999999998, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/minimax.minimax-m2.1", + "name": "bedrock/ap-northeast-1/minimax.minimax-m2.1", + "provider": "bedrock", + "ctx": 196000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/minimax.minimax-m2.5", + "name": "bedrock/ap-northeast-1/minimax.minimax-m2.5", + "provider": "bedrock", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/moonshotai.kimi-k2-thinking", + "name": "bedrock/ap-northeast-1/moonshotai.kimi-k2-thinking", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.73, + "pOut": 3.03, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/moonshotai.kimi-k2.5", + "name": "bedrock/ap-northeast-1/moonshotai.kimi-k2.5", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.72, + "pOut": 3.5999999999999996, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-northeast-1/qwen.qwen3-coder-next", + "name": "bedrock/ap-northeast-1/qwen.qwen3-coder-next", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/moonshotai.kimi-k2-thinking", + "name": "bedrock/moonshotai.kimi-k2-thinking", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.73, + "pOut": 3.03, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/moonshotai.kimi-k2.5", + "name": "bedrock/moonshotai.kimi-k2.5", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 3.03, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-south-1/meta.llama3-70b-instruct-v1:0", + "name": "bedrock/ap-south-1/meta.llama3-70b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 3.18, + "pOut": 4.199999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-south-1/meta.llama3-8b-instruct-v1:0", + "name": "bedrock/ap-south-1/meta.llama3-8b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 0.72, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-south-1/deepseek.v3.2", + "name": "bedrock/ap-south-1/deepseek.v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.74, + "pOut": 2.2199999999999998, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-south-1/minimax.minimax-m2.1", + "name": "bedrock/ap-south-1/minimax.minimax-m2.1", + "provider": "bedrock", + "ctx": 196000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-south-1/minimax.minimax-m2.5", + "name": "bedrock/ap-south-1/minimax.minimax-m2.5", + "provider": "bedrock", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-south-1/moonshotai.kimi-k2-thinking", + "name": "bedrock/ap-south-1/moonshotai.kimi-k2-thinking", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.71, + "pOut": 2.94, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-south-1/moonshotai.kimi-k2.5", + "name": "bedrock/ap-south-1/moonshotai.kimi-k2.5", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.72, + "pOut": 3.5999999999999996, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-south-1/qwen.qwen3-coder-next", + "name": "bedrock/ap-south-1/qwen.qwen3-coder-next", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-southeast-2/minimax.minimax-m2.5", + "name": "bedrock/ap-southeast-2/minimax.minimax-m2.5", + "provider": "bedrock", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.309, + "pOut": 1.236, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-southeast-3/deepseek.v3.2", + "name": "bedrock/ap-southeast-3/deepseek.v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.74, + "pOut": 2.2199999999999998, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-southeast-3/minimax.minimax-m2.1", + "name": "bedrock/ap-southeast-3/minimax.minimax-m2.1", + "provider": "bedrock", + "ctx": 196000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-southeast-3/minimax.minimax-m2.5", + "name": "bedrock/ap-southeast-3/minimax.minimax-m2.5", + "provider": "bedrock", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-southeast-3/moonshotai.kimi-k2.5", + "name": "bedrock/ap-southeast-3/moonshotai.kimi-k2.5", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.72, + "pOut": 3.5999999999999996, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ap-southeast-3/qwen.qwen3-coder-next", + "name": "bedrock/ap-southeast-3/qwen.qwen3-coder-next", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ca-central-1/meta.llama3-70b-instruct-v1:0", + "name": "bedrock/ca-central-1/meta.llama3-70b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 3.05, + "pOut": 4.03, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/ca-central-1/meta.llama3-8b-instruct-v1:0", + "name": "bedrock/ca-central-1/meta.llama3-8b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.35, + "pOut": 0.69, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-north-1/deepseek.v3.2", + "name": "bedrock/eu-north-1/deepseek.v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.74, + "pOut": 2.2199999999999998, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-north-1/minimax.minimax-m2.1", + "name": "bedrock/eu-north-1/minimax.minimax-m2.1", + "provider": "bedrock", + "ctx": 196000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-north-1/minimax.minimax-m2.5", + "name": "bedrock/eu-north-1/minimax.minimax-m2.5", + "provider": "bedrock", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-north-1/moonshotai.kimi-k2.5", + "name": "bedrock/eu-north-1/moonshotai.kimi-k2.5", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.72, + "pOut": 3.5999999999999996, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-central-1/1-month-commitment/anthropic.claude-instant-v1", + "name": "bedrock/eu-central-1/1-month-commitment/anthropic.claude-instant-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-central-1/1-month-commitment/anthropic.claude-v1", + "name": "bedrock/eu-central-1/1-month-commitment/anthropic.claude-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-central-1/1-month-commitment/anthropic.claude-v2:1", + "name": "bedrock/eu-central-1/1-month-commitment/anthropic.claude-v2:1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-central-1/6-month-commitment/anthropic.claude-instant-v1", + "name": "bedrock/eu-central-1/6-month-commitment/anthropic.claude-instant-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-central-1/6-month-commitment/anthropic.claude-v1", + "name": "bedrock/eu-central-1/6-month-commitment/anthropic.claude-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-central-1/6-month-commitment/anthropic.claude-v2:1", + "name": "bedrock/eu-central-1/6-month-commitment/anthropic.claude-v2:1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-central-1/anthropic.claude-instant-v1", + "name": "bedrock/eu-central-1/anthropic.claude-instant-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 2.48, + "pOut": 8.379999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-central-1/anthropic.claude-v1", + "name": "bedrock/eu-central-1/anthropic.claude-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-central-1/anthropic.claude-v2:1", + "name": "bedrock/eu-central-1/anthropic.claude-v2:1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-central-1/minimax.minimax-m2.1", + "name": "bedrock/eu-central-1/minimax.minimax-m2.1", + "provider": "bedrock", + "ctx": 196000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-central-1/minimax.minimax-m2.5", + "name": "bedrock/eu-central-1/minimax.minimax-m2.5", + "provider": "bedrock", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-central-1/qwen.qwen3-coder-next", + "name": "bedrock/eu-central-1/qwen.qwen3-coder-next", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-west-1/meta.llama3-70b-instruct-v1:0", + "name": "bedrock/eu-west-1/meta.llama3-70b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 2.8600000000000003, + "pOut": 3.78, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-west-1/meta.llama3-8b-instruct-v1:0", + "name": "bedrock/eu-west-1/meta.llama3-8b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.32, + "pOut": 0.65, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-west-1/minimax.minimax-m2.1", + "name": "bedrock/eu-west-1/minimax.minimax-m2.1", + "provider": "bedrock", + "ctx": 196000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-west-1/minimax.minimax-m2.5", + "name": "bedrock/eu-west-1/minimax.minimax-m2.5", + "provider": "bedrock", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-west-1/qwen.qwen3-coder-next", + "name": "bedrock/eu-west-1/qwen.qwen3-coder-next", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-west-2/meta.llama3-70b-instruct-v1:0", + "name": "bedrock/eu-west-2/meta.llama3-70b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 3.45, + "pOut": 4.55, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-west-2/meta.llama3-8b-instruct-v1:0", + "name": "bedrock/eu-west-2/meta.llama3-8b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.39, + "pOut": 0.78, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-west-2/minimax.minimax-m2.1", + "name": "bedrock/eu-west-2/minimax.minimax-m2.1", + "provider": "bedrock", + "ctx": 196000, + "maxOut": 8192, + "pIn": 0.47, + "pOut": 1.8599999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-west-2/minimax.minimax-m2.5", + "name": "bedrock/eu-west-2/minimax.minimax-m2.5", + "provider": "bedrock", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.47, + "pOut": 1.8599999999999999, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-west-2/qwen.qwen3-coder-next", + "name": "bedrock/eu-west-2/qwen.qwen3-coder-next", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 8192, + "pIn": 0.78, + "pOut": 1.8599999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-west-3/mistral.mistral-7b-instruct-v0:2", + "name": "bedrock/eu-west-3/mistral.mistral-7b-instruct-v0:2", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 0.19999999999999998, + "pOut": 0.26, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-west-3/mistral.mistral-large-2402-v1:0", + "name": "bedrock/eu-west-3/mistral.mistral-large-2402-v1:0", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 10.4, + "pOut": 31.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-west-3/mistral.mixtral-8x7b-instruct-v0:1", + "name": "bedrock/eu-west-3/mistral.mixtral-8x7b-instruct-v0:1", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 0.59, + "pOut": 0.9099999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-south-1/minimax.minimax-m2.1", + "name": "bedrock/eu-south-1/minimax.minimax-m2.1", + "provider": "bedrock", + "ctx": 196000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-south-1/minimax.minimax-m2.5", + "name": "bedrock/eu-south-1/minimax.minimax-m2.5", + "provider": "bedrock", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/eu-south-1/qwen.qwen3-coder-next", + "name": "bedrock/eu-south-1/qwen.qwen3-coder-next", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/invoke/anthropic.claude-3-5-sonnet-20240620-v1:0", + "name": "bedrock/invoke/anthropic.claude-3-5-sonnet-20240620-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock/sa-east-1/meta.llama3-70b-instruct-v1:0", + "name": "bedrock/sa-east-1/meta.llama3-70b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 4.45, + "pOut": 5.88, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/sa-east-1/meta.llama3-8b-instruct-v1:0", + "name": "bedrock/sa-east-1/meta.llama3-8b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.5, + "pOut": 1.01, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/sa-east-1/deepseek.v3.2", + "name": "bedrock/sa-east-1/deepseek.v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.74, + "pOut": 2.2199999999999998, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/sa-east-1/minimax.minimax-m2.1", + "name": "bedrock/sa-east-1/minimax.minimax-m2.1", + "provider": "bedrock", + "ctx": 196000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/sa-east-1/minimax.minimax-m2.5", + "name": "bedrock/sa-east-1/minimax.minimax-m2.5", + "provider": "bedrock", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.36, + "pOut": 1.44, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/sa-east-1/moonshotai.kimi-k2-thinking", + "name": "bedrock/sa-east-1/moonshotai.kimi-k2-thinking", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.73, + "pOut": 3.03, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/sa-east-1/moonshotai.kimi-k2.5", + "name": "bedrock/sa-east-1/moonshotai.kimi-k2.5", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.72, + "pOut": 3.5999999999999996, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/sa-east-1/qwen.qwen3-coder-next", + "name": "bedrock/sa-east-1/qwen.qwen3-coder-next", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 1.44, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/1-month-commitment/anthropic.claude-instant-v1", + "name": "bedrock/us-east-1/1-month-commitment/anthropic.claude-instant-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/1-month-commitment/anthropic.claude-v1", + "name": "bedrock/us-east-1/1-month-commitment/anthropic.claude-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/1-month-commitment/anthropic.claude-v2:1", + "name": "bedrock/us-east-1/1-month-commitment/anthropic.claude-v2:1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/6-month-commitment/anthropic.claude-instant-v1", + "name": "bedrock/us-east-1/6-month-commitment/anthropic.claude-instant-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/6-month-commitment/anthropic.claude-v1", + "name": "bedrock/us-east-1/6-month-commitment/anthropic.claude-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/6-month-commitment/anthropic.claude-v2:1", + "name": "bedrock/us-east-1/6-month-commitment/anthropic.claude-v2:1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/anthropic.claude-instant-v1", + "name": "bedrock/us-east-1/anthropic.claude-instant-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 0.7999999999999999, + "pOut": 2.4, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/anthropic.claude-v1", + "name": "bedrock/us-east-1/anthropic.claude-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/anthropic.claude-v2:1", + "name": "bedrock/us-east-1/anthropic.claude-v2:1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/meta.llama3-70b-instruct-v1:0", + "name": "bedrock/us-east-1/meta.llama3-70b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 2.65, + "pOut": 3.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/meta.llama3-8b-instruct-v1:0", + "name": "bedrock/us-east-1/meta.llama3-8b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/mistral.mistral-7b-instruct-v0:2", + "name": "bedrock/us-east-1/mistral.mistral-7b-instruct-v0:2", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 0.15, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/mistral.mistral-large-2402-v1:0", + "name": "bedrock/us-east-1/mistral.mistral-large-2402-v1:0", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/mistral.mixtral-8x7b-instruct-v0:1", + "name": "bedrock/us-east-1/mistral.mixtral-8x7b-instruct-v0:1", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 0.44999999999999996, + "pOut": 0.7, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/deepseek.v3.2", + "name": "bedrock/us-east-1/deepseek.v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.62, + "pOut": 1.85, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/minimax.minimax-m2.1", + "name": "bedrock/us-east-1/minimax.minimax-m2.1", + "provider": "bedrock", + "ctx": 196000, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/minimax.minimax-m2.5", + "name": "bedrock/us-east-1/minimax.minimax-m2.5", + "provider": "bedrock", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/moonshotai.kimi-k2-thinking", + "name": "bedrock/us-east-1/moonshotai.kimi-k2-thinking", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/moonshotai.kimi-k2.5", + "name": "bedrock/us-east-1/moonshotai.kimi-k2.5", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/qwen.qwen3-coder-next", + "name": "bedrock/us-east-1/qwen.qwen3-coder-next", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 8192, + "pIn": 0.5, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-2/deepseek.v3.2", + "name": "bedrock/us-east-2/deepseek.v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.62, + "pOut": 1.85, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-2/minimax.minimax-m2.1", + "name": "bedrock/us-east-2/minimax.minimax-m2.1", + "provider": "bedrock", + "ctx": 196000, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-2/minimax.minimax-m2.5", + "name": "bedrock/us-east-2/minimax.minimax-m2.5", + "provider": "bedrock", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-2/moonshotai.kimi-k2-thinking", + "name": "bedrock/us-east-2/moonshotai.kimi-k2-thinking", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-2/moonshotai.kimi-k2.5", + "name": "bedrock/us-east-2/moonshotai.kimi-k2.5", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-2/qwen.qwen3-coder-next", + "name": "bedrock/us-east-2/qwen.qwen3-coder-next", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 8192, + "pIn": 0.5, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-east-1/amazon.nova-pro-v1:0", + "name": "bedrock/us-gov-east-1/amazon.nova-pro-v1:0", + "provider": "bedrock", + "ctx": 300000, + "maxOut": 10000, + "pIn": 0.96, + "pOut": 3.84, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-east-1/amazon.titan-embed-text-v1", + "name": "bedrock/us-gov-east-1/amazon.titan-embed-text-v1", + "provider": "bedrock", + "ctx": 8192, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-east-1/amazon.titan-embed-text-v2:0", + "name": "bedrock/us-gov-east-1/amazon.titan-embed-text-v2:0", + "provider": "bedrock", + "ctx": 8192, + "maxOut": null, + "pIn": 0.19999999999999998, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-east-1/amazon.titan-text-express-v1", + "name": "bedrock/us-gov-east-1/amazon.titan-text-express-v1", + "provider": "bedrock", + "ctx": 42000, + "maxOut": 8000, + "pIn": 1.3, + "pOut": 1.7, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-east-1/amazon.titan-text-lite-v1", + "name": "bedrock/us-gov-east-1/amazon.titan-text-lite-v1", + "provider": "bedrock", + "ctx": 42000, + "maxOut": 4000, + "pIn": 0.3, + "pOut": 0.39999999999999997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-east-1/amazon.titan-text-premier-v1:0", + "name": "bedrock/us-gov-east-1/amazon.titan-text-premier-v1:0", + "provider": "bedrock", + "ctx": 42000, + "maxOut": 32000, + "pIn": 0.5, + "pOut": 1.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-east-1/anthropic.claude-3-5-sonnet-20240620-v1:0", + "name": "bedrock/us-gov-east-1/anthropic.claude-3-5-sonnet-20240620-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3.5999999999999996, + "pOut": 18, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-east-1/anthropic.claude-3-haiku-20240307-v1:0", + "name": "bedrock/us-gov-east-1/anthropic.claude-3-haiku-20240307-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 0.3, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-east-1/anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "bedrock/us-gov-east-1/anthropic.claude-sonnet-4-5-20250929-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-east-1/claude-sonnet-4-5-20250929-v1:0", + "name": "bedrock/us-gov-east-1/claude-sonnet-4-5-20250929-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-east-1/meta.llama3-70b-instruct-v1:0", + "name": "bedrock/us-gov-east-1/meta.llama3-70b-instruct-v1:0", + "provider": "meta", + "ctx": 8000, + "maxOut": 2048, + "pIn": 2.65, + "pOut": 3.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-east-1/meta.llama3-8b-instruct-v1:0", + "name": "bedrock/us-gov-east-1/meta.llama3-8b-instruct-v1:0", + "provider": "meta", + "ctx": 8000, + "maxOut": 2048, + "pIn": 0.3, + "pOut": 2.65, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/amazon.nova-pro-v1:0", + "name": "bedrock/us-gov-west-1/amazon.nova-pro-v1:0", + "provider": "bedrock", + "ctx": 300000, + "maxOut": 10000, + "pIn": 0.96, + "pOut": 3.84, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/amazon.titan-embed-text-v1", + "name": "bedrock/us-gov-west-1/amazon.titan-embed-text-v1", + "provider": "bedrock", + "ctx": 8192, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/amazon.titan-embed-text-v2:0", + "name": "bedrock/us-gov-west-1/amazon.titan-embed-text-v2:0", + "provider": "bedrock", + "ctx": 8192, + "maxOut": null, + "pIn": 0.19999999999999998, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/amazon.titan-text-express-v1", + "name": "bedrock/us-gov-west-1/amazon.titan-text-express-v1", + "provider": "bedrock", + "ctx": 42000, + "maxOut": 8000, + "pIn": 1.3, + "pOut": 1.7, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/amazon.titan-text-lite-v1", + "name": "bedrock/us-gov-west-1/amazon.titan-text-lite-v1", + "provider": "bedrock", + "ctx": 42000, + "maxOut": 4000, + "pIn": 0.3, + "pOut": 0.39999999999999997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/amazon.titan-text-premier-v1:0", + "name": "bedrock/us-gov-west-1/amazon.titan-text-premier-v1:0", + "provider": "bedrock", + "ctx": 42000, + "maxOut": 32000, + "pIn": 0.5, + "pOut": 1.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/anthropic.claude-3-7-sonnet-20250219-v1:0", + "name": "bedrock/us-gov-west-1/anthropic.claude-3-7-sonnet-20250219-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3.5999999999999996, + "pOut": 18, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/anthropic.claude-3-5-sonnet-20240620-v1:0", + "name": "bedrock/us-gov-west-1/anthropic.claude-3-5-sonnet-20240620-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3.5999999999999996, + "pOut": 18, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/anthropic.claude-3-haiku-20240307-v1:0", + "name": "bedrock/us-gov-west-1/anthropic.claude-3-haiku-20240307-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 0.3, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "bedrock/us-gov-west-1/anthropic.claude-sonnet-4-5-20250929-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/claude-sonnet-4-5-20250929-v1:0", + "name": "bedrock/us-gov-west-1/claude-sonnet-4-5-20250929-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/meta.llama3-70b-instruct-v1:0", + "name": "bedrock/us-gov-west-1/meta.llama3-70b-instruct-v1:0", + "provider": "meta", + "ctx": 8000, + "maxOut": 2048, + "pIn": 2.65, + "pOut": 3.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/meta.llama3-8b-instruct-v1:0", + "name": "bedrock/us-gov-west-1/meta.llama3-8b-instruct-v1:0", + "provider": "meta", + "ctx": 8000, + "maxOut": 2048, + "pIn": 0.3, + "pOut": 2.65, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-1/meta.llama3-70b-instruct-v1:0", + "name": "bedrock/us-west-1/meta.llama3-70b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 2.65, + "pOut": 3.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-1/meta.llama3-8b-instruct-v1:0", + "name": "bedrock/us-west-1/meta.llama3-8b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/1-month-commitment/anthropic.claude-instant-v1", + "name": "bedrock/us-west-2/1-month-commitment/anthropic.claude-instant-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/1-month-commitment/anthropic.claude-v1", + "name": "bedrock/us-west-2/1-month-commitment/anthropic.claude-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/1-month-commitment/anthropic.claude-v2:1", + "name": "bedrock/us-west-2/1-month-commitment/anthropic.claude-v2:1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/6-month-commitment/anthropic.claude-instant-v1", + "name": "bedrock/us-west-2/6-month-commitment/anthropic.claude-instant-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/6-month-commitment/anthropic.claude-v1", + "name": "bedrock/us-west-2/6-month-commitment/anthropic.claude-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/6-month-commitment/anthropic.claude-v2:1", + "name": "bedrock/us-west-2/6-month-commitment/anthropic.claude-v2:1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/anthropic.claude-instant-v1", + "name": "bedrock/us-west-2/anthropic.claude-instant-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 0.7999999999999999, + "pOut": 2.4, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/anthropic.claude-v1", + "name": "bedrock/us-west-2/anthropic.claude-v1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/anthropic.claude-v2:1", + "name": "bedrock/us-west-2/anthropic.claude-v2:1", + "provider": "claude", + "ctx": 100000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/mistral.mistral-7b-instruct-v0:2", + "name": "bedrock/us-west-2/mistral.mistral-7b-instruct-v0:2", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 0.15, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/mistral.mistral-large-2402-v1:0", + "name": "bedrock/us-west-2/mistral.mistral-large-2402-v1:0", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/mistral.mixtral-8x7b-instruct-v0:1", + "name": "bedrock/us-west-2/mistral.mixtral-8x7b-instruct-v0:1", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 0.44999999999999996, + "pOut": 0.7, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/deepseek.v3.2", + "name": "bedrock/us-west-2/deepseek.v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.62, + "pOut": 1.85, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/minimax.minimax-m2.1", + "name": "bedrock/us-west-2/minimax.minimax-m2.1", + "provider": "bedrock", + "ctx": 196000, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/minimax.minimax-m2.5", + "name": "bedrock/us-west-2/minimax.minimax-m2.5", + "provider": "bedrock", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/moonshotai.kimi-k2-thinking", + "name": "bedrock/us-west-2/moonshotai.kimi-k2-thinking", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/moonshotai.kimi-k2.5", + "name": "bedrock/us-west-2/moonshotai.kimi-k2.5", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/qwen.qwen3-coder-next", + "name": "bedrock/us-west-2/qwen.qwen3-coder-next", + "provider": "bedrock", + "ctx": 262144, + "maxOut": 8192, + "pIn": 0.5, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us.anthropic.claude-3-5-haiku-20241022-v1:0", + "name": "bedrock/us.anthropic.claude-3-5-haiku-20241022-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 0.7999999999999999, + "pOut": 4, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "cerebras/llama-3.3-70b", + "name": "cerebras/llama-3.3-70b", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.85, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cerebras/llama3.1-70b", + "name": "cerebras/llama3.1-70b", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.6, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cerebras/llama3.1-8b", + "name": "cerebras/llama3.1-8b", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cerebras/gpt-oss-120b", + "name": "cerebras/gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.35, + "pOut": 0.75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "cerebras/qwen-3-32b", + "name": "cerebras/qwen-3-32b", + "provider": "cerebras", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.39999999999999997, + "pOut": 0.7999999999999999, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "cerebras/zai-glm-4.6", + "name": "cerebras/zai-glm-4.6", + "provider": "cerebras", + "ctx": 128000, + "maxOut": 128000, + "pIn": 2.25, + "pOut": 2.75, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "cerebras/zai-glm-4.7", + "name": "cerebras/zai-glm-4.7", + "provider": "cerebras", + "ctx": 128000, + "maxOut": 128000, + "pIn": 2.25, + "pOut": 2.75, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "chatdolphin", + "name": "chatdolphin", + "provider": "chatdolphin", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.5, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "chatgpt-4o-latest", + "name": "chatgpt-4o-latest", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "claude-haiku-4-5-20251001", + "name": "claude-haiku-4-5-20251001", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-haiku-4-5", + "name": "claude-haiku-4-5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-3-7-sonnet-20250219", + "name": "claude-3-7-sonnet-20250219", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-3-haiku-20240307", + "name": "claude-3-haiku-20240307", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 0.25, + "pOut": 1.25, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "claude-3-opus-20240229", + "name": "claude-3-opus-20240229", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "claude-4-opus-20250514", + "name": "claude-4-opus-20250514", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-4-sonnet-20250514", + "name": "claude-4-sonnet-20250514", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-sonnet-4-5", + "name": "claude-sonnet-4-5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-sonnet-4-5-20250929", + "name": "claude-sonnet-4-5-20250929", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-sonnet-4-6", + "name": "claude-sonnet-4-6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-sonnet-4-5-20250929-v1:0", + "name": "claude-sonnet-4-5-20250929-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-opus-4-1", + "name": "claude-opus-4-1", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-opus-4-1-20250805", + "name": "claude-opus-4-1-20250805", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-opus-4-20250514", + "name": "claude-opus-4-20250514", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-opus-4-5-20251101", + "name": "claude-opus-4-5-20251101", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-opus-4-5", + "name": "claude-opus-4-5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-opus-4-6", + "name": "claude-opus-4-6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-opus-4-6-20260205", + "name": "claude-opus-4-6-20260205", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-opus-4-7", + "name": "claude-opus-4-7", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-opus-4-7-20260416", + "name": "claude-opus-4-7-20260416", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-opus-4-8", + "name": "claude-opus-4-8", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "claude-sonnet-4-20250514", + "name": "claude-sonnet-4-20250514", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "cloudflare/@cf/meta/llama-2-7b-chat-fp16", + "name": "cloudflare/@cf/meta/llama-2-7b-chat-fp16", + "provider": "meta", + "ctx": 3072, + "maxOut": 3072, + "pIn": 1.923, + "pOut": 1.923, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cloudflare/@cf/meta/llama-2-7b-chat-int8", + "name": "cloudflare/@cf/meta/llama-2-7b-chat-int8", + "provider": "meta", + "ctx": 2048, + "maxOut": 2048, + "pIn": 1.923, + "pOut": 1.923, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cloudflare/@cf/mistral/mistral-7b-instruct-v0.1", + "name": "cloudflare/@cf/mistral/mistral-7b-instruct-v0.1", + "provider": "mistral", + "ctx": 8192, + "maxOut": 8192, + "pIn": 1.923, + "pOut": 1.923, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cloudflare/@hf/thebloke/codellama-7b-instruct-awq", + "name": "cloudflare/@hf/thebloke/codellama-7b-instruct-awq", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 1.923, + "pOut": 1.923, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "codestral/codestral-2405", + "name": "codestral/codestral-2405", + "provider": "codestral", + "ctx": 32000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "codestral/codestral-latest", + "name": "codestral/codestral-latest", + "provider": "codestral", + "ctx": 32000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "codex-mini-latest", + "name": "codex-mini-latest", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.5, + "pOut": 6, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "cohere.command-light-text-v14", + "name": "cohere.command-light-text-v14", + "provider": "cohere", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.3, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cohere.command-r-plus-v1:0", + "name": "cohere.command-r-plus-v1:0", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4096, + "pIn": 3, + "pOut": 15, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cohere.command-r-v1:0", + "name": "cohere.command-r-v1:0", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.5, + "pOut": 1.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cohere.command-text-v14", + "name": "cohere.command-text-v14", + "provider": "cohere", + "ctx": 4096, + "maxOut": 4096, + "pIn": 1.5, + "pOut": 2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cohere.embed-english-v3", + "name": "cohere.embed-english-v3", + "provider": "cohere", + "ctx": 512, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cohere.embed-multilingual-v3", + "name": "cohere.embed-multilingual-v3", + "provider": "cohere", + "ctx": 512, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cohere.embed-v4:0", + "name": "cohere.embed-v4:0", + "provider": "cohere", + "ctx": 128000, + "maxOut": null, + "pIn": 0.12, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cohere/embed-v4.0", + "name": "cohere/embed-v4.0", + "provider": "cohere", + "ctx": 128000, + "maxOut": null, + "pIn": 0.12, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "cohere.rerank-v3-5:0", + "name": "cohere.rerank-v3-5:0", + "provider": "cohere", + "ctx": 32000, + "maxOut": 32000, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "command", + "name": "command", + "provider": "cohere", + "ctx": 4096, + "maxOut": 4096, + "pIn": 1, + "pOut": 2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "command-a-03-2025", + "name": "command-a-03-2025", + "provider": "cohere", + "ctx": 256000, + "maxOut": 8000, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "command-light", + "name": "command-light", + "provider": "cohere", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.3, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "command-nightly", + "name": "command-nightly", + "provider": "cohere", + "ctx": 4096, + "maxOut": 4096, + "pIn": 1, + "pOut": 2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "command-r", + "name": "command-r", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "command-r-08-2024", + "name": "command-r-08-2024", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "command-r-plus", + "name": "command-r-plus", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4096, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "command-r-plus-08-2024", + "name": "command-r-plus-08-2024", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4096, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "command-r7b-12-2024", + "name": "command-r7b-12-2024", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.15, + "pOut": 0.0375, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "computer-use-preview", + "name": "computer-use-preview", + "provider": "computer-use-preview", + "ctx": 8192, + "maxOut": 1024, + "pIn": 3, + "pOut": 12, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "deepseek-chat", + "name": "deepseek-chat", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 8192, + "pIn": 0.28, + "pOut": 0.42, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "deepseek-reasoner", + "name": "deepseek-reasoner", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 65536, + "pIn": 0.28, + "pOut": 0.42, + "tools": false, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "dashscope/qwen-coder", + "name": "dashscope/qwen-coder", + "provider": "dashscope", + "ctx": 1000000, + "maxOut": 16384, + "pIn": 0.3, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-flash", + "name": "dashscope/qwen-flash", + "provider": "dashscope", + "ctx": 997952, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-flash-2025-07-28", + "name": "dashscope/qwen-flash-2025-07-28", + "provider": "dashscope", + "ctx": 997952, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-max", + "name": "dashscope/qwen-max", + "provider": "dashscope", + "ctx": 30720, + "maxOut": 8192, + "pIn": 1.5999999999999999, + "pOut": 6.3999999999999995, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-plus", + "name": "dashscope/qwen-plus", + "provider": "dashscope", + "ctx": 129024, + "maxOut": 16384, + "pIn": 0.39999999999999997, + "pOut": 1.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-plus-2025-01-25", + "name": "dashscope/qwen-plus-2025-01-25", + "provider": "dashscope", + "ctx": 129024, + "maxOut": 8192, + "pIn": 0.39999999999999997, + "pOut": 1.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-plus-2025-04-28", + "name": "dashscope/qwen-plus-2025-04-28", + "provider": "dashscope", + "ctx": 129024, + "maxOut": 16384, + "pIn": 0.39999999999999997, + "pOut": 1.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-plus-2025-07-14", + "name": "dashscope/qwen-plus-2025-07-14", + "provider": "dashscope", + "ctx": 129024, + "maxOut": 16384, + "pIn": 0.39999999999999997, + "pOut": 1.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-plus-2025-07-28", + "name": "dashscope/qwen-plus-2025-07-28", + "provider": "dashscope", + "ctx": 997952, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-plus-2025-09-11", + "name": "dashscope/qwen-plus-2025-09-11", + "provider": "dashscope", + "ctx": 997952, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-plus-latest", + "name": "dashscope/qwen-plus-latest", + "provider": "dashscope", + "ctx": 997952, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-turbo", + "name": "dashscope/qwen-turbo", + "provider": "dashscope", + "ctx": 129024, + "maxOut": 16384, + "pIn": 0.049999999999999996, + "pOut": 0.19999999999999998, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-turbo-2024-11-01", + "name": "dashscope/qwen-turbo-2024-11-01", + "provider": "dashscope", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.049999999999999996, + "pOut": 0.19999999999999998, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-turbo-2025-04-28", + "name": "dashscope/qwen-turbo-2025-04-28", + "provider": "dashscope", + "ctx": 1000000, + "maxOut": 16384, + "pIn": 0.049999999999999996, + "pOut": 0.19999999999999998, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen-turbo-latest", + "name": "dashscope/qwen-turbo-latest", + "provider": "dashscope", + "ctx": 1000000, + "maxOut": 16384, + "pIn": 0.049999999999999996, + "pOut": 0.19999999999999998, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-30b-a3b", + "name": "dashscope/qwen3-30b-a3b", + "provider": "dashscope", + "ctx": 129024, + "maxOut": 16384, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-coder-flash", + "name": "dashscope/qwen3-coder-flash", + "provider": "dashscope", + "ctx": 997952, + "maxOut": 65536, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-coder-flash-2025-07-28", + "name": "dashscope/qwen3-coder-flash-2025-07-28", + "provider": "dashscope", + "ctx": 997952, + "maxOut": 65536, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-coder-plus", + "name": "dashscope/qwen3-coder-plus", + "provider": "dashscope", + "ctx": 997952, + "maxOut": 65536, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-coder-plus-2025-07-22", + "name": "dashscope/qwen3-coder-plus-2025-07-22", + "provider": "dashscope", + "ctx": 997952, + "maxOut": 65536, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-max-preview", + "name": "dashscope/qwen3-max-preview", + "provider": "dashscope", + "ctx": 258048, + "maxOut": 65536, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-max", + "name": "dashscope/qwen3-max", + "provider": "dashscope", + "ctx": 258048, + "maxOut": 65536, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-max-2026-01-23", + "name": "dashscope/qwen3-max-2026-01-23", + "provider": "dashscope", + "ctx": 258048, + "maxOut": 65536, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-next-80b-a3b-instruct", + "name": "dashscope/qwen3-next-80b-a3b-instruct", + "provider": "dashscope", + "ctx": 262144, + "maxOut": 65536, + "pIn": 0.15, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-next-80b-a3b-thinking", + "name": "dashscope/qwen3-next-80b-a3b-thinking", + "provider": "dashscope", + "ctx": 262144, + "maxOut": 65536, + "pIn": 0.15, + "pOut": 1.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-vl-235b-a22b-instruct", + "name": "dashscope/qwen3-vl-235b-a22b-instruct", + "provider": "dashscope", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.39999999999999997, + "pOut": 1.5999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-vl-235b-a22b-thinking", + "name": "dashscope/qwen3-vl-235b-a22b-thinking", + "provider": "dashscope", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.39999999999999997, + "pOut": 4, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-vl-32b-instruct", + "name": "dashscope/qwen3-vl-32b-instruct", + "provider": "dashscope", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.16, + "pOut": 0.64, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-vl-32b-thinking", + "name": "dashscope/qwen3-vl-32b-thinking", + "provider": "dashscope", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.16, + "pOut": 2.87, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3-vl-plus", + "name": "dashscope/qwen3-vl-plus", + "provider": "dashscope", + "ctx": 260096, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwen3.5-plus", + "name": "dashscope/qwen3.5-plus", + "provider": "dashscope", + "ctx": 991808, + "maxOut": 65536, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dashscope/qwq-plus", + "name": "dashscope/qwq-plus", + "provider": "dashscope", + "ctx": 98304, + "maxOut": 8192, + "pIn": 0.7999999999999999, + "pOut": 2.4, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-bge-large-en", + "name": "databricks/databricks-bge-large-en", + "provider": "databricks", + "ctx": 512, + "maxOut": null, + "pIn": 0.10003000000000001, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-claude-3-7-sonnet", + "name": "databricks/databricks-claude-3-7-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 128000, + "pIn": 2.9999900000000004, + "pOut": 15.000020000000001, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-claude-haiku-4-5", + "name": "databricks/databricks-claude-haiku-4-5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1.0000200000000001, + "pOut": 5.00003, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-claude-opus-4", + "name": "databricks/databricks-claude-opus-4", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15.000020000000001, + "pOut": 75.00003000000001, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-claude-opus-4-1", + "name": "databricks/databricks-claude-opus-4-1", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15.000020000000001, + "pOut": 75.00003000000001, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-claude-opus-4-5", + "name": "databricks/databricks-claude-opus-4-5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 5.00003, + "pOut": 25.000010000000003, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-claude-sonnet-4", + "name": "databricks/databricks-claude-sonnet-4", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 2.9999900000000004, + "pOut": 15.000020000000001, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-claude-sonnet-4-1", + "name": "databricks/databricks-claude-sonnet-4-1", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 2.9999900000000004, + "pOut": 15.000020000000001, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-claude-sonnet-4-5", + "name": "databricks/databricks-claude-sonnet-4-5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 2.9999900000000004, + "pOut": 15.000020000000001, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-gemini-2-5-flash", + "name": "databricks/databricks-gemini-2-5-flash", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.30001999999999995, + "pOut": 2.49998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-gemini-2-5-pro", + "name": "databricks/databricks-gemini-2-5-pro", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 1.24999, + "pOut": 9.999990000000002, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-gemma-3-12b", + "name": "databricks/databricks-gemma-3-12b", + "provider": "databricks", + "ctx": 128000, + "maxOut": 32000, + "pIn": 0.15000999999999998, + "pOut": 0.5000100000000001, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-gpt-5", + "name": "databricks/databricks-gpt-5", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.24999, + "pOut": 9.999990000000002, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-gpt-5-1", + "name": "databricks/databricks-gpt-5-1", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.24999, + "pOut": 9.999990000000002, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-gpt-5-mini", + "name": "databricks/databricks-gpt-5-mini", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.24997000000000005, + "pOut": 1.9999700000000002, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-gpt-5-nano", + "name": "databricks/databricks-gpt-5-nano", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.049980000000000004, + "pOut": 0.39998000000000006, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-gpt-oss-120b", + "name": "databricks/databricks-gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.15000999999999998, + "pOut": 0.59997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-gpt-oss-20b", + "name": "databricks/databricks-gpt-oss-20b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.07, + "pOut": 0.30001999999999995, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-gte-large-en", + "name": "databricks/databricks-gte-large-en", + "provider": "databricks", + "ctx": 8192, + "maxOut": null, + "pIn": 0.12999000000000002, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-llama-2-70b-chat", + "name": "databricks/databricks-llama-2-70b-chat", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.5000100000000001, + "pOut": 1.5000300000000002, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-llama-4-maverick", + "name": "databricks/databricks-llama-4-maverick", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.5000100000000001, + "pOut": 1.5000300000000002, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-meta-llama-3-1-405b-instruct", + "name": "databricks/databricks-meta-llama-3-1-405b-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 5.00003, + "pOut": 15.000020000000001, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-meta-llama-3-1-8b-instruct", + "name": "databricks/databricks-meta-llama-3-1-8b-instruct", + "provider": "meta", + "ctx": 200000, + "maxOut": 128000, + "pIn": 0.15000999999999998, + "pOut": 0.45003000000000004, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-meta-llama-3-3-70b-instruct", + "name": "databricks/databricks-meta-llama-3-3-70b-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.5000100000000001, + "pOut": 1.5000300000000002, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-meta-llama-3-70b-instruct", + "name": "databricks/databricks-meta-llama-3-70b-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 1.0000200000000001, + "pOut": 2.9999900000000004, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-mixtral-8x7b-instruct", + "name": "databricks/databricks-mixtral-8x7b-instruct", + "provider": "databricks", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.5000100000000001, + "pOut": 1.0000200000000001, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-mpt-30b-instruct", + "name": "databricks/databricks-mpt-30b-instruct", + "provider": "databricks", + "ctx": 8192, + "maxOut": 8192, + "pIn": 1.0000200000000001, + "pOut": 1.0000200000000001, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "databricks/databricks-mpt-7b-instruct", + "name": "databricks/databricks-mpt-7b-instruct", + "provider": "databricks", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.5000100000000001, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "davinci-002", + "name": "davinci-002", + "provider": "davinci-002", + "ctx": 16384, + "maxOut": 4096, + "pIn": 2, + "pOut": 2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Gryphe/MythoMax-L2-13b", + "name": "deepinfra/Gryphe/MythoMax-L2-13b", + "provider": "deepinfra", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.08, + "pOut": 0.09, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/NousResearch/Hermes-3-Llama-3.1-405B", + "name": "deepinfra/NousResearch/Hermes-3-Llama-3.1-405B", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 1, + "pOut": 1, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/NousResearch/Hermes-3-Llama-3.1-70B", + "name": "deepinfra/NousResearch/Hermes-3-Llama-3.1-70B", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.3, + "pOut": 0.3, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/QwQ-32B", + "name": "deepinfra/Qwen/QwQ-32B", + "provider": "deepinfra", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.15, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/Qwen2.5-72B-Instruct", + "name": "deepinfra/Qwen/Qwen2.5-72B-Instruct", + "provider": "deepinfra", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.12, + "pOut": 0.39, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/Qwen2.5-7B-Instruct", + "name": "deepinfra/Qwen/Qwen2.5-7B-Instruct", + "provider": "deepinfra", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.04, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/Qwen2.5-VL-32B-Instruct", + "name": "deepinfra/Qwen/Qwen2.5-VL-32B-Instruct", + "provider": "deepinfra", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/Qwen3-14B", + "name": "deepinfra/Qwen/Qwen3-14B", + "provider": "deepinfra", + "ctx": 40960, + "maxOut": 40960, + "pIn": 0.06, + "pOut": 0.24, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/Qwen3-235B-A22B", + "name": "deepinfra/Qwen/Qwen3-235B-A22B", + "provider": "deepinfra", + "ctx": 40960, + "maxOut": 40960, + "pIn": 0.18, + "pOut": 0.54, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "deepinfra/Qwen/Qwen3-235B-A22B-Instruct-2507", + "provider": "deepinfra", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.09, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/Qwen3-235B-A22B-Thinking-2507", + "name": "deepinfra/Qwen/Qwen3-235B-A22B-Thinking-2507", + "provider": "deepinfra", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.3, + "pOut": 2.9000000000000004, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/Qwen3-30B-A3B", + "name": "deepinfra/Qwen/Qwen3-30B-A3B", + "provider": "deepinfra", + "ctx": 40960, + "maxOut": 40960, + "pIn": 0.08, + "pOut": 0.29, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/Qwen3-32B", + "name": "deepinfra/Qwen/Qwen3-32B", + "provider": "deepinfra", + "ctx": 40960, + "maxOut": 40960, + "pIn": 0.09999999999999999, + "pOut": 0.28, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/Qwen3-Coder-480B-A35B-Instruct", + "name": "deepinfra/Qwen/Qwen3-Coder-480B-A35B-Instruct", + "provider": "deepinfra", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.39999999999999997, + "pOut": 1.5999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo", + "name": "deepinfra/Qwen/Qwen3-Coder-480B-A35B-Instruct-Turbo", + "provider": "deepinfra", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.29, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/Qwen3-Next-80B-A3B-Instruct", + "name": "deepinfra/Qwen/Qwen3-Next-80B-A3B-Instruct", + "provider": "deepinfra", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.14, + "pOut": 1.4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Qwen/Qwen3-Next-80B-A3B-Thinking", + "name": "deepinfra/Qwen/Qwen3-Next-80B-A3B-Thinking", + "provider": "deepinfra", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.14, + "pOut": 1.4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Sao10K/L3-8B-Lunaris-v1-Turbo", + "name": "deepinfra/Sao10K/L3-8B-Lunaris-v1-Turbo", + "provider": "deepinfra", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.04, + "pOut": 0.049999999999999996, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Sao10K/L3.1-70B-Euryale-v2.2", + "name": "deepinfra/Sao10K/L3.1-70B-Euryale-v2.2", + "provider": "deepinfra", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.65, + "pOut": 0.75, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/Sao10K/L3.3-70B-Euryale-v2.3", + "name": "deepinfra/Sao10K/L3.3-70B-Euryale-v2.3", + "provider": "deepinfra", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.65, + "pOut": 0.75, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/allenai/olmOCR-7B-0725-FP8", + "name": "deepinfra/allenai/olmOCR-7B-0725-FP8", + "provider": "deepinfra", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.27, + "pOut": 1.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/anthropic/claude-3-7-sonnet-latest", + "name": "deepinfra/anthropic/claude-3-7-sonnet-latest", + "provider": "claude", + "ctx": 200000, + "maxOut": 200000, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/anthropic/claude-4-opus", + "name": "deepinfra/anthropic/claude-4-opus", + "provider": "claude", + "ctx": 200000, + "maxOut": 200000, + "pIn": 16.5, + "pOut": 82.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/anthropic/claude-4-sonnet", + "name": "deepinfra/anthropic/claude-4-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 200000, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/deepseek-ai/DeepSeek-R1", + "name": "deepinfra/deepseek-ai/DeepSeek-R1", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.7, + "pOut": 2.4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/deepseek-ai/DeepSeek-R1-0528", + "name": "deepinfra/deepseek-ai/DeepSeek-R1-0528", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.5, + "pOut": 2.1500000000000004, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/deepseek-ai/DeepSeek-R1-0528-Turbo", + "name": "deepinfra/deepseek-ai/DeepSeek-R1-0528-Turbo", + "provider": "deepseek", + "ctx": 32768, + "maxOut": 32768, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/deepseek-ai/DeepSeek-R1-Distill-Llama-70B", + "name": "deepinfra/deepseek-ai/DeepSeek-R1-Distill-Llama-70B", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", + "name": "deepinfra/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.27, + "pOut": 0.27, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/deepseek-ai/DeepSeek-R1-Turbo", + "name": "deepinfra/deepseek-ai/DeepSeek-R1-Turbo", + "provider": "deepseek", + "ctx": 40960, + "maxOut": 40960, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/deepseek-ai/DeepSeek-V3", + "name": "deepinfra/deepseek-ai/DeepSeek-V3", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.38, + "pOut": 0.8899999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/deepseek-ai/DeepSeek-V3-0324", + "name": "deepinfra/deepseek-ai/DeepSeek-V3-0324", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.25, + "pOut": 0.88, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/deepseek-ai/DeepSeek-V3.1", + "name": "deepinfra/deepseek-ai/DeepSeek-V3.1", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.27, + "pOut": 1, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/deepseek-ai/DeepSeek-V3.1-Terminus", + "name": "deepinfra/deepseek-ai/DeepSeek-V3.1-Terminus", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.27, + "pOut": 1, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/google/gemini-2.0-flash-001", + "name": "deepinfra/google/gemini-2.0-flash-001", + "provider": "gemini", + "ctx": 1000000, + "maxOut": 1000000, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/google/gemini-2.5-flash", + "name": "deepinfra/google/gemini-2.5-flash", + "provider": "gemini", + "ctx": 1000000, + "maxOut": 1000000, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/google/gemini-2.5-pro", + "name": "deepinfra/google/gemini-2.5-pro", + "provider": "gemini", + "ctx": 1000000, + "maxOut": 1000000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/google/gemma-3-12b-it", + "name": "deepinfra/google/gemma-3-12b-it", + "provider": "gemini", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.049999999999999996, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/google/gemma-3-27b-it", + "name": "deepinfra/google/gemma-3-27b-it", + "provider": "gemini", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09, + "pOut": 0.16, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/google/gemma-3-4b-it", + "name": "deepinfra/google/gemma-3-4b-it", + "provider": "gemini", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.04, + "pOut": 0.08, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/meta-llama/Llama-3.2-11B-Vision-Instruct", + "name": "deepinfra/meta-llama/Llama-3.2-11B-Vision-Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.049, + "pOut": 0.049, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/meta-llama/Llama-3.2-3B-Instruct", + "name": "deepinfra/meta-llama/Llama-3.2-3B-Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.02, + "pOut": 0.02, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/meta-llama/Llama-3.3-70B-Instruct", + "name": "deepinfra/meta-llama/Llama-3.3-70B-Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.22999999999999998, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/meta-llama/Llama-3.3-70B-Instruct-Turbo", + "name": "deepinfra/meta-llama/Llama-3.3-70B-Instruct-Turbo", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.13, + "pOut": 0.39, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", + "name": "deepinfra/meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", + "provider": "meta", + "ctx": 1048576, + "maxOut": 1048576, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/meta-llama/Llama-4-Scout-17B-16E-Instruct", + "name": "deepinfra/meta-llama/Llama-4-Scout-17B-16E-Instruct", + "provider": "meta", + "ctx": 327680, + "maxOut": 327680, + "pIn": 0.08, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/meta-llama/Llama-Guard-3-8B", + "name": "deepinfra/meta-llama/Llama-Guard-3-8B", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.055, + "pOut": 0.055, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/meta-llama/Llama-Guard-4-12B", + "name": "deepinfra/meta-llama/Llama-Guard-4-12B", + "provider": "meta", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.18, + "pOut": 0.18, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/meta-llama/Meta-Llama-3-8B-Instruct", + "name": "deepinfra/meta-llama/Meta-Llama-3-8B-Instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.03, + "pOut": 0.06, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/meta-llama/Meta-Llama-3.1-70B-Instruct", + "name": "deepinfra/meta-llama/Meta-Llama-3.1-70B-Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.39999999999999997, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", + "name": "deepinfra/meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.28, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/meta-llama/Meta-Llama-3.1-8B-Instruct", + "name": "deepinfra/meta-llama/Meta-Llama-3.1-8B-Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.03, + "pOut": 0.049999999999999996, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo", + "name": "deepinfra/meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.02, + "pOut": 0.03, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/microsoft/WizardLM-2-8x22B", + "name": "deepinfra/microsoft/WizardLM-2-8x22B", + "provider": "deepinfra", + "ctx": 65536, + "maxOut": 65536, + "pIn": 0.48, + "pOut": 0.48, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/microsoft/phi-4", + "name": "deepinfra/microsoft/phi-4", + "provider": "deepinfra", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.07, + "pOut": 0.14, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/mistralai/Mistral-Nemo-Instruct-2407", + "name": "deepinfra/mistralai/Mistral-Nemo-Instruct-2407", + "provider": "mistral", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.02, + "pOut": 0.04, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/mistralai/Mistral-Small-24B-Instruct-2501", + "name": "deepinfra/mistralai/Mistral-Small-24B-Instruct-2501", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.049999999999999996, + "pOut": 0.08, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/mistralai/Mistral-Small-3.2-24B-Instruct-2506", + "name": "deepinfra/mistralai/Mistral-Small-3.2-24B-Instruct-2506", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.075, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/mistralai/Mixtral-8x7B-Instruct-v0.1", + "name": "deepinfra/mistralai/Mixtral-8x7B-Instruct-v0.1", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.39999999999999997, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/moonshotai/Kimi-K2-Instruct", + "name": "deepinfra/moonshotai/Kimi-K2-Instruct", + "provider": "deepinfra", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.5, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/moonshotai/Kimi-K2-Instruct-0905", + "name": "deepinfra/moonshotai/Kimi-K2-Instruct-0905", + "provider": "deepinfra", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.5, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/nvidia/Llama-3.1-Nemotron-70B-Instruct", + "name": "deepinfra/nvidia/Llama-3.1-Nemotron-70B-Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.6, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/nvidia/Llama-3.3-Nemotron-Super-49B-v1.5", + "name": "deepinfra/nvidia/Llama-3.3-Nemotron-Super-49B-v1.5", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/nvidia/NVIDIA-Nemotron-Nano-9B-v2", + "name": "deepinfra/nvidia/NVIDIA-Nemotron-Nano-9B-v2", + "provider": "deepinfra", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.04, + "pOut": 0.16, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/openai/gpt-oss-120b", + "name": "deepinfra/openai/gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.049999999999999996, + "pOut": 0.44999999999999996, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/openai/gpt-oss-20b", + "name": "deepinfra/openai/gpt-oss-20b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.04, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepinfra/zai-org/GLM-4.5", + "name": "deepinfra/zai-org/GLM-4.5", + "provider": "deepinfra", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.39999999999999997, + "pOut": 1.5999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepseek/deepseek-coder", + "name": "deepseek/deepseek-coder", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.14, + "pOut": 0.28, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepseek/deepseek-reasoner", + "name": "deepseek/deepseek-reasoner", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 65536, + "pIn": 0.28, + "pOut": 0.42, + "tools": false, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "deepseek/deepseek-v3", + "name": "deepseek/deepseek-v3", + "provider": "deepseek", + "ctx": 65536, + "maxOut": 8192, + "pIn": 0.27, + "pOut": 1.1, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepseek.v3-v1:0", + "name": "deepseek.v3-v1:0", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 81920, + "pIn": 0.58, + "pOut": 1.68, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "deepseek.v3.2", + "name": "deepseek.v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.62, + "pOut": 1.85, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "dolphin", + "name": "dolphin", + "provider": "dolphin", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.5, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "deepseek-v3-2-251201", + "name": "deepseek-v3-2-251201", + "provider": "deepseek", + "ctx": 98304, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "glm-4-7-251222", + "name": "glm-4-7-251222", + "provider": "glm-4-7-251222", + "ctx": 204800, + "maxOut": 131072, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "kimi-k2-thinking-251104", + "name": "kimi-k2-thinking-251104", + "provider": "kimi-k2-thinking-251104", + "ctx": 229376, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "doubao-embedding", + "name": "doubao-embedding", + "provider": "doubao-embedding", + "ctx": 4096, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "doubao-embedding-large", + "name": "doubao-embedding-large", + "provider": "doubao-embedding-large", + "ctx": 4096, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "doubao-embedding-large-text-240915", + "name": "doubao-embedding-large-text-240915", + "provider": "doubao-embedding-large-text-240915", + "ctx": 4096, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "doubao-embedding-large-text-250515", + "name": "doubao-embedding-large-text-250515", + "provider": "doubao-embedding-large-text-250515", + "ctx": 4096, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "doubao-embedding-text-240715", + "name": "doubao-embedding-text-240715", + "provider": "doubao-embedding-text-240715", + "ctx": 4096, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "embed-english-light-v2.0", + "name": "embed-english-light-v2.0", + "provider": "embed-english-light-v2.0", + "ctx": 1024, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "embed-english-light-v3.0", + "name": "embed-english-light-v3.0", + "provider": "embed-english-light-v3.0", + "ctx": 1024, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "embed-english-v2.0", + "name": "embed-english-v2.0", + "provider": "embed-english-v2.0", + "ctx": 4096, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "embed-english-v3.0", + "name": "embed-english-v3.0", + "provider": "embed-english-v3.0", + "ctx": 1024, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "embed-multilingual-v2.0", + "name": "embed-multilingual-v2.0", + "provider": "embed-multilingual-v2.0", + "ctx": 768, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "embed-multilingual-v3.0", + "name": "embed-multilingual-v3.0", + "provider": "embed-multilingual-v3.0", + "ctx": 1024, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "embed-multilingual-light-v3.0", + "name": "embed-multilingual-light-v3.0", + "provider": "embed-multilingual-light-v3.0", + "ctx": 1024, + "maxOut": null, + "pIn": 100, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "eu.amazon.nova-lite-v1:0", + "name": "eu.amazon.nova-lite-v1:0", + "provider": "eu.amazon.nova-lite-v1:0", + "ctx": 300000, + "maxOut": 10000, + "pIn": 0.078, + "pOut": 0.312, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "eu.amazon.nova-micro-v1:0", + "name": "eu.amazon.nova-micro-v1:0", + "provider": "eu.amazon.nova-micro-v1:0", + "ctx": 128000, + "maxOut": 10000, + "pIn": 0.046, + "pOut": 0.184, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "eu.amazon.nova-pro-v1:0", + "name": "eu.amazon.nova-pro-v1:0", + "provider": "eu.amazon.nova-pro-v1:0", + "ctx": 300000, + "maxOut": 10000, + "pIn": 1.0499999999999998, + "pOut": 4.199999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-3-5-haiku-20241022-v1:0", + "name": "eu.anthropic.claude-3-5-haiku-20241022-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 0.25, + "pOut": 1.25, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "eu.anthropic.claude-haiku-4-5-20251001-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1.1, + "pOut": 5.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-3-5-sonnet-20240620-v1:0", + "name": "eu.anthropic.claude-3-5-sonnet-20240620-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-3-5-sonnet-20241022-v2:0", + "name": "eu.anthropic.claude-3-5-sonnet-20241022-v2:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-3-7-sonnet-20250219-v1:0", + "name": "eu.anthropic.claude-3-7-sonnet-20250219-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-3-haiku-20240307-v1:0", + "name": "eu.anthropic.claude-3-haiku-20240307-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 0.25, + "pOut": 1.25, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-3-opus-20240229-v1:0", + "name": "eu.anthropic.claude-3-opus-20240229-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-3-sonnet-20240229-v1:0", + "name": "eu.anthropic.claude-3-sonnet-20240229-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-opus-4-1-20250805-v1:0", + "name": "eu.anthropic.claude-opus-4-1-20250805-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-opus-4-20250514-v1:0", + "name": "eu.anthropic.claude-opus-4-20250514-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-sonnet-4-20250514-v1:0", + "name": "eu.anthropic.claude-sonnet-4-20250514-v1:0", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "eu.anthropic.claude-sonnet-4-5-20250929-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "eu.meta.llama3-2-1b-instruct-v1:0", + "name": "eu.meta.llama3-2-1b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.13, + "pOut": 0.13, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "eu.meta.llama3-2-3b-instruct-v1:0", + "name": "eu.meta.llama3-2-3b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.19, + "pOut": 0.19, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "eu.mistral.pixtral-large-2502-v1:0", + "name": "eu.mistral.pixtral-large-2502-v1:0", + "provider": "mistral", + "ctx": 128000, + "maxOut": 4096, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "featherless_ai/featherless-ai/Qwerky-72B", + "name": "featherless_ai/featherless-ai/Qwerky-72B", + "provider": "featherless_ai", + "ctx": 32768, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "featherless_ai/featherless-ai/Qwerky-QwQ-32B", + "name": "featherless_ai/featherless-ai/Qwerky-QwQ-32B", + "provider": "featherless_ai", + "ctx": 32768, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/WhereIsAI/UAE-Large-V1", + "name": "fireworks_ai/WhereIsAI/UAE-Large-V1", + "provider": "fireworks_ai", + "ctx": 512, + "maxOut": null, + "pIn": 0.016, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-coder-v2-instruct", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-coder-v2-instruct", + "provider": "deepseek", + "ctx": 65536, + "maxOut": 65536, + "pIn": 1.2, + "pOut": 1.2, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-r1", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-r1", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 20480, + "pIn": 3, + "pOut": 8, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-r1-0528", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-r1-0528", + "provider": "deepseek", + "ctx": 160000, + "maxOut": 160000, + "pIn": 3, + "pOut": 8, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-r1-basic", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-r1-basic", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 20480, + "pIn": 0.55, + "pOut": 2.1900000000000004, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-v3", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-v3", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-v3-0324", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-v3-0324", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-v3p1", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-v3p1", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.56, + "pOut": 1.68, + "tools": null, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-v3p1-terminus", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-v3p1-terminus", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.56, + "pOut": 1.68, + "tools": null, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-v3p2", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-v3p2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.56, + "pOut": 1.68, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/firefunction-v2", + "name": "fireworks_ai/accounts/fireworks/models/firefunction-v2", + "provider": "fireworks_ai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/glm-4p5", + "name": "fireworks_ai/accounts/fireworks/models/glm-4p5", + "provider": "fireworks_ai", + "ctx": 128000, + "maxOut": 96000, + "pIn": 0.55, + "pOut": 2.1900000000000004, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/glm-4p5-air", + "name": "fireworks_ai/accounts/fireworks/models/glm-4p5-air", + "provider": "fireworks_ai", + "ctx": 128000, + "maxOut": 96000, + "pIn": 0.22, + "pOut": 0.88, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/glm-4p6", + "name": "fireworks_ai/accounts/fireworks/models/glm-4p6", + "provider": "fireworks_ai", + "ctx": 202800, + "maxOut": 202800, + "pIn": 0.55, + "pOut": 2.1900000000000004, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/glm-4p7", + "name": "fireworks_ai/accounts/fireworks/models/glm-4p7", + "provider": "fireworks_ai", + "ctx": 202800, + "maxOut": 202800, + "pIn": 0.6, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/glm-5p1", + "name": "fireworks_ai/accounts/fireworks/models/glm-5p1", + "provider": "fireworks_ai", + "ctx": 202800, + "maxOut": 202800, + "pIn": 1.4, + "pOut": 4.4, + "tools": false, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/gpt-oss-120b", + "name": "fireworks_ai/accounts/fireworks/models/gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/gpt-oss-20b", + "name": "fireworks_ai/accounts/fireworks/models/gpt-oss-20b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.049999999999999996, + "pOut": 0.19999999999999998, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/kimi-k2-instruct", + "name": "fireworks_ai/accounts/fireworks/models/kimi-k2-instruct", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/kimi-k2-instruct-0905", + "name": "fireworks_ai/accounts/fireworks/models/kimi-k2-instruct-0905", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 32768, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/kimi-k2-thinking", + "name": "fireworks_ai/accounts/fireworks/models/kimi-k2-thinking", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/kimi-k2p5", + "name": "fireworks_ai/accounts/fireworks/models/kimi-k2p5", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3p1-405b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3p1-405b-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 16384, + "pIn": 3, + "pOut": 3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3p1-8b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3p1-8b-instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3p2-11b-vision-instruct", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3p2-11b-vision-instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3p2-1b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3p2-1b-instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3p2-3b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3p2-3b-instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3p2-90b-vision-instruct", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3p2-90b-vision-instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama4-maverick-instruct-basic", + "name": "fireworks_ai/accounts/fireworks/models/llama4-maverick-instruct-basic", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.22, + "pOut": 0.88, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama4-scout-instruct-basic", + "name": "fireworks_ai/accounts/fireworks/models/llama4-scout-instruct-basic", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.15, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/minimax-m2p1", + "name": "fireworks_ai/accounts/fireworks/models/minimax-m2p1", + "provider": "fireworks_ai", + "ctx": 204800, + "maxOut": 204800, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mixtral-8x22b-instruct-hf", + "name": "fireworks_ai/accounts/fireworks/models/mixtral-8x22b-instruct-hf", + "provider": "fireworks_ai", + "ctx": 65536, + "maxOut": 65536, + "pIn": 1.2, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2-72b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2-72b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/yi-large", + "name": "fireworks_ai/accounts/fireworks/models/yi-large", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 3, + "pOut": 3, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/glm-4p7", + "name": "fireworks_ai/glm-4p7", + "provider": "fireworks_ai", + "ctx": 202800, + "maxOut": 202800, + "pIn": 0.6, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/glm-5p1", + "name": "fireworks_ai/glm-5p1", + "provider": "fireworks_ai", + "ctx": 202800, + "maxOut": 202800, + "pIn": 1.4, + "pOut": 4.4, + "tools": false, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "fireworks_ai/kimi-k2p5", + "name": "fireworks_ai/kimi-k2p5", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/minimax-m2p1", + "name": "fireworks_ai/minimax-m2p1", + "provider": "fireworks_ai", + "ctx": 204800, + "maxOut": 204800, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "fireworks_ai/nomic-ai/nomic-embed-text-v1", + "name": "fireworks_ai/nomic-ai/nomic-embed-text-v1", + "provider": "fireworks_ai", + "ctx": 8192, + "maxOut": null, + "pIn": 0.008, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/nomic-ai/nomic-embed-text-v1.5", + "name": "fireworks_ai/nomic-ai/nomic-embed-text-v1.5", + "provider": "fireworks_ai", + "ctx": 8192, + "maxOut": null, + "pIn": 0.008, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/thenlper/gte-base", + "name": "fireworks_ai/thenlper/gte-base", + "provider": "fireworks_ai", + "ctx": 512, + "maxOut": null, + "pIn": 0.008, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/thenlper/gte-large", + "name": "fireworks_ai/thenlper/gte-large", + "provider": "fireworks_ai", + "ctx": 512, + "maxOut": null, + "pIn": 0.016, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "friendliai/meta-llama-3.1-70b-instruct", + "name": "friendliai/meta-llama-3.1-70b-instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "friendliai/meta-llama-3.1-8b-instruct", + "name": "friendliai/meta-llama-3.1-8b-instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ft:babbage-002", + "name": "ft:babbage-002", + "provider": "ft:babbage-002", + "ctx": 16384, + "maxOut": 4096, + "pIn": 1.5999999999999999, + "pOut": 1.5999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ft:davinci-002", + "name": "ft:davinci-002", + "provider": "ft:davinci-002", + "ctx": 16384, + "maxOut": 4096, + "pIn": 12, + "pOut": 12, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ft:gpt-3.5-turbo", + "name": "ft:gpt-3.5-turbo", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 3, + "pOut": 6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ft:gpt-3.5-turbo-0125", + "name": "ft:gpt-3.5-turbo-0125", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 3, + "pOut": 6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ft:gpt-3.5-turbo-0613", + "name": "ft:gpt-3.5-turbo-0613", + "provider": "openai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 3, + "pOut": 6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ft:gpt-3.5-turbo-1106", + "name": "ft:gpt-3.5-turbo-1106", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 3, + "pOut": 6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ft:gpt-4-0613", + "name": "ft:gpt-4-0613", + "provider": "openai", + "ctx": 8192, + "maxOut": 4096, + "pIn": 30, + "pOut": 60, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ft:gpt-4o-2024-08-06", + "name": "ft:gpt-4o-2024-08-06", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 3.75, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ft:gpt-4o-2024-11-20", + "name": "ft:gpt-4o-2024-11-20", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 3.75, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ft:gpt-4o-mini-2024-07-18", + "name": "ft:gpt-4o-mini-2024-07-18", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ft:gpt-4.1-2025-04-14", + "name": "ft:gpt-4.1-2025-04-14", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 3, + "pOut": 12, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ft:gpt-4.1-mini-2025-04-14", + "name": "ft:gpt-4.1-mini-2025-04-14", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.7999999999999999, + "pOut": 3.1999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ft:gpt-4.1-nano-2025-04-14", + "name": "ft:gpt-4.1-nano-2025-04-14", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.7999999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ft:o4-mini-2025-04-16", + "name": "ft:o4-mini-2025-04-16", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 4, + "pOut": 16, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-2.0-flash", + "name": "gemini-2.0-flash", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-2.0-flash-001", + "name": "gemini-2.0-flash-001", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-2.5-flash", + "name": "gemini-2.5-flash", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-2.5-flash-image", + "name": "gemini-2.5-flash-image", + "provider": "gemini", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-3-pro-image-preview", + "name": "gemini-3-pro-image-preview", + "provider": "gemini", + "ctx": 65536, + "maxOut": 32768, + "pIn": 2, + "pOut": 12, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-3.1-flash-image-preview", + "name": "gemini-3.1-flash-image-preview", + "provider": "gemini", + "ctx": 65536, + "maxOut": 32768, + "pIn": 0.5, + "pOut": 3, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-3.1-flash-lite-preview", + "name": "gemini-3.1-flash-lite-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.25, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-3.1-flash-lite", + "name": "gemini-3.1-flash-lite", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.25, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "deep-research-pro-preview-12-2025", + "name": "deep-research-pro-preview-12-2025", + "provider": "deep-research-pro-preview-12-2025", + "ctx": 65536, + "maxOut": 32768, + "pIn": 2, + "pOut": 12, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-2.5-flash-lite", + "name": "gemini-2.5-flash-lite", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-2.5-flash-lite-preview-09-2025", + "name": "gemini-2.5-flash-lite-preview-09-2025", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-2.5-flash-preview-09-2025", + "name": "gemini-2.5-flash-preview-09-2025", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-live-2.5-flash-preview-native-audio-09-2025", + "name": "gemini-live-2.5-flash-preview-native-audio-09-2025", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.3, + "pOut": 2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-live-2.5-flash-preview-native-audio-09-2025", + "name": "gemini/gemini-live-2.5-flash-preview-native-audio-09-2025", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.3, + "pOut": 2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-2.5-flash-lite-preview-06-17", + "name": "gemini-2.5-flash-lite-preview-06-17", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-2.5-pro", + "name": "gemini-2.5-pro", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-3-pro-preview", + "name": "gemini-3-pro-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-3.1-pro-preview", + "name": "gemini-3.1-pro-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-3.1-pro-preview-customtools", + "name": "gemini-3.1-pro-preview-customtools", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/gemini-3-pro-preview", + "name": "vertex_ai/gemini-3-pro-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/gemini-3-flash-preview", + "name": "vertex_ai/gemini-3-flash-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.5, + "pOut": 3, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/gemini-3.5-flash", + "name": "vertex_ai/gemini-3.5-flash", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 1.5, + "pOut": 9, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/gemini-3.1-pro-preview", + "name": "vertex_ai/gemini-3.1-pro-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/gemini-3.1-pro-preview-customtools", + "name": "vertex_ai/gemini-3.1-pro-preview-customtools", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-2.5-pro-preview-tts", + "name": "gemini-2.5-pro-preview-tts", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-robotics-er-1.5-preview", + "name": "gemini-robotics-er-1.5-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-robotics-er-1.5-preview", + "name": "gemini/gemini-robotics-er-1.5-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-2.5-computer-use-preview-10-2025", + "name": "gemini-2.5-computer-use-preview-10-2025", + "provider": "gemini", + "ctx": 128000, + "maxOut": 64000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini-embedding-001", + "name": "gemini-embedding-001", + "provider": "gemini", + "ctx": 2048, + "maxOut": null, + "pIn": 0.15, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini-embedding-2-preview", + "name": "gemini-embedding-2-preview", + "provider": "gemini", + "ctx": 8192, + "maxOut": null, + "pIn": 0.19999999999999998, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini-embedding-2", + "name": "gemini-embedding-2", + "provider": "gemini", + "ctx": 8192, + "maxOut": null, + "pIn": 0.19999999999999998, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/gemini-embedding-2-preview", + "name": "vertex_ai/gemini-embedding-2-preview", + "provider": "gemini", + "ctx": 8192, + "maxOut": null, + "pIn": 0.19999999999999998, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/gemini-embedding-2", + "name": "vertex_ai/gemini-embedding-2", + "provider": "gemini", + "ctx": 8192, + "maxOut": null, + "pIn": 0.19999999999999998, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini-flash-experimental", + "name": "gemini-flash-experimental", + "provider": "gemini", + "ctx": 1000000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/gemini-embedding-001", + "name": "gemini/gemini-embedding-001", + "provider": "gemini", + "ctx": 2048, + "maxOut": null, + "pIn": 0.15, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/gemini-embedding-2-preview", + "name": "gemini/gemini-embedding-2-preview", + "provider": "gemini", + "ctx": 8192, + "maxOut": null, + "pIn": 0.19999999999999998, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/gemini-embedding-2", + "name": "gemini/gemini-embedding-2", + "provider": "gemini", + "ctx": 8192, + "maxOut": null, + "pIn": 0.19999999999999998, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/gemini-1.5-flash", + "name": "gemini/gemini-1.5-flash", + "provider": "gemini", + "ctx": 8192, + "maxOut": null, + "pIn": 0.075, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.0-flash", + "name": "gemini/gemini-2.0-flash", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.0-flash-001", + "name": "gemini/gemini-2.0-flash-001", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.5-flash", + "name": "gemini/gemini-2.5-flash", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.5-flash-image", + "name": "gemini/gemini-2.5-flash-image", + "provider": "gemini", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": false, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-3-pro-image-preview", + "name": "gemini/gemini-3-pro-image-preview", + "provider": "gemini", + "ctx": 65536, + "maxOut": 32768, + "pIn": 2, + "pOut": 12, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-3.1-flash-image-preview", + "name": "gemini/gemini-3.1-flash-image-preview", + "provider": "gemini", + "ctx": 65536, + "maxOut": 32768, + "pIn": 0.25, + "pOut": 1.5, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/deep-research-pro-preview-12-2025", + "name": "gemini/deep-research-pro-preview-12-2025", + "provider": "gemini", + "ctx": 65536, + "maxOut": 32768, + "pIn": 2, + "pOut": 12, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.5-flash-lite", + "name": "gemini/gemini-2.5-flash-lite", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.5-flash-lite-preview-09-2025", + "name": "gemini/gemini-2.5-flash-lite-preview-09-2025", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.5-flash-preview-09-2025", + "name": "gemini/gemini-2.5-flash-preview-09-2025", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-flash-latest", + "name": "gemini/gemini-flash-latest", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-flash-lite-latest", + "name": "gemini/gemini-flash-lite-latest", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.5-flash-lite-preview-06-17", + "name": "gemini/gemini-2.5-flash-lite-preview-06-17", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.5-pro", + "name": "gemini/gemini-2.5-pro", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.5-computer-use-preview-10-2025", + "name": "gemini/gemini-2.5-computer-use-preview-10-2025", + "provider": "gemini", + "ctx": 128000, + "maxOut": 64000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/gemini-3-pro-preview", + "name": "gemini/gemini-3-pro-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-3.1-flash-lite-preview", + "name": "gemini/gemini-3.1-flash-lite-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.25, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-3.1-flash-lite", + "name": "gemini/gemini-3.1-flash-lite", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.25, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-3-flash-preview", + "name": "gemini/gemini-3-flash-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.5, + "pOut": 3, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-3.5-flash", + "name": "gemini/gemini-3.5-flash", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 1.5, + "pOut": 9, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-3.1-pro-preview", + "name": "gemini/gemini-3.1-pro-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-3.1-pro-preview-customtools", + "name": "gemini/gemini-3.1-pro-preview-customtools", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-3-flash-preview", + "name": "gemini-3-flash-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.5, + "pOut": 3, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-3.5-flash", + "name": "gemini-3.5-flash", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 1.5, + "pOut": 9, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.5-pro-preview-tts", + "name": "gemini/gemini-2.5-pro-preview-tts", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-exp-1114", + "name": "gemini/gemini-exp-1114", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-exp-1206", + "name": "gemini/gemini-exp-1206", + "provider": "gemini", + "ctx": 2097152, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-gemma-2-27b-it", + "name": "gemini/gemini-gemma-2-27b-it", + "provider": "gemini", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.35, + "pOut": 1.0499999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/gemini-gemma-2-9b-it", + "name": "gemini/gemini-gemma-2-9b-it", + "provider": "gemini", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.35, + "pOut": 1.0499999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/gemma-3-27b-it", + "name": "gemini/gemma-3-27b-it", + "provider": "gemini", + "ctx": 131072, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/learnlm-1.5-pro-experimental", + "name": "gemini/learnlm-1.5-pro-experimental", + "provider": "gemini", + "ctx": 32767, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/lyria-3-clip-preview", + "name": "gemini/lyria-3-clip-preview", + "provider": "gemini", + "ctx": 131072, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": false, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "gemini/lyria-3-pro-preview", + "name": "gemini/lyria-3-pro-preview", + "provider": "gemini", + "ctx": 131072, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": false, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "gemini/veo-2.0-generate-001", + "name": "gemini/veo-2.0-generate-001", + "provider": "gemini", + "ctx": 1024, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/veo-3.1-fast-generate-preview", + "name": "gemini/veo-3.1-fast-generate-preview", + "provider": "gemini", + "ctx": 1024, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/veo-3.1-generate-preview", + "name": "gemini/veo-3.1-generate-preview", + "provider": "gemini", + "ctx": 1024, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/veo-3.1-lite-generate-preview", + "name": "gemini/veo-3.1-lite-generate-preview", + "provider": "gemini", + "ctx": 1024, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/veo-3.1-fast-generate-001", + "name": "gemini/veo-3.1-fast-generate-001", + "provider": "gemini", + "ctx": 1024, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/veo-3.1-generate-001", + "name": "gemini/veo-3.1-generate-001", + "provider": "gemini", + "ctx": 1024, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/claude-haiku-4.5", + "name": "github_copilot/claude-haiku-4.5", + "provider": "claude", + "ctx": 128000, + "maxOut": 16000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/claude-opus-4.5", + "name": "github_copilot/claude-opus-4.5", + "provider": "claude", + "ctx": 128000, + "maxOut": 16000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/claude-opus-4.6-fast", + "name": "github_copilot/claude-opus-4.6-fast", + "provider": "claude", + "ctx": 128000, + "maxOut": 16000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/claude-opus-41", + "name": "github_copilot/claude-opus-41", + "provider": "claude", + "ctx": 80000, + "maxOut": 16000, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/claude-sonnet-4", + "name": "github_copilot/claude-sonnet-4", + "provider": "claude", + "ctx": 128000, + "maxOut": 16000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/claude-sonnet-4.5", + "name": "github_copilot/claude-sonnet-4.5", + "provider": "claude", + "ctx": 128000, + "maxOut": 16000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gemini-2.5-pro", + "name": "github_copilot/gemini-2.5-pro", + "provider": "gemini", + "ctx": 128000, + "maxOut": 64000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gemini-3-pro-preview", + "name": "github_copilot/gemini-3-pro-preview", + "provider": "gemini", + "ctx": 128000, + "maxOut": 64000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-3.5-turbo", + "name": "github_copilot/gpt-3.5-turbo", + "provider": "openai", + "ctx": 16384, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-3.5-turbo-0613", + "name": "github_copilot/gpt-3.5-turbo-0613", + "provider": "openai", + "ctx": 16384, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-4", + "name": "github_copilot/gpt-4", + "provider": "openai", + "ctx": 32768, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-4-0613", + "name": "github_copilot/gpt-4-0613", + "provider": "openai", + "ctx": 32768, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-4-o-preview", + "name": "github_copilot/gpt-4-o-preview", + "provider": "openai", + "ctx": 64000, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-4.1", + "name": "github_copilot/gpt-4.1", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-4.1-2025-04-14", + "name": "github_copilot/gpt-4.1-2025-04-14", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-4o", + "name": "github_copilot/gpt-4o", + "provider": "openai", + "ctx": 64000, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-4o-2024-05-13", + "name": "github_copilot/gpt-4o-2024-05-13", + "provider": "openai", + "ctx": 64000, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-4o-2024-08-06", + "name": "github_copilot/gpt-4o-2024-08-06", + "provider": "openai", + "ctx": 64000, + "maxOut": 16384, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-4o-2024-11-20", + "name": "github_copilot/gpt-4o-2024-11-20", + "provider": "openai", + "ctx": 64000, + "maxOut": 16384, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-4o-mini", + "name": "github_copilot/gpt-4o-mini", + "provider": "openai", + "ctx": 64000, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-4o-mini-2024-07-18", + "name": "github_copilot/gpt-4o-mini-2024-07-18", + "provider": "openai", + "ctx": 64000, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-5", + "name": "github_copilot/gpt-5", + "provider": "openai", + "ctx": 128000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-5-mini", + "name": "github_copilot/gpt-5-mini", + "provider": "openai", + "ctx": 128000, + "maxOut": 64000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-5.1", + "name": "github_copilot/gpt-5.1", + "provider": "openai", + "ctx": 128000, + "maxOut": 64000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-5.1-codex-max", + "name": "github_copilot/gpt-5.1-codex-max", + "provider": "openai", + "ctx": 128000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-5.2", + "name": "github_copilot/gpt-5.2", + "provider": "openai", + "ctx": 128000, + "maxOut": 64000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "github_copilot/gpt-5.3-codex", + "name": "github_copilot/gpt-5.3-codex", + "provider": "openai", + "ctx": 128000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "github_copilot/text-embedding-3-small", + "name": "github_copilot/text-embedding-3-small", + "provider": "github_copilot", + "ctx": 8191, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/text-embedding-3-small-inference", + "name": "github_copilot/text-embedding-3-small-inference", + "provider": "github_copilot", + "ctx": 8191, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "github_copilot/text-embedding-ada-002", + "name": "github_copilot/text-embedding-ada-002", + "provider": "github_copilot", + "ctx": 8191, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "chatgpt/gpt-5.4", + "name": "chatgpt/gpt-5.4", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "chatgpt/gpt-5.4-pro", + "name": "chatgpt/gpt-5.4-pro", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "chatgpt/gpt-5.3-codex", + "name": "chatgpt/gpt-5.3-codex", + "provider": "openai", + "ctx": 128000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "chatgpt/gpt-5.3-codex-spark", + "name": "chatgpt/gpt-5.3-codex-spark", + "provider": "openai", + "ctx": 128000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "chatgpt/gpt-5.3-instant", + "name": "chatgpt/gpt-5.3-instant", + "provider": "openai", + "ctx": 128000, + "maxOut": 64000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "chatgpt/gpt-5.3-chat-latest", + "name": "chatgpt/gpt-5.3-chat-latest", + "provider": "openai", + "ctx": 128000, + "maxOut": 64000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "chatgpt/gpt-5.2-codex", + "name": "chatgpt/gpt-5.2-codex", + "provider": "openai", + "ctx": 128000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "chatgpt/gpt-5.2", + "name": "chatgpt/gpt-5.2", + "provider": "openai", + "ctx": 128000, + "maxOut": 64000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "chatgpt/gpt-5.1-codex-max", + "name": "chatgpt/gpt-5.1-codex-max", + "provider": "openai", + "ctx": 128000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "chatgpt/gpt-5.1-codex-mini", + "name": "chatgpt/gpt-5.1-codex-mini", + "provider": "openai", + "ctx": 128000, + "maxOut": 64000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gigachat/GigaChat-2-Lite", + "name": "gigachat/GigaChat-2-Lite", + "provider": "gigachat", + "ctx": 128000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gigachat/GigaChat-2-Max", + "name": "gigachat/GigaChat-2-Max", + "provider": "gigachat", + "ctx": 128000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gigachat/GigaChat-2-Pro", + "name": "gigachat/GigaChat-2-Pro", + "provider": "gigachat", + "ctx": 128000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gigachat/Embeddings", + "name": "gigachat/Embeddings", + "provider": "gigachat", + "ctx": 512, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gigachat/Embeddings-2", + "name": "gigachat/Embeddings-2", + "provider": "gigachat", + "ctx": 512, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gigachat/EmbeddingsGigaR", + "name": "gigachat/EmbeddingsGigaR", + "provider": "gigachat", + "ctx": 4096, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/anthropic/claude-opus-4.5", + "name": "gmi/anthropic/claude-opus-4.5", + "provider": "claude", + "ctx": 409600, + "maxOut": 32000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/anthropic/claude-sonnet-4.5", + "name": "gmi/anthropic/claude-sonnet-4.5", + "provider": "claude", + "ctx": 409600, + "maxOut": 32000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/anthropic/claude-sonnet-4", + "name": "gmi/anthropic/claude-sonnet-4", + "provider": "claude", + "ctx": 409600, + "maxOut": 32000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/anthropic/claude-opus-4", + "name": "gmi/anthropic/claude-opus-4", + "provider": "claude", + "ctx": 409600, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/openai/gpt-5.2", + "name": "gmi/openai/gpt-5.2", + "provider": "openai", + "ctx": 409600, + "maxOut": 32000, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/openai/gpt-5.1", + "name": "gmi/openai/gpt-5.1", + "provider": "openai", + "ctx": 409600, + "maxOut": 32000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/openai/gpt-5", + "name": "gmi/openai/gpt-5", + "provider": "openai", + "ctx": 409600, + "maxOut": 32000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/openai/gpt-4o", + "name": "gmi/openai/gpt-4o", + "provider": "openai", + "ctx": 131072, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/openai/gpt-4o-mini", + "name": "gmi/openai/gpt-4o-mini", + "provider": "openai", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/deepseek-ai/DeepSeek-V3.2", + "name": "gmi/deepseek-ai/DeepSeek-V3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 16384, + "pIn": 0.28, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/deepseek-ai/DeepSeek-V3-0324", + "name": "gmi/deepseek-ai/DeepSeek-V3-0324", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 16384, + "pIn": 0.28, + "pOut": 0.88, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/google/gemini-3-pro-preview", + "name": "gmi/google/gemini-3-pro-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/google/gemini-3-flash-preview", + "name": "gmi/google/gemini-3-flash-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.5, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/moonshotai/Kimi-K2-Thinking", + "name": "gmi/moonshotai/Kimi-K2-Thinking", + "provider": "gmi", + "ctx": 262144, + "maxOut": 16384, + "pIn": 0.7999999999999999, + "pOut": 1.2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/MiniMaxAI/MiniMax-M2.1", + "name": "gmi/MiniMaxAI/MiniMax-M2.1", + "provider": "gmi", + "ctx": 196608, + "maxOut": 16384, + "pIn": 0.3, + "pOut": 1.2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/Qwen/Qwen3-VL-235B-A22B-Instruct-FP8", + "name": "gmi/Qwen/Qwen3-VL-235B-A22B-Instruct-FP8", + "provider": "gmi", + "ctx": 262144, + "maxOut": 16384, + "pIn": 0.3, + "pOut": 1.4, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gmi/zai-org/GLM-4.7-FP8", + "name": "gmi/zai-org/GLM-4.7-FP8", + "provider": "gmi", + "ctx": 202752, + "maxOut": 16384, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "google.gemma-3-12b-it", + "name": "google.gemma-3-12b-it", + "provider": "gemini", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.09, + "pOut": 0.29, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "google.gemma-3-27b-it", + "name": "google.gemma-3-27b-it", + "provider": "gemini", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.22999999999999998, + "pOut": 0.38, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "google.gemma-3-4b-it", + "name": "google.gemma-3-4b-it", + "provider": "gemini", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.04, + "pOut": 0.08, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "global.anthropic.claude-sonnet-4-5-20250929-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "global.anthropic.claude-sonnet-4-20250514-v1:0", + "name": "global.anthropic.claude-sonnet-4-20250514-v1:0", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "global.anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "global.anthropic.claude-haiku-4-5-20251001-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "global.amazon.nova-2-lite-v1:0", + "name": "global.amazon.nova-2-lite-v1:0", + "provider": "global.amazon.nova-2-lite-v1:0", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-3.5-turbo", + "name": "gpt-3.5-turbo", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-3.5-turbo-0125", + "name": "gpt-3.5-turbo-0125", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-3.5-turbo-1106", + "name": "gpt-3.5-turbo-1106", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 1, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-3.5-turbo-16k", + "name": "gpt-3.5-turbo-16k", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 3, + "pOut": 4, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-3.5-turbo-instruct", + "name": "gpt-3.5-turbo-instruct", + "provider": "openai", + "ctx": 8192, + "maxOut": 4096, + "pIn": 1.5, + "pOut": 2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-3.5-turbo-instruct-0914", + "name": "gpt-3.5-turbo-instruct-0914", + "provider": "openai", + "ctx": 8192, + "maxOut": 4097, + "pIn": 1.5, + "pOut": 2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4", + "name": "gpt-4", + "provider": "openai", + "ctx": 8192, + "maxOut": 4096, + "pIn": 30, + "pOut": 60, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4-0125-preview", + "name": "gpt-4-0125-preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4-0314", + "name": "gpt-4-0314", + "provider": "openai", + "ctx": 8192, + "maxOut": 4096, + "pIn": 30, + "pOut": 60, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4-0613", + "name": "gpt-4-0613", + "provider": "openai", + "ctx": 8192, + "maxOut": 4096, + "pIn": 30, + "pOut": 60, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4-1106-preview", + "name": "gpt-4-1106-preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4-turbo", + "name": "gpt-4-turbo", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4-turbo-2024-04-09", + "name": "gpt-4-turbo-2024-04-09", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4-turbo-preview", + "name": "gpt-4-turbo-preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4.1", + "name": "gpt-4.1", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4.1-2025-04-14", + "name": "gpt-4.1-2025-04-14", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4.1-mini", + "name": "gpt-4.1-mini", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.39999999999999997, + "pOut": 1.5999999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4.1-mini-2025-04-14", + "name": "gpt-4.1-mini-2025-04-14", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.39999999999999997, + "pOut": 1.5999999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4.1-nano", + "name": "gpt-4.1-nano", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4.1-nano-2025-04-14", + "name": "gpt-4.1-nano-2025-04-14", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4o", + "name": "gpt-4o", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4o-2024-05-13", + "name": "gpt-4o-2024-05-13", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4o-2024-08-06", + "name": "gpt-4o-2024-08-06", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4o-2024-11-20", + "name": "gpt-4o-2024-11-20", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4o-audio-preview", + "name": "gpt-4o-audio-preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4o-audio-preview-2024-12-17", + "name": "gpt-4o-audio-preview-2024-12-17", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4o-audio-preview-2025-06-03", + "name": "gpt-4o-audio-preview-2025-06-03", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-audio", + "name": "gpt-audio", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": false, + "json": false, + "src": "litellm" + }, + { + "id": "gpt-audio-1.5", + "name": "gpt-audio-1.5", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": false, + "json": false, + "src": "litellm" + }, + { + "id": "gpt-audio-2025-08-28", + "name": "gpt-audio-2025-08-28", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": false, + "json": false, + "src": "litellm" + }, + { + "id": "gpt-audio-mini", + "name": "gpt-audio-mini", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.6, + "pOut": 2.4, + "tools": true, + "cot": false, + "json": false, + "src": "litellm" + }, + { + "id": "gpt-audio-mini-2025-10-06", + "name": "gpt-audio-mini-2025-10-06", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.6, + "pOut": 2.4, + "tools": true, + "cot": false, + "json": false, + "src": "litellm" + }, + { + "id": "gpt-audio-mini-2025-12-15", + "name": "gpt-audio-mini-2025-12-15", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.6, + "pOut": 2.4, + "tools": true, + "cot": false, + "json": false, + "src": "litellm" + }, + { + "id": "gpt-4o-mini", + "name": "gpt-4o-mini", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4o-mini-2024-07-18", + "name": "gpt-4o-mini-2024-07-18", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4o-mini-audio-preview", + "name": "gpt-4o-mini-audio-preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4o-mini-audio-preview-2024-12-17", + "name": "gpt-4o-mini-audio-preview-2024-12-17", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4o-mini-realtime-preview", + "name": "gpt-4o-mini-realtime-preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.6, + "pOut": 2.4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4o-mini-realtime-preview-2024-12-17", + "name": "gpt-4o-mini-realtime-preview-2024-12-17", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.6, + "pOut": 2.4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4o-mini-search-preview", + "name": "gpt-4o-mini-search-preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4o-mini-search-preview-2025-03-11", + "name": "gpt-4o-mini-search-preview-2025-03-11", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4o-realtime-preview", + "name": "gpt-4o-realtime-preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5, + "pOut": 20, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4o-realtime-preview-2024-12-17", + "name": "gpt-4o-realtime-preview-2024-12-17", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5, + "pOut": 20, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4o-realtime-preview-2025-06-03", + "name": "gpt-4o-realtime-preview-2025-06-03", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5, + "pOut": 20, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-4o-search-preview", + "name": "gpt-4o-search-preview", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-4o-search-preview-2025-03-11", + "name": "gpt-4o-search-preview-2025-03-11", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5", + "name": "gpt-5", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.1", + "name": "gpt-5.1", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.1-2025-11-13", + "name": "gpt-5.1-2025-11-13", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.1-chat-latest", + "name": "gpt-5.1-chat-latest", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.25, + "pOut": 10, + "tools": false, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.2", + "name": "gpt-5.2", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.2-2025-12-11", + "name": "gpt-5.2-2025-12-11", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.2-chat-latest", + "name": "gpt-5.2-chat-latest", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.3-chat-latest", + "name": "gpt-5.3-chat-latest", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.2-pro", + "name": "gpt-5.2-pro", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 21, + "pOut": 168, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.2-pro-2025-12-11", + "name": "gpt-5.2-pro-2025-12-11", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 21, + "pOut": 168, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.5", + "name": "gpt-5.5", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 5, + "pOut": 30, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.5-2026-04-23", + "name": "gpt-5.5-2026-04-23", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 5, + "pOut": 30, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.5-pro", + "name": "gpt-5.5-pro", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 30, + "pOut": 180, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "gpt-5.5-pro-2026-04-23", + "name": "gpt-5.5-pro-2026-04-23", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 30, + "pOut": 180, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "gpt-5.4", + "name": "gpt-5.4", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 2.5, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.4-2026-03-05", + "name": "gpt-5.4-2026-03-05", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 2.5, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.4-pro", + "name": "gpt-5.4-pro", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 30, + "pOut": 180, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "gpt-5.4-pro-2026-03-05", + "name": "gpt-5.4-pro-2026-03-05", + "provider": "openai", + "ctx": 1050000, + "maxOut": 128000, + "pIn": 30, + "pOut": 180, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "gpt-5.4-mini", + "name": "gpt-5.4-mini", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.75, + "pOut": 4.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.4-mini-2026-03-17", + "name": "gpt-5.4-mini-2026-03-17", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.75, + "pOut": 4.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.4-nano", + "name": "gpt-5.4-nano", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 1.25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.4-nano-2026-03-17", + "name": "gpt-5.4-nano-2026-03-17", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 1.25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5-pro", + "name": "gpt-5-pro", + "provider": "openai", + "ctx": 128000, + "maxOut": 272000, + "pIn": 15, + "pOut": 120, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5-pro-2025-10-06", + "name": "gpt-5-pro-2025-10-06", + "provider": "openai", + "ctx": 128000, + "maxOut": 272000, + "pIn": 15, + "pOut": 120, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5-2025-08-07", + "name": "gpt-5-2025-08-07", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5-chat", + "name": "gpt-5-chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.25, + "pOut": 10, + "tools": false, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5-chat-latest", + "name": "gpt-5-chat-latest", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.25, + "pOut": 10, + "tools": false, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5-codex", + "name": "gpt-5-codex", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.1-codex", + "name": "gpt-5.1-codex", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.1-codex-max", + "name": "gpt-5.1-codex-max", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.1-codex-mini", + "name": "gpt-5.1-codex-mini", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.2-codex", + "name": "gpt-5.2-codex", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5.3-codex", + "name": "gpt-5.3-codex", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5-mini", + "name": "gpt-5-mini", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5-mini-2025-08-07", + "name": "gpt-5-mini-2025-08-07", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5-nano", + "name": "gpt-5-nano", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.049999999999999996, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5-nano-2025-08-07", + "name": "gpt-5-nano-2025-08-07", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.049999999999999996, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-realtime", + "name": "gpt-realtime", + "provider": "openai", + "ctx": 32000, + "maxOut": 4096, + "pIn": 4, + "pOut": 16, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-realtime-1.5", + "name": "gpt-realtime-1.5", + "provider": "openai", + "ctx": 32000, + "maxOut": 4096, + "pIn": 4, + "pOut": 16, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-realtime-2", + "name": "gpt-realtime-2", + "provider": "openai", + "ctx": 32000, + "maxOut": 4096, + "pIn": 4, + "pOut": 16, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-realtime-mini", + "name": "gpt-realtime-mini", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.6, + "pOut": 2.4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-realtime-2025-08-28", + "name": "gpt-realtime-2025-08-28", + "provider": "openai", + "ctx": 32000, + "maxOut": 4096, + "pIn": 4, + "pOut": 16, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gradient_ai/alibaba-qwen3-32b", + "name": "gradient_ai/alibaba-qwen3-32b", + "provider": "gradient_ai", + "ctx": 131072, + "maxOut": 40960, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gradient_ai/anthropic-claude-3-opus", + "name": "gradient_ai/anthropic-claude-3-opus", + "provider": "claude", + "ctx": 200000, + "maxOut": 1024, + "pIn": 15, + "pOut": 75, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gradient_ai/anthropic-claude-3.5-haiku", + "name": "gradient_ai/anthropic-claude-3.5-haiku", + "provider": "claude", + "ctx": 200000, + "maxOut": 1024, + "pIn": 0.7999999999999999, + "pOut": 4, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gradient_ai/anthropic-claude-3.5-sonnet", + "name": "gradient_ai/anthropic-claude-3.5-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 1024, + "pIn": 3, + "pOut": 15, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gradient_ai/anthropic-claude-3.7-sonnet", + "name": "gradient_ai/anthropic-claude-3.7-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 1024, + "pIn": 3, + "pOut": 15, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gradient_ai/deepseek-r1-distill-llama-70b", + "name": "gradient_ai/deepseek-r1-distill-llama-70b", + "provider": "deepseek", + "ctx": 32768, + "maxOut": 8000, + "pIn": 0.9900000000000001, + "pOut": 0.9900000000000001, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gradient_ai/llama3-8b-instruct", + "name": "gradient_ai/llama3-8b-instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 512, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gradient_ai/llama3.3-70b-instruct", + "name": "gradient_ai/llama3.3-70b-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": 0.65, + "pOut": 0.65, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gradient_ai/mistral-nemo-instruct-2407", + "name": "gradient_ai/mistral-nemo-instruct-2407", + "provider": "mistral", + "ctx": 128000, + "maxOut": 512, + "pIn": 0.3, + "pOut": 0.3, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gradient_ai/openai-gpt-4o", + "name": "gradient_ai/openai-gpt-4o", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gradient_ai/openai-gpt-4o-mini", + "name": "gradient_ai/openai-gpt-4o-mini", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gradient_ai/openai-o3", + "name": "gradient_ai/openai-o3", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 2, + "pOut": 8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gradient_ai/openai-o3-mini", + "name": "gradient_ai/openai-o3-mini", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lemonade/Qwen3-Coder-30B-A3B-Instruct-GGUF", + "name": "lemonade/Qwen3-Coder-30B-A3B-Instruct-GGUF", + "provider": "lemonade", + "ctx": 262144, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "lemonade/gpt-oss-20b-mxfp4-GGUF", + "name": "lemonade/gpt-oss-20b-mxfp4-GGUF", + "provider": "openai", + "ctx": 131072, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "lemonade/gpt-oss-120b-mxfp-GGUF", + "name": "lemonade/gpt-oss-120b-mxfp-GGUF", + "provider": "openai", + "ctx": 131072, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "lemonade/Gemma-3-4b-it-GGUF", + "name": "lemonade/Gemma-3-4b-it-GGUF", + "provider": "lemonade", + "ctx": 128000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "lemonade/Qwen3-4B-Instruct-2507-GGUF", + "name": "lemonade/Qwen3-4B-Instruct-2507-GGUF", + "provider": "lemonade", + "ctx": 262144, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "amazon-nova/nova-micro-v1", + "name": "amazon-nova/nova-micro-v1", + "provider": "amazon-nova", + "ctx": 128000, + "maxOut": 10000, + "pIn": 0.035, + "pOut": 0.14, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "amazon-nova/nova-lite-v1", + "name": "amazon-nova/nova-lite-v1", + "provider": "amazon-nova", + "ctx": 300000, + "maxOut": 10000, + "pIn": 0.06, + "pOut": 0.24, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "amazon-nova/nova-premier-v1", + "name": "amazon-nova/nova-premier-v1", + "provider": "amazon-nova", + "ctx": 1000000, + "maxOut": 10000, + "pIn": 2.5, + "pOut": 12.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "amazon-nova/nova-pro-v1", + "name": "amazon-nova/nova-pro-v1", + "provider": "amazon-nova", + "ctx": 300000, + "maxOut": 10000, + "pIn": 0.7999999999999999, + "pOut": 3.1999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "groq/llama-3.1-8b-instant", + "name": "groq/llama-3.1-8b-instant", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.049999999999999996, + "pOut": 0.08, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "groq/llama-3.3-70b-versatile", + "name": "groq/llama-3.3-70b-versatile", + "provider": "meta", + "ctx": 128000, + "maxOut": 32768, + "pIn": 0.59, + "pOut": 0.7899999999999999, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "groq/gemma-7b-it", + "name": "groq/gemma-7b-it", + "provider": "groq", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.049999999999999996, + "pOut": 0.08, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "groq/meta-llama/llama-guard-4-12b", + "name": "groq/meta-llama/llama-guard-4-12b", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "groq/meta-llama/llama-4-maverick-17b-128e-instruct", + "name": "groq/meta-llama/llama-4-maverick-17b-128e-instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "groq/meta-llama/llama-4-scout-17b-16e-instruct", + "name": "groq/meta-llama/llama-4-scout-17b-16e-instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 8192, + "pIn": 0.11, + "pOut": 0.33999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "groq/moonshotai/kimi-k2-instruct-0905", + "name": "groq/moonshotai/kimi-k2-instruct-0905", + "provider": "groq", + "ctx": 262144, + "maxOut": 16384, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "groq/openai/gpt-oss-120b", + "name": "groq/openai/gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 32766, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "groq/openai/gpt-oss-20b", + "name": "groq/openai/gpt-oss-20b", + "provider": "openai", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.075, + "pOut": 0.3, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "groq/openai/gpt-oss-safeguard-20b", + "name": "groq/openai/gpt-oss-safeguard-20b", + "provider": "openai", + "ctx": 131072, + "maxOut": 65536, + "pIn": 0.075, + "pOut": 0.3, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "groq/playai-tts", + "name": "groq/playai-tts", + "provider": "groq", + "ctx": 10000, + "maxOut": 10000, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "groq/qwen/qwen3-32b", + "name": "groq/qwen/qwen3-32b", + "provider": "groq", + "ctx": 131000, + "maxOut": 131000, + "pIn": 0.29, + "pOut": 0.59, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "heroku/claude-3-5-haiku", + "name": "heroku/claude-3-5-haiku", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "heroku/claude-3-5-sonnet-latest", + "name": "heroku/claude-3-5-sonnet-latest", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "heroku/claude-3-7-sonnet", + "name": "heroku/claude-3-7-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "heroku/claude-4-sonnet", + "name": "heroku/claude-4-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/NousResearch/Hermes-3-Llama-3.1-70B", + "name": "hyperbolic/NousResearch/Hermes-3-Llama-3.1-70B", + "provider": "meta", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.12, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/Qwen/QwQ-32B", + "name": "hyperbolic/Qwen/QwQ-32B", + "provider": "hyperbolic", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/Qwen/Qwen2.5-72B-Instruct", + "name": "hyperbolic/Qwen/Qwen2.5-72B-Instruct", + "provider": "hyperbolic", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.12, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/Qwen/Qwen2.5-Coder-32B-Instruct", + "name": "hyperbolic/Qwen/Qwen2.5-Coder-32B-Instruct", + "provider": "hyperbolic", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.12, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/Qwen/Qwen3-235B-A22B", + "name": "hyperbolic/Qwen/Qwen3-235B-A22B", + "provider": "hyperbolic", + "ctx": 131072, + "maxOut": 131072, + "pIn": 2, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/deepseek-ai/DeepSeek-R1", + "name": "hyperbolic/deepseek-ai/DeepSeek-R1", + "provider": "deepseek", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.39999999999999997, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/deepseek-ai/DeepSeek-R1-0528", + "name": "hyperbolic/deepseek-ai/DeepSeek-R1-0528", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.25, + "pOut": 0.25, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/deepseek-ai/DeepSeek-V3", + "name": "hyperbolic/deepseek-ai/DeepSeek-V3", + "provider": "deepseek", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/deepseek-ai/DeepSeek-V3-0324", + "name": "hyperbolic/deepseek-ai/DeepSeek-V3-0324", + "provider": "deepseek", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.39999999999999997, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/meta-llama/Llama-3.2-3B-Instruct", + "name": "hyperbolic/meta-llama/Llama-3.2-3B-Instruct", + "provider": "meta", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.12, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/meta-llama/Llama-3.3-70B-Instruct", + "name": "hyperbolic/meta-llama/Llama-3.3-70B-Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.12, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/meta-llama/Meta-Llama-3-70B-Instruct", + "name": "hyperbolic/meta-llama/Meta-Llama-3-70B-Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.12, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/meta-llama/Meta-Llama-3.1-405B-Instruct", + "name": "hyperbolic/meta-llama/Meta-Llama-3.1-405B-Instruct", + "provider": "meta", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.12, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/meta-llama/Meta-Llama-3.1-70B-Instruct", + "name": "hyperbolic/meta-llama/Meta-Llama-3.1-70B-Instruct", + "provider": "meta", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.12, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/meta-llama/Meta-Llama-3.1-8B-Instruct", + "name": "hyperbolic/meta-llama/Meta-Llama-3.1-8B-Instruct", + "provider": "meta", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.12, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "hyperbolic/moonshotai/Kimi-K2-Instruct", + "name": "hyperbolic/moonshotai/Kimi-K2-Instruct", + "provider": "hyperbolic", + "ctx": 131072, + "maxOut": 131072, + "pIn": 2, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "j2-light", + "name": "j2-light", + "provider": "j2-light", + "ctx": 8192, + "maxOut": 8192, + "pIn": 3, + "pOut": 3, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "j2-mid", + "name": "j2-mid", + "provider": "j2-mid", + "ctx": 8192, + "maxOut": 8192, + "pIn": 10, + "pOut": 10, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "j2-ultra", + "name": "j2-ultra", + "provider": "j2-ultra", + "ctx": 8192, + "maxOut": 8192, + "pIn": 15, + "pOut": 15, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "jamba-1.5", + "name": "jamba-1.5", + "provider": "jamba-1.5", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 0.39999999999999997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "jamba-1.5-large", + "name": "jamba-1.5-large", + "provider": "jamba-1.5-large", + "ctx": 256000, + "maxOut": 256000, + "pIn": 2, + "pOut": 8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "jamba-1.5-large@001", + "name": "jamba-1.5-large@001", + "provider": "jamba-1.5-large@001", + "ctx": 256000, + "maxOut": 256000, + "pIn": 2, + "pOut": 8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "jamba-1.5-mini", + "name": "jamba-1.5-mini", + "provider": "jamba-1.5-mini", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 0.39999999999999997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "jamba-1.5-mini@001", + "name": "jamba-1.5-mini@001", + "provider": "jamba-1.5-mini@001", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 0.39999999999999997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "jamba-large-1.6", + "name": "jamba-large-1.6", + "provider": "jamba-large-1.6", + "ctx": 256000, + "maxOut": 256000, + "pIn": 2, + "pOut": 8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "jamba-large-1.7", + "name": "jamba-large-1.7", + "provider": "jamba-large-1.7", + "ctx": 256000, + "maxOut": 256000, + "pIn": 2, + "pOut": 8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "jamba-mini-1.6", + "name": "jamba-mini-1.6", + "provider": "jamba-mini-1.6", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 0.39999999999999997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "jamba-mini-1.7", + "name": "jamba-mini-1.7", + "provider": "jamba-mini-1.7", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 0.39999999999999997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "jina-reranker-v2-base-multilingual", + "name": "jina-reranker-v2-base-multilingual", + "provider": "jina-reranker-v2-base-multilingual", + "ctx": 1024, + "maxOut": 1024, + "pIn": 0.018, + "pOut": 0.018, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "jp.anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "jp.anthropic.claude-sonnet-4-5-20250929-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "jp.anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "jp.anthropic.claude-haiku-4-5-20251001-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1.1, + "pOut": 5.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "crusoe/deepseek-ai/DeepSeek-R1-0528", + "name": "crusoe/deepseek-ai/DeepSeek-R1-0528", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 3, + "pOut": 7, + "tools": false, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "crusoe/deepseek-ai/DeepSeek-V3-0324", + "name": "crusoe/deepseek-ai/DeepSeek-V3-0324", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 1.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "crusoe/google/gemma-3-12b-it", + "name": "crusoe/google/gemma-3-12b-it", + "provider": "gemini", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "crusoe/meta-llama/Llama-3.3-70B-Instruct", + "name": "crusoe/meta-llama/Llama-3.3-70B-Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "crusoe/moonshotai/Kimi-K2-Thinking", + "name": "crusoe/moonshotai/Kimi-K2-Thinking", + "provider": "crusoe", + "ctx": 262144, + "maxOut": 262144, + "pIn": 2.5, + "pOut": 2.5, + "tools": false, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "crusoe/openai/gpt-oss-120b", + "name": "crusoe/openai/gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.7999999999999999, + "pOut": 0.7999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "crusoe/Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "crusoe/Qwen/Qwen3-235B-A22B-Instruct-2507", + "provider": "crusoe", + "ctx": 262144, + "maxOut": 262144, + "pIn": 3, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/deepseek-llama3.3-70b", + "name": "lambda_ai/deepseek-llama3.3-70b", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/deepseek-r1-0528", + "name": "lambda_ai/deepseek-r1-0528", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/deepseek-r1-671b", + "name": "lambda_ai/deepseek-r1-671b", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.7999999999999999, + "pOut": 0.7999999999999999, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/deepseek-v3-0324", + "name": "lambda_ai/deepseek-v3-0324", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/hermes3-405b", + "name": "lambda_ai/hermes3-405b", + "provider": "lambda_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.7999999999999999, + "pOut": 0.7999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/hermes3-70b", + "name": "lambda_ai/hermes3-70b", + "provider": "lambda_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.12, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/hermes3-8b", + "name": "lambda_ai/hermes3-8b", + "provider": "lambda_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.024999999999999998, + "pOut": 0.04, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/lfm-40b", + "name": "lambda_ai/lfm-40b", + "provider": "lambda_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/lfm-7b", + "name": "lambda_ai/lfm-7b", + "provider": "lambda_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.024999999999999998, + "pOut": 0.04, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/llama-4-maverick-17b-128e-instruct-fp8", + "name": "lambda_ai/llama-4-maverick-17b-128e-instruct-fp8", + "provider": "meta", + "ctx": 131072, + "maxOut": 8192, + "pIn": 0.049999999999999996, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/llama-4-scout-17b-16e-instruct", + "name": "lambda_ai/llama-4-scout-17b-16e-instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 8192, + "pIn": 0.049999999999999996, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/llama3.1-405b-instruct-fp8", + "name": "lambda_ai/llama3.1-405b-instruct-fp8", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.7999999999999999, + "pOut": 0.7999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/llama3.1-70b-instruct-fp8", + "name": "lambda_ai/llama3.1-70b-instruct-fp8", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.12, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/llama3.1-8b-instruct", + "name": "lambda_ai/llama3.1-8b-instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.024999999999999998, + "pOut": 0.04, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/llama3.1-nemotron-70b-instruct-fp8", + "name": "lambda_ai/llama3.1-nemotron-70b-instruct-fp8", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.12, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/llama3.2-11b-vision-instruct", + "name": "lambda_ai/llama3.2-11b-vision-instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.015, + "pOut": 0.024999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/llama3.2-3b-instruct", + "name": "lambda_ai/llama3.2-3b-instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.015, + "pOut": 0.024999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/llama3.3-70b-instruct-fp8", + "name": "lambda_ai/llama3.3-70b-instruct-fp8", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.12, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/qwen25-coder-32b-instruct", + "name": "lambda_ai/qwen25-coder-32b-instruct", + "provider": "lambda_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.049999999999999996, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "lambda_ai/qwen3-32b-fp8", + "name": "lambda_ai/qwen3-32b-fp8", + "provider": "lambda_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.049999999999999996, + "pOut": 0.09999999999999999, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "max-x-max/50-steps/stability.stable-diffusion-xl-v0", + "name": "max-x-max/50-steps/stability.stable-diffusion-xl-v0", + "provider": "max-x-max", + "ctx": 77, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "max-x-max/max-steps/stability.stable-diffusion-xl-v0", + "name": "max-x-max/max-steps/stability.stable-diffusion-xl-v0", + "provider": "max-x-max", + "ctx": 77, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "medlm-large", + "name": "medlm-large", + "provider": "medlm-large", + "ctx": 8192, + "maxOut": 1024, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "medlm-medium", + "name": "medlm-medium", + "provider": "medlm-medium", + "ctx": 32768, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama2-13b-chat-v1", + "name": "meta.llama2-13b-chat-v1", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.75, + "pOut": 1, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama2-70b-chat-v1", + "name": "meta.llama2-70b-chat-v1", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 1.95, + "pOut": 2.56, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama3-1-405b-instruct-v1:0", + "name": "meta.llama3-1-405b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5.32, + "pOut": 16, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama3-1-70b-instruct-v1:0", + "name": "meta.llama3-1-70b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": 0.9900000000000001, + "pOut": 0.9900000000000001, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama3-1-8b-instruct-v1:0", + "name": "meta.llama3-1-8b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": 0.22, + "pOut": 0.22, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama3-2-11b-instruct-v1:0", + "name": "meta.llama3-2-11b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.35, + "pOut": 0.35, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama3-2-1b-instruct-v1:0", + "name": "meta.llama3-2-1b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama3-2-3b-instruct-v1:0", + "name": "meta.llama3-2-3b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama3-2-90b-instruct-v1:0", + "name": "meta.llama3-2-90b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 2, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama3-3-70b-instruct-v1:0", + "name": "meta.llama3-3-70b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.72, + "pOut": 0.72, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama3-70b-instruct-v1:0", + "name": "meta.llama3-70b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 2.65, + "pOut": 3.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama3-8b-instruct-v1:0", + "name": "meta.llama3-8b-instruct-v1:0", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama4-maverick-17b-instruct-v1:0", + "name": "meta.llama4-maverick-17b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.24, + "pOut": 0.9700000000000001, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta.llama4-scout-17b-instruct-v1:0", + "name": "meta.llama4-scout-17b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.16999999999999998, + "pOut": 0.66, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta_llama/Llama-3.3-70B-Instruct", + "name": "meta_llama/Llama-3.3-70B-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 4028, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta_llama/Llama-3.3-8B-Instruct", + "name": "meta_llama/Llama-3.3-8B-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 4028, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta_llama/Llama-4-Maverick-17B-128E-Instruct-FP8", + "name": "meta_llama/Llama-4-Maverick-17B-128E-Instruct-FP8", + "provider": "meta", + "ctx": 1000000, + "maxOut": 4028, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "meta_llama/Llama-4-Scout-17B-16E-Instruct-FP8", + "name": "meta_llama/Llama-4-Scout-17B-16E-Instruct-FP8", + "provider": "meta", + "ctx": 10000000, + "maxOut": 4028, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "minimax.minimax-m2", + "name": "minimax.minimax-m2", + "provider": "minimax.minimax-m2", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 1.2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "minimax.minimax-m2.1", + "name": "minimax.minimax-m2.1", + "provider": "minimax.minimax-m2.1", + "ctx": 196000, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "minimax.minimax-m2.5", + "name": "minimax.minimax-m2.5", + "provider": "minimax.minimax-m2.5", + "ctx": 1000000, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral.devstral-2-123b", + "name": "mistral.devstral-2-123b", + "provider": "mistral", + "ctx": 256000, + "maxOut": 8192, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral.magistral-small-2509", + "name": "mistral.magistral-small-2509", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "mistral.ministral-3-14b-instruct", + "name": "mistral.ministral-3-14b-instruct", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral.ministral-3-3b-instruct", + "name": "mistral.ministral-3-3b-instruct", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral.ministral-3-8b-instruct", + "name": "mistral.ministral-3-8b-instruct", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral.mistral-7b-instruct-v0:2", + "name": "mistral.mistral-7b-instruct-v0:2", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 0.15, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral.mistral-large-2402-v1:0", + "name": "mistral.mistral-large-2402-v1:0", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral.mistral-large-2407-v1:0", + "name": "mistral.mistral-large-2407-v1:0", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8191, + "pIn": 3, + "pOut": 9, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral.mistral-large-3-675b-instruct", + "name": "mistral.mistral-large-3-675b-instruct", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral.mistral-small-2402-v1:0", + "name": "mistral.mistral-small-2402-v1:0", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral.mixtral-8x7b-instruct-v0:1", + "name": "mistral.mixtral-8x7b-instruct-v0:1", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 0.44999999999999996, + "pOut": 0.7, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral.voxtral-mini-3b-2507", + "name": "mistral.voxtral-mini-3b-2507", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.04, + "pOut": 0.04, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral.voxtral-small-24b-2507", + "name": "mistral.voxtral-small-24b-2507", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral/codestral-2405", + "name": "mistral/codestral-2405", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 1, + "pOut": 3, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/codestral-2508", + "name": "mistral/codestral-2508", + "provider": "mistral", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.3, + "pOut": 0.8999999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/codestral-latest", + "name": "mistral/codestral-latest", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 1, + "pOut": 3, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/codestral-mamba-latest", + "name": "mistral/codestral-mamba-latest", + "provider": "mistral", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.25, + "pOut": 0.25, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral/devstral-medium-2507", + "name": "mistral/devstral-medium-2507", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/devstral-small-2505", + "name": "mistral/devstral-small-2505", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/devstral-small-2507", + "name": "mistral/devstral-small-2507", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/devstral-small-latest", + "name": "mistral/devstral-small-latest", + "provider": "mistral", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/labs-devstral-small-2512", + "name": "mistral/labs-devstral-small-2512", + "provider": "mistral", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/devstral-latest", + "name": "mistral/devstral-latest", + "provider": "mistral", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/devstral-medium-latest", + "name": "mistral/devstral-medium-latest", + "provider": "mistral", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/devstral-2512", + "name": "mistral/devstral-2512", + "provider": "mistral", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/magistral-medium-2506", + "name": "mistral/magistral-medium-2506", + "provider": "mistral", + "ctx": 40000, + "maxOut": 40000, + "pIn": 2, + "pOut": 5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/magistral-medium-2509", + "name": "mistral/magistral-medium-2509", + "provider": "mistral", + "ctx": 40000, + "maxOut": 40000, + "pIn": 2, + "pOut": 5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/magistral-medium-1-2-2509", + "name": "mistral/magistral-medium-1-2-2509", + "provider": "mistral", + "ctx": 40000, + "maxOut": 40000, + "pIn": 2, + "pOut": 5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/magistral-medium-latest", + "name": "mistral/magistral-medium-latest", + "provider": "mistral", + "ctx": 40000, + "maxOut": 40000, + "pIn": 2, + "pOut": 5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/magistral-small-2506", + "name": "mistral/magistral-small-2506", + "provider": "mistral", + "ctx": 40000, + "maxOut": 40000, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/magistral-small-latest", + "name": "mistral/magistral-small-latest", + "provider": "mistral", + "ctx": 40000, + "maxOut": 40000, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/magistral-small-1-2-2509", + "name": "mistral/magistral-small-1-2-2509", + "provider": "mistral", + "ctx": 40000, + "maxOut": 40000, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-embed", + "name": "mistral/mistral-embed", + "provider": "mistral", + "ctx": 8192, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral/codestral-embed", + "name": "mistral/codestral-embed", + "provider": "mistral", + "ctx": 8192, + "maxOut": null, + "pIn": 0.15, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral/codestral-embed-2505", + "name": "mistral/codestral-embed-2505", + "provider": "mistral", + "ctx": 8192, + "maxOut": null, + "pIn": 0.15, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral/mistral-large-2402", + "name": "mistral/mistral-large-2402", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 4, + "pOut": 12, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-large-2407", + "name": "mistral/mistral-large-2407", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 3, + "pOut": 9, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-large-2411", + "name": "mistral/mistral-large-2411", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-large-latest", + "name": "mistral/mistral-large-latest", + "provider": "mistral", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-large-3", + "name": "mistral/mistral-large-3", + "provider": "mistral", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-large-2512", + "name": "mistral/mistral-large-2512", + "provider": "mistral", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-medium", + "name": "mistral/mistral-medium", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 2.7, + "pOut": 8.1, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-medium-2312", + "name": "mistral/mistral-medium-2312", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 2.7, + "pOut": 8.1, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-medium-2505", + "name": "mistral/mistral-medium-2505", + "provider": "mistral", + "ctx": 131072, + "maxOut": 8191, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-medium-latest", + "name": "mistral/mistral-medium-latest", + "provider": "mistral", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-medium-3-1-2508", + "name": "mistral/mistral-medium-3-1-2508", + "provider": "mistral", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-small", + "name": "mistral/mistral-small", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-small-latest", + "name": "mistral/mistral-small-latest", + "provider": "mistral", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.06, + "pOut": 0.18, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-small-3-2-2506", + "name": "mistral/mistral-small-3-2-2506", + "provider": "mistral", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.06, + "pOut": 0.18, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/ministral-3-3b-2512", + "name": "mistral/ministral-3-3b-2512", + "provider": "mistral", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/ministral-3-8b-2512", + "name": "mistral/ministral-3-8b-2512", + "provider": "mistral", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/ministral-3-14b-2512", + "name": "mistral/ministral-3-14b-2512", + "provider": "mistral", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/ministral-8b-2512", + "name": "mistral/ministral-8b-2512", + "provider": "mistral", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/mistral-tiny", + "name": "mistral/mistral-tiny", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 0.25, + "pOut": 0.25, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/open-codestral-mamba", + "name": "mistral/open-codestral-mamba", + "provider": "mistral", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.25, + "pOut": 0.25, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "mistral/open-mistral-7b", + "name": "mistral/open-mistral-7b", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 0.25, + "pOut": 0.25, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/open-mistral-nemo", + "name": "mistral/open-mistral-nemo", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.3, + "pOut": 0.3, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/open-mistral-nemo-2407", + "name": "mistral/open-mistral-nemo-2407", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.3, + "pOut": 0.3, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/open-mixtral-8x22b", + "name": "mistral/open-mixtral-8x22b", + "provider": "mistral", + "ctx": 65336, + "maxOut": 8191, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/open-mixtral-8x7b", + "name": "mistral/open-mixtral-8x7b", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 0.7, + "pOut": 0.7, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/pixtral-12b-2409", + "name": "mistral/pixtral-12b-2409", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/pixtral-large-2411", + "name": "mistral/pixtral-large-2411", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "mistral/pixtral-large-latest", + "name": "mistral/pixtral-large-latest", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "moonshot.kimi-k2-thinking", + "name": "moonshot.kimi-k2-thinking", + "provider": "moonshot.kimi-k2-thinking", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 2.5, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "moonshotai.kimi-k2.5", + "name": "moonshotai.kimi-k2.5", + "provider": "moonshotai.kimi-k2.5", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/kimi-k2-0711-preview", + "name": "moonshot/kimi-k2-0711-preview", + "provider": "moonshot", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/kimi-k2-0905-preview", + "name": "moonshot/kimi-k2-0905-preview", + "provider": "moonshot", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/kimi-k2-turbo-preview", + "name": "moonshot/kimi-k2-turbo-preview", + "provider": "moonshot", + "ctx": 262144, + "maxOut": 262144, + "pIn": 1.15, + "pOut": 8, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/kimi-k2.5", + "name": "moonshot/kimi-k2.5", + "provider": "moonshot", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 3, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/kimi-k2.6", + "name": "moonshot/kimi-k2.6", + "provider": "moonshot", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.95, + "pOut": 4, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/kimi-latest", + "name": "moonshot/kimi-latest", + "provider": "moonshot", + "ctx": 131072, + "maxOut": 131072, + "pIn": 2, + "pOut": 5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/kimi-latest-128k", + "name": "moonshot/kimi-latest-128k", + "provider": "moonshot", + "ctx": 131072, + "maxOut": 131072, + "pIn": 2, + "pOut": 5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/kimi-latest-32k", + "name": "moonshot/kimi-latest-32k", + "provider": "moonshot", + "ctx": 32768, + "maxOut": 32768, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/kimi-latest-8k", + "name": "moonshot/kimi-latest-8k", + "provider": "moonshot", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/kimi-thinking-preview", + "name": "moonshot/kimi-thinking-preview", + "provider": "moonshot", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.6, + "pOut": 2.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/kimi-k2-thinking", + "name": "moonshot/kimi-k2-thinking", + "provider": "moonshot", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/kimi-k2-thinking-turbo", + "name": "moonshot/kimi-k2-thinking-turbo", + "provider": "moonshot", + "ctx": 262144, + "maxOut": 262144, + "pIn": 1.15, + "pOut": 8, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/moonshot-v1-128k", + "name": "moonshot/moonshot-v1-128k", + "provider": "moonshot", + "ctx": 131072, + "maxOut": 131072, + "pIn": 2, + "pOut": 5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/moonshot-v1-128k-0430", + "name": "moonshot/moonshot-v1-128k-0430", + "provider": "moonshot", + "ctx": 131072, + "maxOut": 131072, + "pIn": 2, + "pOut": 5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/moonshot-v1-128k-vision-preview", + "name": "moonshot/moonshot-v1-128k-vision-preview", + "provider": "moonshot", + "ctx": 131072, + "maxOut": 131072, + "pIn": 2, + "pOut": 5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/moonshot-v1-32k", + "name": "moonshot/moonshot-v1-32k", + "provider": "moonshot", + "ctx": 32768, + "maxOut": 32768, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/moonshot-v1-32k-0430", + "name": "moonshot/moonshot-v1-32k-0430", + "provider": "moonshot", + "ctx": 32768, + "maxOut": 32768, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/moonshot-v1-32k-vision-preview", + "name": "moonshot/moonshot-v1-32k-vision-preview", + "provider": "moonshot", + "ctx": 32768, + "maxOut": 32768, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/moonshot-v1-8k", + "name": "moonshot/moonshot-v1-8k", + "provider": "moonshot", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/moonshot-v1-8k-0430", + "name": "moonshot/moonshot-v1-8k-0430", + "provider": "moonshot", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/moonshot-v1-8k-vision-preview", + "name": "moonshot/moonshot-v1-8k-vision-preview", + "provider": "moonshot", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "moonshot/moonshot-v1-auto", + "name": "moonshot/moonshot-v1-auto", + "provider": "moonshot", + "ctx": 131072, + "maxOut": 131072, + "pIn": 2, + "pOut": 5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "multimodalembedding", + "name": "multimodalembedding", + "provider": "multimodalembedding", + "ctx": 2048, + "maxOut": null, + "pIn": 0.7999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "multimodalembedding@001", + "name": "multimodalembedding@001", + "provider": "multimodalembedding@001", + "ctx": 2048, + "maxOut": null, + "pIn": 0.7999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/deepseek-ai/DeepSeek-R1", + "name": "nebius/deepseek-ai/DeepSeek-R1", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.7999999999999999, + "pOut": 2.4, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/deepseek-ai/DeepSeek-R1-0528", + "name": "nebius/deepseek-ai/DeepSeek-R1-0528", + "provider": "deepseek", + "ctx": 164000, + "maxOut": 164000, + "pIn": 0.7999999999999999, + "pOut": 2.4, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/deepseek-ai/DeepSeek-R1-Distill-Llama-70B", + "name": "nebius/deepseek-ai/DeepSeek-R1-Distill-Llama-70B", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.25, + "pOut": 0.75, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/deepseek-ai/DeepSeek-V3", + "name": "nebius/deepseek-ai/DeepSeek-V3", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/deepseek-ai/DeepSeek-V3-0324", + "name": "nebius/deepseek-ai/DeepSeek-V3-0324", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/google/gemma-3-27b-it", + "name": "nebius/google/gemma-3-27b-it", + "provider": "gemini", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.06, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/meta-llama/Llama-3.3-70B-Instruct", + "name": "nebius/meta-llama/Llama-3.3-70B-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.13, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/meta-llama/Llama-Guard-3-8B", + "name": "nebius/meta-llama/Llama-Guard-3-8B", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.02, + "pOut": 0.06, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/meta-llama/Meta-Llama-3.1-8B-Instruct", + "name": "nebius/meta-llama/Meta-Llama-3.1-8B-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.02, + "pOut": 0.06, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/meta-llama/Meta-Llama-3.1-70B-Instruct", + "name": "nebius/meta-llama/Meta-Llama-3.1-70B-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.13, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/meta-llama/Meta-Llama-3.1-405B-Instruct", + "name": "nebius/meta-llama/Meta-Llama-3.1-405B-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/mistralai/Mistral-Nemo-Instruct-2407", + "name": "nebius/mistralai/Mistral-Nemo-Instruct-2407", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.04, + "pOut": 0.12, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/NousResearch/Hermes-3-Llama-3.1-405B", + "name": "nebius/NousResearch/Hermes-3-Llama-3.1-405B", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/nvidia/Llama-3.1-Nemotron-Ultra-253B-v1", + "name": "nebius/nvidia/Llama-3.1-Nemotron-Ultra-253B-v1", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.6, + "pOut": 1.7999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/nvidia/Llama-3.3-Nemotron-Super-49B-v1", + "name": "nebius/nvidia/Llama-3.3-Nemotron-Super-49B-v1", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/Qwen/Qwen3-235B-A22B", + "name": "nebius/Qwen/Qwen3-235B-A22B", + "provider": "nebius", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/Qwen/Qwen3-32B", + "name": "nebius/Qwen/Qwen3-32B", + "provider": "nebius", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/Qwen/Qwen3-30B-A3B", + "name": "nebius/Qwen/Qwen3-30B-A3B", + "provider": "nebius", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/Qwen/Qwen3-14B", + "name": "nebius/Qwen/Qwen3-14B", + "provider": "nebius", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.08, + "pOut": 0.24, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/Qwen/Qwen3-4B", + "name": "nebius/Qwen/Qwen3-4B", + "provider": "nebius", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.08, + "pOut": 0.24, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/Qwen/QwQ-32B", + "name": "nebius/Qwen/QwQ-32B", + "provider": "nebius", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.15, + "pOut": 0.44999999999999996, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/Qwen/Qwen2.5-72B-Instruct", + "name": "nebius/Qwen/Qwen2.5-72B-Instruct", + "provider": "nebius", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.13, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/Qwen/Qwen2.5-32B-Instruct", + "name": "nebius/Qwen/Qwen2.5-32B-Instruct", + "provider": "nebius", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.06, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/Qwen/Qwen2.5-Coder-7B", + "name": "nebius/Qwen/Qwen2.5-Coder-7B", + "provider": "nebius", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.01, + "pOut": 0.03, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/Qwen/Qwen2.5-VL-72B-Instruct", + "name": "nebius/Qwen/Qwen2.5-VL-72B-Instruct", + "provider": "nebius", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.13, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/Qwen/Qwen2-VL-72B-Instruct", + "name": "nebius/Qwen/Qwen2-VL-72B-Instruct", + "provider": "nebius", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.13, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/Qwen/Qwen2-VL-7B-Instruct", + "name": "nebius/Qwen/Qwen2-VL-7B-Instruct", + "provider": "nebius", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.02, + "pOut": 0.06, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/BAAI/bge-en-icl", + "name": "nebius/BAAI/bge-en-icl", + "provider": "nebius", + "ctx": 32768, + "maxOut": null, + "pIn": 0.01, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/BAAI/bge-multilingual-gemma2", + "name": "nebius/BAAI/bge-multilingual-gemma2", + "provider": "nebius", + "ctx": 8192, + "maxOut": null, + "pIn": 0.01, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nebius/intfloat/e5-mistral-7b-instruct", + "name": "nebius/intfloat/e5-mistral-7b-instruct", + "provider": "mistral", + "ctx": 32768, + "maxOut": null, + "pIn": 0.01, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nvidia.nemotron-nano-12b-v2", + "name": "nvidia.nemotron-nano-12b-v2", + "provider": "nvidia.nemotron-nano-12b-v2", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nvidia.nemotron-nano-9b-v2", + "name": "nvidia.nemotron-nano-9b-v2", + "provider": "nvidia.nemotron-nano-9b-v2", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.06, + "pOut": 0.22999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nvidia.nemotron-nano-3-30b", + "name": "nvidia.nemotron-nano-3-30b", + "provider": "nvidia.nemotron-nano-3-30b", + "ctx": 262144, + "maxOut": 8192, + "pIn": 0.06, + "pOut": 0.24, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "nvidia.nemotron-super-3-120b", + "name": "nvidia.nemotron-super-3-120b", + "provider": "nvidia.nemotron-super-3-120b", + "ctx": 256000, + "maxOut": 32768, + "pIn": 0.15, + "pOut": 0.65, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "o1", + "name": "o1", + "provider": "o1", + "ctx": 200000, + "maxOut": 100000, + "pIn": 15, + "pOut": 60, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "o1-2024-12-17", + "name": "o1-2024-12-17", + "provider": "o1-2024-12-17", + "ctx": 200000, + "maxOut": 100000, + "pIn": 15, + "pOut": 60, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "o1-pro", + "name": "o1-pro", + "provider": "o1-pro", + "ctx": 200000, + "maxOut": 100000, + "pIn": 150, + "pOut": 600, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "o1-pro-2025-03-19", + "name": "o1-pro-2025-03-19", + "provider": "o1-pro-2025-03-19", + "ctx": 200000, + "maxOut": 100000, + "pIn": 150, + "pOut": 600, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "o3", + "name": "o3", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "o3-2025-04-16", + "name": "o3-2025-04-16", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "o3-deep-research", + "name": "o3-deep-research", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 10, + "pOut": 40, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "o3-deep-research-2025-06-26", + "name": "o3-deep-research-2025-06-26", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 10, + "pOut": 40, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "o3-mini", + "name": "o3-mini", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "o3-mini-2025-01-31", + "name": "o3-mini-2025-01-31", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "o3-pro", + "name": "o3-pro", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 20, + "pOut": 80, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "o3-pro-2025-06-10", + "name": "o3-pro-2025-06-10", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 20, + "pOut": 80, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "o4-mini", + "name": "o4-mini", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "o4-mini-2025-04-16", + "name": "o4-mini-2025-04-16", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "o4-mini-deep-research", + "name": "o4-mini-deep-research", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "o4-mini-deep-research-2025-06-26", + "name": "o4-mini-deep-research-2025-06-26", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "oci/meta.llama-3.1-8b-instruct", + "name": "oci/meta.llama-3.1-8b-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 4000, + "pIn": 0.72, + "pOut": 0.72, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/meta.llama-3.1-70b-instruct", + "name": "oci/meta.llama-3.1-70b-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 4000, + "pIn": 0.72, + "pOut": 0.72, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/meta.llama-3.1-405b-instruct", + "name": "oci/meta.llama-3.1-405b-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 4000, + "pIn": 10.68, + "pOut": 10.68, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/meta.llama-3.2-90b-vision-instruct", + "name": "oci/meta.llama-3.2-90b-vision-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 4000, + "pIn": 2, + "pOut": 2, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/meta.llama-3.3-70b-instruct", + "name": "oci/meta.llama-3.3-70b-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 4000, + "pIn": 0.72, + "pOut": 0.72, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/meta.llama-4-maverick-17b-128e-instruct-fp8", + "name": "oci/meta.llama-4-maverick-17b-128e-instruct-fp8", + "provider": "meta", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.72, + "pOut": 0.72, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/meta.llama-4-scout-17b-16e-instruct", + "name": "oci/meta.llama-4-scout-17b-16e-instruct", + "provider": "meta", + "ctx": 10485760, + "maxOut": 8192, + "pIn": 0.72, + "pOut": 0.72, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/xai.grok-3", + "name": "oci/xai.grok-3", + "provider": "oci", + "ctx": 131072, + "maxOut": 131072, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/xai.grok-3-fast", + "name": "oci/xai.grok-3-fast", + "provider": "oci", + "ctx": 131072, + "maxOut": 131072, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/xai.grok-3-mini", + "name": "oci/xai.grok-3-mini", + "provider": "oci", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.3, + "pOut": 0.5, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/xai.grok-3-mini-fast", + "name": "oci/xai.grok-3-mini-fast", + "provider": "oci", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.6, + "pOut": 4, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/xai.grok-4", + "name": "oci/xai.grok-4", + "provider": "oci", + "ctx": 128000, + "maxOut": 128000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/cohere.command-latest", + "name": "oci/cohere.command-latest", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4000, + "pIn": 1.56, + "pOut": 1.56, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/cohere.command-a-03-2025", + "name": "oci/cohere.command-a-03-2025", + "provider": "cohere", + "ctx": 256000, + "maxOut": 4000, + "pIn": 1.56, + "pOut": 1.56, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/cohere.command-plus-latest", + "name": "oci/cohere.command-plus-latest", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4000, + "pIn": 1.56, + "pOut": 1.56, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/google.gemini-2.5-flash", + "name": "oci/google.gemini-2.5-flash", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "oci/google.gemini-2.5-pro", + "name": "oci/google.gemini-2.5-pro", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "oci/google.gemini-2.5-flash-lite", + "name": "oci/google.gemini-2.5-flash-lite", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.075, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "oci/cohere.command-a-vision", + "name": "oci/cohere.command-a-vision", + "provider": "cohere", + "ctx": 256000, + "maxOut": 8192, + "pIn": 1.56, + "pOut": 1.56, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/cohere.command-a-reasoning", + "name": "oci/cohere.command-a-reasoning", + "provider": "cohere", + "ctx": 256000, + "maxOut": 8192, + "pIn": 1.56, + "pOut": 1.56, + "tools": false, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/cohere.command-a-reasoning-08-2025", + "name": "oci/cohere.command-a-reasoning-08-2025", + "provider": "cohere", + "ctx": 256000, + "maxOut": 4000, + "pIn": 1.56, + "pOut": 1.56, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/cohere.command-a-vision-07-2025", + "name": "oci/cohere.command-a-vision-07-2025", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4000, + "pIn": 1.56, + "pOut": 1.56, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/cohere.command-a-translate-08-2025", + "name": "oci/cohere.command-a-translate-08-2025", + "provider": "cohere", + "ctx": 256000, + "maxOut": 4000, + "pIn": 0.09, + "pOut": 0.09, + "tools": false, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/cohere.command-r-08-2024", + "name": "oci/cohere.command-r-08-2024", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4000, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/cohere.command-r-plus-08-2024", + "name": "oci/cohere.command-r-plus-08-2024", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4000, + "pIn": 1.56, + "pOut": 1.56, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/meta.llama-3.2-11b-vision-instruct", + "name": "oci/meta.llama-3.2-11b-vision-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 4000, + "pIn": 2, + "pOut": 2, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/meta.llama-3.3-70b-instruct-fp8-dynamic", + "name": "oci/meta.llama-3.3-70b-instruct-fp8-dynamic", + "provider": "meta", + "ctx": 128000, + "maxOut": 4000, + "pIn": 0.72, + "pOut": 0.72, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/xai.grok-4-fast", + "name": "oci/xai.grok-4-fast", + "provider": "oci", + "ctx": 131072, + "maxOut": 131072, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/xai.grok-4.1-fast", + "name": "oci/xai.grok-4.1-fast", + "provider": "oci", + "ctx": 131072, + "maxOut": 131072, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/xai.grok-4.20", + "name": "oci/xai.grok-4.20", + "provider": "oci", + "ctx": 131072, + "maxOut": 131072, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/xai.grok-4.20-multi-agent", + "name": "oci/xai.grok-4.20-multi-agent", + "provider": "oci", + "ctx": 131072, + "maxOut": 131072, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/xai.grok-code-fast-1", + "name": "oci/xai.grok-code-fast-1", + "provider": "oci", + "ctx": 131072, + "maxOut": 131072, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "oci/openai.gpt-5", + "name": "oci/openai.gpt-5", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "oci/openai.gpt-5-mini", + "name": "oci/openai.gpt-5-mini", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "oci/openai.gpt-5-nano", + "name": "oci/openai.gpt-5-nano", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.049999999999999996, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "oci/cohere.embed-english-v3.0", + "name": "oci/cohere.embed-english-v3.0", + "provider": "cohere", + "ctx": 512, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "oci/cohere.embed-english-light-v3.0", + "name": "oci/cohere.embed-english-light-v3.0", + "provider": "cohere", + "ctx": 512, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "oci/cohere.embed-multilingual-v3.0", + "name": "oci/cohere.embed-multilingual-v3.0", + "provider": "cohere", + "ctx": 512, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "oci/cohere.embed-multilingual-light-v3.0", + "name": "oci/cohere.embed-multilingual-light-v3.0", + "provider": "cohere", + "ctx": 512, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "oci/cohere.embed-english-image-v3.0", + "name": "oci/cohere.embed-english-image-v3.0", + "provider": "cohere", + "ctx": 512, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "oci/cohere.embed-english-light-image-v3.0", + "name": "oci/cohere.embed-english-light-image-v3.0", + "provider": "cohere", + "ctx": 512, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "oci/cohere.embed-multilingual-light-image-v3.0", + "name": "oci/cohere.embed-multilingual-light-image-v3.0", + "provider": "cohere", + "ctx": 512, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "oci/cohere.embed-v4.0", + "name": "oci/cohere.embed-v4.0", + "provider": "cohere", + "ctx": 128000, + "maxOut": null, + "pIn": 0.12, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/codegeex4", + "name": "ollama/codegeex4", + "provider": "meta", + "ctx": 32768, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/codegemma", + "name": "ollama/codegemma", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/codellama", + "name": "ollama/codellama", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/deepseek-coder-v2-base", + "name": "ollama/deepseek-coder-v2-base", + "provider": "deepseek", + "ctx": 8192, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/deepseek-coder-v2-instruct", + "name": "ollama/deepseek-coder-v2-instruct", + "provider": "deepseek", + "ctx": 32768, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/deepseek-coder-v2-lite-base", + "name": "ollama/deepseek-coder-v2-lite-base", + "provider": "deepseek", + "ctx": 8192, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/deepseek-coder-v2-lite-instruct", + "name": "ollama/deepseek-coder-v2-lite-instruct", + "provider": "deepseek", + "ctx": 32768, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/deepseek-v3.1:671b-cloud", + "name": "ollama/deepseek-v3.1:671b-cloud", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/gpt-oss:120b-cloud", + "name": "ollama/gpt-oss:120b-cloud", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/gpt-oss:20b-cloud", + "name": "ollama/gpt-oss:20b-cloud", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/internlm2_5-20b-chat", + "name": "ollama/internlm2_5-20b-chat", + "provider": "meta", + "ctx": 32768, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/llama2", + "name": "ollama/llama2", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/llama2-uncensored", + "name": "ollama/llama2-uncensored", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/llama2:13b", + "name": "ollama/llama2:13b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/llama2:70b", + "name": "ollama/llama2:70b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/llama2:7b", + "name": "ollama/llama2:7b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/llama3", + "name": "ollama/llama3", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/llama3.1", + "name": "ollama/llama3.1", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/llama3:70b", + "name": "ollama/llama3:70b", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/llama3:8b", + "name": "ollama/llama3:8b", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/mistral", + "name": "ollama/mistral", + "provider": "mistral", + "ctx": 8192, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/mistral-7B-Instruct-v0.1", + "name": "ollama/mistral-7B-Instruct-v0.1", + "provider": "mistral", + "ctx": 8192, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/mistral-7B-Instruct-v0.2", + "name": "ollama/mistral-7B-Instruct-v0.2", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/mistral-large-instruct-2407", + "name": "ollama/mistral-large-instruct-2407", + "provider": "mistral", + "ctx": 65536, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/mixtral-8x22B-Instruct-v0.1", + "name": "ollama/mixtral-8x22B-Instruct-v0.1", + "provider": "meta", + "ctx": 65536, + "maxOut": 65536, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/mixtral-8x7B-Instruct-v0.1", + "name": "ollama/mixtral-8x7B-Instruct-v0.1", + "provider": "meta", + "ctx": 32768, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/orca-mini", + "name": "ollama/orca-mini", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/qwen3-coder:480b-cloud", + "name": "ollama/qwen3-coder:480b-cloud", + "provider": "meta", + "ctx": 262144, + "maxOut": 262144, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ollama/vicuna", + "name": "ollama/vicuna", + "provider": "meta", + "ctx": 2048, + "maxOut": 2048, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openai.gpt-oss-120b-1:0", + "name": "openai.gpt-oss-120b-1:0", + "provider": "openai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openai.gpt-oss-20b-1:0", + "name": "openai.gpt-oss-20b-1:0", + "provider": "openai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.07, + "pOut": 0.3, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openai.gpt-oss-safeguard-120b", + "name": "openai.gpt-oss-safeguard-120b", + "provider": "openai", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.15, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openai.gpt-oss-safeguard-20b", + "name": "openai.gpt-oss-safeguard-20b", + "provider": "openai", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.07, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/anthropic/claude-3-haiku", + "name": "openrouter/anthropic/claude-3-haiku", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 0.25, + "pOut": 1.25, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/anthropic/claude-3.5-sonnet", + "name": "openrouter/anthropic/claude-3.5-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/anthropic/claude-3.7-sonnet", + "name": "openrouter/anthropic/claude-3.7-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 128000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/anthropic/claude-opus-4", + "name": "openrouter/anthropic/claude-opus-4", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/anthropic/claude-opus-4.1", + "name": "openrouter/anthropic/claude-opus-4.1", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/anthropic/claude-sonnet-4", + "name": "openrouter/anthropic/claude-sonnet-4", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/anthropic/claude-sonnet-4.6", + "name": "openrouter/anthropic/claude-sonnet-4.6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/anthropic/claude-opus-4.5", + "name": "openrouter/anthropic/claude-opus-4.5", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/anthropic/claude-opus-4.6", + "name": "openrouter/anthropic/claude-opus-4.6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/anthropic/claude-sonnet-4.5", + "name": "openrouter/anthropic/claude-sonnet-4.5", + "provider": "claude", + "ctx": 1000000, + "maxOut": 1000000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/anthropic/claude-haiku-4.5", + "name": "openrouter/anthropic/claude-haiku-4.5", + "provider": "claude", + "ctx": 200000, + "maxOut": 200000, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/anthropic/claude-opus-4.7", + "name": "openrouter/anthropic/claude-opus-4.7", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/bytedance/ui-tars-1.5-7b", + "name": "openrouter/bytedance/ui-tars-1.5-7b", + "provider": "openrouter", + "ctx": 131072, + "maxOut": 2048, + "pIn": 0.09999999999999999, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/deepseek/deepseek-chat", + "name": "openrouter/deepseek/deepseek-chat", + "provider": "deepseek", + "ctx": 65536, + "maxOut": 8192, + "pIn": 0.14, + "pOut": 0.28, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/deepseek/deepseek-chat-v3-0324", + "name": "openrouter/deepseek/deepseek-chat-v3-0324", + "provider": "deepseek", + "ctx": 65536, + "maxOut": 8192, + "pIn": 0.14, + "pOut": 0.28, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/deepseek/deepseek-chat-v3.1", + "name": "openrouter/deepseek/deepseek-chat-v3.1", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.19999999999999998, + "pOut": 0.7999999999999999, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/deepseek/deepseek-v3.2", + "name": "openrouter/deepseek/deepseek-v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.28, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/deepseek/deepseek-v3.2-exp", + "name": "openrouter/deepseek/deepseek-v3.2-exp", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.19999999999999998, + "pOut": 0.39999999999999997, + "tools": true, + "cot": false, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/deepseek/deepseek-r1", + "name": "openrouter/deepseek/deepseek-r1", + "provider": "deepseek", + "ctx": 65336, + "maxOut": 8192, + "pIn": 0.55, + "pOut": 2.1900000000000004, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/deepseek/deepseek-r1-0528", + "name": "openrouter/deepseek/deepseek-r1-0528", + "provider": "deepseek", + "ctx": 65336, + "maxOut": 8192, + "pIn": 0.5, + "pOut": 2.1500000000000004, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/google/gemini-2.0-flash-001", + "name": "openrouter/google/gemini-2.0-flash-001", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/google/gemini-2.5-flash", + "name": "openrouter/google/gemini-2.5-flash", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/google/gemini-2.5-pro", + "name": "openrouter/google/gemini-2.5-pro", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/google/gemini-3-pro-preview", + "name": "openrouter/google/gemini-3-pro-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/google/gemini-3-flash-preview", + "name": "openrouter/google/gemini-3-flash-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.5, + "pOut": 3, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/google/gemini-3.1-flash-lite-preview", + "name": "openrouter/google/gemini-3.1-flash-lite-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.25, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/google/gemini-3.1-flash-lite", + "name": "openrouter/google/gemini-3.1-flash-lite", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.25, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/google/gemini-3.1-pro-preview", + "name": "openrouter/google/gemini-3.1-pro-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 2, + "pOut": 12, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/gryphe/mythomax-l2-13b", + "name": "openrouter/gryphe/mythomax-l2-13b", + "provider": "openrouter", + "ctx": 8192, + "maxOut": null, + "pIn": 1.875, + "pOut": 1.875, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/mancer/weaver", + "name": "openrouter/mancer/weaver", + "provider": "openrouter", + "ctx": 8000, + "maxOut": 2000, + "pIn": 5.625, + "pOut": 5.625, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/meta-llama/llama-3-70b-instruct", + "name": "openrouter/meta-llama/llama-3-70b-instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 8000, + "pIn": 0.59, + "pOut": 0.7899999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/minimax/minimax-m2", + "name": "openrouter/minimax/minimax-m2", + "provider": "openrouter", + "ctx": 204800, + "maxOut": 204800, + "pIn": 0.255, + "pOut": 1.02, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/mistralai/devstral-2512", + "name": "openrouter/mistralai/devstral-2512", + "provider": "mistral", + "ctx": 262144, + "maxOut": 65536, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/mistralai/ministral-3b-2512", + "name": "openrouter/mistralai/ministral-3b-2512", + "provider": "mistral", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/mistralai/ministral-8b-2512", + "name": "openrouter/mistralai/ministral-8b-2512", + "provider": "mistral", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/mistralai/ministral-14b-2512", + "name": "openrouter/mistralai/ministral-14b-2512", + "provider": "mistral", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/mistralai/mistral-large-2512", + "name": "openrouter/mistralai/mistral-large-2512", + "provider": "mistral", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/mistralai/mistral-7b-instruct", + "name": "openrouter/mistralai/mistral-7b-instruct", + "provider": "mistral", + "ctx": 32768, + "maxOut": 8191, + "pIn": 0.13, + "pOut": 0.13, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/mistralai/mistral-large", + "name": "openrouter/mistralai/mistral-large", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8191, + "pIn": 8, + "pOut": 24, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/mistralai/mistral-small-3.1-24b-instruct", + "name": "openrouter/mistralai/mistral-small-3.1-24b-instruct", + "provider": "mistral", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/mistralai/mistral-small-3.2-24b-instruct", + "name": "openrouter/mistralai/mistral-small-3.2-24b-instruct", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/mistralai/mixtral-8x22b-instruct", + "name": "openrouter/mistralai/mixtral-8x22b-instruct", + "provider": "mistral", + "ctx": 65536, + "maxOut": 65536, + "pIn": 0.65, + "pOut": 0.65, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/moonshotai/kimi-k2.5", + "name": "openrouter/moonshotai/kimi-k2.5", + "provider": "openrouter", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-3.5-turbo", + "name": "openrouter/openai/gpt-3.5-turbo", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 1.5, + "pOut": 2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-3.5-turbo-16k", + "name": "openrouter/openai/gpt-3.5-turbo-16k", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 3, + "pOut": 4, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-4", + "name": "openrouter/openai/gpt-4", + "provider": "openai", + "ctx": 8191, + "maxOut": 4096, + "pIn": 30, + "pOut": 60, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-4.1", + "name": "openrouter/openai/gpt-4.1", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-4.1-mini", + "name": "openrouter/openai/gpt-4.1-mini", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.39999999999999997, + "pOut": 1.5999999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-4.1-nano", + "name": "openrouter/openai/gpt-4.1-nano", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-4o", + "name": "openrouter/openai/gpt-4o", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-4o-2024-05-13", + "name": "openrouter/openai/gpt-4o-2024-05-13", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-5-chat", + "name": "openrouter/openai/gpt-5-chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.25, + "pOut": 10, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-5-codex", + "name": "openrouter/openai/gpt-5-codex", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-5.2-codex", + "name": "openrouter/openai/gpt-5.2-codex", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.75, + "pOut": 14, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-5", + "name": "openrouter/openai/gpt-5", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-5-mini", + "name": "openrouter/openai/gpt-5-mini", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.25, + "pOut": 2, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-5-nano", + "name": "openrouter/openai/gpt-5-nano", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 0.049999999999999996, + "pOut": 0.39999999999999997, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-5.1-codex-max", + "name": "openrouter/openai/gpt-5.1-codex-max", + "provider": "openai", + "ctx": 400000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-5.2", + "name": "openrouter/openai/gpt-5.2", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-5.2-chat", + "name": "openrouter/openai/gpt-5.2-chat", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 1.75, + "pOut": 14, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-5.2-pro", + "name": "openrouter/openai/gpt-5.2-pro", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 21, + "pOut": 168, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-oss-120b", + "name": "openrouter/openai/gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.18, + "pOut": 0.7999999999999999, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/openai/gpt-oss-20b", + "name": "openrouter/openai/gpt-oss-20b", + "provider": "openai", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.02, + "pOut": 0.09999999999999999, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/openai/o1", + "name": "openrouter/openai/o1", + "provider": "openrouter", + "ctx": 200000, + "maxOut": 100000, + "pIn": 15, + "pOut": 60, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/openai/o3-mini", + "name": "openrouter/openai/o3-mini", + "provider": "openai", + "ctx": 128000, + "maxOut": 65536, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openai/o3-mini-high", + "name": "openrouter/openai/o3-mini-high", + "provider": "openai", + "ctx": 128000, + "maxOut": 65536, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/qwen/qwen-2.5-coder-32b-instruct", + "name": "openrouter/qwen/qwen-2.5-coder-32b-instruct", + "provider": "openrouter", + "ctx": 33792, + "maxOut": 33792, + "pIn": 0.18, + "pOut": 0.18, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/qwen/qwen-vl-plus", + "name": "openrouter/qwen/qwen-vl-plus", + "provider": "openrouter", + "ctx": 8192, + "maxOut": 2048, + "pIn": 0.21, + "pOut": 0.63, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/qwen/qwen3-coder", + "name": "openrouter/qwen/qwen3-coder", + "provider": "openrouter", + "ctx": 262100, + "maxOut": 262100, + "pIn": 0.22, + "pOut": 0.95, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/qwen/qwen3-coder-plus", + "name": "openrouter/qwen/qwen3-coder-plus", + "provider": "openrouter", + "ctx": 997952, + "maxOut": 65536, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/qwen/qwen3-235b-a22b-2507", + "name": "openrouter/qwen/qwen3-235b-a22b-2507", + "provider": "openrouter", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.071, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/qwen/qwen3-235b-a22b-thinking-2507", + "name": "openrouter/qwen/qwen3-235b-a22b-thinking-2507", + "provider": "openrouter", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.11, + "pOut": 0.6, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/qwen/qwen3.6-plus", + "name": "openrouter/qwen/qwen3.6-plus", + "provider": "openrouter", + "ctx": 1000000, + "maxOut": 65536, + "pIn": 0.325, + "pOut": 1.95, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/qwen/qwen3.5-35b-a3b", + "name": "openrouter/qwen/qwen3.5-35b-a3b", + "provider": "openrouter", + "ctx": 262144, + "maxOut": 65536, + "pIn": 0.25, + "pOut": 2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/qwen/qwen3.5-27b", + "name": "openrouter/qwen/qwen3.5-27b", + "provider": "openrouter", + "ctx": 262144, + "maxOut": 65536, + "pIn": 0.3, + "pOut": 2.4, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/qwen/qwen3.5-122b-a10b", + "name": "openrouter/qwen/qwen3.5-122b-a10b", + "provider": "openrouter", + "ctx": 262144, + "maxOut": 65536, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/qwen/qwen3.5-flash-02-23", + "name": "openrouter/qwen/qwen3.5-flash-02-23", + "provider": "openrouter", + "ctx": 1000000, + "maxOut": 65536, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/qwen/qwen3.5-plus-02-15", + "name": "openrouter/qwen/qwen3.5-plus-02-15", + "provider": "openrouter", + "ctx": 1000000, + "maxOut": 65536, + "pIn": 0.39999999999999997, + "pOut": 2.4, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/qwen/qwen3.5-397b-a17b", + "name": "openrouter/qwen/qwen3.5-397b-a17b", + "provider": "openrouter", + "ctx": 262144, + "maxOut": 65536, + "pIn": 0.6, + "pOut": 3.5999999999999996, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/switchpoint/router", + "name": "openrouter/switchpoint/router", + "provider": "openrouter", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.85, + "pOut": 3.4, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/undi95/remm-slerp-l2-13b", + "name": "openrouter/undi95/remm-slerp-l2-13b", + "provider": "openrouter", + "ctx": 6144, + "maxOut": 4096, + "pIn": 1.875, + "pOut": 1.875, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/x-ai/grok-4", + "name": "openrouter/x-ai/grok-4", + "provider": "openrouter", + "ctx": 256000, + "maxOut": 256000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/z-ai/glm-4.6", + "name": "openrouter/z-ai/glm-4.6", + "provider": "openrouter", + "ctx": 202800, + "maxOut": 131000, + "pIn": 0.39999999999999997, + "pOut": 1.75, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/z-ai/glm-4.6:exacto", + "name": "openrouter/z-ai/glm-4.6:exacto", + "provider": "openrouter", + "ctx": 202800, + "maxOut": 131000, + "pIn": 0.44999999999999996, + "pOut": 1.9, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/xiaomi/mimo-v2-flash", + "name": "openrouter/xiaomi/mimo-v2-flash", + "provider": "openrouter", + "ctx": 262144, + "maxOut": 16384, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/xiaomi/mimo-v2.5-pro", + "name": "openrouter/xiaomi/mimo-v2.5-pro", + "provider": "openrouter", + "ctx": 1048576, + "maxOut": 16384, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/xiaomi/mimo-v2.5", + "name": "openrouter/xiaomi/mimo-v2.5", + "provider": "openrouter", + "ctx": 1048576, + "maxOut": 131072, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/z-ai/glm-4.7", + "name": "openrouter/z-ai/glm-4.7", + "provider": "openrouter", + "ctx": 202752, + "maxOut": 64000, + "pIn": 0.39999999999999997, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/z-ai/glm-4.7-flash", + "name": "openrouter/z-ai/glm-4.7-flash", + "provider": "openrouter", + "ctx": 200000, + "maxOut": 32000, + "pIn": 0.07, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/z-ai/glm-5", + "name": "openrouter/z-ai/glm-5", + "provider": "openrouter", + "ctx": 202752, + "maxOut": 128000, + "pIn": 0.7999999999999999, + "pOut": 2.56, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/minimax/minimax-m2.1", + "name": "openrouter/minimax/minimax-m2.1", + "provider": "openrouter", + "ctx": 204000, + "maxOut": 64000, + "pIn": 0.27, + "pOut": 1.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/minimax/minimax-m2.5", + "name": "openrouter/minimax/minimax-m2.5", + "provider": "openrouter", + "ctx": 196608, + "maxOut": 65536, + "pIn": 0.3, + "pOut": 1.1, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "openrouter/openrouter/auto", + "name": "openrouter/openrouter/auto", + "provider": "openrouter", + "ctx": 2000000, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/openrouter/free", + "name": "openrouter/openrouter/free", + "provider": "openrouter", + "ctx": 200000, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "openrouter/openrouter/bodybuilder", + "name": "openrouter/openrouter/bodybuilder", + "provider": "openrouter", + "ctx": 128000, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "ovhcloud/DeepSeek-R1-Distill-Llama-70B", + "name": "ovhcloud/DeepSeek-R1-Distill-Llama-70B", + "provider": "deepseek", + "ctx": 131000, + "maxOut": 131000, + "pIn": 0.67, + "pOut": 0.67, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "ovhcloud/Llama-3.1-8B-Instruct", + "name": "ovhcloud/Llama-3.1-8B-Instruct", + "provider": "meta", + "ctx": 131000, + "maxOut": 131000, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ovhcloud/Meta-Llama-3_1-70B-Instruct", + "name": "ovhcloud/Meta-Llama-3_1-70B-Instruct", + "provider": "meta", + "ctx": 131000, + "maxOut": 131000, + "pIn": 0.67, + "pOut": 0.67, + "tools": false, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "ovhcloud/Meta-Llama-3_3-70B-Instruct", + "name": "ovhcloud/Meta-Llama-3_3-70B-Instruct", + "provider": "meta", + "ctx": 131000, + "maxOut": 131000, + "pIn": 0.67, + "pOut": 0.67, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ovhcloud/Mistral-7B-Instruct-v0.3", + "name": "ovhcloud/Mistral-7B-Instruct-v0.3", + "provider": "mistral", + "ctx": 127000, + "maxOut": 127000, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ovhcloud/Mistral-Nemo-Instruct-2407", + "name": "ovhcloud/Mistral-Nemo-Instruct-2407", + "provider": "mistral", + "ctx": 118000, + "maxOut": 118000, + "pIn": 0.13, + "pOut": 0.13, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ovhcloud/Mistral-Small-3.2-24B-Instruct-2506", + "name": "ovhcloud/Mistral-Small-3.2-24B-Instruct-2506", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.09, + "pOut": 0.28, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ovhcloud/Mixtral-8x7B-Instruct-v0.1", + "name": "ovhcloud/Mixtral-8x7B-Instruct-v0.1", + "provider": "ovhcloud", + "ctx": 32000, + "maxOut": 32000, + "pIn": 0.63, + "pOut": 0.63, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ovhcloud/Qwen2.5-Coder-32B-Instruct", + "name": "ovhcloud/Qwen2.5-Coder-32B-Instruct", + "provider": "ovhcloud", + "ctx": 32000, + "maxOut": 32000, + "pIn": 0.87, + "pOut": 0.87, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ovhcloud/Qwen2.5-VL-72B-Instruct", + "name": "ovhcloud/Qwen2.5-VL-72B-Instruct", + "provider": "ovhcloud", + "ctx": 32000, + "maxOut": 32000, + "pIn": 0.9099999999999999, + "pOut": 0.9099999999999999, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ovhcloud/Qwen3-32B", + "name": "ovhcloud/Qwen3-32B", + "provider": "ovhcloud", + "ctx": 32000, + "maxOut": 32000, + "pIn": 0.08, + "pOut": 0.22999999999999998, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "ovhcloud/gpt-oss-120b", + "name": "ovhcloud/gpt-oss-120b", + "provider": "openai", + "ctx": 131000, + "maxOut": 131000, + "pIn": 0.08, + "pOut": 0.39999999999999997, + "tools": false, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "ovhcloud/gpt-oss-20b", + "name": "ovhcloud/gpt-oss-20b", + "provider": "openai", + "ctx": 131000, + "maxOut": 131000, + "pIn": 0.04, + "pOut": 0.15, + "tools": false, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "ovhcloud/llava-v1.6-mistral-7b-hf", + "name": "ovhcloud/llava-v1.6-mistral-7b-hf", + "provider": "mistral", + "ctx": 32000, + "maxOut": 32000, + "pIn": 0.29, + "pOut": 0.29, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "ovhcloud/mamba-codestral-7B-v0.1", + "name": "ovhcloud/mamba-codestral-7B-v0.1", + "provider": "ovhcloud", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19, + "pOut": 0.19, + "tools": false, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "palm/chat-bison", + "name": "palm/chat-bison", + "provider": "palm", + "ctx": 8192, + "maxOut": 4096, + "pIn": 0.125, + "pOut": 0.125, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "palm/chat-bison-001", + "name": "palm/chat-bison-001", + "provider": "palm", + "ctx": 8192, + "maxOut": 4096, + "pIn": 0.125, + "pOut": 0.125, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "palm/text-bison", + "name": "palm/text-bison", + "provider": "palm", + "ctx": 8192, + "maxOut": 1024, + "pIn": 0.125, + "pOut": 0.125, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "palm/text-bison-001", + "name": "palm/text-bison-001", + "provider": "palm", + "ctx": 8192, + "maxOut": 1024, + "pIn": 0.125, + "pOut": 0.125, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "palm/text-bison-safety-off", + "name": "palm/text-bison-safety-off", + "provider": "palm", + "ctx": 8192, + "maxOut": 1024, + "pIn": 0.125, + "pOut": 0.125, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "palm/text-bison-safety-recitation-off", + "name": "palm/text-bison-safety-recitation-off", + "provider": "palm", + "ctx": 8192, + "maxOut": 1024, + "pIn": 0.125, + "pOut": 0.125, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/codellama-34b-instruct", + "name": "perplexity/codellama-34b-instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.35, + "pOut": 1.4, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/codellama-70b-instruct", + "name": "perplexity/codellama-70b-instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.7, + "pOut": 2.8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/llama-2-70b-chat", + "name": "perplexity/llama-2-70b-chat", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.7, + "pOut": 2.8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/llama-3.1-70b-instruct", + "name": "perplexity/llama-3.1-70b-instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 1, + "pOut": 1, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/llama-3.1-8b-instruct", + "name": "perplexity/llama-3.1-8b-instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/mistral-7b-instruct", + "name": "perplexity/mistral-7b-instruct", + "provider": "mistral", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.07, + "pOut": 0.28, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/mixtral-8x7b-instruct", + "name": "perplexity/mixtral-8x7b-instruct", + "provider": "perplexity", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.07, + "pOut": 0.28, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/pplx-70b-chat", + "name": "perplexity/pplx-70b-chat", + "provider": "perplexity", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.7, + "pOut": 2.8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/pplx-70b-online", + "name": "perplexity/pplx-70b-online", + "provider": "perplexity", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": 2.8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/pplx-7b-chat", + "name": "perplexity/pplx-7b-chat", + "provider": "perplexity", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.07, + "pOut": 0.28, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/pplx-7b-online", + "name": "perplexity/pplx-7b-online", + "provider": "perplexity", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": 0.28, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/sonar-medium-chat", + "name": "perplexity/sonar-medium-chat", + "provider": "perplexity", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.6, + "pOut": 1.7999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/sonar-medium-online", + "name": "perplexity/sonar-medium-online", + "provider": "perplexity", + "ctx": 12000, + "maxOut": 12000, + "pIn": null, + "pOut": 1.7999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/sonar-reasoning", + "name": "perplexity/sonar-reasoning", + "provider": "perplexity", + "ctx": 128000, + "maxOut": null, + "pIn": 1, + "pOut": 5, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/sonar-small-chat", + "name": "perplexity/sonar-small-chat", + "provider": "perplexity", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.07, + "pOut": 0.28, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/sonar-small-online", + "name": "perplexity/sonar-small-online", + "provider": "perplexity", + "ctx": 12000, + "maxOut": 12000, + "pIn": null, + "pOut": 0.28, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "publicai/swiss-ai/apertus-8b-instruct", + "name": "publicai/swiss-ai/apertus-8b-instruct", + "provider": "publicai", + "ctx": 8192, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "publicai/swiss-ai/apertus-70b-instruct", + "name": "publicai/swiss-ai/apertus-70b-instruct", + "provider": "publicai", + "ctx": 8192, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "publicai/aisingapore/Gemma-SEA-LION-v4-27B-IT", + "name": "publicai/aisingapore/Gemma-SEA-LION-v4-27B-IT", + "provider": "publicai", + "ctx": 8192, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "publicai/BSC-LT/salamandra-7b-instruct-tools-16k", + "name": "publicai/BSC-LT/salamandra-7b-instruct-tools-16k", + "provider": "publicai", + "ctx": 16384, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "publicai/BSC-LT/ALIA-40b-instruct_Q8_0", + "name": "publicai/BSC-LT/ALIA-40b-instruct_Q8_0", + "provider": "publicai", + "ctx": 8192, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "publicai/allenai/Olmo-3-7B-Instruct", + "name": "publicai/allenai/Olmo-3-7B-Instruct", + "provider": "publicai", + "ctx": 32768, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/pplx-embed-v1-0.6b", + "name": "perplexity/pplx-embed-v1-0.6b", + "provider": "perplexity", + "ctx": 32768, + "maxOut": null, + "pIn": 0.004, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "perplexity/pplx-embed-v1-4b", + "name": "perplexity/pplx-embed-v1-4b", + "provider": "perplexity", + "ctx": 32768, + "maxOut": null, + "pIn": 0.03, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "publicai/aisingapore/Qwen-SEA-LION-v4-32B-IT", + "name": "publicai/aisingapore/Qwen-SEA-LION-v4-32B-IT", + "provider": "publicai", + "ctx": 32768, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "publicai/allenai/Olmo-3-7B-Think", + "name": "publicai/allenai/Olmo-3-7B-Think", + "provider": "publicai", + "ctx": 32768, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "publicai/allenai/Olmo-3-32B-Think", + "name": "publicai/allenai/Olmo-3-32B-Think", + "provider": "publicai", + "ctx": 32768, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "qwen.qwen3-coder-480b-a35b-v1:0", + "name": "qwen.qwen3-coder-480b-a35b-v1:0", + "provider": "qwen.qwen3-coder-480b-a35b-v1:0", + "ctx": 262000, + "maxOut": 65536, + "pIn": 0.22, + "pOut": 1.7999999999999998, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "qwen.qwen3-235b-a22b-2507-v1:0", + "name": "qwen.qwen3-235b-a22b-2507-v1:0", + "provider": "qwen.qwen3-235b-a22b-2507-v1:0", + "ctx": 262144, + "maxOut": 131072, + "pIn": 0.22, + "pOut": 0.88, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "qwen.qwen3-coder-30b-a3b-v1:0", + "name": "qwen.qwen3-coder-30b-a3b-v1:0", + "provider": "qwen.qwen3-coder-30b-a3b-v1:0", + "ctx": 262144, + "maxOut": 131072, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "qwen.qwen3-32b-v1:0", + "name": "qwen.qwen3-32b-v1:0", + "provider": "qwen.qwen3-32b-v1:0", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "qwen.qwen3-next-80b-a3b", + "name": "qwen.qwen3-next-80b-a3b", + "provider": "qwen.qwen3-next-80b-a3b", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.15, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "qwen.qwen3-vl-235b-a22b", + "name": "qwen.qwen3-vl-235b-a22b", + "provider": "qwen.qwen3-vl-235b-a22b", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.53, + "pOut": 2.66, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "qwen.qwen3-coder-next", + "name": "qwen.qwen3-coder-next", + "provider": "qwen.qwen3-coder-next", + "ctx": 262144, + "maxOut": 8192, + "pIn": 0.5, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/meta/llama-2-13b", + "name": "replicate/meta/llama-2-13b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/meta/llama-2-13b-chat", + "name": "replicate/meta/llama-2-13b-chat", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/meta/llama-2-70b", + "name": "replicate/meta/llama-2-70b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.65, + "pOut": 2.75, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/meta/llama-2-70b-chat", + "name": "replicate/meta/llama-2-70b-chat", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.65, + "pOut": 2.75, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/meta/llama-2-7b", + "name": "replicate/meta/llama-2-7b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.049999999999999996, + "pOut": 0.25, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/meta/llama-2-7b-chat", + "name": "replicate/meta/llama-2-7b-chat", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.049999999999999996, + "pOut": 0.25, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/meta/llama-3-70b", + "name": "replicate/meta/llama-3-70b", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.65, + "pOut": 2.75, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/meta/llama-3-70b-instruct", + "name": "replicate/meta/llama-3-70b-instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.65, + "pOut": 2.75, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/meta/llama-3-8b", + "name": "replicate/meta/llama-3-8b", + "provider": "meta", + "ctx": 8086, + "maxOut": 8086, + "pIn": 0.049999999999999996, + "pOut": 0.25, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/meta/llama-3-8b-instruct", + "name": "replicate/meta/llama-3-8b-instruct", + "provider": "meta", + "ctx": 8086, + "maxOut": 8086, + "pIn": 0.049999999999999996, + "pOut": 0.25, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/mistralai/mistral-7b-instruct-v0.2", + "name": "replicate/mistralai/mistral-7b-instruct-v0.2", + "provider": "mistral", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.049999999999999996, + "pOut": 0.25, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/mistralai/mistral-7b-v0.1", + "name": "replicate/mistralai/mistral-7b-v0.1", + "provider": "mistral", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.049999999999999996, + "pOut": 0.25, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/mistralai/mixtral-8x7b-instruct-v0.1", + "name": "replicate/mistralai/mixtral-8x7b-instruct-v0.1", + "provider": "mistral", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.3, + "pOut": 1, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/deepseek-ai/deepseek-v3", + "name": "replicate/deepseek-ai/deepseek-v3", + "provider": "deepseek", + "ctx": 65536, + "maxOut": 8192, + "pIn": 1.4500000000000002, + "pOut": 1.4500000000000002, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/deepseek-ai/deepseek-v3.1", + "name": "replicate/deepseek-ai/deepseek-v3.1", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.6719999999999999, + "pOut": 2.016, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "replicate/deepseek-ai/deepseek-r1", + "name": "replicate/deepseek-ai/deepseek-r1", + "provider": "deepseek", + "ctx": 65536, + "maxOut": 8192, + "pIn": 3.75, + "pOut": 10, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "rerank-english-v2.0", + "name": "rerank-english-v2.0", + "provider": "rerank-english-v2.0", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "rerank-english-v3.0", + "name": "rerank-english-v3.0", + "provider": "rerank-english-v3.0", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "rerank-multilingual-v2.0", + "name": "rerank-multilingual-v2.0", + "provider": "rerank-multilingual-v2.0", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "rerank-multilingual-v3.0", + "name": "rerank-multilingual-v3.0", + "provider": "rerank-multilingual-v3.0", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "rerank-v3.5", + "name": "rerank-v3.5", + "provider": "rerank-v3.5", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sagemaker/meta-textgeneration-llama-2-13b", + "name": "sagemaker/meta-textgeneration-llama-2-13b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sagemaker/meta-textgeneration-llama-2-13b-f", + "name": "sagemaker/meta-textgeneration-llama-2-13b-f", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sagemaker/meta-textgeneration-llama-2-70b", + "name": "sagemaker/meta-textgeneration-llama-2-70b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sagemaker/meta-textgeneration-llama-2-70b-b-f", + "name": "sagemaker/meta-textgeneration-llama-2-70b-b-f", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sagemaker/meta-textgeneration-llama-2-7b", + "name": "sagemaker/meta-textgeneration-llama-2-7b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sagemaker/meta-textgeneration-llama-2-7b-f", + "name": "sagemaker/meta-textgeneration-llama-2-7b-f", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sambanova/MiniMax-M2.7", + "name": "sambanova/MiniMax-M2.7", + "provider": "sambanova", + "ctx": 204800, + "maxOut": 131072, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "sambanova/DeepSeek-R1", + "name": "sambanova/DeepSeek-R1", + "provider": "deepseek", + "ctx": 32768, + "maxOut": 32768, + "pIn": 5, + "pOut": 7, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sambanova/DeepSeek-R1-Distill-Llama-70B", + "name": "sambanova/DeepSeek-R1-Distill-Llama-70B", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.7, + "pOut": 1.4, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sambanova/DeepSeek-V3-0324", + "name": "sambanova/DeepSeek-V3-0324", + "provider": "deepseek", + "ctx": 32768, + "maxOut": 32768, + "pIn": 3, + "pOut": 4.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "sambanova/Llama-4-Maverick-17B-128E-Instruct", + "name": "sambanova/Llama-4-Maverick-17B-128E-Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.63, + "pOut": 1.7999999999999998, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "sambanova/Llama-4-Scout-17B-16E-Instruct", + "name": "sambanova/Llama-4-Scout-17B-16E-Instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.39999999999999997, + "pOut": 0.7, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "sambanova/Meta-Llama-3.1-405B-Instruct", + "name": "sambanova/Meta-Llama-3.1-405B-Instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "sambanova/Meta-Llama-3.1-8B-Instruct", + "name": "sambanova/Meta-Llama-3.1-8B-Instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.09999999999999999, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "sambanova/Meta-Llama-3.2-1B-Instruct", + "name": "sambanova/Meta-Llama-3.2-1B-Instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.04, + "pOut": 0.08, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sambanova/Meta-Llama-3.2-3B-Instruct", + "name": "sambanova/Meta-Llama-3.2-3B-Instruct", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.08, + "pOut": 0.16, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sambanova/Meta-Llama-3.3-70B-Instruct", + "name": "sambanova/Meta-Llama-3.3-70B-Instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.6, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "sambanova/Meta-Llama-Guard-3-8B", + "name": "sambanova/Meta-Llama-Guard-3-8B", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.3, + "pOut": 0.3, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sambanova/QwQ-32B", + "name": "sambanova/QwQ-32B", + "provider": "sambanova", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.5, + "pOut": 1, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sambanova/Qwen2-Audio-7B-Instruct", + "name": "sambanova/Qwen2-Audio-7B-Instruct", + "provider": "sambanova", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.5, + "pOut": 100, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sambanova/Qwen3-32B", + "name": "sambanova/Qwen3-32B", + "provider": "sambanova", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.39999999999999997, + "pOut": 0.7999999999999999, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "sambanova/DeepSeek-V3.1", + "name": "sambanova/DeepSeek-V3.1", + "provider": "deepseek", + "ctx": 32768, + "maxOut": 32768, + "pIn": 3, + "pOut": 4.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "sambanova/gpt-oss-120b", + "name": "sambanova/gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 3, + "pOut": 4.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/claude-3-5-sonnet", + "name": "snowflake/claude-3-5-sonnet", + "provider": "claude", + "ctx": 18000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/deepseek-r1", + "name": "snowflake/deepseek-r1", + "provider": "deepseek", + "ctx": 32768, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/gemma-7b", + "name": "snowflake/gemma-7b", + "provider": "snowflake", + "ctx": 8000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/jamba-1.5-large", + "name": "snowflake/jamba-1.5-large", + "provider": "snowflake", + "ctx": 256000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/jamba-1.5-mini", + "name": "snowflake/jamba-1.5-mini", + "provider": "snowflake", + "ctx": 256000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/jamba-instruct", + "name": "snowflake/jamba-instruct", + "provider": "snowflake", + "ctx": 256000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/llama2-70b-chat", + "name": "snowflake/llama2-70b-chat", + "provider": "meta", + "ctx": 4096, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/llama3-70b", + "name": "snowflake/llama3-70b", + "provider": "meta", + "ctx": 8000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/llama3-8b", + "name": "snowflake/llama3-8b", + "provider": "meta", + "ctx": 8000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/llama3.1-405b", + "name": "snowflake/llama3.1-405b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/llama3.1-70b", + "name": "snowflake/llama3.1-70b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/llama3.1-8b", + "name": "snowflake/llama3.1-8b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/llama3.2-1b", + "name": "snowflake/llama3.2-1b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/llama3.2-3b", + "name": "snowflake/llama3.2-3b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/llama3.3-70b", + "name": "snowflake/llama3.3-70b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/mistral-7b", + "name": "snowflake/mistral-7b", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/mistral-large", + "name": "snowflake/mistral-large", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/mistral-large2", + "name": "snowflake/mistral-large2", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/mixtral-8x7b", + "name": "snowflake/mixtral-8x7b", + "provider": "snowflake", + "ctx": 32000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/reka-core", + "name": "snowflake/reka-core", + "provider": "snowflake", + "ctx": 32000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/reka-flash", + "name": "snowflake/reka-flash", + "provider": "snowflake", + "ctx": 100000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/snowflake-arctic", + "name": "snowflake/snowflake-arctic", + "provider": "snowflake", + "ctx": 4096, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/snowflake-llama-3.1-405b", + "name": "snowflake/snowflake-llama-3.1-405b", + "provider": "meta", + "ctx": 8000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "snowflake/snowflake-llama-3.3-70b", + "name": "snowflake/snowflake-llama-3.3-70b", + "provider": "meta", + "ctx": 8000, + "maxOut": 8192, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "stability.sd3-5-large-v1:0", + "name": "stability.sd3-5-large-v1:0", + "provider": "stability.sd3-5-large-v1:0", + "ctx": 77, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "stability.sd3-large-v1:0", + "name": "stability.sd3-large-v1:0", + "provider": "stability.sd3-large-v1:0", + "ctx": 77, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "stability.stable-image-core-v1:0", + "name": "stability.stable-image-core-v1:0", + "provider": "stability.stable-image-core-v1:0", + "ctx": 77, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "stability.stable-image-core-v1:1", + "name": "stability.stable-image-core-v1:1", + "provider": "stability.stable-image-core-v1:1", + "ctx": 77, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "stability.stable-image-ultra-v1:0", + "name": "stability.stable-image-ultra-v1:0", + "provider": "stability.stable-image-ultra-v1:0", + "ctx": 77, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "stability.stable-image-ultra-v1:1", + "name": "stability.stable-image-ultra-v1:1", + "provider": "stability.stable-image-ultra-v1:1", + "ctx": 77, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "text-completion-codestral/codestral-2405", + "name": "text-completion-codestral/codestral-2405", + "provider": "text-completion-codestral", + "ctx": 32000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "text-completion-codestral/codestral-latest", + "name": "text-completion-codestral/codestral-latest", + "provider": "text-completion-codestral", + "ctx": 32000, + "maxOut": 8191, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "text-embedding-004", + "name": "text-embedding-004", + "provider": "text-embedding-004", + "ctx": 2048, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "text-embedding-005", + "name": "text-embedding-005", + "provider": "text-embedding-005", + "ctx": 2048, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "text-embedding-3-large", + "name": "text-embedding-3-large", + "provider": "text-embedding-3-large", + "ctx": 8191, + "maxOut": null, + "pIn": 0.13, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "text-embedding-3-small", + "name": "text-embedding-3-small", + "provider": "text-embedding-3-small", + "ctx": 8191, + "maxOut": null, + "pIn": 0.02, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "text-embedding-ada-002", + "name": "text-embedding-ada-002", + "provider": "text-embedding-ada-002", + "ctx": 8191, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "text-embedding-ada-002-v2", + "name": "text-embedding-ada-002-v2", + "provider": "text-embedding-ada-002-v2", + "ctx": 8191, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "text-embedding-large-exp-03-07", + "name": "text-embedding-large-exp-03-07", + "provider": "text-embedding-large-exp-03-07", + "ctx": 8192, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "text-embedding-preview-0409", + "name": "text-embedding-preview-0409", + "provider": "text-embedding-preview-0409", + "ctx": 3072, + "maxOut": null, + "pIn": 0.0062499999999999995, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "text-multilingual-embedding-002", + "name": "text-multilingual-embedding-002", + "provider": "text-multilingual-embedding-002", + "ctx": 2048, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "text-unicorn", + "name": "text-unicorn", + "provider": "text-unicorn", + "ctx": 8192, + "maxOut": 1024, + "pIn": 10, + "pOut": 28, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "text-unicorn@001", + "name": "text-unicorn@001", + "provider": "text-unicorn@001", + "ctx": 8192, + "maxOut": 1024, + "pIn": 10, + "pOut": 28, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "together-ai-8.1b-21b", + "name": "together-ai-8.1b-21b", + "provider": "together-ai-8.1b-21b", + "ctx": 1000, + "maxOut": null, + "pIn": 0.3, + "pOut": 0.3, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "together_ai/deepseek-ai/DeepSeek-R1", + "name": "together_ai/deepseek-ai/DeepSeek-R1", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 20480, + "pIn": 3, + "pOut": 7, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "together_ai/deepseek-ai/DeepSeek-V3", + "name": "together_ai/deepseek-ai/DeepSeek-V3", + "provider": "deepseek", + "ctx": 65536, + "maxOut": 8192, + "pIn": 1.25, + "pOut": 1.25, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "together_ai/deepseek-ai/DeepSeek-V3.1", + "name": "together_ai/deepseek-ai/DeepSeek-V3.1", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.6, + "pOut": 1.7, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "together_ai/openai/gpt-oss-120b", + "name": "together_ai/openai/gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "together_ai/zai-org/GLM-4.6", + "name": "together_ai/zai-org/GLM-4.6", + "provider": "together_ai", + "ctx": 200000, + "maxOut": 200000, + "pIn": 0.6, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "together_ai/zai-org/GLM-4.7", + "name": "together_ai/zai-org/GLM-4.7", + "provider": "together_ai", + "ctx": 200000, + "maxOut": 200000, + "pIn": 0.44999999999999996, + "pOut": 2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "together_ai/moonshotai/Kimi-K2.5", + "name": "together_ai/moonshotai/Kimi-K2.5", + "provider": "together_ai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.5, + "pOut": 2.8, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "us.amazon.nova-lite-v1:0", + "name": "us.amazon.nova-lite-v1:0", + "provider": "us.amazon.nova-lite-v1:0", + "ctx": 300000, + "maxOut": 10000, + "pIn": 0.06, + "pOut": 0.24, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "us.amazon.nova-micro-v1:0", + "name": "us.amazon.nova-micro-v1:0", + "provider": "us.amazon.nova-micro-v1:0", + "ctx": 128000, + "maxOut": 10000, + "pIn": 0.035, + "pOut": 0.14, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "us.amazon.nova-premier-v1:0", + "name": "us.amazon.nova-premier-v1:0", + "provider": "us.amazon.nova-premier-v1:0", + "ctx": 1000000, + "maxOut": 10000, + "pIn": 2.5, + "pOut": 12.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "us.amazon.nova-pro-v1:0", + "name": "us.amazon.nova-pro-v1:0", + "provider": "us.amazon.nova-pro-v1:0", + "ctx": 300000, + "maxOut": 10000, + "pIn": 0.7999999999999999, + "pOut": 3.1999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-3-5-haiku-20241022-v1:0", + "name": "us.anthropic.claude-3-5-haiku-20241022-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 0.7999999999999999, + "pOut": 4, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "us.anthropic.claude-haiku-4-5-20251001-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1.1, + "pOut": 5.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-3-5-sonnet-20240620-v1:0", + "name": "us.anthropic.claude-3-5-sonnet-20240620-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-3-5-sonnet-20241022-v2:0", + "name": "us.anthropic.claude-3-5-sonnet-20241022-v2:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-3-7-sonnet-20250219-v1:0", + "name": "us.anthropic.claude-3-7-sonnet-20250219-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-3-haiku-20240307-v1:0", + "name": "us.anthropic.claude-3-haiku-20240307-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 0.25, + "pOut": 1.25, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-3-opus-20240229-v1:0", + "name": "us.anthropic.claude-3-opus-20240229-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-3-sonnet-20240229-v1:0", + "name": "us.anthropic.claude-3-sonnet-20240229-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-opus-4-1-20250805-v1:0", + "name": "us.anthropic.claude-opus-4-1-20250805-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "us.anthropic.claude-sonnet-4-5-20250929-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0", + "name": "us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3.3000000000000003, + "pOut": 16.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "au.anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "au.anthropic.claude-haiku-4-5-20251001-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1.1, + "pOut": 5.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-opus-4-20250514-v1:0", + "name": "us.anthropic.claude-opus-4-20250514-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-opus-4-5-20251101-v1:0", + "name": "us.anthropic.claude-opus-4-5-20251101-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 5.5, + "pOut": 27.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "global.anthropic.claude-opus-4-5-20251101-v1:0", + "name": "global.anthropic.claude-opus-4-5-20251101-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "eu.anthropic.claude-opus-4-5-20251101-v1:0", + "name": "eu.anthropic.claude-opus-4-5-20251101-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us.anthropic.claude-sonnet-4-20250514-v1:0", + "name": "us.anthropic.claude-sonnet-4-20250514-v1:0", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "us.deepseek.r1-v1:0", + "name": "us.deepseek.r1-v1:0", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 4096, + "pIn": 1.35, + "pOut": 5.4, + "tools": false, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "us.deepseek.v3.2", + "name": "us.deepseek.v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.62, + "pOut": 1.85, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "eu.deepseek.v3.2", + "name": "eu.deepseek.v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.74, + "pOut": 2.2199999999999998, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "us.meta.llama3-1-405b-instruct-v1:0", + "name": "us.meta.llama3-1-405b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 5.32, + "pOut": 16, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "us.meta.llama3-1-70b-instruct-v1:0", + "name": "us.meta.llama3-1-70b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": 0.9900000000000001, + "pOut": 0.9900000000000001, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "us.meta.llama3-1-8b-instruct-v1:0", + "name": "us.meta.llama3-1-8b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": 0.22, + "pOut": 0.22, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "us.meta.llama3-2-11b-instruct-v1:0", + "name": "us.meta.llama3-2-11b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.35, + "pOut": 0.35, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "us.meta.llama3-2-1b-instruct-v1:0", + "name": "us.meta.llama3-2-1b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "us.meta.llama3-2-3b-instruct-v1:0", + "name": "us.meta.llama3-2-3b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "us.meta.llama3-2-90b-instruct-v1:0", + "name": "us.meta.llama3-2-90b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 2, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "us.meta.llama3-3-70b-instruct-v1:0", + "name": "us.meta.llama3-3-70b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.72, + "pOut": 0.72, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "us.meta.llama4-maverick-17b-instruct-v1:0", + "name": "us.meta.llama4-maverick-17b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.24, + "pOut": 0.9700000000000001, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "us.meta.llama4-scout-17b-instruct-v1:0", + "name": "us.meta.llama4-scout-17b-instruct-v1:0", + "provider": "meta", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.16999999999999998, + "pOut": 0.66, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "us.mistral.pixtral-large-2502-v1:0", + "name": "us.mistral.pixtral-large-2502-v1:0", + "provider": "mistral", + "ctx": 128000, + "maxOut": 4096, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "v0/v0-1.0-md", + "name": "v0/v0-1.0-md", + "provider": "v0", + "ctx": 128000, + "maxOut": 128000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "v0/v0-1.5-lg", + "name": "v0/v0-1.5-lg", + "provider": "v0", + "ctx": 512000, + "maxOut": 512000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "v0/v0-1.5-md", + "name": "v0/v0-1.5-md", + "provider": "v0", + "ctx": 128000, + "maxOut": 128000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/alibaba/qwen-3-14b", + "name": "vercel_ai_gateway/alibaba/qwen-3-14b", + "provider": "vercel_ai_gateway", + "ctx": 40960, + "maxOut": 16384, + "pIn": 0.08, + "pOut": 0.24, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/alibaba/qwen-3-235b", + "name": "vercel_ai_gateway/alibaba/qwen-3-235b", + "provider": "vercel_ai_gateway", + "ctx": 40960, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/alibaba/qwen-3-30b", + "name": "vercel_ai_gateway/alibaba/qwen-3-30b", + "provider": "vercel_ai_gateway", + "ctx": 40960, + "maxOut": 16384, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/alibaba/qwen-3-32b", + "name": "vercel_ai_gateway/alibaba/qwen-3-32b", + "provider": "vercel_ai_gateway", + "ctx": 40960, + "maxOut": 16384, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/alibaba/qwen3-coder", + "name": "vercel_ai_gateway/alibaba/qwen3-coder", + "provider": "vercel_ai_gateway", + "ctx": 262144, + "maxOut": 66536, + "pIn": 0.39999999999999997, + "pOut": 1.5999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/amazon/nova-lite", + "name": "vercel_ai_gateway/amazon/nova-lite", + "provider": "vercel_ai_gateway", + "ctx": 300000, + "maxOut": 8192, + "pIn": 0.06, + "pOut": 0.24, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/amazon/nova-micro", + "name": "vercel_ai_gateway/amazon/nova-micro", + "provider": "vercel_ai_gateway", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.035, + "pOut": 0.14, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/amazon/nova-pro", + "name": "vercel_ai_gateway/amazon/nova-pro", + "provider": "vercel_ai_gateway", + "ctx": 300000, + "maxOut": 8192, + "pIn": 0.7999999999999999, + "pOut": 3.1999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-3-haiku", + "name": "vercel_ai_gateway/anthropic/claude-3-haiku", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 0.25, + "pOut": 1.25, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-3-opus", + "name": "vercel_ai_gateway/anthropic/claude-3-opus", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-3.5-haiku", + "name": "vercel_ai_gateway/anthropic/claude-3.5-haiku", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 0.7999999999999999, + "pOut": 4, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-3.5-sonnet", + "name": "vercel_ai_gateway/anthropic/claude-3.5-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-3.7-sonnet", + "name": "vercel_ai_gateway/anthropic/claude-3.7-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-4-opus", + "name": "vercel_ai_gateway/anthropic/claude-4-opus", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-4-sonnet", + "name": "vercel_ai_gateway/anthropic/claude-4-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-3-5-sonnet", + "name": "vercel_ai_gateway/anthropic/claude-3-5-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-3-5-sonnet-20241022", + "name": "vercel_ai_gateway/anthropic/claude-3-5-sonnet-20241022", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-3-7-sonnet", + "name": "vercel_ai_gateway/anthropic/claude-3-7-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-haiku-4.5", + "name": "vercel_ai_gateway/anthropic/claude-haiku-4.5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-opus-4", + "name": "vercel_ai_gateway/anthropic/claude-opus-4", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-opus-4.1", + "name": "vercel_ai_gateway/anthropic/claude-opus-4.1", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-opus-4.5", + "name": "vercel_ai_gateway/anthropic/claude-opus-4.5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-opus-4.6", + "name": "vercel_ai_gateway/anthropic/claude-opus-4.6", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-sonnet-4", + "name": "vercel_ai_gateway/anthropic/claude-sonnet-4", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/anthropic/claude-sonnet-4.5", + "name": "vercel_ai_gateway/anthropic/claude-sonnet-4.5", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/cohere/command-a", + "name": "vercel_ai_gateway/cohere/command-a", + "provider": "cohere", + "ctx": 256000, + "maxOut": 8000, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/cohere/command-r", + "name": "vercel_ai_gateway/cohere/command-r", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/cohere/command-r-plus", + "name": "vercel_ai_gateway/cohere/command-r-plus", + "provider": "cohere", + "ctx": 128000, + "maxOut": 4096, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/deepseek/deepseek-r1", + "name": "vercel_ai_gateway/deepseek/deepseek-r1", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.55, + "pOut": 2.1900000000000004, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/deepseek/deepseek-r1-distill-llama-70b", + "name": "vercel_ai_gateway/deepseek/deepseek-r1-distill-llama-70b", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.75, + "pOut": 0.9900000000000001, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/deepseek/deepseek-v3", + "name": "vercel_ai_gateway/deepseek/deepseek-v3", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/google/gemini-2.0-flash", + "name": "vercel_ai_gateway/google/gemini-2.0-flash", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/google/gemini-2.0-flash-lite", + "name": "vercel_ai_gateway/google/gemini-2.0-flash-lite", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.075, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/google/gemini-2.5-flash", + "name": "vercel_ai_gateway/google/gemini-2.5-flash", + "provider": "gemini", + "ctx": 1000000, + "maxOut": 65536, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/google/gemini-2.5-pro", + "name": "vercel_ai_gateway/google/gemini-2.5-pro", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/google/gemma-2-9b", + "name": "vercel_ai_gateway/google/gemma-2-9b", + "provider": "gemini", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/inception/mercury-coder-small", + "name": "vercel_ai_gateway/inception/mercury-coder-small", + "provider": "vercel_ai_gateway", + "ctx": 32000, + "maxOut": 16384, + "pIn": 0.25, + "pOut": 1, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/meta/llama-3-70b", + "name": "vercel_ai_gateway/meta/llama-3-70b", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.59, + "pOut": 0.7899999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/meta/llama-3-8b", + "name": "vercel_ai_gateway/meta/llama-3-8b", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.049999999999999996, + "pOut": 0.08, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/meta/llama-3.1-70b", + "name": "vercel_ai_gateway/meta/llama-3.1-70b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.72, + "pOut": 0.72, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/meta/llama-3.1-8b", + "name": "vercel_ai_gateway/meta/llama-3.1-8b", + "provider": "meta", + "ctx": 131000, + "maxOut": 131072, + "pIn": 0.049999999999999996, + "pOut": 0.08, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/meta/llama-3.2-11b", + "name": "vercel_ai_gateway/meta/llama-3.2-11b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.16, + "pOut": 0.16, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/meta/llama-3.2-1b", + "name": "vercel_ai_gateway/meta/llama-3.2-1b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/meta/llama-3.2-3b", + "name": "vercel_ai_gateway/meta/llama-3.2-3b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/meta/llama-3.2-90b", + "name": "vercel_ai_gateway/meta/llama-3.2-90b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.72, + "pOut": 0.72, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/meta/llama-3.3-70b", + "name": "vercel_ai_gateway/meta/llama-3.3-70b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.72, + "pOut": 0.72, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/meta/llama-4-maverick", + "name": "vercel_ai_gateway/meta/llama-4-maverick", + "provider": "meta", + "ctx": 131072, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/meta/llama-4-scout", + "name": "vercel_ai_gateway/meta/llama-4-scout", + "provider": "meta", + "ctx": 131072, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/mistral/codestral", + "name": "vercel_ai_gateway/mistral/codestral", + "provider": "mistral", + "ctx": 256000, + "maxOut": 4000, + "pIn": 0.3, + "pOut": 0.8999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/mistral/devstral-small", + "name": "vercel_ai_gateway/mistral/devstral-small", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.07, + "pOut": 0.28, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/mistral/magistral-medium", + "name": "vercel_ai_gateway/mistral/magistral-medium", + "provider": "mistral", + "ctx": 128000, + "maxOut": 64000, + "pIn": 2, + "pOut": 5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/mistral/magistral-small", + "name": "vercel_ai_gateway/mistral/magistral-small", + "provider": "mistral", + "ctx": 128000, + "maxOut": 64000, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/mistral/ministral-3b", + "name": "vercel_ai_gateway/mistral/ministral-3b", + "provider": "mistral", + "ctx": 128000, + "maxOut": 4000, + "pIn": 0.04, + "pOut": 0.04, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/mistral/ministral-8b", + "name": "vercel_ai_gateway/mistral/ministral-8b", + "provider": "mistral", + "ctx": 128000, + "maxOut": 4000, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/mistral/mistral-large", + "name": "vercel_ai_gateway/mistral/mistral-large", + "provider": "mistral", + "ctx": 32000, + "maxOut": 4000, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/mistral/mistral-saba-24b", + "name": "vercel_ai_gateway/mistral/mistral-saba-24b", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.7899999999999999, + "pOut": 0.7899999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/mistral/mistral-small", + "name": "vercel_ai_gateway/mistral/mistral-small", + "provider": "mistral", + "ctx": 32000, + "maxOut": 4000, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/mistral/mixtral-8x22b-instruct", + "name": "vercel_ai_gateway/mistral/mixtral-8x22b-instruct", + "provider": "mistral", + "ctx": 65536, + "maxOut": 2048, + "pIn": 1.2, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/mistral/pixtral-12b", + "name": "vercel_ai_gateway/mistral/pixtral-12b", + "provider": "mistral", + "ctx": 128000, + "maxOut": 4000, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/mistral/pixtral-large", + "name": "vercel_ai_gateway/mistral/pixtral-large", + "provider": "mistral", + "ctx": 128000, + "maxOut": 4000, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/moonshotai/kimi-k2", + "name": "vercel_ai_gateway/moonshotai/kimi-k2", + "provider": "vercel_ai_gateway", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.55, + "pOut": 2.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/morph/morph-v3-fast", + "name": "vercel_ai_gateway/morph/morph-v3-fast", + "provider": "vercel_ai_gateway", + "ctx": 32768, + "maxOut": 16384, + "pIn": 0.7999999999999999, + "pOut": 1.2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/morph/morph-v3-large", + "name": "vercel_ai_gateway/morph/morph-v3-large", + "provider": "vercel_ai_gateway", + "ctx": 32768, + "maxOut": 16384, + "pIn": 0.8999999999999999, + "pOut": 1.9, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/openai/gpt-3.5-turbo", + "name": "vercel_ai_gateway/openai/gpt-3.5-turbo", + "provider": "openai", + "ctx": 16385, + "maxOut": 4096, + "pIn": 0.5, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/openai/gpt-3.5-turbo-instruct", + "name": "vercel_ai_gateway/openai/gpt-3.5-turbo-instruct", + "provider": "openai", + "ctx": 8192, + "maxOut": 4096, + "pIn": 1.5, + "pOut": 2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/openai/gpt-4-turbo", + "name": "vercel_ai_gateway/openai/gpt-4-turbo", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 10, + "pOut": 30, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/openai/gpt-4.1", + "name": "vercel_ai_gateway/openai/gpt-4.1", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/openai/gpt-4.1-mini", + "name": "vercel_ai_gateway/openai/gpt-4.1-mini", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.39999999999999997, + "pOut": 1.5999999999999999, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/openai/gpt-4.1-nano", + "name": "vercel_ai_gateway/openai/gpt-4.1-nano", + "provider": "openai", + "ctx": 1047576, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/openai/gpt-4o", + "name": "vercel_ai_gateway/openai/gpt-4o", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 2.5, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/openai/gpt-4o-mini", + "name": "vercel_ai_gateway/openai/gpt-4o-mini", + "provider": "openai", + "ctx": 128000, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/openai/o1", + "name": "vercel_ai_gateway/openai/o1", + "provider": "vercel_ai_gateway", + "ctx": 200000, + "maxOut": 100000, + "pIn": 15, + "pOut": 60, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/openai/o3", + "name": "vercel_ai_gateway/openai/o3", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 2, + "pOut": 8, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/openai/o3-mini", + "name": "vercel_ai_gateway/openai/o3-mini", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/openai/o4-mini", + "name": "vercel_ai_gateway/openai/o4-mini", + "provider": "openai", + "ctx": 200000, + "maxOut": 100000, + "pIn": 1.1, + "pOut": 4.4, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/perplexity/sonar", + "name": "vercel_ai_gateway/perplexity/sonar", + "provider": "vercel_ai_gateway", + "ctx": 127000, + "maxOut": 8000, + "pIn": 1, + "pOut": 1, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/perplexity/sonar-pro", + "name": "vercel_ai_gateway/perplexity/sonar-pro", + "provider": "vercel_ai_gateway", + "ctx": 200000, + "maxOut": 8000, + "pIn": 3, + "pOut": 15, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/perplexity/sonar-reasoning", + "name": "vercel_ai_gateway/perplexity/sonar-reasoning", + "provider": "vercel_ai_gateway", + "ctx": 127000, + "maxOut": 8000, + "pIn": 1, + "pOut": 5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/perplexity/sonar-reasoning-pro", + "name": "vercel_ai_gateway/perplexity/sonar-reasoning-pro", + "provider": "vercel_ai_gateway", + "ctx": 127000, + "maxOut": 8000, + "pIn": 2, + "pOut": 8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/vercel/v0-1.0-md", + "name": "vercel_ai_gateway/vercel/v0-1.0-md", + "provider": "vercel_ai_gateway", + "ctx": 128000, + "maxOut": 32000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/vercel/v0-1.5-md", + "name": "vercel_ai_gateway/vercel/v0-1.5-md", + "provider": "vercel_ai_gateway", + "ctx": 128000, + "maxOut": 32768, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/xai/grok-2", + "name": "vercel_ai_gateway/xai/grok-2", + "provider": "vercel_ai_gateway", + "ctx": 131072, + "maxOut": 4000, + "pIn": 2, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/xai/grok-2-vision", + "name": "vercel_ai_gateway/xai/grok-2-vision", + "provider": "vercel_ai_gateway", + "ctx": 32768, + "maxOut": 32768, + "pIn": 2, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/xai/grok-3", + "name": "vercel_ai_gateway/xai/grok-3", + "provider": "vercel_ai_gateway", + "ctx": 131072, + "maxOut": 131072, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/xai/grok-3-fast", + "name": "vercel_ai_gateway/xai/grok-3-fast", + "provider": "vercel_ai_gateway", + "ctx": 131072, + "maxOut": 131072, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/xai/grok-3-mini", + "name": "vercel_ai_gateway/xai/grok-3-mini", + "provider": "vercel_ai_gateway", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.3, + "pOut": 0.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/xai/grok-3-mini-fast", + "name": "vercel_ai_gateway/xai/grok-3-mini-fast", + "provider": "vercel_ai_gateway", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.6, + "pOut": 4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/xai/grok-4", + "name": "vercel_ai_gateway/xai/grok-4", + "provider": "vercel_ai_gateway", + "ctx": 256000, + "maxOut": 256000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/zai/glm-4.5", + "name": "vercel_ai_gateway/zai/glm-4.5", + "provider": "vercel_ai_gateway", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.6, + "pOut": 2.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/zai/glm-4.5-air", + "name": "vercel_ai_gateway/zai/glm-4.5-air", + "provider": "vercel_ai_gateway", + "ctx": 128000, + "maxOut": 96000, + "pIn": 0.19999999999999998, + "pOut": 1.1, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vercel_ai_gateway/zai/glm-4.6", + "name": "vercel_ai_gateway/zai/glm-4.6", + "provider": "vercel_ai_gateway", + "ctx": 200000, + "maxOut": 200000, + "pIn": 0.44999999999999996, + "pOut": 1.7999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-3-5-haiku", + "name": "vertex_ai/claude-3-5-haiku", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-3-5-haiku@20241022", + "name": "vertex_ai/claude-3-5-haiku@20241022", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-haiku-4-5", + "name": "vertex_ai/claude-haiku-4-5", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-haiku-4-5@20251001", + "name": "vertex_ai/claude-haiku-4-5@20251001", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 1, + "pOut": 5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-3-5-sonnet", + "name": "vertex_ai/claude-3-5-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-3-5-sonnet@20240620", + "name": "vertex_ai/claude-3-5-sonnet@20240620", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-3-7-sonnet@20250219", + "name": "vertex_ai/claude-3-7-sonnet@20250219", + "provider": "claude", + "ctx": 200000, + "maxOut": 8192, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-3-haiku", + "name": "vertex_ai/claude-3-haiku", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 0.25, + "pOut": 1.25, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-3-haiku@20240307", + "name": "vertex_ai/claude-3-haiku@20240307", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 0.25, + "pOut": 1.25, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-3-opus", + "name": "vertex_ai/claude-3-opus", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-3-opus@20240229", + "name": "vertex_ai/claude-3-opus@20240229", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-3-sonnet", + "name": "vertex_ai/claude-3-sonnet", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-3-sonnet@20240229", + "name": "vertex_ai/claude-3-sonnet@20240229", + "provider": "claude", + "ctx": 200000, + "maxOut": 4096, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-opus-4", + "name": "vertex_ai/claude-opus-4", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-opus-4-1", + "name": "vertex_ai/claude-opus-4-1", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-opus-4-1@20250805", + "name": "vertex_ai/claude-opus-4-1@20250805", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-opus-4-5", + "name": "vertex_ai/claude-opus-4-5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-opus-4-5@20251101", + "name": "vertex_ai/claude-opus-4-5@20251101", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-opus-4-6", + "name": "vertex_ai/claude-opus-4-6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-opus-4-6@default", + "name": "vertex_ai/claude-opus-4-6@default", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-opus-4-7", + "name": "vertex_ai/claude-opus-4-7", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-opus-4-7@default", + "name": "vertex_ai/claude-opus-4-7@default", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-opus-4-8", + "name": "vertex_ai/claude-opus-4-8", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-opus-4-8@default", + "name": "vertex_ai/claude-opus-4-8@default", + "provider": "claude", + "ctx": 1000000, + "maxOut": 128000, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-sonnet-4-5", + "name": "vertex_ai/claude-sonnet-4-5", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-sonnet-4-6", + "name": "vertex_ai/claude-sonnet-4-6", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-sonnet-4-5@20250929", + "name": "vertex_ai/claude-sonnet-4-5@20250929", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-opus-4@20250514", + "name": "vertex_ai/claude-opus-4@20250514", + "provider": "claude", + "ctx": 200000, + "maxOut": 32000, + "pIn": 15, + "pOut": 75, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-sonnet-4", + "name": "vertex_ai/claude-sonnet-4", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-sonnet-4@20250514", + "name": "vertex_ai/claude-sonnet-4@20250514", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/mistralai/codestral-2@001", + "name": "vertex_ai/mistralai/codestral-2@001", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.3, + "pOut": 0.8999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/codestral-2", + "name": "vertex_ai/codestral-2", + "provider": "vertex_ai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.3, + "pOut": 0.8999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/codestral-2@001", + "name": "vertex_ai/codestral-2@001", + "provider": "vertex_ai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.3, + "pOut": 0.8999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/mistralai/codestral-2", + "name": "vertex_ai/mistralai/codestral-2", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.3, + "pOut": 0.8999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/codestral-2501", + "name": "vertex_ai/codestral-2501", + "provider": "vertex_ai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/codestral@2405", + "name": "vertex_ai/codestral@2405", + "provider": "vertex_ai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/codestral@latest", + "name": "vertex_ai/codestral@latest", + "provider": "vertex_ai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/deepseek-ai/deepseek-v3.1-maas", + "name": "vertex_ai/deepseek-ai/deepseek-v3.1-maas", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 32768, + "pIn": 1.35, + "pOut": 5.4, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/deepseek-ai/deepseek-v3.2-maas", + "name": "vertex_ai/deepseek-ai/deepseek-v3.2-maas", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 32768, + "pIn": 0.56, + "pOut": 1.68, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/deepseek-ai/deepseek-r1-0528-maas", + "name": "vertex_ai/deepseek-ai/deepseek-r1-0528-maas", + "provider": "deepseek", + "ctx": 65336, + "maxOut": 8192, + "pIn": 1.35, + "pOut": 5.4, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/gemini-2.5-flash-image", + "name": "vertex_ai/gemini-2.5-flash-image", + "provider": "gemini", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/gemini-3-pro-image-preview", + "name": "vertex_ai/gemini-3-pro-image-preview", + "provider": "gemini", + "ctx": 65536, + "maxOut": 32768, + "pIn": 2, + "pOut": 12, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/gemini-3.1-flash-image-preview", + "name": "vertex_ai/gemini-3.1-flash-image-preview", + "provider": "gemini", + "ctx": 65536, + "maxOut": 32768, + "pIn": 0.5, + "pOut": 3, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/gemini-3.1-flash-lite-preview", + "name": "vertex_ai/gemini-3.1-flash-lite-preview", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.25, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/gemini-3.1-flash-lite", + "name": "vertex_ai/gemini-3.1-flash-lite", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65536, + "pIn": 0.25, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/deep-research-pro-preview-12-2025", + "name": "vertex_ai/deep-research-pro-preview-12-2025", + "provider": "vertex_ai", + "ctx": 65536, + "maxOut": 32768, + "pIn": 2, + "pOut": 12, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/jamba-1.5", + "name": "vertex_ai/jamba-1.5", + "provider": "vertex_ai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 0.39999999999999997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/jamba-1.5-large", + "name": "vertex_ai/jamba-1.5-large", + "provider": "vertex_ai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 2, + "pOut": 8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/jamba-1.5-large@001", + "name": "vertex_ai/jamba-1.5-large@001", + "provider": "vertex_ai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 2, + "pOut": 8, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/jamba-1.5-mini", + "name": "vertex_ai/jamba-1.5-mini", + "provider": "vertex_ai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 0.39999999999999997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/jamba-1.5-mini@001", + "name": "vertex_ai/jamba-1.5-mini@001", + "provider": "vertex_ai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 0.39999999999999997, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/meta/llama-3.1-405b-instruct-maas", + "name": "vertex_ai/meta/llama-3.1-405b-instruct-maas", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": 5, + "pOut": 16, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/meta/llama-3.1-70b-instruct-maas", + "name": "vertex_ai/meta/llama-3.1-70b-instruct-maas", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/meta/llama-3.1-8b-instruct-maas", + "name": "vertex_ai/meta/llama-3.1-8b-instruct-maas", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/meta/llama-3.2-90b-vision-instruct-maas", + "name": "vertex_ai/meta/llama-3.2-90b-vision-instruct-maas", + "provider": "meta", + "ctx": 128000, + "maxOut": 2048, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/meta/llama-4-maverick-17b-128e-instruct-maas", + "name": "vertex_ai/meta/llama-4-maverick-17b-128e-instruct-maas", + "provider": "meta", + "ctx": 1000000, + "maxOut": 1000000, + "pIn": 0.35, + "pOut": 1.15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/meta/llama-4-maverick-17b-16e-instruct-maas", + "name": "vertex_ai/meta/llama-4-maverick-17b-16e-instruct-maas", + "provider": "meta", + "ctx": 1000000, + "maxOut": 1000000, + "pIn": 0.35, + "pOut": 1.15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/meta/llama-4-scout-17b-128e-instruct-maas", + "name": "vertex_ai/meta/llama-4-scout-17b-128e-instruct-maas", + "provider": "meta", + "ctx": 10000000, + "maxOut": 10000000, + "pIn": 0.25, + "pOut": 0.7, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/meta/llama-4-scout-17b-16e-instruct-maas", + "name": "vertex_ai/meta/llama-4-scout-17b-16e-instruct-maas", + "provider": "meta", + "ctx": 10000000, + "maxOut": 10000000, + "pIn": 0.25, + "pOut": 0.7, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/meta/llama3-405b-instruct-maas", + "name": "vertex_ai/meta/llama3-405b-instruct-maas", + "provider": "meta", + "ctx": 32000, + "maxOut": 32000, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/meta/llama3-70b-instruct-maas", + "name": "vertex_ai/meta/llama3-70b-instruct-maas", + "provider": "meta", + "ctx": 32000, + "maxOut": 32000, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/meta/llama3-8b-instruct-maas", + "name": "vertex_ai/meta/llama3-8b-instruct-maas", + "provider": "meta", + "ctx": 32000, + "maxOut": 32000, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/minimaxai/minimax-m2-maas", + "name": "vertex_ai/minimaxai/minimax-m2-maas", + "provider": "vertex_ai", + "ctx": 196608, + "maxOut": 196608, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/moonshotai/kimi-k2-thinking-maas", + "name": "vertex_ai/moonshotai/kimi-k2-thinking-maas", + "provider": "vertex_ai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/zai-org/glm-4.7-maas", + "name": "vertex_ai/zai-org/glm-4.7-maas", + "provider": "vertex_ai", + "ctx": 200000, + "maxOut": 128000, + "pIn": 0.6, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/zai-org/glm-5-maas", + "name": "vertex_ai/zai-org/glm-5-maas", + "provider": "vertex_ai", + "ctx": 200000, + "maxOut": 128000, + "pIn": 1, + "pOut": 3.1999999999999997, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/mistral-medium-3", + "name": "vertex_ai/mistral-medium-3", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8191, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/mistral-medium-3@001", + "name": "vertex_ai/mistral-medium-3@001", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8191, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/mistralai/mistral-medium-3", + "name": "vertex_ai/mistralai/mistral-medium-3", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8191, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/mistralai/mistral-medium-3@001", + "name": "vertex_ai/mistralai/mistral-medium-3@001", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8191, + "pIn": 0.39999999999999997, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/mistral-large-2411", + "name": "vertex_ai/mistral-large-2411", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8191, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/mistral-large@2407", + "name": "vertex_ai/mistral-large@2407", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8191, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/mistral-large@2411-001", + "name": "vertex_ai/mistral-large@2411-001", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8191, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/mistral-large@latest", + "name": "vertex_ai/mistral-large@latest", + "provider": "mistral", + "ctx": 128000, + "maxOut": 8191, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/mistral-nemo@2407", + "name": "vertex_ai/mistral-nemo@2407", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 3, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/mistral-nemo@latest", + "name": "vertex_ai/mistral-nemo@latest", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/mistral-small-2503", + "name": "vertex_ai/mistral-small-2503", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/mistral-small-2503@001", + "name": "vertex_ai/mistral-small-2503@001", + "provider": "mistral", + "ctx": 32000, + "maxOut": 8191, + "pIn": 1, + "pOut": 3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/openai/gpt-oss-120b-maas", + "name": "vertex_ai/openai/gpt-oss-120b-maas", + "provider": "openai", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.15, + "pOut": 0.6, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/openai/gpt-oss-20b-maas", + "name": "vertex_ai/openai/gpt-oss-20b-maas", + "provider": "openai", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.075, + "pOut": 0.3, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/xai/grok-4.1-fast-non-reasoning", + "name": "vertex_ai/xai/grok-4.1-fast-non-reasoning", + "provider": "vertex_ai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 0.19999999999999998, + "pOut": 0.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/xai/grok-4.1-fast-reasoning", + "name": "vertex_ai/xai/grok-4.1-fast-reasoning", + "provider": "vertex_ai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 0.19999999999999998, + "pOut": 0.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/xai/grok-4.20-non-reasoning", + "name": "vertex_ai/xai/grok-4.20-non-reasoning", + "provider": "vertex_ai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/xai/grok-4.20-reasoning", + "name": "vertex_ai/xai/grok-4.20-reasoning", + "provider": "vertex_ai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/qwen/qwen3-235b-a22b-instruct-2507-maas", + "name": "vertex_ai/qwen/qwen3-235b-a22b-instruct-2507-maas", + "provider": "vertex_ai", + "ctx": 262144, + "maxOut": 16384, + "pIn": 0.25, + "pOut": 1, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/qwen/qwen3-coder-480b-a35b-instruct-maas", + "name": "vertex_ai/qwen/qwen3-coder-480b-a35b-instruct-maas", + "provider": "vertex_ai", + "ctx": 262144, + "maxOut": 32768, + "pIn": 1, + "pOut": 4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/qwen/qwen3-next-80b-a3b-instruct-maas", + "name": "vertex_ai/qwen/qwen3-next-80b-a3b-instruct-maas", + "provider": "vertex_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.15, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/qwen/qwen3-next-80b-a3b-thinking-maas", + "name": "vertex_ai/qwen/qwen3-next-80b-a3b-thinking-maas", + "provider": "vertex_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.15, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/veo-2.0-generate-001", + "name": "vertex_ai/veo-2.0-generate-001", + "provider": "vertex_ai", + "ctx": 1024, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/veo-3.0-fast-generate-001", + "name": "vertex_ai/veo-3.0-fast-generate-001", + "provider": "vertex_ai", + "ctx": 1024, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/veo-3.0-generate-001", + "name": "vertex_ai/veo-3.0-generate-001", + "provider": "vertex_ai", + "ctx": 1024, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/veo-3.1-generate-preview", + "name": "vertex_ai/veo-3.1-generate-preview", + "provider": "vertex_ai", + "ctx": 1024, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/veo-3.1-fast-generate-preview", + "name": "vertex_ai/veo-3.1-fast-generate-preview", + "provider": "vertex_ai", + "ctx": 1024, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/veo-3.1-generate-001", + "name": "vertex_ai/veo-3.1-generate-001", + "provider": "vertex_ai", + "ctx": 1024, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "vertex_ai/veo-3.1-fast-generate-001", + "name": "vertex_ai/veo-3.1-fast-generate-001", + "provider": "vertex_ai", + "ctx": 1024, + "maxOut": null, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/rerank-2", + "name": "voyage/rerank-2", + "provider": "voyage", + "ctx": 16000, + "maxOut": 16000, + "pIn": 0.049999999999999996, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/rerank-2-lite", + "name": "voyage/rerank-2-lite", + "provider": "voyage", + "ctx": 8000, + "maxOut": 8000, + "pIn": 0.02, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/rerank-2.5", + "name": "voyage/rerank-2.5", + "provider": "voyage", + "ctx": 32000, + "maxOut": 32000, + "pIn": 0.049999999999999996, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/rerank-2.5-lite", + "name": "voyage/rerank-2.5-lite", + "provider": "voyage", + "ctx": 32000, + "maxOut": 32000, + "pIn": 0.02, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-2", + "name": "voyage/voyage-2", + "provider": "voyage", + "ctx": 4000, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-3", + "name": "voyage/voyage-3", + "provider": "voyage", + "ctx": 32000, + "maxOut": null, + "pIn": 0.06, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-3-large", + "name": "voyage/voyage-3-large", + "provider": "voyage", + "ctx": 32000, + "maxOut": null, + "pIn": 0.18, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-3-lite", + "name": "voyage/voyage-3-lite", + "provider": "voyage", + "ctx": 32000, + "maxOut": null, + "pIn": 0.02, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-3.5", + "name": "voyage/voyage-3.5", + "provider": "voyage", + "ctx": 32000, + "maxOut": null, + "pIn": 0.06, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-3.5-lite", + "name": "voyage/voyage-3.5-lite", + "provider": "voyage", + "ctx": 32000, + "maxOut": null, + "pIn": 0.02, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-code-2", + "name": "voyage/voyage-code-2", + "provider": "voyage", + "ctx": 16000, + "maxOut": null, + "pIn": 0.12, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-code-3", + "name": "voyage/voyage-code-3", + "provider": "voyage", + "ctx": 32000, + "maxOut": null, + "pIn": 0.18, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-context-3", + "name": "voyage/voyage-context-3", + "provider": "voyage", + "ctx": 120000, + "maxOut": null, + "pIn": 0.18, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-finance-2", + "name": "voyage/voyage-finance-2", + "provider": "voyage", + "ctx": 32000, + "maxOut": null, + "pIn": 0.12, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-large-2", + "name": "voyage/voyage-large-2", + "provider": "voyage", + "ctx": 16000, + "maxOut": null, + "pIn": 0.12, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-law-2", + "name": "voyage/voyage-law-2", + "provider": "voyage", + "ctx": 16000, + "maxOut": null, + "pIn": 0.12, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-lite-01", + "name": "voyage/voyage-lite-01", + "provider": "voyage", + "ctx": 4096, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-lite-02-instruct", + "name": "voyage/voyage-lite-02-instruct", + "provider": "voyage", + "ctx": 4000, + "maxOut": null, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "voyage/voyage-multimodal-3", + "name": "voyage/voyage-multimodal-3", + "provider": "voyage", + "ctx": 32000, + "maxOut": null, + "pIn": 0.12, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/openai/gpt-oss-120b", + "name": "wandb/openai/gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 15000, + "pOut": 60000, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/openai/gpt-oss-20b", + "name": "wandb/openai/gpt-oss-20b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 5000, + "pOut": 20000, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/zai-org/GLM-4.5", + "name": "wandb/zai-org/GLM-4.5", + "provider": "wandb", + "ctx": 131072, + "maxOut": 131072, + "pIn": 55000, + "pOut": 200000, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/Qwen/Qwen3-235B-A22B-Instruct-2507", + "name": "wandb/Qwen/Qwen3-235B-A22B-Instruct-2507", + "provider": "wandb", + "ctx": 262144, + "maxOut": 262144, + "pIn": 10000, + "pOut": 10000, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/Qwen/Qwen3-Coder-480B-A35B-Instruct", + "name": "wandb/Qwen/Qwen3-Coder-480B-A35B-Instruct", + "provider": "wandb", + "ctx": 262144, + "maxOut": 262144, + "pIn": 100000, + "pOut": 150000, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/Qwen/Qwen3-235B-A22B-Thinking-2507", + "name": "wandb/Qwen/Qwen3-235B-A22B-Thinking-2507", + "provider": "wandb", + "ctx": 262144, + "maxOut": 262144, + "pIn": 10000, + "pOut": 10000, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/moonshotai/Kimi-K2-Instruct", + "name": "wandb/moonshotai/Kimi-K2-Instruct", + "provider": "wandb", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.6, + "pOut": 2.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/moonshotai/Kimi-K2.5", + "name": "wandb/moonshotai/Kimi-K2.5", + "provider": "wandb", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 3, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "wandb/MiniMaxAI/MiniMax-M2.5", + "name": "wandb/MiniMaxAI/MiniMax-M2.5", + "provider": "wandb", + "ctx": 197000, + "maxOut": 197000, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "wandb/meta-llama/Llama-3.1-8B-Instruct", + "name": "wandb/meta-llama/Llama-3.1-8B-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 22000, + "pOut": 22000, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/deepseek-ai/DeepSeek-V3.1", + "name": "wandb/deepseek-ai/DeepSeek-V3.1", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 128000, + "pIn": 55000, + "pOut": 165000, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/deepseek-ai/DeepSeek-R1-0528", + "name": "wandb/deepseek-ai/DeepSeek-R1-0528", + "provider": "deepseek", + "ctx": 161000, + "maxOut": 161000, + "pIn": 135000, + "pOut": 540000, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/deepseek-ai/DeepSeek-V3-0324", + "name": "wandb/deepseek-ai/DeepSeek-V3-0324", + "provider": "deepseek", + "ctx": 161000, + "maxOut": 161000, + "pIn": 114000, + "pOut": 275000, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/meta-llama/Llama-3.3-70B-Instruct", + "name": "wandb/meta-llama/Llama-3.3-70B-Instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 71000, + "pOut": 71000, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/meta-llama/Llama-4-Scout-17B-16E-Instruct", + "name": "wandb/meta-llama/Llama-4-Scout-17B-16E-Instruct", + "provider": "meta", + "ctx": 64000, + "maxOut": 64000, + "pIn": 17000, + "pOut": 66000, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "wandb/microsoft/Phi-4-mini-instruct", + "name": "wandb/microsoft/Phi-4-mini-instruct", + "provider": "wandb", + "ctx": 128000, + "maxOut": 128000, + "pIn": 8000, + "pOut": 35000, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/ibm/granite-3-8b-instruct", + "name": "watsonx/ibm/granite-3-8b-instruct", + "provider": "watsonx", + "ctx": 8192, + "maxOut": 1024, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "watsonx/mistralai/mistral-large", + "name": "watsonx/mistralai/mistral-large", + "provider": "mistral", + "ctx": 131072, + "maxOut": 16384, + "pIn": 3, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "watsonx/bigscience/mt0-xxl-13b", + "name": "watsonx/bigscience/mt0-xxl-13b", + "provider": "watsonx", + "ctx": 8192, + "maxOut": 8192, + "pIn": 500, + "pOut": 2000, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/core42/jais-13b-chat", + "name": "watsonx/core42/jais-13b-chat", + "provider": "watsonx", + "ctx": 8192, + "maxOut": 8192, + "pIn": 500, + "pOut": 2000, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/google/flan-t5-xl-3b", + "name": "watsonx/google/flan-t5-xl-3b", + "provider": "gemini", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 0.6, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/ibm/granite-13b-chat-v2", + "name": "watsonx/ibm/granite-13b-chat-v2", + "provider": "watsonx", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 0.6, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/ibm/granite-13b-instruct-v2", + "name": "watsonx/ibm/granite-13b-instruct-v2", + "provider": "watsonx", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.6, + "pOut": 0.6, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/ibm/granite-3-3-8b-instruct", + "name": "watsonx/ibm/granite-3-3-8b-instruct", + "provider": "watsonx", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/ibm/granite-4-h-small", + "name": "watsonx/ibm/granite-4-h-small", + "provider": "watsonx", + "ctx": 20480, + "maxOut": 20480, + "pIn": 0.06, + "pOut": 0.25, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/ibm/granite-guardian-3-2-2b", + "name": "watsonx/ibm/granite-guardian-3-2-2b", + "provider": "watsonx", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/ibm/granite-guardian-3-3-8b", + "name": "watsonx/ibm/granite-guardian-3-3-8b", + "provider": "watsonx", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/ibm/granite-ttm-1024-96-r2", + "name": "watsonx/ibm/granite-ttm-1024-96-r2", + "provider": "watsonx", + "ctx": 512, + "maxOut": 512, + "pIn": 0.38, + "pOut": 0.38, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/ibm/granite-ttm-1536-96-r2", + "name": "watsonx/ibm/granite-ttm-1536-96-r2", + "provider": "watsonx", + "ctx": 512, + "maxOut": 512, + "pIn": 0.38, + "pOut": 0.38, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/ibm/granite-ttm-512-96-r2", + "name": "watsonx/ibm/granite-ttm-512-96-r2", + "provider": "watsonx", + "ctx": 512, + "maxOut": 512, + "pIn": 0.38, + "pOut": 0.38, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/ibm/granite-vision-3-2-2b", + "name": "watsonx/ibm/granite-vision-3-2-2b", + "provider": "watsonx", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/meta-llama/llama-3-2-11b-vision-instruct", + "name": "watsonx/meta-llama/llama-3-2-11b-vision-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.35, + "pOut": 0.35, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/meta-llama/llama-3-2-1b-instruct", + "name": "watsonx/meta-llama/llama-3-2-1b-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/meta-llama/llama-3-2-3b-instruct", + "name": "watsonx/meta-llama/llama-3-2-3b-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.15, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/meta-llama/llama-3-2-90b-vision-instruct", + "name": "watsonx/meta-llama/llama-3-2-90b-vision-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 2, + "pOut": 2, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/meta-llama/llama-3-3-70b-instruct", + "name": "watsonx/meta-llama/llama-3-3-70b-instruct", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.71, + "pOut": 0.71, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/meta-llama/llama-4-maverick-17b", + "name": "watsonx/meta-llama/llama-4-maverick-17b", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.35, + "pOut": 1.4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/meta-llama/llama-guard-3-11b-vision", + "name": "watsonx/meta-llama/llama-guard-3-11b-vision", + "provider": "meta", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.35, + "pOut": 0.35, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/mistralai/mistral-medium-2505", + "name": "watsonx/mistralai/mistral-medium-2505", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 3, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/mistralai/mistral-small-2503", + "name": "watsonx/mistralai/mistral-small-2503", + "provider": "mistral", + "ctx": 32000, + "maxOut": 32000, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/mistralai/mistral-small-3-1-24b-instruct-2503", + "name": "watsonx/mistralai/mistral-small-3-1-24b-instruct-2503", + "provider": "mistral", + "ctx": 32000, + "maxOut": 32000, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/mistralai/pixtral-12b-2409", + "name": "watsonx/mistralai/pixtral-12b-2409", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.35, + "pOut": 0.35, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/openai/gpt-oss-120b", + "name": "watsonx/openai/gpt-oss-120b", + "provider": "openai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.15, + "pOut": 0.6, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "watsonx/sdaia/allam-1-13b-instruct", + "name": "watsonx/sdaia/allam-1-13b-instruct", + "provider": "watsonx", + "ctx": 8192, + "maxOut": 8192, + "pIn": 1.7999999999999998, + "pOut": 1.7999999999999998, + "tools": false, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-2", + "name": "xai/grok-2", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 2, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-2-1212", + "name": "xai/grok-2-1212", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 2, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-2-latest", + "name": "xai/grok-2-latest", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 2, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-2-vision", + "name": "xai/grok-2-vision", + "provider": "xai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 2, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-2-vision-1212", + "name": "xai/grok-2-vision-1212", + "provider": "xai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 2, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-2-vision-latest", + "name": "xai/grok-2-vision-latest", + "provider": "xai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 2, + "pOut": 10, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-3", + "name": "xai/grok-3", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "xai/grok-3-beta", + "name": "xai/grok-3-beta", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "xai/grok-3-fast-beta", + "name": "xai/grok-3-fast-beta", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "xai/grok-3-fast-latest", + "name": "xai/grok-3-fast-latest", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 5, + "pOut": 25, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "xai/grok-3-latest", + "name": "xai/grok-3-latest", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": false, + "src": "litellm" + }, + { + "id": "xai/grok-3-mini", + "name": "xai/grok-3-mini", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.3, + "pOut": 0.5, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "xai/grok-3-mini-beta", + "name": "xai/grok-3-mini-beta", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.3, + "pOut": 0.5, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "xai/grok-3-mini-fast", + "name": "xai/grok-3-mini-fast", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.6, + "pOut": 4, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "xai/grok-3-mini-fast-beta", + "name": "xai/grok-3-mini-fast-beta", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.6, + "pOut": 4, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "xai/grok-3-mini-fast-latest", + "name": "xai/grok-3-mini-fast-latest", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.6, + "pOut": 4, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "xai/grok-3-mini-latest", + "name": "xai/grok-3-mini-latest", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.3, + "pOut": 0.5, + "tools": true, + "cot": true, + "json": false, + "src": "litellm" + }, + { + "id": "xai/grok-4", + "name": "xai/grok-4", + "provider": "xai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-4-fast-reasoning", + "name": "xai/grok-4-fast-reasoning", + "provider": "xai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 0.19999999999999998, + "pOut": 0.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-4-fast-non-reasoning", + "name": "xai/grok-4-fast-non-reasoning", + "provider": "xai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 0.19999999999999998, + "pOut": 0.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-4-0709", + "name": "xai/grok-4-0709", + "provider": "xai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-4-latest", + "name": "xai/grok-4-latest", + "provider": "xai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-4-1-fast", + "name": "xai/grok-4-1-fast", + "provider": "xai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 0.19999999999999998, + "pOut": 0.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "xai/grok-4-1-fast-reasoning", + "name": "xai/grok-4-1-fast-reasoning", + "provider": "xai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 0.19999999999999998, + "pOut": 0.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "xai/grok-4-1-fast-reasoning-latest", + "name": "xai/grok-4-1-fast-reasoning-latest", + "provider": "xai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 0.19999999999999998, + "pOut": 0.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "xai/grok-4-1-fast-non-reasoning", + "name": "xai/grok-4-1-fast-non-reasoning", + "provider": "xai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 0.19999999999999998, + "pOut": 0.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "xai/grok-4-1-fast-non-reasoning-latest", + "name": "xai/grok-4-1-fast-non-reasoning-latest", + "provider": "xai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 0.19999999999999998, + "pOut": 0.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "xai/grok-4.20-multi-agent-beta-0309", + "name": "xai/grok-4.20-multi-agent-beta-0309", + "provider": "xai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-4.20-beta-0309-reasoning", + "name": "xai/grok-4.20-beta-0309-reasoning", + "provider": "xai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-4.20-0309-reasoning", + "name": "xai/grok-4.20-0309-reasoning", + "provider": "xai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-4.20-beta-0309-non-reasoning", + "name": "xai/grok-4.20-beta-0309-non-reasoning", + "provider": "xai", + "ctx": 2000000, + "maxOut": 2000000, + "pIn": 2, + "pOut": 6, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-4.3", + "name": "xai/grok-4.3", + "provider": "xai", + "ctx": 1000000, + "maxOut": 1000000, + "pIn": 1.25, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "xai/grok-4.3-latest", + "name": "xai/grok-4.3-latest", + "provider": "xai", + "ctx": 1000000, + "maxOut": 1000000, + "pIn": 1.25, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "xai/grok-beta", + "name": "xai/grok-beta", + "provider": "xai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 5, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-code-fast", + "name": "xai/grok-code-fast", + "provider": "xai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-code-fast-1", + "name": "xai/grok-code-fast-1", + "provider": "xai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-code-fast-1-0825", + "name": "xai/grok-code-fast-1-0825", + "provider": "xai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "xai/grok-vision-beta", + "name": "xai/grok-vision-beta", + "provider": "xai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 5, + "pOut": 15, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "zai.glm-4.7", + "name": "zai.glm-4.7", + "provider": "zai.glm-4.7", + "ctx": 200000, + "maxOut": 128000, + "pIn": 0.6, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "zai.glm-5", + "name": "zai.glm-5", + "provider": "zai.glm-5", + "ctx": 200000, + "maxOut": 128000, + "pIn": 1, + "pOut": 3.1999999999999997, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "zai.glm-4.7-flash", + "name": "zai.glm-4.7-flash", + "provider": "zai.glm-4.7-flash", + "ctx": 200000, + "maxOut": 128000, + "pIn": 0.07, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-coder-480b-a35b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-coder-480b-a35b-instruct", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.44999999999999996, + "pOut": 1.7999999999999998, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/flux-kontext-pro", + "name": "fireworks_ai/accounts/fireworks/models/flux-kontext-pro", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.04, + "pOut": 0.04, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/SSD-1B", + "name": "fireworks_ai/accounts/fireworks/models/SSD-1B", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.00013, + "pOut": 0.00013, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/chronos-hermes-13b-v2", + "name": "fireworks_ai/accounts/fireworks/models/chronos-hermes-13b-v2", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/code-llama-13b", + "name": "fireworks_ai/accounts/fireworks/models/code-llama-13b", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/code-llama-13b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/code-llama-13b-instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/code-llama-13b-python", + "name": "fireworks_ai/accounts/fireworks/models/code-llama-13b-python", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/code-llama-34b", + "name": "fireworks_ai/accounts/fireworks/models/code-llama-34b", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/code-llama-34b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/code-llama-34b-instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/code-llama-34b-python", + "name": "fireworks_ai/accounts/fireworks/models/code-llama-34b-python", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/code-llama-70b", + "name": "fireworks_ai/accounts/fireworks/models/code-llama-70b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/code-llama-70b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/code-llama-70b-instruct", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/code-llama-70b-python", + "name": "fireworks_ai/accounts/fireworks/models/code-llama-70b-python", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/code-llama-7b", + "name": "fireworks_ai/accounts/fireworks/models/code-llama-7b", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/code-llama-7b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/code-llama-7b-instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/code-llama-7b-python", + "name": "fireworks_ai/accounts/fireworks/models/code-llama-7b-python", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/code-qwen-1p5-7b", + "name": "fireworks_ai/accounts/fireworks/models/code-qwen-1p5-7b", + "provider": "fireworks_ai", + "ctx": 65536, + "maxOut": 65536, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/codegemma-2b", + "name": "fireworks_ai/accounts/fireworks/models/codegemma-2b", + "provider": "fireworks_ai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/codegemma-7b", + "name": "fireworks_ai/accounts/fireworks/models/codegemma-7b", + "provider": "fireworks_ai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/cogito-671b-v2-p1", + "name": "fireworks_ai/accounts/fireworks/models/cogito-671b-v2-p1", + "provider": "fireworks_ai", + "ctx": 163840, + "maxOut": 163840, + "pIn": 1.2, + "pOut": 1.2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-llama-3b", + "name": "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-llama-3b", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-llama-70b", + "name": "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-llama-70b", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-llama-8b", + "name": "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-llama-8b", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-qwen-14b", + "name": "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-qwen-14b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-qwen-32b", + "name": "fireworks_ai/accounts/fireworks/models/cogito-v1-preview-qwen-32b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/flux-kontext-max", + "name": "fireworks_ai/accounts/fireworks/models/flux-kontext-max", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.08, + "pOut": 0.08, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/dbrx-instruct", + "name": "fireworks_ai/accounts/fireworks/models/dbrx-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 1.2, + "pOut": 1.2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-coder-1b-base", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-coder-1b-base", + "provider": "deepseek", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-coder-33b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-coder-33b-instruct", + "provider": "deepseek", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-coder-7b-base", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-coder-7b-base", + "provider": "deepseek", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-coder-7b-base-v1p5", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-coder-7b-base-v1p5", + "provider": "deepseek", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-coder-7b-instruct-v1p5", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-coder-7b-instruct-v1p5", + "provider": "deepseek", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-coder-v2-lite-base", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-coder-v2-lite-base", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.5, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-coder-v2-lite-instruct", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-coder-v2-lite-instruct", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.5, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-prover-v2", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-prover-v2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 1.2, + "pOut": 1.2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-r1-0528-distill-qwen3-8b", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-r1-0528-distill-qwen3-8b", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-llama-70b", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-llama-70b", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-llama-8b", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-llama-8b", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-14b", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-14b", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-1p5b", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-1p5b", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-32b", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-32b", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-7b", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-r1-distill-qwen-7b", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-v2-lite-chat", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-v2-lite-chat", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.5, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/deepseek-v2p5", + "name": "fireworks_ai/accounts/fireworks/models/deepseek-v2p5", + "provider": "deepseek", + "ctx": 32768, + "maxOut": 32768, + "pIn": 1.2, + "pOut": 1.2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/devstral-small-2505", + "name": "fireworks_ai/accounts/fireworks/models/devstral-small-2505", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/dobby-mini-unhinged-plus-llama-3-1-8b", + "name": "fireworks_ai/accounts/fireworks/models/dobby-mini-unhinged-plus-llama-3-1-8b", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/dobby-unhinged-llama-3-3-70b-new", + "name": "fireworks_ai/accounts/fireworks/models/dobby-unhinged-llama-3-3-70b-new", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/dolphin-2-9-2-qwen2-72b", + "name": "fireworks_ai/accounts/fireworks/models/dolphin-2-9-2-qwen2-72b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/dolphin-2p6-mixtral-8x7b", + "name": "fireworks_ai/accounts/fireworks/models/dolphin-2p6-mixtral-8x7b", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.5, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/ernie-4p5-21b-a3b-pt", + "name": "fireworks_ai/accounts/fireworks/models/ernie-4p5-21b-a3b-pt", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/ernie-4p5-300b-a47b-pt", + "name": "fireworks_ai/accounts/fireworks/models/ernie-4p5-300b-a47b-pt", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/fare-20b", + "name": "fireworks_ai/accounts/fireworks/models/fare-20b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/firefunction-v1", + "name": "fireworks_ai/accounts/fireworks/models/firefunction-v1", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.5, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/firellava-13b", + "name": "fireworks_ai/accounts/fireworks/models/firellava-13b", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/firesearch-ocr-v6", + "name": "fireworks_ai/accounts/fireworks/models/firesearch-ocr-v6", + "provider": "fireworks_ai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/fireworks-asr-large", + "name": "fireworks_ai/accounts/fireworks/models/fireworks-asr-large", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/fireworks-asr-v2", + "name": "fireworks_ai/accounts/fireworks/models/fireworks-asr-v2", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/flux-1-dev", + "name": "fireworks_ai/accounts/fireworks/models/flux-1-dev", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/flux-1-dev-controlnet-union", + "name": "fireworks_ai/accounts/fireworks/models/flux-1-dev-controlnet-union", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.001, + "pOut": 0.001, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/flux-1-dev-fp8", + "name": "fireworks_ai/accounts/fireworks/models/flux-1-dev-fp8", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.0005, + "pOut": 0.0005, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/flux-1-schnell", + "name": "fireworks_ai/accounts/fireworks/models/flux-1-schnell", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/flux-1-schnell-fp8", + "name": "fireworks_ai/accounts/fireworks/models/flux-1-schnell-fp8", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.00035, + "pOut": 0.00035, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/gemma-2b-it", + "name": "fireworks_ai/accounts/fireworks/models/gemma-2b-it", + "provider": "fireworks_ai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/gemma-3-27b-it", + "name": "fireworks_ai/accounts/fireworks/models/gemma-3-27b-it", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/gemma-7b", + "name": "fireworks_ai/accounts/fireworks/models/gemma-7b", + "provider": "fireworks_ai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/gemma-7b-it", + "name": "fireworks_ai/accounts/fireworks/models/gemma-7b-it", + "provider": "fireworks_ai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/gemma2-9b-it", + "name": "fireworks_ai/accounts/fireworks/models/gemma2-9b-it", + "provider": "fireworks_ai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/glm-4p5v", + "name": "fireworks_ai/accounts/fireworks/models/glm-4p5v", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 1.2, + "pOut": 1.2, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/gpt-oss-safeguard-120b", + "name": "fireworks_ai/accounts/fireworks/models/gpt-oss-safeguard-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 1.2, + "pOut": 1.2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/gpt-oss-safeguard-20b", + "name": "fireworks_ai/accounts/fireworks/models/gpt-oss-safeguard-20b", + "provider": "openai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.5, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/hermes-2-pro-mistral-7b", + "name": "fireworks_ai/accounts/fireworks/models/hermes-2-pro-mistral-7b", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/internvl3-38b", + "name": "fireworks_ai/accounts/fireworks/models/internvl3-38b", + "provider": "fireworks_ai", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/internvl3-78b", + "name": "fireworks_ai/accounts/fireworks/models/internvl3-78b", + "provider": "fireworks_ai", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/internvl3-8b", + "name": "fireworks_ai/accounts/fireworks/models/internvl3-8b", + "provider": "fireworks_ai", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/japanese-stable-diffusion-xl", + "name": "fireworks_ai/accounts/fireworks/models/japanese-stable-diffusion-xl", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.00013, + "pOut": 0.00013, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/kat-coder", + "name": "fireworks_ai/accounts/fireworks/models/kat-coder", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/kat-dev-32b", + "name": "fireworks_ai/accounts/fireworks/models/kat-dev-32b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/kat-dev-72b-exp", + "name": "fireworks_ai/accounts/fireworks/models/kat-dev-72b-exp", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-guard-2-8b", + "name": "fireworks_ai/accounts/fireworks/models/llama-guard-2-8b", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-guard-3-1b", + "name": "fireworks_ai/accounts/fireworks/models/llama-guard-3-1b", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-guard-3-8b", + "name": "fireworks_ai/accounts/fireworks/models/llama-guard-3-8b", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v2-13b", + "name": "fireworks_ai/accounts/fireworks/models/llama-v2-13b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v2-13b-chat", + "name": "fireworks_ai/accounts/fireworks/models/llama-v2-13b-chat", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v2-70b", + "name": "fireworks_ai/accounts/fireworks/models/llama-v2-70b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v2-70b-chat", + "name": "fireworks_ai/accounts/fireworks/models/llama-v2-70b-chat", + "provider": "meta", + "ctx": 2048, + "maxOut": 2048, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v2-7b", + "name": "fireworks_ai/accounts/fireworks/models/llama-v2-7b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v2-7b-chat", + "name": "fireworks_ai/accounts/fireworks/models/llama-v2-7b-chat", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct-hf", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct-hf", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3-8b", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3-8b", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3-8b-instruct-hf", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3-8b-instruct-hf", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3p1-405b-instruct-long", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3p1-405b-instruct-long", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3p1-70b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3p1-70b-instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3p1-70b-instruct-1b", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3p1-70b-instruct-1b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3p1-nemotron-70b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3p1-nemotron-70b-instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3p2-1b", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3p2-1b", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3p2-3b", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3p2-3b", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llama-v3p3-70b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/llama-v3p3-70b-instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llamaguard-7b", + "name": "fireworks_ai/accounts/fireworks/models/llamaguard-7b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/llava-yi-34b", + "name": "fireworks_ai/accounts/fireworks/models/llava-yi-34b", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/minimax-m1-80k", + "name": "fireworks_ai/accounts/fireworks/models/minimax-m1-80k", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/minimax-m2", + "name": "fireworks_ai/accounts/fireworks/models/minimax-m2", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.3, + "pOut": 1.2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/ministral-3-14b-instruct-2512", + "name": "fireworks_ai/accounts/fireworks/models/ministral-3-14b-instruct-2512", + "provider": "fireworks_ai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/ministral-3-3b-instruct-2512", + "name": "fireworks_ai/accounts/fireworks/models/ministral-3-3b-instruct-2512", + "provider": "fireworks_ai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/ministral-3-8b-instruct-2512", + "name": "fireworks_ai/accounts/fireworks/models/ministral-3-8b-instruct-2512", + "provider": "fireworks_ai", + "ctx": 256000, + "maxOut": 256000, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mistral-7b", + "name": "fireworks_ai/accounts/fireworks/models/mistral-7b", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mistral-7b-instruct-4k", + "name": "fireworks_ai/accounts/fireworks/models/mistral-7b-instruct-4k", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mistral-7b-instruct-v0p2", + "name": "fireworks_ai/accounts/fireworks/models/mistral-7b-instruct-v0p2", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mistral-7b-instruct-v3", + "name": "fireworks_ai/accounts/fireworks/models/mistral-7b-instruct-v3", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mistral-7b-v0p2", + "name": "fireworks_ai/accounts/fireworks/models/mistral-7b-v0p2", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mistral-large-3-fp8", + "name": "fireworks_ai/accounts/fireworks/models/mistral-large-3-fp8", + "provider": "mistral", + "ctx": 256000, + "maxOut": 256000, + "pIn": 1.2, + "pOut": 1.2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mistral-nemo-base-2407", + "name": "fireworks_ai/accounts/fireworks/models/mistral-nemo-base-2407", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mistral-nemo-instruct-2407", + "name": "fireworks_ai/accounts/fireworks/models/mistral-nemo-instruct-2407", + "provider": "mistral", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mistral-small-24b-instruct-2501", + "name": "fireworks_ai/accounts/fireworks/models/mistral-small-24b-instruct-2501", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mixtral-8x22b", + "name": "fireworks_ai/accounts/fireworks/models/mixtral-8x22b", + "provider": "fireworks_ai", + "ctx": 65536, + "maxOut": 65536, + "pIn": 1.2, + "pOut": 1.2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mixtral-8x22b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/mixtral-8x22b-instruct", + "provider": "fireworks_ai", + "ctx": 65536, + "maxOut": 65536, + "pIn": 1.2, + "pOut": 1.2, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mixtral-8x7b", + "name": "fireworks_ai/accounts/fireworks/models/mixtral-8x7b", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.5, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mixtral-8x7b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/mixtral-8x7b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.5, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mixtral-8x7b-instruct-hf", + "name": "fireworks_ai/accounts/fireworks/models/mixtral-8x7b-instruct-hf", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.5, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/mythomax-l2-13b", + "name": "fireworks_ai/accounts/fireworks/models/mythomax-l2-13b", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/nemotron-nano-v2-12b-vl", + "name": "fireworks_ai/accounts/fireworks/models/nemotron-nano-v2-12b-vl", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/nous-capybara-7b-v1p9", + "name": "fireworks_ai/accounts/fireworks/models/nous-capybara-7b-v1p9", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/nous-hermes-2-mixtral-8x7b-dpo", + "name": "fireworks_ai/accounts/fireworks/models/nous-hermes-2-mixtral-8x7b-dpo", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.5, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/nous-hermes-2-yi-34b", + "name": "fireworks_ai/accounts/fireworks/models/nous-hermes-2-yi-34b", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/nous-hermes-llama2-13b", + "name": "fireworks_ai/accounts/fireworks/models/nous-hermes-llama2-13b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/nous-hermes-llama2-70b", + "name": "fireworks_ai/accounts/fireworks/models/nous-hermes-llama2-70b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/nous-hermes-llama2-7b", + "name": "fireworks_ai/accounts/fireworks/models/nous-hermes-llama2-7b", + "provider": "meta", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/nvidia-nemotron-nano-12b-v2", + "name": "fireworks_ai/accounts/fireworks/models/nvidia-nemotron-nano-12b-v2", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/nvidia-nemotron-nano-9b-v2", + "name": "fireworks_ai/accounts/fireworks/models/nvidia-nemotron-nano-9b-v2", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/openchat-3p5-0106-7b", + "name": "fireworks_ai/accounts/fireworks/models/openchat-3p5-0106-7b", + "provider": "fireworks_ai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/openhermes-2-mistral-7b", + "name": "fireworks_ai/accounts/fireworks/models/openhermes-2-mistral-7b", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/openhermes-2p5-mistral-7b", + "name": "fireworks_ai/accounts/fireworks/models/openhermes-2p5-mistral-7b", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/openorca-7b", + "name": "fireworks_ai/accounts/fireworks/models/openorca-7b", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/phi-2-3b", + "name": "fireworks_ai/accounts/fireworks/models/phi-2-3b", + "provider": "fireworks_ai", + "ctx": 2048, + "maxOut": 2048, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/phi-3-mini-128k-instruct", + "name": "fireworks_ai/accounts/fireworks/models/phi-3-mini-128k-instruct", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/phi-3-vision-128k-instruct", + "name": "fireworks_ai/accounts/fireworks/models/phi-3-vision-128k-instruct", + "provider": "fireworks_ai", + "ctx": 32064, + "maxOut": 32064, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/phind-code-llama-34b-python-v1", + "name": "fireworks_ai/accounts/fireworks/models/phind-code-llama-34b-python-v1", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/phind-code-llama-34b-v1", + "name": "fireworks_ai/accounts/fireworks/models/phind-code-llama-34b-v1", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/phind-code-llama-34b-v2", + "name": "fireworks_ai/accounts/fireworks/models/phind-code-llama-34b-v2", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/playground-v2-1024px-aesthetic", + "name": "fireworks_ai/accounts/fireworks/models/playground-v2-1024px-aesthetic", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.00013, + "pOut": 0.00013, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/playground-v2-5-1024px-aesthetic", + "name": "fireworks_ai/accounts/fireworks/models/playground-v2-5-1024px-aesthetic", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.00013, + "pOut": 0.00013, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/pythia-12b", + "name": "fireworks_ai/accounts/fireworks/models/pythia-12b", + "provider": "fireworks_ai", + "ctx": 2048, + "maxOut": 2048, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen-qwq-32b-preview", + "name": "fireworks_ai/accounts/fireworks/models/qwen-qwq-32b-preview", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen-v2p5-14b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen-v2p5-14b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen-v2p5-7b", + "name": "fireworks_ai/accounts/fireworks/models/qwen-v2p5-7b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen1p5-72b-chat", + "name": "fireworks_ai/accounts/fireworks/models/qwen1p5-72b-chat", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2-7b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2-7b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2-vl-2b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2-vl-2b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2-vl-72b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2-vl-72b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2-vl-7b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2-vl-7b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-0p5b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-0p5b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-14b", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-14b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-1p5b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-1p5b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-32b", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-32b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-32b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-32b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-72b", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-72b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-72b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-72b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-7b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-7b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-0p5b", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-0p5b", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-0p5b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-0p5b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-14b", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-14b", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-14b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-14b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-1p5b", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-1p5b", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-1p5b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-1p5b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct-128k", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct-128k", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct-32k-rope", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct-32k-rope", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct-64k", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct-64k", + "provider": "fireworks_ai", + "ctx": 65536, + "maxOut": 65536, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-3b", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-3b", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-3b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-3b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-7b", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-7b", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-7b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-coder-7b-instruct", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-math-72b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-math-72b-instruct", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-32b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-32b-instruct", + "provider": "fireworks_ai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-3b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-3b-instruct", + "provider": "fireworks_ai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-72b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-72b-instruct", + "provider": "fireworks_ai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-7b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen2p5-vl-7b-instruct", + "provider": "fireworks_ai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-0p6b", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-0p6b", + "provider": "fireworks_ai", + "ctx": 40960, + "maxOut": 40960, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-14b", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-14b", + "provider": "fireworks_ai", + "ctx": 40960, + "maxOut": 40960, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-1p7b", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-1p7b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-1p7b-fp8-draft", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-1p7b-fp8-draft", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-1p7b-fp8-draft-131072", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-1p7b-fp8-draft-131072", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-1p7b-fp8-draft-40960", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-1p7b-fp8-draft-40960", + "provider": "fireworks_ai", + "ctx": 40960, + "maxOut": 40960, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-235b-a22b", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-235b-a22b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.22, + "pOut": 0.88, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-235b-a22b-instruct-2507", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-235b-a22b-instruct-2507", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.22, + "pOut": 0.88, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-235b-a22b-thinking-2507", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-235b-a22b-thinking-2507", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.22, + "pOut": 0.88, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-30b-a3b", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-30b-a3b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.15, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-30b-a3b-instruct-2507", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-30b-a3b-instruct-2507", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.5, + "pOut": 0.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-30b-a3b-thinking-2507", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-30b-a3b-thinking-2507", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-32b", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-32b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-4b", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-4b", + "provider": "fireworks_ai", + "ctx": 40960, + "maxOut": 40960, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-4b-instruct-2507", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-4b-instruct-2507", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-8b", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-8b", + "provider": "fireworks_ai", + "ctx": 40960, + "maxOut": 40960, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-coder-30b-a3b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-coder-30b-a3b-instruct", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.15, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-coder-480b-instruct-bf16", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-coder-480b-instruct-bf16", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-embedding-0p6b", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-embedding-0p6b", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-embedding-4b", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-embedding-4b", + "provider": "fireworks_ai", + "ctx": 40960, + "maxOut": 40960, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/", + "name": "fireworks_ai/accounts/fireworks/models/", + "provider": "fireworks_ai", + "ctx": 40960, + "maxOut": 40960, + "pIn": 0.09999999999999999, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-next-80b-a3b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-next-80b-a3b-instruct", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-next-80b-a3b-thinking", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-next-80b-a3b-thinking", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-reranker-0p6b", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-reranker-0p6b", + "provider": "fireworks_ai", + "ctx": 40960, + "maxOut": 40960, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-reranker-4b", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-reranker-4b", + "provider": "fireworks_ai", + "ctx": 40960, + "maxOut": 40960, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-reranker-8b", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-reranker-8b", + "provider": "fireworks_ai", + "ctx": 40960, + "maxOut": 40960, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-vl-235b-a22b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-vl-235b-a22b-instruct", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.22, + "pOut": 0.88, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-vl-235b-a22b-thinking", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-vl-235b-a22b-thinking", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.22, + "pOut": 0.88, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-vl-30b-a3b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-vl-30b-a3b-instruct", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.15, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-vl-30b-a3b-thinking", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-vl-30b-a3b-thinking", + "provider": "fireworks_ai", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.15, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-vl-32b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-vl-32b-instruct", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwen3-vl-8b-instruct", + "name": "fireworks_ai/accounts/fireworks/models/qwen3-vl-8b-instruct", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/qwq-32b", + "name": "fireworks_ai/accounts/fireworks/models/qwq-32b", + "provider": "fireworks_ai", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/rolm-ocr", + "name": "fireworks_ai/accounts/fireworks/models/rolm-ocr", + "provider": "fireworks_ai", + "ctx": 128000, + "maxOut": 128000, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/snorkel-mistral-7b-pairrm-dpo", + "name": "fireworks_ai/accounts/fireworks/models/snorkel-mistral-7b-pairrm-dpo", + "provider": "mistral", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/stable-diffusion-xl-1024-v1-0", + "name": "fireworks_ai/accounts/fireworks/models/stable-diffusion-xl-1024-v1-0", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.00013, + "pOut": 0.00013, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/stablecode-3b", + "name": "fireworks_ai/accounts/fireworks/models/stablecode-3b", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/starcoder-16b", + "name": "fireworks_ai/accounts/fireworks/models/starcoder-16b", + "provider": "fireworks_ai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/starcoder-7b", + "name": "fireworks_ai/accounts/fireworks/models/starcoder-7b", + "provider": "fireworks_ai", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/starcoder2-15b", + "name": "fireworks_ai/accounts/fireworks/models/starcoder2-15b", + "provider": "fireworks_ai", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/starcoder2-3b", + "name": "fireworks_ai/accounts/fireworks/models/starcoder2-3b", + "provider": "fireworks_ai", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.09999999999999999, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/starcoder2-7b", + "name": "fireworks_ai/accounts/fireworks/models/starcoder2-7b", + "provider": "fireworks_ai", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/toppy-m-7b", + "name": "fireworks_ai/accounts/fireworks/models/toppy-m-7b", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/whisper-v3", + "name": "fireworks_ai/accounts/fireworks/models/whisper-v3", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/whisper-v3-turbo", + "name": "fireworks_ai/accounts/fireworks/models/whisper-v3-turbo", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/yi-34b", + "name": "fireworks_ai/accounts/fireworks/models/yi-34b", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/yi-34b-200k-capybara", + "name": "fireworks_ai/accounts/fireworks/models/yi-34b-200k-capybara", + "provider": "fireworks_ai", + "ctx": 200000, + "maxOut": 200000, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/yi-34b-chat", + "name": "fireworks_ai/accounts/fireworks/models/yi-34b-chat", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.8999999999999999, + "pOut": 0.8999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/yi-6b", + "name": "fireworks_ai/accounts/fireworks/models/yi-6b", + "provider": "fireworks_ai", + "ctx": 4096, + "maxOut": 4096, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "fireworks_ai/accounts/fireworks/models/zephyr-7b-beta", + "name": "fireworks_ai/accounts/fireworks/models/zephyr-7b-beta", + "provider": "fireworks_ai", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-v3.2", + "name": "novita/deepseek/deepseek-v3.2", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 65536, + "pIn": 0.26899999999999996, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/minimax/minimax-m2.1", + "name": "novita/minimax/minimax-m2.1", + "provider": "novita", + "ctx": 204800, + "maxOut": 131072, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/zai-org/glm-4.7", + "name": "novita/zai-org/glm-4.7", + "provider": "novita", + "ctx": 204800, + "maxOut": 131072, + "pIn": 0.6, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/xiaomimimo/mimo-v2-flash", + "name": "novita/xiaomimimo/mimo-v2-flash", + "provider": "novita", + "ctx": 262144, + "maxOut": 32000, + "pIn": 0.09999999999999999, + "pOut": 0.3, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/zai-org/autoglm-phone-9b-multilingual", + "name": "novita/zai-org/autoglm-phone-9b-multilingual", + "provider": "novita", + "ctx": 65536, + "maxOut": 65536, + "pIn": 0.035, + "pOut": 0.13799999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/moonshotai/kimi-k2-thinking", + "name": "novita/moonshotai/kimi-k2-thinking", + "provider": "novita", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/minimax/minimax-m2", + "name": "novita/minimax/minimax-m2", + "provider": "novita", + "ctx": 204800, + "maxOut": 131072, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/paddlepaddle/paddleocr-vl", + "name": "novita/paddlepaddle/paddleocr-vl", + "provider": "novita", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.02, + "pOut": 0.02, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-v3.2-exp", + "name": "novita/deepseek/deepseek-v3.2-exp", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 65536, + "pIn": 0.27, + "pOut": 0.41, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-vl-235b-a22b-thinking", + "name": "novita/qwen/qwen3-vl-235b-a22b-thinking", + "provider": "novita", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.98, + "pOut": 3.95, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/zai-org/glm-4.6v", + "name": "novita/zai-org/glm-4.6v", + "provider": "novita", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.3, + "pOut": 0.8999999999999999, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/zai-org/glm-4.6", + "name": "novita/zai-org/glm-4.6", + "provider": "novita", + "ctx": 204800, + "maxOut": 131072, + "pIn": 0.55, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/kwaipilot/kat-coder-pro", + "name": "novita/kwaipilot/kat-coder-pro", + "provider": "novita", + "ctx": 256000, + "maxOut": 128000, + "pIn": 0.3, + "pOut": 1.2, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-next-80b-a3b-instruct", + "name": "novita/qwen/qwen3-next-80b-a3b-instruct", + "provider": "novita", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.15, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-next-80b-a3b-thinking", + "name": "novita/qwen/qwen3-next-80b-a3b-thinking", + "provider": "novita", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.15, + "pOut": 1.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-ocr", + "name": "novita/deepseek/deepseek-ocr", + "provider": "deepseek", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.03, + "pOut": 0.03, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-v3.1-terminus", + "name": "novita/deepseek/deepseek-v3.1-terminus", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.27, + "pOut": 1, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-vl-235b-a22b-instruct", + "name": "novita/qwen/qwen3-vl-235b-a22b-instruct", + "provider": "novita", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.3, + "pOut": 1.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-max", + "name": "novita/qwen/qwen3-max", + "provider": "novita", + "ctx": 262144, + "maxOut": 65536, + "pIn": 2.1100000000000003, + "pOut": 8.450000000000001, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/skywork/r1v4-lite", + "name": "novita/skywork/r1v4-lite", + "provider": "novita", + "ctx": 262144, + "maxOut": 65536, + "pIn": 0.19999999999999998, + "pOut": 0.6, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-v3.1", + "name": "novita/deepseek/deepseek-v3.1", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.27, + "pOut": 1, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/moonshotai/kimi-k2-0905", + "name": "novita/moonshotai/kimi-k2-0905", + "provider": "novita", + "ctx": 262144, + "maxOut": 262144, + "pIn": 0.6, + "pOut": 2.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-coder-480b-a35b-instruct", + "name": "novita/qwen/qwen3-coder-480b-a35b-instruct", + "provider": "novita", + "ctx": 262144, + "maxOut": 65536, + "pIn": 0.3, + "pOut": 1.3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-coder-30b-a3b-instruct", + "name": "novita/qwen/qwen3-coder-30b-a3b-instruct", + "provider": "novita", + "ctx": 160000, + "maxOut": 32768, + "pIn": 0.07, + "pOut": 0.27, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/openai/gpt-oss-120b", + "name": "novita/openai/gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.049999999999999996, + "pOut": 0.25, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/moonshotai/kimi-k2-instruct", + "name": "novita/moonshotai/kimi-k2-instruct", + "provider": "novita", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.5700000000000001, + "pOut": 2.3, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-v3-0324", + "name": "novita/deepseek/deepseek-v3-0324", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 163840, + "pIn": 0.27, + "pOut": 1.12, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/zai-org/glm-4.5", + "name": "novita/zai-org/glm-4.5", + "provider": "novita", + "ctx": 131072, + "maxOut": 98304, + "pIn": 0.6, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-235b-a22b-thinking-2507", + "name": "novita/qwen/qwen3-235b-a22b-thinking-2507", + "provider": "novita", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.3, + "pOut": 3, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/meta-llama/llama-3.1-8b-instruct", + "name": "novita/meta-llama/llama-3.1-8b-instruct", + "provider": "meta", + "ctx": 16384, + "maxOut": 16384, + "pIn": 0.02, + "pOut": 0.049999999999999996, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/google/gemma-3-12b-it", + "name": "novita/google/gemma-3-12b-it", + "provider": "gemini", + "ctx": 131072, + "maxOut": 8192, + "pIn": 0.049999999999999996, + "pOut": 0.09999999999999999, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/zai-org/glm-4.5v", + "name": "novita/zai-org/glm-4.5v", + "provider": "novita", + "ctx": 65536, + "maxOut": 16384, + "pIn": 0.6, + "pOut": 1.7999999999999998, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/openai/gpt-oss-20b", + "name": "novita/openai/gpt-oss-20b", + "provider": "openai", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.04, + "pOut": 0.15, + "tools": null, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-235b-a22b-instruct-2507", + "name": "novita/qwen/qwen3-235b-a22b-instruct-2507", + "provider": "novita", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.09, + "pOut": 0.58, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-r1-distill-qwen-14b", + "name": "novita/deepseek/deepseek-r1-distill-qwen-14b", + "provider": "deepseek", + "ctx": 32768, + "maxOut": 16384, + "pIn": 0.15, + "pOut": 0.15, + "tools": null, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/meta-llama/llama-3.3-70b-instruct", + "name": "novita/meta-llama/llama-3.3-70b-instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 120000, + "pIn": 0.135, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen-2.5-72b-instruct", + "name": "novita/qwen/qwen-2.5-72b-instruct", + "provider": "novita", + "ctx": 32000, + "maxOut": 8192, + "pIn": 0.38, + "pOut": 0.39999999999999997, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/mistralai/mistral-nemo", + "name": "novita/mistralai/mistral-nemo", + "provider": "mistral", + "ctx": 60288, + "maxOut": 16000, + "pIn": 0.04, + "pOut": 0.16999999999999998, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/minimaxai/minimax-m1-80k", + "name": "novita/minimaxai/minimax-m1-80k", + "provider": "novita", + "ctx": 1000000, + "maxOut": 40000, + "pIn": 0.55, + "pOut": 2.2, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-r1-0528", + "name": "novita/deepseek/deepseek-r1-0528", + "provider": "deepseek", + "ctx": 163840, + "maxOut": 32768, + "pIn": 0.7, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-r1-distill-qwen-32b", + "name": "novita/deepseek/deepseek-r1-distill-qwen-32b", + "provider": "deepseek", + "ctx": 64000, + "maxOut": 32000, + "pIn": 0.3, + "pOut": 0.3, + "tools": null, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/meta-llama/llama-3-8b-instruct", + "name": "novita/meta-llama/llama-3-8b-instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.04, + "pOut": 0.04, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/microsoft/wizardlm-2-8x22b", + "name": "novita/microsoft/wizardlm-2-8x22b", + "provider": "novita", + "ctx": 65535, + "maxOut": 8000, + "pIn": 0.62, + "pOut": 0.62, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-r1-0528-qwen3-8b", + "name": "novita/deepseek/deepseek-r1-0528-qwen3-8b", + "provider": "deepseek", + "ctx": 128000, + "maxOut": 32000, + "pIn": 0.06, + "pOut": 0.09, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-r1-distill-llama-70b", + "name": "novita/deepseek/deepseek-r1-distill-llama-70b", + "provider": "deepseek", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.7999999999999999, + "pOut": 0.7999999999999999, + "tools": null, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/meta-llama/llama-3-70b-instruct", + "name": "novita/meta-llama/llama-3-70b-instruct", + "provider": "meta", + "ctx": 8192, + "maxOut": 8000, + "pIn": 0.51, + "pOut": 0.74, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-235b-a22b-fp8", + "name": "novita/qwen/qwen3-235b-a22b-fp8", + "provider": "novita", + "ctx": 40960, + "maxOut": 20000, + "pIn": 0.19999999999999998, + "pOut": 0.7999999999999999, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/meta-llama/llama-4-maverick-17b-128e-instruct-fp8", + "name": "novita/meta-llama/llama-4-maverick-17b-128e-instruct-fp8", + "provider": "meta", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.27, + "pOut": 0.85, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/meta-llama/llama-4-scout-17b-16e-instruct", + "name": "novita/meta-llama/llama-4-scout-17b-16e-instruct", + "provider": "meta", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.18, + "pOut": 0.59, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/nousresearch/hermes-2-pro-llama-3-8b", + "name": "novita/nousresearch/hermes-2-pro-llama-3-8b", + "provider": "meta", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.14, + "pOut": 0.14, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen2.5-vl-72b-instruct", + "name": "novita/qwen/qwen2.5-vl-72b-instruct", + "provider": "novita", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.7999999999999999, + "pOut": 0.7999999999999999, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/sao10k/l3-70b-euryale-v2.1", + "name": "novita/sao10k/l3-70b-euryale-v2.1", + "provider": "novita", + "ctx": 8192, + "maxOut": 8192, + "pIn": 1.48, + "pOut": 1.48, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/baidu/ernie-4.5-21B-a3b-thinking", + "name": "novita/baidu/ernie-4.5-21B-a3b-thinking", + "provider": "novita", + "ctx": 131072, + "maxOut": 65536, + "pIn": 0.07, + "pOut": 0.28, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/sao10k/l3-8b-lunaris", + "name": "novita/sao10k/l3-8b-lunaris", + "provider": "novita", + "ctx": 8192, + "maxOut": 8192, + "pIn": 0.049999999999999996, + "pOut": 0.049999999999999996, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/baichuan/baichuan-m2-32b", + "name": "novita/baichuan/baichuan-m2-32b", + "provider": "novita", + "ctx": 131072, + "maxOut": 131072, + "pIn": 0.07, + "pOut": 0.07, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/baidu/ernie-4.5-vl-424b-a47b", + "name": "novita/baidu/ernie-4.5-vl-424b-a47b", + "provider": "novita", + "ctx": 123000, + "maxOut": 16000, + "pIn": 0.42, + "pOut": 1.25, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/baidu/ernie-4.5-300b-a47b-paddle", + "name": "novita/baidu/ernie-4.5-300b-a47b-paddle", + "provider": "novita", + "ctx": 123000, + "maxOut": 12000, + "pIn": 0.28, + "pOut": 1.1, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-prover-v2-671b", + "name": "novita/deepseek/deepseek-prover-v2-671b", + "provider": "deepseek", + "ctx": 160000, + "maxOut": 160000, + "pIn": 0.7, + "pOut": 2.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-32b-fp8", + "name": "novita/qwen/qwen3-32b-fp8", + "provider": "novita", + "ctx": 40960, + "maxOut": 20000, + "pIn": 0.09999999999999999, + "pOut": 0.44999999999999996, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-30b-a3b-fp8", + "name": "novita/qwen/qwen3-30b-a3b-fp8", + "provider": "novita", + "ctx": 40960, + "maxOut": 20000, + "pIn": 0.09, + "pOut": 0.44999999999999996, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/google/gemma-3-27b-it", + "name": "novita/google/gemma-3-27b-it", + "provider": "gemini", + "ctx": 98304, + "maxOut": 16384, + "pIn": 0.119, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-v3-turbo", + "name": "novita/deepseek/deepseek-v3-turbo", + "provider": "deepseek", + "ctx": 64000, + "maxOut": 16000, + "pIn": 0.39999999999999997, + "pOut": 1.3, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/deepseek/deepseek-r1-turbo", + "name": "novita/deepseek/deepseek-r1-turbo", + "provider": "deepseek", + "ctx": 64000, + "maxOut": 16000, + "pIn": 0.7, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/Sao10K/L3-8B-Stheno-v3.2", + "name": "novita/Sao10K/L3-8B-Stheno-v3.2", + "provider": "novita", + "ctx": 8192, + "maxOut": 32000, + "pIn": 0.049999999999999996, + "pOut": 0.049999999999999996, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/gryphe/mythomax-l2-13b", + "name": "novita/gryphe/mythomax-l2-13b", + "provider": "novita", + "ctx": 4096, + "maxOut": 3200, + "pIn": 0.09, + "pOut": 0.09, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/baidu/ernie-4.5-vl-28b-a3b-thinking", + "name": "novita/baidu/ernie-4.5-vl-28b-a3b-thinking", + "provider": "novita", + "ctx": 131072, + "maxOut": 65536, + "pIn": 0.39, + "pOut": 0.39, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-vl-8b-instruct", + "name": "novita/qwen/qwen3-vl-8b-instruct", + "provider": "novita", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.08, + "pOut": 0.5, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/zai-org/glm-4.5-air", + "name": "novita/zai-org/glm-4.5-air", + "provider": "novita", + "ctx": 131072, + "maxOut": 98304, + "pIn": 0.13, + "pOut": 0.85, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-vl-30b-a3b-instruct", + "name": "novita/qwen/qwen3-vl-30b-a3b-instruct", + "provider": "novita", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 0.7, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-vl-30b-a3b-thinking", + "name": "novita/qwen/qwen3-vl-30b-a3b-thinking", + "provider": "novita", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.19999999999999998, + "pOut": 1, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-omni-30b-a3b-thinking", + "name": "novita/qwen/qwen3-omni-30b-a3b-thinking", + "provider": "novita", + "ctx": 65536, + "maxOut": 16384, + "pIn": 0.25, + "pOut": 0.9700000000000001, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-omni-30b-a3b-instruct", + "name": "novita/qwen/qwen3-omni-30b-a3b-instruct", + "provider": "novita", + "ctx": 65536, + "maxOut": 16384, + "pIn": 0.25, + "pOut": 0.9700000000000001, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen-mt-plus", + "name": "novita/qwen/qwen-mt-plus", + "provider": "novita", + "ctx": 16384, + "maxOut": 8192, + "pIn": 0.25, + "pOut": 0.75, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/baidu/ernie-4.5-vl-28b-a3b", + "name": "novita/baidu/ernie-4.5-vl-28b-a3b", + "provider": "novita", + "ctx": 30000, + "maxOut": 8000, + "pIn": 0.14, + "pOut": 0.56, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/baidu/ernie-4.5-21B-a3b", + "name": "novita/baidu/ernie-4.5-21B-a3b", + "provider": "novita", + "ctx": 120000, + "maxOut": 8000, + "pIn": 0.07, + "pOut": 0.28, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-8b-fp8", + "name": "novita/qwen/qwen3-8b-fp8", + "provider": "novita", + "ctx": 128000, + "maxOut": 20000, + "pIn": 0.035, + "pOut": 0.13799999999999998, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-4b-fp8", + "name": "novita/qwen/qwen3-4b-fp8", + "provider": "novita", + "ctx": 128000, + "maxOut": 20000, + "pIn": 0.03, + "pOut": 0.03, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen2.5-7b-instruct", + "name": "novita/qwen/qwen2.5-7b-instruct", + "provider": "novita", + "ctx": 32000, + "maxOut": 32000, + "pIn": 0.07, + "pOut": 0.07, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "novita/meta-llama/llama-3.2-3b-instruct", + "name": "novita/meta-llama/llama-3.2-3b-instruct", + "provider": "meta", + "ctx": 32768, + "maxOut": 32000, + "pIn": 0.03, + "pOut": 0.049999999999999996, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/sao10k/l31-70b-euryale-v2.2", + "name": "novita/sao10k/l31-70b-euryale-v2.2", + "provider": "novita", + "ctx": 8192, + "maxOut": 8192, + "pIn": 1.48, + "pOut": 1.48, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-embedding-0.6b", + "name": "novita/qwen/qwen3-embedding-0.6b", + "provider": "novita", + "ctx": 32768, + "maxOut": 32768, + "pIn": 0.07, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-embedding-8b", + "name": "novita/qwen/qwen3-embedding-8b", + "provider": "novita", + "ctx": 32768, + "maxOut": 4096, + "pIn": 0.07, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/baai/bge-m3", + "name": "novita/baai/bge-m3", + "provider": "novita", + "ctx": 8192, + "maxOut": 96000, + "pIn": 0.01, + "pOut": 0.01, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/qwen/qwen3-reranker-8b", + "name": "novita/qwen/qwen3-reranker-8b", + "provider": "novita", + "ctx": 32768, + "maxOut": 4096, + "pIn": 0.049999999999999996, + "pOut": 0.049999999999999996, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "novita/baai/bge-reranker-v2-m3", + "name": "novita/baai/bge-reranker-v2-m3", + "provider": "novita", + "ctx": 8000, + "maxOut": 8000, + "pIn": 0.01, + "pOut": 0.01, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "llamagate/llama-3.1-8b", + "name": "llamagate/llama-3.1-8b", + "provider": "meta", + "ctx": 131072, + "maxOut": 8192, + "pIn": 0.03, + "pOut": 0.049999999999999996, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/llama-3.2-3b", + "name": "llamagate/llama-3.2-3b", + "provider": "meta", + "ctx": 131072, + "maxOut": 8192, + "pIn": 0.04, + "pOut": 0.08, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/mistral-7b-v0.3", + "name": "llamagate/mistral-7b-v0.3", + "provider": "mistral", + "ctx": 32768, + "maxOut": 8192, + "pIn": 0.09999999999999999, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/qwen3-8b", + "name": "llamagate/qwen3-8b", + "provider": "meta", + "ctx": 32768, + "maxOut": 8192, + "pIn": 0.04, + "pOut": 0.14, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/dolphin3-8b", + "name": "llamagate/dolphin3-8b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.08, + "pOut": 0.15, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/deepseek-r1-8b", + "name": "llamagate/deepseek-r1-8b", + "provider": "deepseek", + "ctx": 65536, + "maxOut": 16384, + "pIn": 0.09999999999999999, + "pOut": 0.19999999999999998, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/deepseek-r1-7b-qwen", + "name": "llamagate/deepseek-r1-7b-qwen", + "provider": "deepseek", + "ctx": 131072, + "maxOut": 16384, + "pIn": 0.08, + "pOut": 0.15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/openthinker-7b", + "name": "llamagate/openthinker-7b", + "provider": "meta", + "ctx": 32768, + "maxOut": 8192, + "pIn": 0.08, + "pOut": 0.15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/qwen2.5-coder-7b", + "name": "llamagate/qwen2.5-coder-7b", + "provider": "meta", + "ctx": 32768, + "maxOut": 8192, + "pIn": 0.06, + "pOut": 0.12, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/deepseek-coder-6.7b", + "name": "llamagate/deepseek-coder-6.7b", + "provider": "deepseek", + "ctx": 16384, + "maxOut": 4096, + "pIn": 0.06, + "pOut": 0.12, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/codellama-7b", + "name": "llamagate/codellama-7b", + "provider": "meta", + "ctx": 16384, + "maxOut": 4096, + "pIn": 0.06, + "pOut": 0.12, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/qwen3-vl-8b", + "name": "llamagate/qwen3-vl-8b", + "provider": "meta", + "ctx": 32768, + "maxOut": 8192, + "pIn": 0.15, + "pOut": 0.55, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/llava-7b", + "name": "llamagate/llava-7b", + "provider": "meta", + "ctx": 4096, + "maxOut": 2048, + "pIn": 0.09999999999999999, + "pOut": 0.19999999999999998, + "tools": null, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/gemma3-4b", + "name": "llamagate/gemma3-4b", + "provider": "meta", + "ctx": 128000, + "maxOut": 8192, + "pIn": 0.03, + "pOut": 0.08, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "llamagate/nomic-embed-text", + "name": "llamagate/nomic-embed-text", + "provider": "meta", + "ctx": 8192, + "maxOut": null, + "pIn": 0.02, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "llamagate/qwen3-embedding-8b", + "name": "llamagate/qwen3-embedding-8b", + "provider": "meta", + "ctx": 40960, + "maxOut": null, + "pIn": 0.02, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "sarvam/sarvam-m", + "name": "sarvam/sarvam-m", + "provider": "sarvam", + "ctx": 8192, + "maxOut": 32000, + "pIn": null, + "pOut": null, + "tools": null, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-5-search-api", + "name": "gpt-5-search-api", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-5-search-api-2025-10-14", + "name": "gpt-5-search-api-2025-10-14", + "provider": "openai", + "ctx": 272000, + "maxOut": 128000, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": null, + "json": true, + "src": "litellm" + }, + { + "id": "gpt-realtime-mini-2025-10-06", + "name": "gpt-realtime-mini-2025-10-06", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.6, + "pOut": 2.4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gpt-realtime-mini-2025-12-15", + "name": "gpt-realtime-mini-2025-12-15", + "provider": "openai", + "ctx": 128000, + "maxOut": 4096, + "pIn": 0.6, + "pOut": 2.4, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini-2.0-flash-exp-image-generation", + "name": "gemini-2.0-flash-exp-image-generation", + "provider": "gemini", + "ctx": 32768, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.0-flash-exp-image-generation", + "name": "gemini/gemini-2.0-flash-exp-image-generation", + "provider": "gemini", + "ctx": 32768, + "maxOut": 32768, + "pIn": null, + "pOut": null, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini-2.5-flash-native-audio-latest", + "name": "gemini-2.5-flash-native-audio-latest", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 2.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini-2.5-flash-native-audio-preview-09-2025", + "name": "gemini-2.5-flash-native-audio-preview-09-2025", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 2.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini-2.5-flash-native-audio-preview-12-2025", + "name": "gemini-2.5-flash-native-audio-preview-12-2025", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 2.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini-3.1-flash-live-preview", + "name": "gemini-3.1-flash-live-preview", + "provider": "gemini", + "ctx": 131072, + "maxOut": 65536, + "pIn": 0.75, + "pOut": 4.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.5-flash-native-audio-latest", + "name": "gemini/gemini-2.5-flash-native-audio-latest", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 2.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.5-flash-native-audio-preview-09-2025", + "name": "gemini/gemini-2.5-flash-native-audio-preview-09-2025", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 2.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/gemini-2.5-flash-native-audio-preview-12-2025", + "name": "gemini/gemini-2.5-flash-native-audio-preview-12-2025", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 8192, + "pIn": 0.3, + "pOut": 2.5, + "tools": null, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini/gemini-3.1-flash-live-preview", + "name": "gemini/gemini-3.1-flash-live-preview", + "provider": "gemini", + "ctx": 131072, + "maxOut": 65536, + "pIn": 0.75, + "pOut": 4.5, + "tools": true, + "cot": null, + "json": null, + "src": "litellm" + }, + { + "id": "gemini-flash-latest", + "name": "gemini-flash-latest", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-flash-lite-latest", + "name": "gemini-flash-lite-latest", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.09999999999999999, + "pOut": 0.39999999999999997, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-pro-latest", + "name": "gemini-pro-latest", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini/gemini-pro-latest", + "name": "gemini/gemini-pro-latest", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 1.25, + "pOut": 10, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "gemini-exp-1206", + "name": "gemini-exp-1206", + "provider": "gemini", + "ctx": 1048576, + "maxOut": 65535, + "pIn": 0.3, + "pOut": 2.5, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "vertex_ai/claude-sonnet-4-6@default", + "name": "vertex_ai/claude-sonnet-4-6@default", + "provider": "claude", + "ctx": 1000000, + "maxOut": 64000, + "pIn": 3, + "pOut": 15, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock_mantle/openai.gpt-oss-120b", + "name": "bedrock_mantle/openai.gpt-oss-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock_mantle/openai.gpt-oss-20b", + "name": "bedrock_mantle/openai.gpt-oss-20b", + "provider": "openai", + "ctx": 131072, + "maxOut": 32768, + "pIn": 0.075, + "pOut": 0.3, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock_mantle/openai.gpt-oss-safeguard-120b", + "name": "bedrock_mantle/openai.gpt-oss-safeguard-120b", + "provider": "openai", + "ctx": 131072, + "maxOut": 65536, + "pIn": 0.15, + "pOut": 0.6, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock_mantle/openai.gpt-oss-safeguard-20b", + "name": "bedrock_mantle/openai.gpt-oss-safeguard-20b", + "provider": "openai", + "ctx": 131072, + "maxOut": 65536, + "pIn": 0.075, + "pOut": 0.3, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "volcengine/doubao-seed-2-0-pro-260215", + "name": "volcengine/doubao-seed-2-0-pro-260215", + "provider": "volcengine", + "ctx": 256000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "volcengine/doubao-seed-2-0-lite-260215", + "name": "volcengine/doubao-seed-2-0-lite-260215", + "provider": "volcengine", + "ctx": 256000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "volcengine/doubao-seed-2-0-mini-260215", + "name": "volcengine/doubao-seed-2-0-mini-260215", + "provider": "volcengine", + "ctx": 256000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "volcengine/doubao-seed-2-0-code-preview-260215", + "name": "volcengine/doubao-seed-2-0-code-preview-260215", + "provider": "volcengine", + "ctx": 256000, + "maxOut": 128000, + "pIn": null, + "pOut": null, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-east-1/zai.glm-5", + "name": "bedrock/us-east-1/zai.glm-5", + "provider": "bedrock", + "ctx": 200000, + "maxOut": 128000, + "pIn": 1, + "pOut": 3.1999999999999997, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-west-2/zai.glm-5", + "name": "bedrock/us-west-2/zai.glm-5", + "provider": "bedrock", + "ctx": 200000, + "maxOut": 128000, + "pIn": 1, + "pOut": 3.1999999999999997, + "tools": true, + "cot": true, + "json": null, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-east-1/anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "bedrock/us-gov-east-1/anthropic.claude-haiku-4-5-20251001-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1.2, + "pOut": 6, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + }, + { + "id": "bedrock/us-gov-west-1/anthropic.claude-haiku-4-5-20251001-v1:0", + "name": "bedrock/us-gov-west-1/anthropic.claude-haiku-4-5-20251001-v1:0", + "provider": "claude", + "ctx": 200000, + "maxOut": 64000, + "pIn": 1.2, + "pOut": 6, + "tools": true, + "cot": true, + "json": true, + "src": "litellm" + } + ] +} \ No newline at end of file diff --git a/deploy/MODAL_GEMMA_VLLM.md b/deploy/MODAL_GEMMA_VLLM.md new file mode 100644 index 0000000000000000000000000000000000000000..827871f75e864298bc180d60d4eb046d546a00e3 --- /dev/null +++ b/deploy/MODAL_GEMMA_VLLM.md @@ -0,0 +1,48 @@ +# JackAILocal Gemma 4 12B IT on Modal vLLM + +This deploys an OpenAI-compatible vLLM endpoint used by the hosted Hugging Face Space packaging agent. + +## Setup + +```bash +pip install modal +modal setup +modal secret create gemma-secrets \ + HF_TOKEN=hf_xxxxxxxxxxxxxxxxx \ + VLLM_API_KEY=change_this_long_random_value +``` + +## Deploy + +```bash +modal deploy deploy/modal_gemma_vllm.py +``` + +The deployed URL is used by the Hugging Face Space: + +```text +MODAL_BASE_URL=https://--jackailocal-gemma-4-12b-it-vllm-serve.modal.run +MODAL_API_KEY=change_this_long_random_value +MODEL_ID=google/gemma-4-12B-it +``` + +## Test + +```bash +curl "$MODAL_BASE_URL/v1/chat/completions" \ + -H "Authorization: Bearer $MODAL_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "google/gemma-4-12B-it", + "messages": [ + {"role": "system", "content": "Return only JSON."}, + {"role": "user", "content": "Return {\"ok\":true}."} + ], + "temperature": 0.1, + "max_tokens": 128, + "response_format": {"type": "json_object"}, + "chat_template_kwargs": {"enable_thinking": false} + }' +``` + +The endpoint is for packaging intelligence only. The generated JackAILocal runtime remains local/offline. diff --git a/deploy/appliance/Dockerfile b/deploy/appliance/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..9a92aa89daff4b862c2c723a59e7aaae7125f363 --- /dev/null +++ b/deploy/appliance/Dockerfile @@ -0,0 +1,33 @@ +FROM rust:1-bookworm AS builder +RUN apt-get update \ + && apt-get install -y --no-install-recommends cmake curl git python3 \ + && rm -rf /var/lib/apt/lists/* +WORKDIR /src +COPY . . +RUN cargo build --release --locked +RUN ./factory/unix/build-whisper-backend.sh \ + && ./unix/Install-Voice.sh /src + +FROM debian:bookworm-slim +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates \ + && rm -rf /var/lib/apt/lists/* +WORKDIR /opt/jackailocal +COPY --from=builder /src/target/release/jackailocald ./bin/jackailocald +COPY webui ./webui +COPY config ./config +COPY content ./content +COPY content-packs ./content-packs +COPY manifest ./manifest +COPY --from=builder /src/backends/whisper.cpp/linux ./backends/whisper.cpp/linux +COPY --from=builder /src/backends/piper/linux ./backends/piper/linux +COPY --from=builder /src/models/whisper ./models/whisper +COPY --from=builder /src/models/piper ./models/piper +COPY deploy/appliance/entrypoint.sh /usr/local/bin/jackailocal-entrypoint +RUN chmod +x /opt/jackailocal/bin/jackailocald /usr/local/bin/jackailocal-entrypoint \ + && mkdir -p models/ollama models/gguf workspace/documents workspace/threads workspace/exports .jackailocal/logs +ENV OLLAMA_HOST=http://ollama:11434 +ENV OLLAMA_NO_CLOUD=true +ENV RUST_LOG=info +EXPOSE 4891 +ENTRYPOINT ["/usr/local/bin/jackailocal-entrypoint"] diff --git a/deploy/appliance/README.md b/deploy/appliance/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0ce4d8fedf1b92e1f8b85623e17ca04d8f0f48b6 --- /dev/null +++ b/deploy/appliance/README.md @@ -0,0 +1,17 @@ +# JackAILocal LAN Appliance + +This deployment runs the real Rust runtime and a real Ollama service on a NAS or mini-PC. + +```bash +cd deploy/appliance +JACKAILOCAL_MODEL=qwen3.5:4b docker compose up --build -d +./show-url.sh +``` + +The first start pulls the selected model into the persistent `ollama-models` volume. After that preload completes, normal use is local. API requests from LAN clients require the pairing token included in the URL printed by `show-url.sh`. + +Stop the appliance with: + +```bash +docker compose down +``` diff --git a/deploy/appliance/docker-compose.yml b/deploy/appliance/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..23938c99b857f7400b97d4b3cc8663681a4b63ce --- /dev/null +++ b/deploy/appliance/docker-compose.yml @@ -0,0 +1,47 @@ +services: + ollama: + image: ollama/ollama:latest + restart: unless-stopped + environment: + OLLAMA_NO_CLOUD: "true" + volumes: + - ollama-models:/root/.ollama + healthcheck: + test: ["CMD", "ollama", "list"] + interval: 5s + timeout: 5s + retries: 30 + + model-init: + image: ollama/ollama:latest + depends_on: + ollama: + condition: service_healthy + environment: + OLLAMA_HOST: http://ollama:11434 + OLLAMA_NO_CLOUD: "true" + volumes: + - ollama-models:/root/.ollama + command: ["pull", "${JACKAILOCAL_MODEL:-qwen3.5:4b}"] + restart: "no" + + jackailocal: + build: + context: ../.. + dockerfile: deploy/appliance/Dockerfile + restart: unless-stopped + depends_on: + model-init: + condition: service_completed_successfully + environment: + OLLAMA_HOST: http://ollama:11434 + OLLAMA_NO_CLOUD: "true" + ports: + - "${JACKAILOCAL_PORT:-4891}:4891" + volumes: + - jackailocal-workspace:/opt/jackailocal/workspace + - ollama-models:/opt/jackailocal/models/ollama:ro + +volumes: + ollama-models: + jackailocal-workspace: diff --git a/deploy/appliance/entrypoint.sh b/deploy/appliance/entrypoint.sh new file mode 100644 index 0000000000000000000000000000000000000000..fccff0b0ece252d52bdfeabf9bb39c4c8f343078 --- /dev/null +++ b/deploy/appliance/entrypoint.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env sh +set -eu + +cd /opt/jackailocal +mkdir -p workspace/documents workspace/threads workspace/exports workspace/settings .jackailocal/logs +if [ ! -f workspace/settings/settings.json ]; then + printf '%s\n' '{"phone_access_enabled":true}' > workspace/settings/settings.json +fi +exec ./bin/jackailocald serve --config config/jackailocal.appliance.toml diff --git a/deploy/appliance/nim-compose.example.yml b/deploy/appliance/nim-compose.example.yml new file mode 100644 index 0000000000000000000000000000000000000000..184707e691eb614264beccd7676f2cb5a954f458 --- /dev/null +++ b/deploy/appliance/nim-compose.example.yml @@ -0,0 +1,31 @@ +# Exemple de déploiement NVIDIA NIM pour l'appliance JackAILocal (GPU NVIDIA). +# Le conteneur est tiré UNE FOIS au build (Internet + clé NGC requis), puis +# tourne entièrement hors ligne chez le client. +# +# docker login nvcr.io # user: $oauthtoken / password: clé API NGC +# docker compose -f nim-compose.example.yml pull +# docker compose -f nim-compose.example.yml up -d +# +# L'API (compatible OpenAI) écoute sur 127.0.0.1:8000. Configurer ensuite +# jackailocald: [backends.llamacpp] enabled = true, url = "http://127.0.0.1:8000". +# Remplacer l'image par le modèle choisi sur build.nvidia.com/models et +# vérifier la licence du modèle avant toute distribution commerciale. +services: + nim-llm: + image: nvcr.io/nim/qwen/qwen3.5-9b-instruct:latest + runtime: nvidia + environment: + - NGC_API_KEY=${NGC_API_KEY:-} + - NIM_CACHE_PATH=/opt/nim/.cache + volumes: + - ./nim-cache:/opt/nim/.cache + ports: + - "127.0.0.1:8000:8000" + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu] + restart: unless-stopped diff --git a/deploy/appliance/show-url.sh b/deploy/appliance/show-url.sh new file mode 100644 index 0000000000000000000000000000000000000000..2f7f50b189519dc5b68bad42b04251350f06c077 --- /dev/null +++ b/deploy/appliance/show-url.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env sh +set -eu + +COMPOSE_FILE="${1:-$(dirname "$0")/docker-compose.yml}" +token="$(docker compose -f "$COMPOSE_FILE" exec -T jackailocal sh -c "sed -n 's/.*\"phone_access_token\"[[:space:]]*:[[:space:]]*\"\\([^\"]*\\)\".*/\\1/p' workspace/settings/settings.json" | tr -d '\r\n')" +if [ -z "$token" ]; then + echo "Pairing token is not available yet. Wait for the jackailocal service to start." >&2 + exit 1 +fi +host_ip="$(hostname -I 2>/dev/null | awk '{print $1}')" +host_ip="${host_ip:-127.0.0.1}" +port="${JACKAILOCAL_PORT:-4891}" +echo "http://$host_ip:$port/?pair=$token" diff --git a/deploy/modal_gemma_vllm.py b/deploy/modal_gemma_vllm.py new file mode 100644 index 0000000000000000000000000000000000000000..c8bd6698f336ff8325190fd608dbcf57f8c0476a --- /dev/null +++ b/deploy/modal_gemma_vllm.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +import modal + +APP_NAME = "jackailocal-gemma-4-12b-it-vllm" +MODEL_NAME = "google/gemma-4-12B-it" +VLLM_PORT = 8000 +MINUTES = 60 + +app = modal.App(APP_NAME) + +hf_cache_vol = modal.Volume.from_name("jackailocal-gemma-hf-cache", create_if_missing=True) +vllm_cache_vol = modal.Volume.from_name("jackailocal-gemma-vllm-cache", create_if_missing=True) + +image = ( + modal.Image.from_registry( + "nvidia/cuda:12.9.0-devel-ubuntu22.04", + add_python="3.12", + ) + .entrypoint([]) + .uv_pip_install( + "vllm", + "huggingface-hub", + "fastapi[standard]", + ) + .env( + { + "HF_XET_HIGH_PERFORMANCE": "1", + "VLLM_LOG_STATS_INTERVAL": "10", + } + ) +) + + +@app.function( + image=image, + gpu="A100-80GB", + timeout=10 * MINUTES, + scaledown_window=10 * MINUTES, + secrets=[modal.Secret.from_name("gemma-secrets")], + volumes={ + "/root/.cache/huggingface": hf_cache_vol, + "/root/.cache/vllm": vllm_cache_vol, + }, +) +@modal.concurrent(max_inputs=32) +@modal.web_server(port=VLLM_PORT, startup_timeout=10 * MINUTES) +def serve() -> None: + import os + import subprocess + + api_key = os.environ["VLLM_API_KEY"] + + cmd = [ + "vllm", + "serve", + MODEL_NAME, + "--served-model-name", + MODEL_NAME, + "--host", + "0.0.0.0", + "--port", + str(VLLM_PORT), + "--api-key", + api_key, + "--max-model-len", + "8192", + "--gpu-memory-utilization", + "0.90", + "--uvicorn-log-level", + "info", + ] + + subprocess.Popen(cmd).wait() diff --git a/diagnostics/hardware.json b/diagnostics/hardware.json new file mode 100644 index 0000000000000000000000000000000000000000..a1cb716150d1b60d9da6cf4b830885867ff5db5a --- /dev/null +++ b/diagnostics/hardware.json @@ -0,0 +1,26 @@ +{ + "collected_at": "2026-06-10T21:38:17.9694218-04:00", + "os": "windows", + "cpu_name": "12th Gen Intel(R) Core(TM) i9-12900K", + "cpu_threads": 24, + "ram_gb": 31.8, + "gpus": [ + { + "name": "Meta Virtual Monitor", + "vram_gb": 0, + "driver": "11.1.45.729" + }, + { + "name": "Intel(R) UHD Graphics 770", + "vram_gb": 2, + "driver": "32.0.101.6129" + }, + { + "name": "NVIDIA GeForce RTX 3090", + "vram_gb": 4, + "driver": "32.0.15.9186" + } + ], + "vram_gb": 4, + "whichllm": null +} diff --git a/docs/BACKEND_INSTALLATION.md b/docs/BACKEND_INSTALLATION.md new file mode 100644 index 0000000000000000000000000000000000000000..ed05c78fb1d4d41814c8e2da3ad91c8a271555ed --- /dev/null +++ b/docs/BACKEND_INSTALLATION.md @@ -0,0 +1,29 @@ +# Backend Installation + +## Ollama + +Ollama is the default backend installed by the platform builders. + +```text +backends/ollama/windows/ollama.exe +backends/ollama/macos/ollama +backends/ollama/linux/ollama +models/ollama/ +``` + +Launchers set `OLLAMA_MODELS` so models remain inside the JackAILocal target. + +## llama.cpp + +The Rust runtime can call a reachable OpenAI-compatible `llama.cpp` server at `LLAMA_CPP_URL`. This is an optional local chat fallback and is not required by the standard builder payload. + +## Voice + +```text +backends/whisper.cpp//whisper-cli +backends/piper//piper +models/whisper/ggml-base.bin +models/piper/en_US-libritts_r-medium.onnx +``` + +`windows/Install-Voice.ps1` and `unix/Install-Voice.sh` download verified official assets. Linux and macOS build whisper.cpp from the pinned source tag before installing the model assets. diff --git a/docs/BUILDER_PATH_AND_MANIFEST_AUTO_CAPTURE.md b/docs/BUILDER_PATH_AND_MANIFEST_AUTO_CAPTURE.md new file mode 100644 index 0000000000000000000000000000000000000000..f94792f6178de6b139f2864e0442457a4de5922b --- /dev/null +++ b/docs/BUILDER_PATH_AND_MANIFEST_AUTO_CAPTURE.md @@ -0,0 +1,42 @@ +# Builder path and manifest auto-capture + +`BUILD-USB.cmd` and `TEST-WITHOUT-USB.cmd` must be executed from inside the extracted JackAILocal Builder folder. + +They must not be copied alone to the target USB key. + +Correct structure: + +```text +JackAILocal-Builder-v12/ +├── BUILD-USB.cmd +├── TEST-WITHOUT-USB.cmd +├── client-builder/ +│ └── windows/ +│ └── JackAILocal-USB-Builder.ps1 +├── manifest-input/ +├── webui/ +├── config/ +└── windows/ +``` + +The builder automatically looks for Gradio-generated build manifests in: + +1. The extracted builder folder. +2. `manifest-input/` inside the extracted builder folder. +3. The user's `Downloads` folder. + +Expected filename pattern: + +```text +jackailocal-build-manifest-*.zip +``` + +The user does not extract the manifest ZIP. The builder reads it directly. + +After the target USB/SSD is built, the customer only uses: + +```text +START-HERE.cmd +``` + +The target USB does not need the `client-builder/` folder. diff --git a/docs/BUILDER_ZIP_AUTO_CAPTURE.md b/docs/BUILDER_ZIP_AUTO_CAPTURE.md new file mode 100644 index 0000000000000000000000000000000000000000..60b49c096f8b058e6859cafcd986612297dd1e8d --- /dev/null +++ b/docs/BUILDER_ZIP_AUTO_CAPTURE.md @@ -0,0 +1,40 @@ +# Builder ZIP Auto-Capture Policy + +The user must not manually extract a manifest ZIP, find a JSON file, or pass command-line arguments. + +## Correct SaaS output + +The Gradio/SaaS layer should generate a platform-specific builder package: + +- `JackAILocal-Windows-Builder.zip` +- `JackAILocal-macOS-Builder.zip` + +Each builder ZIP contains: + +- the build manifest already embedded under `manifest/build-manifest.json` +- the one-click builder script for the target OS +- the runtime template +- the UI +- the config +- the model policy + +## Correct user flow + +1. Download builder ZIP. +2. Open/extract ZIP. +3. Insert USB/SSD. +4. Double-click the builder. +5. Confirm target drive. +6. Wait. +7. Launch JackAILocal from the prepared USB/SSD. + +## Incorrect user flow + +- Download manifest ZIP. +- Extract manifest manually. +- Copy JSON into another folder. +- Run PowerShell with a manifest path. +- Understand model IDs. +- Understand runtime folders. + +The manifest exists, but it is an internal input to the builder, not a user-facing artifact. diff --git a/docs/BUILD_AND_SHIP_CHECKLIST.md b/docs/BUILD_AND_SHIP_CHECKLIST.md new file mode 100644 index 0000000000000000000000000000000000000000..ef7fd3569f36b6e2f97552fb69fdba70e049fccb --- /dev/null +++ b/docs/BUILD_AND_SHIP_CHECKLIST.md @@ -0,0 +1,28 @@ +# Build and Ship Checklist + +## Before Building + +- Stage the platform `jackailocald` runtime. +- Stage the platform Ollama binary. +- Confirm the selected Ollama model references. +- Confirm enough target disk space. +- Keep `config/update-private-key.xml` outside every exported target. + +## Acceptance + +- At least one Ollama model exists in `models/ollama`. +- Voice status reports whisper.cpp and Piper available. +- Chat returns a local response. +- SCOUT returns a local image response with a compatible model. +- Thread create, rename, filter, and delete work. +- AES export/import works with a passphrase. +- Phone Access remains disabled by default. +- Support ZIP excludes conversations and documents. +- SHA-256 manifest verification passes. + +## Platform Release Work + +- Sign Windows binaries and installer. +- Sign and notarize the macOS application. +- Test antivirus, SmartScreen, Gatekeeper, and external-drive policies. +- Test the target offline after preload. diff --git a/docs/COMMERCIALIZATION_PLAN.md b/docs/COMMERCIALIZATION_PLAN.md new file mode 100644 index 0000000000000000000000000000000000000000..cd30c221c987ab3a043dc695e0bfd023ddc692af --- /dev/null +++ b/docs/COMMERCIALIZATION_PLAN.md @@ -0,0 +1,134 @@ +# Plan de commercialisation JackAILocal + +Synthèse de la recherche multi-agents du 2026-06-09 (deep-research : 108 agents, +26 sources, 128 claims extraits, 23 confirmés par vérification adversariale 3-0, +2 réfutés) + 3 recherches ciblées complémentaires (licences de composants, +conformité Québec/Canada, commerce/concurrence). Les éléments marqués ✅ sont +déjà implémentés dans ce repo. + +--- + +## 1. État du produit vs exigences commerciales + +| Exigence | État | +|---|---| +| Runtime offline, WebUI bilingue EN/FR | ✅ existant (v15) | +| Système de licence client offline (Ed25519) | ✅ implémenté (`src/license.rs`, `/api/license`, CLI `jackailocald license keygen/issue/verify`, panneau Réglages WebUI) | +| Mises à jour offline signées (USB, air-gapped) | ✅ implémenté (`updates/Create-UpdatePackage.ps1`, `updates/JackAILocal-Update-Offline.ps1`, backup + rollback, testé bout en bout) | +| Hooks de signature de code dans les builds | ✅ implémenté (`factory/powershell/Sign-Executables.ps1`, `factory/unix/sign-and-notarize-macos.sh`, intégrés aux builders/installateurs) | +| EULA + politique de confidentialité EN+FR | ✅ rédigés (`legal/`) — **à faire réviser par un avocat avant la première vente** | +| Notices tierces complètes | ✅ (`licenses/THIRD_PARTY_NOTICES.md`, `NOTICE_GEMMA.txt`, `GEMMA_TERMS_OF_USE.txt`, `APACHE-2.0.txt`, `MIT_COMPONENTS.txt`) | +| Voix TTS commercialement redistribuable | ✅ corrigé — lessac (non-commercial) remplacée par `en_US-libritts_r-medium` (CC BY 4.0) | +| Certificats de signature réels | ❌ à acquérir (voir §3) | +| Boutique / paiement / fabrication USB | ❌ à mettre en place (voir §6) | +| Inscription TPS/TVQ, responsable Loi 25 | ❌ démarches administratives (voir §5) | + +## 2. Licences des composants redistribués (vérifié) + +| Composant | Licence | Vente sur USB | Obligations | +|---|---|---|---| +| Qwen3/3.5/3.6 + VL (toutes tailles) | Apache 2.0 (annonce avril 2025, vérifié par taille) | OK | Copie de licence + notices | +| Gemma 4 | Apache 2.0 (`ai.google.dev/gemma/apache_2`) | OK | Copie de licence + notices | +| Gemma ≤3 | Gemma Terms of Use (révocable, modifiable par Google) | OK sous conditions | Terms remis au client, Prohibited Use Policy exécutoire dans l'EULA (✅ clause ajoutée), fichier NOTICE exact (✅) | +| Llama 4 (si ajouté) | Llama Community License | OK | « Built with Llama » visible, copie licence, NOTICE exact, AUP flow-down. **Interdit aux entités UE (multimodal)** — bloquant pour white-label européen | +| Whisper (code + poids) | MIT (c) OpenAI 2022 | OK | Notice MIT (✅) | +| whisper.cpp / llama.cpp | MIT (c) The ggml authors | OK | Notice MIT (✅) | +| Ollama | MIT (c) Ollama | OK | Notice MIT (✅). **Marque** : ne pas utiliser nom/logo Ollama dans le branding du produit ni suggérer une affiliation | +| Piper | MIT | OK | Notice MIT (✅) | +| Voix Piper | **Par voix !** | Variable | lessac/Blizzard = NON-COMMERCIAL (retirée ✅). Sûres : LibriTTS/LibriTTS-R (CC BY 4.0), LJSpeech (domaine public). Vérifier la MODEL_CARD de chaque voix ajoutée. espeak-ng (phonemizer) est GPL-3.0 — Piper l'appelle comme bibliothèque ; à réévaluer si linkage statique custom | + +## 3. Signature de code (vérifié, état 2026) + +- **Windows** : les certificats EV ne contournent **plus** SmartScreen (retiré ~mars 2024 ; la croyance contraire est obsolète — réfutée 0-3). Recommandation : **Azure Artifact Signing** (ex-Trusted Signing), ~9,99 $US/mois (5 000 signatures), clés en HSM, intégration CI. Caveat : éligibilité = organisation avec ~3 ans d'historique vérifiable ; une société nouvellement incorporée pourrait devoir passer par un certificat OV classique (jeton matériel obligatoire depuis juin 2023, ~250-400 $US/an). La réputation SmartScreen se construit sur des semaines dans tous les cas. **Smart App Control (Win 11) bloque les exécutables non signés même depuis une clé USB** → signature obligatoire en pratique. Hook prêt : variables `JACKAI_SIGN_CERT_THUMBPRINT` ou `JACKAI_SIGN_PFX`. +- **macOS** : Developer ID (99 $US/an Apple Developer Program) + notarisation + **stapling obligatoire pour le hors-ligne**. Le stapling ne fonctionne que sur .app/.dmg/.pkg — **les binaires nus sur USB ne peuvent pas être staplés** : livrer le runtime macOS en bundle .app sur la clé (le build-dmg.sh signe + notarise + staple ✅). macOS Sequoia+ a retiré le contournement Ctrl-clic. + +## 4. Licensing client offline (vérifié) + +Le pattern implémenté (fichiers de licence signés Ed25519, vérification locale +par clé publique embarquée, activation par transfert physique de fichier) est +exactement le standard de l'industrie (Keygen, LicenseSpring, Cryptlex). +Décisions prises : licences perpétuelles par défaut (`expires` optionnel), +pas de TTL ni de révocation à distance (cohérent avec l'air-gapped ; la +révocation à distance est impossible hors-ligne par nature). + +Flux vendeur : +``` +jackailocald license keygen --out-dir license-keys # une fois; garder license-keys/ hors ligne, JAMAIS dans un package ni git (exclusions ✅) +copier license-keys/license-public-key.txt -> config/license-public-key.txt du package +jackailocald license issue --key license-keys/license-signing-key.txt --customer-name "Client" --edition pro --out license/license.json +# license/license.json est préchargé sur la clé USB du client +``` + +## 5. Conformité Québec/Canada (vérifié) + +- **Charte de la langue française** : logiciel disponible en français (✅ WebUI), + emballage/docs en français (✅ README bilingue FR d'abord, EULA/privacy FR), + **version française de l'EULA remise en premier** (art. 55, contrat + d'adhésion). Site web et factures en français aussi. Amendes entreprises + 3 000–30 000 $/infraction/jour. +- **Loi 25** : s'applique à l'entreprise même si le produit ne collecte rien — + désigner un **responsable de la protection des renseignements personnels**, + publier titre+coordonnées sur le site, politique de confidentialité pour la + boutique (✅ modèle rédigé, sections [À COMPLÉTER] à remplir). +- **TPS/TVQ** : 5 % + 9,975 %. Inscription obligatoire au-delà de 30 000 $ de + ventes taxables sur 4 trimestres. USB = bien corporel (taxé selon lieu de + livraison ; export hors Canada détaxé). Licence téléchargée = incorporel + (taxée selon l'adresse du preneur). +- **LPC** : garantie légale art. 37-38 s'applique. Vente à distance : divulgations + préalables obligatoires, contrat remis ≤15 jours, résolution 7 jours si + déficient. **Garantie de bon fonctionnement (5 oct. 2026)** : ne couvre ni + USB ni logiciel, **mais une appliance = ordinateur neuf → 3 ans pièces et + main-d'œuvre obligatoires**, durée affichée près du prix. +- **Export crypto (AES-256)** : exemption « marché de masse » (Note 3) pour le + logiciel grand public vendu sans restriction ; É.-U. toujours OK ; pour les + ventes B2B/appliance hors marché de masse, considérer le permis EU+5. +- **IA** : aucune loi fédérale en vigueur (AIDA morte au feuilleton, non + réintroduite mi-2026). Restent : Loi sur la concurrence (pas d'« AI + washing »), responsabilité civile CCQ/LPC (✅ disclaimer IA dans l'EULA). + +## 6. Commerce et fabrication (recherche, non vérifié adversarialement) + +- **Concurrence directe** : PortableMind — USB 49 $ (CORE) / 79 $ (v1.5 voix+vision) / + 129 $ (drive rapide) / 199 $ (PRO Navigator) ; appliance « Survival Terminal » + ~699 $US. Haut de gamme : NVIDIA DGX Spark 4 699 $US. **Trou de marché entre + 200 $ et 4 700 $** pour une appliance PME — exactement le créneau + JackAILocal appliance/LAN. Le DIY gratuit (Ollama+AnythingLLM) plafonne la + valeur du contenu brut : la valeur vendable = curation + UX + licence + support. +- **Pricing de référence** : consumer one-time 50-150 $ ; prosumer 149 $/an ou + 349 $ lifetime (Msty) ; entreprise ~500 $US/mois (LM Studio). « Pay once, no + subscription » est l'argument central du segment. +- **Suggestion de gamme** (à valider) : Personnelle USB 99-129 $CA ; Pro SSD + rapide 199-249 $CA ; Affaires/appliance 999-2 499 $CA + contrat de mise à + jour annuel ; white-label : licence builder + redevance par unité. +- **Plateformes** : Paddle et Lemon Squeezy refusent le physique → **Shopify + (Basic ~49 $CA/mois, Shopify Payments 2,8 %+30 ¢) pour USB/SSD/appliances + + licences numériques**, taxes canadiennes automatisées (le vendeur reste + responsable de la conformité). Stripe direct est l'option du concurrent. +- **Duplication USB** : préchargement 30-60 Go = service payant au Go partout + (le « preload gratuit » est limité à ~1 Go) ; ~12-15 $/unité à petite + quantité, devis obligatoire au volume (NAND volatil). **Pour 100 Go+, SSD + externes préférables** (vitesse de chargement des modèles = axe de pricing + prouvé par le tier MAX-SPEED du concurrent). Option interne : SSD retail + (~80-100 $/To) + duplicateur 1→N. + +## 7. Checklist avant la première vente + +1. [ ] Incorporation/NEQ, inscription TPS-TVQ (si > 30 k$ ou volontaire). +2. [ ] Réviser EULA/Privacy par avocat (qc) ; compléter les champs [À COMPLÉTER]. +3. [ ] Désigner le responsable Loi 25 et publier ses coordonnées sur le site. +4. [ ] **Régénérer toutes les clés** : `updates/Generate-UpdateKeys.ps1` (les clés du repo de dev sont compromises par définition) + `jackailocald license keygen`. Garder les privées hors ligne. +5. [ ] Acquérir la signature Windows (Artifact Signing ou OV) et Apple Developer ID ; exporter `JACKAI_SIGN_*`/`JACKAI_CODESIGN_*` dans le pipeline. +6. [ ] Construire un package pilote, vérifier : SmartScreen/Gatekeeper, panneau Licence, docs légaux présents, `licenses/` complet, voix libritts_r. +7. [ ] Lancer la réputation SmartScreen tôt (distribuer un utilitaire signé gratuit ou la version d'essai plusieurs semaines avant le lancement). +8. [ ] Site web bilingue (français d'abord), boutique Shopify, fiches produits avec divulgations LPC (vente à distance). +9. [ ] Processus de support : courriel support, SLA simple, flux « dossier de soutien » documenté. +10. [ ] Premier lot USB/SSD : devis duplication, test de chargement des modèles sur le média réel (vitesse), QA `qa/static_audit.py` + `Assert-TargetReady`. + +## 8. Risques résiduels + +- **Gemma ≤3** : licence révocable/modifiable unilatéralement par Google → préférer Gemma 4 (Apache 2.0) comme agent de config ; vérifier la version réellement tirée par `ollama pull`. +- **Marque Ollama** : pas de brand guidelines publiées ; rester « composant tiers », ne jamais co-brander. +- **Éligibilité Artifact Signing** (3 ans d'historique) : plan B = certificat OV sur jeton. +- **Appliance + garantie 2026** : intégrer 3 ans de garantie matériel dans le prix de l'appliance. +- **macOS sur USB** : livrer en .app staplé, pas en binaires nus (Gatekeeper hors-ligne). +- Sources et verbatims complets : sortie du workflow deep-research du 2026-06-09 (23 claims 3-0 ; 2 réfutés dont « EV contourne SmartScreen »). diff --git a/docs/CURRENT_PACKAGE_STATE.md b/docs/CURRENT_PACKAGE_STATE.md new file mode 100644 index 0000000000000000000000000000000000000000..7e6fb6c85de2ccdb0a81f469bbf10378d9ef2167 --- /dev/null +++ b/docs/CURRENT_PACKAGE_STATE.md @@ -0,0 +1,29 @@ +# Current Package State + +## Implemented + +- Rust local runtime and bilingual WebUI +- Real Ollama chat and SCOUT requests +- Real local LLM-in-the-loop config agent endpoint, with Gemma 4 12B as the preferred agent model when installed +- whisper.cpp speech-to-text and Piper text-to-speech +- Persistent thread history +- AES-256-GCM transfer packs +- Opt-in paired Phone Access +- Field Manual and prompt packs +- Windows local-folder, USB, and SSD builders +- macOS local-folder, USB, and SSD builders +- Windows installer and macOS DMG build sources +- Docker LAN appliance +- Linux boot-disk preparation +- Gradio manifest and builder package export + +## Environment-Dependent + +- Windows code signing +- Apple signing and notarization +- Physical USB and SSD performance +- Linux boot hardware and driver compatibility +- Installer compiler availability +- Additional Ollama model download time and disk space + +The builders fail when required runtime assets are absent. They do not report an incomplete target as ready. diff --git a/docs/DEFAULT_MODEL_POLICY.md b/docs/DEFAULT_MODEL_POLICY.md new file mode 100644 index 0000000000000000000000000000000000000000..23e52f6e9313b367206924ced4476a55064da8a8 --- /dev/null +++ b/docs/DEFAULT_MODEL_POLICY.md @@ -0,0 +1,27 @@ +# JackAILocal Default Model Policy + +JackAILocal must include the same product idea expressed in the referenced market discussion: the bundled models are a sensible default for general offline use, not the product boundary. + +## Required policy + +1. Ship or build a default model pack that works on a normal laptop. +2. Do not market JackAILocal as only a single 9B model. +3. Let users keep, remove, or add compatible models. +4. The SaaS builder selects a model set from detected hardware. +5. Normal USB insertion must not download models. +6. Model downloads happen during factory preparation, customer build, model-add workflow, or signed update. +7. The value proposition is the turnkey offline package: runtime, model policy, UI, diagnostics, support bundle, presets, and content packs. + +## Product wording + +Use: + +"JackAILocal ships with a practical offline default pack sized for normal laptops. It is not locked to that pack. The builder can prepare larger model sets when the target machine supports them." + +Avoid: + +"JackAILocal is a 9B model on a USB." + +## Hackathon rule + +For the Hugging Face Build Small submission, keep all selectable models under or equal to the 32B parameter constraint. Avoid ambiguous models whose upstream card reports more than 32B parameters. diff --git a/docs/FACTORY_COMMANDS.md b/docs/FACTORY_COMMANDS.md new file mode 100644 index 0000000000000000000000000000000000000000..4635f9be183fd6f7f56d1e546bc415f9815a6511 --- /dev/null +++ b/docs/FACTORY_COMMANDS.md @@ -0,0 +1,28 @@ +# Factory Commands + +## Windows portable build + +Open PowerShell as normal user on the factory PC: + +```powershell +cd X:\ +powershell -ExecutionPolicy Bypass -File .\factory\powershell\Install-All.ps1 -BrandName "JackAILocal" +``` + +This builds the Rust router, installs backends, pulls models onto the drive, applies branding, generates hashes and runs preflight. + +## Windows build without model pull + +```powershell +powershell -ExecutionPolicy Bypass -File .\factory\powershell\Install-All.ps1 -SkipModels +``` + +## Linux boot disk build + +Run from a Linux factory machine: + +```bash +sudo ./linux/factory/prepare-linux-boot-disk.sh /dev/sdX +``` + +This destroys the target disk. diff --git a/docs/FACTORY_OVERVIEW.md b/docs/FACTORY_OVERVIEW.md new file mode 100644 index 0000000000000000000000000000000000000000..e294238aa85a675fd81b212371e5cd3b9f6eeb8b --- /dev/null +++ b/docs/FACTORY_OVERVIEW.md @@ -0,0 +1,38 @@ +# Factory Overview + +## Goal + +Produce a USB/SSD that works immediately when inserted into a Windows machine and also has an optional Linux boot mode. + +## Manufacturing sequence + +1. Format or prepare the drive. +2. Copy this runtime kit to the drive root. +3. Run factory/powershell/Prepare-WindowsPortableDrive.ps1 on a connected factory PC. +4. Download or copy backends into backends/*. +5. Pull/preload LLM models into models/ollama and/or models/gguf. +6. Build JackAILocald Rust binaries for Windows and Linux. +7. Apply branding. +8. Generate manifest and hashes. +9. Run QA benchmark. +10. Ship. + +## Customer sequence + +Windows path: + +1. Insert drive. +2. Open drive. +3. Double-click START-HERE.cmd. +4. JackAILocal starts the local backend and opens the UI. + +Linux path: + +1. Insert drive. +2. Reboot computer. +3. Choose USB from boot menu. +4. Linux starts JackAILocal in kiosk mode. + +## Model policy + +Models must be on the drive before shipment. The user should never be forced to download a model at first run. Optional extra models can be downloaded later if the user deliberately uses model management while online. diff --git a/docs/GRADIO_SAAS_SECTION.md b/docs/GRADIO_SAAS_SECTION.md new file mode 100644 index 0000000000000000000000000000000000000000..647d4ab07c45d3cf09912e13a0c6c6e776edec92 --- /dev/null +++ b/docs/GRADIO_SAAS_SECTION.md @@ -0,0 +1,108 @@ +# Optional Gradio SaaS section + +## Decision + +JackAILocal keeps its existing product architecture. Gradio is added only as an optional SaaS/configuration surface. + +```text +Gradio SaaS layer +↓ +hardware profile intake +↓ +model/backend recommendation +↓ +build manifest export +↓ +existing JackAILocal builder/factory pipeline +↓ +prepared USB key +↓ +offline JackAILocal runtime +``` + +## What changes + +Added: + +- `saas/gradio/app.py` +- `saas/gradio/requirements.txt` +- `saas/gradio/README.md` +- `scripts/run-gradio-saas-preview.cmd` +- `docs/GRADIO_SAAS_SECTION.md` + +## What does not change + +Unchanged: + +- Windows portable flow +- Linux bootable flow +- `jackailocald` Rust runtime +- USB web UI +- backend policy +- model storage policy +- update signing policy +- offline runtime behavior + +## Intended use + +The Gradio layer is for: + +- hackathon demonstration +- SaaS MVP preview +- customer hardware profile collection +- model recommendation display +- package manifest generation +- one-click local target preparation when Gradio runs on the operator's Windows machine +- platform package publication for Windows, macOS, and Linux/Docker appliance targets + +It is not the production USB runtime. + +## Model download policy + +Models are never downloaded on every USB insertion. + +Allowed download moments: + +1. factory preparation +2. customer package build +3. explicit signed update +4. explicit advanced add-model workflow + +Normal use after preparation is offline. + +## Security boundary + +The Gradio SaaS layer is online by nature. The USB runtime is offline by design. + +Rules: + +- no private signing keys in the Gradio app +- no shell/system tools exposed from hosted Gradio +- local Gradio may call the checked-in builder scripts only for explicit operator-initiated target preparation +- no model execution claims unless a real model backend is attached +- no automatic LAN exposure +- no runtime override of USB security defaults + +## Production path + +Hackathon/MVP: + +```text +Gradio only +``` + +Production: + +```text +Gradio frontend +↓ +FastAPI API +↓ +build worker +↓ +object storage +↓ +signed manifests +↓ +existing JackAILocal builder +``` diff --git a/docs/HACKATHON_MODEL_SELECTION.md b/docs/HACKATHON_MODEL_SELECTION.md new file mode 100644 index 0000000000000000000000000000000000000000..99038b0f86e0b1fdec0351cadeff1d6ee19586e9 --- /dev/null +++ b/docs/HACKATHON_MODEL_SELECTION.md @@ -0,0 +1,22 @@ +# Hackathon Model Selection + +The hackathon package should demonstrate that JackAILocal can choose models up to the allowed small-model limit while still offering a safe default. + +## Default + +Use a 4B or 9B model for the public demo because it is practical on normal hardware. + +## Power profile + +When the uploaded, manually entered, or locally scanned profile shows 24 GB VRAM, unlock the Power profile and recommend a 27B or 30B candidate that stays inside the 32B rule. + +## Rule + +Do not include models above the hackathon maximum. Do not include ambiguous models whose published parameter count exceeds the rule. + + +## Qwen3.6-27B update + +Qwen3.6-27B is allowed as a Power profile candidate because it is a 27B model and stays under the hackathon 32B cap. It should be presented as a high-end profile for RTX 3090-class hardware, not as the ordinary laptop default. + +The default demo should still run with a smaller model, such as Qwen3.5 4B or 9B, because the Space and common laptops should not depend on 24 GB VRAM. diff --git a/docs/HARDWARE_PROFILE_JSON.md b/docs/HARDWARE_PROFILE_JSON.md new file mode 100644 index 0000000000000000000000000000000000000000..3ce39722d436c6cd33091989303e371b85c5e39c --- /dev/null +++ b/docs/HARDWARE_PROFILE_JSON.md @@ -0,0 +1,59 @@ +# Hardware Profile JSON + +JackAILocal SaaS Builder accepts an optional hardware profile JSON object. + +If the field is empty, the selected sample profile is used. + +## Minimum fields + +```json +{ + "os": "windows", + "cpu_threads": 8, + "ram_gb": 16, + "vram_gb": 0, + "gpu_name": "CPU-only office laptop", + "disk": "USB 3.2 SSD", + "usb_storage_gb": 128 +} +``` + +## RTX 3090 example + +```json +{ + "os": "windows", + "cpu_threads": 24, + "ram_gb": 64, + "vram_gb": 24, + "gpu_name": "NVIDIA RTX 3090", + "disk": "USB-C NVMe", + "usb_storage_gb": 512 +} +``` + +## Optional whichllm block + +```json +{ + "os": "windows", + "cpu_threads": 24, + "ram_gb": 64, + "vram_gb": 24, + "gpu_name": "NVIDIA RTX 3090", + "disk": "USB-C NVMe", + "usb_storage_gb": 512, + "whichllm": { + "source": "optional", + "top_model": "Qwen/Qwen3.6-27B" + } +} +``` + +## Selection behavior + +The current hackathon Gradio app is rule-based. It does not perform a live hardware scan inside the browser. + +The selected sample profile or pasted JSON is normalized, then compared against the signed model catalog. + +The standard pack defaults to a practical laptop baseline up to 9B. Power profiles can select compatible models up to the hackathon limit of 32B. diff --git a/docs/LANGUAGE_POLICY.md b/docs/LANGUAGE_POLICY.md new file mode 100644 index 0000000000000000000000000000000000000000..7ff7c73087b3c0c218f30f71d6873a78c3d5685f --- /dev/null +++ b/docs/LANGUAGE_POLICY.md @@ -0,0 +1,25 @@ +# Language policy + +JackAILocal follows this language split: + +```text +Code identifiers: English +Code comments: English +Shell/PowerShell output: English +Documentation: English +UI text: English and French +Customer-facing README files: English unless a separate localized pack is created +``` + +The UI owns localization through `webui/app.js`. French strings are allowed only as UI translation values. All implementation comments and developer documentation must stay in English. + +## Current UI languages + +```text +English: default +French: selectable in the sidebar +``` + +## Future localization packs + +Additional languages should be added as UI translation dictionaries, not by duplicating pages. diff --git a/docs/LINUX_BOOT_FLOW.md b/docs/LINUX_BOOT_FLOW.md new file mode 100644 index 0000000000000000000000000000000000000000..0369f117b5dee3548c3c3a685eae8b0ffb373b90 --- /dev/null +++ b/docs/LINUX_BOOT_FLOW.md @@ -0,0 +1,13 @@ +# Linux Boot Flow + +Linux boot is optional and secondary. + +1. User shuts down the computer. +2. User inserts USB/SSD. +3. User chooses USB/SSD from boot menu. +4. Linux starts from the drive. +5. systemd runs hardware scan. +6. Ollama and JackAILocald start. +7. UI opens in kiosk mode. + +This mode gives more isolation, but worse universal GPU-driver predictability than Windows portable mode. diff --git a/docs/LLM_STORAGE_AND_DOWNLOAD_POLICY.md b/docs/LLM_STORAGE_AND_DOWNLOAD_POLICY.md new file mode 100644 index 0000000000000000000000000000000000000000..d112d82adaa1b3c6cf3bf9570a8194b8b271783f --- /dev/null +++ b/docs/LLM_STORAGE_AND_DOWNLOAD_POLICY.md @@ -0,0 +1,65 @@ +# LLM storage and download policy + +## Where the LLMs live + +Built key: + +```text +models/ollama/ Ollama model blobs +models/gguf/ optional llama.cpp GGUF files +models/stt/ speech-to-text models +models/tts/ text-to-speech models +models/vision/ vision models or pointers +``` + +## Customer insertion flow + +Normal use does not download models. + +```text +Insert key +open START-HERE.cmd +runtime starts +models are read locally from the key/cache +``` + +## When downloads happen + +Downloads happen only in these cases: + +1. factory preloading before shipping, +2. SaaS-generated package construction, +3. explicit model add action, +4. signed update/model pack. + +## Ollama storage + +Windows launcher sets: + +```powershell +$env:OLLAMA_MODELS = "$DriveRoot\models\ollama" +``` + +Linux services set: + +```bash +OLLAMA_MODELS=/opt/jackailocal/models/ollama +``` + +This makes Ollama read/write models inside the JackAILocal key/runtime path. + +## Why not download at insertion + +Downloading at insertion breaks the product claim: + +- no offline mode, +- no airplane mode, +- no guaranteed first-run experience, +- customer sees model management complexity, +- support load explodes. + +Correct model: + +```text +Build once -> use offline repeatedly. +``` diff --git a/docs/LORA_ADAPTERS.md b/docs/LORA_ADAPTERS.md new file mode 100644 index 0000000000000000000000000000000000000000..bb0a46134b72557b895000b4df1d10911a10cfe4 --- /dev/null +++ b/docs/LORA_ADAPTERS.md @@ -0,0 +1,43 @@ +# Adapters LoRA — personnalisation des modèles par client + +JackAILocal supporte les adapters LoRA au format GGUF, intégrés **au moment du +build** comme tout le reste (rien n'est téléchargé chez le client). C'est le +mécanisme de personnalisation pour le white-label B2B : un client d'affaires +fournit son adapter fine-tuné (ton, vocabulaire métier, langue) et chaque clé +livrée embarque « son » modèle. + +## Comment ça marche + +1. Le fine-tuning est fait en dehors de JackAILocal (axolotl, unsloth, PEFT…) + puis l'adapter est converti en **GGUF** (`convert_lora_to_gguf.py` de + llama.cpp). L'adapter doit cibler exactement le modèle de base packagé + (ex. `qwen3.5:4b`) — un adapter entraîné sur un autre base model produit du + bruit. +2. Déclarer l'adapter dans `manifest/lora-adapters.json` (voir + `manifest/lora-adapters.example.json`) : nom du modèle final, modèle de + base, fichier (URL + SHA-256 ou chemin local), prompt système optionnel, + paramètres (`temperature`, `num_ctx`…). +3. Pendant le build, `factory/powershell/Install-LoraAdapters.ps1` : + - place l'adapter dans `models/lora/`, + - génère un Modelfile (`FROM base` + `ADAPTER fichier` + SYSTEM/PARAMETER), + - exécute `ollama create ` dans le store Ollama de la cible. +4. Ajouter le modèle créé au catalogue (`Add-ModelToCatalog.ps1`) pour que le + runtime puisse le sélectionner, et inscrire la licence de l'adapter dans + `licenses/THIRD_PARTY_NOTICES.md`. + +## Backend llama.cpp + +Le serveur llama.cpp accepte aussi les adapters au lancement : +`llama-server -m base.gguf --lora adapter.gguf`. Convention identique : +fichiers sous `models/lora/`. + +## Limites + +- LoRA sur modèles **quantifiés** : Ollama applique l'adapter sur le modèle + de base tel quel; pour une qualité maximale, fusionner l'adapter dans les + poids avant quantification (merge + quantize) et livrer un GGUF fusionné + dans `models/gguf/` à la place. +- Un adapter = un modèle nommé dans le store; l'espace disque est celui du + base model (partagé) + l'adapter (petits Mo). +- Toujours vérifier la licence du dataset de fine-tuning du client avant + redistribution. diff --git a/docs/MACOS_BUILDER_AND_RUNTIME.md b/docs/MACOS_BUILDER_AND_RUNTIME.md new file mode 100644 index 0000000000000000000000000000000000000000..9569043fe9fea15bcea91dbef629510be941841a --- /dev/null +++ b/docs/MACOS_BUILDER_AND_RUNTIME.md @@ -0,0 +1,54 @@ +# macOS Builder and Runtime + +This document describes how JackAILocal supports Mac users without changing the core product model. + +## Product rule + +The SaaS generates a builder package. The manifest is embedded in that package. The user does not manually select the manifest. + +## Builder package contents + +- `BUILD-USB-MAC.command` +- `macos/JackAILocal-USB-Builder.sh` +- `manifest/build-manifest.json` +- `webui/` +- `config/` +- `models/` +- `backends/` +- `bin/` + +## Runtime package on the USB/SSD + +- `START-HERE.command` +- `macos/Start-JackAILocal.sh` +- `macos/Stop-JackAILocal.sh` +- `config/jackailocal.macos.toml` +- `models/ollama/` +- `webui/` +- optional macOS `ollama` binary +- optional macOS `jackailocald` binary + +## Backends + +For Mac, backend priority should be: + +1. Ollama for ease of use. +2. A local OpenAI-compatible `llama.cpp` server when explicitly supplied. + +## Build command + +```bash +./BUILD-LOCAL-MAC.command +./BUILD-USB-MAC.command +./BUILD-SSD-MAC.command +``` + +## Launch command on prepared USB/SSD + +```bash +./START-HERE.command +``` + +## Commercial requirement + +A production Mac product should be packaged as a signed and notarized `.app`. The `.command` files are acceptable for hackathon/MVP testing, not for a polished non-technical customer flow. diff --git a/docs/MACOS_USER_FLOW.md b/docs/MACOS_USER_FLOW.md new file mode 100644 index 0000000000000000000000000000000000000000..fd016c4bf0c7a042df393e49a0c68a89c104320f --- /dev/null +++ b/docs/MACOS_USER_FLOW.md @@ -0,0 +1,33 @@ +# macOS User Flow + +JackAILocal must support macOS without requiring the user to understand manifests, model folders, Ollama, llama.cpp, or terminal commands. + +## Hackathon/MVP flow + +1. The user downloads the JackAILocal builder ZIP from the SaaS. +2. Safari usually extracts the ZIP automatically. If not, the user double-clicks it. +3. The user inserts a USB/SSD. +4. The user double-clicks `BUILD-USB-MAC.command`. +5. The builder finds the embedded manifest automatically. +6. The builder detects external volumes under `/Volumes`. +7. The user confirms the target volume. +8. The builder writes the runtime, config, manifest, folders, and planned models. +9. If Ollama is available, the builder downloads selected Ollama models to the USB/SSD. +10. The user opens the prepared USB/SSD and double-clicks `START-HERE.command`. + +## Production flow + +For a real non-technical product, replace `.command` files with a signed and notarized `.app` bundle: + +- `JackAILocal Builder.app` +- `JackAILocal.app` on the prepared key + +This avoids Gatekeeper confusion and reduces the visible terminal experience. + +## macOS constraints + +- Gatekeeper may block unsigned apps or downloaded shell scripts. +- Code signing and notarization are required for a commercial Mac experience. +- Apple Silicon uses unified memory, not discrete VRAM. Model policy should consider total RAM and chip family. +- Metal-enabled llama.cpp builds are important for Apple Silicon performance. +- The Mac version should not promise the same GPU behavior as Windows/NVIDIA. diff --git a/docs/MODEL_SOURCES.md b/docs/MODEL_SOURCES.md new file mode 100644 index 0000000000000000000000000000000000000000..6c1d1186a1bbc12512ddd848d047d2d5c6088070 --- /dev/null +++ b/docs/MODEL_SOURCES.md @@ -0,0 +1,76 @@ +# Sources de modèles et backends supportés + +Comment les modèles entrent dans un package JackAILocal, selon le backend +d'exécution. Les modèles sont préchargés au moment du build (factory ou +builder) — jamais téléchargés à l'usage chez le client. + +## Vue d'ensemble + +| Source | Backend cible | Format | Outil de préchargement | +|---|---|---|---| +| Registre Ollama (`registry.ollama.ai`) | Ollama | blobs Ollama | `ollama pull` orchestré par les builders (`Pull-OllamaModels`) | +| Hugging Face (`huggingface.co`) | llama.cpp | GGUF | `factory/powershell/Install-GGUFModels.ps1` + `manifest/gguf-downloads.json` | +| Hugging Face (`huggingface.co`) | vLLM | safetensors (repo HF complet) | `huggingface-cli download` vers `models/hf/` (appliance Linux) | +| NVIDIA `build.nvidia.com` / NGC | NIM (conteneur) | conteneur NIM + moteur TRT-LLM | `docker pull nvcr.io/nim/...` (appliance GPU NVIDIA seulement) | + +## 1. Ollama (canal par défaut) + +Le catalogue (`config/model-catalog.json`) référence des modèles Ollama +(`qwen3.5:*`, `gemma4:12b`, `qwen3-vl:*`). Les builders Windows/macOS +démarrent un Ollama de build sur un port dédié, font `ollama pull` selon le +manifest, et le store résultant (`models/ollama/`) part sur la clé. C'est le +chemin recommandé pour USB/SSD/installateur. + +## 2. Hugging Face — GGUF pour llama.cpp + +Pour un modèle absent du registre Ollama ou pour le fallback llama.cpp : +décrire les fichiers dans `manifest/gguf-downloads.json` (URL `resolve/main` +de huggingface.co + SHA-256), puis exécuter `Install-GGUFModels.ps1`. Les +fichiers atterrissent dans `models/gguf/` et le serveur llama.cpp les sert en +API compatible OpenAI. + +`config/model-alias-map.json` fait la correspondance entre IDs Hugging Face +et références Ollama quand les deux existent. + +## 3. Hugging Face — repos complets pour vLLM (appliance Linux GPU) + +vLLM consomme les repos HF en safetensors. Sur l'appliance : + +```bash +pip install vllm huggingface_hub +huggingface-cli download Qwen/Qwen3.5-9B-Instruct --local-dir /opt/JackAILocal/models/hf/qwen3.5-9b +backends/vllm/linux/start-vllm.sh /opt/JackAILocal/models/hf/qwen3.5-9b # API OpenAI sur 127.0.0.1:8000 +``` + +Le runtime jackailocald le consomme via la config `[backends.llamacpp]` +(toute API compatible OpenAI). Réservé aux machines Linux avec GPU dédié. + +## 4. NVIDIA build.nvidia.com — NIM (appliance GPU NVIDIA) + +Les modèles de `build.nvidia.com/models` se déploient localement sous forme +de microservices NIM (conteneurs NGC, moteurs TensorRT-LLM optimisés). +Exigences : GPU NVIDIA récent, Docker + NVIDIA Container Toolkit, clé API +NGC (gratuite) **au moment du build seulement** — l'exécution est locale. + +Voir `deploy/appliance/nim-compose.example.yml`. Étapes : + +```bash +docker login nvcr.io # $oauthtoken / clé NGC +docker compose -f nim-compose.example.yml pull # au build, avec Internet +docker compose -f nim-compose.example.yml up -d # chez le client, hors ligne +``` + +Le NIM expose une API compatible OpenAI sur le port configuré; pointer +`[backends.llamacpp].url` dessus. Attention aux licences propres à chaque +modèle NIM (NVIDIA AI Enterprise pour certains) — vérifier la fiche du modèle +sur build.nvidia.com avant de l'intégrer à une offre commerciale. + +## Règles + +- Aucun téléchargement de modèle au lancement chez le client (offline-first). +- Chaque modèle ajouté doit passer par `config/model-catalog.json` (limite + `hackathon_max_params_b` appliquée par l'agent de config). +- Chaque modèle ajouté doit avoir sa licence vérifiée et inscrite dans + `licenses/THIRD_PARTY_NOTICES.md` (voir la mise en garde voix Piper). +- L'agent d'analyse de configuration est **Gemma 4 12B** (`gemma4:12b`), + localement via Ollama ou `google/gemma-4-12B-it` via l'endpoint distant. diff --git a/docs/MODEL_STORAGE_POLICY.md b/docs/MODEL_STORAGE_POLICY.md new file mode 100644 index 0000000000000000000000000000000000000000..47f44915a6d3bb2096e57fab0c42fbad239a1931 --- /dev/null +++ b/docs/MODEL_STORAGE_POLICY.md @@ -0,0 +1,36 @@ +# Model Storage Policy + +## Are the LLMs on the key? + +Yes. Shipping product: the LLMs are already stored on the USB/SSD before the customer receives it. + +Windows mode: + +- Ollama model blobs live under `models/ollama` on the drive. +- Optional GGUF files live under `models/gguf` when a local `llama.cpp` server is supplied. + +Linux boot mode: + +- The same model directories are copied or mounted under `/opt/JackAILocal/models/*`. + +## Are models downloaded when the key is inserted? + +No. Customer first run must not download models. The product must work offline and in airplane mode. + +Downloads happen only in these cases: + +1. Factory preparation before shipment. +2. Explicit signed update pack. +3. Explicit advanced model-management action while online. + +## Why? + +Because the product promise is offline, no-install, no-admin, non-technical. A customer who inserts the drive should not hit a 4 GB to 20 GB download, a failed Hugging Face token, a firewall block, a slow hotel network, or a broken model pull. + +## Runtime flow + +At first launch, the launcher sets: + +`OLLAMA_MODELS=\models\ollama` + +Then Ollama reads model blobs from the drive. Ollama officially supports changing the model storage directory with `OLLAMA_MODELS`; this is what makes the portable model directory possible. diff --git a/docs/NON_TECH_USB_BUILDER_FLOW.md b/docs/NON_TECH_USB_BUILDER_FLOW.md new file mode 100644 index 0000000000000000000000000000000000000000..8ad85d640e361c71feb100e0fc69c54145a873dd --- /dev/null +++ b/docs/NON_TECH_USB_BUILDER_FLOW.md @@ -0,0 +1,41 @@ +# Non-Technical Builder Flow + +## User Experience + +### Gradio simple flow + +1. Open the Gradio Builder. +2. Choose the intended use. +3. Choose the target: + - this computer + - USB key + - external SSD + - Windows builder ZIP + - macOS builder ZIP + - Linux/Docker appliance ZIP +4. Click `Prepare / publish now`. +5. Use `START-HERE` on the prepared target, or give the published ZIP to the target user. + +The user is not asked to choose an AI model, backend, quantization, context size, or model directory. + +### Extracted builder package + +1. Download and extract the platform builder package. +2. Insert a USB drive or external SSD, or choose the local-folder edition. +3. Double-click the matching build launcher. +4. Confirm the target. +5. Wait for model and Voice installation to complete. +6. Open the prepared target and run `START-HERE`. + +## Builder Responsibilities + +- Find the embedded or nearby build manifest +- Prevent source and target path overlap +- Copy the runtime payload +- Install selected Ollama models +- Install and verify Voice assets +- Write the target build manifest +- Generate SHA-256 integrity data +- Refuse success if the target cannot run + +Normal target use does not download models. Downloads occur during build, factory preload, explicit add-model actions, or controlled updates. diff --git a/docs/ODYSSEUS_INTEGRATION_DECISION.md b/docs/ODYSSEUS_INTEGRATION_DECISION.md new file mode 100644 index 0000000000000000000000000000000000000000..6f5118bf611ff55f8f4e44da7609b0cb8bfd39b6 --- /dev/null +++ b/docs/ODYSSEUS_INTEGRATION_DECISION.md @@ -0,0 +1,23 @@ +# JackAILocal Odysseus Integration Decision + +Odysseus is treated as a product/UI reference, not as the runtime base. + +Implemented in JackAILocal: + +- Workspace shell: Chat, Model Cookbook, Documents, Benchmark, Settings, Support. +- Model Cookbook UX: model profiles, RAM/VRAM requirements, backend metadata. +- Local documents area constrained to `workspace/documents`. +- Benchmark panel for live demos and Reddit-style proof requests. +- Settings panel with Simple/Advanced mode and locked shell/system tools. +- Feature flags endpoint so the UI can show what is enabled. + +Deliberately not enabled for the consumer USB edition: + +- Shell agents. +- MCP tools with system access. +- Email/calendar automation. +- LAN exposure. +- Open web research by default. +- Arbitrary filesystem access. + +Reason: JackAILocal is for non-technical offline use. Odysseus-style richness is useful, but the USB product must remain deterministic, local, offline and supportable. diff --git a/docs/PREPARE_USB_FROM_ZERO.md b/docs/PREPARE_USB_FROM_ZERO.md new file mode 100644 index 0000000000000000000000000000000000000000..0f5103ca1d2066f44d559c2308f1871ffdb6ca0d --- /dev/null +++ b/docs/PREPARE_USB_FROM_ZERO.md @@ -0,0 +1,117 @@ +# Prepare a JackAILocal USB drive from zero + +## Real product behavior + +JackAILocal must be prepared before delivery or before offline use. The customer must not download models when the drive is inserted. Models are copied or downloaded during manufacturing, then read locally from the drive. + +Normal customer flow: + +```text +insert the USB/SSD drive into Windows +open the drive +double-click START-HERE.cmd +JackAILocal starts locally +browser opens http://127.0.0.1:4891 +``` + +## Recommended hardware + +Serious product minimum: + +```text +USB 3.2 SSD or external NVMe +128 GB minimum +256 GB recommended +separate SSD or dedicated image for Linux boot mode +``` + +Avoid cheap low-speed USB sticks. They increase first-load time and support cost. + +## Factory prerequisites + +On the factory/build computer: + +```text +Windows 10/11 +PowerShell 5+ +Rust toolchain if building jackailocald.exe locally +Ollama Windows binary +llama.cpp llama-server.exe binary +Internet access only during preparation/model download +``` + +## Prepare the backend cache + +Recommended cache layout: + +```text +C:\\JackAILocalBackendCache\\ +├── ollama.exe +└── llama-server.exe +``` + +Keep fixed, audited backend versions in the cache. Do not use floating versions for shipped drives. + +## Prepare the drive + +```powershell +powershell -ExecutionPolicy Bypass -File .\\factory\\powershell\\Prepare-JackAILocalUSB.ps1 ` + -TargetDrive "E:\\" ` + -BackendCache "C:\\JackAILocalBackendCache" ` + -ModelSet balanced +``` + +Options: + +```powershell +-ModelSet lite +-ModelSet balanced +-ModelSet full +-SkipModels +-SkipRustBuild +-UseSystemOllamaAsSource +``` + +## Offline acceptance test + +1. Disconnect the network or enable airplane mode. +2. Insert the prepared drive. +3. Run `START-HERE.cmd`. +4. Confirm that the UI opens. +5. Send a short chat prompt. +6. Open Models and confirm the catalog loads. +7. Open Support and prepare a report. +8. Confirm that no model download is attempted during normal use. + +## Expected customer-ready state + +Required files: + +```text +START-HERE.cmd +bin\\jackailocald.exe +backends\\ollama\\windows\\ollama.exe +models\\ollama\\... +webui\\index.html +config\\model-catalog.json +manifest\\sha256-manifest.json +``` + +Do not ship a drive with placeholder backend files unless it is clearly labeled as a developer kit. + +## Claims to avoid + +Do not claim: + +```text +automatic execution when inserted +zero trace on the Windows host +perfect GPU compatibility on every PC +unbreakable anti-copy protection +``` + +Safe claim: + +```text +Insert the drive, open it, double-click START-HERE, and use local AI without installation after the drive has been prepared. +``` diff --git a/docs/QA_DEMO_PROTOCOL.md b/docs/QA_DEMO_PROTOCOL.md new file mode 100644 index 0000000000000000000000000000000000000000..1d7ee63e9716750e829f8cefc4be264328defdbc --- /dev/null +++ b/docs/QA_DEMO_PROTOCOL.md @@ -0,0 +1,22 @@ +# QA Demo Protocol + +Purpose: answer the Reddit critique that the product needs real-time proof on a normal laptop. + +Record a single-take video: + +1. Show the laptop model and RAM. +2. Show Wi-Fi disabled. +3. Insert USB/SSD. +4. Open drive. +5. Run START-HERE.cmd. +6. Ask one normal question. +7. Ask one offline survival/reference question. +8. Ask one image scan if vision pack is included. +9. Show elapsed startup time. +10. Show no cloud website is opened. + +Run: + +`factory/powershell/QA-Benchmark.ps1` + +Save results under `qa/benchmark-result.json`. diff --git a/docs/QWEN_MODEL_POLICY.md b/docs/QWEN_MODEL_POLICY.md new file mode 100644 index 0000000000000000000000000000000000000000..ad07aabaf279d6aa3e6f25fb207d0f80a450c262 --- /dev/null +++ b/docs/QWEN_MODEL_POLICY.md @@ -0,0 +1,57 @@ +# Qwen Model Policy for JackAILocal + +JackAILocal uses Qwen3.5 as the practical default family and Qwen3.6-27B as a power profile for high-end machines. + +## Default pack + +The default pack is not the ceiling. It is a sensible offline default designed to run on ordinary laptops without forcing the user to understand model size, quantization, VRAM, context length, or backend selection. + +Default candidates: + +- Qwen3.5 2B for low-spec machines. +- Qwen3.5 4B for normal laptops. +- Qwen3.5 9B as the default smart offline assistant. +- Qwen3.5 27B when the machine has enough memory. + +## Power pack + +Qwen3.6-27B is included as a power profile candidate. + +Use it when: + +- the machine has RTX 3090-class hardware or better; +- the machine has roughly 24 GB VRAM or enough RAM for a quantized fallback; +- the user selected the Power package; +- the use case benefits from stronger coding, reasoning, vision, or agentic workflows. + +Do not use Qwen3.6-27B as the default for ordinary laptops. + +## Hackathon rule + +The hackathon rule is not "Qwen 9B only". The rule is that the Space must be powered by models under the 32B parameter limit. Qwen3.6-27B is eligible by parameter count. Avoid ambiguous models whose published parameter count is over the limit. + +## Backend policy + +Qwen3.5 models can be used through Ollama when the selected tag is available. They can also be provided as GGUF files for llama.cpp. + +Qwen3.6-27B is packaged through its Ollama model reference when that model is selected. A GGUF quantization may also be used for an explicitly supplied local `llama.cpp` server if the artifact and hash are pinned in the manifest. + +## Context policy + +Do not default to the maximum native context. Long contexts increase memory usage and can cause out-of-memory errors. + +USB defaults: + +- normal profiles: 4K to 8K context; +- power profiles: 8K to 16K context; +- long-context mode: explicit advanced option only. + +## Product wording + +Use this wording: + +> JackAILocal ships with a practical Qwen3.5 default pack for ordinary laptops, then unlocks Qwen3.6-27B and other power models when the detected machine can run them. + +Do not say: + +> JackAILocal is limited to Qwen 9B. diff --git a/docs/SAAS_PACKAGE_BUILDER_ARCHITECTURE.md b/docs/SAAS_PACKAGE_BUILDER_ARCHITECTURE.md new file mode 100644 index 0000000000000000000000000000000000000000..babe7c11718ed7b6d8ede8698ce1e3bee21b5278 --- /dev/null +++ b/docs/SAAS_PACKAGE_BUILDER_ARCHITECTURE.md @@ -0,0 +1,50 @@ +# SaaS package builder architecture + +## Goal + +The SaaS detects customer hardware and produces a build manifest that creates a ready-to-use JackAILocal key. + +## Flow + +```text +Customer downloads profiler +↓ +Profiler collects CPU/RAM/GPU/disk and optional whichllm JSON +↓ +Profiler submits hardware profile to SaaS +↓ +SaaS planner maps hardware to model and target mode +↓ +SaaS returns build manifest +↓ +Customer inserts USB/SSD +↓ +Platform builder installs selected assets to the target +↓ +Key becomes offline usable +``` + +## Important + +The key cannot build itself magically just by insertion. Windows does not reliably autorun executables from USB. The customer must launch the builder or START-HERE. + +## whichllm integration + +`whichllm` can be used as advisory input. JackAILocal maps compatible Hugging Face model IDs to local Ollama references through `config/model-alias-map.json`. + +## Build manifest + +A build manifest contains: + +- product name +- build id +- target platform +- selected model profiles +- backend URLs or factory asset names +- checksums +- default preset +- update channel +- branding +- license terms + +No private signing key is shipped to customers. diff --git a/docs/SECURITY_AND_ANTI_HACK.md b/docs/SECURITY_AND_ANTI_HACK.md new file mode 100644 index 0000000000000000000000000000000000000000..c654befdec6436a53b7176fc5c207ff730637b06 --- /dev/null +++ b/docs/SECURITY_AND_ANTI_HACK.md @@ -0,0 +1,43 @@ +# Security and anti-hack model + +## Correct claim + +JackAILocal can be hardened. It cannot be made impossible to copy, inspect or reverse engineer once shipped to a customer. + +## Defensive controls included + +- signed update manifests, +- SHA256 file manifest, +- runtime integrity check, +- local-only binding to 127.0.0.1, +- no admin requirement for normal use, +- path traversal protection in scripts, +- no shell/tool execution exposed to the model, +- logs stored under `.jackailocal/logs`, +- optional license token check for SaaS updates, +- model pack signing before install, +- update rollback folder. + +## Not included by design + +- malware-like persistence, +- anti-debug rootkits, +- secret destructive locks, +- bypassing OS security, +- covert collection of user data, +- claims that the host has no traces. + +## Host traces + +Windows can create USB device registry entries, jump lists, recent files, Defender logs, and Prefetch/event traces depending on configuration. JackAILocal can minimize application writes, not erase the host OS history. + +## Recommended production hardening + +- code-sign `jackailocald.exe`, +- code-sign PowerShell wrappers or ship a signed EXE launcher, +- use version-pinned backend binaries, +- verify every downloaded binary checksum, +- maintain SBOM and third-party notices, +- run updater with least privilege, +- never ship private signing keys, +- test with Defender, SmartScreen and common AV products. diff --git a/docs/SECURITY_CLAIMS.md b/docs/SECURITY_CLAIMS.md new file mode 100644 index 0000000000000000000000000000000000000000..34da6c720e4648b94b51ef1705dfb118f50dbfa4 --- /dev/null +++ b/docs/SECURITY_CLAIMS.md @@ -0,0 +1,19 @@ +# Security Claims + +Allowed claim: + +- Runs locally. +- Does not require Internet for normal use. +- Does not require admin rights in Windows portable mode. +- Does not intentionally upload user data. +- Network is bound to 127.0.0.1 by default. + +Do not claim: + +- Leaves no trace on the host. +- Forensics-proof. +- Impossible to inspect or copy. +- Secure against a malicious host. +- Secure against keyloggers or screen capture. + +Windows records USB insertion and execution metadata. A hostile host can inspect memory, capture keystrokes, or copy files. diff --git a/docs/SYSTEM_BEHAVIOR.md b/docs/SYSTEM_BEHAVIOR.md new file mode 100644 index 0000000000000000000000000000000000000000..d64694ea1b46de47df91b153aa2a20bef7453b97 --- /dev/null +++ b/docs/SYSTEM_BEHAVIOR.md @@ -0,0 +1,69 @@ +# JackAILocal System Behavior + +## Layers + +1. The Gradio Builder accepts a real hardware profile, recommends compatible models, and exports manifests or builder packages. +2. Platform builders prepare a local folder, USB drive, or external SSD and validate the resulting payload. +3. `jackailocald` serves the WebUI and routes requests only to reachable local AI engines. + +## Runtime + +The Rust runtime serves static WebUI files and these primary APIs: + +```text +GET /api/status +GET /api/features +GET /api/models +POST /v1/chat/completions +GET /api/threads +POST /api/threads +POST /api/threads/:id/messages +POST /api/vision/analyze +GET /api/voice/status +POST /api/voice/transcribe +POST /api/voice/synthesize +POST /api/packs/export +POST /api/packs/import +POST /api/agent/recommend +GET /api/phone +POST /api/phone +POST /api/runtime/restart +``` + +Chat and SCOUT use installed Ollama models. An OpenAI-compatible `llama.cpp` server can be used as an explicit local chat fallback. Voice uses whisper.cpp and Piper binaries and models stored in the package. + +`POST /api/agent/recommend` adds a local LLM-in-the-loop for client configuration and model selection review. It uses only installed local models, enforces the <=32B catalog policy, prefers Gemma 4 12B (`gemma4:12b`) when present, and returns an explicit error when a requested agent model is unavailable. + +The runtime reports a feature as available only when the required local engine and model are available. + +## Storage + +```text +workspace/threads/ conversation JSON files +workspace/documents/ user notes +workspace/settings/ local settings and phone pairing token +workspace/exports/ encrypted packs and support ZIPs +workspace/voice/ temporary voice files +workspace/scout/ SCOUT workspace +models/ollama/ Ollama model store +models/whisper/ speech-to-text models +models/piper/ text-to-speech models +``` + +Threads are persistent and support filtering, rename, and individual deletion. + +## Phone Access + +The default bind is `127.0.0.1:4891`. Phone Access must be enabled explicitly. After restart, the runtime binds to `0.0.0.0:4891`, generates a LAN URL and QR code, and requires `X-JackAILocal-Token` for non-loopback API requests. + +## Transfer Packs + +Transfer packs use AES-256-GCM with PBKDF2-HMAC-SHA256. Passphrases are not stored. Imported settings are sanitized so a pack cannot silently enable Phone Access or transfer a pairing token. + +## Builders + +Builders install selected Ollama models, install Voice assets, generate a SHA-256 manifest, and refuse success when required files are missing. The source package and target path must not overlap. + +## Offline Boundary + +Normal use does not require internet after models and Voice assets are present. The runtime has no cloud inference fallback. A capability that cannot run locally returns an explicit unavailable or error response. diff --git a/docs/TEST_WITHOUT_USB.md b/docs/TEST_WITHOUT_USB.md new file mode 100644 index 0000000000000000000000000000000000000000..4ba0403f3bd4f341d5cfa4411b806e3f3ede05bd --- /dev/null +++ b/docs/TEST_WITHOUT_USB.md @@ -0,0 +1,31 @@ +# Test Without a USB Drive + +This flow builds and runs the same payload in a local folder. + +## Windows + +```cmd +TEST-WITHOUT-USB.cmd +``` + +The target is `%LOCALAPPDATA%\JackAILocal-Test`. The builder installs models and Voice assets, validates the payload, then launches `START-HERE.cmd`. + +## macOS + +```bash +./TEST-WITHOUT-USB-MAC.command +``` + +The target is `$HOME/JackAILocal-Test`. The builder validates the macOS runtime, Ollama, models, and Voice assets before launch. + +## What This Validates + +- Manifest detection +- Model installation into the target model store +- Voice asset installation +- Runtime startup +- Local inference +- WebUI and API behavior +- SHA-256 manifest generation + +It does not validate removable-drive speed, host security policies for external media, or physical drive detection. diff --git a/docs/UI_POLISH_V4_NOTES.md b/docs/UI_POLISH_V4_NOTES.md new file mode 100644 index 0000000000000000000000000000000000000000..83ebd0a4daf2e1f11e31d70645b063e5b79dcb9d --- /dev/null +++ b/docs/UI_POLISH_V4_NOTES.md @@ -0,0 +1,19 @@ +# UI Polish v4 Notes + +This update fixes the settings screen layout and improves the consumer-safe settings experience. + +## Changes + +- Fixed checkbox and radio controls stretching across the settings panel. +- Replaced raw settings labels with readable option cards. +- Added explanatory microcopy for Simple, Advanced, Offline Lock, and Tool Lock. +- Preserved bilingual UI text. +- Kept code and documentation in English. +- No external UI framework or CDN dependency was added. + +## Validation + +- JavaScript syntax check passes. +- JSON configuration files parse correctly. +- TOML configuration files parse correctly. +- ZIP integrity passes after packaging. diff --git a/docs/UI_SECURITY_BOUNDARIES.md b/docs/UI_SECURITY_BOUNDARIES.md new file mode 100644 index 0000000000000000000000000000000000000000..afb4164724f2bbf3869d0134ba61473adfa699b5 --- /dev/null +++ b/docs/UI_SECURITY_BOUNDARIES.md @@ -0,0 +1,21 @@ +# UI Security Boundaries + +JackAILocal's UI is intentionally local and constrained. + +Hard boundaries: + +- bind to `127.0.0.1` only +- no shell execution from chat +- no OS automation exposed to model output +- no arbitrary path reads or writes +- documents saved only under `workspace/documents` +- support bundle excludes conversations by default +- model additions require manifest/hash verification in production builder +- updates require signed manifest in production + +Non-goals: + +- uncopyable USB +- protection from a compromised host OS +- guaranteed zero forensic trace in Windows +- guaranteed autorun on insertion diff --git a/docs/WHAT_TO_DO_AFTER_GRADIO.md b/docs/WHAT_TO_DO_AFTER_GRADIO.md new file mode 100644 index 0000000000000000000000000000000000000000..a8dc695b6a61ecba1c49ffd1bbf8f4c6bde3909b --- /dev/null +++ b/docs/WHAT_TO_DO_AFTER_GRADIO.md @@ -0,0 +1,26 @@ +# What To Do After Gradio + +## Simple flow + +1. Choose the intended use. +2. Choose the target. +3. Click `Prepare / publish now`. +4. If Gradio is running locally on Windows, it can prepare this computer, a USB key, or an external SSD directly. +5. If the target is a package, download the generated ZIP and give it to the target user. + +## Advanced/manual flow + +1. Download the manifest ZIP or platform builder ZIP. +2. Extract the full builder package. +3. Choose a target: + +```text +BUILD-LOCAL.cmd +BUILD-USB.cmd +BUILD-SSD.cmd +``` + +4. Confirm the target and wait for the builder to finish. +5. Start the prepared target with `START-HERE.cmd`. + +The builder writes models to `models/ollama`, installs Voice assets, and reports success only after validating the target. diff --git a/docs/WINDOWS_PORTABLE_FLOW.md b/docs/WINDOWS_PORTABLE_FLOW.md new file mode 100644 index 0000000000000000000000000000000000000000..7925ce2277718de33cca3a39c7d19bb4724e7cd3 --- /dev/null +++ b/docs/WINDOWS_PORTABLE_FLOW.md @@ -0,0 +1,14 @@ +# Windows Portable Flow + +1. User inserts USB/SSD. +2. Windows shows the drive. +3. User opens the drive and double-clicks `START-HERE.cmd`. +4. Script runs `windows/Start-JackAILocal.ps1`. +5. Hardware scan writes `diagnostics/hardware.json`. +6. Launcher sets local environment variables. +7. Ollama starts from `backends/ollama/windows/ollama.exe`. +8. Ollama reads models from `models/ollama` on the drive. +9. `JackAILocald.exe` starts from `bin/JackAILocald.exe`. +10. Browser opens `http://127.0.0.1:4891`. + +No admin rights are required if the host allows execution from USB and local loopback ports. diff --git a/factory/linux/build-linux-binary.sh b/factory/linux/build-linux-binary.sh new file mode 100644 index 0000000000000000000000000000000000000000..0c22970974206c8bdf3086ba467d99cdbc50969e --- /dev/null +++ b/factory/linux/build-linux-binary.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -euo pipefail +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +cd "$ROOT" +cargo build --release +install -D target/release/jackailocald bin/jackailocald +echo "Linux runtime staged at bin/jackailocald" diff --git a/factory/linux/install-linux-backends.sh b/factory/linux/install-linux-backends.sh new file mode 100644 index 0000000000000000000000000000000000000000..36cbefcbb25987f408ba8ab9e05935eb96abe993 --- /dev/null +++ b/factory/linux/install-linux-backends.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +mkdir -p "$ROOT/backends/ollama/linux" "$ROOT/backends/llama.cpp/linux" "$ROOT/downloads" +echo "Install Ollama Linux backend. Preferred factory method: copy a tested ollama binary to backends/ollama/linux/ollama." +if command -v ollama >/dev/null 2>&1; then + cp "$(command -v ollama)" "$ROOT/backends/ollama/linux/ollama" + chmod +x "$ROOT/backends/ollama/linux/ollama" +else + echo "Ollama not found on factory machine. Install/test Ollama, then rerun or place the binary manually." >&2 + exit 1 +fi +echo "For llama.cpp: download a tested release for your CPU/GPU target and place llama-server at backends/llama.cpp/linux/llama-server." diff --git a/factory/linux/verify-language-policy.sh b/factory/linux/verify-language-policy.sh new file mode 100644 index 0000000000000000000000000000000000000000..f6d2b7ad63953e85c5d7663d0ef67cfe8ada5260 --- /dev/null +++ b/factory/linux/verify-language-policy.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail +ROOT="${1:-$(cd "$(dirname "$0")/../.." && pwd)}" +python3 - "$ROOT" <<'EOF_PY' +from pathlib import Path +import sys +root = Path(sys.argv[1]) +paths = list((root / 'docs').glob('*.md')) + [root / 'README_START_HERE.md', root / 'README-FIRST.txt'] +terms = ['clé','modèle','démarrer','préparer','réglage','réseau','hors ligne','sauver','rafraîchir','préparation'] +accents = set('éèêàùçôîïÉÈÀÇ') +findings = [] +for path in paths: + text = path.read_text(encoding='utf-8') + lower = text.lower() + for term in terms: + if term in lower: + findings.append(f'{path.relative_to(root)}: French term found: {term}') + if any(ch in accents for ch in text): + findings.append(f'{path.relative_to(root)}: accented French character found') +if findings: + print('Language policy warning: French text found outside UI.') + print('\n'.join(findings)) + raise SystemExit(1) +print('Language policy check passed for docs/root README files.') +EOF_PY diff --git a/factory/linux/verify-package.sh b/factory/linux/verify-package.sh new file mode 100644 index 0000000000000000000000000000000000000000..542a7e22ceb18ffc0c4c55aaf9049a3a71aa561a --- /dev/null +++ b/factory/linux/verify-package.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$ROOT" +python3 - <<'PY' +import json, pathlib, hashlib, tomllib +for p in pathlib.Path('.').rglob('*.json'): + json.load(open(p, encoding='utf-8')) + print('JSON OK', p) +for p in pathlib.Path('.').rglob('*.toml'): + tomllib.load(open(p, 'rb')) + print('TOML OK', p) +manifest = json.load(open('manifest/sha256-manifest.json')) +fail = 0 +for e in manifest: + p = pathlib.Path(e['path']) + if not p.exists(): + print('MISSING', p); fail += 1; continue + h = hashlib.sha256(p.read_bytes()).hexdigest() + if h != e['sha256']: + print('HASH FAIL', p); fail += 1 +if fail: + raise SystemExit(fail) +print('SHA256 OK') +PY +for f in $(find . -name '*.sh'); do bash -n "$f"; echo "BASH OK $f"; done +grep -RIn "PUT_\|Signature verification hook required\|NOT TESTED" . || true diff --git a/factory/powershell/Add-ModelToCatalog.ps1 b/factory/powershell/Add-ModelToCatalog.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..f956f9a30e4586d7a0c3186264aef138b63519d1 --- /dev/null +++ b/factory/powershell/Add-ModelToCatalog.ps1 @@ -0,0 +1,16 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$Id, + [Parameter(Mandatory=$true)][string]$Label, + [Parameter(Mandatory=$true)][string]$OllamaName, + [double]$MinRamGb=8, + [double]$MinVramGb=0, + [int]$Context=4096 +) +$Root = Resolve-Path (Join-Path $PSScriptRoot "..\..") +$Path = Join-Path $Root "config\model-catalog.json" +$j = Get-Content $Path -Raw | ConvertFrom-Json +$new = [pscustomobject]@{ id=$Id; label=$Label; description="Custom model"; min_ram_gb=$MinRamGb; min_vram_gb=$MinVramGb; default_backend="ollama"; ollama=$OllamaName; gguf=$null; features=@("chat"); context=$Context } +$j.profiles += $new +$j | ConvertTo-Json -Depth 20 | Set-Content -Encoding UTF8 $Path +Write-Host "Added model profile $Id." diff --git a/factory/powershell/Apply-Branding.ps1 b/factory/powershell/Apply-Branding.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..e3d576f85b1df807334b7c15e56fcab4e374bc07 --- /dev/null +++ b/factory/powershell/Apply-Branding.ps1 @@ -0,0 +1,5 @@ +[CmdletBinding()] +param([string]$BrandName="JackAILocal") +$Root = Resolve-Path (Join-Path $PSScriptRoot "..\..") +(Get-Content (Join-Path $Root "manifest\product.json") -Raw | ConvertFrom-Json) | ForEach-Object { $_.product=$BrandName; $_ } | ConvertTo-Json -Depth 10 | Set-Content -Encoding UTF8 (Join-Path $Root "manifest\product.json") +Write-Host "Brand applied: $BrandName" diff --git a/factory/powershell/Build-RustBinaries.ps1 b/factory/powershell/Build-RustBinaries.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..11c7dee16a198dda3d98ce9733593fcfc2bf1311 --- /dev/null +++ b/factory/powershell/Build-RustBinaries.ps1 @@ -0,0 +1,14 @@ +[CmdletBinding()] +param() +$ErrorActionPreference = "Stop" +$Root = Resolve-Path (Join-Path $PSScriptRoot "..\..") +if (!(Get-Command cargo -ErrorAction SilentlyContinue)) { throw "cargo not found. Install Rust toolchain." } +Push-Location $Root +cargo build --release +New-Item -ItemType Directory -Force -Path "$Root\bin" | Out-Null +Copy-Item "$Root\target\release\jackailocald.exe" "$Root\bin\jackailocald.exe" -Force -ErrorAction SilentlyContinue +Copy-Item "$Root\target\release\jackailocald" "$Root\bin\jackailocald" -Force -ErrorAction SilentlyContinue +if (Test-Path "$Root\bin\jackailocald.exe") { + & (Join-Path $PSScriptRoot "Sign-Executables.ps1") -Path "$Root\bin\jackailocald.exe" +} +Pop-Location diff --git a/factory/powershell/Generate-Manifest.ps1 b/factory/powershell/Generate-Manifest.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..67742416a778143bf8fc83953b0e88e39a6fcfd3 --- /dev/null +++ b/factory/powershell/Generate-Manifest.ps1 @@ -0,0 +1,36 @@ +[CmdletBinding()] +param() +$ErrorActionPreference = "Stop" +$Root = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path +$excludedDirs = @( + "\.git\", + "\target\", + "\workspace\", + "\diagnostics\", + "\dist\", + "\.jackailocal\", + "\.jackailocal-builder\", + "\.claude\", + "\.venv\", + "\.jackaiagent\", + "\__pycache__\", + "\cache\", + "\logs\" +) +$files = Get-ChildItem $Root -Recurse -File -ErrorAction SilentlyContinue | Where-Object { + $full = $_.FullName + if ($full -like "*\manifest\sha256-manifest.json") { return $false } + if ($full -like "*\config\update-private-key.xml") { return $false } + if ($full -like "*\Generate-UpdateKeys.ps1" -or $full -like "*\Sign-Manifest.ps1") { return $false } + foreach ($dir in $excludedDirs) { + if ($full.Contains($dir)) { return $false } + } + return $true +} +$list = foreach ($f in $files) { + if (!(Test-Path -LiteralPath $f.FullName -PathType Leaf)) { continue } + $rel = $f.FullName.Substring($Root.Length).TrimStart("\", "/") + [pscustomobject]@{ path=$rel.Replace('\','/'); sha256=(Get-FileHash -Algorithm SHA256 -LiteralPath $f.FullName).Hash.ToLowerInvariant(); size=$f.Length } +} +$json = @{ product="JackAILocal"; generated_at=(Get-Date).ToString("o"); files=$list } | ConvertTo-Json -Depth 20 +[System.IO.File]::WriteAllText((Join-Path $Root "manifest\sha256-manifest.json"), $json, [System.Text.UTF8Encoding]::new($false)) diff --git a/factory/powershell/Install-All.ps1 b/factory/powershell/Install-All.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..23db7adf292cc9402dd55aba57a5ebc11518bcae --- /dev/null +++ b/factory/powershell/Install-All.ps1 @@ -0,0 +1,16 @@ +param( + [string]$DriveRoot = (Resolve-Path "$PSScriptRoot\..\..").Path, + [string]$BrandName = "JackAILocal", + [switch]$SkipModels, + [switch]$SkipBackends, + [switch]$SkipRust +) +$ErrorActionPreference = 'Stop' +Set-Location $DriveRoot +if (!$SkipRust) { & "$DriveRoot\factory\powershell\Build-RustBinaries.ps1" -DriveRoot $DriveRoot } +if (!$SkipBackends) { & "$DriveRoot\factory\powershell\Install-Backends.ps1" -DriveRoot $DriveRoot } +if (!$SkipModels) { & "$DriveRoot\factory\powershell\Install-Models.ps1" -DriveRoot $DriveRoot } +& "$DriveRoot\factory\powershell\Apply-Branding.ps1" -DriveRoot $DriveRoot -BrandName $BrandName +& "$DriveRoot\factory\powershell\Generate-Manifest.ps1" -DriveRoot $DriveRoot +& "$DriveRoot\windows\Preflight-Windows.ps1" +Write-Host "Ready for Windows portable QA. Run factory\powershell\QA-Benchmark.ps1 next." diff --git a/factory/powershell/Install-Backends.ps1 b/factory/powershell/Install-Backends.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..ad45d7f87e8519ee3c0e12d541e09daa2162775e --- /dev/null +++ b/factory/powershell/Install-Backends.ps1 @@ -0,0 +1,20 @@ +[CmdletBinding()] +param( + [string]$BackendCache = "", + [switch]$AllowDownload +) +$ErrorActionPreference = "Stop" +$Root = Resolve-Path (Join-Path $PSScriptRoot "..\..") +$ollamaDest = Join-Path $Root "backends\ollama\windows\ollama.exe" +$llamaDest = Join-Path $Root "backends\llama.cpp\windows\llama-server.exe" +New-Item -ItemType Directory -Force -Path (Split-Path $ollamaDest),(Split-Path $llamaDest) | Out-Null + +if ($BackendCache) { + if (Test-Path "$BackendCache\ollama.exe") { Copy-Item "$BackendCache\ollama.exe" $ollamaDest -Force } + if (Test-Path "$BackendCache\llama-server.exe") { Copy-Item "$BackendCache\llama-server.exe" $llamaDest -Force } +} + +if (!(Test-Path $ollamaDest)) { Write-Warning "Missing ollama.exe. Put it in $ollamaDest or pass -BackendCache." } +if (!(Test-Path $llamaDest)) { Write-Warning "Missing llama-server.exe. Put it in $llamaDest or pass -BackendCache." } + +# Production rule: pin backend versions and verify checksums in manifest/backend-sources.json. diff --git a/factory/powershell/Install-GGUFModels.ps1 b/factory/powershell/Install-GGUFModels.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..1613ac372bc8ddd3f10fdbcf74c0d99e2f1d285f --- /dev/null +++ b/factory/powershell/Install-GGUFModels.ps1 @@ -0,0 +1,20 @@ +param( + [string]$DriveRoot = (Resolve-Path "$PSScriptRoot\..\..").Path, + [string]$ManifestPath = "manifest\gguf-downloads.json" +) +$ErrorActionPreference = 'Stop' +Set-Location $DriveRoot +$full = Join-Path $DriveRoot $ManifestPath +if (!(Test-Path $full)) { throw "Missing $ManifestPath. Create it from manifest\gguf-downloads.example.json." } +$m = Get-Content $full -Raw | ConvertFrom-Json +New-Item -ItemType Directory -Force -Path "$DriveRoot\models\gguf" | Out-Null +foreach ($item in $m.files) { + $out = Join-Path $DriveRoot $item.path + New-Item -ItemType Directory -Force -Path (Split-Path $out -Parent) | Out-Null + Write-Host "Downloading $($item.name)" + Invoke-WebRequest -Uri $item.url -OutFile $out + if ($item.sha256) { + $h = (Get-FileHash $out -Algorithm SHA256).Hash.ToLowerInvariant() + if ($h -ne $item.sha256.ToLowerInvariant()) { throw "SHA256 mismatch for $($item.name)" } + } +} diff --git a/factory/powershell/Install-LoraAdapters.ps1 b/factory/powershell/Install-LoraAdapters.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..cc2a1a365675426281afa62c2d89194b66df1fa8 --- /dev/null +++ b/factory/powershell/Install-LoraAdapters.ps1 @@ -0,0 +1,77 @@ +# Installs LoRA adapters into the package's Ollama model store at build time. +# +# Reads manifest\lora-adapters.json (see manifest\lora-adapters.example.json): +# each entry declares a base Ollama model, a GGUF LoRA adapter file (local path +# or URL + sha256), optional system prompt and inference parameters. The script +# stages the adapter under models\lora\, generates an Ollama Modelfile, and +# runs `ollama create` against the builder's Ollama store so the fine-tuned +# model ships on the key like any other model. +# +# Requires the builder Ollama running (same convention as model pulls): +# OLLAMA_HOST / OLLAMA_MODELS must point at the target store. +[CmdletBinding()] +param( + [string]$DriveRoot = (Resolve-Path "$PSScriptRoot\..\..").Path, + [string]$ManifestPath = "manifest\lora-adapters.json", + [string]$OllamaExe = "" +) +$ErrorActionPreference = "Stop" +Set-Location $DriveRoot + +$full = Join-Path $DriveRoot $ManifestPath +if (!(Test-Path $full)) { + Write-Host "No LoRA manifest found at $ManifestPath - nothing to do." + exit 0 +} +if (-not $OllamaExe) { + $candidates = @( + (Join-Path $DriveRoot "backends\ollama\windows\ollama.exe"), + "ollama" + ) + $OllamaExe = $candidates | Where-Object { (Test-Path $_) -or (Get-Command $_ -ErrorAction SilentlyContinue) } | Select-Object -First 1 +} +if (-not $OllamaExe) { throw "ollama.exe not found. Stage backends\ollama\windows\ollama.exe first." } + +$manifest = Get-Content $full -Raw | ConvertFrom-Json +$loraDir = Join-Path $DriveRoot "models\lora" +New-Item -ItemType Directory -Force -Path $loraDir | Out-Null + +foreach ($adapter in $manifest.adapters) { + if (-not $adapter.name) { throw "Each adapter needs a 'name' (the Ollama model name to create)." } + if (-not $adapter.base_model) { throw "Adapter $($adapter.name): 'base_model' is required (e.g. qwen3.5:4b)." } + + # Stage the adapter GGUF locally (URL + sha256, or a path already on disk). + $adapterFile = Join-Path $loraDir ("{0}.gguf" -f $adapter.name) + if ($adapter.url) { + if (!(Test-Path $adapterFile) -or ((Get-FileHash $adapterFile -Algorithm SHA256).Hash.ToLowerInvariant() -ne $adapter.sha256.ToLowerInvariant())) { + Write-Host "Downloading LoRA adapter $($adapter.name)" -ForegroundColor Cyan + Invoke-WebRequest -Uri $adapter.url -OutFile $adapterFile -UseBasicParsing + } + if ($adapter.sha256) { + $h = (Get-FileHash $adapterFile -Algorithm SHA256).Hash.ToLowerInvariant() + if ($h -ne $adapter.sha256.ToLowerInvariant()) { throw "SHA256 mismatch for adapter $($adapter.name)" } + } + } elseif ($adapter.path) { + $source = Join-Path $DriveRoot $adapter.path + if (!(Test-Path $source)) { throw "Adapter file not found: $source" } + Copy-Item $source $adapterFile -Force + } else { + throw "Adapter $($adapter.name): provide 'url' (+sha256) or 'path'." + } + + # Generate the Modelfile next to the adapter for traceability. + $lines = @("FROM $($adapter.base_model)", "ADAPTER $adapterFile") + if ($adapter.system) { $lines += "SYSTEM `"`"`"$($adapter.system)`"`"`"" } + if ($adapter.parameters) { + foreach ($p in $adapter.parameters.PSObject.Properties) { $lines += "PARAMETER $($p.Name) $($p.Value)" } + } + $modelfile = Join-Path $loraDir ("{0}.Modelfile" -f $adapter.name) + $lines -join "`n" | Set-Content -Encoding utf8 $modelfile + + Write-Host "Creating Ollama model $($adapter.name) (base $($adapter.base_model) + LoRA)" -ForegroundColor Cyan + & $OllamaExe create $adapter.name -f $modelfile + if ($LASTEXITCODE -ne 0) { throw "ollama create failed for $($adapter.name) (exit $LASTEXITCODE)" } + Write-Host "Created: $($adapter.name)" -ForegroundColor Green + + Write-Host "Reminder: add '$($adapter.name)' to config\model-catalog.json (Add-ModelToCatalog.ps1) so the runtime can select it, and record the adapter license in licenses\THIRD_PARTY_NOTICES.md." +} diff --git a/factory/powershell/Install-Models.ps1 b/factory/powershell/Install-Models.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..e8d5933bfb05c5560d750af0c79eb44e53b1d747 --- /dev/null +++ b/factory/powershell/Install-Models.ps1 @@ -0,0 +1,17 @@ +[CmdletBinding()] +param([string[]]$Models = @("qwen3.5:2b","qwen3.5:4b","qwen3.5:9b","qwen3-vl:4b")) +$ErrorActionPreference = "Stop" +$Root = Resolve-Path (Join-Path $PSScriptRoot "..\..") +$OllamaExe = Join-Path $Root "backends\ollama\windows\ollama.exe" +if (!(Test-Path $OllamaExe)) { throw "Missing Ollama binary: $OllamaExe" } +$env:OLLAMA_MODELS = Join-Path $Root "models\ollama" +New-Item -ItemType Directory -Force -Path $env:OLLAMA_MODELS | Out-Null +foreach ($m in $Models) { + Write-Host "Pulling $m into $env:OLLAMA_MODELS" + & $OllamaExe pull $m + if ($LASTEXITCODE -ne 0) { throw "ollama pull failed: $m" } +} + + +# Qwen3.6-27B is a Hugging Face power profile. It is not pulled with Ollama by default. +# Use the SaaS Builder manifest to download a vetted HF or GGUF artifact and pin its SHA256 before shipping. diff --git a/factory/powershell/JackAILocal-Factory-All.ps1 b/factory/powershell/JackAILocal-Factory-All.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..0ee88e5140e9facabf62d9a84de1e9644396dc4e --- /dev/null +++ b/factory/powershell/JackAILocal-Factory-All.ps1 @@ -0,0 +1,15 @@ +[CmdletBinding()] +param( + [string]$BrandName="JackAILocal", + [switch]$SkipModels, + [switch]$SkipRustBuild +) +$ErrorActionPreference = "Stop" +$Root = Resolve-Path (Join-Path $PSScriptRoot "..\..") +& "$PSScriptRoot\Apply-Branding.ps1" -BrandName $BrandName +if (-not $SkipRustBuild) { & "$PSScriptRoot\Build-RustBinaries.ps1" } +& "$PSScriptRoot\Install-Backends.ps1" +if (-not $SkipModels) { & "$PSScriptRoot\Install-Models.ps1" } +& "$PSScriptRoot\Generate-Manifest.ps1" +& "$PSScriptRoot\QA-Benchmark.ps1" -SmokeOnly +Write-Host "JackAILocal factory build complete." diff --git a/factory/powershell/Prepare-JackAILocalUSB.ps1 b/factory/powershell/Prepare-JackAILocalUSB.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..46861ca0d881b467210bb1b8f0fece1822d48d05 --- /dev/null +++ b/factory/powershell/Prepare-JackAILocalUSB.ps1 @@ -0,0 +1,151 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$TargetDrive, + [string]$BackendCache = "", + [ValidateSet("none","lite","balanced","full")][string]$ModelSet = "balanced", + [switch]$UseSystemOllamaAsSource, + [switch]$SkipRustBuild, + [switch]$SkipModels, + [switch]$GenerateManifestOnly +) + +$ErrorActionPreference = "Stop" + +function Resolve-Root { + return (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path +} + +function Ensure-Dir([string]$Path) { + New-Item -ItemType Directory -Force -Path $Path | Out-Null +} + +function Copy-IfExists([string]$From, [string]$To) { + if (Test-Path $From) { + Ensure-Dir (Split-Path $To) + Copy-Item $From $To -Force + return $true + } + return $false +} + +function Find-BackendFile([string]$Cache, [string[]]$Names) { + if (-not $Cache -or !(Test-Path $Cache)) { return $null } + foreach ($n in $Names) { + $direct = Join-Path $Cache $n + if (Test-Path $direct) { return (Resolve-Path $direct).Path } + $found = Get-ChildItem -Path $Cache -Recurse -File -Filter $n -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($found) { return $found.FullName } + } + return $null +} + +function Get-ModelList([string]$Set) { + switch ($Set) { + "none" { return @() } + "lite" { return @("qwen3.5:2b") } + "balanced" { return @("qwen3.5:2b", "qwen3.5:4b") } + "full" { return @("qwen3.5:2b", "qwen3.5:4b", "qwen3.5:9b", "qwen3-vl:4b") } + } +} + +function Generate-Manifest([string]$Root) { + $manifestDir = Join-Path $Root "manifest" + Ensure-Dir $manifestDir + $manifest = @() + $exclude = @(".jackailocal", "workspace", "diagnostics") + Get-ChildItem -Path $Root -Recurse -File | ForEach-Object { + $rel = $_.FullName.Substring($Root.Length).TrimStart('\','/') -replace '\\','/' + $first = ($rel -split '/')[0] + if ($exclude -contains $first) { return } + if ($rel -eq "manifest/sha256-manifest.json") { return } + $hash = (Get-FileHash -Algorithm SHA256 -Path $_.FullName).Hash.ToLowerInvariant() + $manifest += [ordered]@{ path=$rel; sha256=$hash; bytes=$_.Length } + } + $manifest | ConvertTo-Json -Depth 5 | Set-Content -Encoding UTF8 (Join-Path $manifestDir "sha256-manifest.json") +} + +$SourceRoot = Resolve-Root +$TargetRoot = (Resolve-Path $TargetDrive).Path +if (-not $TargetRoot.EndsWith("\")) { $TargetRoot += "\" } + +Write-Host "JackAILocal USB preparation" +Write-Host "Source: $SourceRoot" +Write-Host "Target: $TargetRoot" + +if ($GenerateManifestOnly) { + Generate-Manifest -Root $TargetRoot + Write-Host "Manifest generated." + exit 0 +} + +$requiredFreeGb = 16 +if ($ModelSet -eq "full") { $requiredFreeGb = 96 } +if ($ModelSet -eq "balanced") { $requiredFreeGb = 32 } +$drive = Get-PSDrive -Name $TargetRoot.Substring(0,1) +$freeGb = [math]::Round($drive.Free / 1GB, 1) +if ($freeGb -lt $requiredFreeGb) { throw "Target drive has $freeGb GB free. Required estimated minimum: $requiredFreeGb GB." } + +$items = @( + "START-HERE.cmd", "STOP-JACKAILOCAL.cmd", "README-FIRST.txt", "autorun.inf", + "webui", "windows", "config", "factory", "docs", "manifest", "models", "backends", "bin", "src", "Cargo.toml", "updates", "scripts", "tools", "content-packs", "licenses", "client-profiler", "client-builder", "saas", "linux" +) +foreach ($item in $items) { + $src = Join-Path $SourceRoot $item + $dst = Join-Path $TargetRoot $item + if (Test-Path $src) { + if (Test-Path $dst) { Remove-Item $dst -Recurse -Force } + Copy-Item $src $dst -Recurse -Force + } +} + +Ensure-Dir (Join-Path $TargetRoot "bin") +Ensure-Dir (Join-Path $TargetRoot "models\ollama") +Ensure-Dir (Join-Path $TargetRoot "models\gguf") +Ensure-Dir (Join-Path $TargetRoot ".jackailocal\logs") +Ensure-Dir (Join-Path $TargetRoot "workspace\documents") + +if (-not $SkipRustBuild) { + $cargo = Get-Command cargo -ErrorAction SilentlyContinue + if ($cargo) { + Push-Location $TargetRoot + cargo build --release + if ($LASTEXITCODE -ne 0) { throw "cargo build failed" } + Copy-Item (Join-Path $TargetRoot "target\release\jackailocald.exe") (Join-Path $TargetRoot "bin\jackailocald.exe") -Force + Pop-Location + } else { + Write-Warning "cargo not found. Put compiled jackailocald.exe in bin\ before shipping." + } +} + +$ollamaDest = Join-Path $TargetRoot "backends\ollama\windows\ollama.exe" +$llamaDest = Join-Path $TargetRoot "backends\llama.cpp\windows\llama-server.exe" + +$ollamaSource = Find-BackendFile -Cache $BackendCache -Names @("ollama.exe") +$llamaSource = Find-BackendFile -Cache $BackendCache -Names @("llama-server.exe") + +if (-not $ollamaSource -and $UseSystemOllamaAsSource) { + $cmd = Get-Command ollama.exe -ErrorAction SilentlyContinue + if ($cmd) { $ollamaSource = $cmd.Source } +} + +if ($ollamaSource) { Copy-IfExists $ollamaSource $ollamaDest | Out-Null } else { Write-Warning "ollama.exe missing. Put it in $ollamaDest or pass -BackendCache." } +if ($llamaSource) { Copy-IfExists $llamaSource $llamaDest | Out-Null } else { Write-Warning "llama-server.exe missing. Fallback disabled until installed." } + +if (-not $SkipModels -and $ModelSet -ne "none") { + if (!(Test-Path $ollamaDest)) { throw "Cannot pull models: missing $ollamaDest" } + $env:OLLAMA_MODELS = Join-Path $TargetRoot "models\ollama" + $env:OLLAMA_HOST = "127.0.0.1:11434" + $models = Get-ModelList $ModelSet + foreach ($m in $models) { + Write-Host "Pulling model to USB: $m" + & $ollamaDest pull $m + if ($LASTEXITCODE -ne 0) { throw "ollama pull failed: $m" } + } +} + +Generate-Manifest -Root $TargetRoot + +Write-Host "" +Write-Host "Preparation complete." +Write-Host "Client launch command: $TargetRoot\START-HERE.cmd" +Write-Host "Required before shipping: bin\jackailocald.exe, backends\ollama\windows\ollama.exe, models\ollama contents." diff --git a/factory/powershell/Prepare-WindowsPortableDrive.ps1 b/factory/powershell/Prepare-WindowsPortableDrive.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..ce4608cc7f81e6cb0acb289eac3834dbfe7f3b6d --- /dev/null +++ b/factory/powershell/Prepare-WindowsPortableDrive.ps1 @@ -0,0 +1,19 @@ +param( + [string]$DriveRoot = (Resolve-Path "$PSScriptRoot\..\..").Path, + [switch]$DownloadBackends, + [switch]$PullModels, + [switch]$BuildRust, + [string]$BrandName = "JackAILocal" +) +$ErrorActionPreference = 'Stop' +Set-Location $DriveRoot +Write-Host "Preparing $BrandName at $DriveRoot" +New-Item -ItemType Directory -Force -Path bin, logs, diagnostics, cache\tmp, cache\runtime, models\ollama, models\gguf, models\hf | Out-Null + +if ($BuildRust) { & "$DriveRoot\factory\powershell\Build-RustBinaries.ps1" -DriveRoot $DriveRoot } +if ($DownloadBackends) { & "$DriveRoot\factory\powershell\Install-Backends.ps1" -DriveRoot $DriveRoot } +if ($PullModels) { & "$DriveRoot\factory\powershell\Install-Models.ps1" -DriveRoot $DriveRoot } +& "$DriveRoot\factory\powershell\Apply-Branding.ps1" -DriveRoot $DriveRoot -BrandName $BrandName +& "$DriveRoot\factory\powershell\Generate-Manifest.ps1" -DriveRoot $DriveRoot +& "$DriveRoot\windows\Preflight-Windows.ps1" +Write-Host "Factory preparation complete." diff --git a/factory/powershell/QA-Benchmark.ps1 b/factory/powershell/QA-Benchmark.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a781bb05fcc907229e6a13a4793463f953a52262 --- /dev/null +++ b/factory/powershell/QA-Benchmark.ps1 @@ -0,0 +1,9 @@ +[CmdletBinding()] +param([switch]$SmokeOnly) +$Root = Resolve-Path (Join-Path $PSScriptRoot "..\..") +$report = [ordered]@{ product="JackAILocal"; time=(Get-Date).ToString("o"); checks=@() } +foreach ($p in @("START-HERE.cmd","windows\Start-JackAILocal.ps1","config\model-catalog.json","manifest\product.json")) { + $report.checks += [pscustomobject]@{ path=$p; exists=(Test-Path (Join-Path $Root $p)) } +} +$report | ConvertTo-Json -Depth 10 | Set-Content -Encoding UTF8 (Join-Path $Root "diagnostics\qa-smoke-report.json") +Write-Host "QA smoke report written." diff --git a/factory/powershell/Set-DefaultModel.ps1 b/factory/powershell/Set-DefaultModel.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..74c7c93138c045d0dca992bb6ba4d114633324e4 --- /dev/null +++ b/factory/powershell/Set-DefaultModel.ps1 @@ -0,0 +1,11 @@ +[CmdletBinding()] +param([Parameter(Mandatory=$true)][string]$ProfileId) +$Root = Resolve-Path (Join-Path $PSScriptRoot "..\..") +$Path = Join-Path $Root "config\jackailocal.windows.toml" +$content = Get-Content $Path -Raw +if ($content -match 'default_profile\s*=') { + $content = $content -replace 'default_profile\s*=\s*"[^"]+"', "default_profile = `"$ProfileId`"" +} else { + $content += "`ndefault_profile = `"$ProfileId`"`n" +} +$content | Set-Content -Encoding UTF8 $Path diff --git a/factory/powershell/Sign-Executables.ps1 b/factory/powershell/Sign-Executables.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..ebcee5079f93a7e923a82a0fff8a378c3cf092da --- /dev/null +++ b/factory/powershell/Sign-Executables.ps1 @@ -0,0 +1,58 @@ +# Signs Windows executables with Authenticode so SmartScreen and antivirus +# engines can establish publisher reputation. Without a signature, customers +# see "Windows protected your PC" on every launch. +# +# Configuration (environment variables): +# JACKAI_SIGN_CERT_THUMBPRINT thumbprint of a cert in the CurrentUser/LocalMachine store +# JACKAI_SIGN_PFX path to a .pfx file (alternative to thumbprint) +# JACKAI_SIGN_PFX_PASSWORD password for the .pfx +# JACKAI_TIMESTAMP_URL RFC3161 timestamp server (default: DigiCert) +# +# If neither JACKAI_SIGN_CERT_THUMBPRINT nor JACKAI_SIGN_PFX is set, the script +# warns and exits 0 so development builds keep working unsigned. +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string[]]$Path +) +$ErrorActionPreference = "Stop" + +$thumbprint = $env:JACKAI_SIGN_CERT_THUMBPRINT +$pfxPath = $env:JACKAI_SIGN_PFX +if (-not $thumbprint -and -not $pfxPath) { + Write-Warning "Code signing skipped: set JACKAI_SIGN_CERT_THUMBPRINT or JACKAI_SIGN_PFX to sign release builds. Unsigned builds trigger SmartScreen warnings for customers." + exit 0 +} + +$timestampUrl = if ($env:JACKAI_TIMESTAMP_URL) { $env:JACKAI_TIMESTAMP_URL } else { "http://timestamp.digicert.com" } + +$signtool = Get-Command signtool.exe -ErrorAction SilentlyContinue +if (-not $signtool) { + $kitsRoot = "${env:ProgramFiles(x86)}\Windows Kits\10\bin" + if (Test-Path $kitsRoot) { + $candidate = Get-ChildItem -Path $kitsRoot -Recurse -Filter signtool.exe -ErrorAction SilentlyContinue | + Where-Object { $_.FullName -match "x64" } | + Sort-Object FullName -Descending | + Select-Object -First 1 + if ($candidate) { $signtool = $candidate.FullName } + } +} +if (-not $signtool) { throw "signtool.exe not found. Install the Windows SDK or add signtool to PATH." } +$signtoolPath = if ($signtool -is [string]) { $signtool } else { $signtool.Source } + +foreach ($file in $Path) { + if (!(Test-Path $file -PathType Leaf)) { throw "Cannot sign missing file: $file" } + $arguments = @("sign", "/fd", "SHA256", "/td", "SHA256", "/tr", $timestampUrl) + if ($thumbprint) { + $arguments += @("/sha1", $thumbprint) + } else { + $arguments += @("/f", $pfxPath) + if ($env:JACKAI_SIGN_PFX_PASSWORD) { $arguments += @("/p", $env:JACKAI_SIGN_PFX_PASSWORD) } + } + $arguments += $file + & $signtoolPath @arguments + if ($LASTEXITCODE -ne 0) { throw "signtool failed for $file (exit $LASTEXITCODE)" } + & $signtoolPath verify /pa $file + if ($LASTEXITCODE -ne 0) { throw "signature verification failed for $file" } + Write-Host "Signed: $file" -ForegroundColor Green +} diff --git a/factory/unix/build-whisper-backend.sh b/factory/unix/build-whisper-backend.sh new file mode 100644 index 0000000000000000000000000000000000000000..28c749b67854fa4cb2848247933fef02f5e5850b --- /dev/null +++ b/factory/unix/build-whisper-backend.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +TAG="${WHISPER_CPP_TAG:-v1.8.6}" +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT + +case "$(uname -s)" in + Darwin) PLATFORM=macos ;; + Linux) PLATFORM=linux ;; + *) echo "Unsupported platform: $(uname -s)" >&2; exit 1 ;; +esac + +for cmd in git cmake; do + command -v "$cmd" >/dev/null 2>&1 || { echo "$cmd is required to build whisper.cpp." >&2; exit 1; } +done + +git clone --depth 1 --branch "$TAG" https://github.com/ggml-org/whisper.cpp.git "$WORK/whisper.cpp" +cmake -S "$WORK/whisper.cpp" -B "$WORK/whisper.cpp/build" -DCMAKE_BUILD_TYPE=Release +cmake --build "$WORK/whisper.cpp/build" --config Release --parallel + +binary="$(find "$WORK/whisper.cpp/build" -type f -name whisper-cli -perm -111 | head -n 1)" +[ -n "$binary" ] || { echo "whisper-cli was not produced by the build." >&2; exit 1; } +dest="$ROOT/backends/whisper.cpp/$PLATFORM" +mkdir -p "$dest" +cp "$binary" "$dest/whisper-cli" +chmod +x "$dest/whisper-cli" +echo "whisper.cpp $TAG staged at $dest/whisper-cli" diff --git a/factory/unix/sign-and-notarize-macos.sh b/factory/unix/sign-and-notarize-macos.sh new file mode 100644 index 0000000000000000000000000000000000000000..be5fd5dae46e77e7d41e1f6b43cefb583560152f --- /dev/null +++ b/factory/unix/sign-and-notarize-macos.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# Signs and notarizes macOS binaries so Gatekeeper accepts them. +# Unsigned binaries distributed outside the App Store are blocked by default +# on modern macOS; notarization is required for a frictionless first launch. +# +# Configuration (environment variables): +# JACKAI_CODESIGN_IDENTITY "Developer ID Application: Your Name (TEAMID)" +# JACKAI_NOTARY_PROFILE notarytool keychain profile name (created with +# `xcrun notarytool store-credentials`) +# +# Usage: +# sign-and-notarize-macos.sh [file...] +# +# If JACKAI_CODESIGN_IDENTITY is unset, warns and exits 0 so development +# builds keep working unsigned. +set -euo pipefail + +if [[ $# -lt 1 ]]; then + echo "usage: $0 [file...]" >&2 + exit 2 +fi + +if [[ -z "${JACKAI_CODESIGN_IDENTITY:-}" ]]; then + echo "WARNING: code signing skipped. Set JACKAI_CODESIGN_IDENTITY to sign release builds." >&2 + echo " Unsigned builds are blocked by Gatekeeper on customer machines." >&2 + exit 0 +fi + +for file in "$@"; do + if [[ ! -f "$file" ]]; then + echo "ERROR: cannot sign missing file: $file" >&2 + exit 1 + fi + codesign --force --options runtime --timestamp \ + --sign "$JACKAI_CODESIGN_IDENTITY" "$file" + codesign --verify --strict "$file" + echo "Signed: $file" +done + +if [[ -n "${JACKAI_NOTARY_PROFILE:-}" ]]; then + workdir="$(mktemp -d)" + trap 'rm -rf "$workdir"' EXIT + archive="$workdir/notarize.zip" + /usr/bin/ditto -c -k --keepParent "$1" "$archive" + xcrun notarytool submit "$archive" --keychain-profile "$JACKAI_NOTARY_PROFILE" --wait + if xcrun stapler staple "$1" 2>/dev/null; then + echo "Stapled notarization ticket: $1" + else + echo "NOTE: stapling skipped (plain binaries cannot be stapled; DMG/app bundles can)." >&2 + fi +else + echo "WARNING: notarization skipped. Set JACKAI_NOTARY_PROFILE to notarize release builds." >&2 +fi diff --git a/installer/macos/build-dmg.sh b/installer/macos/build-dmg.sh new file mode 100644 index 0000000000000000000000000000000000000000..be42085c3f626403a73c66b8e842dea1d4eaef12 --- /dev/null +++ b/installer/macos/build-dmg.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +OUT_DIR="${1:-$ROOT/dist/dmg}" +APP_DIR="$OUT_DIR/JackAILocal.app" +CONTENTS="$APP_DIR/Contents" +RESOURCES="$CONTENTS/Resources/JackAILocal" +MACOS_DIR="$CONTENTS/MacOS" + +require_file() { + [ -f "$ROOT/$1" ] || { echo "Required DMG payload file is missing: $ROOT/$1" >&2; exit 1; } +} + +require_file "bin/jackailocald" +require_file "backends/ollama/macos/ollama" +require_file "webui/index.html" +require_file "webui/app-v15.js" +require_file "config/jackailocal.macos.toml" + +if ! find "$ROOT/models/ollama" -type f ! -name '*.txt' -print -quit | grep -q .; then + echo "No real Ollama model files were found under models/ollama. Preload at least one model before building the DMG." >&2 + exit 1 +fi + +rm -rf "$APP_DIR" +mkdir -p "$RESOURCES" "$MACOS_DIR" +rsync -a \ + --exclude '.git' --exclude 'target' --exclude 'workspace' --exclude 'diagnostics' \ + --exclude '.jackailocal' --exclude '.jackailocal-builder' --exclude 'config/update-private-key.xml' \ + --exclude 'license-keys' --exclude '__pycache__' \ + "$ROOT/" "$RESOURCES/" + +cat > "$MACOS_DIR/JackAILocal" <<'SH' +#!/usr/bin/env bash +set -euo pipefail +ROOT="$(cd "$(dirname "$0")/../Resources/JackAILocal" && pwd)" +chmod +x "$ROOT/macos/Start-JackAILocal.sh" "$ROOT/bin/jackailocald" "$ROOT/backends/ollama/macos/ollama" +exec "$ROOT/macos/Start-JackAILocal.sh" +SH +chmod +x "$MACOS_DIR/JackAILocal" + +cat > "$CONTENTS/Info.plist" <<'PLIST' + + + + + CFBundleDisplayNameJackAILocal + CFBundleExecutableJackAILocal + CFBundleIdentifierio.jackailocal.desktop + CFBundleNameJackAILocal + CFBundlePackageTypeAPPL + CFBundleShortVersionString1.5.0 + + +PLIST + +if [ -n "${JACKAI_CODESIGN_IDENTITY:-}" ]; then + codesign --force --deep --options runtime --timestamp \ + --sign "$JACKAI_CODESIGN_IDENTITY" "$APP_DIR" + codesign --verify --strict "$APP_DIR" + echo "Signed app bundle: $APP_DIR" +else + echo "WARNING: code signing skipped. Set JACKAI_CODESIGN_IDENTITY to sign release builds." >&2 + echo " Unsigned apps are blocked by Gatekeeper on customer machines." >&2 +fi + +mkdir -p "$OUT_DIR" +hdiutil create -volname JackAILocal -srcfolder "$APP_DIR" -ov -format UDZO "$OUT_DIR/JackAILocal.dmg" + +if [ -n "${JACKAI_CODESIGN_IDENTITY:-}" ] && [ -n "${JACKAI_NOTARY_PROFILE:-}" ]; then + codesign --force --timestamp --sign "$JACKAI_CODESIGN_IDENTITY" "$OUT_DIR/JackAILocal.dmg" + xcrun notarytool submit "$OUT_DIR/JackAILocal.dmg" --keychain-profile "$JACKAI_NOTARY_PROFILE" --wait + xcrun stapler staple "$OUT_DIR/JackAILocal.dmg" + echo "Notarized and stapled: $OUT_DIR/JackAILocal.dmg" +elif [ -n "${JACKAI_CODESIGN_IDENTITY:-}" ]; then + echo "WARNING: notarization skipped. Set JACKAI_NOTARY_PROFILE to notarize release builds." >&2 +fi + +echo "Real macOS DMG created: $OUT_DIR/JackAILocal.dmg" diff --git a/installer/windows/Build-Installer.ps1 b/installer/windows/Build-Installer.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..62d50f6755183291209f865f13df9cae3a5adf89 --- /dev/null +++ b/installer/windows/Build-Installer.ps1 @@ -0,0 +1,52 @@ +[CmdletBinding()] +param( + [string]$PayloadRoot = "", + [string]$OutputDir = "", + [string]$IsccPath = "" +) + +$ErrorActionPreference = "Stop" +$Root = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path +if (-not $PayloadRoot) { $PayloadRoot = $Root } +$PayloadRoot = (Resolve-Path $PayloadRoot).Path +if (-not $OutputDir) { $OutputDir = Join-Path $Root "dist\installer" } +New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null +$OutputDir = (Resolve-Path $OutputDir).Path + +function Require-File([string]$RelativePath) { + $path = Join-Path $PayloadRoot $RelativePath + if (!(Test-Path $path -PathType Leaf)) { throw "Required installer payload file is missing: $path" } +} + +Require-File "bin\jackailocald.exe" +Require-File "backends\ollama\windows\ollama.exe" +Require-File "webui\index.html" +Require-File "webui\app-v15.js" +Require-File "config\jackailocal.windows.toml" +Require-File "START-DESKTOP.cmd" + +$modelFiles = @(Get-ChildItem -Path (Join-Path $PayloadRoot "models\ollama") -Recurse -File -ErrorAction SilentlyContinue | + Where-Object { $_.Name -notlike "*.txt" }) +if ($modelFiles.Count -eq 0) { + throw "No real Ollama model files were found under models\ollama. Preload at least one model before building the installer." +} + +if (-not $IsccPath) { + $candidates = @( + (Join-Path ${env:ProgramFiles(x86)} "Inno Setup 6\ISCC.exe"), + (Join-Path $env:ProgramFiles "Inno Setup 6\ISCC.exe") + ) + $IsccPath = $candidates | Where-Object { $_ -and (Test-Path $_) } | Select-Object -First 1 +} +if (-not $IsccPath -or !(Test-Path $IsccPath)) { + throw "Inno Setup 6 compiler was not found. Install Inno Setup or pass -IsccPath." +} + +$iss = Join-Path $PSScriptRoot "JackAILocal.iss" +& $IsccPath "/DPayloadRoot=$PayloadRoot" "/DOutputDir=$OutputDir" $iss +if ($LASTEXITCODE -ne 0) { throw "Inno Setup failed with exit code $LASTEXITCODE." } + +$installer = Join-Path $OutputDir "JackAILocalSetup.exe" +if (!(Test-Path $installer)) { throw "Installer was not created: $installer" } +& (Join-Path $Root "factory\powershell\Sign-Executables.ps1") -Path $installer +Write-Host "Real Windows installer created: $installer" -ForegroundColor Green diff --git a/installer/windows/JackAILocal.iss b/installer/windows/JackAILocal.iss new file mode 100644 index 0000000000000000000000000000000000000000..936b6929416a684c6a7c001f625e0aae3d783d63 --- /dev/null +++ b/installer/windows/JackAILocal.iss @@ -0,0 +1,52 @@ +#ifndef PayloadRoot + #error PayloadRoot must be provided by Build-Installer.ps1 +#endif +#ifndef OutputDir + #define OutputDir "." +#endif + +#define AppName "JackAILocal" +#define AppVersion "1.5.0" +#define AppPublisher "JackAILocal" +#define AppExeName "START-DESKTOP.cmd" + +[Setup] +AppId={{B7FE19CB-AD20-4C60-A6D0-7FA165BB50A8} +AppName={#AppName} +AppVersion={#AppVersion} +AppPublisher={#AppPublisher} +DefaultDirName={localappdata}\Programs\{#AppName} +DefaultGroupName={#AppName} +DisableProgramGroupPage=yes +OutputDir={#OutputDir} +OutputBaseFilename=JackAILocalSetup +Compression=lzma2 +SolidCompression=yes +PrivilegesRequired=lowest +ArchitecturesAllowed=x64compatible +ArchitecturesInstallIn64BitMode=x64compatible +UninstallDisplayIcon={app}\{#AppExeName} +WizardStyle=modern + +[Files] +Source: "{#PayloadRoot}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: ".git\*,target\*,workspace\*,diagnostics\*,.jackailocal\*,.jackailocal-builder\*,config\update-private-key.xml,license-keys\*,saas\gradio\__pycache__\*" + +[Dirs] +Name: "{app}\workspace\documents" +Name: "{app}\workspace\threads" +Name: "{app}\workspace\exports" +Name: "{app}\.jackailocal\logs" + +[Icons] +Name: "{group}\JackAILocal"; Filename: "{app}\{#AppExeName}"; WorkingDir: "{app}" +Name: "{group}\Stop JackAILocal"; Filename: "{app}\STOP-JACKAILOCAL.cmd"; WorkingDir: "{app}" +Name: "{autodesktop}\JackAILocal"; Filename: "{app}\{#AppExeName}"; WorkingDir: "{app}"; Tasks: desktopicon + +[Tasks] +Name: "desktopicon"; Description: "Create a desktop shortcut"; GroupDescription: "Additional shortcuts:" + +[Run] +Filename: "{app}\{#AppExeName}"; Description: "Launch JackAILocal"; Flags: postinstall nowait skipifsilent + +[UninstallRun] +Filename: "{cmd}"; Parameters: "/c ""{app}\STOP-JACKAILOCAL.cmd"""; Flags: runhidden skipifdoesntexist; RunOnceId: "StopJackAILocal" diff --git a/legal/EULA_EN.md b/legal/EULA_EN.md new file mode 100644 index 0000000000000000000000000000000000000000..eb450fb157db28d0f673f687f20aab02376add6d --- /dev/null +++ b/legal/EULA_EN.md @@ -0,0 +1,133 @@ +# JackAILocal — End User License Agreement (EULA) + +**Version 1.0 — Effective date: to be set at first commercial release** + +> NOTE TO PUBLISHER: have this document reviewed by legal counsel before first +> commercial sale. The French version (`EULA_FR.md`) must be provided to +> customers in Québec; in case of discrepancy for a Québec consumer, the +> French version prevails. + +This End User License Agreement ("Agreement") is a contract between you +("Customer") and the publisher of JackAILocal ("Publisher") and governs the +use of the JackAILocal software, including its runtime, web interface, +builders, documentation and any update provided by the Publisher +(collectively the "Software"). + +By installing, copying or using the Software, you accept this Agreement. If +you do not accept it, do not use the Software and contact your seller for a +refund according to the applicable sales terms. + +## 1. License grant + +Subject to payment and to this Agreement, the Publisher grants you a +non-exclusive, non-transferable license to install and use the Software: + +- **Personal edition**: on devices belonging to a single household, for + personal, non-commercial use. +- **Pro edition**: by a single named user, for professional use. +- **Business edition**: by the number of users or devices stated in your + license file (`license/license.json`). +- **White-label / Appliance editions**: per the signed agreement between the + Publisher and the business customer. + +The edition, scope and any expiry date are recorded in the cryptographically +signed license file delivered with your purchase. + +## 2. Third-party components and model weights + +The Software ships with third-party components and AI model weights that are +licensed under their own terms, listed in `licenses/THIRD_PARTY_NOTICES.md`. +Those terms — including any acceptable-use restrictions attached to specific +model weights — apply to your use of those components and prevail over this +Agreement for those components. In particular, if your package includes: + +- **Gemma models (Google)**: your use is subject to the Gemma Terms of Use + (ai.google.dev/gemma/terms), and you agree to comply with the use + restrictions of the Gemma Prohibited Use Policy + (ai.google.dev/gemma/prohibited_use_policy), which are incorporated into + this Agreement by reference. A copy of the Gemma Terms of Use is provided + in the `licenses/` folder. +- **Llama models (Meta)**: your use is subject to the applicable Llama + Community License and Meta's Acceptable Use Policy, which are incorporated + into this Agreement by reference; a copy is provided in the `licenses/` + folder. + +## 3. Restrictions + +You may not: (a) redistribute, resell, rent or provide the Software as a +service to third parties without a written agreement with the Publisher; +(b) remove copyright, license or attribution notices; (c) circumvent the +license verification mechanism; (d) reverse engineer the Software except to +the extent permitted by mandatory law. + +## 4. AI output disclaimer + +The Software runs artificial-intelligence models locally on your hardware. +AI-generated content may be inaccurate, incomplete or inappropriate. It is +provided for information only and does not constitute professional advice +(legal, medical, financial, safety-critical or otherwise). You are solely +responsible for reviewing and validating AI output before relying on it or +distributing it. In professional contexts, a qualified human must review +AI-assisted work product. + +## 5. Privacy + +The Software is designed to operate entirely offline. It does not transmit +your conversations, documents or files to the Publisher or to any third +party. See `legal/PRIVACY_EN.md` for details. + +## 6. Updates and support + +Updates are delivered as cryptographically signed packages (online or via +physical media). The Publisher's support and update commitments, if any, are +stated in your sales terms. Installing updates from any source other than the +Publisher is at your own risk. + +## 7. Ownership + +The Software is licensed, not sold. The Publisher and its licensors retain +all rights not expressly granted. You retain all rights to the content you +create and to your data. + +## 8. Warranty + +Where you are a consumer, you benefit from the legal warranties provided by +applicable consumer-protection law (including the Québec *Consumer Protection +Act* and the legal warranty of quality), which this Agreement does not and +cannot exclude. To the maximum extent permitted by applicable law, and except +for those mandatory warranties, the Software is provided "as is" without +other warranty of any kind. + +## 9. Limitation of liability + +To the maximum extent permitted by applicable law, the Publisher's total +liability under this Agreement is limited to the amount you paid for the +Software in the twelve (12) months preceding the claim, and the Publisher is +not liable for indirect or consequential damages, loss of data not caused by +the Software, or decisions made in reliance on AI-generated content. Nothing +in this Agreement limits liability that cannot be limited under applicable +law, including under the Québec *Consumer Protection Act* and the *Civil Code +of Québec*. + +## 10. Termination + +This Agreement terminates automatically if you materially breach it. Upon +termination you must stop using the Software and destroy your copies, except +for data you created, which remains yours. + +## 11. Governing law + +This Agreement is governed by the laws of the Province of Québec and the laws +of Canada applicable therein. Disputes are subject to the courts of the +judicial district of the Publisher's head office, without depriving a +consumer of the right to proceed in their own district or before the small +claims division. + +## 12. Language + +The parties confirm that, where permitted by law, they have requested that +this Agreement be drawn up in English. A French version is provided and, for +Québec consumers, prevails in case of discrepancy. / Les parties confirment +avoir demandé, là où la loi le permet, que cette convention soit rédigée en +anglais; une version française est fournie et, pour les consommateurs du +Québec, a préséance en cas de divergence. diff --git a/legal/EULA_FR.md b/legal/EULA_FR.md new file mode 100644 index 0000000000000000000000000000000000000000..80e13ffddab0983dced320f9349af4329d1e6a4d --- /dev/null +++ b/legal/EULA_FR.md @@ -0,0 +1,136 @@ +# JackAILocal — Contrat de licence d'utilisateur final (CLUF) + +**Version 1.0 — Date d'entrée en vigueur : à fixer au premier lancement commercial** + +> NOTE À L'ÉDITEUR : faire réviser ce document par un conseiller juridique +> avant la première vente commerciale. Pour les consommateurs du Québec, la +> présente version française a préséance sur la version anglaise en cas de +> divergence. + +Le présent contrat de licence d'utilisateur final (« Contrat ») est conclu +entre vous (« Client ») et l'éditeur de JackAILocal (« Éditeur ») et régit +l'utilisation du logiciel JackAILocal, y compris son moteur d'exécution, son +interface web, ses outils de création de packages, sa documentation et toute +mise à jour fournie par l'Éditeur (collectivement le « Logiciel »). + +En installant, copiant ou utilisant le Logiciel, vous acceptez ce Contrat. Si +vous ne l'acceptez pas, n'utilisez pas le Logiciel et contactez votre vendeur +pour un remboursement selon les conditions de vente applicables. + +## 1. Octroi de licence + +Sous réserve du paiement et du présent Contrat, l'Éditeur vous accorde une +licence non exclusive et non transférable d'installation et d'utilisation du +Logiciel : + +- **Édition Personnelle** : sur les appareils d'un même foyer, pour un usage + personnel et non commercial. +- **Édition Pro** : par un seul utilisateur désigné, pour un usage + professionnel. +- **Édition Affaires** : selon le nombre d'utilisateurs ou d'appareils inscrit + dans votre fichier de licence (`license/license.json`). +- **Éditions Marque blanche / Appliance** : selon l'entente signée entre + l'Éditeur et le client d'affaires. + +L'édition, la portée et toute date d'expiration sont inscrites dans le fichier +de licence signé cryptographiquement livré avec votre achat. + +## 2. Composants tiers et poids de modèles + +Le Logiciel est livré avec des composants tiers et des poids de modèles +d'intelligence artificielle régis par leurs propres licences, listées dans +`licenses/THIRD_PARTY_NOTICES.md`. Ces conditions — y compris toute +restriction d'utilisation rattachée à des poids de modèles particuliers — +s'appliquent à votre utilisation de ces composants et ont préséance sur le +présent Contrat pour ces composants. En particulier, si votre package inclut : + +- **Modèles Gemma (Google)** : votre utilisation est assujettie aux Gemma + Terms of Use (ai.google.dev/gemma/terms) et vous vous engagez à respecter + les restrictions d'utilisation de la Gemma Prohibited Use Policy + (ai.google.dev/gemma/prohibited_use_policy), intégrées au présent Contrat + par renvoi. Une copie des Gemma Terms of Use est fournie dans le dossier + `licenses/`. +- **Modèles Llama (Meta)** : votre utilisation est assujettie à la Llama + Community License applicable et à l'Acceptable Use Policy de Meta, + intégrées au présent Contrat par renvoi; une copie est fournie dans le + dossier `licenses/`. + +## 3. Restrictions + +Vous ne pouvez pas : a) redistribuer, revendre, louer le Logiciel ni l'offrir +en service à des tiers sans entente écrite avec l'Éditeur; b) retirer les +mentions de droit d'auteur, de licence ou d'attribution; c) contourner le +mécanisme de vérification de licence; d) procéder à l'ingénierie inverse du +Logiciel, sauf dans la mesure permise par une loi d'ordre public. + +## 4. Avertissement sur le contenu généré par IA + +Le Logiciel exécute des modèles d'intelligence artificielle localement sur +votre matériel. Le contenu généré par IA peut être inexact, incomplet ou +inapproprié. Il est fourni à titre informatif seulement et ne constitue pas un +avis professionnel (juridique, médical, financier, critique pour la sécurité +ou autre). Vous êtes seul responsable de réviser et de valider le contenu +généré avant de vous y fier ou de le diffuser. Dans un contexte +professionnel, un humain qualifié doit réviser tout travail assisté par IA. + +## 5. Vie privée + +Le Logiciel est conçu pour fonctionner entièrement hors ligne. Il ne transmet +ni vos conversations, ni vos documents, ni vos fichiers à l'Éditeur ou à des +tiers. Voir `legal/PRIVACY_FR.md` pour les détails. + +## 6. Mises à jour et soutien + +Les mises à jour sont livrées sous forme de packages signés +cryptographiquement (en ligne ou sur support physique). Les engagements de +soutien et de mise à jour de l'Éditeur, le cas échéant, sont énoncés dans vos +conditions de vente. L'installation de mises à jour provenant d'une autre +source que l'Éditeur est à vos risques. + +## 7. Propriété + +Le Logiciel est concédé sous licence et non vendu. L'Éditeur et ses concédants +conservent tous les droits non expressément accordés. Vous conservez tous vos +droits sur le contenu que vous créez et sur vos données. + +## 8. Garantie + +Si vous êtes un consommateur, vous bénéficiez des garanties légales prévues +par les lois applicables en matière de protection du consommateur (notamment +la *Loi sur la protection du consommateur* du Québec et la garantie légale de +qualité), que le présent Contrat n'exclut pas et ne peut exclure. Dans la +mesure maximale permise par la loi applicable, et sous réserve de ces +garanties impératives, le Logiciel est fourni « tel quel », sans autre +garantie de quelque nature. + +## 9. Limitation de responsabilité + +Dans la mesure maximale permise par la loi applicable, la responsabilité +totale de l'Éditeur en vertu du présent Contrat est limitée au montant que +vous avez payé pour le Logiciel au cours des douze (12) mois précédant la +réclamation, et l'Éditeur n'est pas responsable des dommages indirects ou +consécutifs, de la perte de données non causée par le Logiciel, ni des +décisions prises sur la foi de contenu généré par IA. Rien dans le présent +Contrat ne limite une responsabilité qui ne peut être limitée en vertu de la +loi applicable, notamment la *Loi sur la protection du consommateur* et le +*Code civil du Québec*. + +## 10. Résiliation + +Le présent Contrat prend fin automatiquement en cas de manquement important de +votre part. À la résiliation, vous devez cesser d'utiliser le Logiciel et +détruire vos copies, à l'exception des données que vous avez créées, qui +demeurent les vôtres. + +## 11. Droit applicable + +Le présent Contrat est régi par les lois de la province de Québec et les lois +du Canada qui s'y appliquent. Les litiges relèvent des tribunaux du district +judiciaire du siège de l'Éditeur, sans priver un consommateur du droit d'agir +dans son propre district ou devant la division des petites créances. + +## 12. Langue + +La présente version française est fournie conformément à la *Charte de la +langue française*. Pour les consommateurs du Québec, elle a préséance sur la +version anglaise en cas de divergence. diff --git a/legal/PRIVACY_EN.md b/legal/PRIVACY_EN.md new file mode 100644 index 0000000000000000000000000000000000000000..60323bd52f467ec3103af5e760ddd49d9369c65b --- /dev/null +++ b/legal/PRIVACY_EN.md @@ -0,0 +1,57 @@ +# JackAILocal — Privacy Policy + +**Version 1.0 — Effective date: to be set at first commercial release** + +> NOTE TO PUBLISHER: complete the "Publisher contact" section (legal name, +> address, privacy officer) before first sale. Québec's Act respecting the +> protection of personal information in the private sector (Law 25) requires +> a designated person in charge of personal information protection. + +## The short version + +JackAILocal runs entirely on your device. Your conversations, documents, +images and voice recordings are processed locally and are never transmitted +to the Publisher or to any third party by the Software. + +## 1. What the Software processes locally + +- Chat conversations, documents, images analyzed by SCOUT, and voice audio + are processed by AI models running on your own hardware and stored only in + the Software's workspace folder on your device or drive. +- Encrypted export packs (AES-256-GCM) are created only when you request + them, with a password you choose. +- Optional Phone Access exposes the interface only on your local network, + is disabled by default, and requires a pairing token. + +## 2. What the Publisher collects + +The Software itself contains **no telemetry, no analytics, no account system +and no automatic update check**. The Publisher collects personal information +only outside the Software, when you: + +- purchase a product (name, billing address, email — processed by the store + and payment provider listed in your sales terms); +- request support and choose to send a support bundle. Support bundles + contain hardware status, backend status and logs; they exclude your + conversations and documents by design. You can inspect the ZIP before + sending it. + +That information is used only to deliver the product, provide support and +meet legal obligations (taxes, accounting), and is kept only as long as +necessary for those purposes. + +## 3. Your rights + +Subject to applicable law (including Québec Law 25 and PIPEDA), you may +request access to, correction of, or deletion of the personal information the +Publisher holds about you, and you may withdraw consent where applicable. +Contact the privacy officer below. You may also file a complaint with the +Commission d'accès à l'information du Québec or the Office of the Privacy +Commissioner of Canada. + +## 4. Publisher contact + +- Legal name: [TO COMPLETE] +- Address: [TO COMPLETE] +- Person in charge of personal information protection: [TO COMPLETE] +- Email: [TO COMPLETE] diff --git a/legal/PRIVACY_FR.md b/legal/PRIVACY_FR.md new file mode 100644 index 0000000000000000000000000000000000000000..73f6004eb284cc0742c3f0e95bf225e6087b26c7 --- /dev/null +++ b/legal/PRIVACY_FR.md @@ -0,0 +1,61 @@ +# JackAILocal — Politique de confidentialité + +**Version 1.0 — Date d'entrée en vigueur : à fixer au premier lancement commercial** + +> NOTE À L'ÉDITEUR : compléter la section « Coordonnées de l'Éditeur » (nom +> légal, adresse, responsable de la protection des renseignements personnels) +> avant la première vente. La Loi 25 du Québec exige la désignation d'un +> responsable de la protection des renseignements personnels. + +## En bref + +JackAILocal fonctionne entièrement sur votre appareil. Vos conversations, +documents, images et enregistrements vocaux sont traités localement et ne +sont jamais transmis à l'Éditeur ni à des tiers par le Logiciel. + +## 1. Ce que le Logiciel traite localement + +- Les conversations, les documents, les images analysées par SCOUT et l'audio + vocal sont traités par des modèles d'IA exécutés sur votre propre matériel + et stockés uniquement dans le dossier de travail du Logiciel, sur votre + appareil ou votre support. +- Les packs d'exportation chiffrés (AES-256-GCM) ne sont créés qu'à votre + demande, avec un mot de passe que vous choisissez. +- L'Accès téléphone, optionnel, expose l'interface uniquement sur votre + réseau local; il est désactivé par défaut et exige un jeton d'appariement. + +## 2. Ce que l'Éditeur recueille + +Le Logiciel ne contient **aucune télémétrie, aucun outil d'analyse, aucun +compte utilisateur et aucune vérification automatique de mise à jour**. +L'Éditeur ne recueille des renseignements personnels qu'en dehors du +Logiciel, lorsque vous : + +- achetez un produit (nom, adresse de facturation, courriel — traités par la + boutique et le fournisseur de paiement indiqués dans vos conditions de + vente); +- demandez du soutien et choisissez d'envoyer un dossier de soutien. Les + dossiers de soutien contiennent l'état du matériel, l'état des moteurs et + les journaux; ils excluent par conception vos conversations et vos + documents. Vous pouvez inspecter le fichier ZIP avant de l'envoyer. + +Ces renseignements servent uniquement à livrer le produit, à fournir le +soutien et à respecter les obligations légales (taxes, comptabilité), et ne +sont conservés que le temps nécessaire à ces fins. + +## 3. Vos droits + +Sous réserve des lois applicables (notamment la Loi 25 du Québec et la +LPRPDE), vous pouvez demander l'accès aux renseignements personnels que +l'Éditeur détient à votre sujet, leur rectification ou leur suppression, et +retirer votre consentement lorsque cela s'applique. Communiquez avec le +responsable indiqué ci-dessous. Vous pouvez aussi déposer une plainte auprès +de la Commission d'accès à l'information du Québec ou du Commissariat à la +protection de la vie privée du Canada. + +## 4. Coordonnées de l'Éditeur + +- Nom légal : [À COMPLÉTER] +- Adresse : [À COMPLÉTER] +- Responsable de la protection des renseignements personnels : [À COMPLÉTER] +- Courriel : [À COMPLÉTER] diff --git a/licenses/APACHE-2.0.txt b/licenses/APACHE-2.0.txt new file mode 100644 index 0000000000000000000000000000000000000000..d645695673349e3947e8e5ae42332d0ac3164cd7 --- /dev/null +++ b/licenses/APACHE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/GEMMA_TERMS_OF_USE.txt b/licenses/GEMMA_TERMS_OF_USE.txt new file mode 100644 index 0000000000000000000000000000000000000000..378431e2f4598f4d27bf6d4a057790496b6d4634 --- /dev/null +++ b/licenses/GEMMA_TERMS_OF_USE.txt @@ -0,0 +1,260 @@ +The terms below apply to Gemma models listed in the Appendix at bottom of this page. For Gemma 4 terms, see the Gemma 4 license. + +Last modified: April 1, 2026 + +By using, reproducing, modifying, distributing, performing or displaying any +portion or element of Gemma, Model Derivatives including via any Hosted Service, +(each as defined below) (collectively, the "Gemma Services") or otherwise +accepting the terms of this Agreement, you agree to be bound by this Agreement. + +## Section 1: DEFINITIONS + +## 1.1 Definitions + +(a) "Agreement" or "Gemma Terms of Use" means these terms and conditions +that govern the use, reproduction, Distribution or modification of the Gemma +Services and any terms and conditions incorporated by reference. + +(b) "Distribution" or "Distribute" means any transmission, publication, +or other sharing of Gemma or Model Derivatives to a third party, including by +providing or making Gemma or its functionality available as a hosted service via +API, web access, or any other electronic or remote means ("Hosted Service"). + +(c) "Gemma" means the set of machine learning language models, trained model +weights and parameters identified in the Appendix, +regardless of the source that you obtained it from. + +(d) "Google" means Google LLC. + +(e) "Model Derivatives" means all (i) modifications to Gemma, (ii) works based +on Gemma, or (iii) any other machine learning model which is created by transfer +of patterns of the weights, parameters, operations, or Output of Gemma, to that +model in order to cause that model to perform similarly to Gemma, including +distillation methods that use intermediate data representations or methods based +on the generation of synthetic data Outputs by Gemma for training that model. +For clarity, Outputs are not deemed Model Derivatives. + +(f) "Output" means the information content output of Gemma or a Model +Derivative that results from operating or otherwise using Gemma or the Model +Derivative, including via a Hosted Service. + +## 1.2 + +As used in this Agreement, "including" means +"including without limitation". + +## Section 2: ELIGIBILITY AND USAGE + +## 2.1 Eligibility + +You represent and warrant that you have the legal capacity to enter into this +Agreement (including being of sufficient age of consent). If you are accessing +or using any of the Gemma Services for or on behalf of a legal entity, (a) you +are entering into this Agreement on behalf of yourself and that legal entity, +(b) you represent and warrant that you have the authority to act on behalf of +and bind that entity to this Agreement and (c) references to "you" or +"your" in the remainder of this Agreement refers to both you (as an +individual) and that entity. + +## 2.2 Use + +You may use, reproduce, modify, Distribute, perform or display any of the Gemma +Services only in accordance with the terms of this Agreement, and must not +violate (or encourage or permit anyone else to violate) any term of this +Agreement. + +## Section 3: DISTRIBUTION AND RESTRICTIONS + +## 3.1 Distribution and Redistribution + +You may reproduce or Distribute copies of Gemma or Model Derivatives if you meet +all of the following conditions: + +- You must include the use restrictions referenced in Section 3.2 as an +enforceable provision in any agreement (e.g., license agreement, terms of use, +etc.) governing the use and/or distribution of Gemma or Model Derivatives and +you must provide notice to subsequent users you Distribute to that Gemma or +Model Derivatives are subject to the use restrictions in Section 3.2. + +- You must provide all third party recipients of Gemma or Model Derivatives a +copy of this Agreement. + +- You must cause any modified files to carry prominent notices stating that you +modified the files. + +- All Distributions (other than through a Hosted Service) must be accompanied +by a "Notice" text file that contains the following notice: +"Gemma is provided under and subject to the Gemma Terms of Use found at ai.google.dev/gemma/terms". + +You may add your own intellectual property statement to your modifications and, +except as set forth in this Section, may provide additional or different terms +and conditions for use, reproduction, or Distribution of your modifications, or +for any such Model Derivatives as a whole, provided your use, reproduction, +modification, Distribution, performance, and display of Gemma otherwise complies +with the terms and conditions of this Agreement. Any additional or different +terms and conditions you impose must not conflict with the terms of this +Agreement. + +## 3.2 Use Restrictions + +You must not use any of the Gemma Services: + +- for the restricted uses set forth in the Gemma Prohibited Use Policy at +ai.google.dev/gemma/prohibited_use_policy +("Prohibited Use Policy"), which is hereby incorporated by reference into +this Agreement; or + +- in violation of applicable laws and regulations. + +To the maximum extent permitted by law, Google reserves the right to restrict +(remotely or otherwise) usage of any of the Gemma Services that Google +reasonably believes are in violation of this Agreement. + +## 3.3 Generated Output + +Google claims no rights in Outputs you generate using Gemma. You and your users +are solely responsible for Outputs and their subsequent uses. + +## Section 4: ADDITIONAL PROVISIONS + +## 4.1 Updates + +Google may update Gemma from time to time. + +## 4.2 Trademarks + +Nothing in this Agreement grants you any rights to use Google's trademarks, +trade names, logos or to otherwise suggest endorsement or misrepresent the +relationship between you and Google. Google reserves any rights not expressly +granted herein. + +## 4.3 DISCLAIMER OF WARRANTY + +UNLESS REQUIRED BY APPLICABLE LAW, THE GEMMA SERVICES, AND OUTPUTS, ARE PROVIDED +ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER +EXPRESS OR IMPLIED, INCLUDING ANY WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. YOU ARE +SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USING, REPRODUCING, +MODIFYING, PERFORMING, DISPLAYING OR DISTRIBUTING ANY OF THE GEMMA SERVICES +OR OUTPUTS AND ASSUME ANY AND ALL RISKS ASSOCIATED WITH YOUR USE OR DISTRIBUTION +OF ANY OF THE GEMMA SERVICES OR OUTPUTS AND YOUR EXERCISE OF RIGHTS AND +PERMISSIONS UNDER THIS AGREEMENT. + +## 4.4 LIMITATION OF LIABILITY + +TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO +LEGAL THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), PRODUCT LIABILITY, +CONTRACT, OR OTHERWISE, UNLESS REQUIRED BY APPLICABLE LAW, SHALL GOOGLE OR ITS +AFFILIATES BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, +SPECIAL, INCIDENTAL, EXEMPLARY, CONSEQUENTIAL, OR PUNITIVE DAMAGES, OR LOST +PROFITS OF ANY KIND ARISING FROM THIS AGREEMENT OR RELATED TO, ANY OF THE GEMMA +SERVICES OR OUTPUTS EVEN IF GOOGLE OR ITS AFFILIATES HAVE BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +## 4.5 Term, Termination, and Survival + +The term of this Agreement will commence upon your acceptance of this Agreement +(including acceptance by your use, modification, or Distribution, reproduction, +performance or display of any portion or element of the Gemma Services) and will +continue in full force and effect until terminated in accordance with the terms +of this Agreement. Google may terminate this Agreement if you are in breach of +any term of this Agreement. Upon termination of this Agreement, you must delete +and cease use and Distribution of all copies of Gemma and Model Derivatives in +your possession or control. Sections 1, 2.1, 3.3, 4.2 to 4.9 shall survive the +termination of this Agreement. + +## 4.6 Governing Law and Jurisdiction + +This Agreement will be governed by the laws of the State of California without +regard to choice of law principles. The UN Convention on Contracts for the +International Sale of Goods does not apply to this Agreement. The state and +federal courts of Santa Clara County, California shall have exclusive +jurisdiction of any dispute arising out of this Agreement. + +## 4.7 Severability + +If any provision of this Agreement is held to be invalid, illegal or +unenforceable, the remaining provisions shall be unaffected thereby and remain +valid as if such provision had not been set forth herein. + +## 4.8 Entire Agreement + +This Agreement states all the terms agreed between the parties and supersedes +all other agreements between the parties as of the date of acceptance relating +to its subject matter. + +## 4.9 No Waiver + +Google will not be treated as having waived any rights by not exercising (or +delaying the exercise of) any rights under this Agreement. + +## Appendix + +- Gemma 1 + +- Gemma 1.1 + +- Gemma 2 + +- Gemma 3 + +- Gemma 3n + +- FunctionGemma + +- EmbeddingGemma + +- PaliGemma + +- PaliGemma 2 + +- ShieldGemma + +- ShieldGemma 2 + +- CodeGemma + +- CodeGemma 1.1 + +- Gemma 2 JPN + +- DataGemma RIG + +- DataGemma RAG + +- RecurrentGemma + +- Gemma Scope + +- Gemma-APS + +- T5Gemma + +- VaultGemma + +- FunctionGemma + +- T5Gemma 2 + +- TranslateGemma + +Note: Previous versions of these Terms are archived here. +- + + + + + + + + + + + + + + + + + + Send feedback diff --git a/licenses/MIT_COMPONENTS.txt b/licenses/MIT_COMPONENTS.txt new file mode 100644 index 0000000000000000000000000000000000000000..c994e1337544b16dfe5914a4e62ae411a0bd4569 --- /dev/null +++ b/licenses/MIT_COMPONENTS.txt @@ -0,0 +1,25 @@ +MIT License — applies to the following bundled components: + + - Ollama Copyright (c) Ollama + - llama.cpp Copyright (c) 2023-2024 The ggml authors + - whisper.cpp Copyright (c) 2023-2024 The ggml authors + - Piper Copyright (c) Michael Hansen (Rhasspy) + - Whisper model weights Copyright (c) 2022 OpenAI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/NOTICE_GEMMA.txt b/licenses/NOTICE_GEMMA.txt new file mode 100644 index 0000000000000000000000000000000000000000..27fe1d325f98f3f4643f3ba5735584cffd2553dd --- /dev/null +++ b/licenses/NOTICE_GEMMA.txt @@ -0,0 +1 @@ +Gemma is provided under and subject to the Gemma Terms of Use found at ai.google.dev/gemma/terms diff --git a/licenses/THIRD_PARTY_NOTICES.md b/licenses/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000000000000000000000000000000000000..3ec3b9d71527851457aaf6dc0a0cc9ca0fdd025f --- /dev/null +++ b/licenses/THIRD_PARTY_NOTICES.md @@ -0,0 +1,106 @@ +# Third-Party Notices / Avis relatifs aux composants tiers + +JackAILocal packages include the third-party software and model weights listed +below. Each component remains governed by its own license. Copies of the +referenced licenses are distributed in this `licenses/` folder or are available +at the URLs indicated. This file must ship with every customer package. + +Les packages JackAILocal incluent les logiciels et poids de modèles tiers +listés ci-dessous. Chaque composant demeure régi par sa propre licence. Ce +fichier doit accompagner chaque package livré à un client. + +--- + +## Runtime backends + +### Ollama +- Role: local model runtime (`backends/ollama/`) +- License: MIT License — Copyright (c) Ollama +- Source: https://github.com/ollama/ollama + +### llama.cpp (optional backend) +- Role: OpenAI-compatible local inference server (`backends/llama.cpp/`) +- License: MIT License — Copyright (c) 2023-2024 The ggml authors +- Source: https://github.com/ggml-org/llama.cpp + +### whisper.cpp +- Role: offline speech-to-text (`backends/whisper.cpp/`) +- License: MIT License — Copyright (c) 2023-2024 The ggml authors +- Source: https://github.com/ggml-org/whisper.cpp + +### Piper +- Role: offline text-to-speech (`backends/piper/`) +- License: MIT License — Copyright (c) Michael Hansen (Rhasspy) +- Source: https://github.com/rhasspy/piper + +--- + +## Model weights + +> Model weights are licensed separately from the software. The licenses below +> apply to the weights preloaded by the package builder. If you (or a +> white-label partner) change the preloaded models, regenerate this section. + +### Qwen family (Alibaba Cloud) +- Models: as listed in `config/model-catalog.json` (chat, vision) +- License: Apache License 2.0 (verify the license file of each specific + release on its model card before shipping; some Qwen releases have used + different terms) +- Obligations: retain license text and copyright notice. +- Source: https://huggingface.co/Qwen + +### Gemma (Google) +- Models: Gemma-family config agent model (see `config/model-catalog.json`) +- License — **Gemma 4 and later**: Apache License 2.0 (`APACHE-2.0.txt`), with + Google's Prohibited Use Policy published alongside + (https://ai.google.dev/gemma/prohibited_use_policy). +- License — **Gemma 3 and earlier**: Gemma Terms of Use (NOT an open-source + license; copy included as `GEMMA_TERMS_OF_USE.txt`, last modified + 2026-04-01). Distribution must include the Terms and pass the use + restrictions of the Prohibited Use Policy to recipients; the required + notice text is included as `NOTICE_GEMMA.txt`. +- Source: https://ai.google.dev/gemma/terms , https://ai.google.dev/gemma/apache_2 + +### Whisper (OpenAI) +- Models: `models/whisper/ggml-*.bin` (converted Whisper weights) +- License: MIT License — Copyright (c) OpenAI +- Source: https://github.com/openai/whisper + +### Piper voices +- Models: `models/piper/en_US-libritts_r-medium.onnx` +- License: CC BY 4.0 (trained on LibriTTS-R; attribution required — this + notice satisfies it). Voice card: + https://huggingface.co/rhasspy/piper-voices/tree/main/en/en_US/libritts_r +- **WARNING — adding voices**: Piper voice licenses vary per voice. Voices + trained on Blizzard datasets (e.g. `en_US-lessac-*`) are NON-COMMERCIAL and + must never ship in a sold package. Safe choices: LibriTTS/LibriTTS-R + (CC BY 4.0) and LJSpeech-based voices (public domain). Check the + MODEL_CARD of every voice before adding it. +- Source: https://huggingface.co/rhasspy/piper-voices + +--- + +## Rust crates (jackailocald) + +The `jackailocald` binary statically links Rust crates that are dual-licensed +MIT OR Apache-2.0 unless noted otherwise (axum, tokio, serde, reqwest, chrono, +clap, aes-gcm, ed25519-dalek, and their dependencies). Generate the full, +authoritative list for a release with: + +``` +cargo install cargo-license +cargo license --json > licenses/rust-crates-licenses.json +``` + +--- + +## Web UI + +The Web UI (`webui/`) is original JackAILocal code and uses no bundled +third-party JavaScript frameworks. + +--- + +If a component listed here is removed from a package, its notice may be +removed. If a component is added, its notice MUST be added before the package +is delivered to a customer. diff --git a/licenses/THIRD_PARTY_NOTICE_TEMPLATE.md b/licenses/THIRD_PARTY_NOTICE_TEMPLATE.md new file mode 100644 index 0000000000000000000000000000000000000000..e39c074cd39cc16251da7bbc01b9139bbf71b18d --- /dev/null +++ b/licenses/THIRD_PARTY_NOTICE_TEMPLATE.md @@ -0,0 +1,15 @@ +# Third-party notices + +Add verified license notices for every shipped backend, model, UI dependency, and content pack before commercial distribution. + +Minimum fields: + +```text +Component +Version +Source URL +License +Included files +Modification status +Redistribution notes +``` diff --git a/linux/factory/Build-Bootable-Linux-USB.sh b/linux/factory/Build-Bootable-Linux-USB.sh new file mode 100644 index 0000000000000000000000000000000000000000..e143bca4b8e249ed94fe0b21da909dbca314d8f7 --- /dev/null +++ b/linux/factory/Build-Bootable-Linux-USB.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -euo pipefail +TARGET="" +CONFIRM="no" +while [[ $# -gt 0 ]]; do + case "$1" in + --target) TARGET="$2"; shift 2;; + --confirm-erase) CONFIRM="yes"; shift;; + *) echo "unknown arg $1"; exit 2;; + esac +done +if [[ -z "$TARGET" || "$CONFIRM" != "yes" ]]; then + echo "usage: sudo $0 --target /dev/sdX --confirm-erase"; exit 2 +fi +if [[ ! -b "$TARGET" ]]; then echo "not a block device: $TARGET"; exit 1; fi +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +exec "$SCRIPT_DIR/prepare-linux-boot-disk.sh" "$TARGET" --confirm-erase diff --git a/linux/factory/prepare-linux-boot-disk.sh b/linux/factory/prepare-linux-boot-disk.sh new file mode 100644 index 0000000000000000000000000000000000000000..91a877b1e076be95b1f78067760fbc33d6972f35 --- /dev/null +++ b/linux/factory/prepare-linux-boot-disk.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +set -euo pipefail +DEVICE="${1:-}" +shift || true +DIST="${DIST:-bookworm}" +MODEL="${JACKAILOCAL_MODEL:-qwen3.5:4b}" +CONFIRM="" +for arg in "$@"; do + case "$arg" in + --confirm-erase) CONFIRM="ERASE" ;; + *) echo "Unknown argument: $arg" >&2; exit 2 ;; + esac +done +if [[ -z "$DEVICE" ]]; then + echo "Usage: sudo $0 /dev/sdX [--confirm-erase]" + exit 1 +fi +if [[ $EUID -ne 0 ]]; then echo "Run this script as root." >&2; exit 1; fi +if [[ ! -b "$DEVICE" ]]; then echo "$DEVICE is not a block device"; exit 1; fi +if [[ "$CONFIRM" != "ERASE" ]]; then + read -r -p "This will ERASE $DEVICE. Type ERASE to continue: " CONFIRM +fi +[[ "$CONFIRM" == "ERASE" ]] || exit 1 + +ROOT_SRC="$(cd "$(dirname "$0")/../.." && pwd)" +if findmnt -rn -S "${DEVICE}*" >/dev/null 2>&1; then + echo "A partition on $DEVICE is mounted. Unmount it before continuing." >&2 + exit 1 +fi + +"$ROOT_SRC/factory/linux/build-linux-binary.sh" +"$ROOT_SRC/factory/linux/install-linux-backends.sh" +"$ROOT_SRC/factory/unix/build-whisper-backend.sh" +"$ROOT_SRC/unix/Install-Voice.sh" "$ROOT_SRC" +[ -x "$ROOT_SRC/bin/jackailocald" ] || { echo "Missing staged Linux runtime." >&2; exit 1; } +[ -x "$ROOT_SRC/backends/ollama/linux/ollama" ] || { echo "Missing staged Linux Ollama backend." >&2; exit 1; } + +mkdir -p "$ROOT_SRC/models/ollama" "$ROOT_SRC/.jackailocal-builder/logs" +export OLLAMA_MODELS="$ROOT_SRC/models/ollama" +export OLLAMA_HOST="127.0.0.1:11435" +"$ROOT_SRC/backends/ollama/linux/ollama" serve > "$ROOT_SRC/.jackailocal-builder/logs/ollama-linux-builder.log" 2>&1 & +OLLAMA_PID=$! +cleanup_ollama() { kill "$OLLAMA_PID" >/dev/null 2>&1 || true; } +trap cleanup_ollama EXIT +for _ in $(seq 1 60); do + "$ROOT_SRC/backends/ollama/linux/ollama" list >/dev/null 2>&1 && break + sleep 0.5 +done +"$ROOT_SRC/backends/ollama/linux/ollama" pull "$MODEL" +cleanup_ollama +trap - EXIT + +apt-get update +apt-get install -y parted dosfstools e2fsprogs debootstrap grub-efi-amd64-bin rsync +parted -s "$DEVICE" mklabel gpt +parted -s "$DEVICE" mkpart ESP fat32 1MiB 513MiB +parted -s "$DEVICE" set 1 esp on +parted -s "$DEVICE" mkpart root ext4 513MiB 100% +partprobe "$DEVICE" +udevadm settle +EFI="${DEVICE}1"; ROOT="${DEVICE}2" +if [[ "$DEVICE" =~ nvme|mmcblk ]]; then EFI="${DEVICE}p1"; ROOT="${DEVICE}p2"; fi +mkfs.vfat -F32 -n JACKAI_EFI "$EFI" +mkfs.ext4 -F -L JACKAI_ROOT "$ROOT" +MOUNT_ROOT=/mnt/jackailocal-root +mkdir -p "$MOUNT_ROOT" +mount "$ROOT" "$MOUNT_ROOT" +cleanup_mounts() { umount -R "$MOUNT_ROOT" >/dev/null 2>&1 || true; } +trap cleanup_mounts EXIT +mkdir -p "$MOUNT_ROOT/boot/efi" +mount "$EFI" "$MOUNT_ROOT/boot/efi" +debootstrap --arch amd64 "$DIST" "$MOUNT_ROOT" http://deb.debian.org/debian +cat > "$MOUNT_ROOT/etc/fstab" <&2; exit 1; } +[ -x "$ROOT/backends/ollama/linux/ollama" ] || { echo "Missing $ROOT/backends/ollama/linux/ollama" >&2; exit 1; } +[ -x "$ROOT/backends/whisper.cpp/linux/whisper-cli" ] || { echo "Missing $ROOT/backends/whisper.cpp/linux/whisper-cli" >&2; exit 1; } +[ -x "$ROOT/backends/piper/linux/piper" ] || { echo "Missing $ROOT/backends/piper/linux/piper" >&2; exit 1; } +install -d /var/log/jackailocal /var/lib/jackailocal "$ROOT/.jackailocal/cache" "$ROOT/.jackailocal/logs" "$ROOT/workspace/documents" "$ROOT/workspace/threads" "$ROOT/workspace/exports" "$ROOT/diagnostics" +chmod +x "$ROOT/tools/pm-hwscan.sh" 2>/dev/null || true +cp "$ROOT/linux/systemd/jackailocald.service" /etc/systemd/system/jackailocald.service +cp "$ROOT/linux/systemd/jackailocal-hwscan.service" /etc/systemd/system/jackailocal-hwscan.service +cp "$ROOT/linux/systemd/jackailocal-kiosk.service" /etc/systemd/system/jackailocal-kiosk.service +cp "$ROOT/linux/systemd/ollama-jackailocal.service" /etc/systemd/system/ollama-jackailocal.service +systemctl daemon-reload +systemctl enable jackailocal-hwscan.service ollama-jackailocal.service jackailocald.service jackailocal-kiosk.service +ln -sf /opt/jackailocal/bin/jackailocald /usr/local/bin/jackailocald +echo "JackAILocal Linux runtime installed." diff --git a/linux/systemd/jackailocal-hwscan.service b/linux/systemd/jackailocal-hwscan.service new file mode 100644 index 0000000000000000000000000000000000000000..2fdd0e7a4feb83beab19987ae10bd649ee4ebc96 --- /dev/null +++ b/linux/systemd/jackailocal-hwscan.service @@ -0,0 +1,11 @@ +[Unit] +Description=JackAILocal hardware scan +Before=jackailocald.service + +[Service] +Type=oneshot +WorkingDirectory=/opt/jackailocal +ExecStart=/bin/sh -c '/opt/jackailocal/bin/jackailocald hwscan > /opt/jackailocal/diagnostics/hardware.json' + +[Install] +WantedBy=multi-user.target diff --git a/linux/systemd/jackailocal-kiosk.service b/linux/systemd/jackailocal-kiosk.service new file mode 100644 index 0000000000000000000000000000000000000000..b3a3dbde2911674123dba3522dc4a475f7497933 --- /dev/null +++ b/linux/systemd/jackailocal-kiosk.service @@ -0,0 +1,17 @@ +[Unit] +Description=JackAILocal kiosk browser +After=jackailocald.service network.target +Requires=jackailocald.service + +[Service] +Type=simple +User=jackai +Environment=HOME=/home/jackai +Environment=DISPLAY=:0 +TTYPath=/dev/tty1 +StandardInput=tty-force +ExecStart=/usr/bin/xinit /usr/bin/chromium --kiosk --no-first-run --disable-restore-session-state http://127.0.0.1:4891 -- :0 vt1 -keeptty +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/linux/systemd/jackailocald.service b/linux/systemd/jackailocald.service new file mode 100644 index 0000000000000000000000000000000000000000..43c121e990f1fce5cd4efed4d1591c29dab75f43 --- /dev/null +++ b/linux/systemd/jackailocald.service @@ -0,0 +1,17 @@ +[Unit] +Description=JackAILocal local router +After=network.target ollama-jackailocal.service + +[Service] +Type=simple +WorkingDirectory=/opt/jackailocal +ExecStart=/opt/jackailocal/bin/jackailocald serve --config config/jackailocal.linux.toml +Restart=on-failure +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=full +ProtectHome=true +ReadWritePaths=/opt/jackailocal/workspace /opt/jackailocal/.jackailocal /opt/jackailocal/diagnostics + +[Install] +WantedBy=multi-user.target diff --git a/linux/systemd/ollama-jackailocal.service b/linux/systemd/ollama-jackailocal.service new file mode 100644 index 0000000000000000000000000000000000000000..7f77e56df783f399fe07e7a263cf61f0e6b51fcd --- /dev/null +++ b/linux/systemd/ollama-jackailocal.service @@ -0,0 +1,20 @@ +[Unit] +Description=Ollama for JackAILocal +After=network.target + +[Service] +Type=simple +Environment=OLLAMA_HOST=127.0.0.1:11434 +Environment=OLLAMA_MODELS=/opt/jackailocal/models/ollama +Environment=OLLAMA_NO_CLOUD=true +WorkingDirectory=/opt/jackailocal +ExecStart=/opt/jackailocal/backends/ollama/linux/ollama serve +Restart=on-failure +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=full +ProtectHome=true +ReadWritePaths=/opt/jackailocal/models/ollama /opt/jackailocal/.jackailocal + +[Install] +WantedBy=multi-user.target diff --git a/macos/JackAILocal-USB-Builder.sh b/macos/JackAILocal-USB-Builder.sh new file mode 100644 index 0000000000000000000000000000000000000000..5a0a49cab569816176a172a777050631181533c7 --- /dev/null +++ b/macos/JackAILocal-USB-Builder.sh @@ -0,0 +1,335 @@ +#!/usr/bin/env bash +set -euo pipefail + +# JackAILocal macOS Builder +# This script prepares a local folder, USB drive, or SSD without requiring manual model handling. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +SKIP_MODEL_DOWNLOAD="false" +TARGET_VOLUME="" +TARGET_MODE="usb" + +for arg in "$@"; do + case "$arg" in + --skip-model-download) SKIP_MODEL_DOWNLOAD="true" ;; + --target=*) TARGET_VOLUME="${arg#--target=}" ;; + --mode=usb|--mode=ssd|--mode=local) TARGET_MODE="${arg#--mode=}" ;; + *) echo "Unknown argument: $arg" >&2; exit 2 ;; + esac +done + +print_title() { + printf '\n============================================================\n' + printf '%s\n' "$1" + printf '============================================================\n' +} + +ensure_dir() { + mkdir -p "$1" +} + +find_manifest() { + local candidates + candidates=$(find "$ROOT_DIR" -type f \( -name '*build-manifest*.json' -o -name '*build-manifest*.zip' -o -name 'jackailocal-build-manifest*.zip' \) 2>/dev/null | sort -r || true) + if [ -z "$candidates" ]; then + echo "" + return 0 + fi + echo "$candidates" | head -n 1 +} + +extract_manifest_json() { + local manifest_file="$1" + local output_dir="$ROOT_DIR/.jackailocal-builder/manifest" + rm -rf "$output_dir" + mkdir -p "$output_dir" + + if [ -z "$manifest_file" ]; then + cat > "$output_dir/build-manifest.json" <<'JSON' +{ + "product": "JackAILocal", + "package_goal": "Standard offline assistant", + "build_id": "macos-default", + "hardware": { "os": "macos", "ram_gb": 16, "vram_gb": 0, "gpu_name": "Apple/Intel Mac", "usb_storage_gb": 128 }, + "default_model": { "id": "qwen35_smart_9b", "provider": "ollama", "model_ref": "qwen3.5:9b", "params_b": 9 }, + "allowed_models": [ + { "id": "qwen35_fast_2b", "provider": "ollama", "model_ref": "qwen3.5:2b", "params_b": 2 }, + { "id": "qwen35_balanced_4b", "provider": "ollama", "model_ref": "qwen3.5:4b", "params_b": 4 }, + { "id": "qwen35_smart_9b", "provider": "ollama", "model_ref": "qwen3.5:9b", "params_b": 9 } + ] +} +JSON + echo "$output_dir/build-manifest.json" + return 0 + fi + + case "$manifest_file" in + *.zip) + unzip -q "$manifest_file" -d "$output_dir" + find "$output_dir" -type f -name '*.json' | grep -E 'manifest|build-manifest' | head -n 1 + ;; + *.json) + cp "$manifest_file" "$output_dir/build-manifest.json" + echo "$output_dir/build-manifest.json" + ;; + *) + echo "Unsupported manifest file: $manifest_file" >&2 + exit 1 + ;; + esac +} + +select_target_volume() { + if [ -n "$TARGET_VOLUME" ]; then + if [ "$TARGET_MODE" = "local" ]; then + mkdir -p "$TARGET_VOLUME" + elif [ ! -d "$TARGET_VOLUME" ]; then + echo "Target volume does not exist: $TARGET_VOLUME" >&2 + exit 1 + fi + echo "$TARGET_VOLUME" + return 0 + fi + + if [ "$TARGET_MODE" = "local" ]; then + mkdir -p "$HOME/JackAILocal" + echo "$HOME/JackAILocal" + return 0 + fi + + local volumes=() + while IFS= read -r volume; do + case "$volume" in + /Volumes/Macintosh\ HD|/Volumes/com.apple.TimeMachine.localsnapshots) continue ;; + esac + [ -d "$volume" ] && volumes+=("$volume") + done < <(find /Volumes -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sort) + + if [ "${#volumes[@]}" -eq 0 ]; then + echo "No external volume was found. Insert the USB/SSD, then run BUILD-USB-MAC.command again." >&2 + exit 1 + fi + + echo "Detected external volumes:" >&2 + local i=1 + for volume in "${volumes[@]}"; do + local size + size=$(df -h "$volume" | awk 'NR==2 {print $2 " total, " $4 " free"}') + echo "[$i] $volume ($size)" >&2 + i=$((i+1)) + done + + local selection + if [ "${#volumes[@]}" -eq 1 ]; then + read -r -p "Use ${volumes[0]} as the JackAILocal target? Type YES: " selection + [ "$selection" = "YES" ] || { echo "Cancelled." >&2; exit 1; } + echo "${volumes[0]}" + return 0 + fi + + read -r -p "Type the number of the target volume: " selection + if ! [[ "$selection" =~ ^[0-9]+$ ]] || [ "$selection" -lt 1 ] || [ "$selection" -gt "${#volumes[@]}" ]; then + echo "Invalid selection." >&2 + exit 1 + fi + local target="${volumes[$((selection-1))]}" + read -r -p "Confirm target $target. Type BUILD: " confirm + [ "$confirm" = "BUILD" ] || { echo "Cancelled." >&2; exit 1; } + echo "$target" +} + +copy_runtime_payload() { + local target="$1" + local items=( + "README-FIRST.txt" "START-HERE.cmd" "START-DESKTOP.cmd" "UPDATE-FROM-USB.cmd" "START-HERE.command" "STOP-JACKAILOCAL.cmd" "STOP-JACKAILOCAL.command" + "webui" "windows" "macos" "unix" "profiler" "config" "manifest" "models" "backends" "bin" "docs" "updates" "content" "content-packs" "licenses" "legal" + ) + for item in "${items[@]}"; do + if [ -e "$ROOT_DIR/$item" ]; then + rm -rf "$target/$item" + cp -R "$ROOT_DIR/$item" "$target/$item" + fi + done + ensure_dir "$target/models/ollama" + ensure_dir "$target/models/gguf" + ensure_dir "$target/models/hf" + ensure_dir "$target/workspace/documents" + ensure_dir "$target/workspace/threads" + ensure_dir "$target/workspace/exports" + ensure_dir "$target/.jackailocal/logs" + rm -f "$target/config/update-private-key.xml" + chmod +x "$target"/*.command 2>/dev/null || true + chmod +x "$target/macos"/*.sh 2>/dev/null || true +} + +models_to_install() { + local manifest_json="$1" + if command -v python3 >/dev/null 2>&1; then + python3 "$SCRIPT_DIR/read_manifest_models.py" "$manifest_json" + else + echo "qwen3.5:9b" + fi +} + +find_ollama() { + local target="$1" + if [ -x "$target/backends/ollama/macos/ollama" ]; then + echo "$target/backends/ollama/macos/ollama" + return 0 + fi + if command -v ollama >/dev/null 2>&1; then + command -v ollama + return 0 + fi + echo "" +} + +start_ollama_server() { + local ollama_bin="$1" + local model_dir="$2" + export OLLAMA_HOST="127.0.0.1:11437" + export OLLAMA_NO_CLOUD=true + export OLLAMA_MODELS="$model_dir" + if curl -fsS http://127.0.0.1:11437/api/tags >/dev/null 2>&1; then + echo "Builder Ollama port 11437 is already in use. Stop that process before running the builder." >&2 + return 1 + fi + nohup "$ollama_bin" serve > "$model_dir/../../.jackailocal/logs/ollama.log" 2>&1 & + OLLAMA_BUILDER_PID=$! + for _ in $(seq 1 40); do + sleep 0.5 + if curl -fsS http://127.0.0.1:11437/api/tags >/dev/null 2>&1; then + return 0 + fi + done + echo "Ollama did not become ready." >&2 + return 1 +} + +pull_models() { + local target="$1" + local manifest_json="$2" + local model_dir="$target/models/ollama" + ensure_dir "$model_dir" + models_to_install "$manifest_json" > "$target/manifest/models-to-install.txt" + + if [ "$SKIP_MODEL_DOWNLOAD" = "true" ]; then + echo "Model download skipped. Planned models written to manifest/models-to-install.txt" + return 0 + fi + + local ollama_bin + ollama_bin=$(find_ollama "$target") + if [ -z "$ollama_bin" ]; then + echo "Ollama was not found. Install Ollama on this Mac or include backends/ollama/macos/ollama in the builder package." >&2 + return 1 + fi + if [ "$ollama_bin" != "$target/backends/ollama/macos/ollama" ]; then + ensure_dir "$target/backends/ollama/macos" + cp "$ollama_bin" "$target/backends/ollama/macos/ollama" + chmod +x "$target/backends/ollama/macos/ollama" + ollama_bin="$target/backends/ollama/macos/ollama" + fi + + start_ollama_server "$ollama_bin" "$model_dir" + trap '[ -n "${OLLAMA_BUILDER_PID:-}" ] && kill "$OLLAMA_BUILDER_PID" >/dev/null 2>&1 || true' RETURN + while IFS= read -r model; do + [ -z "$model" ] && continue + echo "Downloading model to USB: $model" + OLLAMA_MODELS="$model_dir" OLLAMA_HOST="127.0.0.1:11437" "$ollama_bin" pull "$model" + done < "$target/manifest/models-to-install.txt" + [ -n "${OLLAMA_BUILDER_PID:-}" ] && kill "$OLLAMA_BUILDER_PID" >/dev/null 2>&1 || true + OLLAMA_BUILDER_PID="" + trap - RETURN +} + +write_hash_manifest() { + local target="$1" + local output="$target/manifest/sha256-manifest.json" + ensure_dir "$target/manifest" + (cd "$target" && find . -type f ! -path './manifest/sha256-manifest.json' ! -path './.jackailocal/*' ! -path './workspace/*' ! -path './diagnostics/*' -print0 | sort -z | xargs -0 shasum -a 256) \ + | python3 -c 'import json,sys; rows=[] +for line in sys.stdin: + h,p=line.strip().split(None,1); rows.append({"path":p[2:] if p.startswith("./") else p,"sha256":h}) +print(json.dumps(rows,indent=2))' > "$output" 2>/dev/null || true +} + +assert_target_ready() { + local target="$1" + local required=( + "bin/jackailocald" + "backends/ollama/macos/ollama" + "backends/whisper.cpp/macos/whisper-cli" + "backends/piper/macos/piper" + "models/whisper/ggml-base.bin" + "models/piper/en_US-libritts_r-medium.onnx" + "models/piper/en_US-libritts_r-medium.onnx.json" + "content/field-manual/cards.json" + "content-packs/prompts/general-assistant.json" + "webui/index.html" + "webui/app-v15.js" + "config/jackailocal.macos.toml" + ) + for relative in "${required[@]}"; do + [ -f "$target/$relative" ] || { echo "Target validation failed. Required file is missing: $target/$relative" >&2; exit 1; } + done + find "$target/models/ollama" -type f ! -name '*.txt' -print -quit | grep -q . || { + echo "Target validation failed. No real Ollama model files were found under models/ollama." >&2 + exit 1 + } + if [ -f "$target/manifest/models-to-install.txt" ]; then + while IFS= read -r model; do + [ -z "$model" ] && continue + name="${model%%:*}" + tag="${model#*:}" + [ "$tag" = "$model" ] && tag="latest" + IFS='/' read -r first second extra <<< "$name" + if [ -n "${extra:-}" ]; then + echo "Target validation failed. Unsupported Ollama model reference in plan: $model" >&2 + exit 1 + fi + if [ -n "${second:-}" ]; then + manifest_path="$target/models/ollama/manifests/registry.ollama.ai/$first/$second/$tag" + else + manifest_path="$target/models/ollama/manifests/registry.ollama.ai/library/$first/$tag" + fi + [ -f "$manifest_path" ] || { + echo "Target validation failed. Planned Ollama model is missing from target store: $model" >&2 + exit 1 + } + done < "$target/manifest/models-to-install.txt" + fi +} + +print_title "JackAILocal macOS Builder" +echo "This builder uses the manifest embedded in the downloaded package." +echo "The user does not need to extract or select the manifest manually." + +command -v python3 >/dev/null 2>&1 || { echo "python3 is required by the macOS builder." >&2; exit 1; } +[ -x "$ROOT_DIR/bin/jackailocald" ] || { echo "Required runtime binary is missing: $ROOT_DIR/bin/jackailocald" >&2; exit 1; } + +manifest_file=$(find_manifest) +manifest_json=$(extract_manifest_json "$manifest_file") +target=$(select_target_volume) + +echo "" +echo "Target mode: $TARGET_MODE" +echo "Target volume: $target" +echo "Manifest: $manifest_json" +echo "Skip model download: $SKIP_MODEL_DOWNLOAD" +read -r -p "Type YES to build this JackAILocal target: " confirm +[ "$confirm" = "YES" ] || { echo "Cancelled."; exit 1; } + +copy_runtime_payload "$target" +ensure_dir "$target/manifest" +cp "$manifest_json" "$target/manifest/build-manifest.json" +pull_models "$target" "$target/manifest/build-manifest.json" +chmod +x "$target/unix/Install-Voice.sh" +"$target/unix/Install-Voice.sh" "$target" +assert_target_ready "$target" +write_hash_manifest "$target" + +print_title "Build complete" +echo "The target is ready: $target" +echo "Client launch file: $target/START-HERE.command" diff --git a/macos/Preflight-macOS.sh b/macos/Preflight-macOS.sh new file mode 100644 index 0000000000000000000000000000000000000000..4ffad011199fa69bc01c30fe74392987577f2633 --- /dev/null +++ b/macos/Preflight-macOS.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +echo "JackAILocal macOS preflight" +echo "Root: $ROOT_DIR" +[ -d "$ROOT_DIR/webui" ] || { echo "Missing webui"; exit 1; } +[ -d "$ROOT_DIR/config" ] || { echo "Missing config"; exit 1; } +[ -d "$ROOT_DIR/models" ] || { echo "Missing models"; exit 1; } +[ -x "$ROOT_DIR/bin/jackailocald" ] || { echo "Missing executable bin/jackailocald"; exit 1; } +[ -x "$ROOT_DIR/backends/ollama/macos/ollama" ] || command -v ollama >/dev/null 2>&1 || { echo "Missing Ollama backend"; exit 1; } +echo "Preflight passed." diff --git a/macos/Start-JackAILocal.sh b/macos/Start-JackAILocal.sh new file mode 100644 index 0000000000000000000000000000000000000000..49683f223b41d8d8c4b96351d15084c77f172028 --- /dev/null +++ b/macos/Start-JackAILocal.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +LOG_DIR="$ROOT_DIR/.jackailocal/logs" +mkdir -p "$LOG_DIR" +export JACKAILOCAL_ROOT="$ROOT_DIR" +export OLLAMA_HOST="127.0.0.1:11436" +export OLLAMA_MODELS="$ROOT_DIR/models/ollama" +export OLLAMA_NO_CLOUD=true + +find_ollama() { + if [ -x "$ROOT_DIR/backends/ollama/macos/ollama" ]; then + echo "$ROOT_DIR/backends/ollama/macos/ollama" + return 0 + fi + if command -v ollama >/dev/null 2>&1; then + command -v ollama + return 0 + fi + echo "" +} + +OLLAMA_BIN=$(find_ollama) +if [ -z "$OLLAMA_BIN" ]; then + echo "Ollama was not found. Rebuild the target with the real macOS backend." >&2 + exit 1 +fi +if ! curl -fsS http://127.0.0.1:11436/api/tags >/dev/null 2>&1; then + nohup "$OLLAMA_BIN" serve > "$LOG_DIR/ollama.log" 2>&1 & + sleep 2 +fi + +if [ ! -x "$ROOT_DIR/bin/jackailocald" ]; then + echo "bin/jackailocald is missing. Rebuild the target with the real macOS runtime." >&2 + exit 1 +fi +if ! curl -fsS http://127.0.0.1:4891/api/status >/dev/null 2>&1; then + nohup "$ROOT_DIR/bin/jackailocald" serve --config "$ROOT_DIR/config/jackailocal.macos.toml" > "$LOG_DIR/jackailocald.log" 2>&1 & + sleep 1 +fi + +URL="http://127.0.0.1:4891" +curl -fsS "$URL/api/status" >/dev/null 2>&1 || { echo "JackAILocal runtime did not become ready. See $LOG_DIR/jackailocald.log" >&2; exit 1; } +open "$URL" diff --git a/macos/Stop-JackAILocal.sh b/macos/Stop-JackAILocal.sh new file mode 100644 index 0000000000000000000000000000000000000000..26c706e3d844462354786ad9d3c4889d3f5be752 --- /dev/null +++ b/macos/Stop-JackAILocal.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail +pkill -f jackailocald || true +pkill -f 'ollama serve' || true +echo "JackAILocal local processes stopped." diff --git a/macos/hwscan-macos.sh b/macos/hwscan-macos.sh new file mode 100644 index 0000000000000000000000000000000000000000..448005fbb0a5a572f202114bd65efd901b3bee4b --- /dev/null +++ b/macos/hwscan-macos.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +OUT="$ROOT_DIR/diagnostics/hardware-macos.json" +mkdir -p "$(dirname "$OUT")" +RAM_GB=$(($(sysctl -n hw.memsize) / 1024 / 1024 / 1024)) +CPU_THREADS=$(sysctl -n hw.logicalcpu 2>/dev/null || echo 4) +CHIP=$(sysctl -n machdep.cpu.brand_string 2>/dev/null || sysctl -n hw.model 2>/dev/null || echo "Mac") +cat > "$OUT" < None: + value = str(ref or "").strip() + if value and value not in models: + models.append(value) + +def add_model(candidate: dict | None) -> None: + if not isinstance(candidate, dict): + return + if candidate.get("provider") != "ollama": + return + ref = str(candidate.get("model_ref", "")).strip() + add_ref(ref) + +backend_plan = manifest.get("backend_plan", {}) +install_plan = backend_plan.get("model_install_plan", {}) if isinstance(backend_plan, dict) else {} +for ref in install_plan.get("ollama", []) if isinstance(install_plan, dict) else []: + add_ref(ref) + +add_model(manifest.get("default_model")) +add_model(manifest.get("agent_model")) +for candidate in manifest.get("allowed_models", []): + try: + params = float(candidate.get("params_b", 0)) + except (TypeError, ValueError): + params = 0 + if params <= 9: + add_model(candidate) +if not models: + models.append("qwen3.5:9b") + +for model in models: + print(model) diff --git a/manifest-input/build-manifest.json b/manifest-input/build-manifest.json new file mode 100644 index 0000000000000000000000000000000000000000..033e966ac9fa5ee78c0dfbe3653849eb0f1a235b --- /dev/null +++ b/manifest-input/build-manifest.json @@ -0,0 +1,447 @@ +{ + "product": "JackAILocal", + "mode": "SaaS Builder & Local Runtime Console", + "selector_version": "v15-real-builder", + "build_id": "1f48822b-4a34-401a-8fa3-3445a83c70a9", + "created_at": "2026-06-10T00:45:30.966408+00:00", + "package_goal": "Power workstation", + "hardware": { + "os": "windows", + "cpu_threads": 24, + "ram_gb": 31.8, + "vram_gb": 24.0, + "gpu_name": "Unknown", + "disk": "Unknown", + "usb_storage_gb": 0, + "whichllm": null + }, + "default_model": { + "id": "qwen35_smart_9b", + "label_en": "Smart Default 9B", + "label_fr": "Défaut intelligent 9B", + "provider": "ollama", + "model_ref": "qwen3.5:9b", + "hf": "", + "params_b": 9, + "min_ram_gb": 16, + "min_vram_gb": 0, + "task": "chat", + "tier": "standard", + "features": [ + "chat", + "vision", + "thinking" + ], + "notes_en": "Practical offline baseline for normal laptops. This is the default ceiling for the standard pack, not the product ceiling.", + "notes_fr": "Base offline pratique pour portable normal. C’est la limite du pack standard, pas la limite du produit." + }, + "agent_model": { + "id": "gemma_config_agent_12b", + "label_en": "Gemma 4 12B Config Agent", + "label_fr": "Agent config Gemma 4 12B", + "provider": "ollama", + "model_ref": "gemma4:12b", + "hf": "google/gemma-4-12B-it", + "params_b": 11.95, + "min_ram_gb": 24, + "min_vram_gb": 0, + "task": "chat", + "tier": "agent", + "features": [ + "chat", + "vision", + "thinking", + "audio", + "tools", + "agent", + "config_agent", + "model_selection" + ], + "notes_en": "Preferred real local LLM-in-the-loop for hardware analysis, model selection, and safe client configuration review.", + "notes_fr": "LLM local prefere pour l'analyse materielle, la selection des modeles et la revue de configuration client." + }, + "llm_in_the_loop": { + "enabled": true, + "preferred_model_ref": "gemma4:12b", + "selected_agent_model_ref": "gemma4:12b", + "selected_agent_model_id": "gemma_config_agent_12b", + "runtime_endpoint": "/api/agent/recommend", + "strict_local_runtime": true, + "max_params_b": 32, + "fallback_behavior": "Fail explicitly if the requested local agent model is not installed. Use Auto only when the operator allows another installed local model." + }, + "allowed_models": [ + { + "id": "qwen35_fast_2b", + "label_en": "Fast 2B", + "label_fr": "Rapide 2B", + "provider": "ollama", + "model_ref": "qwen3.5:2b", + "hf": "", + "params_b": 2, + "min_ram_gb": 6, + "min_vram_gb": 0, + "task": "chat", + "tier": "standard", + "features": [ + "chat", + "vision", + "low_power" + ], + "notes_en": "Small CPU-friendly default for weak laptops.", + "notes_fr": "Petit modèle adapté aux portables faibles et au CPU." + }, + { + "id": "qwen35_balanced_4b", + "label_en": "Balanced 4B", + "label_fr": "Équilibré 4B", + "provider": "ollama", + "model_ref": "qwen3.5:4b", + "hf": "", + "params_b": 4, + "min_ram_gb": 8, + "min_vram_gb": 0, + "task": "chat", + "tier": "standard", + "features": [ + "chat", + "vision", + "voice" + ], + "notes_en": "Good default for standard laptops.", + "notes_fr": "Bon défaut pour les portables standards." + }, + { + "id": "qwen35_smart_9b", + "label_en": "Smart Default 9B", + "label_fr": "Défaut intelligent 9B", + "provider": "ollama", + "model_ref": "qwen3.5:9b", + "hf": "", + "params_b": 9, + "min_ram_gb": 16, + "min_vram_gb": 0, + "task": "chat", + "tier": "standard", + "features": [ + "chat", + "vision", + "thinking" + ], + "notes_en": "Practical offline baseline for normal laptops. This is the default ceiling for the standard pack, not the product ceiling.", + "notes_fr": "Base offline pratique pour portable normal. C’est la limite du pack standard, pas la limite du produit." + }, + { + "id": "gemma_config_agent_12b", + "label_en": "Gemma 4 12B Config Agent", + "label_fr": "Agent config Gemma 4 12B", + "provider": "ollama", + "model_ref": "gemma4:12b", + "hf": "google/gemma-4-12B-it", + "params_b": 11.95, + "min_ram_gb": 24, + "min_vram_gb": 0, + "task": "chat", + "tier": "agent", + "features": [ + "chat", + "vision", + "thinking", + "audio", + "tools", + "agent", + "config_agent", + "model_selection" + ], + "notes_en": "Preferred real local LLM-in-the-loop for hardware analysis, model selection, and safe client configuration review.", + "notes_fr": "LLM local prefere pour l'analyse materielle, la selection des modeles et la revue de configuration client." + }, + { + "id": "qwen_vl_8b", + "label_en": "Vision 8B", + "label_fr": "Vision 8B", + "provider": "ollama", + "model_ref": "qwen3-vl:8b", + "hf": "", + "params_b": 8, + "min_ram_gb": 16, + "min_vram_gb": 6, + "task": "vision", + "tier": "standard", + "features": [ + "vision", + "document", + "image" + ], + "notes_en": "Standard vision/document profile.", + "notes_fr": "Profil standard vision/document." + } + ], + "content_packs": [ + { + "id": "starter_prompt_packs", + "label_en": "Starter Prompt Packs", + "label_fr": "Packs de prompts de départ", + "recommended": true, + "min_storage_gb": 32 + } + ], + "backends": [ + "ollama", + "llama.cpp" + ], + "backend_plan": { + "chat": { + "selected": { + "backend": "ollama", + "model_ref": "qwen3.5:9b", + "install": { + "type": "ollama", + "ref": "qwen3.5:9b" + }, + "runtime": "local" + }, + "alternatives": [ + { + "backend": "ollama", + "model_ref": "qwen3.5:9b", + "install": { + "type": "ollama", + "ref": "qwen3.5:9b" + }, + "runtime": "local" + }, + { + "backend": "llama.cpp", + "model_path": "models/gguf/qwen3.5-9b-q4_k_m.gguf", + "install": { + "type": "gguf", + "path": "models/gguf/qwen3.5-9b-q4_k_m.gguf", + "source": "factory_or_builder" + }, + "runtime": "local" + } + ] + }, + "planning_agent": null, + "runtime_config_agent": { + "selected": { + "backend": "ollama", + "model_ref": "gemma4:12b", + "install": { + "type": "ollama", + "ref": "gemma4:12b" + }, + "runtime": "local" + }, + "alternatives": [ + { + "backend": "ollama", + "model_ref": "gemma4:12b", + "install": { + "type": "ollama", + "ref": "gemma4:12b" + }, + "runtime": "local" + }, + { + "backend": "vllm", + "hf_repo": "google/gemma-4-12B-it", + "install": { + "type": "hf", + "repo": "google/gemma-4-12B-it", + "source": "modal_or_server" + }, + "runtime": "server" + } + ] + }, + "vision": { + "selected": { + "backend": "ollama", + "model_ref": "qwen3-vl:8b", + "install": { + "type": "ollama", + "ref": "qwen3-vl:8b" + }, + "runtime": "local" + }, + "runtime": "local" + }, + "stt": { + "backend": "whisper.cpp", + "binary": "backends/whisper.cpp//whisper-cli", + "model_path": "models/whisper/ggml-base.bin", + "runtime": "local" + }, + "tts": { + "backend": "piper", + "binary": "backends/piper//piper", + "voice_path": "models/piper/en_US-libritts_r-medium.onnx", + "runtime": "local" + }, + "model_install_plan": { + "ollama": [ + "qwen3.5:2b", + "qwen3.5:4b", + "qwen3.5:9b", + "gemma4:12b", + "qwen3-vl:8b" + ], + "gguf": [ + "models/gguf/qwen3.5-2b-q4_k_m.gguf", + "models/gguf/qwen3.5-4b-q4_k_m.gguf", + "models/gguf/qwen3.5-9b-q4_k_m.gguf" + ], + "hf": [ + "google/gemma-4-12B-it" + ], + "whisper": [ + "models/whisper/ggml-base.bin" + ], + "piper": [ + "models/piper/en_US-libritts_r-medium.onnx", + "models/piper/en_US-libritts_r-medium.onnx.json" + ] + }, + "compatibility": { + "ollama_models_use_ollama_refs": true, + "llama_cpp_models_use_gguf_paths": true, + "vllm_models_use_hf_repos": true, + "voice_models_are_backend_specific": true + } + }, + "agent_decision": { + "status": "applied", + "provider": "ollama", + "model": "gemma4:12b", + "applied": true, + "applied_fields": [ + "default_model", + "preferred_backend" + ], + "rejected_fields": [], + "parsed": { + "decision": "BUILD_LOCAL_FOLDER", + "confidence": 1.0, + "risk_level": "low", + "selected_backend": "ollama", + "selected_model": "qwen3.5:9b", + "selected_client_model_ref": "qwen35_smart_9b", + "selected_runtime_agent_model_ref": "gemma4:12b", + "reasons": [ + "User explicitly requested BUILD_LOCAL_FOLDER.", + "Hardware profile (32GB RAM, 24GB VRAM) supports the selected models.", + "Selected model qwen3.5:9b is within the 32B parameter limit and fits local runtime constraints.", + "Agent model gemma4:12b is suitable for configuration tasks." + ], + "required_human_review": false, + "missing_secrets": [], + "questions": [], + "manifest_patch": { + "selected_backend": "ollama", + "selected_client_model_ref": "qwen35_smart_9b", + "voice_mode": false, + "phone_access": false, + "scout_vision": true, + "content_pack_ids": [ + "starter_prompt_packs" + ] + }, + "blocked": false, + "provider_status": { + "configured": true, + "provider": "ollama", + "model": "gemma4:12b", + "endpoint": "http://localhost:11434/v1" + }, + "source": "real_remote_llm", + "applied_fields": [ + "default_model", + "preferred_backend" + ], + "rejected_fields": [] + } + }, + "max_params_b": 32, + "selection_policy": { + "is_hardcoded_static_result": false, + "is_rule_based_catalog": true, + "uses_ai_in_loop": true, + "uses_live_hardware_scan_when_local": true, + "uses_custom_json_when_provided": true, + "whichllm_is_optional": true, + "standard_pack_cap_b": 9, + "power_pack_cap_b": 32, + "default_is_not_limit": true, + "ai_provider": "ollama", + "ai_model": "gemma4:12b" + }, + "download_policy": "Models and content packs are fetched during package build or factory preload, not during normal USB insertion.", + "runtime_policy": "Windows portable first. Linux bootable is secondary. USB runtime binds to 127.0.0.1 only.", + "product_policy": "The default pack is a practical offline baseline; the customer can keep, remove, or add compatible models based on hardware.", + "security_policy": { + "signed_updates": true, + "sha256_manifest": true, + "local_only": true, + "no_shell_tools_by_default": true, + "no_admin_required_for_normal_use": true + }, + "internal_decision": { + "decision": "BUILD_LOCAL_FOLDER", + "confidence": 1.0, + "risk_level": "low", + "selected_backend": "ollama", + "selected_model": "qwen3.5:9b", + "selected_client_model_ref": "qwen35_smart_9b", + "selected_runtime_agent_model_ref": "gemma4:12b", + "reasons": [ + "User explicitly requested BUILD_LOCAL_FOLDER.", + "Hardware profile (32GB RAM, 24GB VRAM) supports the selected models.", + "Selected model qwen3.5:9b is within the 32B parameter limit and fits local runtime constraints.", + "Agent model gemma4:12b is suitable for configuration tasks." + ], + "required_human_review": false, + "missing_secrets": [], + "questions": [], + "manifest_patch": { + "selected_backend": "ollama", + "selected_client_model_ref": "qwen35_smart_9b", + "voice_mode": false, + "phone_access": false, + "scout_vision": true, + "content_pack_ids": [ + "starter_prompt_packs" + ] + }, + "blocked": false, + "provider_status": { + "configured": true, + "provider": "ollama", + "model": "gemma4:12b", + "endpoint": "http://localhost:11434/v1" + }, + "source": "real_remote_llm", + "applied_fields": [ + "default_model", + "preferred_backend" + ], + "rejected_fields": [] + }, + "features": { + "voice_mode": false, + "phone_access": false, + "scout_vision": true + }, + "manifest_patch": { + "selected_backend": "ollama", + "selected_client_model_ref": "qwen35_smart_9b", + "voice_mode": false, + "phone_access": false, + "scout_vision": true, + "content_pack_ids": [ + "starter_prompt_packs" + ] + }, + "preferred_backend": "ollama" +} \ No newline at end of file diff --git a/manifest-input/jackailocal-gemma-agent-build-manifest.zip b/manifest-input/jackailocal-gemma-agent-build-manifest.zip new file mode 100644 index 0000000000000000000000000000000000000000..8763b632153198d60d1f1bc634b89ca81334d837 Binary files /dev/null and b/manifest-input/jackailocal-gemma-agent-build-manifest.zip differ diff --git a/manifest/backend-sources.example.json b/manifest/backend-sources.example.json new file mode 100644 index 0000000000000000000000000000000000000000..4445c88b72670a7f39800baa84ea3c1636a7b180 --- /dev/null +++ b/manifest/backend-sources.example.json @@ -0,0 +1,9 @@ +{ + "product": "JackAILocal", + "note": "Pin exact versions and hashes before production. Do not ship unverified binaries.", + "assets": [ + {"id":"ollama-windows", "path":"backends/ollama/windows/ollama.exe", "source":"private-cache-or-official-release", "version":"PIN", "sha256":"REPLACE"}, + {"id":"llama-server-windows", "path":"backends/llama.cpp/windows/llama-server.exe", "source":"private-cache-or-official-release", "version":"PIN", "sha256":"REPLACE"}, + {"id":"jackailocald-windows", "path":"bin/jackailocald.exe", "source":"cargo-build-release", "version":"0.3.0", "sha256":"REPLACE"} + ] +} diff --git a/manifest/gguf-downloads.example.json b/manifest/gguf-downloads.example.json new file mode 100644 index 0000000000000000000000000000000000000000..40202db04265d2db24e09d86a6e8735748410385 --- /dev/null +++ b/manifest/gguf-downloads.example.json @@ -0,0 +1,10 @@ +{ + "files": [ + { + "name": "qwen3.5-2b-q4_k_m", + "url": "https://REPLACE-WITH-YOUR-APPROVED-GGUF-URL/qwen3.5-2b-q4_k_m.gguf", + "path": "models/gguf/qwen3.5-2b-q4_k_m.gguf", + "sha256": "" + } + ] +} diff --git a/manifest/model-sources.json b/manifest/model-sources.json new file mode 100644 index 0000000000000000000000000000000000000000..20e82459425b3574c1b31e84eb2348e3ee8fca25 --- /dev/null +++ b/manifest/model-sources.json @@ -0,0 +1,15 @@ +{ + "notes": "Factory script pulls models before shipment. Verify licenses for every model and quantization before commercial distribution.", + "ollama_models": [ + "qwen3.5:2b", + "qwen3.5:4b", + "qwen3.5:9b", + "qwen3-vl:4b" + ], + "gguf_files": [ + "models/gguf/qwen3.5-2b-q4_k_m.gguf", + "models/gguf/qwen3.5-4b-q4_k_m.gguf", + "models/gguf/qwen3.5-9b-q4_k_m.gguf" + ], + "uncensored_policy": "Do not ship open-response variants in the default UX. Place them in a separate SKU or locked advanced pack with clear usage terms." +} diff --git a/manifest/product.json b/manifest/product.json new file mode 100644 index 0000000000000000000000000000000000000000..9e886b3dd10179bdd10d2fe1d69bf9b3d93fbc6f --- /dev/null +++ b/manifest/product.json @@ -0,0 +1,32 @@ +{ + "product": "JackAILocal", + "channel": "v15", + "version": "v15", + "modes": [ + "windows-local-folder", + "windows-portable", + "windows-desktop-installer", + "macos-local-folder", + "macos-portable", + "macos-desktop-dmg", + "linux-boot", + "lan-appliance", + "saas-generated-package" + ], + "offline_default": true, + "network_on_insert": false, + "default_pack_is_not_limit": true, + "content_packs": true, + "field_manual_core": true, + "voice_mode": true, + "scout_vision": true, + "phone_access_opt_in": true, + "thread_history": true, + "aes_transfer_packs": true, + "llm_config_agent": true, + "preferred_config_agent": "gemma4:12b", + "model_policy": "Qwen3.5 default pack; Gemma 4 12B local LLM-in-the-loop config agent when installed; Qwen3.6-27B power profile for RTX 3090-class hardware; all catalog models stay <=32B.", + "name": "JackAILocal", + "package_version": "v15", + "readme_behavior_documented": true +} diff --git a/manifest/sha256-manifest.json b/manifest/sha256-manifest.json new file mode 100644 index 0000000000000000000000000000000000000000..e6acf1e35a646ed09ff65388e2ceb729b730a46e --- /dev/null +++ b/manifest/sha256-manifest.json @@ -0,0 +1,3236 @@ +{ + "generated_at": "2026-06-05T14:25:16.0116461-04:00", + "files": [ + { + "path": ".dockerignore", + "sha256": "fec0a7e6b33b2faef077c6745ba31dcb7ad49bbcceb2d841e87c9c707b6038fd", + "size": 139 + }, + { + "path": ".gitignore", + "sha256": "c0f960eacda49245e20a784f06f598eba151889e0191bf516070bc872af5644c", + "size": 982 + }, + { + "path": "app.py", + "sha256": "5ea852a86ba99dfa4df9aa6cff4973c2a07acc44cefafbaf4ea7a6623ad81b86", + "size": 296 + }, + { + "path": "autorun.inf", + "sha256": "b4f4589bed48dc9a46c22399d3f1faab8d76893f184de48b23a38bec679633a5", + "size": 103 + }, + { + "path": "BUILD-LOCAL-MAC.command", + "sha256": "fb0eaed2c8b10415271e0966e0b5fae71aed27dc1f7fa222d7bc995cd78999cd", + "size": 190 + }, + { + "path": "BUILD-LOCAL.cmd", + "sha256": "44187adc8fbc3e70784b7f705100f838908636063804ba86b5527773b08ba698", + "size": 764 + }, + { + "path": "BUILD-SSD-MAC.command", + "sha256": "29b72e6072a2583725295271eaa8d6eedd1f31f31eefda9c6d317a8f1d783176", + "size": 188 + }, + { + "path": "BUILD-SSD.cmd", + "sha256": "e0a781927907983efe0ac92b1e30a2c277a2dbb7abc4cc9e7f197a2ced52f68e", + "size": 570 + }, + { + "path": "BUILD-USB-MAC.command", + "sha256": "548c6307562acf483c181c9a5c6787a94a41f7aae375693f9084da3aa5e01e4a", + "size": 188 + }, + { + "path": "BUILD-USB.cmd", + "sha256": "18956458d788410ce2ac72f743d7d2b41dc7f5bd0e930a76d308e9f3ab72d9aa", + "size": 692 + }, + { + "path": "Cargo.lock", + "sha256": "54d8a9c10a59455b4d6a4367c100c9a38e8061e679b77e594bcbc55536eb10e9", + "size": 63498 + }, + { + "path": "Cargo.toml", + "sha256": "5ecd2f81feea2c6229c27c79f33d9e6001a47ea329e179cfefd00599369086a2", + "size": 776 + }, + { + "path": "CHECK-PACKAGE.cmd", + "sha256": "e1392df62ed7f4b00e336cd27a071d2d52dffb616e6c34e6d91669374ae36f2c", + "size": 948 + }, + { + "path": "CLAUDE.md", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 0 + }, + { + "path": "Directory.Build.targets", + "sha256": "fda2853f4f17b87c968ad4ceee0321b1c76c294b012bd80a01e6755d3ed3f67c", + "size": 1502 + }, + { + "path": "PRODUCTION_CHECKLIST.md", + "sha256": "566986f6df36fad3e3cd99a6eb7107fd845655ffd3f55ed19ca4e2eb19735a95", + "size": 1478 + }, + { + "path": "README-FIRST.txt", + "sha256": "83ca10dd1d044a26b2cd9e320358335c167acff16f298ab2363b0434a41e5016", + "size": 410 + }, + { + "path": "README.md", + "sha256": "96e303c5ca687257b1882c75d68e33c69b9b99eb6c50a262a31feb2bc8fdfb7f", + "size": 4882 + }, + { + "path": "requirements.txt", + "sha256": "846505957c6de3a17cace1cdc719052aa3c1162a9c530f4dd57abd094bf86b2f", + "size": 24 + }, + { + "path": "START-DESKTOP.cmd", + "sha256": "7afbc8822c2363bc69c12e784805df95927f76da94e06af1d236b37694be1fab", + "size": 434 + }, + { + "path": "START-HERE.cmd", + "sha256": "7b982deb551c36429c6de7e205262f37365da7e8bbafbb0c12499b2a2b1b085b", + "size": 417 + }, + { + "path": "START-HERE.command", + "sha256": "f884bee5bd1dc18f2213644b17b9d8b0d5f688ed602affd45b2ec9d2d2e5c16b", + "size": 165 + }, + { + "path": "STOP-JACKAILOCAL.cmd", + "sha256": "8d117b322c6057a0b4d804572879915b69a3fb7bab28e6fa2c612ef2f198844d", + "size": 131 + }, + { + "path": "STOP-JACKAILOCAL.command", + "sha256": "6a1db21004446967730b97669fb4e26e7344f4d4087cb54bc27bc79e73db3094", + "size": 124 + }, + { + "path": "TEST-WITHOUT-USB-MAC.command", + "sha256": "e3f4fb521f67bd13eb410d7aa99f867084df95dbd5b4e234b545e9b1632b910f", + "size": 497 + }, + { + "path": "TEST-WITHOUT-USB.cmd", + "sha256": "9480cd678c2b2d32538c631396c5e0542d5996f5ac2c1128a5e801a20431a2f3", + "size": 841 + }, + { + "path": ".out-of-code-insights/annotations.json", + "sha256": "e73e1b85b26d4ba1067ba52cddcafee999f84ec628b150ef5a3c487d4dd1d2f8", + "size": 45 + }, + { + "path": "backends/ollama/windows/ollama.exe", + "sha256": "24f6353c1e4eb943262a8e7887dff465c1f992df2c34fb190b7eb1c36eceec80", + "size": 35552648 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-base.dll", + "sha256": "df1946b52bccf4c328f61c3ea96b5ae442bb9dd0c4368b378c5ea8723a7bef92", + "size": 817032 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-alderlake.dll", + "sha256": "efec8a7cc9a7e08dc3b74564829cfc689a5ec4642736f1fc0ab81411157717f1", + "size": 1479048 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-cannonlake.dll", + "sha256": "e2befbfcadec14930736e7ca349e4c14c4148f059fe7eb2e7c79447adaf7d117", + "size": 1737608 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-cascadelake.dll", + "sha256": "a0e3e93788b40cf3287ab88f6663c264c507474a25355dc28c395da8486f026f", + "size": 1719688 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-cooperlake.dll", + "sha256": "feabda6f07c67752bb563ccacddb4461bb41e66159a8ec6ce0f78e10e43703a2", + "size": 1722760 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-haswell.dll", + "sha256": "ca6d1c0a81ce0f6c07b6391a2656abf9a0349bc9590683f2c8545b5512f82524", + "size": 1486216 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-icelake.dll", + "sha256": "2c35dba827a64264896cb72cd3050fd8aaf7f979b53cde52cc785cf398df9e67", + "size": 1726856 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-ivybridge.dll", + "sha256": "8c1dc649dec36e16a10513a6234f9bbdf65691d1a0ca1cb09c083724eb35b9dd", + "size": 1380744 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-piledriver.dll", + "sha256": "c4a667bbc4ee363ef11e151b5716a99a238e66a399202ebefd6ccafb60ee95ed", + "size": 1383816 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-sandybridge.dll", + "sha256": "0a9439c4fe5c9a4988603e2d64b36d5d1a507f4fbbd18b24b9879ff7fe10b5df", + "size": 1360776 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-sapphirerapids.dll", + "sha256": "216a7b3f8a97a691d2db31d06e89fc4dc6e558f6f4d2dd48aff37966cfdae8bc", + "size": 2024328 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-skylakex.dll", + "sha256": "dc6e2f3bc91fabf6873c92f5ae0fe3539dcdcfec1f6cb3c832f6f8425154c4db", + "size": 1729928 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-sse42.dll", + "sha256": "6c04c36d3cda3f40ad265823ecb9cf48c2f3c8da71676c654b964a735b68b775", + "size": 1175432 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-x64.dll", + "sha256": "44de9115d78b80186f861ebca0a61571130b6be2661e6394b6f3293b706fd509", + "size": 1163656 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml-cpu-zen4.dll", + "sha256": "2dc68c9c5d05ab672c7aa20ec7a2732d65daf9217d0911c83c2f3325ef4bcf7c", + "size": 1729928 + }, + { + "path": "backends/ollama/windows/lib/ollama/ggml.dll", + "sha256": "19dbf8c2ba0b1de3b64f4cbc4435ef7e5fc5e80a897642a03986bc1d058c9ab6", + "size": 50568 + }, + { + "path": "backends/ollama/windows/lib/ollama/libc++.dll", + "sha256": "bf7e1ee68af16e6b94a7c4e9fb9cfe1fea922437cf93c0a1de6c657b516a2256", + "size": 1767816 + }, + { + "path": "backends/ollama/windows/lib/ollama/libllama-common.dll", + "sha256": "63bb5fa0ab6669b859ffd6313b7ad81116fa9d362e85a3b4421b5459522641fc", + "size": 4003720 + }, + { + "path": "backends/ollama/windows/lib/ollama/libllama-quantize-impl.dll", + "sha256": "b2a3984e38b7ec366a4a1de1e962c9d0d04f32ee5bbf468f0a5a79fe804385c4", + "size": 96648 + }, + { + "path": "backends/ollama/windows/lib/ollama/libllama-server-impl.dll", + "sha256": "b67402d52c20407bb7aecfeb7498f2eb74921d1c1f638acc989f53ff6192f8ad", + "size": 3091336 + }, + { + "path": "backends/ollama/windows/lib/ollama/libllama.dll", + "sha256": "2e2688663803b71e67a0c6850b3a171cf3a9c44c4c4e6088084a3d25062266c5", + "size": 2749320 + }, + { + "path": "backends/ollama/windows/lib/ollama/libmtmd.dll", + "sha256": "d699732680e282948280253195e9c82b4a965bcbbd24f8a42c0fafc6f9b66b9c", + "size": 1131400 + }, + { + "path": "backends/ollama/windows/lib/ollama/libomp.dll", + "sha256": "32e1837b7a6d2c596632ccf402e3ebed3df8b48d1f55acf3908f4632a551b906", + "size": 965000 + }, + { + "path": "backends/ollama/windows/lib/ollama/libunwind.dll", + "sha256": "56fd375efce40f5826a375ef96115d8cf73b73a169fafe062c60161469d6951c", + "size": 107912 + }, + { + "path": "backends/ollama/windows/lib/ollama/libwinpthread-1.dll", + "sha256": "735769df92f1c8c1f2b1eb88a2ed008f4b978d7146bb96c23ae466c6aa9bee1a", + "size": 390024 + }, + { + "path": "backends/ollama/windows/lib/ollama/llama-quantize.exe", + "sha256": "6c07d2fe7a7396b145219d535d5227e47e7be835e26243ef1dc4aad426c71047", + "size": 22920 + }, + { + "path": "backends/ollama/windows/lib/ollama/llama-server.exe", + "sha256": "296cb0293c5ec37b44237a29dd9f2248084104f50dbf74d9cdfc3d5deef02353", + "size": 22920 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/concrt140.dll", + "sha256": "b37160f4467dd5066e6f4f97b7b02f6d8e2de935c48d262e105f0ef5d683ce2c", + "size": 311688 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/cublas64_12.dll", + "sha256": "4e4f220c25902933b2726ec36e8f9f0213e87f016d1bc5a34a0bd49d4aa9d584", + "size": 113720712 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/cublasLt64_12.dll", + "sha256": "f589d77473ff3ae051c23aa55561820ccf21d7c7fc6ab614fa83ec4efec3ac29", + "size": 692449672 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/cudart64_12.dll", + "sha256": "388e8d08f2e26f59a316e141473bf605881912b65c0663d713fc60cafa22795b", + "size": 582024 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/ggml-cuda.dll", + "sha256": "5afb3d1852bbe8903e185c5021f130db7bcd5f19404c24a8ac8ec2def6f092bd", + "size": 371161480 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/msvcp140.dll", + "sha256": "bae3aa3d903c7322f2a73867c9bf0a4b241d6278f3c27e84b643e3abffe519f2", + "size": 545160 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/msvcp140_1.dll", + "sha256": "b9650fcc3be5af63d88cb5ea7881349da3012864f93ed235f413e29a22e9ce74", + "size": 23432 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/msvcp140_2.dll", + "sha256": "d85b6a441ea21e0570caf384c2d394326b9d1a595b61a1615d5be8563cdf3ffd", + "size": 267656 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/msvcp140_atomic_wait.dll", + "sha256": "6cae6f39e0ff33b0a1e7220e0d361e5f429718264302d78f46059e030ade5ba7", + "size": 37768 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/msvcp140_codecvt_ids.dll", + "sha256": "0e8e87755aff506fb12be195c60e23d48dbb2eaa86ffab40990af388e3b38bce", + "size": 19336 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/vccorlib140.dll", + "sha256": "6a945a2df1168c7b5c3ee1d9dc81dcd9a47c993628b9dfca8f10a03bf0d23776", + "size": 339848 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/vcruntime140.dll", + "sha256": "815a4b18d5973c9c9821f49ccf4df53ceb4123dac80c1ae5697e38d418ebe043", + "size": 112008 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/vcruntime140_1.dll", + "sha256": "0cf8c531e2c127262eab43336e9eaf93ef072c82a5e5b4e0da4c5a20911a03cb", + "size": 37256 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v12/vcruntime140_threads.dll", + "sha256": "a9aa3a7d6660260fa124553b87f4b29f84279c66b652187f4e3b57bdf3b21acf", + "size": 25992 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v13/concrt140.dll", + "sha256": "07ace40bc283594e7de383b5af1695c6a547410ee47f86cc7ba73617def0b42d", + "size": 311688 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v13/cublas64_13.dll", + "sha256": "940d18718706b771657469dd95b591b3584b275a7a2815c82bc3e6235486c481", + "size": 50285960 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v13/cublasLt64_13.dll", + "sha256": "e7e153aa8b746186fee405d0c430643eaefa5b53ea5c275fec96a0182c93dbf0", + "size": 477894536 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v13/ggml-cuda.dll", + "sha256": "983fd8dde7f9fe19c8ceb89f11e8f299e7be35a841d6a52a3c34e7bc0bc8564d", + "size": 97635720 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v13/msvcp140.dll", + "sha256": "0faa85bc11662de64471eb2259c7ab980c8b85d5f61350638b2c5a12b7659568", + "size": 545160 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v13/msvcp140_1.dll", + "sha256": "4880188b4ee98ea19b2c4e5edba200d923cb739b10307310e29605a6285cadce", + "size": 23432 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v13/msvcp140_2.dll", + "sha256": "5e0975b550bd968529402a023e19388f7133b0482813e3f7f0bbb542390d180d", + "size": 267656 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v13/msvcp140_atomic_wait.dll", + "sha256": "c7a3f60663a7ac55ac89ba3a763ff98e7cd6276bd92199cf56cc8304552de9a5", + "size": 37768 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v13/msvcp140_codecvt_ids.dll", + "sha256": "b6a7fce46b53c3bf34b13e3e85f3015ed56823bda20cc083caf9ea8cca9741b7", + "size": 19336 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v13/vccorlib140.dll", + "sha256": "cc37791afc279fc0f51f70f587c221c5424a1ef678351558bcaae215ca13fb28", + "size": 339848 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v13/vcruntime140.dll", + "sha256": "415e3bcb34050e5b43212d67140a751fd3cb8068f806ebf5993647b169b62123", + "size": 112008 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v13/vcruntime140_1.dll", + "sha256": "e8603996ed68864e2b55aa7bcd8bcea80156aa8375b67e1f4363b4be8e16da5d", + "size": 37256 + }, + { + "path": "backends/ollama/windows/lib/ollama/cuda_v13/vcruntime140_threads.dll", + "sha256": "3557bb62c713c27db5f8eaa503a95a382f85e0edaffd7a5ab25433a3edbad042", + "size": 25992 + }, + { + "path": "backends/ollama/windows/lib/ollama/vulkan/concrt140.dll", + "sha256": "9325e8a4c9f49e21f49ad452984f058db4ab61b6a92c345b28dd0b5a2d264f94", + "size": 311688 + }, + { + "path": "backends/ollama/windows/lib/ollama/vulkan/ggml-vulkan.dll", + "sha256": "d6637f2321b87e0f417f31c09cadfa4d0f9baa7d96da0f6a206620077a37b3d0", + "size": 58064776 + }, + { + "path": "backends/ollama/windows/lib/ollama/vulkan/msvcp140.dll", + "sha256": "c5bc809c6679915aadc311d1174905ccc2d91109e18059f52a26a2af70da185d", + "size": 545160 + }, + { + "path": "backends/ollama/windows/lib/ollama/vulkan/msvcp140_1.dll", + "sha256": "672bb6a52481ed461677fff34162e5508035ec8bd317382bef7ba266064e25c0", + "size": 23432 + }, + { + "path": "backends/ollama/windows/lib/ollama/vulkan/msvcp140_2.dll", + "sha256": "5513bb5b235adacc0523b4c040b04462a0761ff01c48adc5a6397b48a4568f59", + "size": 267656 + }, + { + "path": "backends/ollama/windows/lib/ollama/vulkan/msvcp140_atomic_wait.dll", + "sha256": "c4c93b4fe4d5238344a1ea1868227fa148296478c612c4407715b2131ccb7349", + "size": 37768 + }, + { + "path": "backends/ollama/windows/lib/ollama/vulkan/msvcp140_codecvt_ids.dll", + "sha256": "7f870891313ae49d3f280b47035c60b6de77ba04c9c8d45b309d194760b789e6", + "size": 19336 + }, + { + "path": "backends/ollama/windows/lib/ollama/vulkan/vccorlib140.dll", + "sha256": "a28608fce0174337d6037d7585e1e76e27c81857dbe08fc3e9dd95bb97843f76", + "size": 339848 + }, + { + "path": "backends/ollama/windows/lib/ollama/vulkan/vcruntime140.dll", + "sha256": "8a03429eb53aea1540c15d6e242cbeb22406a4c6921380b600f09d7e6c24ec12", + "size": 112008 + }, + { + "path": "backends/ollama/windows/lib/ollama/vulkan/vcruntime140_1.dll", + "sha256": "5754658ad4939cdb81b001cda8c70a8a435ba5ff527dfe963fe0a09e9385677e", + "size": 37256 + }, + { + "path": "backends/ollama/windows/lib/ollama/vulkan/vcruntime140_threads.dll", + "sha256": "dc013a9926dd5d78130c27ff875f0a7b1af07f34d0276cab95b5813cf3b3ca23", + "size": 25992 + }, + { + "path": "backends/ollama/windows/lib/ollama/vulkan/vulkan-1.dll", + "sha256": "67d3bccfc56e2bb406a79900d758ed708230b0f256ab7829ed33e0346a0765d1", + "size": 1613192 + }, + { + "path": "backends/piper/windows/espeak-ng.dll", + "sha256": "9588480f8197df62fd8461a8431f8eaec6e8e7749c5ffcbe7fee656fe40a2189", + "size": 380928 + }, + { + "path": "backends/piper/windows/libtashkeel_model.ort", + "sha256": "9f27090af3e0f661913af048a739632a3f577b3233512551d8c90580ccad4aa8", + "size": 10261536 + }, + { + "path": "backends/piper/windows/onnxruntime.dll", + "sha256": "a630f67f4a82b6689e4178bf81d362d945522dd907b2a92cb43cebf72c83a06f", + "size": 9271704 + }, + { + "path": "backends/piper/windows/onnxruntime_providers_shared.dll", + "sha256": "71705b9bd76baced583eb37fe5ec2101946b4de50f1e9bdb156109f7e8082980", + "size": 22424 + }, + { + "path": "backends/piper/windows/piper.exe", + "sha256": "96f3da3811151580073e40bb4dd20eb0fb8115f5f5f76e2fb54282b3edfa5c1f", + "size": 509952 + }, + { + "path": "backends/piper/windows/piper_phonemize.dll", + "sha256": "4b5f344b2f31204a8a94a0bf485f93e4971671e81188a0d67f326e113bfb0b2e", + "size": 407040 + }, + { + "path": "backends/piper/windows/espeak-ng-data/af_dict", + "sha256": "25729d3bf4c4a0f08da60aea9eb5a0cf352630f83fab1ab0c3955b7740da1776", + "size": 121473 + }, + { + "path": "backends/piper/windows/espeak-ng-data/am_dict", + "sha256": "9954365860682c756c8c4459577d3aa0ab77ad0cba6798b746033ada5c3d4bea", + "size": 63878 + }, + { + "path": "backends/piper/windows/espeak-ng-data/an_dict", + "sha256": "6cf38cedb6693aade49e5a5d27a3c92516a5335125bf85fcae58282e66fb82fd", + "size": 6691 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ar_dict", + "sha256": "72316426e797777fe4df9420935a3b6a79b37d7e3f3948537ba71cd7b21b2541", + "size": 478165 + }, + { + "path": "backends/piper/windows/espeak-ng-data/as_dict", + "sha256": "39e52fd47507660fced36db407b12c6325498dc71fa9d3379d28b303506753ac", + "size": 5005 + }, + { + "path": "backends/piper/windows/espeak-ng-data/az_dict", + "sha256": "e352b2dcf811662d79f4beaaa0c7750706cf669c0409fd815a9bd0244b1adfc7", + "size": 43773 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ba_dict", + "sha256": "246c8c655ceed43d9ff44a65de5f933f16255489d0b181b1a46a83e1fd5a0354", + "size": 2098 + }, + { + "path": "backends/piper/windows/espeak-ng-data/be_dict", + "sha256": "232fb0debfc8fdca9b8f2494770ff23535f7f887bbebb0178abffc243490e798", + "size": 2652 + }, + { + "path": "backends/piper/windows/espeak-ng-data/bg_dict", + "sha256": "8fa3adec8e18a3e695b2bbea1d4780182e17222d8b9f8da9908a237828f6b063", + "size": 87051 + }, + { + "path": "backends/piper/windows/espeak-ng-data/bn_dict", + "sha256": "562a64ec5ea319622ec2286c103d4110222a1712129a3a9f07f2ea14a660038a", + "size": 89979 + }, + { + "path": "backends/piper/windows/espeak-ng-data/bpy_dict", + "sha256": "a4321d9ba80bd60f49e0bee5aae57f1ae9c49b7a1c3d4ba384338fe9bf3b6a92", + "size": 5226 + }, + { + "path": "backends/piper/windows/espeak-ng-data/bs_dict", + "sha256": "d5df564f511af082884719685db86ec4df23467cf012ce25e898540baf9de65b", + "size": 47068 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ca_dict", + "sha256": "1be203855ecb0bdfe66578ef5552f83289e39972d78c52714d061b7f7493df4a", + "size": 45566 + }, + { + "path": "backends/piper/windows/espeak-ng-data/chr_dict", + "sha256": "cc9bc3447852e1dd49578956588d71987509e5aa8fa1995a4fbab0f71add3be2", + "size": 2859 + }, + { + "path": "backends/piper/windows/espeak-ng-data/cmn_dict", + "sha256": "109aaa7708d3727382acb3ae41d8e2094a7e2bb9f651a81835be22a6f08071fe", + "size": 1566335 + }, + { + "path": "backends/piper/windows/espeak-ng-data/cs_dict", + "sha256": "5ec02aba90465007f20a5c8aeb889275834cfa8c8f621c3d9bee1ca90d21788d", + "size": 49645 + }, + { + "path": "backends/piper/windows/espeak-ng-data/cv_dict", + "sha256": "8260837c8f18fbe5870905ebb2850dfd18e252150ad6c868b96d9d2c6398ea8a", + "size": 1344 + }, + { + "path": "backends/piper/windows/espeak-ng-data/cy_dict", + "sha256": "31d74ccad218ab80b79566caeb8d21bcf0fe43326ed7c437fa736d39b85e603e", + "size": 43130 + }, + { + "path": "backends/piper/windows/espeak-ng-data/da_dict", + "sha256": "ae6c2dd4f0f4d38342918a776a9e2c46d919572f02336c864f843ff7b262caf8", + "size": 245287 + }, + { + "path": "backends/piper/windows/espeak-ng-data/de_dict", + "sha256": "e9ac048df5cde03b74907d591c7b29b45ee9ec5fcd4592b0b894f0083572fcfd", + "size": 68276 + }, + { + "path": "backends/piper/windows/espeak-ng-data/el_dict", + "sha256": "5d9f759750131da777d2fd4f06aa602ed746d38f27b84dd9a2d4a550f2cd452e", + "size": 72841 + }, + { + "path": "backends/piper/windows/espeak-ng-data/en_dict", + "sha256": "71bd330ba8a2e3e8076e631508208ef49449d6147c17b7bd2b4b1e1468292e35", + "size": 166944 + }, + { + "path": "backends/piper/windows/espeak-ng-data/eo_dict", + "sha256": "849f061abddd5388bac112b32e6f7bf747e15e1b3ae53fe91079381effff4cc9", + "size": 4666 + }, + { + "path": "backends/piper/windows/espeak-ng-data/es_dict", + "sha256": "3fa93e251eab80838a2d6207ee58c9c847270a65fe3fae98850e0185b5a2186c", + "size": 49252 + }, + { + "path": "backends/piper/windows/espeak-ng-data/et_dict", + "sha256": "1a6734b4e6bc9de9a4c94cfb434873e0a7f4ae4ec89549b3137ae5491cf655da", + "size": 44263 + }, + { + "path": "backends/piper/windows/espeak-ng-data/eu_dict", + "sha256": "762a6eff63552ef72cd88bf1e92dc5a823eb378d50cb85c7917e8c5bba079137", + "size": 48841 + }, + { + "path": "backends/piper/windows/espeak-ng-data/fa_dict", + "sha256": "f92f35ce7eb1d97360016617d97786412a94af55b776a18f1c280dab4f43befd", + "size": 292423 + }, + { + "path": "backends/piper/windows/espeak-ng-data/fi_dict", + "sha256": "dda999437bd4b37a7f7400a2230aace1200a52906ab0b1ff3507d44ab19d64b1", + "size": 43928 + }, + { + "path": "backends/piper/windows/espeak-ng-data/fr_dict", + "sha256": "e399ab924c4d10beef1fc310b30ea56e4ddfd8b4b64b8ed978e9c65394d49b2d", + "size": 63727 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ga_dict", + "sha256": "b2db03d21f7900b697d7dbb8bec855cd1117e9867f19884faf11c4df626659f5", + "size": 52673 + }, + { + "path": "backends/piper/windows/espeak-ng-data/gd_dict", + "sha256": "e92b47dcf9a001363ffee1e62863faf264ff5f16e10cd9cf1f1e83ca89ca14aa", + "size": 49121 + }, + { + "path": "backends/piper/windows/espeak-ng-data/gn_dict", + "sha256": "7640df272b192a68ba401294bf9197ab46d0b141da7a358ad1986e7f77e7f29a", + "size": 3248 + }, + { + "path": "backends/piper/windows/espeak-ng-data/grc_dict", + "sha256": "488779493993d221b09e713e104a221ac1bb35765f826acc26d3d3c1aa3a35ed", + "size": 3433 + }, + { + "path": "backends/piper/windows/espeak-ng-data/gu_dict", + "sha256": "cb1fbfb53bab2ec6d9e44cd9d12ccb9b80de49f274d772314389c477eef530fd", + "size": 82480 + }, + { + "path": "backends/piper/windows/espeak-ng-data/hak_dict", + "sha256": "e3075c7d281f95a2ce2264c01edbd09e962cb3ed29400766c5d2116a08a185c8", + "size": 3335 + }, + { + "path": "backends/piper/windows/espeak-ng-data/haw_dict", + "sha256": "2b4e77b61cadfc5ee622aa8b1d23941e36101d9bdbf8d809e0fa1b442d2c3f74", + "size": 2443 + }, + { + "path": "backends/piper/windows/espeak-ng-data/he_dict", + "sha256": "a8bf0d939d630e9e5097ad361a7765297baf297fd7d06e89f48a79ead980869e", + "size": 6963 + }, + { + "path": "backends/piper/windows/espeak-ng-data/hi_dict", + "sha256": "5a68c9532624e57ac845b26ce1e2e5034c4f6353bede46ecbe57e583ec8effd6", + "size": 92143 + }, + { + "path": "backends/piper/windows/espeak-ng-data/hr_dict", + "sha256": "5f98fcf05f97335a5436a8a9643677da6f158f2a9ee1d41bdda1725b1bb3527e", + "size": 49388 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ht_dict", + "sha256": "d577219d15c29396db254aed958358456ec1501cdf62cb4ca5413752f2ee7cc1", + "size": 1803 + }, + { + "path": "backends/piper/windows/espeak-ng-data/hu_dict", + "sha256": "055d563b839d599db40be5b1cc2af9d31793795b328eb8d4f389ec7d38fe7e81", + "size": 153785 + }, + { + "path": "backends/piper/windows/espeak-ng-data/hy_dict", + "sha256": "8b808ad8f1d4cae32b3b785b96d1e27b3af28e32f4dddfed7c93e129d63b2587", + "size": 62263 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ia_dict", + "sha256": "1c665603a82beb81e0cef2878c9b44642ae6d519e301fbbd06326e03325febf3", + "size": 331275 + }, + { + "path": "backends/piper/windows/espeak-ng-data/id_dict", + "sha256": "8fb3f23f80f71f023219cee7032223f3f6886cc5040eb4c8c507f79d1b84e98d", + "size": 43458 + }, + { + "path": "backends/piper/windows/espeak-ng-data/intonations", + "sha256": "3f8af65fd3eda9759a10f021d61361c120871f463515229c925995c7f90918cc", + "size": 2040 + }, + { + "path": "backends/piper/windows/espeak-ng-data/io_dict", + "sha256": "0d9d6995fead1ccdd5512f7909ec8f9efc8ccc97f5c9d23ecae6b3ba77ffadbf", + "size": 2165 + }, + { + "path": "backends/piper/windows/espeak-ng-data/is_dict", + "sha256": "8eeab5b1ca3f75362881fae3de3316fcaff281179a4c51fceb0098f11ead96e0", + "size": 44354 + }, + { + "path": "backends/piper/windows/espeak-ng-data/it_dict", + "sha256": "111f968359c9ebe36b107a2565ba9f258bab337d3cbe37e44871980a8f21ce6b", + "size": 152889 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ja_dict", + "sha256": "64a4ec3b422dcfcd5109e39e35e0f8a499e98347b7948e04777354378aabb384", + "size": 47652 + }, + { + "path": "backends/piper/windows/espeak-ng-data/jbo_dict", + "sha256": "71298417de1e5fd07df275270a5d47ca8820cb8c9f5dc2156c070b7a3ae7999c", + "size": 2243 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ka_dict", + "sha256": "c8624947110a13bee72d4fc15b06026004df3f40c16c7b917d27e369ca122a32", + "size": 87775 + }, + { + "path": "backends/piper/windows/espeak-ng-data/kk_dict", + "sha256": "88dc0106629a57d0aca5073afb11db1a044799185355088a7ad4331c836affd3", + "size": 1859 + }, + { + "path": "backends/piper/windows/espeak-ng-data/kl_dict", + "sha256": "ec441964d7fbd4622bb628327903d5c6c999c95d539b2e061884decf95ac30e3", + "size": 2838 + }, + { + "path": "backends/piper/windows/espeak-ng-data/kn_dict", + "sha256": "c51e0385937b407b36c02fd2c85dfecdc5cac411398ee8a74eca15b35617af64", + "size": 87828 + }, + { + "path": "backends/piper/windows/espeak-ng-data/kok_dict", + "sha256": "aef3783d335b8e893767b9c26d1b93af1f3c6eca75f87a511f109c1170798d64", + "size": 6394 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ko_dict", + "sha256": "c79e6f90ad4a5eb8e80c3861c0192b7cd814bb346c193f98524dea2f9b8b6c26", + "size": 47523 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ku_dict", + "sha256": "65de1c5429e8d193775b36fcf169a60fbbb9964ef13a4a1bab7ff0ce855cad20", + "size": 2265 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ky_dict", + "sha256": "def4427fd4070673c073b892c7b229f8c3f17eb1df36bea2ec7188ddcfb4d0ea", + "size": 64977 + }, + { + "path": "backends/piper/windows/espeak-ng-data/la_dict", + "sha256": "17818dd567936fde032404fbbd6177844b9f5fc5a915480f567272147936f917", + "size": 3806 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lb_dict", + "sha256": "d819a062b9722334f187540d3530fac1de365cd200d4a466f5ecccd7e3c05772", + "size": 687931 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lfn_dict", + "sha256": "10281bc404f429165a330b800eb64674e37a135d1b4c689e57fb1d9e827d6924", + "size": 2793 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lt_dict", + "sha256": "68ef55aba9545d27ce73e7d4aca1c2fa51d2c3d3d626152fbc22bf5a90fbff11", + "size": 49890 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lv_dict", + "sha256": "3e246c76b7da924235ffb75925070865e669da2ce3e41e98395e99be1f8db9f7", + "size": 66337 + }, + { + "path": "backends/piper/windows/espeak-ng-data/mi_dict", + "sha256": "401621fdf61da47d0513e80279134a3515ced898aaadd5205b82cced0f2df042", + "size": 1346 + }, + { + "path": "backends/piper/windows/espeak-ng-data/mk_dict", + "sha256": "e4960ff4e74e1096ff1c3bccab62ce0429fa4faa725e546cd5d33e43f45225be", + "size": 63859 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ml_dict", + "sha256": "b12687557c38f9bcd17417854253ddde3781e3b0c16f9470d732205818e7cd38", + "size": 92345 + }, + { + "path": "backends/piper/windows/espeak-ng-data/mr_dict", + "sha256": "f712a7525db108ef9769aee53fdd1cd141f0df372778924691b89623aa6fc22f", + "size": 87391 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ms_dict", + "sha256": "74b01897385ba1f018de44c0268048fa5be588bbbb702a900bb35c691a466707", + "size": 53541 + }, + { + "path": "backends/piper/windows/espeak-ng-data/mto_dict", + "sha256": "6d51cb6be959dba045d41d3d63ff662a7363e1287cce2c633b071295ce55d560", + "size": 3960 + }, + { + "path": "backends/piper/windows/espeak-ng-data/mt_dict", + "sha256": "1f76761bdfd481a984b15d07522df23c9444f4ac21ea9671917e08bbffa7f8d3", + "size": 4384 + }, + { + "path": "backends/piper/windows/espeak-ng-data/my_dict", + "sha256": "9d1bdeb8b68a3ed7ab96d8c62b91014300ec26aca0837de90423bfb369ff3b57", + "size": 95948 + }, + { + "path": "backends/piper/windows/espeak-ng-data/nci_dict", + "sha256": "38c7201e7726c8d508f8f8611349cbc44fcc4684a0fee4db7285d83e808a5b69", + "size": 1534 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ne_dict", + "sha256": "43831c3f53ea6e939cac37b5ae5bd7a2ea8237aa46f0e7f2441ab772a3c51dba", + "size": 95377 + }, + { + "path": "backends/piper/windows/espeak-ng-data/nl_dict", + "sha256": "d28641049f98c76b460c3dddde3e2edecb7782c4eb9a3f242391301ff526c83f", + "size": 65979 + }, + { + "path": "backends/piper/windows/espeak-ng-data/nog_dict", + "sha256": "8b921398a2f1907b43c07b96a72277d8d3d8545e385955eebae5f340aae33bd2", + "size": 3294 + }, + { + "path": "backends/piper/windows/espeak-ng-data/no_dict", + "sha256": "43b828f8c1709e7abb23a0cc6416fb32e4814e3f1ff1db4baf2935bf7c77c9ff", + "size": 4178 + }, + { + "path": "backends/piper/windows/espeak-ng-data/om_dict", + "sha256": "3624e67ef8d9c20d894db0adccaccc61ee852977cb77d5fe5801e7b4a528fcf5", + "size": 2302 + }, + { + "path": "backends/piper/windows/espeak-ng-data/or_dict", + "sha256": "e0dd737e9104da63a27d86cd464cf7764837625b1513169b8d28fca1bd3c8c3b", + "size": 89246 + }, + { + "path": "backends/piper/windows/espeak-ng-data/pap_dict", + "sha256": "770039bbf4ea8c5a3ffddd98c4831ca02c9f39e1ff6b3de7729fa71a8f3bc0f1", + "size": 2128 + }, + { + "path": "backends/piper/windows/espeak-ng-data/pa_dict", + "sha256": "cee0e2a2bfb166e602ff84e185351a8ac311571ccdf46e1831c7a25245563c10", + "size": 79953 + }, + { + "path": "backends/piper/windows/espeak-ng-data/phondata", + "sha256": "4e0288957874029a8c3c9f41a8f517ad4bf18127046decbdd4b9d1d6807ce3a3", + "size": 550424 + }, + { + "path": "backends/piper/windows/espeak-ng-data/phondata-manifest", + "sha256": "3187abb77531275530234293c7294e3b0743fda4392e03007c57ec22c03e7a2b", + "size": 22808 + }, + { + "path": "backends/piper/windows/espeak-ng-data/phonindex", + "sha256": "3ca7b8fa3b42624e4b0f152707e7a39245fce569aa99ea47c055d9e622fcf0c4", + "size": 39074 + }, + { + "path": "backends/piper/windows/espeak-ng-data/phontab", + "sha256": "886f3fa402cb0ba73d483aa8ad000af47a6b7cc06293c75a97913fba68a530f6", + "size": 55796 + }, + { + "path": "backends/piper/windows/espeak-ng-data/piqd_dict", + "sha256": "33004a36210c33291aef8a838d477667eed7af52ff5093152891379b1774188d", + "size": 1710 + }, + { + "path": "backends/piper/windows/espeak-ng-data/pl_dict", + "sha256": "0e0ce6cfe4eba41a1223362a046390dca359a3e597af8243adaac84d624ef18f", + "size": 76730 + }, + { + "path": "backends/piper/windows/espeak-ng-data/pt_dict", + "sha256": "7e8398f15fa4a21db39a213381f28ec0d302b22663c644e865abb61a09c1b004", + "size": 67817 + }, + { + "path": "backends/piper/windows/espeak-ng-data/py_dict", + "sha256": "2255348879547940e8b49e0ca1b80452748f142cb6e267e49cab0560c720129a", + "size": 2409 + }, + { + "path": "backends/piper/windows/espeak-ng-data/qdb_dict", + "sha256": "c9b7f307ffe54f4aaf323290eca1c620572919035b0eb268e5c94f5d7a430058", + "size": 3028 + }, + { + "path": "backends/piper/windows/espeak-ng-data/quc_dict", + "sha256": "458c1ca6dcfa288ea2584915d206c5127512501174d82655de2cf031378e6dfd", + "size": 1450 + }, + { + "path": "backends/piper/windows/espeak-ng-data/qu_dict", + "sha256": "2a35508b6d98457910847cbbacfcfdcde36fa79341cc4ab8cc3b03d9b403b597", + "size": 1919 + }, + { + "path": "backends/piper/windows/espeak-ng-data/qya_dict", + "sha256": "e56ae297ebac26ed6c77a9718812e087f0bd198241dac3c0d964f36510984783", + "size": 1939 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ro_dict", + "sha256": "b14f68866b0440c4b0d95fb7da3b7ffd3c59b693e8593d188474fabfc9a1117d", + "size": 68538 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ru_dict", + "sha256": "f0f6181bbbf9e53cd1e8f9d26bde8fc62119c4f78181948f961cb29866e5e585", + "size": 8532392 + }, + { + "path": "backends/piper/windows/espeak-ng-data/sd_dict", + "sha256": "95cf1a258896d836101d27f50beb78f1c2cf1af7e26db69d0c0a82c6198a729f", + "size": 59928 + }, + { + "path": "backends/piper/windows/espeak-ng-data/shn_dict", + "sha256": "369847e6d45db63b3eee9b73d6eaaf0aff692195d1666d345aa287447ea53a4f", + "size": 88172 + }, + { + "path": "backends/piper/windows/espeak-ng-data/si_dict", + "sha256": "a241437d4b64fd328ac4a3e786c9465c60488f6fa31cbba04153f628cdc61f27", + "size": 85384 + }, + { + "path": "backends/piper/windows/espeak-ng-data/sjn_dict", + "sha256": "eb70510144230f6791574d86bae0918b4191b076dd66c555e6ae3d5637daadf7", + "size": 1783 + }, + { + "path": "backends/piper/windows/espeak-ng-data/sk_dict", + "sha256": "58b9bc11c9a95d81e96966242367009f8d02ab7a3f8f4fd77e3581ed4159f0a8", + "size": 50002 + }, + { + "path": "backends/piper/windows/espeak-ng-data/sl_dict", + "sha256": "9cb2bcbf616a5e7dd7ca769e7d222a48c613299ef1d76e44c08b002f8c22861a", + "size": 45047 + }, + { + "path": "backends/piper/windows/espeak-ng-data/smj_dict", + "sha256": "815270ce38a59c192ef5aa8801119d9b3ed973d9e23dfa6da8d5d27ded5a9cd6", + "size": 35095 + }, + { + "path": "backends/piper/windows/espeak-ng-data/sq_dict", + "sha256": "6b74bef94c3319407c27e44d944e765d036a88b30b4c17f30bbc182dbffcc4ce", + "size": 45003 + }, + { + "path": "backends/piper/windows/espeak-ng-data/sr_dict", + "sha256": "770cae9c516e48af7896629faf4abefaf041e1fd4ae184011aad07b97196f613", + "size": 46832 + }, + { + "path": "backends/piper/windows/espeak-ng-data/sv_dict", + "sha256": "565f058b14f1c89c6bfa44fc3700793886a39c7818f1037ad1f4961fa7517509", + "size": 47836 + }, + { + "path": "backends/piper/windows/espeak-ng-data/sw_dict", + "sha256": "375fab2ccd0d669177ad406b20b0cdb5851a1e86f093672f24f72c29fec8a815", + "size": 47804 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ta_dict", + "sha256": "8f3d855c8d7a35ad0e2b22bc7c15a8830e5fc235492894e5db40458655076ecd", + "size": 209553 + }, + { + "path": "backends/piper/windows/espeak-ng-data/te_dict", + "sha256": "640407c66a5bb975671243325de616c221f90bb2842d3a36278facb68c0772f8", + "size": 94837 + }, + { + "path": "backends/piper/windows/espeak-ng-data/th_dict", + "sha256": "edf7ea9bf45606da18305082ab35b842642941946b5c2ea4203238a0be646a2e", + "size": 2301 + }, + { + "path": "backends/piper/windows/espeak-ng-data/tk_dict", + "sha256": "b3cd4a246f014f0564ec9efae79731c0fe73ffe1cd1617359dd8d5344ee3f9c8", + "size": 20868 + }, + { + "path": "backends/piper/windows/espeak-ng-data/tn_dict", + "sha256": "67f68dc336e7c65b946c95414cc8ae96cd10d241783bf857b3f990ac73221b2b", + "size": 3072 + }, + { + "path": "backends/piper/windows/espeak-ng-data/tr_dict", + "sha256": "311b0557059f2dab5ee2faabb51361f5033368f0e2a54c4bdfe45e730674f26e", + "size": 46793 + }, + { + "path": "backends/piper/windows/espeak-ng-data/tt_dict", + "sha256": "60d138d38330e44a78b4224cbea452bfca22bce6734c8ce3f640c0216ce14001", + "size": 2121 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ug_dict", + "sha256": "7c4567ffe6bd8d5224b6b3fd8fab65b1343254636e6228d738ae3a708428e8e7", + "size": 2070 + }, + { + "path": "backends/piper/windows/espeak-ng-data/uk_dict", + "sha256": "a779bb0f51ce5cebb650a4e2282162fc8c44609210a2baa700f81f3e83e6e10c", + "size": 3492 + }, + { + "path": "backends/piper/windows/espeak-ng-data/ur_dict", + "sha256": "c9b152fbfb134f6281de8565bd4f41aef006f57b278c03c557dd5feff6e0dbec", + "size": 133556 + }, + { + "path": "backends/piper/windows/espeak-ng-data/uz_dict", + "sha256": "790d42ac417b26dd7b1bbfed4be72329c16ed1839dd75f55f7c962eb7bd5857a", + "size": 2540 + }, + { + "path": "backends/piper/windows/espeak-ng-data/vi_dict", + "sha256": "9c108de08a16543f97441af6e4ad8652f7f0e86f578c91bfaf21d4826ce72e87", + "size": 52608 + }, + { + "path": "backends/piper/windows/espeak-ng-data/yue_dict", + "sha256": "1d26afa203034698772107abfff1b53acbee30434700a4d2b75dee98951588f8", + "size": 563571 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/eu", + "sha256": "a16e4e4d9b910a6e25a2a55913c812badc4fc3cbece0b8d49a64af112782d5a5", + "size": 54 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/ko", + "sha256": "c2dfbf88d9aa2ac9bf2f30e92e260120e4f2c21123a02265014ae98d59d5774e", + "size": 51 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/qu", + "sha256": "3069e443ecf3421bcb3b35b864e1a89fa9465af072f86eedd46a415990aba6e6", + "size": 88 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/aav/vi", + "sha256": "3199c980f9e23a88a2aa693cd631bf4fcb0f3408c4272bc01b7ac0ff8e79d778", + "size": 111 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/aav/vi-VN-x-central", + "sha256": "b351f13c6ed0da37561df25f45579872ff9863920ad66b94e27327a2c611755b", + "size": 143 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/aav/vi-VN-x-south", + "sha256": "c0a0a2c894cd57b0bbacf9ef4efb8d89bfaf9feb164830e1539f599e7cc4e021", + "size": 142 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/art/eo", + "sha256": "0254b93c8af79d9666a7667ca8b33b21a5453012da4ea0acd79660a9695f89d4", + "size": 41 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/art/ia", + "sha256": "8fb11718030f8b29146f773ca462b838a6c660f166933eb6f1498c361f2e958b", + "size": 29 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/art/io", + "sha256": "8e8391c7f40b7f9d2307faff8018a40dd2b448df4bd581a2950ee8393a00cebf", + "size": 50 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/art/jbo", + "sha256": "d9529de82dccbd95f83c1de401f4ea661609bde644546e9ef421241b7043c00e", + "size": 69 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/art/lfn", + "sha256": "394874fd9022e37cdc307c3386e81f1c0c133a8e9c5fdff74888b519f2c6d648", + "size": 135 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/art/piqd", + "sha256": "d1a559b714cd8dd0ea9e46c4574e06c4f451957fb17bf39b945a89b9586f8b97", + "size": 56 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/art/py", + "sha256": "59e54b95e8571417f43db37a7fd7e351af4e22a8c250cfb66af394f8ed6c4e9c", + "size": 140 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/art/qdb", + "sha256": "52b83fc4f294580ebabd71df641ab5beec6b308758a3458b497124737589a7d8", + "size": 57 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/art/qya", + "sha256": "78ae3f4d70208a9f69e2c26a6a3b5db28d7e6a58740fae937f51dd7fc9faeb1b", + "size": 173 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/art/sjn", + "sha256": "61d85974b6841dfbfcc8cb92ad0742696cb87a6a85d5a28fe99895e46ef9de6f", + "size": 175 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/azc/nci", + "sha256": "7bf70cb18e969398f74428b91c694a4483eaebd6733ecc437eb8714bbf7fff76", + "size": 114 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/bat/lt", + "sha256": "7691c7ca1e79fc9728c70a0d22830eb113a0124bf96807e2a1ace3c6bf0c2f89", + "size": 28 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/bat/ltg", + "sha256": "be716cbbdcb5fe319eed88abd4a44c3329a06e1dc468c8363d3f43cca2683a11", + "size": 312 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/bat/lv", + "sha256": "f08a801dacccace62f5b90166428e90e088818d92dd5f9333ebbe834b0f21c16", + "size": 229 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/bnt/sw", + "sha256": "271dd28b3ec41adeea53f1146249ba6cab61572963ab2743041114d95784e368", + "size": 41 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/bnt/tn", + "sha256": "2243a1fc208dfdc37e154e837c0e782afc325d4acfbacc22718b7f05583ace92", + "size": 42 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/ccs/ka", + "sha256": "68788b99dba7b82cdf8fc9dd05134bd72cf21f0b4c07b13b67b6f036e6294cba", + "size": 124 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/cel/cy", + "sha256": "67bc33b4a66041e6343dfb98e1950d1089eecb68731f346fcc4f47cd07e79970", + "size": 37 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/cel/ga", + "sha256": "29c2cc5736ceb2bd9ef745694d4abf7ed363a33569dfbe745811d4703b7b6cce", + "size": 66 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/cel/gd", + "sha256": "7da0ba464579c367089109826ec15c2584a17417cca4a2c66dae0318d33e3128", + "size": 51 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/cus/om", + "sha256": "b7c563f1effd83c08f3d229fb5a13d074513b85165ce4359a674ef30ca71786b", + "size": 39 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/dra/kn", + "sha256": "c988a5226fa27ee6768b45566ce999ced9a4b68f8e79232b323d5903f4bb2258", + "size": 55 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/dra/ml", + "sha256": "aa7e1f1f239ba55a8a79ed1f3544e9a51b94efe53925de60d4ff737273660b5c", + "size": 57 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/dra/ta", + "sha256": "5bc213eb163bbf069cbfe4f2e9f7e7db71a719d8c0a96e95401d94c992960ed7", + "size": 51 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/dra/te", + "sha256": "176ca4a81feb2181393edff3e2d5c0253cb52239392c4b809c61501d60c69811", + "size": 70 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/esx/kl", + "sha256": "d08cf6ff60b99e6c2ceda549bcbb1a57580915d6a4d50c7f0f5d75289d9fb45a", + "size": 30 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmq/da", + "sha256": "e8dd5e0d30ff0694a7ee839faa8ed35c015eb927a737aba1f120c00005692eaf", + "size": 43 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmq/is", + "sha256": "16c077e6222acfc7702046fa3579bb5d6b57ece98646229e13be2233f9fdcbd7", + "size": 27 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmq/nb", + "sha256": "d47966621312fb95570539f446982f6deb98fd46d9b71fec4a0005a6bace8f46", + "size": 87 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmq/sv", + "sha256": "02727d3b25fc82409bd2cd1ebb444fdb2d5d3f3de7f76b95fc9ed9164c3e59c3", + "size": 25 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmw/af", + "sha256": "fb8ccc05f5eae1754b5aa7e92cb919defdd0810b70eec19f7c77cf836e51db09", + "size": 123 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmw/de", + "sha256": "f3cca92f94b70f8c25a29ee0a4c9ce4c7f1022241532e0647fa2b7f698bf104e", + "size": 42 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmw/en", + "sha256": "4605d5330801de3641c6e366d15f129ea1f5ffbce8722642aba01ace07ab9c83", + "size": 140 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmw/en-029", + "sha256": "faeb8cb201056775f733acbd908c66555fe57bf01cb063920f178f389cbf85d3", + "size": 335 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmw/en-GB-scotland", + "sha256": "1ce4282c1f4385dbaf0035e799b2ab1ee9e9a3ab4829100594a3047727b52353", + "size": 295 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmw/en-GB-x-gbclan", + "sha256": "2040e176f1f7f27bdce81c32d7e2b1662449d54d264afd3c0ed871b8e42b6e46", + "size": 238 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmw/en-GB-x-gbcwmd", + "sha256": "927a5ab891c65b30b4426b151d1623a01024f6b773a38497ba1c90cd77a95747", + "size": 188 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmw/en-GB-x-rp", + "sha256": "d0625af7f58561b1b8cf96fd7f93eee6553bcb3eadb9020ae0757bf96e5115e5", + "size": 249 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmw/en-US", + "sha256": "41534c2a22df5dd4f1052ff9e1a33a3ea7bff5a26b5c02bdad5ba8ddb7524704", + "size": 257 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmw/en-US-nyc", + "sha256": "e8ee9168376d1f7d9e7dcda7c4644c36c529a8bea816e6380895364db46ea2e3", + "size": 271 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmw/lb", + "sha256": "071beb0e769afeba32177796fc0f1386bdc89a3234bed2bf720e76cbf73f5b99", + "size": 31 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/gmw/nl", + "sha256": "56ae04fae28ae9371ed6db28193189e4ee962ba8236f0bcdeef6a06ea4dee374", + "size": 23 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/grk/el", + "sha256": "0b529c998af07c72bf516b5750709464f6258c56c6916b8b8f0c843be9836fca", + "size": 23 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/grk/grc", + "sha256": "319f950b1ad7b1f37efaa390540bf957bddac16d0557a6c3adaff27e0c64e12e", + "size": 99 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/inc/as", + "sha256": "3f58f5628f792b00a5ae6e8fe3bf8696f64af2838e5723799ddbab6027a0b378", + "size": 42 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/inc/bn", + "sha256": "bc3ce23384b9d0bb3bac06a09d90f690fae32a492dcbb030f0e56ba67b5d656c", + "size": 25 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/inc/bpy", + "sha256": "e528d1189b995b94966cef4cc182469cc54f7da8efc5860375c4ecf186ce6146", + "size": 39 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/inc/gu", + "sha256": "f938a755d270745188c2cf2aacba06642a96bb73ba2a0325384121b9d5ec5407", + "size": 42 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/inc/hi", + "sha256": "3c1c3f916f57f2d6d6cbf7923c0f39a36ac2140e2a69ddffce917d7b8bf3ccab", + "size": 23 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/inc/kok", + "sha256": "810ee23fbbe9c186a036bc12f4e5c0de674a132e2e099c9b3dece2a1c9cdb28a", + "size": 26 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/inc/mr", + "sha256": "29063ff3a5257d0d3d7c215dfd4c14deed39e1d48f15894202e6712459720efd", + "size": 41 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/inc/ne", + "sha256": "d53fda082ec174ef33ed3c3468fc973c43b4ef71aca8c1a28cb344668f4838dc", + "size": 37 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/inc/or", + "sha256": "9b52ede7d19659f884854de3dff12597a96f4d6ec7cb4146d2f8dad1453c742f", + "size": 39 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/inc/pa", + "sha256": "eed3cf477f81d13559dbb367256f9532b20dad1432c15b206c777217b8f570ca", + "size": 25 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/inc/sd", + "sha256": "65bfe0e37799029d42a370eb627cca5aa4108b967fba6c8141878d693c903619", + "size": 66 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/inc/si", + "sha256": "8f0993b35ba5b075c38ae76640971256e560abfbd721b608d16a48588932596d", + "size": 55 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/inc/ur", + "sha256": "5b7af7474fac265c7bd7d269111994a09610f954690698f55a8613f3fa973318", + "size": 94 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/ine/hy", + "sha256": "b6ad6129079c49ca915c3005ecb74a02bf5d69cbbc8ec3f8e0f6b949cb3a768f", + "size": 61 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/ine/hyw", + "sha256": "26f8a863c74b3d19c9c42fcf16ae5151c12ac52ef8fab36cae1e5f0d4ad30e6a", + "size": 365 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/ine/sq", + "sha256": "865fde9017b5af82acb844e5f9793998fed422a208ad8aaf9dedacad3bf27af1", + "size": 103 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/ira/fa", + "sha256": "0dc7e93be4d1fbdf7ff52de2fe8bcdc23304e6d86e012b4ab8276d626b5d980c", + "size": 90 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/ira/fa-Latn", + "sha256": "a04cf8efed8db1ac33b306f1f8c6f42733dace87c9fc9f34a2d227b3bb291e28", + "size": 269 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/ira/ku", + "sha256": "57db3a210d73412a49e961dbed9b27077f5c7b967a19a0eb9f9363ea4b16cea5", + "size": 40 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/iro/chr", + "sha256": "f0e2fb8549306c07f571d3e77c89b1d3e9022d448d58e5d424441e01ccce88be", + "size": 569 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/itc/la", + "sha256": "b70d13a264352eba2d22a5d0ef21730849509d18f7277320b008a56decc6efc2", + "size": 297 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/jpx/ja", + "sha256": "6fd980445bc52e0f593062929f5b62831448abaae2a92739beba49df27a79743", + "size": 52 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/map/haw", + "sha256": "b19f22efb6714354d8d1360f449e05f0f84d50d6fa3c98fcfd619ea1b4ba8996", + "size": 42 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/miz/mto", + "sha256": "e33aba5e8159730f62004b22518d627c796f7d039431e5d9217b498f10341976", + "size": 183 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/myn/quc", + "sha256": "76a75b77c672c432b56b84d517310d6aa75c4b3f9de580e290199804366174ea", + "size": 210 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/poz/id", + "sha256": "ce1fcddab3e756a7cdba3dd009ad24cd57e1fde29ce395698355da24e32a6075", + "size": 134 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/poz/mi", + "sha256": "2146cf3868c79b853bb5e41ddbf611d51b4b0a14087199f16194313562b98987", + "size": 367 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/poz/ms", + "sha256": "8a965182ee0c5a41357197c05cf047d21b96744775a57a344d831a961e30351b", + "size": 430 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/roa/an", + "sha256": "ff8aa97c55286cc90531cad5df3cd36165abba11502a84d53b0fcd923f965f23", + "size": 27 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/roa/ca", + "sha256": "787df034320467236292bb46f1f8781a2998a185f117d350c018b6cfcf360854", + "size": 25 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/roa/es", + "sha256": "966aa015ea5646d79f0ca4807cf5da7339aabd3782b55cfa5eb0d8c3fc8fc588", + "size": 63 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/roa/es-419", + "sha256": "ff0f84457ac40e3bcf61d6de58028312be74e0e6cbc3b742f31b3ce729c2aa0c", + "size": 167 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/roa/fr", + "sha256": "95f44834b48c075dad13eace54d2c98ff79b81aa0074dd67eebaf66c2909eef8", + "size": 79 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/roa/fr-BE", + "sha256": "72496a316e47b997f893500b8953dc01a10b86a2d4238e13c1260f50b47d792a", + "size": 84 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/roa/fr-CH", + "sha256": "b449b0fd99d9cd93261fe1c6b816448b99b4a10420bee3f301e669568ad0de55", + "size": 86 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/roa/ht", + "sha256": "389bdf3e984a2f93b7060baad82452dac65b83bf4482d3884525a176ae0e0524", + "size": 140 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/roa/it", + "sha256": "0d9069eb9a96db1c55c131b2bb7d1f5255c68fbddc2199ffd3295b52519a3256", + "size": 109 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/roa/pap", + "sha256": "41a5b5e91482a5498a66711c05873a634bbd418786b0ba037032df846344cfc1", + "size": 62 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/roa/pt", + "sha256": "a44060072f790e41912c57b7d52f7b6438c4383e0dabdb052f6fdb7aacc23493", + "size": 95 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/roa/pt-BR", + "sha256": "84dd4e34c970fadbe32211eadecfdfaab52432479c0911e43300da88a33398ba", + "size": 109 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/roa/ro", + "sha256": "b12af10ffc3f89cb87767ca701c6fea0def8a712de63426a9c3d0bf2432467f8", + "size": 26 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/sai/gn", + "sha256": "ff82d2d163d3e06adb580254d89012e689b2615c46579883562838ab401fb5d5", + "size": 47 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/sem/am", + "sha256": "5970b16bf865528ee96a78c08498244bc8f54218fc2add499716b0e0c09969fb", + "size": 41 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/sem/ar", + "sha256": "43f418f8140e5a7bd7096963da20ccba9247f6be1fcdb08cb53dd7967dd72eee", + "size": 50 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/sem/he", + "sha256": "de9195e743b91fb7fbef7493bd5ddccad14c380a58d5aeb7c1d11d3419ef3b86", + "size": 40 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/sem/mt", + "sha256": "d23f38fae3253b6fb42e3336760ab482e676893692455465448bc9fbc0ac3e83", + "size": 41 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/sit/cmn", + "sha256": "a130a38e3b622735c2401cb37f6719f46995e84a133729185cef9a543ec0b072", + "size": 686 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/sit/cmn-Latn-pinyin", + "sha256": "14c2fc097db280416b688c2bf97de2e5da532c279754020fde7bf6cd624cdd10", + "size": 161 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/sit/hak", + "sha256": "c03c30b43e0b0abdfa689445baf9f442664a0373154a5d0ca75a33d30f46aeed", + "size": 128 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/sit/my", + "sha256": "4b5ae2566cbf30aa59207020469e09832e66a7883818788f253996c534303dca", + "size": 56 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/sit/yue", + "sha256": "83f3dac92af5b4aaecc119515698a6979524be236b28ea8f1024ec707d99ea5b", + "size": 194 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/sit/yue-Latn-jyutping", + "sha256": "c56d45bb9a9cd97efb38edabe31c7dd3feb0518cc900aac6a891dcb6bd8eb428", + "size": 213 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/tai/shn", + "sha256": "f16bf2de219d3ae9a6aec263ce9c684d9cd3e61258d04c7948db0757c67e2c96", + "size": 92 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/tai/th", + "sha256": "6c8ac809868de659e06673f1fc2b56dbf4637ff9980da7a4726b846478b4cfe6", + "size": 37 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/trk/az", + "sha256": "52b424332de4c443f3e9c5ce4de5aed6fbfd421269fc825756dfc6bcbd7e1ad8", + "size": 45 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/trk/ba", + "sha256": "a0cc3a06293962093c66533f9c1cffbccf083a2eb666751f2778a02a5316d98c", + "size": 25 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/trk/cv", + "sha256": "94baf6bacf581cc20337471c599f85ecbbe211f0b8476351ff19450c6541ea92", + "size": 40 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/trk/kk", + "sha256": "2219f4d6de0fbb1dc9982157c6f3cc5f4c8abe5fcb605908a9d1054b9880cd51", + "size": 40 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/trk/ky", + "sha256": "8239dfe8bdc6d4725880f5192b4eaf645f839e40cadfd145a4409c8387ac2d59", + "size": 43 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/trk/nog", + "sha256": "e0e27fe61d406b6f4a4879fb8f81a14bc65997106666933991b818c7d79fabc1", + "size": 39 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/trk/tk", + "sha256": "d78263398c135119909ad63a3ecb6ffe3aff08383212934dd03695222a9786d0", + "size": 25 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/trk/tr", + "sha256": "490eb5a2f777a2f3396f3a824ed8021bd77a94be801214411461bde6689738c9", + "size": 25 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/trk/tt", + "sha256": "8b9617a9b6c56f4a2fe2f59d9ce55426837196567962716f9330f70948f8f9b0", + "size": 23 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/trk/ug", + "sha256": "65d0cdbc96efab4d9e3cd0095440752a31dc7f425edd3ba0a53c4dd33eac5d63", + "size": 24 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/trk/uz", + "sha256": "04fd711dfda1e00d17dc8b1b250c24d70cc3c498b27e35d31065ac47441a6759", + "size": 39 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/urj/et", + "sha256": "59c77747c4774e54a63ab74d2c6a95bc3c5b5b0a50d3781f187bd1fc3a613de5", + "size": 237 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/urj/fi", + "sha256": "61f58ec3bba5b55a392aff7ecf8a84d024200aeaa404b5553fcfb47770936928", + "size": 237 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/urj/hu", + "sha256": "c575421d2ae397065d35af0eb4f79dff4e85f3435ccf45da874f46b3fee1338b", + "size": 73 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/urj/smj", + "sha256": "3d709dec77bb633af0d57b89668dbaaa72d9be8688118d6909f7d457bec12a50", + "size": 45 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zle/be", + "sha256": "5beb002d15a36bf3d0d764ed0d483c75a1df6909eaa3b10fe5f6546700c9bfec", + "size": 52 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zle/ru", + "sha256": "9f52d00a279aaeaa45a786b4fd3a98b34e95fedbf823f8cdb9fecc1339751d3a", + "size": 57 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zle/ru-cl", + "sha256": "200ea345b505ec9e7b8838490aeaf1822af41033429aeae4ea68159047f59f90", + "size": 91 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zle/ru-LV", + "sha256": "980e379667ccb94abe33b9c9c660c08800e3f31e36fe4a939551398ab98457ae", + "size": 280 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zle/uk", + "sha256": "df3d1a9f77bac0531e72f87be398d9eb1e903bd6f839d4f04ffec5abb4e34634", + "size": 97 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zls/bg", + "sha256": "9b91accccb0d53bb2a7bf21922846ba58c74bc082ba71ae32bf12cb0d092f853", + "size": 111 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zls/bs", + "sha256": "0820e7c78b68fbdaf86d83c782cdde1901b642d1cdbeae4181c6f5f5e21e5e89", + "size": 230 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zls/hr", + "sha256": "cef3ef0411c66f154e6c82f7decd95beffa8cbfbe953335658df9f125bdd6096", + "size": 262 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zls/mk", + "sha256": "6dbc2cfb8999141bd0782cd463d4880468161088190c6b4c9412f43552dc39e2", + "size": 28 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zls/sl", + "sha256": "e1c2bd1f75c0eeb86f64182472d5f597ce23e64ad7478c248c2f75b694655942", + "size": 43 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zls/sr", + "sha256": "0e076528d3566f6e59b807e16e274dee505e88a6d477da188ab8ec442eae44f4", + "size": 250 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zlw/cs", + "sha256": "1477cdba5ba3bce85db559accbbd8a24b86b3f6d9d9988bc587e89357c1c4431", + "size": 23 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zlw/pl", + "sha256": "83c586a2b724b24b9f98d21ade5022112c465ac446ca09930286980ceae1c781", + "size": 38 + }, + { + "path": "backends/piper/windows/espeak-ng-data/lang/zlw/sk", + "sha256": "f46ff673c7d58c1646096c174d58da34047d893d0d2993dbca260ce54abe77ef", + "size": 24 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/adam", + "sha256": "b21efbf56fd7d58470e7ce4759c6ef9f23949706b269f2d777832204658ee0dd", + "size": 75 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Alex", + "sha256": "1cf9874567f4fa8109329f084e9ad9ed788fb2362298921d0a04a57b24f1141f", + "size": 128 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Alicia", + "sha256": "6d3f60ec866706e9d4ab7da2a79eedb8c11b50f63620f87e58317f316ea3d58e", + "size": 474 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Andrea", + "sha256": "6ca52734ec2cf7c419d2d3ad7c3449cd1a1776342c4d152ea12c5b1e72801e89", + "size": 357 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Andy", + "sha256": "0e13ba1a940cd9df7e65358c36445c9740f3d75f1360c7f7c71ab9f74b45edca", + "size": 320 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/anika", + "sha256": "228771ce84cad16a089e0b23efe2e69edf25f1ab7c0cb3a50d366ac70e82fbbd", + "size": 493 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/anikaRobot", + "sha256": "3ba724be6aab2fc1a084b25cdb16f469250eb9de4c6c0924a18788d0246fc32f", + "size": 512 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Annie", + "sha256": "40f0f829c5c6e7abf4a02a21f128a572756d6f6693014c26d09a5c29ede5ec33", + "size": 315 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/announcer", + "sha256": "4af6a6748c720cbbaac1e3dcac17bb3bc7a4c553499ec7ee69e054db51a50de3", + "size": 300 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/antonio", + "sha256": "c90477080cd9aa9c384770c0ec18901d01e9851f1bc7d15526a2565a3b0c4da5", + "size": 381 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/AnxiousAndy", + "sha256": "7dfe126f517b76cc78269618dbee2c30d324bea9460b400938756ec93995eaf8", + "size": 361 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/aunty", + "sha256": "0aede4d1ca614f32c348f3d728eab58322ccd016e8531234fdc3dfcf00e32bf1", + "size": 358 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/belinda", + "sha256": "9bd791b7115820e25ada82b4911134d5116e8238b4fd4d214dfccf162576141c", + "size": 340 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/benjamin", + "sha256": "3fa10b0d865fc751fd9c8ed1dabac4bb9b011ea2b95064d4d25a8c719f7785d8", + "size": 201 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/boris", + "sha256": "f23d0a6ee2eeb7e3340506d61d422b691fb04f4e98977ea15c1e0ee5715846d5", + "size": 224 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/caleb", + "sha256": "bcde145ec713eae6d3451a6f25dbbdc0b6756fd44e9e092b7aef89f2cff25be8", + "size": 57 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/croak", + "sha256": "2906a2c6f7b7687db2f1c862164a9b80435d5afc3ba16d74619728bf8c3a8ca7", + "size": 93 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/david", + "sha256": "50b5cb5f61a50f5ff5299dd22e5d645446746dbc36909595f6ae47bac0bda9c3", + "size": 112 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Demonic", + "sha256": "241b0e78362bd3208bb586b65bdf52330e1e8322f8f226c4790c4abeb47cfb09", + "size": 3858 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Denis", + "sha256": "a09fe9f7a4e478d2ba1e139c2995633e2b99de9abdaaa82307954599b0e3d6fd", + "size": 305 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Diogo", + "sha256": "a4a17c453d13242b48783c247816bff8d2cf7bc8931c3f8237887d88a2881b18", + "size": 379 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/ed", + "sha256": "12ddb6355b40a2e39a3b6cef66a2bf4db15a25045769b330a9a3b344a608026d", + "size": 287 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/edward", + "sha256": "b7a12bcb2af280c40e4be4280b675001024619ed529262dbf3e819db53b5b310", + "size": 151 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/edward2", + "sha256": "59d166f7adfee4515a492993405e95432889050aaa47b6cd7eaeeb457fbca227", + "size": 152 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/f1", + "sha256": "7cdef453c6b0391d50a02b8f35392e6f41249cea65df1021affd68a64917b85f", + "size": 324 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/f2", + "sha256": "62b17f6c8f94add0310db032f5e463c13510c5ff83ef1f21e8894ccbf00b00e3", + "size": 357 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/f3", + "sha256": "d0342ee8ceebe3047831774634d234e90281ec73ce2aed8dd0bdd915ccf500fc", + "size": 375 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/f4", + "sha256": "ff9e2907a818920e3b976232c90eabaf270fff0de0b8a479af9a3633e9b7921f", + "size": 350 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/f5", + "sha256": "6c76406d94540d59dee16de11520c37124a1f6dfc236981e483877075f0271b3", + "size": 432 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/fast", + "sha256": "fa2f9c2918f351499037f6bffb25294d9b781b2785d9772a8ae3293c0c0037fe", + "size": 149 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Gene", + "sha256": "06258dae680cde2122a4939dbd4f23ed4ea145b7475068bf7dd4d466090afd2a", + "size": 281 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Gene2", + "sha256": "bcdb3039296eea37398e4423dc9bb6849cda9c5c812b83702ebc49682c649f57", + "size": 283 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/grandma", + "sha256": "e033d57ba9b098971fdb995c13830f9c709206add081c586daaec323ff69a392", + "size": 263 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/grandpa", + "sha256": "f25627887b95e8bccac5e6ce99cb97c6e34290ff0bf919febe64a9987138796d", + "size": 256 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/gustave", + "sha256": "5fa89cf8625cd460b9309748eca019ab5b325a04dccbd29f1a9b2a00caa2e848", + "size": 253 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Henrique", + "sha256": "5c6bf1cf5b6bc7c7d48f2a8368ae348c6ff64232c047f40f9d9a6e6824b4ce6a", + "size": 381 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Hugo", + "sha256": "906acbef890dc4da67cb54b5b0ddc2cab7ee308a95d350c899f0b0cb6a83dd5f", + "size": 378 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/ian", + "sha256": "c0722c7954d3e6582e3f5b3f584140675ab060ac3c6cac5dd11a74592db5550a", + "size": 3168 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/iven", + "sha256": "10ab9de8365ff7bf5a20621d08f1a2019d4bf5521f3c6f65447c9256057406c8", + "size": 261 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/iven2", + "sha256": "f94830e5abe118dc4c0909630d66637ba2a7c94957279c31fd78e313374054cd", + "size": 279 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/iven3", + "sha256": "27ccf2d2f7bb8f1ff28c705c39cf6ff575eb2458c39a518ed33741ad9c1af252", + "size": 262 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/iven4", + "sha256": "11b3d46c01d4307eedcb60bd8dbcc73a40f3eb86c289176d6fd5bb418223b92f", + "size": 261 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Jacky", + "sha256": "41884ed9b66160aeb5a0dc418370a59fe39ead0013d93e023e6d607ef01c1a6e", + "size": 267 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/john", + "sha256": "bfbcc1cec33ef0581fdeb4955fbcd0d824a5d4be5493d731e4a2ef7f8f3ba670", + "size": 3186 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/kaukovalta", + "sha256": "b9f6016145835c3999bcd5872b8a53442729bd903591781ea3340746b3b4956b", + "size": 361 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/klatt", + "sha256": "9abb6d05d0ca354831c570e14323dd4e821459b53166d96395875a48bacdbde2", + "size": 38 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/klatt2", + "sha256": "afab662a55953532ff8d6f9c615b824ecec87f7aef6790a58c4ccbc4505a742a", + "size": 38 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/klatt3", + "sha256": "f1c0cd147bd656f6c4830b3341c9694483438c43aea15c982dcc1945dbb334b9", + "size": 39 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/klatt4", + "sha256": "3459c15aa8fa7d7218ac34388dfc0833c850ffc923013949308c2f643f3daad2", + "size": 39 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/klatt5", + "sha256": "44397d8282454a2974078d25509caa470bceb093e484e727843a5147b4193e60", + "size": 39 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/klatt6", + "sha256": "7d3930cd97ef7a0732f2e19f53f84e480f2d441d263390647b92bb0b4c645d05", + "size": 39 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Lee", + "sha256": "a1584887cddb40c10e04992fb770025b52e66b70504656094f80975f1d6bbf6b", + "size": 338 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/linda", + "sha256": "df06b4c29f5953034e7815ea7062e356917266f8fab576f76c036045525c8e28", + "size": 350 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/m1", + "sha256": "7f2de023bdfe18651d2d0453d2e7ff5e8ade9f094912a79247336ddc90fd7783", + "size": 335 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/m2", + "sha256": "6eb925eb6691621a6de66c87596c6ed879c80a04215a9a8437dffc28fcfa14dd", + "size": 264 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/m3", + "sha256": "7a4ac872387439814ddd65f5e1ff73017122975911df6b5dc62c5709e6fdb611", + "size": 300 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/m4", + "sha256": "6bef82cea3538da1246b7a2c203206e90155cbb4af86faa8ef03366c3119ceee", + "size": 290 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/m5", + "sha256": "08590ef7ce37630a6875d9cbe200dc473a9054b4184287808a4fd1c8e27dd02a", + "size": 262 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/m6", + "sha256": "fc0e01af458a96d43f55878010d3abcbbda85e846922b25361a78d27c3a9ca00", + "size": 188 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/m7", + "sha256": "bdfc54a3558ca98f9ca082479c5211034a824d8721db91804f9d77867db6fab3", + "size": 254 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/m8", + "sha256": "060130540e2fae5f8acfb19bcd4ef3dc5dc8f8d2134c502bb57af277fd4cc491", + "size": 284 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/marcelo", + "sha256": "3238a933a17c40c61cdd385c0e742bf54c42c008f4110d4b0353a6ae72a5d1a0", + "size": 251 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Marco", + "sha256": "04025677360c0270f094bbb6716b83c8284902f6da9b496bedbbf590963835fb", + "size": 467 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Mario", + "sha256": "a94ac65d132030df496e8aa364d5cde5f2eafa4b6b3ae361a2901d512c6029d8", + "size": 270 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/max", + "sha256": "c88f70a60a19e76a6774dfbd7664be639b9aa8aa587c680834c305ffa434aaaf", + "size": 225 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Michael", + "sha256": "4409be23ef5fedf4c8bffbfaa8a802a342824d7e97f1d92938336a25f92f0ebe", + "size": 270 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/michel", + "sha256": "c0068ad9cb792dfb78aedbdff9c3cec68e5083736bae0c33bf19088186101087", + "size": 404 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/miguel", + "sha256": "9c8c2bbe9fba6d1f06eb63ee67d98d7ebb8846b2bf13847a9b359c90ba9fa8fe", + "size": 382 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Mike", + "sha256": "8999b264606c0af23d346a877e10ca05aa757f7ea433e6cfcb3bef50a56ba2cf", + "size": 112 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/mike2", + "sha256": "274ebc5df252b3258e8996f1276137b2cf9d6e5006e1145a5172997284f1dba0", + "size": 188 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Mr serious", + "sha256": "794abd78deb07ce9b03419d20f7744df4c0151d5abff7981217e9d0926a2f240", + "size": 3193 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Nguyen", + "sha256": "4f3d557d9c045080f56022a96e5b3659c4d1caa5fa1d702e12d3ef6bec2e3a45", + "size": 280 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/norbert", + "sha256": "35b9923e15caf36280542270282bd2392c6d86ef5db9a8be7be92ce2241a46cb", + "size": 3189 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/pablo", + "sha256": "053a85c8a6d7e7a0f6432a3158fe73fa9accf06202e5d69c273d4e5bae8bafc8", + "size": 3142 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/paul", + "sha256": "75fb8098bea28859513de6ce033092307382d6f20455e71b051193dfc0793d5e", + "size": 284 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/pedro", + "sha256": "7429792ba833fa2446bec6db790068f5862b9ed099392002cd0e724ec6ab2a66", + "size": 352 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/quincy", + "sha256": "e9945bee7b4f3240221866f497bf2511a886ed7702d8389688a03c2ea120caf0", + "size": 354 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Reed", + "sha256": "727dbc3f6bff0be4dd7d0438f66d3bb82cff68ed148cf52cb58e73314aaec1e4", + "size": 202 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/RicishayMax", + "sha256": "7ddc7a57711ccd7a736540f14271adadf1e2e0422941ecaa7cc43ab87f8e2017", + "size": 233 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/RicishayMax2", + "sha256": "3de5e414d4b2ef082a7b974bb7ede94937e93b5b873d2b2f7928b19d30745180", + "size": 435 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/RicishayMax3", + "sha256": "36d20550b68a8c229d34d956657b7d642929962f7e5d4fddd6396af9e5378b56", + "size": 435 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/rob", + "sha256": "fbfffcdb0afb2e3b70f886c02282feae22509d77fe6e7103daf907f7a356595d", + "size": 265 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/robert", + "sha256": "72604f85afe5bb52a5ce7e3427b196e1e3e426a14a64e2d93b6617e248a46417", + "size": 274 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/robosoft", + "sha256": "ce2820edd349f3fbc734bfe94095518f10ecbefdb304560109f37106f9f4d2fa", + "size": 451 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/robosoft2", + "sha256": "249a44f4069b323640e6b532ff6a9bc30dca35f5f9a5f666b647937cbc58c685", + "size": 454 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/robosoft3", + "sha256": "f8543efe18019c735ebad99d1cab4e06e0efd565ac269a895415dfebf342c383", + "size": 455 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/robosoft4", + "sha256": "d5f728e0d0a9d27f604f34e835e059590fd01411fd467c91328b573c7597bb2d", + "size": 447 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/robosoft5", + "sha256": "39da0efe1768e14eefe05a7019fab416795bbffb222fce23ad7f963991447f7b", + "size": 445 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/robosoft6", + "sha256": "46426971d76c34266580133fad2c26ab829395a71c1713e6205d9d46b0871672", + "size": 287 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/robosoft7", + "sha256": "e19c1045259d9cfb078c229d5d1372f7c5af971d0e2536a41b89c40ca3e23ede", + "size": 410 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/robosoft8", + "sha256": "389230461647836c1b66c51939c0a493e3fbc81e2b7afc01c6f88b8f9da21e2b", + "size": 243 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/sandro", + "sha256": "1b8c36a3ce35a27cbbabf4679df0264cd37f4c79b66aa1f4c310eb31b9c80490", + "size": 530 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/shelby", + "sha256": "5e96565045bcdd5b42e7ad0eab2bbd670c67cf5c9e3f2ac6b50b30580fb92be4", + "size": 280 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/steph", + "sha256": "78186aeddce4c449348f368046b2665375b1a492e948d739249b0c964f082861", + "size": 364 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/steph2", + "sha256": "ca88414b48553fed1fd409389548a94ea90cf282d6340e3cb968c8538a2ac2ec", + "size": 367 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/steph3", + "sha256": "a7d796211b6969ba92213f89de54b053890306af774424ee38fb02ace45e852d", + "size": 377 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Storm", + "sha256": "e817c7d1ce10de527ebc19c0835f580cb584c3ae7d36d30a4f414482602e7b89", + "size": 420 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/travis", + "sha256": "7d5a34be05778ee850d97b0491a62db7683c058d660ff2afc71c08c87360f30d", + "size": 383 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/Tweaky", + "sha256": "c34d1c1d8b7922ceb886b95ad6ab534ca048f9c47e19698ec2aae5cf0c32cdbc", + "size": 3189 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/UniRobot", + "sha256": "23dba52c2d1cb332db6225304818d85488f110a668ddcdbfd7f0438afe37794b", + "size": 417 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/victor", + "sha256": "a880a8083ac5eaadf94f71c63b51f34c1426d4bfe0a26f6761ddec5c6e9a5c76", + "size": 253 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/whisper", + "sha256": "1e3534c843768898cea47a3f83b2fd91720c0775b438ea5c0134676940a66e96", + "size": 186 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/whisperf", + "sha256": "7fea42b10114f1bfd2004181e6b0eb626997cca987893e23d24b5c616aac89d7", + "size": 392 + }, + { + "path": "backends/piper/windows/espeak-ng-data/voices/!v/zac", + "sha256": "edaf508b2cfbfd6088a163dcc78d5f6a26850a7d39a5a0e5128b09526f56b6db", + "size": 275 + }, + { + "path": "backends/whisper.cpp/windows/bench.exe", + "sha256": "3d86e66c1b54e392271cf62d7e560466b67874785819ec2ebf618f09f67d07c4", + "size": 28160 + }, + { + "path": "backends/whisper.cpp/windows/command.exe", + "sha256": "8c6850fdac016eb139c8de27c9ce6f32eeb5f0efb86b120d19a383c3a019e408", + "size": 28160 + }, + { + "path": "backends/whisper.cpp/windows/ggml-base.dll", + "sha256": "017cd9c859d0da3c6d0e8da120ec5641db7c8d1f266df7ce1f9eca42029186ba", + "size": 636416 + }, + { + "path": "backends/whisper.cpp/windows/ggml-cpu.dll", + "sha256": "cb5bfd79c0255e282982527fee42d8aa8407b63ae46ef1acd395c3e21d1f52f9", + "size": 782848 + }, + { + "path": "backends/whisper.cpp/windows/ggml.dll", + "sha256": "722ff1350efe25a1bffa048bef2a8aa7fe7552fce3c38d2c1505f99beb0fb1f7", + "size": 67072 + }, + { + "path": "backends/whisper.cpp/windows/main.exe", + "sha256": "a50301e8d8f4c29d9a2ad608b15dd7d62c320c03fa1800e66ad7c3fc59b49f0c", + "size": 28160 + }, + { + "path": "backends/whisper.cpp/windows/SDL2.dll", + "sha256": "de23db1694a3c7a4a735e7ecd3d214b2023cc2267922c6c35d30c7fc7370d677", + "size": 2500096 + }, + { + "path": "backends/whisper.cpp/windows/stream.exe", + "sha256": "9d08a04de78c18f9703ce1d5dfb43ed4ee0a1e80270496a2f27a6bdc4a240822", + "size": 28160 + }, + { + "path": "backends/whisper.cpp/windows/test-vad-full.exe", + "sha256": "8a65f5f06df373d7ae8e604b40aedcb7c0d48c2a9aa90a9790c55b35971dd8c4", + "size": 368640 + }, + { + "path": "backends/whisper.cpp/windows/test-vad.exe", + "sha256": "5b07666f11368d328da03b4cf714a6b16291005aa783027ee849dfc43509584b", + "size": 368128 + }, + { + "path": "backends/whisper.cpp/windows/wchess.exe", + "sha256": "b8fb0a12b73f458d0494666d9dbd2be4219d5a18a37b7e88374d2dbc7c732c36", + "size": 165376 + }, + { + "path": "backends/whisper.cpp/windows/whisper-bench.exe", + "sha256": "5c16577b47f92180af1475d7699f07071eb536dad00ee3e8fb49d559cccd41fe", + "size": 20992 + }, + { + "path": "backends/whisper.cpp/windows/whisper-cli.exe", + "sha256": "111bd344b7bf0356818f2795a525cb5240ed0a99028fa9f3f1c68b4ff5b17b91", + "size": 489472 + }, + { + "path": "backends/whisper.cpp/windows/whisper-command.exe", + "sha256": "8bbfa8501ffa3af47638071c6c4c2840c9504096d2a4e177fe2b5c424cad9257", + "size": 168960 + }, + { + "path": "backends/whisper.cpp/windows/whisper-lsp.exe", + "sha256": "cd96c405799fdd8d36eeb5097d7c3882198def8c61e7a3367e2475e65c32bb61", + "size": 180736 + }, + { + "path": "backends/whisper.cpp/windows/whisper-quantize.exe", + "sha256": "230a675ef5fb5a25907b430b994f487339e1b91261732dd67b719c9a9291f256", + "size": 103936 + }, + { + "path": "backends/whisper.cpp/windows/whisper-server.exe", + "sha256": "d52adc17f509da58717e853a0c56a281a40847c56a12d01933e227426616eabf", + "size": 728064 + }, + { + "path": "backends/whisper.cpp/windows/whisper-stream.exe", + "sha256": "c9becc2c3d9add767da664b54f00a7f620eb09ff5e240aa16fc8ae7a89ee73c9", + "size": 390656 + }, + { + "path": "backends/whisper.cpp/windows/whisper-talk-llama.exe", + "sha256": "aca10f6ec11193bcbae2f1df806bdfb8e2780128791f566938977b5e70ea5e0b", + "size": 2197504 + }, + { + "path": "backends/whisper.cpp/windows/whisper-vad-speech-segments.exe", + "sha256": "245d897626443bbbf0a93541d2be1df25c75fb87136ff7134bc8b45666c6b5d2", + "size": 376320 + }, + { + "path": "backends/whisper.cpp/windows/whisper.dll", + "sha256": "aecc185550327461d74a7c89436e13a62e12cc408c05719e7a677e1586a9cda3", + "size": 484864 + }, + { + "path": "bin/jackailocald.exe", + "sha256": "d3d8ad7252134d9b1690c3e32c5934341211225acd39ce5c621130e691289084", + "size": 6866944 + }, + { + "path": "client-builder/windows/Build-JackAILocalUSB.ps1", + "sha256": "8b06f3c2f9c66aa012300121aa0817ccd08897db0a9e7b1d4084f0aece1cf29f", + "size": 1796 + }, + { + "path": "client-profiler/windows/Collect-JackAILocalProfile.ps1", + "sha256": "342a16fa3e0923d4cb718820c3ed4e54b3e0354869eb7e076fda1fd695dd77ef", + "size": 1293 + }, + { + "path": "config/content-packs.json", + "sha256": "16e04534d3131fc7a4893a17010c387bfa8a4341aae6a1728af7bc4cbfda7c39", + "size": 1159 + }, + { + "path": "config/jackailocal.appliance.toml", + "sha256": "082ab8ed0cbe4e6b482fdedd6efbb22ecfcaea68748d6d78421d2a7b682ad18c", + "size": 430 + }, + { + "path": "config/jackailocal.linux.toml", + "sha256": "9d25c98875acf34443bdfcdb32600cf82a3a0daa33e03983521ad72c7f8ef5b8", + "size": 434 + }, + { + "path": "config/jackailocal.macos.toml", + "sha256": "ac1559141d51ca1cdbce515cc7314f449b14d2bb9c1bbe0e2cc95f350f3687c3", + "size": 408 + }, + { + "path": "config/jackailocal.windows.toml", + "sha256": "460a5e844e97d85bd6076d6fb130d2141bf0b54066eae6fd52ed5048ccf0c05d", + "size": 434 + }, + { + "path": "config/model-alias-map.json", + "sha256": "34964a21c308249499783281ac0aec11b38d56f01f4cbaf7b937dd8ae4c36862", + "size": 694 + }, + { + "path": "config/model-catalog.json", + "sha256": "1a09b168117150d28b2ab6b598fef8031805e884fc34e54fa031272f5e6763de", + "size": 7701 + }, + { + "path": "config/models.json", + "sha256": "1a09b168117150d28b2ab6b598fef8031805e884fc34e54fa031272f5e6763de", + "size": 7701 + }, + { + "path": "config/update-public-key.xml", + "sha256": "0c8f6ff771d54cba4f793e23a6c71bbb40ec1dc59f5632355ce60c9e4b86f150", + "size": 420 + }, + { + "path": "config/voice-assets.json", + "sha256": "938b7a58f2ac31e0c68ae29a2f52fa140144f52bc01db3255a5b64d082df5ecb", + "size": 2046 + }, + { + "path": "content/field-manual/cards.json", + "sha256": "ff1bcfcb753d23594d4c6efe550767f4e91623bcbc0b920715f58cf3fd72e9c8", + "size": 9041 + }, + { + "path": "content/field-manual/README.md", + "sha256": "b360f3b0681acddc2cc582b7d5e81911f4e6033c587a850c79c2f65f1471302f", + "size": 250 + }, + { + "path": "content-packs/prompts/business-operator.json", + "sha256": "d15c88d0c9a1b15e78a665caeb8a1f210839ef383bdfc605a8318aca6c65dbf1", + "size": 250 + }, + { + "path": "content-packs/prompts/creator-studio.json", + "sha256": "a950d0ee0492c9e057d374240720248498162590562d83ae400d806416d339f3", + "size": 244 + }, + { + "path": "content-packs/prompts/general-assistant.json", + "sha256": "f07888bb2f2cfef84e6b7191b7bca0ca285df4bc0347f2ce608ec2098e431d8d", + "size": 273 + }, + { + "path": "content-packs/prompts/outage.json", + "sha256": "66baed17ddb13cc151ccec5176bfc8d63483347b1c833ed14a3398af7687209b", + "size": 255 + }, + { + "path": "content-packs/prompts/privacy.json", + "sha256": "06cb8d173527c5ffec8b89a1aee8b235350ed2d012125160e75c315b3917e62b", + "size": 246 + }, + { + "path": "content-packs/prompts/rapid-learning.json", + "sha256": "f51cff5475200a7f6bb06196af5c1e50fa9363d0f564c3fa05ff9ae8ecbfd171", + "size": 236 + }, + { + "path": "content-packs/prompts/research.json", + "sha256": "ce610ff286072272e961bfdc6cb9602be9569c94635c1efbe1cb09dee2571b94", + "size": 243 + }, + { + "path": "content-packs/prompts/study.json", + "sha256": "a58f9d65d8802693beb153b16613d7c46e6d5f780ac93297f84233da2cc8f095", + "size": 206 + }, + { + "path": "content-packs/prompts/survival-mode.json", + "sha256": "1135ecfd4d15cfd0bf708cf51332459c0c0ecef105b26d72edcd9165caa973ac", + "size": 425 + }, + { + "path": "content-packs/prompts/translation.json", + "sha256": "ec7b15fadac3fadc2784719f61853382fbca0c4d1fc855003ae92a48eb77d26a", + "size": 249 + }, + { + "path": "content-packs/prompts/travel.json", + "sha256": "9b1e5050d2dee5f53c2675d30e4b0e764aa772f7b00dc29a2c39c410995780fd", + "size": 229 + }, + { + "path": "data/models-cache.json", + "sha256": "756eaf610f4ade1cad6bc9d3b08a43d591d16bd4c3c99f5f2db80fd0967eaf9e", + "size": 817997 + }, + { + "path": "deploy/MODAL_GEMMA_VLLM.md", + "sha256": "312729a5642c9fdd18c565eb5081cd64c93f4a8a63c6d6c6cbcea204d1331522", + "size": 1207 + }, + { + "path": "deploy/modal_gemma_vllm.py", + "sha256": "445a19e6e9aa874653df295ab869b1e0c2c3033e10d335ddfc3f73e31ba64b71", + "size": 1648 + }, + { + "path": "deploy/appliance/docker-compose.yml", + "sha256": "84ea727b14d28f873c2a51e01905f9df4ab4b126683b163a03016405a2b99c17", + "size": 1131 + }, + { + "path": "deploy/appliance/Dockerfile", + "sha256": "f1a6e8969ee9b7339ca43c48a65cedc5011fa5081bcf651db2c33f755df692d7", + "size": 1354 + }, + { + "path": "deploy/appliance/entrypoint.sh", + "sha256": "40f8911074baf4cdb95907ce056e0ecf5b87f465746710230150605cb28b8b09", + "size": 359 + }, + { + "path": "deploy/appliance/README.md", + "sha256": "50cdac5cfcf0e04592bbcbe456e20913dc3f68a46cf519c6a08c4166b59315cb", + "size": 522 + }, + { + "path": "deploy/appliance/show-url.sh", + "sha256": "09c462021b988abdb4931f02eb2f26561eb43525ffeacc046c36cecfd8a4f30d", + "size": 581 + }, + { + "path": "docs/BACKEND_INSTALLATION.md", + "sha256": "f411d87caaf183a762ec8d7c82f75843d8f35da75c9200152a607b9bb9a8c293", + "size": 859 + }, + { + "path": "docs/BUILDER_PATH_AND_MANIFEST_AUTO_CAPTURE.md", + "sha256": "eaac255772cce8c99cdab31afc0530af6dbb53d31d1eabee595031867bef0e69", + "size": 1000 + }, + { + "path": "docs/BUILDER_ZIP_AUTO_CAPTURE.md", + "sha256": "220f39a2e080fd5bdb77c47a0aca46a08982de38c4e21b40b4815faccd4c3663", + "size": 1023 + }, + { + "path": "docs/BUILD_AND_SHIP_CHECKLIST.md", + "sha256": "b86c5ce01303153f577b4b9b2f9fd2f68c2260e41db44e4f8f26821463a8fb53", + "size": 960 + }, + { + "path": "docs/CURRENT_PACKAGE_STATE.md", + "sha256": "eaf9589712800515fe6ecb71c214e5978e9f45cbd5a14c5e42563d66ae4bbba8", + "size": 1005 + }, + { + "path": "docs/DEFAULT_MODEL_POLICY.md", + "sha256": "e2b495c09449a28f96689fdffb0bb06996cdd5435c1711f7dea85c49b2c259f0", + "size": 1272 + }, + { + "path": "docs/FACTORY_COMMANDS.md", + "sha256": "d92c627ea92c0eacafb2a0d196c30233072c28eee5d73eebb69919fb360d9767", + "size": 668 + }, + { + "path": "docs/FACTORY_OVERVIEW.md", + "sha256": "271aa5978ce6d8fd5be584d56d6fb54f54199be9ec56fce4e9184cc612dd00dd", + "size": 1113 + }, + { + "path": "docs/GRADIO_SAAS_SECTION.md", + "sha256": "f14b70d1d4821960d24b9586096438180e70aee6f64be62e9128fd11e05cf2eb", + "size": 2118 + }, + { + "path": "docs/HACKATHON_MODEL_SELECTION.md", + "sha256": "151eee5809c0dcde8e8c635c2911e5fbf3e06a6a1382cd7722bf76cf4af58c17", + "size": 1019 + }, + { + "path": "docs/HARDWARE_PROFILE_JSON.md", + "sha256": "763ad53a587bbb3b21aaef83f591afa10394a67ead0b63ce9e13fb535741af7c", + "size": 1218 + }, + { + "path": "docs/LANGUAGE_POLICY.md", + "sha256": "6b2c156efaeb976b2ebcb9c223996a2fe9bad1c3fbfa4ec4527b285be92bbb4e", + "size": 689 + }, + { + "path": "docs/LINUX_BOOT_FLOW.md", + "sha256": "c0b4219c1203291c594dc8d62861c15ec8570257110ae19ba3dea96dc7248bb2", + "size": 387 + }, + { + "path": "docs/LLM_STORAGE_AND_DOWNLOAD_POLICY.md", + "sha256": "9bf2d93f7bd413186cec8e0fecc64d2bd3b86624ffcb3a60aea111528b03d5c1", + "size": 1227 + }, + { + "path": "docs/MACOS_BUILDER_AND_RUNTIME.md", + "sha256": "40db695aec841ba7df911c4fff9008d287bb08dcb8c58d61154d1d2fb3354cf0", + "size": 1263 + }, + { + "path": "docs/MACOS_USER_FLOW.md", + "sha256": "212b535e8822a314390ed61c4eba97761c2cdeae881d57277d34a96b4da9d408", + "size": 1526 + }, + { + "path": "docs/MODEL_STORAGE_POLICY.md", + "sha256": "9aa6467362f44c74bb5bef858b9a3482bd785bf85406f4745167fea78caed807", + "size": 1290 + }, + { + "path": "docs/NON_TECH_USB_BUILDER_FLOW.md", + "sha256": "b55e4652bad554656d0a3318dd998f6a7b6f539e985dfb49a6a2a5b1571df888", + "size": 1305 + }, + { + "path": "docs/ODYSSEUS_INTEGRATION_DECISION.md", + "sha256": "30f2129d5c7f7906a22a2a36f61cfc35ecd607ce3e1b93c519b568f818c0e267", + "size": 951 + }, + { + "path": "docs/PREPARE_USB_FROM_ZERO.md", + "sha256": "bead8f82ea8cadef26acf59c3d4b20c4025366b85d249f24fee0d47ad6a78569", + "size": 2619 + }, + { + "path": "docs/QA_DEMO_PROTOCOL.md", + "sha256": "7bb677560d672ea4afc02ec38e0d46b85b940d40cba0ad72a459978b17faf3ce", + "size": 546 + }, + { + "path": "docs/QWEN_MODEL_POLICY.md", + "sha256": "5a8d4d91116a5190f633d7aef366bbbd9b246e6965d69ae16f10f1f1cef386f7", + "size": 2168 + }, + { + "path": "docs/SAAS_PACKAGE_BUILDER_ARCHITECTURE.md", + "sha256": "fe857adc14e32bb8cd16488298729d9ccab21c59cd0308569db9982668832826", + "size": 1169 + }, + { + "path": "docs/SECURITY_AND_ANTI_HACK.md", + "sha256": "ee3d0c2f4299982b59488f995751011499ca5ee88953e8354d3cb08af7f3c2bf", + "size": 1424 + }, + { + "path": "docs/SECURITY_CLAIMS.md", + "sha256": "91eb6a1a4ce278e1d29fb573e661e4cfd21bfa1ee37f3e081797058cd782c3e8", + "size": 546 + }, + { + "path": "docs/SYSTEM_BEHAVIOR.md", + "sha256": "60ca58a81eb465563c86e2ab254e95c694c21eeb8fbc55cb3e5aa0d8b4720d0c", + "size": 2904 + }, + { + "path": "docs/TEST_WITHOUT_USB.md", + "sha256": "c24c378cd74135a821f70b2b0ceb0ba1e786579720a54a8665a5f7dca2fccd6a", + "size": 800 + }, + { + "path": "docs/UI_POLISH_V4_NOTES.md", + "sha256": "a5119b8850924c82888fc5d8067ac1a636084ab4f30fa3b6ea6795cdb674af07", + "size": 653 + }, + { + "path": "docs/UI_SECURITY_BOUNDARIES.md", + "sha256": "416709b383190219ea6b92b98fcf03dfaf84bded77641c5790fea97f44f274ad", + "size": 612 + }, + { + "path": "docs/WHAT_TO_DO_AFTER_GRADIO.md", + "sha256": "93fe21ace7a47fc36cba358f760f2fc0b452fd8f23d6ecdb4f76c93723f717ba", + "size": 760 + }, + { + "path": "docs/WINDOWS_PORTABLE_FLOW.md", + "sha256": "ea72606ef6cf2e1ed097f11d07e055f4bf004f297ee8f2e4397b2d42d0be9718", + "size": 598 + }, + { + "path": "factory/linux/build-linux-binary.sh", + "sha256": "b76dd078ce5b53f9b03e54961abd10333147219efabe27bf86fca32d99955238", + "size": 219 + }, + { + "path": "factory/linux/install-linux-backends.sh", + "sha256": "df119bf7a6ada4099f4895868ec22c61b73c9f10ddd9a274e3d03c126a2ea107", + "size": 718 + }, + { + "path": "factory/linux/verify-language-policy.sh", + "sha256": "e22f5729ce416514a39f5a25ea47be3db8917b7c912c54cd39fa9816f4b6e35e", + "size": 1021 + }, + { + "path": "factory/linux/verify-package.sh", + "sha256": "b2f73fcc66ac00030c5962a003a3f54f4fe60f4c66fc9249afe2bd27f484b187", + "size": 891 + }, + { + "path": "factory/powershell/Add-ModelToCatalog.ps1", + "sha256": "06bb25822ff9e8705dee0f6167526a3139b3c57a03e4929d7a2ff7b5a02ae504", + "size": 731 + }, + { + "path": "factory/powershell/Apply-Branding.ps1", + "sha256": "fd15a41d8b654bfeb3fb760ab58ef03d231d63cce82f79a7e9059438314fcd9d", + "size": 377 + }, + { + "path": "factory/powershell/Build-RustBinaries.ps1", + "sha256": "f3d80f3ffc94ac3b548650f62cceda8b2e45124f578c227c40ea930fb4806129", + "size": 566 + }, + { + "path": "factory/powershell/Generate-Manifest.ps1", + "sha256": "12a66b91355ec53395d2635a5cee0982963915acc54b7301bf8a96f401cf31b9", + "size": 1390 + }, + { + "path": "factory/powershell/Install-All.ps1", + "sha256": "986b827431018432e31bea779466c82ca56c46a93357eaa08a9198d328af4974", + "size": 844 + }, + { + "path": "factory/powershell/Install-Backends.ps1", + "sha256": "e105aa472968c099e376ab906abe2ab208c6b10ff3a012b387c7a2a027c4f3c5", + "size": 987 + }, + { + "path": "factory/powershell/Install-GGUFModels.ps1", + "sha256": "b51fd05f5a78535ccb8e40dc295f81fab73f4805f62f25f5368d74af8f3d4b4d", + "size": 903 + }, + { + "path": "factory/powershell/Install-Models.ps1", + "sha256": "efd6d8aac64b98627f36950d0119529537b73e7ac4aff3dc06935fff8ce444d1", + "size": 816 + }, + { + "path": "factory/powershell/JackAILocal-Factory-All.ps1", + "sha256": "b5718c2ed17fecb88e08c99985ebe4020d0e6f7fd5f06e42b05635f9efe0d404", + "size": 564 + }, + { + "path": "factory/powershell/Prepare-JackAILocalUSB.ps1", + "sha256": "28289ef829040c1c17dba7e9f1b1fdaee721e260e3efcc039c9b387d9606bce7", + "size": 5669 + }, + { + "path": "factory/powershell/Prepare-WindowsPortableDrive.ps1", + "sha256": "5ab5354d54bd8c53a3c41c763241a7da6f89c8453a66ae673ba30f05b0f4cc6d", + "size": 994 + }, + { + "path": "factory/powershell/QA-Benchmark.ps1", + "sha256": "d86946876effe6f14284c9586ed9a26454e3f430d06181f5336ae4eb80528b35", + "size": 557 + }, + { + "path": "factory/powershell/Set-DefaultModel.ps1", + "sha256": "01ac48778a50820f823bcec2ed9079350e0f61f100b57a17eea560c7b67e5135", + "size": 471 + }, + { + "path": "factory/unix/build-whisper-backend.sh", + "sha256": "04cb80cdb32a4343621225971a668288dfaf102753ca73170496f3d18c5c79f4", + "size": 1064 + }, + { + "path": "installer/macos/build-dmg.sh", + "sha256": "8281737490d167ca82b06ca800319cc711140c05eaf29819d53f28e161f25ccf", + "size": 2167 + }, + { + "path": "installer/windows/Build-Installer.ps1", + "sha256": "418bddc0dd4787b231525fb215baeeb2dfc9294af478b46821040808a889a079", + "size": 2062 + }, + { + "path": "installer/windows/JackAILocal.iss", + "sha256": "3a0c9a64d62ba29df358351aa1f1d45636beff458c0325d9f610631f28c6991a", + "size": 1833 + }, + { + "path": "licenses/THIRD_PARTY_NOTICE_TEMPLATE.md", + "sha256": "02f4b1c39865b3446911fc15a10ad86d2bdb08852dee8de036c9660cc309dfed", + "size": 273 + }, + { + "path": "linux/factory/Build-Bootable-Linux-USB.sh", + "sha256": "3206a9aee9d41e6aad3196a83244e858d0ec8bfef0dfc7b26484afcef4782cf0", + "size": 561 + }, + { + "path": "linux/factory/prepare-linux-boot-disk.sh", + "sha256": "78a28cb640e5b8279a0f5440e0ee2b0591c13891a523ce0e8d8b333e8afab303", + "size": 3844 + }, + { + "path": "linux/runtime/install-linux-runtime.sh", + "sha256": "db3f13aec8c3a173f1674e0b1c1e0f871c978e7962357d5332e8e5de69c66319", + "size": 1407 + }, + { + "path": "linux/systemd/jackailocal-hwscan.service", + "sha256": "cb171b292a8b3dcce5d1ebe7909ae4b777839f66d321a8a1f1e661f64b4a00b1", + "size": 278 + }, + { + "path": "linux/systemd/jackailocal-kiosk.service", + "sha256": "1219ffb508062f50acfb45e8869a2bc1f5b678f293170f13fe38153605dff95f", + "size": 443 + }, + { + "path": "linux/systemd/jackailocald.service", + "sha256": "38a1106c19cb16862ce96c11487d4f8a2ab8f4ce38716592bc062ea618e1abd9", + "size": 469 + }, + { + "path": "linux/systemd/ollama-jackailocal.service", + "sha256": "f185d82135d4a711ab103c98f9d3d291afbba2338a3f2b721f9814a49e3b93de", + "size": 518 + }, + { + "path": "macos/hwscan-macos.sh", + "sha256": "477bde520e01fbd19eb46495f489adfacb734a07788389f142fbf6c997302ac8", + "size": 670 + }, + { + "path": "macos/JackAILocal-USB-Builder.sh", + "sha256": "d35a1856de7140812054965df81e561eebe6d4e66bf41652beb5e8f485bce27f", + "size": 11392 + }, + { + "path": "macos/Preflight-macOS.sh", + "sha256": "7df520dccb244df955d61f56ed44b647173df4662dccf92d0f197f1bc6bdb508", + "size": 597 + }, + { + "path": "macos/read_manifest_models.py", + "sha256": "7af41bbf64ac169fae0f2e89308fd5ff7f593858e34fe5d03894c53e7e06af08", + "size": 1293 + }, + { + "path": "macos/Start-JackAILocal.sh", + "sha256": "c082d669fa594d56f6e695483562f99a3c1d4ba9a132dc0c60868b39192626ad", + "size": 1387 + }, + { + "path": "macos/Stop-JackAILocal.sh", + "sha256": "ed29d98f7f65fd33ead8ef80c27c6468527eacea13a1e9b9627852339d126908", + "size": 144 + }, + { + "path": "manifest/backend-sources.example.json", + "sha256": "627bc0f36e1e5006fbdb229b06b70db7d650b65f5234ce45de47242c19579a71", + "size": 610 + }, + { + "path": "manifest/gguf-downloads.example.json", + "sha256": "b833a4c8d68fe11df9f7e1a3af582f8d4580323d6ac51917e7afb3c61a68a2a2", + "size": 222 + }, + { + "path": "manifest/model-sources.json", + "sha256": "d27f02abaf9b175b43afd1378c06ac18f538c2eebc7f36433892d3c5ac8e5ca2", + "size": 546 + }, + { + "path": "manifest/product.json", + "sha256": "cd2679704313121c7d837ddb5549373877042643e575f6a48250a43c2d40352f", + "size": 949 + }, + { + "path": "manifest-input/build-manifest.json", + "sha256": "5ab572bd253295a5745a0a892296befec26079e321926a4279d192741f8f041d", + "size": 13476 + }, + { + "path": "manifest-input/jackailocal-gemma-agent-build-manifest.zip", + "sha256": "48c54a3c46b43fcdadb673dab9a644ec77e4c36e2b0a04775fb103cb24f022f3", + "size": 3189 + }, + { + "path": "models/ollama/blobs/sha256-0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "sha256": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "size": 10174 + }, + { + "path": "models/ollama/blobs/sha256-1278394b693672ac2799eadc9a83fd98259a6a88a40acfb1dcaa6c6fc895a606", + "sha256": "1278394b693672ac2799eadc9a83fd98259a6a88a40acfb1dcaa6c6fc895a606", + "size": 7381382048 + }, + { + "path": "models/ollama/blobs/sha256-56380ca2ab89f1f68c283f4d50863c0bcab52ae3f1b9a88e4ab5617b176f71a3", + "sha256": "56380ca2ab89f1f68c283f4d50863c0bcab52ae3f1b9a88e4ab5617b176f71a3", + "size": 42 + }, + { + "path": "models/ollama/blobs/sha256-675ad6e68101ca9413ec806855c452362f0213f2dfc5800996b086fdb8119842", + "sha256": "675ad6e68101ca9413ec806855c452362f0213f2dfc5800996b086fdb8119842", + "size": 175115584 + }, + { + "path": "models/ollama/blobs/sha256-7339fa418c9ad3e8e12e74ad0fd26a9cc4be8703f9c110728a992b193be85cb2", + "sha256": "7339fa418c9ad3e8e12e74ad0fd26a9cc4be8703f9c110728a992b193be85cb2", + "size": 11355 + }, + { + "path": "models/ollama/blobs/sha256-9371364b27a52acac9d87f88bd93c9db1174d8d6ec57f6888925cdc1788871ff", + "sha256": "9371364b27a52acac9d87f88bd93c9db1174d8d6ec57f6888925cdc1788871ff", + "size": 65 + }, + { + "path": "models/ollama/blobs/sha256-9be69ef463066202c1b1bd299aaf42bad370a01ba4b40d293617859720776c17", + "sha256": "9be69ef463066202c1b1bd299aaf42bad370a01ba4b40d293617859720776c17", + "size": 11354 + }, + { + "path": "models/ollama/blobs/sha256-b709d81508a078a686961de6ca07a953b895d9b286c46e17f00fb267f4f2d297", + "sha256": "b709d81508a078a686961de6ca07a953b895d9b286c46e17f00fb267f4f2d297", + "size": 2741180928 + }, + { + "path": "models/ollama/blobs/sha256-be595b49fe22012bd1f5605ec14c7ffa58331783a88a4fd8c22e5fc8ec42cf9f", + "sha256": "be595b49fe22012bd1f5605ec14c7ffa58331783a88a4fd8c22e5fc8ec42cf9f", + "size": 475 + }, + { + "path": "models/ollama/blobs/sha256-c805f5b265d8e695c44f4065dfc368206cd8026447604925fef8db57ee32ee23", + "sha256": "c805f5b265d8e695c44f4065dfc368206cd8026447604925fef8db57ee32ee23", + "size": 548 + }, + { + "path": "models/ollama/blobs/sha256-dec52a44569a2a25341c4e4d3fee25846eed4f6f0b936278e3a3c900bb99d37c", + "sha256": "dec52a44569a2a25341c4e4d3fee25846eed4f6f0b936278e3a3c900bb99d37c", + "size": 6594462816 + }, + { + "path": "models/ollama/blobs/sha256-ee043a99abe5e8317272712ed08ee2993af1f7930d69aefa3eba562cdc2822bd", + "sha256": "ee043a99abe5e8317272712ed08ee2993af1f7930d69aefa3eba562cdc2822bd", + "size": 473 + }, + { + "path": "models/ollama/manifests/registry.ollama.ai/library/gemma4/12b", + "sha256": "4eb23ef187e2c5462566d6a1d3bbbc2f1346d0b4327cbb66d58fffbcc9b2b05c", + "size": 905 + }, + { + "path": "models/ollama/manifests/registry.ollama.ai/library/qwen3.5/2b", + "sha256": "324d162be6ca5629ae4517c8710434d0bd2d665bc94dbad46e9af8fbf8a2f0df", + "size": 709 + }, + { + "path": "models/ollama/manifests/registry.ollama.ai/library/qwen3.5/9b", + "sha256": "6488c96fa5faab64bb65cbd30d4289e20e6130ef535a93ef9a49f42eda893ea7", + "size": 709 + }, + { + "path": "models/piper/en_US-lessac-medium.onnx", + "sha256": "5efe09e69902187827af646e1a6e9d269dee769f9877d17b16b1b46eeaaf019f", + "size": 63201294 + }, + { + "path": "models/piper/en_US-lessac-medium.onnx.json", + "sha256": "efe19c417bed055f2d69908248c6ba650fa135bc868b0e6abb3da181dab690a0", + "size": 4885 + }, + { + "path": "models/whisper/ggml-base.bin", + "sha256": "60ed5bc3dd14eea856493d334349b405782ddcaf0028d4b5df4088345fba2efe", + "size": 147951465 + }, + { + "path": "qa/static_audit.py", + "sha256": "cbab6f7a34866e603e1c1911b2dc70ad404b8f522c3effbee3aced02bcfe7f7f", + "size": 1629 + }, + { + "path": "reports/mission-1780526823161.json", + "sha256": "366bff21a04ad83a04768ec7ae5bdb6cb46bd5ef6f6b6c78bc4ac54cfe9477de", + "size": 1146 + }, + { + "path": "reports/mission-1780526823163-2a611d51.json", + "sha256": "3f66c285dc4d8e6ce16109ee1d7bdae743f20e52ba18a2fb7ac5d3bcc66b6ce1", + "size": 1582 + }, + { + "path": "saas/__init__.py", + "sha256": "6818918df0a6cf5cd3236570ec088e9281b572b46bbe4848374aa4feb6fc0181", + "size": 54 + }, + { + "path": "saas/api/main.py", + "sha256": "ad2fa9e18f7f5b9ff5d66f80c3b502a33bd0ab6db6240a00dab5dcbcc1fbcae3", + "size": 3399 + }, + { + "path": "saas/api/requirements.txt", + "sha256": "ba560a3c64a98d7beaa959974c33de69b938fce9678958b093e1ada5cf9dd87a", + "size": 35 + }, + { + "path": "saas/gradio/app.py", + "sha256": "f71df013234407f4d7f9d5528257c387e7fb779601598eb83a39a610b1b25348", + "size": 154369 + }, + { + "path": "saas/gradio/decision_engine.py", + "sha256": "030fde040e1c8ea51d9a86e0f2a1a2d1b78c05848901e3fe081b410c002659c1", + "size": 15616 + }, + { + "path": "saas/gradio/README.md", + "sha256": "96f0940c34f25d24b235894230a7b97ec9b153b0b2aabc900f0c23431d2ffff1", + "size": 3939 + }, + { + "path": "saas/gradio/requirements.txt", + "sha256": "91b9c415c97ce6bfdc4f3a5f9a626b20328ccc8f3d21eb895beb818bc5578c34", + "size": 64 + }, + { + "path": "saas/gradio/__init__.py", + "sha256": "78f5c6a65f7a5db472d70097eefa037cddc05675b9e6fc457dc20379967adc37", + "size": 30 + }, + { + "path": "saas/gradio/examples/linux-appliance.json", + "sha256": "870226dcb8101a82c5cdd82b365a436b20f2f7e4b03986d2f1b80e2f70e1a3aa", + "size": 120 + }, + { + "path": "saas/gradio/examples/low-end-laptop.json", + "sha256": "f91f2488c32ce6d93148640c971edcea34d53af5aad5d922e3ee185e38f3351e", + "size": 131 + }, + { + "path": "saas/gradio/examples/normal-laptop.json", + "sha256": "48d3c6ff4e4619cac3c18d3475574ad7b60b1c7d0d7511f977b761b15e211687", + "size": 137 + }, + { + "path": "saas/worker/build_package.py", + "sha256": "8eb5eb3855e166ac081a1129b6fe990d7c757318f755f9c6dcaef3940eecd8df", + "size": 1099 + }, + { + "path": "scripts/dev-start.ps1", + "sha256": "54b89f326b715cb7193b6bd0a6ce566162d29339bfa54fa80163d568662ecc96", + "size": 1631 + }, + { + "path": "scripts/New-SvnDevBranch.ps1", + "sha256": "5a45085cd06830a454ee65d08b0d3aac80b489d3dbd2e0c593b9f07f69d1fe49", + "size": 5599 + }, + { + "path": "scripts/run-gradio-saas-preview.cmd", + "sha256": "396e9e60b892f5ee445279619895e9e6af2d1ac1f524585d86ff3b3628152c18", + "size": 217 + }, + { + "path": "scripts/support-bundle.ps1", + "sha256": "f9c7cad51ea10b189e3864ae650f42708db95612b8dc55f53e3937b5ae45e37f", + "size": 761 + }, + { + "path": "src/crypto_pack.rs", + "sha256": "a7454dc222d0dba5e8e6b7bbb4f22ecc98f76eb849be37d9b466ca122f6df67b", + "size": 6648 + }, + { + "path": "src/local_ai.rs", + "sha256": "542df93749c7857919956b0b983a6e022999ed9d6ff3f737ab476f16951fa66a", + "size": 20825 + }, + { + "path": "src/main.rs", + "sha256": "8bea6db8e0d839149c8c35785d0ec9e132ce379726d58643392a4eb72279d0ab", + "size": 39874 + }, + { + "path": "src/phone.rs", + "sha256": "c5a58800e5c0a650cfa69b719f34019baf4b0ceaf561503cac2014ead370e8d4", + "size": 6418 + }, + { + "path": "src/storage.rs", + "sha256": "18398202c66953359d687c78f997cbead4a86b757a14e223811b9cd0ec9e03a7", + "size": 8655 + }, + { + "path": "tools/pm-hwscan.sh", + "sha256": "aad9f8454259f154f71aa6f6ad6d18f496be9cf8e259c79d87bb9f2950dc3006", + "size": 1113 + }, + { + "path": "unix/Install-Voice.sh", + "sha256": "f7c72858615f355105f49bf620ad5ad189e6e7a88aeab1eaa231703cf9ca253f", + "size": 3282 + }, + { + "path": "updates/JackAILocal-Update.ps1", + "sha256": "852d56d1f03b33f714296313c6825686aaee3744c0054c9c16a50a3748204494", + "size": 1875 + }, + { + "path": "updates/update-manifest.example.json", + "sha256": "8a313cd88932d5f3c18bdd6ee138b985711069682dfff1e79e5e7410e01bedd9", + "size": 234 + }, + { + "path": "updates/update-manifest.example.json.sig", + "sha256": "8566bb8126235fcf02149cdd23b2d82516907822e13568a88d9502c6618402c6", + "size": 256 + }, + { + "path": "updates/Verify-SignedManifest.ps1", + "sha256": "4ddedde8b4c357a31699474c9ecea132922734d49cd35f73f735b925124956a1", + "size": 660 + }, + { + "path": "usb-root/autorun.inf", + "sha256": "4aa8e65d16d8af4cf305436a672eda67e4bd527c746cb4ad34c214d50e77e139", + "size": 96 + }, + { + "path": "usb-root/README-FIRST.txt", + "sha256": "1dfb965c582b5a4de66ede7afd7b0fc7ddb0b8ce61addc60e15f9664656e9cd7", + "size": 311 + }, + { + "path": "usb-root/START-HERE.cmd", + "sha256": "7b982deb551c36429c6de7e205262f37365da7e8bbafbb0c12499b2a2b1b085b", + "size": 417 + }, + { + "path": "usb-root/STOP-JACKAILOCAL.cmd", + "sha256": "8d117b322c6057a0b4d804572879915b69a3fb7bab28e6fa2c612ef2f198844d", + "size": 131 + }, + { + "path": "webui/app-v15.js", + "sha256": "97a22fdaf2e5c140faecbe39f226eacb3bc35de9e4a033646ec889aef6c43b50", + "size": 45537 + }, + { + "path": "webui/index.html", + "sha256": "5d1aa1dfc575b292550c96b765a5cf12bcc1c3c592bf381c8cf56da38d81d7a9", + "size": 22373 + }, + { + "path": "webui/styles.css", + "sha256": "cfa6d2fb754150a6579cfe974d6a500320d546178781cd91c43037cb881a8a52", + "size": 17055 + }, + { + "path": "windows/Add-Model.ps1", + "sha256": "4069233d9c6c21ec72eb02b8f0f46ce09d181fce9dd934c1d27144a83fedd33a", + "size": 545 + }, + { + "path": "windows/hwscan-windows.ps1", + "sha256": "cba3d92a13699a9bb61576fe86fc808d96d18d8c3b780e36349fa8c67f2f8b31", + "size": 1203 + }, + { + "path": "windows/Install-Voice.ps1", + "sha256": "721b5d3722fb9028e625918b12113c58651a25a390fa9cbd27f2cc11a4f1f53e", + "size": 3522 + }, + { + "path": "windows/Integrity-Check.ps1", + "sha256": "1164a8e0770c203e847e314ff9a5e4272942ebc0bdb7bb7c586af67a97aea9a7", + "size": 836 + }, + { + "path": "windows/JackAILocal-USB-Builder.ps1", + "sha256": "c9d17103f5539c0450c57482dce748b21b4cfc8f589fbe9dd10ae03988eb5e7f", + "size": 24540 + }, + { + "path": "windows/Preflight-Windows.ps1", + "sha256": "039568c3d1be967b7ee78dfb3e1b1f3cfd1a63233f034df6df68c673c7f04104", + "size": 1322 + }, + { + "path": "windows/Start-JackAILocal.ps1", + "sha256": "e7ed260f5be90400959ccd4f36cbb439a56c11d3982190dae051edfeded0b6a0", + "size": 3944 + }, + { + "path": "windows/Start-LlamaCppFallback.ps1", + "sha256": "04cc7243b8554cfc334594f3609261f75d7f7d90b5ec19068c183c4032cd442a", + "size": 554 + }, + { + "path": "windows/Stop-JackAILocal.ps1", + "sha256": "04ef7ac80ace1c5bde8a8eea05d9767bc1e427ebd0553d8b4bff12e40cb2c275", + "size": 937 + } + ], + "product": "JackAILocal" +} \ No newline at end of file diff --git a/profiler/README.txt b/profiler/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..3b3a9b97e7be235bd1ba84a2f7f36a640e72e87b --- /dev/null +++ b/profiler/README.txt @@ -0,0 +1,31 @@ +JackAILocal - Analyseur de PC / PC Analyzer + +=== FRANCAIS === + +Cet utilitaire analyse votre ordinateur (processeur, memoire, carte graphique, +espace disque) pour que le builder JackAILocal choisisse les bons modeles d'IA. +Il fonctionne entierement hors ligne : RIEN n'est envoye sur Internet. + +1. Windows : double-cliquez sur SCAN-MY-PC.cmd + macOS : double-cliquez sur SCAN-MY-MAC.command + Linux : bash scan-unix.sh +2. Le fichier JackAILocal-hardware-profile.json est cree sur votre Bureau. +3. Retournez sur la page du builder JackAILocal et televersez ce fichier + (ou collez son contenu) dans la zone "profil materiel". +4. L'agent d'analyse (Gemma 4 12B) propose alors la configuration et les + modeles adaptes a votre machine, et le package est prepare. + +=== ENGLISH === + +This utility analyzes your computer (CPU, memory, graphics card, disk space) +so the JackAILocal builder can pick the right AI models. It runs fully +offline: NOTHING is sent over the Internet. + +1. Windows: double-click SCAN-MY-PC.cmd + macOS: double-click SCAN-MY-MAC.command + Linux: bash scan-unix.sh +2. The file JackAILocal-hardware-profile.json is created on your Desktop. +3. Go back to the JackAILocal builder page and upload that file (or paste its + content) into the "hardware profile" box. +4. The analysis agent (Gemma 4 12B) then proposes the configuration and models + suited to your machine, and the package is prepared. diff --git a/profiler/SCAN-MY-MAC.command b/profiler/SCAN-MY-MAC.command new file mode 100644 index 0000000000000000000000000000000000000000..7ed7720217d1547e9b3b1b6970307784f103895b --- /dev/null +++ b/profiler/SCAN-MY-MAC.command @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +cd "$(dirname "$0")" +exec bash ./scan-unix.sh diff --git a/profiler/SCAN-MY-PC.cmd b/profiler/SCAN-MY-PC.cmd new file mode 100644 index 0000000000000000000000000000000000000000..eab0cad9052ce50b3abec210b6c4ffb5d2b9eefd --- /dev/null +++ b/profiler/SCAN-MY-PC.cmd @@ -0,0 +1,4 @@ +@echo off +cd /d "%~dp0" +powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0scan-windows.ps1" +pause diff --git a/profiler/scan-unix.sh b/profiler/scan-unix.sh new file mode 100644 index 0000000000000000000000000000000000000000..8594fefa3a62bcba2ec0694805680d7c5165c7e5 --- /dev/null +++ b/profiler/scan-unix.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# JackAILocal PC analyzer for macOS and Linux (standalone, offline). +# Writes JackAILocal-hardware-profile.json on the Desktop (fallback: $HOME). +set -u + +# Write next to the script (predictable); fall back to HOME if read-only. +OUT_DIR="$(cd "$(dirname "$0")" && pwd)" +if ! touch "$OUT_DIR/.jackailocal-write-test" 2>/dev/null; then + OUT_DIR="$HOME" +else + rm -f "$OUT_DIR/.jackailocal-write-test" +fi +OUT="$OUT_DIR/JackAILocal-hardware-profile.json" + +OS="linux" +CPU_NAME="" +CPU_THREADS=0 +RAM_GB=0 +VRAM_GB=0 +GPU_NAME="unknown" + +if [ "$(uname -s)" = "Darwin" ]; then + OS="macos" + CPU_NAME="$(sysctl -n machdep.cpu.brand_string 2>/dev/null || echo unknown)" + CPU_THREADS="$(sysctl -n hw.logicalcpu 2>/dev/null || echo 0)" + RAM_GB="$(awk "BEGIN { printf \"%.1f\", $(sysctl -n hw.memsize 2>/dev/null || echo 0) / 1073741824 }")" + GPU_NAME="$(system_profiler SPDisplaysDataType 2>/dev/null | awk -F': ' '/Chipset Model/ {print $2; exit}')" + # Apple Silicon shares RAM with the GPU. + case "$CPU_NAME" in *Apple*) VRAM_GB="$RAM_GB" ;; esac +else + CPU_NAME="$(awk -F': ' '/model name/ {print $2; exit}' /proc/cpuinfo 2>/dev/null || echo unknown)" + CPU_THREADS="$(nproc 2>/dev/null || echo 0)" + RAM_GB="$(awk '/MemTotal/ { printf "%.1f", $2 / 1048576 }' /proc/meminfo 2>/dev/null || echo 0)" + if command -v nvidia-smi >/dev/null 2>&1; then + LINE="$(nvidia-smi --query-gpu=name,memory.total --format=csv,noheader,nounits 2>/dev/null | head -n 1)" + GPU_NAME="$(echo "$LINE" | cut -d',' -f1 | sed 's/^ *//')" + VRAM_GB="$(echo "$LINE" | cut -d',' -f2 | awk '{ printf "%.1f", $1 / 1024 }')" + fi +fi + +FREE_GB="$(df -k "$HOME" 2>/dev/null | awk 'NR==2 { printf "%.1f", $4 / 1048576 }')" + +cat > "$OUT" <$null + if ($smiOut) { + $gpus = @($smiOut | ForEach-Object { + $parts = $_.Split(",") + [pscustomobject]@{ + name = $parts[0].Trim() + vram_gb = [math]::Round([double]$parts[1].Trim() / 1024, 1) + compute_cap = $parts[2].Trim() + driver = $parts[3].Trim() + } + }) + } +} + +$disks = Get-CimInstance Win32_LogicalDisk | Where-Object { $_.DriveType -in @(2, 3) } | ForEach-Object { + [pscustomobject]@{ drive = $_.DeviceID; type = if ($_.DriveType -eq 2) { "removable" } else { "fixed" }; size_gb = [math]::Round($_.Size / 1GB, 1); free_gb = [math]::Round($_.FreeSpace / 1GB, 1) } +} + +# Short sequential write/read measurement on the drive this script runs from. +# Model load time is dominated by drive speed; this maps to the catalog's +# drive_class (standard_usb3 vs max_speed_ssd_or_nvme). +Write-Host "Measuring drive speed (a few seconds)..." -ForegroundColor DarkGray +$speedMbps = $null +try { + $testFile = Join-Path $PSScriptRoot "jackailocal-speedtest.tmp" + $payload = New-Object byte[] (64MB) + (New-Object Random).NextBytes($payload) + $writeWatch = [System.Diagnostics.Stopwatch]::StartNew() + [IO.File]::WriteAllBytes($testFile, $payload) + $writeWatch.Stop() + $readWatch = [System.Diagnostics.Stopwatch]::StartNew() + $null = [IO.File]::ReadAllBytes($testFile) + $readWatch.Stop() + Remove-Item $testFile -Force + $totalSeconds = ($writeWatch.Elapsed.TotalSeconds + $readWatch.Elapsed.TotalSeconds) + if ($totalSeconds -gt 0) { $speedMbps = [math]::Round((128 / $totalSeconds), 0) } +} catch {} + +$report = [ordered]@{ + schema = "jackailocal-hardware-profile/1" + collected_at = (Get-Date).ToUniversalTime().ToString("o") + os = "windows" + os_version = $osInfo.Caption + cpu_name = $cpu.Name + cpu_threads = $cpu.NumberOfLogicalProcessors + cpu_max_clock_mhz = $cpu.MaxClockSpeed + ram_gb = [math]::Round($memBytes / 1GB, 1) + gpus = @($gpus) + vram_gb = [double](($gpus | Measure-Object -Property vram_gb -Maximum).Maximum) + disks = @($disks) + usb_storage_gb = [double](($disks | Where-Object { $_.type -eq "removable" } | Measure-Object -Property size_gb -Maximum).Maximum) + script_drive_mbps = $speedMbps +} + +if (-not $OutputPath) { + $OutputPath = Join-Path $PSScriptRoot "JackAILocal-hardware-profile.json" +} +try { + $report | ConvertTo-Json -Depth 10 | Set-Content -Encoding UTF8 $OutputPath +} catch { + # Script folder not writable (e.g. read-only media): fall back to Desktop. + $OutputPath = Join-Path ([Environment]::GetFolderPath("Desktop")) "JackAILocal-hardware-profile.json" + $report | ConvertTo-Json -Depth 10 | Set-Content -Encoding UTF8 $OutputPath +} + +Write-Host "" +Write-Host "Hardware profile created:" -ForegroundColor Green +Write-Host " $OutputPath" +Write-Host "" +Write-Host "NEXT STEP: go back to the JackAILocal builder (web page) and upload this" +Write-Host "file (or paste its content) in the 'hardware profile' box." +Write-Host "" +Write-Host "Summary:" +Write-Host (" CPU : {0} ({1} threads)" -f $report.cpu_name, $report.cpu_threads) +Write-Host (" RAM : {0} GB" -f $report.ram_gb) +Write-Host (" GPU : {0} (VRAM max {1} GB)" -f (($gpus | Select-Object -First 1).name), $report.vram_gb) +if ($speedMbps) { Write-Host (" Drive : ~{0} MB/s (where this script ran)" -f $speedMbps) } diff --git a/qa/static_audit.py b/qa/static_audit.py new file mode 100644 index 0000000000000000000000000000000000000000..82d95ee61182ff5c12473771f54b588a9ffd11fe --- /dev/null +++ b/qa/static_audit.py @@ -0,0 +1,27 @@ +import json, pathlib, sys, tomllib, subprocess +root = pathlib.Path(__file__).resolve().parents[1] +checks=[] +def ok(name, passed, detail=''): + checks.append((name, passed, detail)) +for p in ['config/model-catalog.json','config/models.json','manifest/product.json']: + try: json.loads((root/p).read_text(encoding='utf-8')); ok(f'json {p}', True) + except Exception as e: ok(f'json {p}', False, str(e)) +for p in ['config/jackailocal.windows.toml','config/jackailocal.linux.toml','config/jackailocal.macos.toml','config/jackailocal.appliance.toml']: + try: tomllib.loads((root/p).read_text(encoding='utf-8')); ok(f'toml {p}', True) + except Exception as e: ok(f'toml {p}', False, str(e)) +for p in ['webui/index.html','webui/app-v15.js','webui/styles.css','src/main.rs']: + ok(f'exists {p}', (root/p).exists()) +legacy = list(root.rglob('*PortableMind*')) +ok('product rename JackAILocal', len(legacy)==0, f'legacy names: {[str(x.relative_to(root)) for x in legacy[:5]]}') +placeholders = [str(p.relative_to(root)) for p in root.rglob('*PUT_*')] +ok('no placeholder payload files', len(placeholders)==0, f'placeholders: {placeholders[:5]}') +product_text = '\n'.join( + p.read_text(encoding='utf-8', errors='replace') + for p in [root/'README.md', root/'saas/gradio/README.md', root/'manifest/product.json'] + if p.exists() +).lower() +for forbidden in ['mock server', 'fake usb', 'usb simulator', 'demomode']: + ok(f'no product claim: {forbidden}', forbidden not in product_text) +for name, passed, detail in checks: + print(('PASS' if passed else 'FAIL'), name, detail) +if any(not p for _,p,_ in checks): sys.exit(1) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..efb7dfe440383bb55c68f6bfea09c9bcecc6eccc --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +gradio[oauth]>=4.44.0 +requests +huggingface_hub>=0.34.0 +pydantic>=2.0.0 diff --git a/saas/__init__.py b/saas/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0d89250eaf1cc3775c9eca309bc7f47091a54122 --- /dev/null +++ b/saas/__init__.py @@ -0,0 +1 @@ +"""JackAILocal SaaS and hosted builder interfaces.""" diff --git a/saas/api/main.py b/saas/api/main.py new file mode 100644 index 0000000000000000000000000000000000000000..5907604e9b1b2666840b4a7da7f3da81a47caa74 --- /dev/null +++ b/saas/api/main.py @@ -0,0 +1,92 @@ +from __future__ import annotations +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel, Field +from typing import Any, Optional +from uuid import uuid4 +from datetime import datetime, timezone +from pathlib import Path +import hashlib + +app = FastAPI(title="JackAILocal SaaS Builder", version="0.1.0") +BUILDS: dict[str, dict[str, Any]] = {} +ROOT = Path(__file__).resolve().parents[2] + +class HardwareProfile(BaseModel): + product: str = "JackAILocal" + customer_email: str = "" + os: str = "windows" + cpu_name: Optional[str] = None + cpu_threads: int = 4 + ram_gb: float = 8 + vram_gb: float = 0 + gpus: list[Any] = Field(default_factory=list) + whichllm: Optional[Any] = None + +MODEL_RULES = [ + {"profile":"lite", "label":"Fast", "min_ram_gb":6, "min_vram_gb":0, "ollama":"qwen3.5:2b"}, + {"profile":"balanced", "label":"Balanced", "min_ram_gb":8, "min_vram_gb":0, "ollama":"qwen3.5:4b"}, + {"profile":"smart", "label":"Smart", "min_ram_gb":16, "min_vram_gb":6, "ollama":"qwen3.5:9b"}, + {"profile":"vision", "label":"Vision", "min_ram_gb":12, "min_vram_gb":4, "ollama":"qwen3-vl:4b"}, +] + +def plan(profile: HardwareProfile) -> dict[str, Any]: + # 3% slack: physical "32 GB" machines report ~31.7 GB usable. + allowed = [r for r in MODEL_RULES if profile.ram_gb >= r["min_ram_gb"] * 0.97 and profile.vram_gb >= r["min_vram_gb"] * 0.97] + if not allowed: + allowed = [MODEL_RULES[0]] + default = allowed[-1] + return { + "default_profile": default["profile"], + "profiles": allowed, + "ollama_models": sorted(set(r["ollama"] for r in allowed)), + "backends": ["ollama"], + "notes": ["Models are installed during target build or factory preload, not on every runtime launch."] + } + +def asset_entry(relative: str) -> dict[str, Any] | None: + path = ROOT / relative + if not path.is_file(): + return None + return { + "path": relative.replace("\\", "/"), + "sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + "bytes": path.stat().st_size, + } + +@app.post("/api/v1/hardware/submit") +def submit(profile: HardwareProfile): + build_id = str(uuid4()) + build_plan = plan(profile) + BUILDS[build_id] = { + "build_id": build_id, + "product": "JackAILocal", + "created_at": datetime.now(timezone.utc).isoformat(), + "hardware": profile.model_dump(), + "plan": build_plan, + } + return {"build_id": build_id, "manifest_url": f"/api/v1/builds/{build_id}/manifest", "plan": build_plan} + +@app.get("/api/v1/builds/{build_id}/manifest") +def manifest(build_id: str): + b = BUILDS.get(build_id) + if not b: + raise HTTPException(404, "build not found") + plan = b["plan"] + assets = [ + item for item in [ + asset_entry("backends/ollama/windows/ollama.exe"), + asset_entry("bin/jackailocald.exe"), + asset_entry("config/voice-assets.json"), + asset_entry("content/field-manual/cards.json"), + ] if item is not None + ] + return { + "product":"JackAILocal", + "build_id":build_id, + "created_at":b["created_at"], + "default_profile":plan["default_profile"], + "ollama_models":plan["ollama_models"], + "assets": assets, + "security":{"signed_updates":True,"local_only":True,"no_admin_normal_use":True}, + "notes":"Use the platform builder package to install these assets and selected models into the target." + } diff --git a/saas/api/requirements.txt b/saas/api/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..50ac7c0f351fa92f1750e3dc620c7a5d88e22f5e --- /dev/null +++ b/saas/api/requirements.txt @@ -0,0 +1,3 @@ +fastapi +uvicorn[standard] +pydantic diff --git a/saas/gradio/README.md b/saas/gradio/README.md new file mode 100644 index 0000000000000000000000000000000000000000..98f8a02ff6625e5f41c3b205428e3ba9f67a5bb4 --- /dev/null +++ b/saas/gradio/README.md @@ -0,0 +1,101 @@ +# JackAILocal Gradio Builder + +This folder contains the Gradio package-builder and local runtime console. + +## Responsibilities + +- Provide a simple one-click flow for non-technical users +- Accept a real uploaded or manually entered hardware profile +- Scan the current Windows machine when running locally +- Recommend compatible Ollama models from the JackAILocal catalog +- Export a build manifest ZIP +- Export Windows and macOS builder ZIPs only when their required runtime assets exist +- Export a Linux/Docker appliance package +- Write a physical USB, SSD, or local-folder target when running locally +- Connect to a real local JackAILocal runtime for chat, benchmark, documents, and status +- Run AI-in-the-loop packaging with a real hosted Gemma 4 12B IT endpoint when Modal or HF secrets are configured +- Run a schema-validated internal decision engine that can return `ASK_USER`, `ASK_HUMAN_REVIEW`, `REJECT`, or a build action +- Run local LLM-in-the-loop review through a prepared runtime; Gemma 4 12B (`gemma4:12b`) is preferred when installed and no hosted answer is simulated + +A hosted Space cannot access a client computer or its drives. It exports builders and reports local runtime actions as unavailable. Hosted AI-in-the-loop is allowed only through a configured Modal vLLM endpoint or HF Inference Provider. + +## Hosted AI-in-the-loop + +Recommended production demo path: + +```text +Hugging Face Space Gradio + -> Modal vLLM OpenAI-compatible endpoint + -> google/gemma-4-12B-it +``` + +Space secrets: + +```text +MODAL_BASE_URL=https://--jackailocal-gemma-4-12b-it-vllm-serve.modal.run +MODAL_API_KEY= +MODEL_ID=google/gemma-4-12B-it +``` + +Optional fallback: + +```text +CONFIG_AGENT_PROVIDER=hf +HF_TOKEN=hf_... +MODEL_ID=google/gemma-4-12B-it +HF_INFERENCE_PROVIDER=auto +``` + +Set `REQUIRE_REMOTE_CONFIG_AGENT=true` when final manifest export must fail if the hosted agent cannot be reached. Otherwise the Space falls back to the deterministic hardware-policy plan and records the agent failure in `agent_decision`. + +The remote agent modifies only the packaging manifest. The shipped runtime remains local/offline after models are preloaded. + +## Internal Decision Engine + +The Builder now treats the LLM as an internal decision engine, not as a chatbot. The UI sends: + +- selected use case and target +- hardware profile +- available backend/model plan +- client constraints +- previous answers to AI questions + +The model must return validated JSON. If it lacks critical information, it returns `ASK_USER` with concrete questions. The UI waits for the user/operator to provide answers in the decision answers JSON, then the decision is rerun. The policy gate still has final authority and can block USB builds without a real target, backend mismatches, missing secrets, or unsafe manifest patches. + +No fake decision is generated when Modal/HF is not configured. In that case the decision panel reports the missing secrets explicitly. + +## Simple Flow + +The first screen is for users who do not know AI terms. They choose: + +1. the intended use +2. the target: this computer, USB key, external SSD, Windows ZIP, macOS ZIP, or Linux/Docker appliance +3. `Prepare / publish now` + +The app then generates the manifest, selects the local model, writes the local target when possible, or publishes the requested package ZIP. Advanced controls remain available below for operator/debug use. + +## Run + +From the repository root: + +```bash +pip install -r requirements.txt +python app.py +``` + +Or from this folder: + +```bash +pip install -r requirements.txt +python app.py +``` + +Open `http://127.0.0.1:7860`. + +## Product Boundary + +- The Gradio layer does not provide cloud inference. +- The Gradio layer does not bypass signed manifest policy. +- Normal runtime use does not download models. +- LAN exposure remains disabled unless the user explicitly enables Phone Access. +- The Rust runtime and WebUI remain the customer runtime. diff --git a/saas/gradio/SPACE_README.example.md b/saas/gradio/SPACE_README.example.md new file mode 100644 index 0000000000000000000000000000000000000000..1c8f364b7eec55b1e8adbb6aa736b6c14171a550 --- /dev/null +++ b/saas/gradio/SPACE_README.example.md @@ -0,0 +1,31 @@ +--- +title: JackAILocal Builder +emoji: 🔒 +colorFrom: green +colorTo: gray +sdk: gradio +app_file: app.py +pinned: false +hf_oauth: true +hf_oauth_scopes: + - inference-api +--- + +# JackAILocal Builder (Hugging Face Space) + +Copy this frontmatter into the README.md of the Space repository. The +`hf_oauth` block enables "Sign in with Hugging Face": each visitor's Gemma 4 +configuration review runs through the HF Inference API **on their own +account** (scope `inference-api`), never on the Space owner's token. + +Token precedence used by the app for the configuration agent: + +1. Visitor OAuth token (sign-in button) — per-session, billed to the visitor. +2. `HF_TOKEN` Space secret — billed to the OWNER; set it only if you want to + sponsor all visitors' usage. +3. `MODAL_BASE_URL` + `MODAL_API_KEY` — your own vLLM endpoint. +4. Local console only: a key pasted in the API panel (hidden on hosted + Spaces because it is process-wide, not per-session). + +The hosted Space only exports packages (Windows/macOS/appliance ZIPs and the +PC analyzer); it never claims to run a local runtime for the visitor. diff --git a/saas/gradio/__init__.py b/saas/gradio/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c92110fa6ba572d0484a4878054e15cfdca45765 --- /dev/null +++ b/saas/gradio/__init__.py @@ -0,0 +1 @@ +"""Gradio builder package.""" diff --git a/saas/gradio/__pycache__/app.cpython-313.pyc b/saas/gradio/__pycache__/app.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9a55cdd9f11327ef7418a7fdd4ba469475d2865 Binary files /dev/null and b/saas/gradio/__pycache__/app.cpython-313.pyc differ diff --git a/saas/gradio/app.py b/saas/gradio/app.py new file mode 100644 index 0000000000000000000000000000000000000000..4b92bc6cd20b28d731cb84abc117b2c338b1c844 --- /dev/null +++ b/saas/gradio/app.py @@ -0,0 +1,4715 @@ +from __future__ import annotations + +import io +import json +import os +import platform +import copy +import html +import re +import shutil +import tempfile +import time +import zipfile +import subprocess +from datetime import datetime, timezone +from typing import Any +from uuid import uuid4 + +import gradio as gr +import requests + +try: + from . import decision_engine +except ImportError: + import decision_engine + +PRODUCT_NAME = "JackAILocal" +PRODUCT_MODE = "SaaS Builder & Local Runtime Console" +SELECTOR_VERSION = "v15-real-builder" +MAX_MODEL_PARAMS_B = 32 +CONFIG_AGENT_MODEL_ID = "gemma_config_agent_12b" +CONFIG_AGENT_MODEL_REF = "gemma4:12b" +REMOTE_CONFIG_AGENT_MODEL_ID = os.getenv("REMOTE_CONFIG_AGENT_MODEL_ID", "Qwen/Qwen2.5-7B-Instruct") +REMOTE_AGENT_REQUIRED = os.getenv("REQUIRE_REMOTE_CONFIG_AGENT", "").lower() in {"1", "true", "yes"} +REMOTE_AGENT_TIMEOUT_SECONDS = int(os.getenv("REMOTE_AGENT_TIMEOUT_SECONDS", "240")) + +# Detect if we are running in the local workspace with scripts available +FILE_DIR = os.path.dirname(os.path.abspath(__file__)) +WORKSPACE_ROOT = os.path.abspath(os.path.join(FILE_DIR, "..", "..")) +IS_LOCAL = os.path.exists(os.path.join(WORKSPACE_ROOT, "windows", "hwscan-windows.ps1")) and (platform.system() == "Windows") and ("SPACE_ID" not in os.environ) + +# Model catalog kept identical to preserve full compatibility with builder scripts +MODEL_CATALOG = [ + { + "id": "qwen35_fast_2b", + "label_en": "Fast 2B", + "label_fr": "Rapide 2B", + "provider": "ollama", + "model_ref": "qwen3.5:2b", + "hf": "", + "params_b": 2, + "min_ram_gb": 6, + "min_vram_gb": 0, + "task": "chat", + "tier": "standard", + "features": ["chat", "vision", "low_power"], + "notes_en": "Small CPU-friendly default for weak laptops.", + "notes_fr": "Petit modèle adapté aux portables faibles et au CPU." + }, + { + "id": "qwen35_balanced_4b", + "label_en": "Balanced 4B", + "label_fr": "Équilibré 4B", + "provider": "ollama", + "model_ref": "qwen3.5:4b", + "hf": "", + "params_b": 4, + "min_ram_gb": 8, + "min_vram_gb": 0, + "task": "chat", + "tier": "standard", + "features": ["chat", "vision", "voice"], + "notes_en": "Good default for standard laptops.", + "notes_fr": "Bon défaut pour les portables standards." + }, + { + "id": "qwen35_smart_9b", + "label_en": "Smart Default 9B", + "label_fr": "Défaut intelligent 9B", + "provider": "ollama", + "model_ref": "qwen3.5:9b", + "hf": "", + "params_b": 9, + "min_ram_gb": 16, + "min_vram_gb": 0, + "task": "chat", + "tier": "standard", + "features": ["chat", "vision", "thinking"], + "notes_en": "Practical offline baseline for normal laptops. This is the default ceiling for the standard pack, not the product ceiling.", + "notes_fr": "Base offline pratique pour portable normal. C’est la limite du pack standard, pas la limite du produit." + }, + { + "id": CONFIG_AGENT_MODEL_ID, + "label_en": "Gemma 4 12B Config Agent", + "label_fr": "Agent config Gemma 4 12B", + "provider": "ollama", + "model_ref": CONFIG_AGENT_MODEL_REF, + "hf": REMOTE_CONFIG_AGENT_MODEL_ID, + "params_b": 11.95, + "min_ram_gb": 24, + "min_vram_gb": 0, + "task": "chat", + "tier": "agent", + "features": ["chat", "vision", "thinking", "audio", "tools", "agent", "config_agent", "model_selection"], + "notes_en": "Preferred real local LLM-in-the-loop for hardware analysis, model selection, and safe client configuration review.", + "notes_fr": "LLM local prefere pour l'analyse materielle, la selection des modeles et la revue de configuration client." + }, + { + "id": "qwen35_power_27b", + "label_en": "Power 27B", + "label_fr": "Puissance 27B", + "provider": "ollama", + "model_ref": "qwen3.5:27b", + "hf": "", + "params_b": 27, + "min_ram_gb": 32, + "min_vram_gb": 16, + "task": "chat", + "tier": "power", + "features": ["chat", "vision", "thinking", "power"], + "notes_en": "Power profile for stronger GPUs. Still under the 32B hackathon limit.", + "notes_fr": "Profil puissance pour GPU plus fort. Reste sous la limite 32B du hackathon." + }, + { + "id": "qwen36_power_27b", + "label_en": "Qwen3.6 Power 27B", + "label_fr": "Qwen3.6 Puissance 27B", + "provider": "ollama", + "model_ref": "qwen3.6:27b", + "hf": "Qwen/Qwen3.6-27B", + "params_b": 27, + "min_ram_gb": 32, + "min_vram_gb": 24, + "task": "chat", + "tier": "power", + "features": ["chat", "coding", "thinking", "power"], + "notes_en": "Power profile for RTX 3090/4090-class hardware. Context must be capped by policy.", + "notes_fr": "Profil puissance pour matériel type RTX 3090/4090. Le contexte doit être limité par politique." + }, + { + "id": "qwen3_power_30b", + "label_en": "Power 30B", + "label_fr": "Puissance 30B", + "provider": "ollama", + "model_ref": "qwen3:30b", + "hf": "", + "params_b": 30, + "min_ram_gb": 48, + "min_vram_gb": 24, + "task": "chat", + "tier": "power", + "features": ["chat", "thinking", "power"], + "notes_en": "Optional power model under 32B when available in the target backend.", + "notes_fr": "Modèle puissance optionnel sous 32B si disponible dans le backend cible." + }, + { + "id": "qwen_vl_8b", + "label_en": "Vision 8B", + "label_fr": "Vision 8B", + "provider": "ollama", + "model_ref": "qwen3-vl:8b", + "hf": "", + "params_b": 8, + "min_ram_gb": 16, + "min_vram_gb": 6, + "task": "vision", + "tier": "standard", + "features": ["vision", "document", "image"], + "notes_en": "Standard vision/document profile.", + "notes_fr": "Profil standard vision/document." + }, + { + "id": "qwen_vl_30b", + "label_en": "Vision Pro 30B", + "label_fr": "Vision Pro 30B", + "provider": "ollama", + "model_ref": "qwen3-vl:30b", + "hf": "", + "params_b": 30, + "min_ram_gb": 48, + "min_vram_gb": 24, + "task": "vision", + "tier": "power", + "features": ["vision", "document", "image", "power"], + "notes_en": "Power vision model under the hackathon limit.", + "notes_fr": "Modèle vision puissance sous la limite du hackathon." + }, +] + +CONTENT_PACKS = [ + {"id": "field_manual_core", "label_en": "Field Manual Core", "label_fr": "Manuel terrain de base", "recommended": True, "min_storage_gb": 32}, + {"id": "starter_prompt_packs", "label_en": "Starter Prompt Packs", "label_fr": "Packs de prompts de départ", "recommended": True, "min_storage_gb": 32}, +] + +GGUF_BY_MODEL_REF = { + "qwen3.5:2b": "models/gguf/qwen3.5-2b-q4_k_m.gguf", + "qwen3.5:4b": "models/gguf/qwen3.5-4b-q4_k_m.gguf", + "qwen3.5:9b": "models/gguf/qwen3.5-9b-q4_k_m.gguf", + "qwen3.5:27b": "models/gguf/qwen3.5-27b-q4_k_m.gguf", + "qwen3.6:27b": "models/gguf/qwen3.6-27b-q4_k_m.gguf", + "qwen3:30b": "models/gguf/qwen3-30b-q4_k_m.gguf", +} + +VLLM_BY_MODEL_ID = { + CONFIG_AGENT_MODEL_ID: REMOTE_CONFIG_AGENT_MODEL_ID, + "qwen36_power_27b": "Qwen/Qwen3.6-27B", +} + +SAMPLE_PROFILES = { + "Low-end laptop": { + "os": "windows", + "cpu_threads": 4, + "ram_gb": 8, + "vram_gb": 0, + "gpu_name": "Integrated graphics", + "disk": "USB 3.0", + "usb_storage_gb": 64 + }, + "Normal laptop": { + "os": "windows", + "cpu_threads": 8, + "ram_gb": 16, + "vram_gb": 0, + "gpu_name": "CPU-only office laptop", + "disk": "USB 3.2 SSD", + "usb_storage_gb": 128 + }, + "Modest GPU laptop": { + "os": "windows", + "cpu_threads": 8, + "ram_gb": 16, + "vram_gb": 6, + "gpu_name": "Entry NVIDIA/AMD GPU", + "disk": "USB 3.2 SSD", + "usb_storage_gb": 128 + }, + "GPU workstation (24 GB VRAM)": { + "os": "windows", + "cpu_threads": 24, + "ram_gb": 64, + "vram_gb": 24, + "gpu_name": "NVIDIA 24 GB-class GPU", + "disk": "USB-C NVMe", + "usb_storage_gb": 512 + }, + "Linux appliance": { + "os": "linux", + "cpu_threads": 16, + "ram_gb": 32, + "vram_gb": 12, + "gpu_name": "NVIDIA GPU", + "disk": "NVMe", + "usb_storage_gb": 256 + }, + "Apple Silicon MacBook": { + "os": "macos", + "cpu_threads": 10, + "ram_gb": 24, + "vram_gb": 0, + "gpu_name": "Apple Silicon unified memory", + "disk": "USB-C SSD", + "usb_storage_gb": 256 + }, +} + +PROFILE_EXAMPLE = { + "os": "windows", + "cpu_threads": 24, + "ram_gb": 64, + "vram_gb": 24, + "gpu_name": "NVIDIA RTX 3090", + "disk": "USB-C NVMe", + "usb_storage_gb": 512, + "whichllm": { + "source": "optional", + "top_model": "Qwen/Qwen3.6-27B" + } +} + +VIEW_NAMES = ["saas", "console", "dashboard", "chat", "docs", "bench", "policy"] + +SIMPLE_GOAL_VALUES = [ + "daily_private", + "family_private", + "work_notes", + "field_manual", + "vision_docs", + "power_workstation", +] + +SIMPLE_GOAL_LABELS = { + "EN": { + "daily_private": "Private AI for daily use", + "family_private": "Family AI without cloud", + "work_notes": "Work, notes and documents", + "field_manual": "Offline field / emergency AI", + "vision_docs": "Document AI with vision", + "power_workstation": "Power AI for workstation", + }, + "FR": { + "daily_private": "AI privee pour tous les jours", + "family_private": "AI familiale sans cloud", + "work_notes": "AI travail, notes et documents", + "field_manual": "AI terrain / urgence hors ligne", + "vision_docs": "AI documents avec vision", + "power_workstation": "AI puissante pour station de travail", + }, +} + +SIMPLE_GOAL_TO_PACKAGE_GOAL = { + "daily_private": "Privacy offline assistant", + "family_private": "Standard offline assistant", + "work_notes": "Knowledge and notes", + "field_manual": "Survival / field manual", + "vision_docs": "Vision documents", + "power_workstation": "Power workstation", +} + +SIMPLE_TARGET_VALUES = [ + "local_runtime", + "usb_runtime", + "ssd_runtime", + "publish_windows", + "publish_macos", + "publish_appliance", +] + +SIMPLE_TARGET_LABELS_BY_LANG = { + "EN": { + "local_runtime": "Install on this computer", + "usb_runtime": "Prepare a USB key", + "ssd_runtime": "Prepare an external SSD", + "publish_windows": "Publish a Windows ZIP", + "publish_macos": "Publish a macOS ZIP", + "publish_appliance": "Publish a Linux / Docker appliance", + }, + "FR": { + "local_runtime": "Installer sur cet ordinateur", + "usb_runtime": "Preparer une cle USB", + "ssd_runtime": "Preparer un SSD externe", + "publish_windows": "Publier un ZIP Windows", + "publish_macos": "Publier un ZIP macOS", + "publish_appliance": "Publier une appliance Linux / Docker", + }, +} + +SIMPLE_TARGET_MODES = { + "local_runtime": "LocalFolder", + "usb_runtime": "USB", + "ssd_runtime": "SSD", +} + +L10N = { + "FR": { + "status_disconnected": "Cible JackAILocal non creee", + "status_ready": "Cible JackAILocal prete (arretee)", + "status_running": "Système Actif (127.0.0.1)", + "btn_start": "Demarrer JackAILocal (START-HERE)", + "btn_stop": "Arreter JackAILocal", + "status_label": "Statut de la cible JackAILocal", + "sidebar_header": "CONSOLE RUNTIME LOCAL", + "nav_config": "1. Configurer le package", + "nav_console": "2. Console de build", + "nav_dashboard": "Tableau de bord local", + "nav_chat": "Chat interactif (API)", + "nav_docs": "Gestionnaire de documents", + "nav_bench": "Test de performance", + "nav_policy": "3. Sécurité et confidentialité", + "build_success": "Sommes de controle SHA256 validees. Cible JackAILocal prete !", + "run_first_prompt": "Veuillez preparer la cible puis la demarrer avec 'START-HERE' dans la barre laterale pour activer les fonctions locales.", + "dashboard_desc": "Tableau de bord local de JackAILocal", + "chat_desc": "Chat local privé (runtime vérifié seulement)", + "docs_desc": "Gestionnaire de documents locaux (sur la cible JackAILocal)", + "bench_desc": "Test d'évaluation de vitesse de génération locale", + "policy_desc": "Politiques système et sécurité du produit", + "btn_build": "Construire la cible JackAILocal", + "lbl_os": "Système d'exploitation cible", + "lbl_sample": "Profil matériel exemple", + "lbl_goal": "Objectif du package", + "lbl_override": "Remplacement JSON (Profil matériel personnalisé)", + "lbl_manifest": "Plan de build recommandé (Markdown)", + "lbl_json_manifest": "Manifest de build (JSON)", + "lbl_run_bench": "Lancer le Benchmark", + "lbl_doc_title": "Titre du document (ex: guide_eau.txt)", + "lbl_doc_content": "Contenu du document...", + "lbl_save_doc": "Enregistrer localement sur la cible", + "lbl_doc_list": "Documents enregistres sur la cible", + "lbl_chat_model": "Modèle local sélectionné", + "lbl_starter_prompts": "Prompts suggérés (selon l'objectif)", + "lbl_input_prompt": "Saisissez votre question...", + "lbl_clear": "Effacer", + "lbl_send": "Envoyer", + "simple_title": "Mode simple: AI locale prete en un clic", + "simple_desc": "Choisissez l'usage et la cible. JackAILocal recommande le modele, installe la cible locale/USB/SSD ou publie un package partageable.", + "simple_goal_label": "Usage final", + "simple_target_label": "Cible a preparer ou publier", + "simple_prepare": "Construire / publier apres revue", + "simple_ready": "Pret. Le mode simple masque les choix de modele, backend et quantization.", + "advanced_options": "Options avancees", + "sample_label": "Source du profil matériel", + "profile_src_real": "Cette machine (scan matériel réel)", + "profile_src_upload": "PC du client — importer le .json du PC analyzer", + "profile_src_example": "Exemple : {name}", + "profile_src_need_json": "Aucun profil matériel fourni. Importez le .json du PC analyzer du client (ou lancez le scan réel en local), ou choisissez explicitement un profil d'exemple.", + "hackathon_badge": "🤗 **[HF Build Small Hackathon](https://huggingface.co/build-small-hackathon)**\n\n`≤ 32B params` · `100 % inférence locale` · `Ollama / llama.cpp / whisper.cpp / Piper`\n\n🔍 Chaque revue IA expose sa **trace d'agent complète** (prompt + sortie brute du modèle) dans le JSON d'audit — rien n'est simulé.", + "package_goal_label": "Objectif du package", + "target_mode_label": "Mode de cible", + "target_drive_label": "Lecteur ou dossier cible", + "hardware_json_label": "Profil materiel JSON (laisser vide pour utiliser le profil choisi)", + "plan_initial": "Generez un plan pour voir les recommandations.", + "agent_review_title": "Revue agent LLM locale", + "export_manifest": "Exporter le manifest ZIP", + "export_windows": "Exporter le builder Windows", + "export_macos": "Exporter le builder macOS", + "manifest_file": "Fichier manifest ZIP", + "windows_file": "Builder Windows ZIP", + "macos_file": "Builder macOS ZIP", + "console_title": "Console de creation / publication", + "console_ready": "Terminal pret. Lancez le mode simple ou la construction avancee pour commencer.", + "published_package": "Package publie par le mode simple", + "chat_offline": "Systeme arrete. Preparez puis demarrez un runtime local pour utiliser le chat.", + "profiler_upload_label": "Téléverser le profil matériel (.json de l'analyseur PC)", + "profiler_btn": "Télécharger l'analyseur PC (le client l'exécute et renvoie le .json)", + "profiler_zip_label": "ZIP de l'analyseur PC", + "tab_light": "Flux simple en un clic", + "tab_advanced": "Contrôles avancés opérateur", + "refresh_btn": "Actualiser", + "why_title": "Pourquoi ce modèle", + "why_rules": [ + "- Ce résultat est calculé par règles à partir du profil matériel.", + "- Le catalogue et les seuils de recommandation sont transparents.", + "- Si vous fournissez un JSON personnalisé, il remplace le profil d'exemple.", + "- Le pack standard reste plafonné à 9B par défaut pour rester fluide sur laptop normal.", + "- Les machines avec GPU RTX 3090/4090 débloquent les modèles de 27B ou 30B." + ], + "plan_title": "Résultat de la recommandation", + "plan_default_model": "Modèle par défaut recommandé", + "plan_agent_label": "Agent LLM-in-the-loop", + "plan_agent_fallback": "Gemma 4 12B `{model_ref}` non sélectionné pour ce profil matériel ; le mode Agent du runtime peut tout de même utiliser Auto avec un modèle de chat local installé.", + "plan_hosted_agent": "Agent de packaging hébergé", + "plan_no_hosted_agent": "Aucun agent de planification hébergé configuré", + "plan_agent_status": "Statut décision agent", + "plan_not_configured": "non configuré", + "plan_goal": "But du package", + "plan_backends": "Backends", + "plan_build_id": "Build ID", + "plan_models_title": "Modèles compatibles dans ce profil", + "plan_packs_title": "Packs hors ligne sélectionnés", + "plan_no_packs": "- Aucun pack sélectionné.", + "plan_policy_title": "Politique produit", + "plan_policy_rules": [ + "- Le modèle par défaut est une base, pas une limite stricte.", + "- Les modèles sont pré-chargés pendant la préparation du package, pas à chaque insertion de la clé." + ], + "flow_title": "Flow simple prêt", + "builder_scope_note": "Ce builder sert uniquement à créer et publier des packages. Le chat, les documents et le benchmark se trouvent dans l'interface du runtime livré (START-HERE).", + "oauth_note": "Connectez-vous avec Hugging Face pour exécuter la revue IA : l'inférence est facturée à votre propre compte HF.", + "flow_agent_running": "Revue du plan par l'agent de configuration IA en cours...", + "flow_agent_failed": "Revue de l'agent indisponible : {detail}. Le plan basé sur les règles est appliqué tel quel.", + "flow_target": "Cible", + "flow_model": "Modèle local recommandé", + "flow_agent": "Agent AI-in-the-loop", + "flow_updates": "Mise à jour : manifeste signé + installation contrôlée", + "flow_enduser": "Utilisateur final : lancer `START-HERE` ou le runtime publié, sans choisir de modèle ni backend", + "err_plan_failed": "Échec de la construction du plan de déploiement :", + "publish_windows": "Construction du package Windows pour {target}...", + "publish_macos": "Construction du package macOS pour {target}...", + "publish_appliance": "Construction du package appliance Linux pour {target}...", + "success_windows": "ZIP builder Windows prêt :", + "success_macos": "ZIP builder macOS prêt :", + "success_appliance": "ZIP appliance Linux prêt :", + "ready_windows": "Package Windows prêt au téléchargement.", + "ready_macos": "Package macOS prêt au téléchargement.", + "ready_appliance": "Appliance Linux/Docker prête au téléchargement.", + "target_ready_started": "Cible prête. Runtime démarré.", + "target_ready_attention": "Cible prête. Démarrage à vérifier : {detail}", + "console_reset": "Réinitialisation de la console... Démarrage du flux de compilation...", + "build_hosted_unavailable": "La création d'un disque physique est indisponible dans une Space hébergée. Exportez le vrai ZIP builder et exécutez-le sur l'ordinateur cible.", + "err_select_drive": "Sélectionnez un lecteur ou dossier cible réel.", + "build_stop_processes": "Arrêt des processus runtime locaux pour libérer les verrous de fichiers...", + "build_warn_shutdown": "Avertissement pendant l'arrêt des processus : {detail}", + "build_starting": "Démarrage de l'installation USB physique...", + "build_copy_ollama": "Copie du binaire Ollama système vers le répertoire payload...", + "build_warn_ollama": "Avertissement : impossible de copier l'Ollama système : {detail}", + "build_done": "Cible JackAILocal construite et configurée avec succès !", + "build_failed_rc": "Échec de la construction de la cible JackAILocal (code de sortie {code}).", + "build_failed_start": "Échec du démarrage du processus builder de la cible : {detail}", + "sidebar_model": "Modèle : {model}", + "err_remote_required": "L'AI-in-the-loop distant est requis mais non configuré. Définissez MODAL_BASE_URL/MODAL_API_KEY ou HF_TOKEN dans les secrets de la Space.", + "err_more_info": "Le moteur de décision IA a besoin de plus d'informations avant le déploiement :\n{questions}", + "err_remote_failed": "Échec de l'AI-in-the-loop distant : {detail}", + "chat_hosted_unavailable": "Le chat local est indisponible dans le builder hébergé. Démarrez un runtime JackAILocal préparé.", + "chat_stats": "**Métriques réelles (local)** : {wps} mots/s | Latence : {ms} ms | Backend : {backend}", + "chat_api_error": "L'API locale vérifiée n'a pas retourné de réponse : {detail}", + "doc_hosted_unavailable": "L'écriture de documents est indisponible dans le builder hébergé.", + "doc_title_empty": "Le titre ne peut pas être vide", + "doc_saved": "Fichier enregistré sur le lecteur : {path}", + "doc_save_error": "Erreur lors de l'enregistrement du fichier : {detail}", + "doc_no_target": "Aucun lecteur cible local vérifié n'est disponible.", + "bench_hosted_unavailable": "Un vrai benchmark local est indisponible dans le builder hébergé.", + "bench_running": "Test des performances d'inférence locale réelle (interrogation du daemon API Rust local)...", + "bench_status_error": "L'API locale a retourné le code {code} : {detail}", + "bench_service_error": "Erreur du service de benchmark local : {detail}", + "bench_report": "=== BENCHMARK D'INFÉRENCE LOCALE RÉELLE ===\n\nProfil du modèle : {model}\nLatence d'inférence : {ms} ms\nVitesse de sortie approximative : {wps} mots/s\n\nTexte de réponse exemple :\n\"{sample}\"\n", + "bench_connect_error": "Connexion impossible à l'API Rust locale : {detail}", + "bench_initial": "Appuyez sur « Lancer le Benchmark » pour démarrer le profilage local...", + "scan_detected": "Détecté : {os} | {cpu} threads CPU | {ram} Go RAM | {vram} Go VRAM ({gpu})", + "audit_saas_warning_title": "[Avertissement décision SaaS]", + "audit_saas_warning_body": "Échec de l'interrogation du moteur de décision cloud : {detail}", + "audit_saas_skipped_title": "[Décision SaaS]", + "audit_saas_skipped_body": "*(Moteur de décision IA hébergé ignoré : aucune clé cloud configurée)*", + "audit_local_warning_title": "[Avertissement audit LLM local]", + "audit_local_warning_body": "*(Revue de l'agent LLM local ignorée ou en échec : {detail})*", + "agent_review_hosted_title": "Revue de packaging AI-in-the-loop hébergée", + "agent_review_local_title": "Revue LLM-in-the-loop locale", + "agent_review_ollama_title": "Revue LLM-in-the-loop via Ollama local", + "agent_lbl_provider": "Fournisseur", + "agent_lbl_model": "Modèle", + "agent_lbl_agent_model": "Modèle de l'agent", + "agent_lbl_selected_client": "Modèle client sélectionné", + "agent_lbl_selected": "Modèle sélectionné", + "agent_lbl_final_default": "Modèle par défaut final", + "agent_lbl_decision_status": "Statut de la décision", + "agent_lbl_confidence": "Confiance", + "agent_risk_flags": "Drapeaux de risque", + "agent_next_steps": "Prochaines étapes", + "agent_none_reported": "- Rien à signaler", + "agent_non_json": "Le modèle local a retourné un texte non JSON. Réponse brute :", + "agent_gemma_missing": "L'agent de configuration requiert **Gemma 4 12B** (`gemma4:12b`), et il n'est pas installé dans l'Ollama local.\n\nInstallez-le, puis relancez la revue :\n\n```\nollama pull gemma4:12b\n```\n\n(`JACKAI_AGENT_ALLOW_FALLBACK=1` autorise un agent de secours plus petit pour les tests uniquement.)", + "agent_ollama_query_failed": "Échec de la requête de revue via l'agent Ollama local : {detail}", + "agent_no_runtime": "Aucun daemon runtime JackAILocal local (port 4891) ni instance Ollama locale (port 11434/11435) n'a répondu. Démarrez votre environnement IA local avant de lancer la revue agent.", + "app_title": "JackAILocal - Builder SaaS et console runtime local", + "header_subtitle": "Builder d'espace de travail IA hors ligne portable et console runtime local vérifié", + "lang_label": "Language / Langue", + "footer_text": "JackAILocal v15 Runtime local", + "hero_title": "Préparez un vrai package IA local", + "hero_desc": "Choisissez l'usage et la cible. JackAILocal analyse le matériel, appelle le véritable agent de décision lorsqu'il est configuré, demande les informations manquantes au besoin, puis prépare un package local/hors ligne.", + "hero_step1_title": "1. Choisir", + "hero_step1_desc": "Usage, cible et matériel.", + "hero_step2_title": "2. Décider", + "hero_step2_desc": "Gemma 4 12B IT retourne un JSON validé.", + "hero_step3_title": "3. Construire", + "hero_step3_desc": "Export ou écriture seulement après validation des politiques.", + "step1_title": "Étape 1 : Configuration de la cible et du profil", + "step3_title": "Étape 3 : Compilation et export", + "audit_accordion": "Vérification et audit IA (optionnel)", + "audit_desc": "Auditez la configuration d'installation avec les agents LLM hébergés (SaaS) et locaux pour la vérifier face aux contraintes matérielles de la cible.", + "operator_notes_label": "Notes opérateur / contraintes", + "operator_notes_ph": "Exemple : priorité à la vie privée, pas de LAN par défaut, voix requise, cible portable peu puissant.", + "audit_saas_title": "Configuration SaaS", + "answers_label": "Réponses JSON aux questions de l'IA", + "api_accordion": "Fournisseur d'API cloud / local (remplacement optionnel)", + "api_desc": "Configurez une vraie API cloud ou un endpoint local pour exécuter le moteur de décision IA dans la boucle.", + "api_provider_label": "Fournisseur d'API", + "api_model_label": "ID du modèle", + "api_key_label": "Clé API / jeton", + "api_key_ph": "Collez la clé API ici...", + "api_url_label": "URL de base personnalisée (optionnel)", + "audit_local_title": "Configuration LLM locale", + "agent_choice_auto": "Préférer Gemma 4 12B si installé, sinon Auto", + "agent_choice_exact": "Exiger exactement Gemma 4 12B", + "agent_model_label": "Modèle de l'agent de configuration", + "audit_run_btn": "Lancer la vérification et l'audit IA", + "audit_results_title": "Résultats de l'audit", + "audit_initial": "Lancez l'audit avant la construction pour vérifier la configuration.", + "audit_json_label": "Aperçu du manifest d'audit", + "export_accordion": "Exporter les builders et fichiers du package", + "dash_runtime_lbl": "Runtime", + "dash_hardware_lbl": "Matériel", + "dash_model_lbl": "Modèle recommandé", + "dash_network_lbl": "Sécurité réseau", + "dash_policy_note": "Sélectionné par la politique locale", + "dash_loopback_note": "Loopback uniquement", + "dash_checklist_title": "Liste de préparation", + "dash_api_label": "API locale", + "dash_api_stopped": "Arrêtée", + "dash_api_active": "Active (port 4891)", + "dash_default_model_label": "Modèle par défaut", + "dash_api_binding_label": "Liaison API", + "dash_model_catalog_label": "Catalogue de modèles", + "dash_security_label": "Limites de sécurité", + "dash_inactive": "Inactif", + "dash_unavailable": "Indisponible", + "dash_no_backend": "Aucun backend local vérifié", + "dash_no_hw": "Aucun état matériel en direct", + "dash_no_gpu": "Aucun état GPU en direct", + "dash_model_available": "disponible", + "dash_model_missing": "non installé", + "starter_capability": "Question de capacités", + "starter_coding": "Tâche de codage", + "starter_privacy": "Question de confidentialité", + "docs_title": "Bloc-notes local (synchronisation réelle des documents de la cible)", + "doc_content_ph": "Écrivez le texte de la note ici...", + "bench_title": "Lanceur de benchmark d'inférence locale", + "policy_body": """### Politiques de sécurité et d'architecture + +JackAILocal applique des frontières de sécurité strictes entre Internet (SaaS) et les nœuds locaux privés. + +1. **Loopback uniquement** : + - L'API USB hors ligne (`jackailocald`) se lie strictement à `127.0.0.1`. + - Les utilisateurs normaux ne peuvent pas exposer l'API au réseau local (LAN) par défaut. + +2. **Plafond standard à 9B** : + - La configuration par défaut limite les packages standards à 9 milliards de paramètres. + - Cela garantit que l'exécution CPU reste fonctionnelle sur les portables courants sans saturer la mémoire. + +3. **Profil Puissance (jusqu'à 32B)** : + - Pour les machines de classe RTX 3090/4090 ou Apple Silicon avec 24 Go+ de mémoire unifiée, le builder recommande des modèles plus grands (comme Qwen 27B) jusqu'au plafond hackathon de 32B paramètres. + +4. **Agent de configuration LLM-in-the-loop** : + - Gemma 4 12B (`gemma4:12b`) est l'agent local préféré pour la revue de configuration client lorsqu'il est installé. + - L'agent peut conseiller la sélection de modèle et la configuration, mais il ne peut pas outrepasser la politique local-only et <=32B. + +5. **Validation cryptographique** : + - Chaque fichier sur la clé USB est vérifié par somme de contrôle contre un manifeste `SHA256` signé. + - Toute injection manuelle de code ou de binaire déclenche une erreur de sécurité au démarrage. +""" + }, + "EN": { + "status_disconnected": "JackAILocal target not created", + "status_ready": "JackAILocal target ready (Stopped)", + "status_running": "System Active (127.0.0.1)", + "btn_start": "Start JackAILocal (START-HERE)", + "btn_stop": "Stop JackAILocal", + "status_label": "JackAILocal Target Status", + "sidebar_header": "LOCAL RUNTIME CONSOLE", + "nav_config": "1. Configure the package", + "nav_console": "2. Build console", + "nav_dashboard": "Local dashboard", + "nav_chat": "Interactive chat (API)", + "nav_docs": "Document manager", + "nav_bench": "Performance benchmark", + "nav_policy": "3. Security & privacy", + "build_success": "SHA256 checksums verified. JackAILocal target ready!", + "run_first_prompt": "Please prepare the target and then start it with 'START-HERE' in the sidebar to activate local functions.", + "dashboard_desc": "JackAILocal Local Dashboard", + "chat_desc": "Local private chat (verified runtime only)", + "docs_desc": "Local document manager (on the JackAILocal target)", + "bench_desc": "Local generation speed evaluation test", + "policy_desc": "System and security policies of the product", + "btn_build": "Build JackAILocal Target", + "lbl_os": "Target Operating System", + "lbl_sample": "Sample hardware profile", + "lbl_goal": "Package goal", + "lbl_override": "JSON Override (Custom Hardware Profile)", + "lbl_manifest": "Recommended build plan (Markdown)", + "lbl_json_manifest": "Build Manifest (JSON)", + "lbl_run_bench": "Run Benchmark", + "lbl_doc_title": "Document title (e.g. water_guide.txt)", + "lbl_doc_content": "Document content...", + "lbl_save_doc": "Save locally to target", + "lbl_doc_list": "Saved documents on target", + "lbl_chat_model": "Selected local model", + "lbl_starter_prompts": "Suggested prompts (based on goal)", + "lbl_input_prompt": "Type your question...", + "lbl_clear": "Clear", + "lbl_send": "Send", + "simple_title": "Simple mode: local AI ready in one click", + "simple_desc": "Choose the use case and target. JackAILocal recommends the model, installs the local/USB/SSD target, or publishes a shareable package.", + "simple_goal_label": "Final use", + "simple_target_label": "Target to prepare or publish", + "simple_prepare": "Build / publish after review", + "simple_ready": "Ready. Simple mode hides model, backend, and quantization choices.", + "advanced_options": "Advanced options", + "sample_label": "Hardware profile source", + "profile_src_real": "This machine (real hardware scan)", + "profile_src_upload": "Customer PC — upload the PC analyzer .json", + "profile_src_example": "Example: {name}", + "profile_src_need_json": "No hardware profile provided. Upload the customer's PC analyzer .json (or run the real scan locally), or explicitly pick an example profile.", + "hackathon_badge": "🤗 **[HF Build Small Hackathon](https://huggingface.co/build-small-hackathon)**\n\n`≤ 32B params` · `100% local inference` · `Ollama / llama.cpp / whisper.cpp / Piper`\n\n🔍 Every AI review exposes its **full agent trace** (prompt + raw model output) in the audit JSON — nothing is simulated.", + "package_goal_label": "Package goal", + "target_mode_label": "Target mode", + "target_drive_label": "Target drive or folder", + "hardware_json_label": "Hardware profile JSON (leave empty to use selected profile)", + "plan_initial": "Generate a build plan to see recommendations.", + "agent_review_title": "Local LLM agent review", + "export_manifest": "Export Manifest ZIP", + "export_windows": "Export Windows Builder", + "export_macos": "Export macOS Builder", + "manifest_file": "Manifest ZIP File", + "windows_file": "Windows Builder ZIP", + "macos_file": "macOS Builder ZIP", + "console_title": "Target creation / publication console", + "console_ready": "Terminal ready. Run simple mode or the advanced build to start.", + "published_package": "Package published by simple mode", + "chat_offline": "System offline or stopped. Prepare and start a local runtime to chat.", + "profiler_upload_label": "Upload hardware profile (.json from the PC analyzer)", + "profiler_btn": "Download PC analyzer (customer runs it, sends back the .json)", + "profiler_zip_label": "PC analyzer ZIP", + "tab_light": "Light One-Click Flow", + "tab_advanced": "Advanced Operator Controls", + "refresh_btn": "Refresh", + "why_title": "Why this model", + "why_rules": [ + "- The result is computed from the hardware profile by explicit rules.", + "- The catalog and thresholds are visible.", + "- If custom JSON is provided, it overrides the sample profile.", + "- The standard pack caps the default at 9B so it remains practical on normal laptops.", + "- RTX 3090/24GB-class machines can unlock Qwen3.6 27B or another model under 32B." + ], + "plan_title": "Recommendation Result", + "plan_default_model": "Recommended default model", + "plan_agent_label": "LLM-in-the-loop agent", + "plan_agent_fallback": "Gemma 4 12B `{model_ref}` not selected for this hardware profile; runtime Agent mode can still use Auto with an installed local chat model.", + "plan_hosted_agent": "Hosted packaging agent", + "plan_no_hosted_agent": "No hosted planning agent configured", + "plan_agent_status": "Agent decision status", + "plan_not_configured": "not configured", + "plan_goal": "Package goal", + "plan_backends": "Backends", + "plan_build_id": "Build ID", + "plan_models_title": "Compatible models for this profile", + "plan_packs_title": "Selected offline packs", + "plan_no_packs": "- No pack selected.", + "plan_policy_title": "Product policy", + "plan_policy_rules": [ + "- The default model is a baseline, not a product limit.", + "- Models are downloaded during package preparation, not on every USB insertion." + ], + "flow_title": "Simple flow ready", + "builder_scope_note": "This builder only creates and publishes packages. Chat, documents, and benchmark live in the delivered runtime's interface (START-HERE).", + "oauth_note": "Sign in with Hugging Face to run the AI review: inference is billed to your own HF account.", + "flow_agent_running": "AI configuration agent review in progress...", + "flow_agent_failed": "Agent review unavailable: {detail}. The rule-based plan is applied as-is.", + "flow_target": "Target", + "flow_model": "Recommended local model", + "flow_agent": "AI-in-the-loop agent", + "flow_updates": "Updates: signed manifest + controlled install path", + "flow_enduser": "End user: launch `START-HERE` or the published runtime, without choosing a model or backend", + "err_plan_failed": "Failed to build deployment plan:", + "publish_windows": "Building Windows package for {target}...", + "publish_macos": "Building macOS package for {target}...", + "publish_appliance": "Building Linux appliance package for {target}...", + "success_windows": "Windows builder ZIP ready:", + "success_macos": "macOS builder ZIP ready:", + "success_appliance": "Linux appliance ZIP ready:", + "ready_windows": "Windows package ready for download.", + "ready_macos": "macOS package ready for download.", + "ready_appliance": "Linux/Docker appliance package ready for download.", + "target_ready_started": "Target ready. Runtime started.", + "target_ready_attention": "Target ready. Start needs attention: {detail}", + "console_reset": "Resetting console... Starting target build flow...", + "build_hosted_unavailable": "Physical drive build is unavailable in a hosted Space. Export the real builder ZIP and run it on the target computer.", + "err_select_drive": "Select a real target drive or folder.", + "build_stop_processes": "Ensuring local runtime processes are stopped to release file locks...", + "build_warn_shutdown": "Warning during process shutdown: {detail}", + "build_starting": "Starting physical USB installation...", + "build_copy_ollama": "Copying system Ollama binary to payload directory...", + "build_warn_ollama": "Warning: Could not copy system Ollama: {detail}", + "build_done": "JackAILocal target built and configured successfully!", + "build_failed_rc": "JackAILocal target build failed with exit code {code}.", + "build_failed_start": "Failed to start/run target builder process: {detail}", + "sidebar_model": "Model: {model}", + "err_remote_required": "Remote AI-in-the-loop is required but not configured. Set MODAL_BASE_URL/MODAL_API_KEY or HF_TOKEN in the Space secrets.", + "err_more_info": "AI decision engine requires more information before deployment:\n{questions}", + "err_remote_failed": "Remote AI-in-the-loop failed: {detail}", + "chat_hosted_unavailable": "Local chat is unavailable in the hosted builder. Start a prepared JackAILocal runtime.", + "chat_stats": "**Real local metrics**: {wps} words/s | Latency: {ms} ms | Backend: {backend}", + "chat_api_error": "The verified local API did not return a response: {detail}", + "doc_hosted_unavailable": "Document writes are unavailable in the hosted builder.", + "doc_title_empty": "Title cannot be empty", + "doc_saved": "File saved successfully on drive at: {path}", + "doc_save_error": "Error saving file: {detail}", + "doc_no_target": "No verified local target drive is available.", + "bench_hosted_unavailable": "A real local benchmark is unavailable in the hosted builder.", + "bench_running": "Testing real local inference performance (polling local Rust API daemon)...", + "bench_status_error": "Local API returned status code {code}: {detail}", + "bench_service_error": "Error from local benchmark service: {detail}", + "bench_report": "=== REAL LOCAL INFERENCE BENCHMARK ===\n\nModel Profile: {model}\nInference Latency: {ms} ms\nApproximate output speed: {wps} words/sec\n\nSample Response Text:\n\"{sample}\"\n", + "bench_connect_error": "Could not connect to local Rust API: {detail}", + "bench_initial": "Press 'Run Benchmark' to start local profiling...", + "scan_detected": "Detected: {os} | {cpu} CPU Threads | {ram}GB RAM | {vram}GB VRAM ({gpu})", + "audit_saas_warning_title": "[SaaS Decision Warning]", + "audit_saas_warning_body": "Failed to query cloud decision engine: {detail}", + "audit_saas_skipped_title": "[SaaS Decision]", + "audit_saas_skipped_body": "*(Hosted AI decision engine skipped: No cloud keys configured)*", + "audit_local_warning_title": "[Local LLM Audit Warning]", + "audit_local_warning_body": "*(Local LLM Agent Review skipped or failed: {detail})*", + "agent_review_hosted_title": "Hosted AI-in-the-loop packaging review", + "agent_review_local_title": "Local LLM-in-the-loop review", + "agent_review_ollama_title": "Local Ollama LLM-in-the-loop review", + "agent_lbl_provider": "Provider", + "agent_lbl_model": "Model", + "agent_lbl_agent_model": "Agent model", + "agent_lbl_selected_client": "Selected client model", + "agent_lbl_selected": "Selected model", + "agent_lbl_final_default": "Final default model", + "agent_lbl_decision_status": "Decision status", + "agent_lbl_confidence": "Confidence", + "agent_risk_flags": "Risk flags", + "agent_next_steps": "Next steps", + "agent_none_reported": "- None reported", + "agent_non_json": "The local model returned non-JSON text. Raw answer:", + "agent_gemma_missing": "The configuration agent requires **Gemma 4 12B** (`gemma4:12b`), and it is not installed in the local Ollama.\n\nInstall it, then run the review again:\n\n```\nollama pull gemma4:12b\n```\n\n(`JACKAI_AGENT_ALLOW_FALLBACK=1` allows a smaller fallback agent for testing only.)", + "agent_ollama_query_failed": "Direct local Ollama agent review query failed: {detail}", + "agent_no_runtime": "No local JackAILocal runtime daemon (port 4891) or local Ollama instance (port 11434/11435) responded. Please start your local AI environment before running the agent review.", + "app_title": "JackAILocal - SaaS Builder & Local Runtime Console", + "header_subtitle": "Portable offline AI workspace builder and verified local runtime console", + "lang_label": "Language / Langue", + "footer_text": "JackAILocal v15 Local Runtime", + "hero_title": "Prepare a real local AI package", + "hero_desc": "Pick the use case and target. JackAILocal analyzes hardware, calls the real decision agent when configured, asks for missing information when needed, then prepares a local/offline package.", + "hero_step1_title": "1. Choose", + "hero_step1_desc": "Use case, target, and hardware.", + "hero_step2_title": "2. Decide", + "hero_step2_desc": "Gemma 4 12B IT returns validated JSON.", + "hero_step3_title": "3. Build", + "hero_step3_desc": "Export or write only after policy validation.", + "step1_title": "Step 1: Target & Profile Configuration", + "step3_title": "Step 3: Compile & Export", + "audit_accordion": "AI Verification & Audit (Optional)", + "audit_desc": "Audit the installation configuration using Hosted (SaaS) and Local LLM Agents to verify against target hardware constraints.", + "operator_notes_label": "Operator Notes / Constraints", + "operator_notes_ph": "Example: privacy-first, no LAN by default, voice required, target is a weak laptop.", + "audit_saas_title": "SaaS Configuration", + "answers_label": "Answers to AI questions JSON", + "api_accordion": "Cloud / Local API Provider (Optional override)", + "api_desc": "Configure a real cloud API or local endpoint to run the AI decision engine in the loop.", + "api_provider_label": "API Provider", + "api_model_label": "Model ID", + "api_key_label": "API Key / Token", + "api_key_ph": "Paste API key here...", + "api_url_label": "Custom Base URL (Optional)", + "audit_local_title": "Local LLM Configuration", + "agent_choice_auto": "Prefer Gemma 4 12B if installed, else Auto", + "agent_choice_exact": "Require Gemma 4 12B exactly", + "agent_model_label": "Config agent model", + "audit_run_btn": "Run AI Verification & Audit", + "audit_results_title": "Audit Results", + "audit_initial": "Run the audit before building to verify the configuration.", + "audit_json_label": "Audit Manifest Preview", + "export_accordion": "Export Package Builders & Files", + "dash_runtime_lbl": "Runtime", + "dash_hardware_lbl": "Hardware", + "dash_model_lbl": "Recommended model", + "dash_network_lbl": "Network Security", + "dash_policy_note": "Selected by local policy", + "dash_loopback_note": "Loopback Only", + "dash_checklist_title": "Preparation checklist", + "dash_api_label": "Local API", + "dash_api_stopped": "Stopped", + "dash_api_active": "Active (Port 4891)", + "dash_default_model_label": "Default model", + "dash_api_binding_label": "API binding", + "dash_model_catalog_label": "Model Catalog", + "dash_security_label": "Security limits", + "dash_inactive": "Inactive", + "dash_unavailable": "Unavailable", + "dash_no_backend": "No verified local backend", + "dash_no_hw": "No live hardware status", + "dash_no_gpu": "No live GPU status", + "dash_model_available": "available", + "dash_model_missing": "not installed", + "starter_capability": "Capability Query", + "starter_coding": "Coding Task", + "starter_privacy": "Privacy Query", + "docs_title": "Local Notepad (Real Target Document Sync)", + "doc_content_ph": "Write note text here...", + "bench_title": "Local Inference Benchmark runner", + "policy_body": """### Security & Architectural Policies + +JackAILocal operates on strict security borders between the internet (SaaS) and private local nodes. + +1. **Loopback Only**: + - The offline USB API (`jackailocald`) binds strictly to `127.0.0.1`. + - Normal users cannot expose the API to the local area network (LAN) by default. + +2. **Standard ceiling at 9B**: + - The default configuration caps standard packages at 9 Billion parameters. + - This ensures CPU execution remains functional on common laptops without crashing memory limits. + +3. **Power Profile (up to 32B)**: + - For users with RTX 3090/4090 class hardware or Apple Silicon with 24GB+ Unified memory, the builder recommends larger models (like Qwen 27B) up to the 32B parameter hackathon ceiling. + +4. **LLM-in-the-loop configuration agent**: + - Gemma 4 12B (`gemma4:12b`) is the preferred local agent for client configuration review when installed. + - The agent can advise model selection and config, but it cannot override the local-only and <=32B policy. + +5. **Cryptographic Validation**: + - Every file on the USB is checksummed against a signed `SHA256` manifest. + - Any manual code or binary injection triggers a security mismatch at startup. +""" + } +} + +def lang_key(language: str) -> str: + return "fr" if str(language).upper().startswith("FR") else "en" + +def get_txt(key: str, lang: str) -> str: + lang_code = "FR" if lang == "FR" else "EN" + return L10N[lang_code].get(key, key) + +def simple_goal_choices(language: str) -> list[tuple[str, str]]: + lang_code = "FR" if language == "FR" else "EN" + labels = SIMPLE_GOAL_LABELS[lang_code] + return [(labels[value], value) for value in SIMPLE_GOAL_VALUES] + +def simple_target_choices(language: str) -> list[tuple[str, str]]: + lang_code = "FR" if language == "FR" else "EN" + labels = SIMPLE_TARGET_LABELS_BY_LANG[lang_code] + # Hosted (online) builder can only EXPORT packages: writing a local + # folder/USB/SSD would target the server, not the visitor's machine. + values = SIMPLE_TARGET_VALUES if IS_LOCAL else [v for v in SIMPLE_TARGET_VALUES if v.startswith("publish_")] + return [(labels[value], value) for value in values] + +def simple_target_label(simple_target: str, language: str) -> str: + lang_code = "FR" if language == "FR" else "EN" + return SIMPLE_TARGET_LABELS_BY_LANG[lang_code].get(simple_target, simple_target) + +def package_goal_choices(language: str) -> list[tuple[str, str]]: + if language == "FR": + return [ + ("Assistant hors ligne standard", "Standard offline assistant"), + ("Assistant prive hors ligne", "Privacy offline assistant"), + ("Survie / manuel terrain", "Survival / field manual"), + ("Connaissances et notes", "Knowledge and notes"), + ("Documents avec vision", "Vision documents"), + ("Station de travail puissante", "Power workstation"), + ] + return [ + ("Standard offline assistant", "Standard offline assistant"), + ("Privacy offline assistant", "Privacy offline assistant"), + ("Survival / field manual", "Survival / field manual"), + ("Knowledge and notes", "Knowledge and notes"), + ("Vision documents", "Vision documents"), + ("Power workstation", "Power workstation"), + ] + +def target_mode_choices(language: str) -> list[tuple[str, str]]: + if language == "FR": + return [ + ("Cle USB", "USB"), + ("SSD externe", "SSD"), + ("Dossier local", "LocalFolder"), + ] + return [ + ("USB key", "USB"), + ("External SSD", "SSD"), + ("Local folder", "LocalFolder"), + ] + +# Hardware profile sources. Real sources first; the fictional presets are +# explicitly labelled examples and are only used when the user picks one. +PROFILE_SOURCE_REAL = "__this_machine__" +PROFILE_SOURCE_UPLOAD = "__uploaded_profile__" + +def profile_source_choices(language: str) -> list[tuple[str, str]]: + choices: list[tuple[str, str]] = [] + if IS_LOCAL: + choices.append((get_txt("profile_src_real", language), PROFILE_SOURCE_REAL)) + choices.append((get_txt("profile_src_upload", language), PROFILE_SOURCE_UPLOAD)) + for name in SAMPLE_PROFILES: + choices.append((get_txt("profile_src_example", language).format(name=name), name)) + return choices + +def default_profile_source() -> str: + if IS_LOCAL: + return PROFILE_SOURCE_REAL + # Hosted demo: preselect a labelled example profile so the build console + # works out of the box; provenance stays "sample" in the audit JSON. + return next(iter(SAMPLE_PROFILES)) + +def parse_profile_json(raw_json: str, sample_name: str) -> dict[str, Any]: + if raw_json and raw_json.strip(): + try: + profile = json.loads(raw_json) + except json.JSONDecodeError as exc: + raise gr.Error(f"Invalid hardware profile JSON: {exc}") from exc + if not isinstance(profile, dict): + raise gr.Error("Hardware profile JSON must be a JSON object.") + return profile + if sample_name in SAMPLE_PROFILES: + return dict(SAMPLE_PROFILES[sample_name]) + if sample_name == PROFILE_SOURCE_REAL and IS_LOCAL: + hw = get_real_system_hardware() + if hw: + return dict(hw) + # No silent fallback to a fictional profile: a real profile is required. + raise gr.Error(get_txt("profile_src_need_json", "EN")) + +def _first_number(value: Any, default: float) -> float: + if value is None: + return default + if isinstance(value, (int, float)): + return float(value) + text = str(value) + match = re.search(r"(\d+(?:\.\d+)?)", text) + return float(match.group(1)) if match else default + +def normalize_profile(profile: dict[str, Any]) -> dict[str, Any]: + gpu_name = profile.get("gpu_name", profile.get("gpu", profile.get("gpu_model", "Unknown"))) + ram = profile.get("ram_gb", profile.get("memory_gb", profile.get("total_ram_gb", 8))) + vram = profile.get("vram_gb", profile.get("gpu_vram_gb", profile.get("vram", 0))) + threads = profile.get("cpu_threads", profile.get("threads", profile.get("logical_processors", 4))) + storage = profile.get("usb_storage_gb", profile.get("storage_gb", 128)) + + return { + "os": str(profile.get("os", "windows")).lower(), + "cpu_threads": int(_first_number(threads, 4)), + "ram_gb": float(_first_number(ram, 8)), + "vram_gb": float(_first_number(vram, 0)), + "gpu_name": str(gpu_name), + "disk": str(profile.get("disk", profile.get("drive", "Unknown"))), + "usb_storage_gb": int(_first_number(storage, 128)), + "whichllm": profile.get("whichllm"), + } + +# Physical "32 GB" machines report ~31.7 GB usable; without this slack a +# 24 GB-VRAM workstation misses the power tier by 0.3 GB of RAM. +HARDWARE_GATE_TOLERANCE = 0.97 + +def compatible_models(profile: dict[str, Any]) -> list[dict[str, Any]]: + allowed = [] + for model in MODEL_CATALOG: + if model["params_b"] > MAX_MODEL_PARAMS_B: + continue + ram_ok = profile["ram_gb"] >= model["min_ram_gb"] * HARDWARE_GATE_TOLERANCE + vram_ok = profile["vram_gb"] >= model["min_vram_gb"] * HARDWARE_GATE_TOLERANCE + if ram_ok and vram_ok: + allowed.append(model) + return allowed or [MODEL_CATALOG[0]] + +def select_default_model(allowed: list[dict[str, Any]], profile: dict[str, Any], package_goal: str) -> dict[str, Any]: + goal = str(package_goal).lower() + + if "vision" in goal: + vision = [m for m in allowed if m["task"] == "vision"] + if "power" in goal: + power_vision = [m for m in vision if m["tier"] == "power"] + if power_vision: + return sorted(power_vision, key=lambda m: m["params_b"])[-1] + return sorted(vision, key=lambda m: m["params_b"])[-1] if vision else MODEL_CATALOG[0] + + chat = [m for m in allowed if m["task"] == "chat"] + if not chat: + return allowed[0] + + if "power" in goal or profile["vram_gb"] >= 24: + preferred_order = ["qwen36_power_27b", "qwen35_power_27b", "qwen3_power_30b"] + for model_id in preferred_order: + for model in chat: + if model["id"] == model_id: + return model + + standard_chat = [m for m in chat if m["tier"] == "standard" and m["params_b"] <= 9] + return sorted(standard_chat, key=lambda m: m["params_b"])[-1] if standard_chat else sorted(chat, key=lambda m: m["params_b"])[0] + +def select_content_packs(profile: dict[str, Any], package_goal: str) -> list[dict[str, Any]]: + storage = profile["usb_storage_gb"] + goal = str(package_goal).lower() + selected = [pack for pack in CONTENT_PACKS if pack["recommended"] and storage >= pack["min_storage_gb"]] + + seen = set() + output = [] + for pack in selected: + if pack["id"] not in seen: + seen.add(pack["id"]) + output.append(pack) + return output + +def remote_agent_provider() -> dict[str, Any] | None: + provider = os.getenv("CONFIG_AGENT_PROVIDER", "").strip().lower() + modal_base_url = os.getenv("MODAL_BASE_URL", "").strip().rstrip("/") + modal_api_key = os.getenv("MODAL_API_KEY", "").strip() + hf_token = os.getenv("HF_TOKEN", "").strip() + if provider in {"", "modal", "modal_vllm"} and modal_base_url and modal_api_key: + return { + "id": "modal_vllm", + "base_url": modal_base_url, + "api_key": modal_api_key, + "model": os.getenv("MODEL_ID", REMOTE_CONFIG_AGENT_MODEL_ID).strip() or REMOTE_CONFIG_AGENT_MODEL_ID, + } + if provider in {"", "hf", "huggingface", "hf_inference"} and hf_token: + return { + "id": "hf_inference", + "api_key": hf_token, + "model": os.getenv("MODEL_ID", REMOTE_CONFIG_AGENT_MODEL_ID).strip() or REMOTE_CONFIG_AGENT_MODEL_ID, + "hf_provider": os.getenv("HF_INFERENCE_PROVIDER", "auto").strip() or "auto", + } + return None + +def backend_options_for_model(model: dict[str, Any]) -> list[dict[str, Any]]: + options: list[dict[str, Any]] = [] + model_ref = model.get("model_ref") + if model.get("provider") == "ollama" and model_ref: + options.append({ + "backend": "ollama", + "model_ref": model_ref, + "install": {"type": "ollama", "ref": model_ref}, + "runtime": "local", + }) + gguf_path = GGUF_BY_MODEL_REF.get(str(model_ref)) + if gguf_path: + options.append({ + "backend": "llama.cpp", + "model_path": gguf_path, + "install": {"type": "gguf", "path": gguf_path, "source": "factory_or_builder"}, + "runtime": "local", + }) + hf_repo = VLLM_BY_MODEL_ID.get(str(model.get("id"))) or model.get("hf") + if hf_repo: + options.append({ + "backend": "vllm", + "hf_repo": hf_repo, + "install": {"type": "hf", "repo": hf_repo, "source": "modal_or_server"}, + "runtime": "server", + }) + return options + +def backend_policy(profile: dict[str, Any], default_model: dict[str, Any]) -> list[str]: + backends = ["ollama"] + if GGUF_BY_MODEL_REF.get(str(default_model.get("model_ref"))): + backends.append("llama.cpp") + if VLLM_BY_MODEL_ID.get(str(default_model.get("id"))) or default_model.get("hf"): + backends.append("vllm") + return backends + +def build_backend_plan(profile: dict[str, Any], default_model: dict[str, Any], agent_model: dict[str, Any] | None, allowed: list[dict[str, Any]]) -> dict[str, Any]: + remote_agent = remote_agent_provider() + default_options = backend_options_for_model(default_model) + chat_backend = default_options[0] if default_options else { + "backend": default_model.get("provider", "unknown"), + "model_ref": default_model.get("model_ref"), + "runtime": "unknown", + } + local_agent_options = backend_options_for_model(agent_model) if agent_model else [] + planning_agent = ( + { + "backend": remote_agent["id"], + "model": remote_agent["model"], + "endpoint": remote_agent.get("base_url") or "huggingface_inference_provider", + "runtime": "hosted", + "purpose": "AI-in-the-loop packaging decision", + } + if remote_agent + else None + ) + model_install_plan = { + "ollama": [], + "gguf": [], + "hf": [], + "whisper": ["models/whisper/ggml-base.bin"], + "piper": ["models/piper/en_US-libritts_r-medium.onnx", "models/piper/en_US-libritts_r-medium.onnx.json"], + } + for model in allowed: + for option in backend_options_for_model(model): + install = option.get("install", {}) + if install.get("type") == "ollama" and install.get("ref") not in model_install_plan["ollama"]: + model_install_plan["ollama"].append(install["ref"]) + elif install.get("type") == "gguf" and install.get("path") not in model_install_plan["gguf"]: + model_install_plan["gguf"].append(install["path"]) + elif install.get("type") == "hf" and install.get("repo") not in model_install_plan["hf"]: + model_install_plan["hf"].append(install["repo"]) + + return { + "chat": { + "selected": chat_backend, + "alternatives": default_options, + }, + "planning_agent": planning_agent, + "runtime_config_agent": { + "selected": local_agent_options[0] if local_agent_options else None, + "alternatives": local_agent_options, + }, + "vision": { + "selected": next((backend_options_for_model(m)[0] for m in allowed if m.get("task") == "vision" and backend_options_for_model(m)), None), + "runtime": "local", + }, + "stt": { + "backend": "whisper.cpp", + "binary": "backends/whisper.cpp//whisper-cli", + "model_path": "models/whisper/ggml-base.bin", + "runtime": "local", + }, + "tts": { + "backend": "piper", + "binary": "backends/piper//piper", + "voice_path": "models/piper/en_US-libritts_r-medium.onnx", + "runtime": "local", + }, + "model_install_plan": model_install_plan, + "compatibility": { + "ollama_models_use_ollama_refs": True, + "llama_cpp_models_use_gguf_paths": True, + "vllm_models_use_hf_repos": True, + "voice_models_are_backend_specific": True, + }, + } + +def select_config_agent_model(allowed: list[dict[str, Any]]) -> dict[str, Any] | None: + for model in allowed: + if model["id"] == CONFIG_AGENT_MODEL_ID: + return model + return None + +def build_plan_object(language: str, sample_name: str, raw_profile_json: str, package_goal: str) -> dict[str, Any]: + profile = normalize_profile(parse_profile_json(raw_profile_json, sample_name)) + allowed = compatible_models(profile) + default = select_default_model(allowed, profile, package_goal) + agent_model = select_config_agent_model(allowed) + return { + "product": PRODUCT_NAME, + "mode": PRODUCT_MODE, + "selector_version": SELECTOR_VERSION, + "build_id": str(uuid4()), + "created_at": datetime.now(timezone.utc).isoformat(), + "package_goal": package_goal, + "profile_source": ( + "custom_json" if raw_profile_json and raw_profile_json.strip() + else "real_scan" if sample_name == PROFILE_SOURCE_REAL + else "sample" + ), + "hardware": profile, + "default_model": default, + "agent_model": agent_model, + "llm_in_the_loop": { + "enabled": True, + "preferred_model_ref": CONFIG_AGENT_MODEL_REF, + "selected_agent_model_ref": agent_model["model_ref"] if agent_model else None, + "selected_agent_model_id": agent_model["id"] if agent_model else None, + "runtime_endpoint": "/api/agent/recommend", + "strict_local_runtime": True, + "max_params_b": MAX_MODEL_PARAMS_B, + "fallback_behavior": "Fail explicitly if the requested local agent model is not installed. Use Auto only when the operator allows another installed local model." + }, + "allowed_models": allowed, + "content_packs": select_content_packs(profile, package_goal), + "backends": backend_policy(profile, default), + "backend_plan": build_backend_plan(profile, default, agent_model, allowed), + "agent_decision": { + "status": "not_run", + "provider": None, + "model": None, + "applied": False, + "reason": "Base plan is deterministic. Hosted AI-in-the-loop runs during final export/build when Modal or HF inference secrets are configured." + }, + "max_params_b": MAX_MODEL_PARAMS_B, + "selection_policy": { + "is_hardcoded_static_result": False, + "is_rule_based_catalog": True, + "uses_ai_in_loop": False, + "uses_live_hardware_scan_when_local": IS_LOCAL, + "uses_custom_json_when_provided": True, + "whichllm_is_optional": True, + "standard_pack_cap_b": 9, + "power_pack_cap_b": 32, + "default_is_not_limit": True + }, + "download_policy": "Models and content packs are fetched during package build or factory preload, not during normal USB insertion.", + "runtime_policy": "Windows portable first. Linux bootable is secondary. USB runtime binds to 127.0.0.1 only.", + "product_policy": "The default pack is a practical offline baseline; the customer can keep, remove, or add compatible models based on hardware.", + "security_policy": { + "signed_updates": True, + "sha256_manifest": True, + "local_only": True, + "no_shell_tools_by_default": True, + "no_admin_required_for_normal_use": True + } + } + +def as_bullet_list(value: Any) -> list[str]: + """Normalize an LLM field to a list of strings. A model may return a bare + string for risk_flags/next_steps; iterating it directly would render one + bullet per character, so wrap a non-empty string in a single-item list.""" + if value is None or value == "": + return [] + if isinstance(value, str): + return [value] + if isinstance(value, (list, tuple)): + return [str(item) for item in value if str(item).strip()] + return [str(value)] + +def parse_json_object_from_text(text: str) -> dict[str, Any] | None: + if not text: + return None + stripped = text.strip() + if stripped.startswith("```"): + stripped = re.sub(r"^```(?:json)?\s*", "", stripped, flags=re.IGNORECASE) + stripped = re.sub(r"\s*```$", "", stripped) + try: + value = json.loads(stripped) + return value if isinstance(value, dict) else None + except json.JSONDecodeError: + pass + start = stripped.find("{") + end = stripped.rfind("}") + if start >= 0 and end > start: + try: + value = json.loads(stripped[start:end + 1]) + return value if isinstance(value, dict) else None + except json.JSONDecodeError: + return None + return None + +def agent_messages_for_plan(plan: dict[str, Any], language: str) -> list[dict[str, str]]: + lang = lang_key(language) + system = ( + "You are JackAILocal Packaging Agent. You are in the packaging decision loop, not a chatbot. " + "Use only the provided hardware profile, compatible model list, backend options, content packs, and constraints. " + f"Never select a model above {MAX_MODEL_PARAMS_B}B. Do not invent installed models or unsupported backends. " + "Return only valid JSON with keys: selected_client_model_id, selected_client_model_ref, selected_backend, " + "selected_runtime_agent_model_ref, backend_plan_changes, content_pack_ids, risk_flags, human_summary, next_steps. " + "selected_backend must be one of ollama, llama.cpp, or vllm. " + "The runtime package must remain local-first/offline after model preload; hosted inference is only for packaging intelligence. " + f"Answer human_summary and next_steps in {'French' if lang == 'fr' else 'English'}." + ) + user = json.dumps({ + "task": "Finalize the customer packaging plan.", + "base_plan": plan, + "optimization_goals": [ + "choose the best client default model for the actual hardware", + "choose backend per model family", + "keep the end-user workflow non-technical", + "avoid over-downloading models", + "preserve offline local runtime after build", + ], + }, ensure_ascii=False, indent=2) + return [{"role": "system", "content": system}, {"role": "user", "content": user}] + +def call_modal_vllm_agent(provider: dict[str, Any], messages: list[dict[str, str]]) -> dict[str, Any]: + url = provider["base_url"].rstrip("/") + "/v1/chat/completions" + response = requests.post( + url, + headers={ + "Authorization": f"Bearer {provider['api_key']}", + "Content-Type": "application/json", + }, + json={ + "model": provider["model"], + "messages": messages, + "temperature": 0.1, + "max_tokens": 768, + "response_format": {"type": "json_object"}, + "chat_template_kwargs": {"enable_thinking": False}, + }, + timeout=REMOTE_AGENT_TIMEOUT_SECONDS, + ) + if response.status_code != 200: + raise RuntimeError(f"Modal vLLM agent failed: HTTP {response.status_code} {response.text[:800]}") + payload = response.json() + content = payload.get("choices", [{}])[0].get("message", {}).get("content", "") + parsed = parse_json_object_from_text(content) + if not parsed: + raise RuntimeError("Modal vLLM agent returned non-JSON content") + return {"provider": "modal_vllm", "model": provider["model"], "raw": content, "parsed": parsed, "response": payload} + +def call_hf_inference_agent(provider: dict[str, Any], messages: list[dict[str, str]]) -> dict[str, Any]: + try: + from huggingface_hub import InferenceClient + except ImportError as exc: + raise RuntimeError("huggingface_hub is not installed; add it to the Space requirements") from exc + # Not pinned to one model: try the configured model first, then the shared + # served-model fallback list; keep the first that returns valid JSON. + candidates: list[str] = [provider["model"], *decision_engine.cloud_agent_candidates()] + seen: set[str] = set() + models = [m for m in candidates if m and not (m in seen or seen.add(m))] + last_error = None + for model in models: + try: + client = InferenceClient( + model=model, + provider=provider.get("hf_provider", "auto"), + token=provider["api_key"], + timeout=REMOTE_AGENT_TIMEOUT_SECONDS, + ) + response = client.chat.completions.create( + model=model, + messages=messages, + temperature=0.1, + max_tokens=768, + response_format={"type": "json_object"}, + ) + message = response.choices[0].message + content = (message.content if hasattr(message, "content") else str(message)) or "" + parsed = parse_json_object_from_text(content) + if parsed: + return {"provider": "hf_inference", "model": model, "raw": content, "parsed": parsed, "response": response} + last_error = f"empty/non-JSON from {model}" + except Exception as exc: + last_error = f"{model}: {exc}" + continue + raise RuntimeError(f"HF Inference agent returned no valid JSON (last error: {last_error})") + +def ui_remote_agent_provider() -> dict[str, Any] | None: + """Cloud agent provider configured through the UI: a Hugging Face token + pasted in the API accordion makes the Gemma 4 review work on a hosted + Space even when no HF_TOKEN secret is set.""" + provider = decision_engine.configured_provider() + if not provider or not provider.get("api_key"): + return None + if provider.get("id") == "hf": + return { + "id": "hf_inference", + "api_key": provider["api_key"], + "model": provider.get("model") or REMOTE_CONFIG_AGENT_MODEL_ID, + "hf_provider": "auto", + } + return None + +def oauth_agent_provider(oauth_token) -> dict[str, Any] | None: + """Per-visitor provider from 'Sign in with Hugging Face' (Space OAuth). + Inference is billed to the VISITOR's account, never to the Space owner, + and the token is per-session (unlike env/UI configuration).""" + token_value = getattr(oauth_token, "token", None) if oauth_token else None + if not token_value: + return None + # The visitor's HF token bills HF Inference Providers, so this MUST be a + # model actually served on the HF router (HF_OAUTH_MODEL_ID), kept separate + # from MODEL_ID which may point at a sponsor endpoint (Modal/MiniCPM/etc.). + return { + "id": "hf_inference", + "api_key": token_value, + "model": os.getenv("HF_OAUTH_MODEL_ID", REMOTE_CONFIG_AGENT_MODEL_ID).strip() or REMOTE_CONFIG_AGENT_MODEL_ID, + "hf_provider": os.getenv("HF_INFERENCE_PROVIDER", "auto").strip() or "auto", + } + +def call_remote_config_agent(plan: dict[str, Any], language: str, oauth_token=None) -> dict[str, Any]: + provider = oauth_agent_provider(oauth_token) or remote_agent_provider() or ui_remote_agent_provider() + if not provider: + raise RuntimeError("No remote config agent is configured. Sign in with Hugging Face, set MODAL_BASE_URL/MODAL_API_KEY or HF_TOKEN, or paste a Hugging Face token in the API panel.") + messages = agent_messages_for_plan(plan, language) + if provider["id"] == "modal_vllm": + return call_modal_vllm_agent(provider, messages) + if provider["id"] == "hf_inference": + return call_hf_inference_agent(provider, messages) + raise RuntimeError(f"Unsupported remote agent provider: {provider['id']}") + +def apply_agent_decision(plan: dict[str, Any], agent_result: dict[str, Any]) -> dict[str, Any]: + finalized = copy.deepcopy(plan) + parsed = agent_result["parsed"] + allowed = finalized["allowed_models"] + selected_ref = parsed.get("selected_client_model_ref") or parsed.get("client_model_ref") or parsed.get("selected_model_ref") + selected_id = parsed.get("selected_client_model_id") or parsed.get("client_model_id") + selected_model = None + for model in allowed: + if selected_ref and model.get("model_ref") == selected_ref: + selected_model = model + break + if selected_id and model.get("id") == selected_id: + selected_model = model + break + applied_fields = [] + rejected_fields = [] + if selected_model and selected_model.get("task") == "chat" and selected_model.get("tier") != "agent": + finalized["default_model"] = selected_model + applied_fields.append("default_model") + elif selected_ref or selected_id: + rejected_fields.append({ + "field": "default_model", + "reason": "agent selected a model that is not an allowed client chat model", + "selected_ref": selected_ref, + "selected_id": selected_id, + }) + + selected_backend = parsed.get("selected_backend") + supported_backends = {option.get("backend") for option in backend_options_for_model(finalized["default_model"])} + if selected_backend in {"ollama", "llama.cpp", "vllm"} and selected_backend in supported_backends: + finalized["preferred_backend"] = selected_backend + applied_fields.append("preferred_backend") + elif selected_backend: + rejected_fields.append({ + "field": "preferred_backend", + "reason": "backend is unsupported for the selected client model", + "value": selected_backend, + "supported": sorted(supported_backends), + }) + + agent_model = finalized.get("agent_model") + finalized["backends"] = backend_policy(finalized["hardware"], finalized["default_model"]) + finalized["backend_plan"] = build_backend_plan(finalized["hardware"], finalized["default_model"], agent_model, allowed) + if finalized.get("preferred_backend"): + preferred_option = next( + ( + option for option in finalized["backend_plan"]["chat"].get("alternatives", []) + if option.get("backend") == finalized["preferred_backend"] + ), + None, + ) + if preferred_option: + finalized["backend_plan"]["chat"]["selected"] = preferred_option + finalized["agent_decision"] = { + "status": "applied" if applied_fields else "reviewed", + "provider": agent_result["provider"], + "model": agent_result["model"], + "applied": bool(applied_fields), + "applied_fields": applied_fields, + "rejected_fields": rejected_fields, + "parsed": parsed, + "raw_content": agent_result["raw"], + } + finalized["selection_policy"]["uses_ai_in_loop"] = True + finalized["selection_policy"]["ai_provider"] = agent_result["provider"] + finalized["selection_policy"]["ai_model"] = agent_result["model"] + return finalized + +ALLOWED_INTERNAL_PATCH_KEYS = { + "distribution_mode", + "target_mode", + "requires_usb", + "remote_ai_required", + "selected_backend", + "selected_model", + "selected_client_model_ref", + "selected_runtime_agent_model_ref", + "content_pack_ids", + "phone_access", + "voice_mode", + "scout_vision", + "risk_flags", + "notes", + "policy_override", +} + + +def parse_optional_json_object(raw_json: str, label: str) -> dict[str, Any]: + if not raw_json or not raw_json.strip(): + return {} + try: + parsed = json.loads(raw_json) + except json.JSONDecodeError as exc: + raise gr.Error(f"Invalid {label} JSON: {exc}") from exc + if not isinstance(parsed, dict): + raise gr.Error(f"{label} JSON must be an object.") + return parsed + + +def target_available_for_decision(target_mode: str, target_drive: str) -> bool: + mode = target_mode or "" + if mode in {"USB", "SSD"}: + if not IS_LOCAL: + return False + drives = get_real_drives(mode) + return bool(target_drive and target_drive in drives) + if mode == "LocalFolder": + return bool(target_drive or not IS_LOCAL) + return False + + +def requested_action_for_target(simple_target: str, target_mode: str) -> str: + if simple_target in {"publish_windows", "publish_macos"}: + return "BUILD_ZIP_PACKAGE" + if simple_target == "publish_appliance": + return "PUBLISH_APPLIANCE" + if target_mode in {"USB", "SSD"}: + return "BUILD_USB_PACKAGE" + if target_mode == "LocalFolder": + return "BUILD_LOCAL_FOLDER" + return "BUILD_ZIP_PACKAGE" + + +def build_decision_app_state( + language: str, + plan: dict[str, Any], + raw_profile_json: str, + simple_target: str = "", + target_mode: str = "", + target_drive: str = "", + user_answers_json: str = "", + operator_notes: str = "", + decision_engine_required: bool = False, +) -> dict[str, Any]: + answers = parse_optional_json_object(user_answers_json, "decision answers") + mode = target_mode or simple_target_mode(simple_target) or "" + allowed_compact = [ + { + "id": model.get("id"), + "model_ref": model.get("model_ref"), + "hf": model.get("hf"), + "provider": model.get("provider"), + "params_b": model.get("params_b"), + "task": model.get("task"), + "tier": model.get("tier"), + "features": model.get("features", []), + } + for model in plan.get("allowed_models", []) + ] + return { + "language": lang_key(language), + "product": PRODUCT_NAME, + "selector_version": SELECTOR_VERSION, + "build_id": plan.get("build_id"), + "package_goal": plan.get("package_goal"), + "requested_action": requested_action_for_target(simple_target, mode), + "simple_target": simple_target or "", + "target_mode": mode, + "target_drive": target_drive or "", + "target_available": target_available_for_decision(mode, target_drive), + "running_in_hosted_space": not IS_LOCAL, + "profile_source": plan.get("profile_source") or ("custom_json" if raw_profile_json and raw_profile_json.strip() else "sample"), + "hardware_profile": plan.get("hardware", {}), + "default_model": plan.get("default_model"), + "agent_model": plan.get("agent_model"), + "allowed_models": allowed_compact, + "backend_plan": plan.get("backend_plan", {}), + "content_packs": plan.get("content_packs", []), + "max_params_b": MAX_MODEL_PARAMS_B, + "constraints": { + "no_fake_or_simulated_backend": True, + "local_runtime_after_preload": True, + "max_params_b": MAX_MODEL_PARAMS_B, + "normal_runtime_local_only": True, + "backend_specific_models": True, + }, + "user_answers": answers, + "operator_notes": operator_notes or "", + "decision_engine_required": decision_engine_required, + } + + +def extract_default_features_from_goal(package_goal: str, notes: str) -> dict[str, bool]: + goal = str(package_goal).lower() + notes_lower = str(notes).lower() + + # Heuristics for features based on goals and notes + voice = any(w in goal or w in notes_lower for w in ["voice", "vocal", "talk", "parler", "speech", "tts", "stt"]) + phone = any(w in goal or w in notes_lower for w in ["phone", "mobile", "lan", "network", "remote", "share", "partager"]) + vision = any(w in goal or w in notes_lower for w in ["vision", "scout", "image", "multimodal", "voir", "camera", "photo"]) + + return { + "voice_mode": voice, + "phone_access": phone, + "scout_vision": vision, + } + + +def apply_internal_decision_to_plan(plan: dict[str, Any], decision: dict[str, Any]) -> dict[str, Any]: + finalized = copy.deepcopy(plan) + finalized["internal_decision"] = decision + provider_status = decision.get("provider_status", {}) + applied_fields: list[str] = [] + rejected_fields: list[dict[str, Any]] = [] + + if provider_status.get("configured"): + finalized["selection_policy"]["uses_ai_in_loop"] = True + finalized["selection_policy"]["ai_provider"] = provider_status.get("provider") + finalized["selection_policy"]["ai_model"] = provider_status.get("model") + + # Always initialize default features + defaults = extract_default_features_from_goal(plan.get("package_goal", ""), "") + finalized["features"] = defaults + + if decision.get("decision") in {"ASK_USER", "ASK_HUMAN_REVIEW", "REJECT"} or decision.get("blocked"): + finalized["agent_decision"] = { + "status": str(decision.get("decision", "review_required")).lower(), + "provider": provider_status.get("provider"), + "model": provider_status.get("model"), + "applied": False, + "questions": decision.get("questions", []), + "parsed": decision, + } + return finalized + + patch = { + key: value + for key, value in (decision.get("manifest_patch") or {}).items() + if key in ALLOWED_INTERNAL_PATCH_KEYS + } + finalized["manifest_patch"] = patch + + # Apply features from patch if present, otherwise fall back to defaults + finalized["features"] = { + "voice_mode": patch.get("voice_mode", defaults["voice_mode"]), + "phone_access": patch.get("phone_access", defaults["phone_access"]), + "scout_vision": patch.get("scout_vision", defaults["scout_vision"]), + } + + # Apply content packs from patch if present + content_pack_ids = patch.get("content_pack_ids") + if isinstance(content_pack_ids, list): + finalized["content_packs"] = [ + pack for pack in CONTENT_PACKS if pack["id"] in content_pack_ids + ] + + + selected_ref = ( + decision.get("selected_client_model_ref") + or patch.get("selected_client_model_ref") + or patch.get("selected_model") + or decision.get("selected_model") + ) + selected_model = None + for model in finalized.get("allowed_models", []): + if selected_ref and selected_ref in {model.get("model_ref"), model.get("id"), model.get("hf")}: + selected_model = model + break + if selected_model and selected_model.get("task") == "chat" and selected_model.get("tier") != "agent": + finalized["default_model"] = selected_model + applied_fields.append("default_model") + elif selected_ref: + rejected_fields.append({ + "field": "default_model", + "reason": "selected model is not an allowed client chat model for this hardware profile", + "value": selected_ref, + }) + + selected_backend = patch.get("selected_backend") or decision.get("selected_backend") + if selected_backend in {"ollama", "llama.cpp", "vllm"}: + supported_backends = {option.get("backend") for option in backend_options_for_model(finalized["default_model"])} + if selected_backend in supported_backends: + finalized["preferred_backend"] = selected_backend + applied_fields.append("preferred_backend") + else: + rejected_fields.append({ + "field": "preferred_backend", + "reason": "backend is unsupported for the selected client model", + "value": selected_backend, + "supported": sorted(supported_backends), + }) + + agent_model = finalized.get("agent_model") + finalized["backends"] = backend_policy(finalized["hardware"], finalized["default_model"]) + finalized["backend_plan"] = build_backend_plan(finalized["hardware"], finalized["default_model"], agent_model, finalized["allowed_models"]) + if finalized.get("preferred_backend"): + preferred_option = next( + ( + option for option in finalized["backend_plan"]["chat"].get("alternatives", []) + if option.get("backend") == finalized["preferred_backend"] + ), + None, + ) + if preferred_option: + finalized["backend_plan"]["chat"]["selected"] = preferred_option + + finalized["internal_decision"]["applied_fields"] = applied_fields + finalized["internal_decision"]["rejected_fields"] = rejected_fields + finalized["agent_decision"] = { + "status": "applied" if applied_fields else "reviewed", + "provider": provider_status.get("provider"), + "model": provider_status.get("model"), + "applied": bool(applied_fields), + "applied_fields": applied_fields, + "rejected_fields": rejected_fields, + "parsed": decision, + } + return finalized + + +def decision_requires_user_response(decision: dict[str, Any]) -> bool: + return decision.get("decision") in {"ASK_USER", "ASK_HUMAN_REVIEW", "REJECT"} or bool(decision.get("blocked")) + + +def decision_questions_text(decision: dict[str, Any]) -> str: + questions = decision.get("questions") or [] + if not questions: + return "No question returned." + return "\n".join( + f"- {item.get('label') or item.get('id')}: {item.get('question')} " + f"(type: {item.get('answer_type')}, required: {item.get('required')})" + for item in questions + ) + + +def format_internal_decision_markdown(decision: dict[str, Any], language: str) -> str: + lang = lang_key(language) + provider = decision.get("provider_status", {}) + reasons = decision.get("reasons") or [] + questions = decision.get("questions") or [] + + labels = { + "fr": { + "title": "Moteur de décision IA", + "status": "Décision", + "confidence": "Confiance", + "risk": "Risque", + "provider": "Fournisseur", + "model": "Modèle", + "source": "Source", + "reasons": "Raisons de la recommandation", + "questions": "Questions à résoudre", + "none": "Aucune", + "type": "Type", + "required": "requis", + }, + "en": { + "title": "AI Decision Engine", + "status": "Decision", + "confidence": "Confidence", + "risk": "Risk", + "provider": "Provider", + "model": "Model", + "source": "Source", + "reasons": "Reasons for recommendation", + "questions": "Questions to resolve", + "none": "None", + "type": "Type", + "required": "required", + }, + }[lang] + + reasons_html = ( + "".join(f"
  • {html.escape(str(item))}
  • " for item in reasons) + if reasons + else f"
  • {labels['none']}
  • " + ) + + if questions: + questions_html = "
      " + "".join( + f"
    • {html.escape(str(item.get('label') or item.get('id') or ''))}: " + f"{html.escape(str(item.get('question') or ''))}
      " + f"{labels['type']}: {html.escape(str(item.get('answer_type') or ''))}, " + f"{labels['required']}: {html.escape(str(item.get('required')))}
    • " + for item in questions + ) + "
    " + else: + questions_html = f"

    {labels['none']}

    " + + return f""" +
    +

    {labels['title']}

    +

    {labels['status']}: {html.escape(str(decision.get('decision') or 'n/a'))}

    +

    {labels['confidence']}: {html.escape(str(decision.get('confidence') or 'n/a'))}

    +

    {labels['risk']}: {html.escape(str(decision.get('risk_level') or 'n/a'))}

    +

    {labels['provider']}: {html.escape(str(provider.get('provider') or 'n/a'))}

    +

    {labels['model']}: {html.escape(str(provider.get('model') or 'n/a'))}

    +

    {labels['source']}: {html.escape(str(decision.get('source') or 'n/a'))}

    + +

    {labels['reasons']}

    +
      + {reasons_html} +
    + +

    {labels['questions']}

    + {questions_html} +
    +""" + + +def localized_decision_label(decision_status: str, lang: str) -> str: + labels = { + "BUILD_LOCAL_FOLDER": {"fr": "Créer un dossier local", "en": "Build a local folder"}, + "BUILD_USB_PACKAGE": {"fr": "Préparer une clé USB", "en": "Build a USB package"}, + "BUILD_ZIP_PACKAGE": {"fr": "Créer un package ZIP", "en": "Build a ZIP package"}, + "PUBLISH_APPLIANCE": {"fr": "Publier l'appliance", "en": "Publish the appliance"}, + "ASK_USER": {"fr": "Informations supplémentaires requises", "en": "More information required"}, + "ASK_HUMAN_REVIEW": {"fr": "Validation humaine requise", "en": "Human review required"}, + "REJECT": {"fr": "Configuration refusée", "en": "Configuration rejected"}, + } + return labels.get(decision_status, {}).get(lang, decision_status or "n/a") + + +def localized_provider_label(provider: str, lang: str) -> str: + labels = { + "ollama": {"fr": "Ollama local", "en": "Local Ollama"}, + "local": {"fr": "Serveur local", "en": "Local server"}, + "modal": {"fr": "Modal", "en": "Modal"}, + "hf": {"fr": "Hugging Face", "en": "Hugging Face"}, + "none": {"fr": "Non configuré", "en": "Not configured"}, + "not configured": {"fr": "Non configuré", "en": "Not configured"}, + } + return labels.get(provider, {}).get(lang, provider or "n/a") + + +def plan_summary_card(plan: dict[str, Any], language: str) -> str: + lang = lang_key(language) + model = plan.get("default_model", {}) + backend = plan.get("backend_plan", {}).get("chat", {}).get("selected", {}) + decision = plan.get("internal_decision") or plan.get("agent_decision", {}) + provider = (decision.get("provider_status") or {}).get("provider") or decision.get("provider") or "not configured" + decision_status = decision.get("decision") or decision.get("status", "not_run") + labels = { + "fr": { + "title": "Configuration recommandée", + "model": "Modèle local", + "backend": "Moteur d'exécution", + "action": "Action recommandée", + "decision_engine": "Moteur de décision", + "features": "Fonctionnalités", + "voice": "Mode vocal hors ligne", + "phone": "Accès depuis un téléphone", + "vision": "Analyse d'images", + "yes": "Oui", + "no": "Non", + "packs": "Packs de contenu", + "none": "Aucun", + "build": "Identifiant du build", + }, + "en": { + "title": "Recommended Configuration", + "model": "Local model", + "backend": "Runtime backend", + "action": "Recommended action", + "decision_engine": "Decision engine", + "features": "Features", + "voice": "Offline voice mode", + "phone": "Phone access", + "vision": "Image analysis", + "yes": "Yes", + "no": "No", + "packs": "Content packs", + "none": "None", + "build": "Build ID", + }, + }[lang] + + features = plan.get("features", {}) + feature_rows = [ + (labels["voice"], bool(features.get("voice_mode", False))), + (labels["phone"], bool(features.get("phone_access", False))), + (labels["vision"], bool(features.get("scout_vision", False))), + ] + feature_items = "".join( + f'
  • {feature_label}' + f'' + f'{labels["yes"] if enabled else labels["no"]}
  • ' + for feature_label, enabled in feature_rows + ) + features_html = ( + f'
    {labels["features"]}' + f'
      {feature_items}
    ' + ) + + packs = plan.get("content_packs", []) + if packs: + packs_list = "".join( + f"
  • {html.escape(str(pack.get('label_fr' if lang == 'fr' else 'label_en') or pack.get('id') or ''))}
  • " + for pack in packs + ) + packs_html = f"
    {labels['packs']}
      {packs_list}
    " + else: + packs_html = f"

    {labels['packs']}
    {labels['none']}

    " + + decision_label = localized_decision_label(str(decision_status), lang) + provider_label = localized_provider_label(str(provider), lang) + + return f""" +
    +

    {labels['title']}

    +

    {labels['model']}
    {html.escape(str(model.get('model_ref', 'n/a')))}

    +

    {labels['backend']}
    {html.escape(str(backend.get('backend', 'n/a')))}

    +

    {labels['action']}
    {html.escape(decision_label)} {html.escape(str(decision_status))}

    +

    {labels['decision_engine']}
    {html.escape(provider_label)}

    + {features_html} + {packs_html} +

    {labels['build']}
    {html.escape(str(plan.get('build_id', 'n/a')))}

    +
    +""" + + + +def run_internal_decision_ui( + language: str, + sample_name: str, + raw_profile_json: str, + package_goal: str, + simple_target: str, + target_mode: str, + target_drive: str, + decision_answers_json: str, + operator_notes: str, +) -> tuple[str, str]: + plan = build_plan_object(language, sample_name, raw_profile_json, package_goal) + app_state = build_decision_app_state( + language, + plan, + raw_profile_json, + simple_target=simple_target, + target_mode=target_mode, + target_drive=target_drive, + user_answers_json=decision_answers_json, + operator_notes=operator_notes, + decision_engine_required=True, + ) + try: + decision = decision_engine.decide_internal_action(app_state) + except Exception as exc: + raise gr.Error(f"AI decision engine failed: {exc}") from exc + finalized = apply_internal_decision_to_plan(plan, decision) + return ( + plan_summary_card(finalized, language) + format_internal_decision_markdown(decision, language), + json.dumps(finalized, indent=2, ensure_ascii=False), + ) + + +def run_unified_ai_audit( + language: str, + sample_name: str, + raw_profile_json: str, + package_goal: str, + simple_target: str, + target_mode: str, + target_drive: str, + decision_answers_json: str, + operator_notes: str, + agent_model_choice: str, + oauth_token: "gr.OAuthToken | None" = None, +) -> tuple[str, str]: + plan = build_plan_object(language, sample_name, raw_profile_json, package_goal) + + # 1. Run Hosted/SaaS Decision Engine (if configured) + has_remote = False + decision_card = "" + if decision_engine.configured_provider(): + app_state = build_decision_app_state( + language, + plan, + raw_profile_json, + simple_target=simple_target, + target_mode=target_mode, + target_drive=target_drive, + user_answers_json=decision_answers_json, + operator_notes=operator_notes, + decision_engine_required=False, + ) + try: + decision = decision_engine.decide_internal_action(app_state) + plan = apply_internal_decision_to_plan(plan, decision) + decision_card = plan_summary_card(plan, language) + format_internal_decision_markdown(decision, language) + has_remote = True + except Exception as exc: + decision_card = f"### {get_txt('audit_saas_warning_title', language)}\n{get_txt('audit_saas_warning_body', language).format(detail=exc)}\n" + else: + decision_card = f"### {get_txt('audit_saas_skipped_title', language)}\n{get_txt('audit_saas_skipped_body', language)}\n" + + # 2. Run Local LLM Agent Review (if Ollama or local daemon is running) + agent_markdown = "" + try: + agent_markdown, plan_json_str = run_agent_plan_review( + language, + sample_name, + raw_profile_json, + package_goal, + agent_model_choice, + operator_notes, + oauth_token=oauth_token, + ) + plan = json.loads(plan_json_str) + except Exception as exc: + agent_markdown = f"\n\n### {get_txt('audit_local_warning_title', language)}\n{get_txt('audit_local_warning_body', language).format(detail=exc)}" + + # 3. Combine reports + merged_markdown = "" + if has_remote: + merged_markdown += decision_card + merged_markdown += "\n---\n" + merged_markdown += agent_markdown + else: + merged_markdown += decision_card + merged_markdown += "\n" + merged_markdown += agent_markdown + + # Attach the full agent trace (prompt + raw model output + validated + # decision): verifiable proof that a real LLM call produced the review. + display_payload = dict(plan) + trace = decision_engine.get_last_decision_trace() + if trace: + display_payload["agent_trace"] = trace + return merged_markdown, json.dumps(display_payload, indent=2, ensure_ascii=False) + + +def build_final_plan_object( + language: str, + sample_name: str, + raw_profile_json: str, + package_goal: str, + require_remote_agent: bool | None = None, + simple_target: str = "", + target_mode: str = "", + target_drive: str = "", + decision_answers_json: str = "", + operator_notes: str = "", +) -> dict[str, Any]: + plan = build_plan_object(language, sample_name, raw_profile_json, package_goal) + must_use_agent = REMOTE_AGENT_REQUIRED if require_remote_agent is None else require_remote_agent + app_state = build_decision_app_state( + language, + plan, + raw_profile_json, + simple_target=simple_target, + target_mode=target_mode, + target_drive=target_drive, + user_answers_json=decision_answers_json, + operator_notes=operator_notes, + decision_engine_required=must_use_agent, + ) + if not decision_engine.configured_provider(): + if must_use_agent: + raise gr.Error(get_txt("err_remote_required", language)) + plan["internal_decision"] = decision_engine.decide_internal_action(app_state) + return plan + try: + decision = decision_engine.decide_internal_action(app_state) + finalized = apply_internal_decision_to_plan(plan, decision) + block_on_questions = os.getenv("BLOCK_ON_AI_DECISION_QUESTIONS", "true").lower() not in {"0", "false", "no"} + if decision_requires_user_response(decision) and (must_use_agent or block_on_questions): + raise gr.Error( + get_txt("err_more_info", language).format(questions=decision_questions_text(decision)) + ) + return finalized + except Exception as exc: + if must_use_agent: + raise gr.Error(get_txt("err_remote_failed", language).format(detail=exc)) from exc + plan["internal_decision"] = { + "status": "failed", + "provider": (decision_engine.provider_status() or {}).get("provider"), + "model": (decision_engine.provider_status() or {}).get("model"), + "applied": False, + "error": str(exc), + } + return plan + +def plan_to_markdown(plan: dict[str, Any], language: str) -> str: + lang = lang_key(language) + default = plan["default_model"] + agent = plan.get("agent_model") + label_key = "label_fr" if lang == "fr" else "label_en" + notes_key = "notes_fr" if lang == "fr" else "notes_en" + agent_line = ( + f"{agent[label_key]} `{agent['model_ref']}`" + if agent + else get_txt("plan_agent_fallback", language).format(model_ref=CONFIG_AGENT_MODEL_REF) + ) + agent_decision = plan.get("agent_decision", {}) + agent_status = agent_decision.get("status", "not_run") + agent_provider = agent_decision.get("provider") or get_txt("plan_not_configured", language) + planning_agent = plan.get("backend_plan", {}).get("planning_agent") + planning_agent_line = ( + f"{planning_agent.get('backend')} `{planning_agent.get('model')}`" + if planning_agent + else get_txt("plan_no_hosted_agent", language) + ) + + model_rows = [] + for model in plan["allowed_models"]: + model_rows.append( + f"- **{model[label_key]}**: `{model['model_ref']}` / {model['params_b']}B / " + f"{model['task']} / {model['tier']} / {model[notes_key]}" + ) + + pack_rows = [] + for pack in plan["content_packs"]: + pack_rows.append(f"- **{pack[label_key]}** (`{pack['id']}`)") + + why_rows = "\n".join(get_txt("why_rules", language)) + policy_rows = "\n".join(get_txt("plan_policy_rules", language)) + return f""" +### {get_txt("plan_title", language)} + +**{get_txt("plan_default_model", language)}**: {default[label_key]} `{default['model_ref']}` +**{get_txt("plan_agent_label", language)}**: {agent_line} +**{get_txt("plan_hosted_agent", language)}**: {planning_agent_line} +**{get_txt("plan_agent_status", language)}**: `{agent_status}` / `{agent_provider}` +**{get_txt("plan_goal", language)}**: {plan['package_goal']} +**{get_txt("plan_backends", language)}**: {', '.join(plan['backends'])} +**{get_txt("plan_build_id", language)}**: `{plan['build_id']}` + +### {get_txt("why_title", language)} +{why_rows} + +### {get_txt("plan_models_title", language)} +{chr(10).join(model_rows)} + +### {get_txt("plan_packs_title", language)} +{chr(10).join(pack_rows) if pack_rows else get_txt("plan_no_packs", language)} + +### {get_txt("plan_policy_title", language)} +{policy_rows} +""" + +def build_plan(language: str, sample_name: str, raw_profile_json: str, package_goal: str) -> tuple[str, str]: + plan = build_plan_object(language, sample_name, raw_profile_json, package_goal) + return json.dumps(plan, indent=2, ensure_ascii=False), plan_to_markdown(plan, language) + +def export_manifest_zip( + language: str, + sample_name: str, + raw_profile_json: str, + package_goal: str, + simple_target: str = "", + target_mode: str = "", + target_drive: str = "", + decision_answers_json: str = "", + operator_notes: str = "", +) -> str: + plan = build_final_plan_object( + language, + sample_name, + raw_profile_json, + package_goal, + simple_target=simple_target, + target_mode=target_mode, + target_drive=target_drive, + decision_answers_json=decision_answers_json, + operator_notes=operator_notes, + ) + with tempfile.NamedTemporaryFile(delete=False, suffix=".zip", prefix="jackailocal-build-manifest-") as tmp: + output_path = tmp.name + readme = f"""# JackAILocal Build Manifest + +Build ID: {plan['build_id']} +Selector: {SELECTOR_VERSION} +Default model: {plan['default_model']['model_ref']} +Agent model: {(plan.get('agent_model') or {}).get('model_ref') or 'not selected for this hardware profile'} +Planning agent: {plan.get('agent_decision', {}).get('provider') or 'not configured'} / {plan.get('agent_decision', {}).get('model') or 'n/a'} +Agent decision: {plan.get('agent_decision', {}).get('status')} +Package goal: {plan['package_goal']} + +This manifest ZIP drives the real builder. It does not contain the runtime payload by itself. +Use a platform builder ZIP to install the runtime and preload models on a target drive. +""" + with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as archive: + archive.writestr("manifest/build-manifest.json", json.dumps(plan, indent=2, ensure_ascii=False).encode("utf-8")) + archive.writestr("manifest/model-policy.json", json.dumps({ + "max_params_b": MAX_MODEL_PARAMS_B, + "default_is_not_limit": True, + "standard_pack_cap_b": 9, + "power_pack_cap_b": 32, + "selector_version": SELECTOR_VERSION + }, indent=2).encode("utf-8")) + archive.writestr("examples/hardware-profile-gpu-workstation.json", json.dumps(SAMPLE_PROFILES["GPU workstation (24 GB VRAM)"], indent=2).encode("utf-8")) + archive.writestr("examples/hardware-profile-normal-laptop.json", json.dumps(SAMPLE_PROFILES["Normal laptop"], indent=2).encode("utf-8")) + archive.writestr("README.md", readme.encode("utf-8")) + return output_path + +def export_profiler_zip() -> str: + """ZIP of the standalone PC analyzer the customer runs on their own machine.""" + with tempfile.NamedTemporaryFile(delete=False, suffix=".zip", prefix="jackailocal-pc-analyzer-") as tmp: + output_path = tmp.name + profiler_dir = os.path.join(WORKSPACE_ROOT, "profiler") + names = ["README.txt", "SCAN-MY-PC.cmd", "scan-windows.ps1", "SCAN-MY-MAC.command", "scan-unix.sh"] + added = 0 + with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as archive: + for name in names: + path = os.path.join(profiler_dir, name) + if os.path.isfile(path): + archive.write(path, f"JackAILocal-PC-Analyzer/{name}") + added += 1 + if added == 0: + raise gr.Error("Profiler files are missing from the workspace (profiler/ folder).") + return output_path + +def load_profile_file(file_path: str | None) -> str: + """Reads an uploaded hardware-profile JSON into the profile textbox.""" + if not file_path: + return "" + try: + with open(file_path, "r", encoding="utf-8-sig") as handle: + content = handle.read() + parsed = json.loads(content) + except Exception as error: + raise gr.Error(f"Invalid hardware profile file: {error}") + if not isinstance(parsed, dict) or not any( + key in parsed for key in ("ram_gb", "cpu_name", "vram_gb", "gpus", "os") + ): + raise gr.Error("This JSON file is not a JackAILocal hardware profile (no hardware keys found).") + return content + +def export_platform_builder_zip( + language: str, + sample_name: str, + raw_profile_json: str, + package_goal: str, + platform: str, + simple_target: str = "", + target_mode: str = "", + target_drive: str = "", + decision_answers_json: str = "", + operator_notes: str = "", +) -> str: + platform_key = "macos" if "mac" in str(platform).lower() else "windows" + plan = build_final_plan_object( + language, + sample_name, + raw_profile_json, + package_goal, + simple_target=simple_target or f"publish_{platform_key}", + target_mode=target_mode, + target_drive=target_drive, + decision_answers_json=decision_answers_json, + operator_notes=operator_notes, + ) + prefix = "jackailocal-macos-builder-" if platform_key == "macos" else "jackailocal-windows-builder-" + # Core (small) files must always be present; the heavy native binaries are + # the runtime payload. On a hosted Space those binaries are not shipped + # (HF rejects large binaries), so we still export a "lite" builder whose + # START-HERE script fetches the runtime locally on first run. + core_files = [ + "config/voice-assets.json", + "webui/index.html", + "webui/app-v15.js", + ] + if platform_key == "windows": + binary_payload = ["bin/jackailocald.exe", "backends/ollama/windows/ollama.exe"] + else: + binary_payload = ["bin/jackailocald", "backends/ollama/macos/ollama", "backends/whisper.cpp/macos/whisper-cli"] + missing_core = [p for p in core_files if not os.path.isfile(os.path.join(WORKSPACE_ROOT, p))] + if missing_core: + raise gr.Error( + f"Cannot export a {platform_key} builder. Missing core files: {', '.join(missing_core)}" + ) + missing_binaries = [p for p in binary_payload if not os.path.isfile(os.path.join(WORKSPACE_ROOT, p))] + if missing_binaries and IS_LOCAL: + # Locally the binaries are expected; their absence is a real error. + raise gr.Error( + f"Cannot export a real {platform_key} builder. Missing required payload files: {', '.join(missing_binaries)}" + ) + lite_build = bool(missing_binaries) + payload_items = [ + "START-HERE.cmd", "START-DESKTOP.cmd", "STOP-JACKAILOCAL.cmd", "START-HERE.command", "STOP-JACKAILOCAL.command", + "BUILD-LOCAL.cmd", "BUILD-USB.cmd", "BUILD-SSD.cmd", "BUILD-LOCAL-MAC.command", "BUILD-USB-MAC.command", "BUILD-SSD-MAC.command", "README-FIRST.txt", "autorun.inf", + "webui", "windows", "macos", "unix", "config", "manifest", "models", "backends", "bin", + "docs", "tools", "scripts", "updates", "licenses", "client-builder", "content", "content-packs", + "installer", "deploy", + ] + with tempfile.NamedTemporaryFile(delete=False, suffix=".zip", prefix=prefix) as tmp: + output_path = tmp.name + + plan["hosted_lite_build"] = lite_build + plan["bundled_native_binaries"] = not lite_build + manifest_bytes = json.dumps(plan, indent=2, ensure_ascii=False).encode("utf-8") + with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as archive: + archive.writestr("manifest/build-manifest.json", manifest_bytes) + if lite_build: + payload_note = ( + "This is a LITE package exported from the hosted demo Space. It contains " + "the real builder scripts, WebUI and the AI-reviewed build manifest, but " + "NOT the native runtime binaries (the hosted Space cannot ship them).\n\n" + "It is meant for inspection of the plan/manifest. To actually BUILD and RUN " + "JackAILocal locally, use the full package (the GitHub repository, which " + "includes the binaries) and run BUILD-LOCAL.cmd / BUILD-USB.cmd / BUILD-SSD.cmd. " + "The builder also works if a system-wide Ollama is already installed, but it " + "still requires bin/jackailocald.exe from the full package.\n\n" + f"Binaries not bundled: {', '.join(binary_payload)}\n" + ) + else: + payload_note = ( + "This package contains the real JackAILocal builder and runtime payload. " + "The embedded manifest drives local model installation.\n" + ) + archive.writestr("README.md", ( + f"# JackAILocal {platform_key} Builder{' (lite)' if lite_build else ''}\n\n" + f"Build ID: {plan['build_id']}\n" + f"Default model: {plan['default_model']['model_ref']}\n\n" + f"Agent model: {(plan.get('agent_model') or {}).get('model_ref') or 'not selected for this hardware profile'}\n\n" + + payload_note + ).encode("utf-8")) + for item in payload_items: + source = os.path.join(WORKSPACE_ROOT, item) + if os.path.isfile(source): + archive.write(source, item) + elif os.path.isdir(source): + for current_root, dirs, files in os.walk(source): + dirs[:] = [name for name in dirs if name not in {".git", "__pycache__", "target", ".jackailocal-builder", "license-keys"}] + for filename in files: + full_path = os.path.join(current_root, filename) + relative = os.path.relpath(full_path, WORKSPACE_ROOT).replace("\\", "/") + if relative == "config/update-private-key.xml" or filename.endswith(".pyc"): + continue + archive.write(full_path, relative) + return output_path + +def _write_payload_item(archive: zipfile.ZipFile, item: str) -> None: + source = os.path.join(WORKSPACE_ROOT, item) + if os.path.isfile(source): + archive.write(source, item.replace("\\", "/")) + return + if not os.path.isdir(source): + return + excluded_dirs = {".git", "__pycache__", "target", ".jackailocal-builder", ".jackailocal", "workspace", "diagnostics", "license-keys"} + for current_root, dirs, files in os.walk(source): + dirs[:] = [name for name in dirs if name not in excluded_dirs] + for filename in files: + if filename.endswith(".pyc"): + continue + full_path = os.path.join(current_root, filename) + relative = os.path.relpath(full_path, WORKSPACE_ROOT).replace("\\", "/") + if relative == "config/update-private-key.xml": + continue + archive.write(full_path, relative) + +def export_appliance_builder_zip( + language: str, + sample_name: str, + raw_profile_json: str, + package_goal: str, + decision_answers_json: str = "", + operator_notes: str = "", +) -> str: + plan = build_final_plan_object( + language, + sample_name, + raw_profile_json, + package_goal, + simple_target="publish_appliance", + decision_answers_json=decision_answers_json, + operator_notes=operator_notes, + ) + required_files = [ + "Cargo.toml", + "Cargo.lock", + "deploy/appliance/Dockerfile", + "deploy/appliance/docker-compose.yml", + "deploy/appliance/entrypoint.sh", + "src/main.rs", + "config/jackailocal.appliance.toml", + "config/voice-assets.json", + "webui/index.html", + "webui/app-v15.js", + ] + missing = [path for path in required_files if not os.path.isfile(os.path.join(WORKSPACE_ROOT, path))] + if missing: + raise gr.Error( + f"Cannot export a Linux appliance package. Missing required files: {', '.join(missing)}" + ) + + with tempfile.NamedTemporaryFile(delete=False, suffix=".zip", prefix="jackailocal-linux-appliance-") as tmp: + output_path = tmp.name + + model_ref = plan["default_model"]["model_ref"] + readme = f"""# JackAILocal Linux Appliance + +Build ID: {plan['build_id']} +Default model: {model_ref} + +This package publishes a Docker/Linux runtime for a mini-PC, NAS, or local server. + +Quick start: + +```bash +cd deploy/appliance +cp .env.example .env +docker compose up --build -d +./show-url.sh +``` + +The first start preloads the selected Ollama model into the appliance volume. +After preload, normal use stays local. +""" + + payload_items = [ + "Cargo.toml", "Cargo.lock", "src", + "deploy/appliance", "factory/unix", "unix", + "webui", "config", "manifest", "content", "content-packs", "licenses", + "docs", "README-FIRST.txt", + ] + + with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as archive: + archive.writestr("manifest/build-manifest.json", json.dumps(plan, indent=2, ensure_ascii=False).encode("utf-8")) + archive.writestr("README.md", readme.encode("utf-8")) + archive.writestr("deploy/appliance/.env.example", ( + f"JACKAILOCAL_MODEL={model_ref}\n" + "JACKAILOCAL_PORT=4891\n" + ).encode("utf-8")) + for item in payload_items: + _write_payload_item(archive, item) + return output_path + +def simple_package_goal(simple_goal: str, fallback_goal: str = "Standard offline assistant") -> str: + return SIMPLE_GOAL_TO_PACKAGE_GOAL.get(simple_goal, fallback_goal) + +def simple_target_mode(simple_target: str) -> str | None: + return SIMPLE_TARGET_MODES.get(simple_target) + +def default_local_target_path() -> str: + return os.path.join(os.environ.get("LOCALAPPDATA") or os.path.expanduser("~"), "JackAILocal") + +def target_drive_choices_for_mode(mode: str, current_value: str = "") -> tuple[list[str], str]: + drives = get_real_drives(mode) + fallback = default_local_target_path() if mode == "LocalFolder" else "E:\\" + choices = list(drives) if drives else [fallback] + value = fallback if mode == "LocalFolder" else (current_value or (drives[0] if drives else fallback)) + if mode != "LocalFolder" and drives and value not in drives: + value = drives[0] + if value and value not in choices: + choices.append(value) + return choices, value + +def sync_simple_goal(simple_goal: str) -> dict: + return gr.update(value=simple_package_goal(simple_goal)) + +def sync_simple_target(simple_target: str, current_drive: str) -> tuple[dict, dict]: + mode = simple_target_mode(simple_target) or "LocalFolder" + choices, value = target_drive_choices_for_mode(mode, current_drive) + return gr.update(value=mode), gr.update(choices=choices, value=value) + +def simple_flow_summary(plan: dict[str, Any], simple_target: str, language: str) -> str: + target_label = simple_target_label(simple_target, language) + model = plan["default_model"]["model_ref"] + decision = plan.get("internal_decision") or plan.get("agent_decision", {}) + provider = ( + (decision.get("provider_status") or {}).get("provider") + or decision.get("provider") + or get_txt("plan_not_configured", language) + ) + ai_status = decision.get("decision") or decision.get("status", "not_run") + ai_line = f"{ai_status} / {provider}" + return ( + f"### {get_txt('flow_title', language)}\n\n" + f"- {get_txt('flow_target', language)}: **{target_label}**\n" + f"- {get_txt('flow_model', language)}: `{model}`\n" + f"- {get_txt('flow_agent', language)}: `{ai_line}`\n" + f"- {get_txt('flow_updates', language)}\n" + f"- {get_txt('flow_enduser', language)}\n" + ) + +def run_one_click_flow( + language: str, + simple_goal: str, + simple_target: str, + sample_name: str, + raw_profile_json: str, + target_drive: str, + decision_answers_json: str = "", + operator_notes: str = "", + oauth_token: "gr.OAuthToken | None" = None, +): + package_goal = simple_package_goal(simple_goal) + mode = simple_target_mode(simple_target) or "LocalFolder" + if mode == "LocalFolder": + target_drive = default_local_target_path() + try: + plan = build_final_plan_object( + language, + sample_name, + raw_profile_json, + package_goal, + simple_target=simple_target, + target_mode=mode, + target_drive=target_drive, + decision_answers_json=decision_answers_json, + operator_notes=operator_notes, + ) + except Exception as exc: + err_msg = f"[ERROR] {get_txt('err_plan_failed', language)}\n{exc}" + yield ( + err_msg, + False, + get_sidebar_status(False, False, language, "None")[0], + gr.update(visible=True), + gr.update(visible=False), + err_msg, + None, + "", + "", + ) + return + plan_json_text = json.dumps(plan, indent=2, ensure_ascii=False) + plan_md_text = plan_to_markdown(plan, language) + target_label = simple_target_label(simple_target, language) + summary = simple_flow_summary(plan, simple_target, language) + + # The config agent (Gemma 4 12B) is part of the one-click flow, not an + # optional advanced step: its review is shown in the console before the + # build starts. If no local agent is reachable, say so explicitly. + yield ( + f"[AGENT] {get_txt('flow_agent_running', language)}", + False, + get_sidebar_status(False, False, language, plan["default_model"]["model_ref"])[0], + gr.update(visible=False), + gr.update(visible=False), + f"{plan_md_text}\n\n*{get_txt('flow_agent_running', language)}*", + None, + plan_json_text, + plan_md_text, + ) + try: + audit_md, _audit_json = run_agent_plan_review( + language, sample_name, raw_profile_json, package_goal, "auto", operator_notes or "", + oauth_token=oauth_token, + ) + except Exception as exc: + audit_md = get_txt("flow_agent_failed", language).format(detail=exc) + summary = f"{plan_md_text}\n\n---\n\n{audit_md}\n\n---\n\n{summary}" + + if simple_target == "publish_windows": + yield ( + f"[PUBLISH] {get_txt('publish_windows', language).format(target=target_label)}", + False, + get_sidebar_status(False, False, language, plan["default_model"]["model_ref"])[0], + gr.update(visible=False), + gr.update(visible=False), + summary, + None, + plan_json_text, + plan_md_text, + ) + package_path = export_platform_builder_zip( + language, + sample_name, + raw_profile_json, + package_goal, + "windows", + simple_target, + mode, + target_drive, + decision_answers_json, + operator_notes, + ) + msg = "\n" + get_txt("ready_windows", language) + yield ( + f"[SUCCESS] {get_txt('success_windows', language)}\n{package_path}", + False, + get_sidebar_status(False, False, language, plan["default_model"]["model_ref"])[0], + gr.update(visible=False), + gr.update(visible=False), + summary + msg, + package_path, + plan_json_text, + plan_md_text, + ) + return + + if simple_target == "publish_macos": + yield ( + f"[PUBLISH] {get_txt('publish_macos', language).format(target=target_label)}", + False, + get_sidebar_status(False, False, language, plan["default_model"]["model_ref"])[0], + gr.update(visible=False), + gr.update(visible=False), + summary, + None, + plan_json_text, + plan_md_text, + ) + package_path = export_platform_builder_zip( + language, + sample_name, + raw_profile_json, + package_goal, + "macos", + simple_target, + mode, + target_drive, + decision_answers_json, + operator_notes, + ) + msg = "\n" + get_txt("ready_macos", language) + yield ( + f"[SUCCESS] {get_txt('success_macos', language)}\n{package_path}", + False, + get_sidebar_status(False, False, language, plan["default_model"]["model_ref"])[0], + gr.update(visible=False), + gr.update(visible=False), + summary + msg, + package_path, + plan_json_text, + plan_md_text, + ) + return + + if simple_target == "publish_appliance": + yield ( + f"[PUBLISH] {get_txt('publish_appliance', language).format(target=target_label)}", + False, + get_sidebar_status(False, False, language, plan["default_model"]["model_ref"])[0], + gr.update(visible=False), + gr.update(visible=False), + summary, + None, + plan_json_text, + plan_md_text, + ) + package_path = export_appliance_builder_zip( + language, + sample_name, + raw_profile_json, + package_goal, + decision_answers_json, + operator_notes, + ) + msg = "\n" + get_txt("ready_appliance", language) + yield ( + f"[SUCCESS] {get_txt('success_appliance', language)}\n{package_path}", + False, + get_sidebar_status(False, False, language, plan["default_model"]["model_ref"])[0], + gr.update(visible=False), + gr.update(visible=False), + summary + msg, + package_path, + plan_json_text, + plan_md_text, + ) + return + + for log_text, ready, sidebar_html, start_update, stop_update in run_physical_usb_build( + language, + sample_name, + raw_profile_json, + package_goal, + target_drive, + mode, + ): + ready_note = "" + if ready: + started, start_detail = start_target_runtime_no_browser(target_drive) + sidebar_html, start_update, stop_update = get_sidebar_status( + ready, + started, + language, + plan["default_model"]["model_ref"], + ) + if started: + ready_note = "\n" + get_txt("target_ready_started", language) + else: + ready_note = "\n" + get_txt("target_ready_attention", language).format(detail=start_detail) + yield ( + log_text, + ready, + sidebar_html, + start_update, + stop_update, + summary + ready_note, + None, + plan_json_text, + plan_md_text, + ) + +def run_agent_plan_review(language: str, sample_name: str, raw_profile_json: str, package_goal: str, agent_model_choice: str, customer_notes: str, oauth_token=None) -> tuple[str, str]: + plan = build_plan_object(language, sample_name, raw_profile_json, package_goal) + if oauth_agent_provider(oauth_token) or remote_agent_provider() or ui_remote_agent_provider(): + plan["operator_notes"] = customer_notes or "" + result = call_remote_config_agent(plan, language, oauth_token) + finalized = apply_agent_decision(plan, result) + parsed = result["parsed"] + summary = parsed.get("human_summary") or result["raw"] + selected = parsed.get("selected_client_model_ref") or parsed.get("selected_model_ref") or finalized["default_model"]["model_ref"] + markdown = f"""### {get_txt("agent_review_hosted_title", language)} + +**{get_txt("agent_lbl_provider", language)}**: `{result['provider']}` +**{get_txt("agent_lbl_model", language)}**: `{result['model']}` +**{get_txt("agent_lbl_selected_client", language)}**: `{selected}` +**{get_txt("agent_lbl_final_default", language)}**: `{finalized['default_model']['model_ref']}` +**{get_txt("agent_lbl_decision_status", language)}**: `{finalized['agent_decision']['status']}` + +{summary} + +**{get_txt("agent_risk_flags", language)}** +{chr(10).join(f"- {item}" for item in as_bullet_list(parsed.get("risk_flags"))) if as_bullet_list(parsed.get("risk_flags")) else get_txt("agent_none_reported", language)} + +**{get_txt("agent_next_steps", language)}** +{chr(10).join(f"- {item}" for item in as_bullet_list(parsed.get("next_steps"))) if as_bullet_list(parsed.get("next_steps")) else get_txt("agent_none_reported", language)} +""" + return markdown, json.dumps(finalized, indent=2, ensure_ascii=False) + + agent_model = agent_model_choice or "auto" + payload = { + "language": lang_key(language), + "package_goal": package_goal, + "hardware": plan["hardware"], + "default_model": plan["default_model"], + "candidate_models": plan["allowed_models"], + "content_packs": plan["content_packs"], + "current_config": { + "target": "Gradio builder manifest", + "build_id": plan["build_id"], + "llm_in_the_loop": plan["llm_in_the_loop"], + }, + "customer_notes": customer_notes or "", + "agent_model": agent_model, + "max_params_b": MAX_MODEL_PARAMS_B, + "agent_max_tokens": 192, + "agent_timeout_seconds": 180, + } + + daemon_success = False + result = {} + try: + response = requests.post("http://127.0.0.1:4891/api/agent/recommend", json=payload, timeout=5) + if response.status_code == 200: + result = response.json() + daemon_success = True + except Exception: + pass + + if daemon_success: + parsed = result.get("agent_json") + raw = result.get("raw_content", "") + if parsed: + summary = parsed.get("human_summary") or parsed.get("summary") or raw + selected = parsed.get("selected_model_ref") or parsed.get("selected_model_id") or "n/a" + confidence = parsed.get("confidence", "n/a") + risk_flags = as_bullet_list(parsed.get("risk_flags")) + next_steps = as_bullet_list(parsed.get("next_steps")) + markdown = f"""### {get_txt("agent_review_local_title", language)} + +**{get_txt("agent_lbl_agent_model", language)}**: `{result.get('agent_model')}` +**{get_txt("agent_lbl_selected", language)}**: `{selected}` +**{get_txt("agent_lbl_confidence", language)}**: `{confidence}` + +{summary} + +**{get_txt("agent_risk_flags", language)}** +{chr(10).join(f"- {item}" for item in risk_flags) if risk_flags else get_txt("agent_none_reported", language)} + +**{get_txt("agent_next_steps", language)}** +{chr(10).join(f"- {item}" for item in next_steps) if next_steps else get_txt("agent_none_reported", language)} +""" + else: + markdown = f"""### {get_txt("agent_review_local_title", language)} + +**{get_txt("agent_lbl_agent_model", language)}**: `{result.get('agent_model')}` + +{get_txt("agent_non_json", language)} + +```text +{raw} +``` +""" + return markdown, json.dumps(result, indent=2, ensure_ascii=False) + + # FALLBACK: Try querying local Ollama instance directly! + ollama_endpoints = [] + env_host = os.environ.get("OLLAMA_HOST", "").strip().rstrip("/") + if env_host: + if not env_host.startswith("http"): + if ":" in env_host: + ollama_endpoints.append(f"http://{env_host}") + else: + ollama_endpoints.append(f"http://127.0.0.1:{env_host}") + else: + ollama_endpoints.append(env_host) + ollama_endpoints.extend(["http://127.0.0.1:11434", "http://127.0.0.1:11435"]) + + ollama_host = None + installed = [] + for endpoint in ollama_endpoints: + try: + r = requests.get(f"{endpoint}/api/tags", timeout=1.5) + if r.status_code == 200: + installed = [m["name"] for m in r.json().get("models", [])] + ollama_host = endpoint + break + except Exception: + continue + + if ollama_host and installed: + model_to_use = None + requested = CONFIG_AGENT_MODEL_REF # gemma4:12b + if agent_model_choice != "auto": + if requested in installed: + model_to_use = requested + else: + for inst in installed: + if inst.startswith(requested) or requested.startswith(inst): + model_to_use = inst + break + if not model_to_use: + for p in ["gemma4:12b", "gemma4:latest"]: + if p in installed: + model_to_use = p + break + if not model_to_use and os.environ.get("JACKAI_AGENT_ALLOW_FALLBACK", "").strip() == "1": + priority = ["qwen3.5:9b", "qwen3.5:4b", "qwen3.5:2b", "gemma:latest"] + for p in priority: + if p in installed: + model_to_use = p + break + if not model_to_use: + model_to_use = installed[0] + if not model_to_use: + # The configuration agent must be Gemma 4 12B; a silent downgrade + # would produce lower-quality plans without the operator knowing. + error_md = ( + f"### {get_txt('agent_review_local_title', language)}\n\n" + + get_txt("agent_gemma_missing", language) + ) + return error_md, json.dumps( + {"error": "config agent gemma4:12b is not installed", "installed": installed}, + indent=2, + ) + + try: + url = f"{ollama_host}/api/chat" + chat_payload = { + "model": model_to_use, + "messages": agent_messages_for_plan(plan, language), + "stream": False, + "options": { + "temperature": 0.1, + "num_predict": 768 + }, + "format": "json" + } + res = requests.post(url, json=chat_payload, timeout=180) + if res.status_code == 200: + response_json = res.json() + content = response_json.get("message", {}).get("content", "") + parsed = parse_json_object_from_text(content) + if parsed: + result = { + "provider": "local_ollama", + "model": model_to_use, + "raw": content, + "parsed": parsed, + } + finalized = apply_agent_decision(plan, result) + summary = parsed.get("human_summary") or parsed.get("summary") or content + selected = parsed.get("selected_client_model_ref") or parsed.get("selected_model_ref") or finalized["default_model"]["model_ref"] + confidence = parsed.get("confidence", "n/a") + risk_flags = as_bullet_list(parsed.get("risk_flags")) + next_steps = as_bullet_list(parsed.get("next_steps")) + + markdown = f"""### {get_txt("agent_review_ollama_title", language)} + +**{get_txt("agent_lbl_agent_model", language)}**: `{model_to_use}` +**{get_txt("agent_lbl_selected", language)}**: `{selected}` +**{get_txt("agent_lbl_confidence", language)}**: `{confidence}` + +{summary} + +**{get_txt("agent_risk_flags", language)}** +{chr(10).join(f"- {item}" for item in risk_flags) if risk_flags else get_txt("agent_none_reported", language)} + +**{get_txt("agent_next_steps", language)}** +{chr(10).join(f"- {item}" for item in next_steps) if next_steps else get_txt("agent_none_reported", language)} +""" + return markdown, json.dumps(finalized, indent=2, ensure_ascii=False) + except Exception as ollama_err: + raise gr.Error(get_txt("agent_ollama_query_failed", language).format(detail=ollama_err)) + + raise gr.Error(get_txt("agent_no_runtime", language)) + +# REAL TARGET DETECTOR (Only works locally on Windows) +def get_real_drives(target_mode: str = "USB") -> list[str]: + if not IS_LOCAL: + return [] + if target_mode == "LocalFolder": + local_base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~") + return [os.path.join(local_base, "JackAILocal")] + try: + # Get removable drives (USB keys) + cmd = "powershell -Command \"Get-CimInstance Win32_LogicalDisk | Where-Object { $_.DriveType -eq 2 } | ForEach-Object { $_.DeviceID }\"" + res = subprocess.check_output(cmd, shell=True, text=True, errors="replace") + removable = [d.strip() + "\\" for d in res.split("\n") if d.strip()] + if target_mode != "SSD": + return removable + + # Fixed non-system drives are eligible for the external SSD mode. + cmd2 = "powershell -Command \"$system=$env:SystemDrive; Get-CimInstance Win32_LogicalDisk | Where-Object { $_.DriveType -eq 3 -and $_.DeviceID -ne $system } | ForEach-Object { $_.DeviceID }\"" + res2 = subprocess.check_output(cmd2, shell=True, text=True, errors="replace") + fixed = [d.strip() + "\\" for d in res2.split("\n") if d.strip()] + return removable + fixed + except Exception: + return [] + +# REAL HARDWARE SCANNER +def get_real_system_hardware(max_age_seconds: int = 3600) -> dict[str, Any] | None: + if not IS_LOCAL: + return None + try: + diag_dir = os.path.join(WORKSPACE_ROOT, "diagnostics") + os.makedirs(diag_dir, exist_ok=True) + out_path = os.path.join(diag_dir, "hardware.json") + + # Reuse a recent scan: the PowerShell scanner takes ~40 s and local + # hardware does not change between Gradio sessions. + try: + if max_age_seconds and time.time() - os.path.getmtime(out_path) < max_age_seconds: + with open(out_path, "r", encoding="utf-8-sig") as f: + cached = json.load(f) + if isinstance(cached, dict) and cached.get("ram_gb"): + return cached + except OSError: + pass + + # Run local scanner script + subprocess.run([ + "powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(WORKSPACE_ROOT, "windows", "hwscan-windows.ps1"), "-OutputPath", out_path + ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=WORKSPACE_ROOT) + + with open(out_path, "r", encoding="utf-8-sig") as f: + hw = json.load(f) + return hw + except Exception: + return None + +def is_local_runtime_reachable() -> bool: + if not IS_LOCAL: + return False + try: + response = requests.get("http://127.0.0.1:4891/health", timeout=2) + return response.status_code == 200 + except Exception: + return False + +def start_target_runtime_no_browser(target_drive: str) -> tuple[bool, str]: + if not IS_LOCAL: + return False, "Runtime start is only available from the local Windows builder." + if is_local_runtime_reachable(): + return True, "Runtime already running." + drive_path = target_drive or default_local_target_path() + script_path = os.path.join(drive_path, "windows", "Start-JackAILocal.ps1") + if not os.path.exists(script_path): + return False, f"Start script not found: {script_path}" + try: + cmd = [ + "powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", + script_path, "-NoBrowser" + ] + DETACHED_PROCESS = 0x00000008 + subprocess.Popen(cmd, creationflags=DETACHED_PROCESS, cwd=drive_path) + for _ in range(40): + if is_local_runtime_reachable(): + return True, "Runtime started." + time.sleep(0.5) + return False, "Runtime did not answer on 127.0.0.1:4891 after start." + except Exception as exc: + return False, str(exc) + +# Physical target build execution. Hosted Spaces can export builders but cannot write a client drive. +def run_physical_usb_build( + language: str, + sample_name: str, + raw_profile_json: str, + package_goal: str, + target_drive: str, + target_mode: str, + decision_answers_json: str = "", + operator_notes: str = "", +): + try: + plan = build_final_plan_object( + language, + sample_name, + raw_profile_json, + package_goal, + target_mode=target_mode, + target_drive=target_drive, + decision_answers_json=decision_answers_json, + operator_notes=operator_notes, + ) + except Exception as exc: + err_msg = f"[ERROR] {get_txt('err_plan_failed', language)}\n{exc}" + sidebar_html, start_upd, stop_upd = get_sidebar_status(False, False, language, "None") + yield err_msg, False, sidebar_html, start_upd, stop_upd + return + + default_model = plan["default_model"] + + lang = lang_key(language) + logs = [] + + def make_yields(log_str, formatted, running): + sidebar_html, start_upd, stop_upd = get_sidebar_status(formatted, running, language, default_model["model_ref"]) + return log_str, formatted, sidebar_html, start_upd, stop_upd + + if not IS_LOCAL: + message = get_txt("build_hosted_unavailable", language) + yield make_yields(message, False, False) + return + + if not target_drive.strip(): + yield make_yields(f"[ERROR] {get_txt('err_select_drive', language)}", False, False) + return + + # LOCAL REAL PATH EXECUTION + if IS_LOCAL: + yield make_yields(f"[REAL BUILD] {get_txt('build_stop_processes', language)}", False, False) + try: + workspace_stop = os.path.join(WORKSPACE_ROOT, "windows", "Stop-JackAILocal.ps1") + if os.path.exists(workspace_stop): + subprocess.run([ + "powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", + workspace_stop + ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=5) + except Exception as e: + yield make_yields(f"[REAL BUILD] {get_txt('build_warn_shutdown', language).format(detail=e)}", False, False) + time.sleep(0.5) + + # 1. Pre-stage directories and write real manifest file + manifest_dir = os.path.join(WORKSPACE_ROOT, "manifest-input") + os.makedirs(manifest_dir, exist_ok=True) + manifest_path = os.path.join(manifest_dir, "build-manifest.json") + with open(manifest_path, "w", encoding="utf-8") as f: + json.dump(plan, f, indent=2, ensure_ascii=False) + + yield make_yields(f"[REAL BUILD] {get_txt('build_starting', language)}", False, False) + + # Copy system Ollama binary if it exists in local Programs folder + local_app_data = os.environ.get("LOCALAPPDATA", "") + ollama_system = os.path.join(local_app_data, "Programs", "Ollama", "ollama.exe") if local_app_data else "" + ollama_local = os.path.join(WORKSPACE_ROOT, "backends", "ollama", "windows", "ollama.exe") + if ollama_system and os.path.exists(ollama_system) and not os.path.exists(ollama_local): + yield make_yields(f"[REAL BUILD] {get_txt('build_copy_ollama', language)}", False, False) + os.makedirs(os.path.dirname(ollama_local), exist_ok=True) + try: + shutil.copy2(ollama_system, ollama_local) + except Exception as e: + yield make_yields(f"[REAL BUILD] {get_txt('build_warn_ollama', language).format(detail=e)}", False, False) + time.sleep(0.5) + + # 2. Start real PowerShell installer subprocess + cmd = [ + "powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", + os.path.join(WORKSPACE_ROOT, "windows", "JackAILocal-USB-Builder.ps1"), + "-ManifestPath", manifest_path, + "-TargetDrive", target_drive, + "-TargetMode", target_mode + ] + + env = os.environ.copy() + env["AUTO_CONFIRM"] = "true" # skips PowerShell prompts + + try: + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1, + env=env, + cwd=WORKSPACE_ROOT, + encoding="utf-8", + errors="replace" + ) + + while True: + line = process.stdout.readline() + if not line and process.poll() is not None: + break + if line: + logs.append(line.rstrip()) + yield make_yields("\n".join(logs), False, False) + + rc = process.wait() + if rc == 0: + yield make_yields("\n".join(logs) + f"\n\n[SUCCESS] {get_txt('build_done', language)}", True, False) + else: + yield make_yields("\n".join(logs) + f"\n\n[ERROR] {get_txt('build_failed_rc', language).format(code=rc)}", False, False) + except Exception as e: + yield make_yields("\n".join(logs) + f"\n\n[ERROR] {get_txt('build_failed_start', language).format(detail=e)}", False, False) + +# SIDEBAR STATUS UPDATE +def get_sidebar_status(usb_formatted: bool, running: bool, lang: str, recommended_model_ref: str = "") -> tuple[str, dict, dict]: + dot_color = "red" + if running: + dot_color = "green" + text = get_txt("status_running", lang) + elif usb_formatted: + dot_color = "orange" + text = get_txt("status_ready", lang) + else: + text = get_txt("status_disconnected", lang) + + html = f""" +
    +
    + + {text} +
    + """ + if usb_formatted and recommended_model_ref: + model_text = get_txt("sidebar_model", lang).format(model=recommended_model_ref) + html += f'
    {model_text}
    ' + html += "
    " + + start_visible = usb_formatted and not running + stop_visible = running + + return html, gr.update(visible=start_visible), gr.update(visible=stop_visible) + +# ROUTING FUNCTION +def make_route(target_view: str) -> list[dict]: + updates = [] + for name in VIEW_NAMES: + updates.append(gr.update(visible=(name == target_view))) + return updates + +def route_to(target_view: str, lang: str) -> list[Any]: + title_key = "nav_config" if target_view == "saas" else f"nav_{target_view}" + title_text = get_txt(title_key, lang) + view_updates = make_route(target_view) + return view_updates + [f"

    {title_text}

    "] + +def route_to_console_and_reset(lang: str) -> list[Any]: + view_updates = route_to("console", lang) + console_init = f"[REAL BUILD] {get_txt('console_reset', lang)}\n" + return view_updates + [gr.update(value=console_init), gr.update(value=None)] + +# DYNAMIC CHAT DROP DOWN ACCORDING TO COMPILED MANIFEST +def update_chat_models(language: str, sample_name: str, raw_profile_json: str): + profile = normalize_profile(parse_profile_json(raw_profile_json, sample_name)) + allowed = compatible_models(profile) + label_key = "label_fr" if lang_key(language) == "fr" else "label_en" + choices = [(f"{m[label_key]} ({m['model_ref']})", m["model_ref"]) for m in allowed] + default_model = select_default_model(allowed, profile, "Standard") + return gr.update(choices=choices, value=default_model["model_ref"]) + +# GET STARTER PROMPTS +def get_starters(goal: str, language: str) -> list[str]: + lang = lang_key(language) + goal_lower = str(goal).lower() + + if "survival" in goal_lower or "manuel" in goal_lower: + if lang == "fr": + return [ + "Comment filtrer de l'eau en forêt ?", + "Quels sont les premiers soins en cas de brûlure ?", + "Comment allumer un feu sans allumettes ?" + ] + else: + return [ + "How do I filter water in the wild?", + "What is the first aid for a severe burn?", + "How do I build a temporary shelter?" + ] + elif "privacy" in goal_lower or "confidential" in goal_lower: + if lang == "fr": + return [ + "Pourquoi l'exécution hors ligne est-elle sécurisée ?", + "Comment vérifier que le réseau local n'y accède pas ?", + "Où sont stockées mes conversations ?" + ] + else: + return [ + "Why is offline execution secure?", + "How do I verify LAN access is blocked?", + "Where are my conversations stored?" + ] + elif "knowledge" in goal_lower or "notes" in goal_lower: + if lang == "fr": + return [ + "Explique le fonctionnement de la cryptographie.", + "Synthétise l'histoire de la conquête spatiale.", + "Qu'est-ce que le paradoxe de Fermi ?" + ] + else: + return [ + "Explain the basics of cryptography.", + "Summarize the history of space exploration.", + "What is the Fermi paradox?" + ] + elif "vision" in goal_lower: + if lang == "fr": + return [ + "Comment fonctionne l'analyse d'images hors ligne ?", + "Peut-on extraire le texte d'un PDF scanné ?", + "Quelles résolutions d'images sont supportées ?" + ] + else: + return [ + "How does offline image analysis work?", + "Can we extract text from a scanned PDF?", + "What image resolutions are supported?" + ] + elif "power" in goal_lower or "station" in goal_lower: + if lang == "fr": + return [ + "Écris un serveur TCP asynchrone en Rust.", + "Optimise un plan d'exécution SQL complexe.", + "Explique la quantification Q4_K_M vs Q8_0." + ] + else: + return [ + "Write an asynchronous TCP server in Rust.", + "Optimize a complex SQL execution plan.", + "Explain Q4_K_M vs Q8_0 quantization." + ] + else: # Standard + if lang == "fr": + return [ + "Quelles sont tes capacités hors ligne ?", + "Génère une fonction Python pour trier une liste.", + "Donne-moi 5 idées de projets de codage simples." + ] + else: + return [ + "What are your offline capabilities?", + "Generate a Python function to sort a list.", + "Give me 5 simple coding project ideas." + ] + +def update_starter_btn_labels(goal: str, language: str) -> tuple[dict, dict, dict]: + starters = get_starters(goal, language) + return gr.update(value=starters[0]), gr.update(value=starters[1]), gr.update(value=starters[2]) + +# Start a real local runtime and verify it through the local API. +def handle_start_usb(language: str, sample_name: str, raw_profile_json: str, package_goal: str, target_drive: str): + plan = build_plan_object(language, sample_name, raw_profile_json, package_goal) + profile = plan["hardware"] + default_model = plan["default_model"] + + running = False + formatted = True + + lang = lang_key(language) + + # Start the real target runtime if we are running locally. + if IS_LOCAL: + drive_path = target_drive + script_path = os.path.join(drive_path, "windows", "Start-JackAILocal.ps1") + if os.path.exists(script_path): + cmd = [ + "powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", + script_path, "-NoBrowser" + ] + DETACHED_PROCESS = 0x00000008 + subprocess.Popen(cmd, creationflags=DETACHED_PROCESS, cwd=drive_path) + + # Fetch dashboard state only from the running local API. + hw_info = None + model_catalog = None + if IS_LOCAL: + for _ in range(30): + try: + r = requests.get("http://127.0.0.1:4891/api/status", timeout=2) + if r.status_code == 200: + hw_info = r.json() + running = True + models_response = requests.get("http://127.0.0.1:4891/api/models", timeout=2) + if models_response.status_code == 200: + model_catalog = models_response.json() + break + except Exception: + time.sleep(0.5) + + sidebar_html, start_upd, stop_upd = get_sidebar_status(formatted, running, language, default_model["model_ref"]) + + # Compile dashboard display outputs + if hw_info and "hardware" in hw_info: + real_hw = hw_info["hardware"] + runtime_text = "jackailocald v1.5" + backend_text = str(hw_info.get("backend", "unavailable")) + hardware_text = f"RAM: {real_hw.get('ram_gb', profile['ram_gb'])}GB | CPU: {real_hw.get('cpu_threads', profile['cpu_threads'])} threads" + gpu_text = f"VRAM: {real_hw.get('vram_gb', 0)}GB | GPU Vendor: {real_hw.get('gpu_vendor', 'unknown')}" + else: + runtime_text = get_txt("dash_unavailable", language) + backend_text = get_txt("dash_no_backend", language) + hardware_text = get_txt("dash_no_hw", language) + gpu_text = get_txt("dash_no_gpu", language) + + model_name = default_model['label_fr'] if lang == 'fr' else default_model['label_en'] + available_models = (model_catalog or {}).get("models", []) + model_available = any( + item.get("available") and item.get("ollama") == default_model["model_ref"] + for item in available_models + ) + model_state = get_txt("dash_model_available", language) if model_available else get_txt("dash_model_missing", language) + model_text = f"{model_name} ({default_model['model_ref']}) - {model_state}" + network_text = str(hw_info.get("bind", "127.0.0.1:4891")) if hw_info else get_txt("dash_unavailable", language) + + # Preparation Checklist HTML + dot = "green" if running else "red" + model_dot = "green" if model_available else "red" + api_state = get_txt("dash_api_active", language) if running else get_txt("dash_unavailable", language) + check_api = f'
    {get_txt("dash_api_label", language)}: {api_state}
    ' + check_model = f'
    {get_txt("dash_default_model_label", language)}: {model_state}
    ' + check_security = f'
    {get_txt("dash_api_binding_label", language)}: {network_text}
    ' + checklist_html = f"{check_api}{check_model}{check_security}" + + route_updates = route_to("dashboard", language) + + return [ + running, + sidebar_html, + start_upd, + stop_upd, + runtime_text, + backend_text, + hardware_text, + gpu_text, + model_text, + network_text, + checklist_html + ] + route_updates + +# Stop a real local runtime. +def handle_stop_usb(language: str, sample_name: str, raw_profile_json: str, target_drive: str): + plan = build_plan_object(language, sample_name, raw_profile_json, "Standard") + default_model = plan["default_model"] + + running = False + formatted = True + + if IS_LOCAL: + # Run real Stop script + drive_path = target_drive + script_path = os.path.join(drive_path, "windows", "Stop-JackAILocal.ps1") + if os.path.exists(script_path): + cmd = [ + "powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", + script_path + ] + subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + sidebar_html, start_upd, stop_upd = get_sidebar_status(formatted, running, language, default_model["model_ref"]) + route_updates = route_to("dashboard", language) + + inactive_metric = "---" + check_api = f'
    {get_txt("dash_api_label", language)}: {get_txt("dash_api_stopped", language)}
    ' + check_model = f'
    {get_txt("dash_model_catalog_label", language)}: {get_txt("dash_inactive", language)}
    ' + check_security = f'
    {get_txt("dash_security_label", language)}: {get_txt("dash_inactive", language)}
    ' + checklist_html = f"{check_api}{check_model}{check_security}" + + return [ + running, + sidebar_html, + start_upd, + stop_upd, + inactive_metric, + inactive_metric, + inactive_metric, + inactive_metric, + inactive_metric, + inactive_metric, + checklist_html + ] + route_updates + +def chat_history_to_messages(history: list[Any]) -> list[dict[str, str]]: + messages: list[dict[str, str]] = [] + for item in history or []: + if isinstance(item, dict): + role = str(item.get("role", "")).strip() + content = str(item.get("content", "")).strip() + if role in {"user", "assistant", "system"} and content: + messages.append({"role": role, "content": content}) + elif isinstance(item, (list, tuple)) and len(item) >= 2: + if item[0]: + messages.append({"role": "user", "content": str(item[0])}) + if item[1]: + messages.append({"role": "assistant", "content": str(item[1])}) + return messages + +def run_live_chat_messages(message: str, history: list[Any], language: str, sample_name: str, raw_profile_json: str, package_goal: str, selected_model_id: str, usb_ready: bool, runtime_running: bool): + lang = lang_key(language) + history_messages = chat_history_to_messages(history) + if not message.strip(): + return "", history_messages, "" + + history_messages.append({"role": "user", "content": message}) + runtime_available = runtime_running or is_local_runtime_reachable() + if not (usb_ready and runtime_available): + history_messages.append({"role": "assistant", "content": f"ERROR: {get_txt('run_first_prompt', language)}"}) + return "", history_messages, "" + if not IS_LOCAL: + err = get_txt("chat_hosted_unavailable", language) + history_messages.append({"role": "assistant", "content": err}) + return "", history_messages, "" + + start_time = time.time() + body = { + "model": selected_model_id, + "messages": history_messages, + "temperature": 0.4, + } + try: + resp = requests.post( + "http://127.0.0.1:4891/v1/chat/completions", + json=body, + headers={"Content-Type": "application/json"}, + timeout=120, + ) + if resp.status_code == 200: + res_json = resp.json() + answer = res_json["choices"][0]["message"]["content"] + elapsed = max(time.time() - start_time, 0.001) + words_per_second = round(max(len(answer.split()), 1) / elapsed, 1) + backend = res_json.get("backend", "local") + stats = get_txt("chat_stats", language).format( + wps=words_per_second, ms=round(elapsed * 1000), backend=backend + ) + history_messages.append({"role": "assistant", "content": answer}) + return "", history_messages, stats + detail = resp.text[:500] + except Exception as exc: + detail = str(exc) + + err = get_txt("chat_api_error", language).format(detail=detail) + history_messages.append({"role": "assistant", "content": err}) + return "", history_messages, "" + +# Real documents list from a prepared local drive. +def load_initial_docs() -> list[dict[str, str]]: + return [] + +def get_documents_list(target_drive: str) -> list[dict[str, str]]: + if IS_LOCAL and target_drive: + docs_dir = os.path.join(target_drive, "workspace", "documents") + if os.path.exists(docs_dir): + docs = [] + for f in os.listdir(docs_dir): + fp = os.path.join(docs_dir, f) + if os.path.isfile(fp): + try: + with open(fp, "r", encoding="utf-8") as file: + content = file.read() + docs.append({"name": f, "content": content}) + except Exception: + pass + if docs: + return docs + return [] + +def save_document(title: str, content: str, docs: list[dict[str, str]], usb_ready: bool, runtime_running: bool, lang_choice: str, target_drive: str) -> tuple[list[dict[str, str]], str, dict]: + if not (usb_ready and runtime_running) and not is_local_runtime_reachable(): + err = get_txt("run_first_prompt", lang_choice) + return docs, f"❌ {err}", gr.update() + if not IS_LOCAL: + err = get_txt("doc_hosted_unavailable", lang_choice) + return docs, f"❌ {err}", gr.update() + + if not title.strip(): + return docs, f"❌ {get_txt('doc_title_empty', lang_choice)}", gr.update() + + # Real file saving if local + if IS_LOCAL and target_drive: + try: + docs_dir = os.path.join(target_drive, "workspace", "documents") + os.makedirs(docs_dir, exist_ok=True) + fp = os.path.join(docs_dir, title.strip()) + with open(fp, "w", encoding="utf-8") as f: + f.write(content) + docs = get_documents_list(target_drive) + status = f"💾 {get_txt('doc_saved', lang_choice).format(path=fp)}" + except Exception as e: + status = f"❌ {get_txt('doc_save_error', lang_choice).format(detail=e)}" + else: + status = f"❌ {get_txt('doc_no_target', lang_choice)}" + + doc_md = "\n".join([f"- **{d['name']}** ({len(d['content'])} chars)" for d in docs]) + return docs, status, gr.update(value=doc_md) + +# Real benchmark runner through the local Rust API. +def run_benchmark_real(language: str, sample_name: str, raw_profile_json: str, model_id: str, usb_ready: bool, runtime_running: bool): + if not (usb_ready and runtime_running) and not is_local_runtime_reachable(): + err = get_txt("run_first_prompt", language) + yield f"❌ {err}" + return + if not IS_LOCAL: + yield get_txt("bench_hosted_unavailable", language) + return + + yield get_txt("bench_running", language) + try: + url = "http://127.0.0.1:4891/api/benchmark/run" + resp = requests.post(url, json={"model": model_id}, timeout=180) + if resp.status_code != 200: + yield get_txt("bench_status_error", language).format(code=resp.status_code, detail=resp.text[:500]) + return + res = resp.json() + if not res.get("ok"): + yield get_txt("bench_service_error", language).format(detail=res.get("error")) + return + elapsed = max(float(res.get("elapsed_ms") or 0), 1.0) + sample = res.get("sample", {}).get("choices", [{}])[0].get("message", {}).get("content", "") + output = get_txt("bench_report", language).format( + model=res.get("model"), + ms=f"{elapsed:.0f}", + wps=round(max(len(sample.split()), 1) / (elapsed / 1000.0), 2), + sample=sample, + ) + yield output + except Exception as e: + yield get_txt("bench_connect_error", language).format(detail=e) + +# CUSTOM CSS FOR STUNNING DESIGN +custom_css = """ +body, .gradio-container { + background-color: #070a0f !important; + background-image: + radial-gradient(circle at 10% 0%, rgba(191, 255, 79, 0.12), transparent 35%), + radial-gradient(circle at 90% 10%, rgba(110, 231, 249, 0.10), transparent 35%), + linear-gradient(150deg, #05070b 0%, #0b111a 50%, #06080d 100%) !important; + color: #edf2f7 !important; + font-family: 'Outfit', 'Inter', -apple-system, sans-serif !important; +} + +/* No backdrop-filter on panels that contain dropdowns: it creates a CSS + containing block, which breaks the fixed positioning of Gradio dropdown + option lists (they render too high or too low). Opaque backgrounds keep + the same look without the filter. */ +.sidebar-panel { + background: rgba(11, 17, 26, 0.94) !important; + border: 1px solid rgba(148, 163, 184, 0.15) !important; + border-radius: 20px !important; + padding: 20px !important; + box-shadow: 0 10px 40px rgba(0,0,0,0.5) !important; +} + +.view-panel { + background: rgba(17, 24, 39, 0.92) !important; + border: 1px solid rgba(148, 163, 184, 0.15) !important; + border-radius: 24px !important; + padding: 24px !important; + box-shadow: 0 12px 50px rgba(0,0,0,0.4) !important; +} + +.ux-hero { + border: 1px solid rgba(191, 255, 79, 0.24); + border-radius: 22px; + padding: 22px; + margin-bottom: 16px; + background: linear-gradient(135deg, rgba(191,255,79,0.12), rgba(15,23,42,0.7) 48%, rgba(110,231,249,0.08)); +} + +.ux-hero h1 { + margin: 0 0 8px 0; + color: #edf2f7; + font-size: 1.7rem; + letter-spacing: -0.03em; +} + +.ux-hero p { + color: #cbd5e1; + max-width: 820px; + margin: 0; + line-height: 1.55; +} + +.ux-step-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; + margin: 14px 0 18px 0; +} + +.ux-step { + border: 1px solid rgba(148, 163, 184, 0.14); + border-radius: 16px; + padding: 14px; + background: rgba(15, 23, 42, 0.68); +} + +.ux-step strong { + color: #bfff4f; + display: block; + margin-bottom: 5px; +} + +.ux-step span { + color: #94a3b8; + font-size: 0.92rem; +} + +.ux-result-card { + border: 1px solid rgba(110, 231, 249, 0.2); + border-radius: 18px; + padding: 16px; + background: rgba(8, 13, 22, 0.72); +} + +.ux-ai-card { + border: 1px solid rgba(191, 255, 79, 0.25); + border-radius: 18px; + padding: 16px; + background: rgba(8, 13, 22, 0.76); + margin-top: 16px; +} + +.ux-ai-card h3 { + margin-top: 0; + color: #bfff4f; + font-size: 1.25rem; +} + +.ux-ai-card p { + margin: 8px 0; +} + +.ux-ai-card ul { + margin: 8px 0; + padding-left: 20px; + color: #94a3b8; +} + +.ux-ai-card li { + margin: 4px 0; +} + +.ux-result-card h3 { + margin-top: 0; + color: #bfff4f; +} + +.ux-feature-summary { + margin: 14px 0; +} + +.ux-feature-list { + display: grid; + gap: 6px; + list-style: none; + margin: 8px 0 0; + padding: 0; +} + +.ux-feature-list li { + align-items: center; + border-bottom: 1px solid rgba(148, 163, 184, 0.12); + display: flex; + gap: 16px; + justify-content: space-between; + min-height: 30px; +} + +.ux-feature-list strong { + flex: 0 0 auto; +} + +.ux-feature-list .is-enabled { + color: #bfff4f; +} + +.ux-feature-list .is-disabled { + color: #94a3b8; +} + +.compact-json textarea, +.compact-json pre { + max-height: 260px !important; +} + +@media (max-width: 900px) { + .ux-step-grid { + grid-template-columns: 1fr; + } +} + +.brand-mark { + width: 48px; + height: 48px; + border-radius: 15px; + display: grid; + place-items: center; + font-size: 24px; + font-weight: 950; + color: #0a0f16; + background: linear-gradient(135deg, #bfff4f, #ffffff 62%, #6ee7f9); + box-shadow: 0 8px 24px rgba(191, 255, 79, 0.22); +} + +.brand-title { + font-size: 1.7em !important; + font-weight: 900 !important; + background: linear-gradient(135deg, #bfff4f 0%, #edf2f7 60%, #6ee7f9 100%) !important; + -webkit-background-clip: text !important; + -webkit-text-fill-color: transparent !important; + letter-spacing: -0.5px !important; + margin: 0 !important; +} + +.terminal-console { + background: #030508 !important; + color: #6ee7f9 !important; + font-family: 'Consolas', 'Fira Code', monospace !important; + font-size: 0.9em !important; + border: 1px solid rgba(110, 231, 249, 0.25) !important; + border-radius: 14px !important; + padding: 16px !important; + line-height: 1.6 !important; + overflow-y: auto !important; + height: 350px !important; + box-shadow: inset 0 4px 20px rgba(0,0,0,0.7) !important; +} + +.metric-card { + background: rgba(11, 17, 26, 0.7) !important; + border: 1px solid rgba(148, 163, 184, 0.15) !important; + border-radius: 16px !important; + padding: 18px !important; + text-align: center !important; + transition: transform 0.2s ease, border-color 0.2s ease !important; +} + +.metric-card:hover { + transform: translateY(-2px) !important; + border-color: rgba(191, 255, 79, 0.35) !important; +} + +.metric-val { + font-size: 1.5em !important; + font-weight: 800 !important; + color: #bfff4f !important; + margin-top: 4px; +} + +.metric-lbl { + font-size: 0.85em !important; + text-transform: uppercase !important; + letter-spacing: 0.5px !important; + color: #9aa8bc !important; +} + +.lock-badge { + background-color: rgba(52, 211, 153, 0.12) !important; + border: 1px solid rgba(52, 211, 153, 0.3) !important; + color: #34d399 !important; + padding: 4px 10px !important; + border-radius: 12px !important; + font-size: 0.8em !important; + font-weight: 700 !important; + display: inline-flex !important; + align-items: center !important; + gap: 6px !important; +} + +.check-row { + display: flex !important; + align-items: center !important; + gap: 10px !important; + padding: 8px 0 !important; + border-bottom: 1px solid rgba(148, 163, 184, 0.08) !important; +} + +.check-dot { + width: 10px; + height: 10px; + border-radius: 50% !important; + display: inline-block !important; +} + +.check-dot.green { + background-color: #34d399 !important; + box-shadow: 0 0 10px #34d399 !important; +} + +.check-dot.orange { + background-color: #fbbf24 !important; + box-shadow: 0 0 10px #fbbf24 !important; +} + +.check-dot.red { + background-color: #fb7185 !important; + box-shadow: 0 0 10px #fb7185 !important; +} +""" + +HEAD_HTML = """ + + + + """ + +def gradio_launch_kwargs() -> dict[str, Any]: + # Gradio 6 moved theme/css/head from the Blocks constructor to launch(). + return { + "theme": gr.themes.Soft(primary_hue="lime", neutral_hue="slate"), + "css": custom_css, + "head": HEAD_HTML, + } + +def find_free_port(start: int, attempts: int = 20) -> int: + """First free port at or after `start`, so a second instance or another + app on 7860 does not kill the builder at startup.""" + import socket + for port in range(start, start + attempts): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as candidate: + try: + candidate.bind(("127.0.0.1", port)) + return port + except OSError: + continue + return start + +# REFRESH DRIVES LIST +def refresh_target_drives(mode, current_val): + drives = get_real_drives(mode) + fallback = os.path.join(os.environ.get("LOCALAPPDATA") or os.path.expanduser("~"), "JackAILocal") if mode == "LocalFolder" else "E:\\" + val = current_val if current_val and (mode == "LocalFolder" or current_val in drives) else (drives[0] if drives else fallback) + choices = list(drives) if drives else [fallback] + if val and val not in choices: + choices.append(val) + return gr.update(choices=choices, value=val) + +def make_header_html(lang: str) -> str: + return f""" +
    +
    J
    +
    +

    JackAILocal

    +

    {get_txt("header_subtitle", lang)}

    +
    +
    + """ + +def make_hero_html(lang: str) -> str: + return f""" +
    +

    {get_txt("hero_title", lang)}

    +

    {get_txt("hero_desc", lang)}

    +
    +
    +
    {get_txt("hero_step1_title", lang)}{get_txt("hero_step1_desc", lang)}
    +
    {get_txt("hero_step2_title", lang)}{get_txt("hero_step2_desc", lang)}
    +
    {get_txt("hero_step3_title", lang)}{get_txt("hero_step3_desc", lang)}
    +
    + """ + +def make_footer_html(lang: str) -> str: + return f""" +
    + {get_txt("footer_text", lang)} +
    + """ + +def build_ui() -> gr.Blocks: + with gr.Blocks(title=get_txt("app_title", "EN")) as demo: + # States + usb_ready = gr.State(value=False) + runtime_running = gr.State(value=False) + saved_docs = gr.State(value=load_initial_docs()) + language = gr.State(value="EN") + + # Header Area + with gr.Row(): + with gr.Column(scale=4): + header_html = gr.HTML(make_header_html("EN")) + + # Main Workspace Row + with gr.Row(): + # Left Sidebar + with gr.Column(scale=1, elem_classes=["sidebar-panel"]): + sidebar_header_md = gr.Markdown(f"### {get_txt('sidebar_header', 'EN')}") + + lang_radio = gr.Radio( + ["EN", "FR"], + value="EN", + label=get_txt("lang_label", "EN"), + ) + + if not IS_LOCAL: + # Hosted Space: each visitor signs in with their own + # Hugging Face account, so the Gemma 4 review bills THEIR + # inference quota, never the Space owner's token. + gr.LoginButton() + gr.Markdown(get_txt("oauth_note", "EN")) + + # USB Status Widget + sidebar_status_html = gr.HTML( + value=get_sidebar_status(False, False, "EN")[0] + ) + + # Runtime Launcher Buttons + btn_start_usb = gr.Button( + get_txt("btn_start", "EN"), + variant="primary", + visible=False + ) + btn_stop_usb = gr.Button( + get_txt("btn_stop", "EN"), + variant="stop", + visible=False + ) + + gr.Markdown("---") + + # Navigation Router Buttons. The builder ONLY creates packages. + # Runtime functions (dashboard, chat, documents, benchmark) + # live in the WebUI of the delivered runtime, not here: those + # views stay hidden so nothing non-functional is shown. + nav_saas = gr.Button(get_txt("nav_config", "EN"), variant="secondary", size="sm") + nav_console = gr.Button(get_txt("nav_console", "EN"), variant="secondary", size="sm") + nav_dashboard = gr.Button(get_txt("nav_dashboard", "EN"), variant="secondary", size="sm", visible=False) + nav_chat = gr.Button(get_txt("nav_chat", "EN"), variant="secondary", size="sm", visible=False) + nav_docs = gr.Button(get_txt("nav_docs", "EN"), variant="secondary", size="sm", visible=False) + nav_bench = gr.Button(get_txt("nav_bench", "EN"), variant="secondary", size="sm", visible=False) + nav_policy = gr.Button(get_txt("nav_policy", "EN"), variant="secondary", size="sm") + builder_scope_md = gr.Markdown(get_txt("builder_scope_note", "EN")) + + gr.Markdown("---") + hackathon_badge_md = gr.Markdown(get_txt("hackathon_badge", "EN")) + + footer_html = gr.HTML(make_footer_html("EN")) + + # Right View Area + with gr.Column(scale=3, elem_classes=["view-panel"]): + right_title = gr.HTML(f"

    {get_txt('nav_config', 'EN')}

    ") + + # ------------------ + # 1. VIEW CONFIG / SAAS BUILDER + # ------------------ + with gr.Column(visible=True) as view_saas: + hero_html = gr.HTML(make_hero_html("EN")) + with gr.Tabs() as saas_tabs: + with gr.Tab(get_txt("tab_light", "EN"), id="tab_light"): + simple_title_md = gr.Markdown(f"### {get_txt('simple_title', 'EN')}") + simple_desc_md = gr.Markdown(get_txt("simple_desc", "EN")) + with gr.Row(): + simple_goal = gr.Dropdown( + choices=simple_goal_choices("EN"), + value="daily_private", + label=get_txt("simple_goal_label", "EN") + ) + simple_target = gr.Dropdown( + choices=simple_target_choices("EN"), + value="local_runtime" if IS_LOCAL else "publish_windows", + label=get_txt("simple_target_label", "EN") + ) + + with gr.Row(equal_height=True): + drives = get_real_drives("LocalFolder") + default_drive = default_local_target_path() + if drives: + default_drive = drives[0] + simple_target_drive = gr.Dropdown( + choices=drives if drives else [default_drive], + value=default_drive, + label=get_txt("target_drive_label", "EN"), + allow_custom_value=True, + scale=4 + ) + simple_btn_refresh_drives = gr.Button(get_txt("refresh_btn", "EN"), variant="secondary", scale=1, min_width=80) + + simple_prepare_btn = gr.Button(get_txt("simple_prepare", "EN"), variant="primary") + simple_ready_md = gr.Markdown(get_txt("simple_ready", "EN")) + + with gr.Tab(get_txt("tab_advanced", "EN"), id="tab_advanced"): + advanced_options_md = gr.Markdown(f"### {get_txt('advanced_options', 'EN')}") + + # STEP 1: Hardware & Target Configuration + step1_md = gr.Markdown(f"#### {get_txt('step1_title', 'EN')}") + with gr.Row(): + with gr.Column(scale=1): + sample = gr.Dropdown(profile_source_choices("EN"), value=default_profile_source(), label=get_txt("sample_label", "EN")) + package_goal = gr.Dropdown( + package_goal_choices("EN"), + value="Standard offline assistant", + label=get_txt("package_goal_label", "EN") + ) + target_mode = gr.Dropdown( + choices=target_mode_choices("EN"), + value="LocalFolder", + label=get_txt("target_mode_label", "EN") + ) + with gr.Row(equal_height=True): + # Dropdown containing connected logical drives + Refresh button + drives = get_real_drives("LocalFolder") + default_drive = default_local_target_path() + if drives: + default_drive = drives[0] + + target_drive = gr.Dropdown( + choices=drives if drives else [default_drive], + value=default_drive, + label=get_txt("target_drive_label", "EN"), + allow_custom_value=True, + scale=4 + ) + btn_refresh_drives = gr.Button(get_txt("refresh_btn", "EN"), variant="secondary", scale=1, min_width=80) + + with gr.Column(scale=1): + profile_json = gr.Textbox( + label=get_txt("hardware_json_label", "EN"), + lines=11, + value="", + placeholder=json.dumps(PROFILE_EXAMPLE, indent=2) + ) + profile_upload = gr.File( + label=get_txt("profiler_upload_label", "EN"), + file_types=[".json"], + type="filepath", + ) + with gr.Row(): + profiler_zip_btn = gr.Button( + get_txt("profiler_btn", "EN"), + variant="secondary", + ) + profiler_zip_file = gr.File(label=get_txt("profiler_zip_label", "EN"), visible=True) + + gr.Markdown("---") + + # STEP 2: Optional AI Verification & Review + with gr.Accordion(get_txt("audit_accordion", "EN"), open=False) as audit_accordion: + audit_desc_md = gr.Markdown(get_txt("audit_desc", "EN")) + + decision_notes = gr.Textbox( + label=get_txt("operator_notes_label", "EN"), + lines=3, + placeholder=get_txt("operator_notes_ph", "EN") + ) + + with gr.Row(): + with gr.Column(scale=1): + with gr.Group(): + saas_config_md = gr.Markdown(f"##### {get_txt('audit_saas_title', 'EN')}") + decision_answers = gr.Textbox( + label=get_txt("answers_label", "EN"), + lines=3, + value="{}", + placeholder='{"ram_gb": 16, "target_os": "windows", "usb_target": "switch_to_zip"}' + ) + # The pasted API key is process-wide, not per-session: + # hidden on hosted Spaces (visitors sign in with HF + # instead), available in the single-operator local console. + with gr.Accordion(get_txt("api_accordion", "EN"), open=False, visible=IS_LOCAL) as api_accordion: + api_desc_md = gr.Markdown(get_txt("api_desc", "EN")) + with gr.Row(): + ui_api_provider = gr.Dropdown( + choices=["none", "ollama", "openai", "anthropic", "gemini", "openrouter", "modal", "hf"], + value="none", + label=get_txt("api_provider_label", "EN") + ) + ui_api_model = gr.Textbox( + label=get_txt("api_model_label", "EN"), + placeholder="e.g. gpt-4o-mini, gemma4:12b, etc.", + value="" + ) + with gr.Row(): + ui_api_key = gr.Textbox( + label=get_txt("api_key_label", "EN"), + placeholder=get_txt("api_key_ph", "EN"), + type="password", + value="" + ) + ui_api_url = gr.Textbox( + label=get_txt("api_url_label", "EN"), + placeholder="e.g. https://api.openai.com/v1", + value="" + ) + + with gr.Column(scale=1): + with gr.Group(): + local_llm_md = gr.Markdown(f"##### {get_txt('audit_local_title', 'EN')}") + agent_model_choice = gr.Dropdown( + choices=[ + (get_txt("agent_choice_auto", "EN"), "auto"), + (get_txt("agent_choice_exact", "EN"), CONFIG_AGENT_MODEL_ID), + ], + value="auto", + label=get_txt("agent_model_label", "EN") + ) + + preview_decision_btn = gr.Button(get_txt("audit_run_btn", "EN"), variant="primary") + + audit_results_md = gr.Markdown(f"##### {get_txt('audit_results_title', 'EN')}") + decision_review_md = gr.Markdown(value=get_txt("audit_initial", "EN")) + decision_review_json = gr.Code(label=get_txt("audit_json_label", "EN"), language="json", elem_classes=["compact-json"]) + + gr.Markdown("---") + + # STEP 3: Execution & Exporting + step3_md = gr.Markdown(f"#### {get_txt('step3_title', 'EN')}") + with gr.Row(): + with gr.Column(scale=2): + btn_build = gr.Button(get_txt("btn_build", "EN"), variant="primary") + plan_md = gr.Markdown(value=get_txt("plan_initial", "EN")) + + with gr.Column(scale=2): + with gr.Accordion(get_txt("export_accordion", "EN"), open=False) as export_accordion: + with gr.Row(): + export_manifest_btn = gr.Button(get_txt("export_manifest", "EN")) + win_builder_btn = gr.Button(get_txt("export_windows", "EN")) + mac_builder_btn = gr.Button(get_txt("export_macos", "EN")) + + with gr.Row(): + manifest_file = gr.File(label=get_txt("manifest_file", "EN")) + win_builder_file = gr.File(label=get_txt("windows_file", "EN")) + mac_builder_file = gr.File(label=get_txt("macos_file", "EN")) + + plan_json = gr.Code(label=get_txt("lbl_json_manifest", "EN"), language="json", visible=False, elem_classes=["compact-json"]) + + # ------------------ + # 2. VIEW BUILD CONSOLE + # ------------------ + with gr.Column(visible=False) as view_console: + console_title_md = gr.Markdown(f"### {get_txt('console_title', 'EN')}") + console_log = gr.Code( + value=get_txt("console_ready", "EN"), + language=None, + elem_classes=["terminal-console"] + ) + simple_status_md = gr.Markdown(value="") + simple_package_file = gr.File(label=get_txt("published_package", "EN")) + + # ------------------ + # 3. VIEW DASHBOARD + # ------------------ + with gr.Column(visible=False) as view_dashboard: + dash_lock_html = gr.HTML(f"
    🔒 {get_txt('status_running', 'EN')}
    ") + + with gr.Row(): + with gr.Column(elem_classes=["metric-card"]): + dash_runtime_lbl_html = gr.HTML(f"
    {get_txt('dash_runtime_lbl', 'EN')}
    ") + dash_runtime = gr.HTML("
    ---
    ") + dash_backend = gr.HTML("
    ---
    ") + with gr.Column(elem_classes=["metric-card"]): + dash_hardware_lbl_html = gr.HTML(f"
    {get_txt('dash_hardware_lbl', 'EN')}
    ") + dash_hardware = gr.HTML("
    ---
    ") + dash_gpu = gr.HTML("
    ---
    ") + with gr.Column(elem_classes=["metric-card"]): + dash_model_lbl_html = gr.HTML(f"
    {get_txt('dash_model_lbl', 'EN')}
    ") + dash_model = gr.HTML("
    ---
    ") + dash_policy_note_html = gr.HTML(f"
    {get_txt('dash_policy_note', 'EN')}
    ") + with gr.Column(elem_classes=["metric-card"]): + dash_network_lbl_html = gr.HTML(f"
    {get_txt('dash_network_lbl', 'EN')}
    ") + dash_network = gr.HTML("
    ---
    ") + dash_loopback_note_html = gr.HTML(f"
    {get_txt('dash_loopback_note', 'EN')}
    ") + + gr.Markdown("---") + dash_checklist_title_md = gr.Markdown(f"### {get_txt('dash_checklist_title', 'EN')}") + dash_checklist = gr.HTML( + value=f'
    {get_txt("dash_api_label", "EN")}: {get_txt("dash_api_stopped", "EN")}
    ' + ) + + # ------------------ + # 4. VIEW INTERACTIVE CHAT + # ------------------ + with gr.Column(visible=False) as view_chat: + with gr.Row(): + chat_model_select = gr.Dropdown( + choices=[(f"{m['label_en']} ({m['model_ref']})", m["model_ref"]) for m in MODEL_CATALOG], + value=MODEL_CATALOG[2]["model_ref"], + label=get_txt("lbl_chat_model", "EN") + ) + + # Starter prompts buttons + starter_md = gr.Markdown(f"***{get_txt('lbl_starter_prompts', 'EN')}***") + with gr.Row(): + starter_1 = gr.Button(get_txt("starter_capability", "EN"), variant="secondary", size="sm") + starter_2 = gr.Button(get_txt("starter_coding", "EN"), variant="secondary", size="sm") + starter_3 = gr.Button(get_txt("starter_privacy", "EN"), variant="secondary", size="sm") + + chatbot = gr.Chatbot(height=350) + + with gr.Row(): + prompt_input = gr.Textbox( + show_label=False, + placeholder=get_txt("lbl_input_prompt", "EN"), + scale=4 + ) + btn_send = gr.Button(get_txt("lbl_send", "EN"), variant="primary", scale=1) + btn_clear = gr.Button(get_txt("lbl_clear", "EN"), variant="secondary", scale=1) + + # Stats display beneath chatbot + stats_banner = gr.Markdown(value=f"*{get_txt('chat_offline', 'EN')}*") + + # ------------------ + # 5. VIEW DOCUMENTS MANAGER + # ------------------ + with gr.Column(visible=False) as view_docs: + docs_title_md = gr.Markdown(f"### {get_txt('docs_title', 'EN')}") + with gr.Row(): + with gr.Column(scale=2): + doc_title = gr.Textbox(label=get_txt("lbl_doc_title", "EN"), placeholder="guide_eau.txt") + doc_content = gr.Textbox(label=get_txt("lbl_doc_content", "EN"), lines=8, placeholder=get_txt("doc_content_ph", "EN")) + btn_save_doc = gr.Button(get_txt("lbl_save_doc", "EN"), variant="primary") + doc_save_status = gr.Markdown("") + with gr.Column(scale=1): + docs_list_title_md = gr.Markdown(f"#### {get_txt('lbl_doc_list', 'EN')}") + initial_md = "\n".join([f"- **{d['name']}** ({len(d['content'])} chars)" for d in load_initial_docs()]) + docs_list_display = gr.Markdown(value=initial_md) + + # ------------------ + # 6. VIEW BENCHMARK UTILITY + # ------------------ + with gr.Column(visible=False) as view_bench: + bench_title_md = gr.Markdown(f"### {get_txt('bench_title', 'EN')}") + btn_run_bench = gr.Button(get_txt("lbl_run_bench", "EN"), variant="primary") + bench_output = gr.Code( + value=get_txt("bench_initial", "EN"), + language=None, + elem_classes=["terminal-console"] + ) + + # ------------------ + # 7. VIEW SYSTEM POLICY + # ------------------ + with gr.Column(visible=False) as view_policy: + policy_md_view = gr.Markdown(get_txt("policy_body", "EN")) + + # VIEW ROUTER CALLBACKS + # Bind nav buttons to routing + nav_saas.click( + lambda lang: route_to("saas", lang), + inputs=[language], + outputs=[view_saas, view_console, view_dashboard, view_chat, view_docs, view_bench, view_policy, right_title], + ) + nav_console.click( + lambda lang: route_to("console", lang), + inputs=[language], + outputs=[view_saas, view_console, view_dashboard, view_chat, view_docs, view_bench, view_policy, right_title], + ) + nav_dashboard.click( + lambda lang: route_to("dashboard", lang), + inputs=[language], + outputs=[view_saas, view_console, view_dashboard, view_chat, view_docs, view_bench, view_policy, right_title], + ) + nav_chat.click( + lambda lang: route_to("chat", lang), + inputs=[language], + outputs=[view_saas, view_console, view_dashboard, view_chat, view_docs, view_bench, view_policy, right_title], + ) + nav_docs.click( + lambda lang: route_to("docs", lang), + inputs=[language], + outputs=[view_saas, view_console, view_dashboard, view_chat, view_docs, view_bench, view_policy, right_title], + ) + nav_bench.click( + lambda lang: route_to("bench", lang), + inputs=[language], + outputs=[view_saas, view_console, view_dashboard, view_chat, view_docs, view_bench, view_policy, right_title], + ) + nav_policy.click( + lambda lang: route_to("policy", lang), + inputs=[language], + outputs=[view_saas, view_console, view_dashboard, view_chat, view_docs, view_bench, view_policy, right_title], + ) + + # LANGUAGE SWITCH UPDATE + # Dynamically updates UI text labels + def update_language_labels(lang: str): + # Nav buttons + saas_upd = gr.update(value=get_txt("nav_config", lang)) + console_upd = gr.update(value=get_txt("nav_console", lang)) + dashboard_upd = gr.update(value=get_txt("nav_dashboard", lang)) + chat_upd = gr.update(value=get_txt("nav_chat", lang)) + docs_upd = gr.update(value=get_txt("nav_docs", lang)) + bench_upd = gr.update(value=get_txt("nav_bench", lang)) + policy_upd = gr.update(value=get_txt("nav_policy", lang)) + + # Action buttons & text + btn_build_upd = gr.update(value=get_txt("btn_build", lang)) + btn_run_bench_upd = gr.update(value=get_txt("lbl_run_bench", lang)) + btn_save_doc_upd = gr.update(value=get_txt("lbl_save_doc", lang)) + + # Chat prompts & labels + prompt_ph = gr.update(placeholder=get_txt("lbl_input_prompt", lang)) + btn_send_upd = gr.update(value=get_txt("lbl_send", lang)) + btn_clear_upd = gr.update(value=get_txt("lbl_clear", lang)) + chat_model_lbl = gr.update(label=get_txt("lbl_chat_model", lang)) + simple_title_upd = gr.update(value=f"### {get_txt('simple_title', lang)}") + simple_desc_upd = gr.update(value=get_txt("simple_desc", lang)) + simple_goal_upd = gr.update(choices=simple_goal_choices(lang), label=get_txt("simple_goal_label", lang)) + simple_target_upd = gr.update(choices=simple_target_choices(lang), label=get_txt("simple_target_label", lang)) + simple_prepare_upd = gr.update(value=get_txt("simple_prepare", lang)) + simple_ready_upd = gr.update(value=get_txt("simple_ready", lang)) + advanced_options_upd = gr.update(value=f"### {get_txt('advanced_options', lang)}") + sample_upd = gr.update(label=get_txt("sample_label", lang), choices=profile_source_choices(lang)) + package_goal_upd = gr.update(choices=package_goal_choices(lang), label=get_txt("package_goal_label", lang)) + target_mode_upd = gr.update(choices=target_mode_choices(lang), label=get_txt("target_mode_label", lang)) + target_drive_upd = gr.update(label=get_txt("target_drive_label", lang)) + profile_json_upd = gr.update(label=get_txt("hardware_json_label", lang)) + export_manifest_upd = gr.update(value=get_txt("export_manifest", lang)) + win_builder_upd = gr.update(value=get_txt("export_windows", lang)) + mac_builder_upd = gr.update(value=get_txt("export_macos", lang)) + manifest_file_upd = gr.update(label=get_txt("manifest_file", lang)) + win_builder_file_upd = gr.update(label=get_txt("windows_file", lang)) + mac_builder_file_upd = gr.update(label=get_txt("macos_file", lang)) + console_title_upd = gr.update(value=f"### {get_txt('console_title', lang)}") + simple_file_upd = gr.update(label=get_txt("published_package", lang)) + simple_target_drive_upd = gr.update(label=get_txt("target_drive_label", lang)) + profile_upload_upd = gr.update(label=get_txt("profiler_upload_label", lang)) + profiler_zip_btn_upd = gr.update(value=get_txt("profiler_btn", lang)) + profiler_zip_file_upd = gr.update(label=get_txt("profiler_zip_label", lang)) + simple_refresh_upd = gr.update(value=get_txt("refresh_btn", lang)) + adv_refresh_upd = gr.update(value=get_txt("refresh_btn", lang)) + + header_upd = gr.update(value=make_header_html(lang)) + sidebar_header_upd = gr.update(value=f"### {get_txt('sidebar_header', lang)}") + hero_upd = gr.update(value=make_hero_html(lang)) + footer_upd = gr.update(value=make_footer_html(lang)) + step1_upd = gr.update(value=f"#### {get_txt('step1_title', lang)}") + audit_accordion_upd = gr.update(label=get_txt("audit_accordion", lang)) + audit_desc_upd = gr.update(value=get_txt("audit_desc", lang)) + decision_notes_upd = gr.update( + label=get_txt("operator_notes_label", lang), + placeholder=get_txt("operator_notes_ph", lang), + ) + saas_config_upd = gr.update(value=f"##### {get_txt('audit_saas_title', lang)}") + decision_answers_upd = gr.update(label=get_txt("answers_label", lang)) + api_accordion_upd = gr.update(label=get_txt("api_accordion", lang)) + api_desc_upd = gr.update(value=get_txt("api_desc", lang)) + api_provider_upd = gr.update(label=get_txt("api_provider_label", lang)) + api_model_upd = gr.update(label=get_txt("api_model_label", lang)) + api_key_upd = gr.update(label=get_txt("api_key_label", lang), placeholder=get_txt("api_key_ph", lang)) + api_url_upd = gr.update(label=get_txt("api_url_label", lang)) + local_llm_upd = gr.update(value=f"##### {get_txt('audit_local_title', lang)}") + agent_model_choice_upd = gr.update( + choices=[ + (get_txt("agent_choice_auto", lang), "auto"), + (get_txt("agent_choice_exact", lang), CONFIG_AGENT_MODEL_ID), + ], + label=get_txt("agent_model_label", lang), + ) + preview_decision_upd = gr.update(value=get_txt("audit_run_btn", lang)) + audit_results_upd = gr.update(value=f"##### {get_txt('audit_results_title', lang)}") + decision_review_json_upd = gr.update(label=get_txt("audit_json_label", lang)) + step3_upd = gr.update(value=f"#### {get_txt('step3_title', lang)}") + export_accordion_upd = gr.update(label=get_txt("export_accordion", lang)) + plan_json_upd = gr.update(label=get_txt("lbl_json_manifest", lang)) + dash_lock_upd = gr.update(value=f"
    🔒 {get_txt('status_running', lang)}
    ") + dash_runtime_lbl_upd = gr.update(value=f"
    {get_txt('dash_runtime_lbl', lang)}
    ") + dash_hardware_lbl_upd = gr.update(value=f"
    {get_txt('dash_hardware_lbl', lang)}
    ") + dash_model_lbl_upd = gr.update(value=f"
    {get_txt('dash_model_lbl', lang)}
    ") + dash_policy_note_upd = gr.update(value=f"
    {get_txt('dash_policy_note', lang)}
    ") + dash_network_lbl_upd = gr.update(value=f"
    {get_txt('dash_network_lbl', lang)}
    ") + dash_loopback_note_upd = gr.update(value=f"
    {get_txt('dash_loopback_note', lang)}
    ") + dash_checklist_title_upd = gr.update(value=f"### {get_txt('dash_checklist_title', lang)}") + starter_md_upd = gr.update(value=f"***{get_txt('lbl_starter_prompts', lang)}***") + docs_title_upd = gr.update(value=f"### {get_txt('docs_title', lang)}") + doc_title_upd = gr.update(label=get_txt("lbl_doc_title", lang)) + doc_content_upd = gr.update(label=get_txt("lbl_doc_content", lang), placeholder=get_txt("doc_content_ph", lang)) + docs_list_title_upd = gr.update(value=f"#### {get_txt('lbl_doc_list', lang)}") + bench_title_upd = gr.update(value=f"### {get_txt('bench_title', lang)}") + policy_body_upd = gr.update(value=get_txt("policy_body", lang)) + builder_scope_upd = gr.update(value=get_txt("builder_scope_note", lang)) + hackathon_badge_upd = gr.update(value=get_txt("hackathon_badge", lang)) + + return [ + saas_upd, console_upd, dashboard_upd, chat_upd, docs_upd, bench_upd, policy_upd, + btn_build_upd, btn_run_bench_upd, btn_save_doc_upd, prompt_ph, btn_send_upd, btn_clear_upd, chat_model_lbl, + simple_title_upd, simple_desc_upd, simple_goal_upd, simple_target_upd, simple_prepare_upd, simple_ready_upd, + advanced_options_upd, sample_upd, package_goal_upd, target_mode_upd, target_drive_upd, profile_json_upd, + export_manifest_upd, win_builder_upd, mac_builder_upd, manifest_file_upd, win_builder_file_upd, mac_builder_file_upd, + console_title_upd, simple_file_upd, simple_target_drive_upd, + profile_upload_upd, profiler_zip_btn_upd, profiler_zip_file_upd, simple_refresh_upd, adv_refresh_upd, + header_upd, sidebar_header_upd, hero_upd, footer_upd, step1_upd, + audit_accordion_upd, audit_desc_upd, decision_notes_upd, saas_config_upd, decision_answers_upd, + api_accordion_upd, api_desc_upd, api_provider_upd, api_model_upd, api_key_upd, api_url_upd, + local_llm_upd, agent_model_choice_upd, preview_decision_upd, audit_results_upd, decision_review_json_upd, + step3_upd, export_accordion_upd, plan_json_upd, + dash_lock_upd, dash_runtime_lbl_upd, dash_hardware_lbl_upd, dash_model_lbl_upd, dash_policy_note_upd, + dash_network_lbl_upd, dash_loopback_note_upd, dash_checklist_title_upd, + starter_md_upd, docs_title_upd, doc_title_upd, doc_content_upd, docs_list_title_upd, + bench_title_upd, policy_body_upd, builder_scope_upd, hackathon_badge_upd, + ] + + lang_radio.change(lambda l: l, inputs=[lang_radio], outputs=[language], queue=False) + lang_radio.change( + update_language_labels, + inputs=[lang_radio], + outputs=[ + nav_saas, nav_console, nav_dashboard, nav_chat, nav_docs, nav_bench, nav_policy, + btn_build, btn_run_bench, btn_save_doc, prompt_input, btn_send, btn_clear, chat_model_select, + simple_title_md, simple_desc_md, simple_goal, simple_target, simple_prepare_btn, simple_ready_md, + advanced_options_md, sample, package_goal, target_mode, target_drive, profile_json, + export_manifest_btn, win_builder_btn, mac_builder_btn, manifest_file, win_builder_file, mac_builder_file, + console_title_md, simple_package_file, simple_target_drive, + profile_upload, profiler_zip_btn, profiler_zip_file, simple_btn_refresh_drives, btn_refresh_drives, + header_html, sidebar_header_md, hero_html, footer_html, step1_md, + audit_accordion, audit_desc_md, decision_notes, saas_config_md, decision_answers, + api_accordion, api_desc_md, ui_api_provider, ui_api_model, ui_api_key, ui_api_url, + local_llm_md, agent_model_choice, preview_decision_btn, audit_results_md, decision_review_json, + step3_md, export_accordion, plan_json, + dash_lock_html, dash_runtime_lbl_html, dash_hardware_lbl_html, dash_model_lbl_html, dash_policy_note_html, + dash_network_lbl_html, dash_loopback_note_html, dash_checklist_title_md, + starter_md, docs_title_md, doc_title, doc_content, docs_list_title_md, + bench_title_md, policy_md_view, builder_scope_md, hackathon_badge_md, + ], + ) + + # ONE-CLICK SIMPLE FLOW + simple_goal.change( + sync_simple_goal, + inputs=[simple_goal], + outputs=[package_goal] + ) + simple_target.change( + sync_simple_target, + inputs=[simple_target, target_drive], + outputs=[target_mode, target_drive] + ).then( + lambda val: val, + inputs=[target_drive], + outputs=[simple_target_drive], + queue=False + ) + + # Sync simple_target_drive and target_drive bidirectional changes + simple_target_drive.change( + lambda val: val, + inputs=[simple_target_drive], + outputs=[target_drive], + queue=False + ) + target_drive.change( + lambda val: val, + inputs=[target_drive], + outputs=[simple_target_drive], + queue=False + ) + + # Refresh target drives from Simple tab + simple_btn_refresh_drives.click( + refresh_target_drives, + inputs=[target_mode, simple_target_drive], + outputs=[simple_target_drive] + ) + + def update_ui_api_config(provider, model, key, url): + decision_engine.set_ui_api_config(provider, model, key, url) + + for comp in [ui_api_provider, ui_api_model, ui_api_key, ui_api_url]: + comp.change( + update_ui_api_config, + inputs=[ui_api_provider, ui_api_model, ui_api_key, ui_api_url], + show_progress=False, + queue=False + ) + + def on_sample_change(sample_name): + if sample_name in SAMPLE_PROFILES: + return json.dumps(SAMPLE_PROFILES[sample_name], indent=2) + if sample_name == PROFILE_SOURCE_REAL and IS_LOCAL: + hw = get_real_system_hardware() + if hw: + return json.dumps(hw, indent=2) + # Upload source (or scan failure): leave the JSON empty so the + # uploaded/real profile is the only thing that can fill it. + return "" + + sample.change( + on_sample_change, + inputs=[sample], + outputs=[profile_json] + ) + + preview_decision_btn.click( + run_unified_ai_audit, + inputs=[ + language, + sample, + profile_json, + package_goal, + simple_target, + target_mode, + target_drive, + decision_answers, + decision_notes, + agent_model_choice, + ], + outputs=[decision_review_md, decision_review_json] + ) + simple_prepare_btn.click( + route_to_console_and_reset, + inputs=[language], + outputs=[view_saas, view_console, view_dashboard, view_chat, view_docs, view_bench, view_policy, right_title, console_log, simple_package_file] + ).then( + run_one_click_flow, + inputs=[language, simple_goal, simple_target, sample, profile_json, target_drive, decision_answers, decision_notes], + outputs=[ + console_log, + usb_ready, + sidebar_status_html, + btn_start_usb, + btn_stop_usb, + simple_status_md, + simple_package_file, + plan_json, + plan_md, + ] + ).then( + update_chat_models, + inputs=[language, sample, profile_json], + outputs=[chat_model_select] + ) + + # REGENERATES PLAN, ROUTES TO CONSOLE, AND RUNS PHYSICAL TARGET CREATION + btn_build.click( + route_to_console_and_reset, + inputs=[language], + outputs=[view_saas, view_console, view_dashboard, view_chat, view_docs, view_bench, view_policy, right_title, console_log, simple_package_file] + ).then( + build_plan, + inputs=[language, sample, profile_json, package_goal], + outputs=[plan_json, plan_md] + ).then( + run_physical_usb_build, + inputs=[language, sample, profile_json, package_goal, target_drive, target_mode, decision_answers, decision_notes], + outputs=[console_log, usb_ready, sidebar_status_html, btn_start_usb, btn_stop_usb] + ).then( + update_chat_models, + inputs=[language, sample, profile_json], + outputs=[chat_model_select] + ) + + # EXPORTS CALLBACKS + export_manifest_btn.click( + export_manifest_zip, + inputs=[language, sample, profile_json, package_goal, simple_target, target_mode, target_drive, decision_answers, decision_notes], + outputs=[manifest_file] + ) + win_builder_btn.click( + lambda l, s, p, g, st, tm, td, da, dn: export_platform_builder_zip(l, s, p, g, "windows", st, tm, td, da, dn), + inputs=[language, sample, profile_json, package_goal, simple_target, target_mode, target_drive, decision_answers, decision_notes], + outputs=[win_builder_file] + ) + mac_builder_btn.click( + lambda l, s, p, g, st, tm, td, da, dn: export_platform_builder_zip(l, s, p, g, "macos", st, tm, td, da, dn), + inputs=[language, sample, profile_json, package_goal, simple_target, target_mode, target_drive, decision_answers, decision_notes], + outputs=[mac_builder_file] + ) + + # RUNTIME START/STOP + btn_start_usb.click( + handle_start_usb, + inputs=[language, sample, profile_json, package_goal, target_drive], + outputs=[ + runtime_running, + sidebar_status_html, + btn_start_usb, + btn_stop_usb, + dash_runtime, + dash_backend, + dash_hardware, + dash_gpu, + dash_model, + dash_network, + dash_checklist, + view_saas, view_console, view_dashboard, view_chat, view_docs, view_bench, view_policy, right_title + ] + ) + + btn_stop_usb.click( + handle_stop_usb, + inputs=[language, sample, profile_json, target_drive], + outputs=[ + runtime_running, + sidebar_status_html, + btn_start_usb, + btn_stop_usb, + dash_runtime, + dash_backend, + dash_hardware, + dash_gpu, + dash_model, + dash_network, + dash_checklist, + view_saas, view_console, view_dashboard, view_chat, view_docs, view_bench, view_policy, right_title + ] + ) + + # STARTER PROMPTS ACTIONS + # Update starter buttons labels when Package Goal or Language changes + package_goal.change(update_starter_btn_labels, inputs=[package_goal, language], outputs=[starter_1, starter_2, starter_3]) + + # When starter prompt clicked, set input box value + starter_1.click(lambda text: text, inputs=[starter_1], outputs=[prompt_input]) + starter_2.click(lambda text: text, inputs=[starter_2], outputs=[prompt_input]) + starter_3.click(lambda text: text, inputs=[starter_3], outputs=[prompt_input]) + + # LIVE CHAT INTERACTION + btn_send.click( + run_live_chat_messages, + inputs=[prompt_input, chatbot, language, sample, profile_json, package_goal, chat_model_select, usb_ready, runtime_running], + outputs=[prompt_input, chatbot, stats_banner] + ) + prompt_input.submit( + run_live_chat_messages, + inputs=[prompt_input, chatbot, language, sample, profile_json, package_goal, chat_model_select, usb_ready, runtime_running], + outputs=[prompt_input, chatbot, stats_banner] + ) + btn_clear.click(lambda: ([], ""), outputs=[chatbot, stats_banner]) + + # SAVING LOCAL DOCUMENTS (Direct write on USB Key if local) + btn_save_doc.click( + save_document, + inputs=[doc_title, doc_content, saved_docs, usb_ready, runtime_running, language, target_drive], + outputs=[saved_docs, doc_save_status, docs_list_display] + ) + + # BENCHMARK RUNNER (Queries local API endpoint directly) + btn_run_bench.click( + run_benchmark_real, + inputs=[language, sample, profile_json, chat_model_select, usb_ready, runtime_running], + outputs=[bench_output] + ) + + + + btn_refresh_drives.click( + refresh_target_drives, + inputs=[target_mode, target_drive], + outputs=[target_drive] + ) + target_mode.change( + refresh_target_drives, + inputs=[target_mode, target_drive], + outputs=[target_drive] + ) + + profile_upload.upload(load_profile_file, inputs=[profile_upload], outputs=[profile_json]) + profiler_zip_btn.click(export_profiler_zip, outputs=[profiler_zip_file]) + + # STARTUP & DYNAMIC LOADER + # If running locally, automatically update UI parameters with real hardware scan! + def on_startup_scan(lang): + hw = get_real_system_hardware() + if hw: + # Compile a custom description for the user + os_str = hw.get("os", "windows") + cpu = hw.get("cpu_threads", 4) + ram = hw.get("ram_gb", 8.0) + vram = hw.get("vram_gb", 0.0) + gpu = hw.get("gpu_name", "Integrated graphics") + + details = get_txt("scan_detected", lang).format( + os=os_str.upper(), cpu=cpu, ram=ram, vram=vram, gpu=gpu + ) + + # Show the scanner output as-is: no invented disk/storage values. + return gr.update(value=json.dumps(hw, indent=2)), gr.update(value=f"### {details}") + return gr.update(), gr.update(value="") + + # Trigger startup callbacks + demo.load(on_startup_scan, inputs=[language], outputs=[profile_json, plan_md]) + demo.load(update_starter_btn_labels, inputs=[package_goal, language], outputs=[starter_1, starter_2, starter_3]) + + # If a JackAILocal runtime is already running on this machine, unlock + # the local console functions immediately instead of asking the user + # to rebuild a target first. + def on_startup_runtime_check(lang): + running = IS_LOCAL and is_local_runtime_reachable() + sidebar_html, start_upd, stop_upd = get_sidebar_status(running, running, lang) + return running, running, sidebar_html, start_upd, stop_upd + + demo.load( + on_startup_runtime_check, + inputs=[language], + outputs=[usb_ready, runtime_running, sidebar_status_html, btn_start_usb, btn_stop_usb], + ) + + return demo + +if __name__ == "__main__": + requested_port = int(os.environ.get("GRADIO_SERVER_PORT", os.environ.get("PORT", "7860"))) + port = find_free_port(requested_port) + if port != requested_port: + print(f"Port {requested_port} is busy; using {port} instead -> http://127.0.0.1:{port}") + build_ui().launch( + server_name="127.0.0.1", + server_port=port, + **gradio_launch_kwargs(), + ) + diff --git a/saas/gradio/decision_engine.py b/saas/gradio/decision_engine.py new file mode 100644 index 0000000000000000000000000000000000000000..32a876197e01413584d2848bedc02656c1e0d27b --- /dev/null +++ b/saas/gradio/decision_engine.py @@ -0,0 +1,796 @@ +from __future__ import annotations + +import json +import os +import re +from typing import Any, Literal + +import requests +from pydantic import BaseModel, Field, ValidationError + + +DEFAULT_MODEL_ID = "Qwen/Qwen2.5-7B-Instruct" +MODEL_ID = os.getenv("MODEL_ID", os.getenv("REMOTE_CONFIG_AGENT_MODEL_ID", DEFAULT_MODEL_ID)).strip() or DEFAULT_MODEL_ID +TIMEOUT_SECONDS = int(os.getenv("REMOTE_AGENT_TIMEOUT_SECONDS", "240")) + +# Cloud agent is NOT pinned to one model: on the HF router we try a priority +# list of served, instruct, <=32B models and keep the first that returns valid +# JSON. This survives a model losing its inference provider or a "thinking" +# model returning empty content. Override the list with CLOUD_AGENT_MODELS +# (comma-separated). MODEL_ID (if set) is always tried first. +_FALLBACK_CLOUD_MODELS = [ + "Qwen/Qwen2.5-7B-Instruct", + "meta-llama/Llama-3.1-8B-Instruct", + "google/gemma-3-27b-it", + "Qwen/Qwen2.5-Coder-7B-Instruct", +] + +def cloud_agent_candidates() -> list[str]: + raw = os.getenv("CLOUD_AGENT_MODELS", "").strip() + candidates = [m.strip() for m in raw.split(",") if m.strip()] if raw else [MODEL_ID, *_FALLBACK_CLOUD_MODELS] + seen: set[str] = set() + ordered: list[str] = [] + for model in candidates: + if model and model not in seen: + seen.add(model) + ordered.append(model) + return ordered + +# Module-level state for UI overrides +_ui_provider = "none" +_ui_model = "" +_ui_key = "" +_ui_url = "" + +# Last real decision trace (prompt, raw model output, validated decision). +# Exposed in the UI so anyone can verify a genuine LLM call happened. +_last_trace: dict[str, Any] | None = None + + +def get_last_decision_trace() -> dict[str, Any] | None: + return _last_trace + + +def set_ui_api_config(provider: str, model: str, key: str, url: str): + global _ui_provider, _ui_model, _ui_key, _ui_url + _ui_provider = provider.strip() if provider else "none" + _ui_model = model.strip() if model else "" + _ui_key = key.strip() if key else "" + _ui_url = url.strip() if url else "" + + + +class DecisionQuestion(BaseModel): + id: str + label: str + question: str + answer_type: Literal["text", "json", "boolean", "choice", "number"] = "text" + options: list[str] = Field(default_factory=list) + required: bool = True + reason: str = "" + + +class DecisionEnvelope(BaseModel): + decision: Literal[ + "BUILD_USB_PACKAGE", + "BUILD_ZIP_PACKAGE", + "BUILD_LOCAL_FOLDER", + "PUBLISH_APPLIANCE", + "ASK_USER", + "ASK_HUMAN_REVIEW", + "REJECT", + ] = "BUILD_LOCAL_FOLDER" + confidence: float = Field(default=1.0, ge=0.0, le=1.0) + risk_level: Literal["low", "medium", "high"] = "low" + selected_backend: Literal["modal", "hf", "ollama", "llama.cpp", "vllm", "none"] = "none" + selected_model: str = "" + selected_client_model_ref: str | None = None + selected_runtime_agent_model_ref: str | None = None + reasons: list[str] = Field(default_factory=list) + required_human_review: bool = False + missing_secrets: list[str] = Field(default_factory=list) + questions: list[DecisionQuestion] = Field(default_factory=list) + manifest_patch: dict[str, Any] = Field(default_factory=dict) + blocked: bool = False + + +def _validate_decision(data: dict[str, Any]) -> DecisionEnvelope: + try: + return DecisionEnvelope.model_validate(data) + except AttributeError: + return DecisionEnvelope.parse_obj(data) + + +def _dump_decision(decision: DecisionEnvelope) -> dict[str, Any]: + try: + return decision.model_dump() + except AttributeError: + return decision.dict() + + +def _parse_json_object_from_text(text: str) -> dict[str, Any] | None: + if not text: + return None + stripped = text.strip() + if stripped.startswith("```"): + stripped = re.sub(r"^```(?:json)?\s*", "", stripped, flags=re.IGNORECASE) + stripped = re.sub(r"\s*```$", "", stripped) + try: + value = json.loads(stripped) + return value if isinstance(value, dict) else None + except json.JSONDecodeError: + pass + start = stripped.find("{") + end = stripped.rfind("}") + if start >= 0 and end > start: + try: + value = json.loads(stripped[start:end + 1]) + return value if isinstance(value, dict) else None + except json.JSONDecodeError: + return None + return None + + +def get_installed_ollama_models(endpoint: str) -> list[str]: + try: + response = requests.get(f"{endpoint.rstrip('/')}/api/tags", timeout=2) + if response.status_code == 200: + data = response.json() + return [model["name"] for model in data.get("models", [])] + except Exception: + pass + return [] + + +OLLAMA_MODEL_PRIORITY = ["gemma4:12b", "gemma4:latest", "qwen3.5:9b", "qwen3.5:4b", "qwen3.5:2b", "qwen3.6:27b", "gemma:latest"] + + +def _ollama_base_url(endpoint: str) -> str: + """Normalize an Ollama endpoint (with or without a trailing /v1) to the bare base URL.""" + url = endpoint.strip().rstrip("/") + if url.endswith("/v1"): + url = url[:-3].rstrip("/") + return url + + +def _ollama_chat_url(endpoint: str) -> str: + return _ollama_base_url(endpoint) + "/v1/chat/completions" + + +def _pick_ollama_model(endpoint: str, requested: str) -> str | None: + """Pick a really-installed Ollama model. Returns None when nothing is + installed or the tags probe fails: the caller must treat that as + 'no provider' instead of guessing a tag that would 404 later.""" + if requested: + return requested + installed = get_installed_ollama_models(_ollama_base_url(endpoint)) + if not installed: + return None + for p in OLLAMA_MODEL_PRIORITY: + if p in installed: + return p + return installed[0] + + +def configured_provider() -> dict[str, Any] | None: + # 1. First check if UI has overridden provider config + if _ui_provider != "none": + prov = _ui_provider.lower() + if prov == "openai": + return { + "id": "openai", + "transport": "openai_compatible", + "url": (_ui_url or "https://api.openai.com/v1").rstrip("/") + "/chat/completions", + "api_key": _ui_key, + "model": _ui_model or "gpt-4o-mini", + } + elif prov == "anthropic": + return { + "id": "anthropic", + "transport": "anthropic", + "url": (_ui_url or "https://api.anthropic.com/v1/messages").rstrip("/"), + "api_key": _ui_key, + "model": _ui_model or "claude-3-5-sonnet-20241022", + } + elif prov == "gemini": + return { + "id": "gemini", + "transport": "openai_compatible", + "url": (_ui_url or "https://generativelanguage.googleapis.com/v1beta/openai").rstrip("/") + "/chat/completions", + "api_key": _ui_key, + "model": _ui_model or "gemini-1.5-flash", + } + elif prov == "openrouter": + return { + "id": "openrouter", + "transport": "openai_compatible", + "url": (_ui_url or "https://openrouter.ai/api/v1").rstrip("/") + "/chat/completions", + "api_key": _ui_key, + "model": _ui_model or "google/gemini-2.5-flash", + } + elif prov == "modal": + return { + "id": "modal", + "transport": "openai_compatible", + "url": (_ui_url or "https://router.modal.com").rstrip("/") + "/v1/chat/completions", + "api_key": _ui_key, + "model": _ui_model or DEFAULT_MODEL_ID, + } + elif prov == "hf": + return { + "id": "hf", + "transport": "openai_compatible", + "url": "https://router.huggingface.co/v1/chat/completions", + "api_key": _ui_key, + "model": _ui_model or DEFAULT_MODEL_ID, + } + elif prov == "ollama": + url_str = _ui_url or "http://localhost:11434" + model_to_use = _pick_ollama_model(url_str, _ui_model.strip()) + if not model_to_use: + # Ollama reachable or not, no installed model was found: do not + # guess a tag that would 404 at decision time. + return None + return { + "id": "ollama", + "transport": "openai_compatible", + "url": _ollama_chat_url(url_str), + "api_key": "ollama-key", + "model": model_to_use, + } + + # 2. Check environment variables + openai_key = os.getenv("OPENAI_API_KEY", "").strip() + anthropic_key = os.getenv("ANTHROPIC_API_KEY", "").strip() + gemini_key = os.getenv("GEMINI_API_KEY", "").strip() + openrouter_key = os.getenv("OPENROUTER_API_KEY", "").strip() + + requested = os.getenv("CONFIG_AGENT_PROVIDER", "").strip().lower() + env_model = os.getenv("CONFIG_AGENT_MODEL", "").strip() + + if (requested == "openai" or (requested == "" and openai_key)) and openai_key: + return { + "id": "openai", + "transport": "openai_compatible", + "url": "https://api.openai.com/v1/chat/completions", + "api_key": openai_key, + "model": env_model or "gpt-4o-mini", + } + if (requested == "anthropic" or (requested == "" and anthropic_key)) and anthropic_key: + return { + "id": "anthropic", + "transport": "anthropic", + "url": "https://api.anthropic.com/v1/messages", + "api_key": anthropic_key, + "model": env_model or "claude-3-5-sonnet-20241022", + } + if (requested == "gemini" or (requested == "" and gemini_key)) and gemini_key: + return { + "id": "gemini", + "transport": "openai_compatible", + "url": "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions", + "api_key": gemini_key, + "model": env_model or "gemini-1.5-flash", + } + if (requested == "openrouter" or (requested == "" and openrouter_key)) and openrouter_key: + return { + "id": "openrouter", + "transport": "openai_compatible", + "url": "https://openrouter.ai/api/v1/chat/completions", + "api_key": openrouter_key, + "model": env_model or "google/gemini-2.5-flash", + } + + modal_base_url = os.getenv("MODAL_BASE_URL", "").strip().rstrip("/") + modal_api_key = os.getenv("MODAL_API_KEY", "").strip() + hf_token = os.getenv("HF_TOKEN", "").strip() + local_endpoint = os.getenv("LOCAL_AGENT_ENDPOINT", "http://localhost:8000/v1").strip().rstrip("/") + ollama_endpoint = os.getenv("OLLAMA_AGENT_ENDPOINT", "http://localhost:11434/v1").strip().rstrip("/") + + if requested in {"", "modal", "modal_vllm"} and modal_base_url and modal_api_key: + return { + "id": "modal", + "transport": "openai_compatible", + "url": f"{modal_base_url}/v1/chat/completions", + "api_key": modal_api_key, + "model": MODEL_ID, + } + if requested in {"", "hf", "huggingface", "hf_inference"} and hf_token: + return { + "id": "hf", + "transport": "openai_compatible", + "url": "https://router.huggingface.co/v1/chat/completions", + "api_key": hf_token, + "model": MODEL_ID, + } + if requested == "ollama": + model_to_use = _pick_ollama_model(ollama_endpoint, env_model or _ui_model.strip()) + if not model_to_use: + return None + return { + "id": "ollama", + "transport": "openai_compatible", + "url": _ollama_chat_url(ollama_endpoint), + "api_key": "ollama-key", + "model": model_to_use, + } + if requested == "local": + return { + "id": "local", + "transport": "openai_compatible", + "url": f"{local_endpoint}/chat/completions", + "api_key": "local-key", + "model": MODEL_ID, + } + + # Auto-detect local agents if no remote credentials are configured + if requested == "" and not modal_api_key and not hf_token: + import socket + from urllib.parse import urlparse + + # 1. Try Ollama (port 11434) + try: + parsed = urlparse(ollama_endpoint) + host = parsed.hostname or "localhost" + port = parsed.port or 11434 + with socket.create_connection((host, port), timeout=0.1): + model_to_use = _pick_ollama_model(f"http://{host}:{port}", env_model or _ui_model.strip()) + if model_to_use: + return { + "id": "ollama", + "transport": "openai_compatible", + "url": _ollama_chat_url(ollama_endpoint), + "api_key": "ollama-key", + "model": model_to_use, + } + except Exception: + pass + + # 2. Try WSL API Server (port 8000) + try: + parsed = urlparse(local_endpoint) + host = parsed.hostname or "localhost" + port = parsed.port or 8000 + with socket.create_connection((host, port), timeout=0.1): + return { + "id": "local", + "transport": "openai_compatible", + "url": f"{local_endpoint}/chat/completions", + "api_key": "local-key", + "model": MODEL_ID, + } + except Exception: + pass + + return None + + +def provider_status() -> dict[str, Any]: + provider = configured_provider() + if not provider: + return { + "configured": False, + "provider": "none", + "model": MODEL_ID, + "missing_any_of": ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY", "OPENROUTER_API_KEY", "MODAL_BASE_URL+MODAL_API_KEY", "HF_TOKEN", "local Ollama with an installed model"], + } + return { + "configured": True, + "provider": provider["id"], + "model": provider["model"], + "endpoint": provider["url"].replace("/chat/completions", "").replace("/v1/messages", ""), + } + + +def _missing_provider_decision(required: bool) -> DecisionEnvelope: + return DecisionEnvelope( + decision="REJECT" if required else "ASK_HUMAN_REVIEW", + confidence=1.0, + risk_level="high" if required else "medium", + selected_backend="none", + selected_model=MODEL_ID, + reasons=[ + "No real AI decision backend is configured.", + "Set OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENROUTER_API_KEY, MODAL_API_KEY, or HF_TOKEN, " + "or run a local Ollama with at least one installed model (e.g. `ollama pull gemma4:12b`).", + "The app does not fabricate a decision when no model is available.", + ], + required_human_review=True, + missing_secrets=["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY", "OPENROUTER_API_KEY", "MODAL_API_KEY", "HF_TOKEN"], + questions=[ + DecisionQuestion( + id="configure_remote_agent", + label="Decision agent", + question="Configure a cloud API key, Hugging Face secrets, or a local Ollama model, then run the decision again.", + answer_type="text", + required=True, + reason="A real cloud/local LLM call is required for AI-in-the-loop decisions.", + ) + ], + manifest_patch={}, + blocked=required, + ) + + +def _state_questions(app_state: dict[str, Any]) -> list[DecisionQuestion]: + questions: list[DecisionQuestion] = [] + hardware = app_state.get("hardware_profile") or {} + user_answers = app_state.get("user_answers") or {} + + if not hardware.get("os"): + questions.append( + DecisionQuestion( + id="target_os", + label="Target OS", + question="Which operating system should this package target: windows, macos, or linux?", + answer_type="choice", + options=["windows", "macos", "linux"], + reason="Backend binaries and builder scripts are platform-specific.", + ) + ) + if not hardware.get("ram_gb"): + questions.append( + DecisionQuestion( + id="ram_gb", + label="RAM", + question="How much system RAM is available on the target computer, in GB?", + answer_type="number", + reason="RAM determines the safe local model size.", + ) + ) + if app_state.get("target_mode") == "USB" and not app_state.get("target_available"): + questions.append( + DecisionQuestion( + id="usb_target", + label="USB target", + question="No real USB target is currently available. Should the builder switch to ZIP/local-folder, or will you provide a USB drive?", + answer_type="choice", + options=["switch_to_zip", "switch_to_local_folder", "provide_usb_drive"], + reason="The product must not claim to build a USB package without a detected target.", + ) + ) + if app_state.get("profile_source") == "sample" and not user_answers.get("confirmed_sample_profile"): + questions.append( + DecisionQuestion( + id="confirm_profile", + label="Hardware profile", + question="Is this sample hardware profile representative of the real client machine?", + answer_type="boolean", + reason="A sample profile is useful for demo, but it should be confirmed before client packaging.", + required=False, + ) + ) + return questions + + +def build_decision_prompt(app_state: dict[str, Any]) -> list[dict[str, str]]: + system = f""" +You are JackAILocal's internal packaging decision engine, not a chatbot. + +CRITICAL: You MUST NOT output any or thinking/reasoning process. Do not explain your choices or write any comments. Start your output directly with the opening curly brace '{{' of the JSON object. + +Return only a single valid JSON object. No markdown, no prose outside JSON. + +Hard rules: +1. Never invent API keys, files, installed models, endpoints, drives, hardware, or user answers. +2. If critical information is missing, use decision ASK_USER and provide concrete questions. +3. If human review is safer than proceeding, use ASK_HUMAN_REVIEW. +4. Never select a model above {app_state.get("max_params_b", 32)}B parameters. +5. Hosted AI is for packaging decisions only. The delivered runtime must remain local/offline after preload. +6. A USB build requires a real available target. If not available, ask the user or select ZIP/local-folder. +7. Choose backend per model support: Ollama refs for Ollama, GGUF paths for llama.cpp, HF repos for vLLM. +8. Prefer Cloud API when configured, otherwise auto-detected local Ollama. +9. Do not execute actions. Return a decision and manifest_patch only. + +Manifest Patch Guidelines: +- Under "manifest_patch", recommend software features and content packs for the target system. +- "voice_mode" (boolean): set to true if the user's goal or operator notes mention voice, TTS, STT, vocal, or talk. +- "phone_access" (boolean): set to true if they mention phone, mobile, sharing, LAN, network access, or sharing. +- "scout_vision" (boolean): set to true if they mention vision, scout, camera, image, or multimodal capabilities. +- "content_pack_ids" (array of strings): select from ["field_manual_core", "starter_prompt_packs"] based on storage limits and goals. +- "selected_backend" (string): must be one of "ollama", "llama.cpp", "vllm". +- "selected_client_model_ref" (string): the exact model ref matching a compatible model in app_state.allowed_models. + +Required JSON contract: +{{ + "decision": "BUILD_USB_PACKAGE | BUILD_ZIP_PACKAGE | BUILD_LOCAL_FOLDER | PUBLISH_APPLIANCE | ASK_USER | ASK_HUMAN_REVIEW | REJECT", + "confidence": 0.0, + "risk_level": "low | medium | high", + "selected_backend": "modal | hf | ollama | llama.cpp | vllm | none", + "selected_model": "string", + "selected_client_model_ref": "string or null", + "selected_runtime_agent_model_ref": "string or null", + "reasons": ["string"], + "required_human_review": false, + "missing_secrets": ["string"], + "questions": [ + {{ + "id": "string", + "label": "string", + "question": "string", + "answer_type": "text | json | boolean | choice | number", + "options": ["string"], + "required": true, + "reason": "string" + }} + ], + "manifest_patch": {{ + "selected_backend": "string", + "selected_client_model_ref": "string", + "voice_mode": false, + "phone_access": false, + "scout_vision": false, + "content_pack_ids": ["string"] + }}, + "blocked": false +}} +""" + user = { + "task": "Choose the safest deployable JackAILocal packaging configuration.", + "instructions": "Start your output directly with the JSON opening brace '{'. Do not write any reasoning, explanations, or thinking block.", + "app_state": app_state, + "provider_status": provider_status(), + } + return [ + {"role": "system", "content": system.strip()}, + {"role": "user", "content": json.dumps(user, ensure_ascii=False)}, + ] + + +def call_decision_model(app_state: dict[str, Any]) -> tuple[DecisionEnvelope, str, dict[str, Any] | None]: + """Run the decision against a real LLM provider. + + Returns (decision, source, provider) where source reflects the path that + actually executed: 'real_local_llm' (ollama/local), 'real_remote_llm' + (cloud APIs), or 'not_configured'. No decision is ever fabricated when no + provider is available.""" + global _last_trace + provider = configured_provider() + if not provider: + _last_trace = { + "source": "not_configured", + "note": "No LLM provider available: the app refused to fabricate a decision.", + } + return ( + _missing_provider_decision(bool(app_state.get("decision_engine_required"))), + "not_configured", + None, + ) + + messages = build_decision_prompt(app_state) + + # Check transport type + is_anthropic = (provider.get("transport") == "anthropic") + + if is_anthropic: + system_prompt = messages[0]["content"] + user_content = messages[1]["content"] + headers = { + "x-api-key": provider["api_key"], + "anthropic-version": "2023-06-01", + "content-type": "application/json", + } + request_json = { + "model": provider["model"], + "max_tokens": 1100, + "temperature": 0.0, + "system": system_prompt, + "messages": [ + {"role": "user", "content": user_content} + ] + } + else: + request_json = { + "model": provider["model"], + "messages": messages, + "temperature": 0.0, + "max_tokens": 1100, + } + # Add response format for models supporting JSON output mode + if provider["id"] in {"openai", "gemini", "openrouter", "modal", "hf"}: + request_json["response_format"] = {"type": "json_object"} + + if provider["id"] == "modal": + request_json["chat_template_kwargs"] = {"enable_thinking": False} + + headers = { + "Authorization": f"Bearer {provider['api_key']}", + "Content-Type": "application/json", + } + + log_dir = os.path.join(os.getenv("JACKAILOCAL_ROOT", "."), ".jackailocal", "logs") + os.makedirs(log_dir, exist_ok=True) + debug_log_path = os.path.join(log_dir, "decision_agent_debug.log") + + # On the HF router we may try several served models; everywhere else the + # provider's single configured model is used. First valid JSON wins, and + # provider["model"] is updated to whatever actually answered. + models_to_try = cloud_agent_candidates() if (not is_anthropic and provider["id"] == "hf") else [provider["model"]] + + content = "" + parsed = None + last_error = None + for candidate in models_to_try: + if not is_anthropic: + request_json["model"] = candidate + try: + with open(debug_log_path, "a", encoding="utf-8") as f: + f.write(f"\n\n=========================================\n") + f.write(f"REQUEST - Provider: {provider['id']}, Model: {candidate}, URL: {provider['url']}\n") + f.write(f"Prompt JSON: {json.dumps(messages, indent=2, ensure_ascii=False)}\n") + except Exception: + pass + + try: + response = requests.post( + provider["url"], + headers=headers, + json=request_json, + timeout=TIMEOUT_SECONDS, + ) + except Exception as err: + last_error = f"connection error ({candidate}): {err}" + try: + with open(debug_log_path, "a", encoding="utf-8") as f: + f.write(f"CONNECTION ERROR: {err}\n") + except Exception: + pass + continue + + try: + with open(debug_log_path, "a", encoding="utf-8") as f: + f.write(f"RESPONSE Status: {response.status_code}\n") + f.write(f"RESPONSE Text: {response.text[:2000]}\n") + except Exception: + pass + + if response.status_code != 200: + last_error = f"HTTP {response.status_code} ({candidate}): {response.text[:300]}" + continue + + payload = response.json() + if is_anthropic: + content = payload.get("content", [{}])[0].get("text", "") or "" + else: + content = payload.get("choices", [{}])[0].get("message", {}).get("content", "") or "" + + candidate_parsed = _parse_json_object_from_text(content) + if candidate_parsed: + parsed = candidate_parsed + provider["model"] = candidate + break + last_error = f"non-JSON/empty content from {candidate}" + + if not parsed: + raise RuntimeError(f"AI decision backend returned non-JSON content (last error: {last_error}); content head: {content[:400]}") + + try: + decision = _validate_decision(parsed) + except ValidationError as exc: + try: + with open(debug_log_path, "a", encoding="utf-8") as f: + f.write(f"VALIDATION ERROR: {exc}\nParsed JSON: {json.dumps(parsed, indent=2, ensure_ascii=False)}\n") + except Exception: + pass + raise RuntimeError(f"AI decision schema validation failed: {exc}") from exc + + try: + with open(debug_log_path, "a", encoding="utf-8") as f: + f.write(f"VALIDATION SUCCESSFUL! Decision: {decision.decision}\n") + except Exception: + pass + + source = "real_local_llm" if provider["id"] in {"ollama", "local"} else "real_remote_llm" + _last_trace = { + "source": source, + "provider": provider["id"], + "model": provider["model"], + "endpoint": provider["url"], + "messages": messages, + "raw_model_output": content, + "validated_decision": _dump_decision(decision), + } + return decision, source, provider + + +def policy_gate(decision: DecisionEnvelope, app_state: dict[str, Any], provider: dict[str, Any] | None = None) -> DecisionEnvelope: + provider_id = provider["id"] if provider else "none" + + if decision.selected_backend in {"modal", "hf"} and decision.selected_backend != provider_id: + return DecisionEnvelope( + decision="ASK_HUMAN_REVIEW", + confidence=1.0, + risk_level="medium", + selected_backend=provider_id if provider_id in {"modal", "hf"} else "none", + selected_model=MODEL_ID, + reasons=[ + f"AI selected backend {decision.selected_backend}, but configured provider is {provider_id}.", + "Policy gate stopped the mismatch before manifest application.", + ], + required_human_review=True, + missing_secrets=decision.missing_secrets, + questions=[ + DecisionQuestion( + id="backend_mismatch", + label="Backend mismatch", + question="Update the Space secrets/provider or rerun the decision with the configured backend.", + answer_type="text", + reason="The selected hosted backend must match the configured real provider.", + ) + ], + manifest_patch={}, + blocked=False, + ) + + preflight_questions = _state_questions(app_state) + required_preflight_questions = [question for question in preflight_questions if question.required] + if required_preflight_questions and decision.decision not in {"ASK_USER", "ASK_HUMAN_REVIEW", "REJECT"}: + return DecisionEnvelope( + decision="ASK_USER", + confidence=1.0, + risk_level="medium", + selected_backend=decision.selected_backend, + selected_model=decision.selected_model, + selected_client_model_ref=decision.selected_client_model_ref, + selected_runtime_agent_model_ref=decision.selected_runtime_agent_model_ref, + reasons=[ + "Policy gate requires more user configuration before package generation.", + *decision.reasons, + ], + required_human_review=False, + missing_secrets=decision.missing_secrets, + questions=required_preflight_questions, + manifest_patch=decision.manifest_patch, + blocked=False, + ) + + if decision.decision == "BUILD_USB_PACKAGE" and not app_state.get("target_available"): + return DecisionEnvelope( + decision="ASK_USER", + confidence=1.0, + risk_level="medium", + selected_backend=decision.selected_backend, + selected_model=decision.selected_model, + selected_client_model_ref=decision.selected_client_model_ref, + selected_runtime_agent_model_ref=decision.selected_runtime_agent_model_ref, + reasons=[ + "The model selected a USB build, but no real USB target is available.", + "Policy gate requires the user to provide a USB target or switch deployment mode.", + ], + required_human_review=False, + missing_secrets=decision.missing_secrets, + questions=[ + DecisionQuestion( + id="usb_target", + label="USB target", + question="Provide a real USB target or choose ZIP/local-folder deployment.", + answer_type="choice", + options=["provide_usb_drive", "switch_to_zip", "switch_to_local_folder"], + reason="A USB build cannot be real without a detected target.", + ) + ], + manifest_patch={ + **decision.manifest_patch, + "policy_override": "usb_target_missing", + }, + blocked=False, + ) + + return decision + + +def decide_internal_action(app_state: dict[str, Any]) -> dict[str, Any]: + decision, source, provider = call_decision_model(app_state) + gated = policy_gate(decision, app_state, provider) + output = _dump_decision(gated) + if provider: + output["provider_status"] = { + "configured": True, + "provider": provider["id"], + "model": provider["model"], + "endpoint": provider["url"].replace("/chat/completions", "").replace("/v1/messages", ""), + } + else: + output["provider_status"] = provider_status() + output["source"] = source + return output diff --git a/saas/gradio/examples/linux-appliance.json b/saas/gradio/examples/linux-appliance.json new file mode 100644 index 0000000000000000000000000000000000000000..b913acca9026f2c1366ced219831fa731e0b485d --- /dev/null +++ b/saas/gradio/examples/linux-appliance.json @@ -0,0 +1,8 @@ +{ + "os": "linux", + "cpu_threads": 16, + "ram_gb": 32, + "vram_gb": 12, + "gpu_name": "NVIDIA GPU", + "disk": "NVMe" +} diff --git a/saas/gradio/examples/low-end-laptop.json b/saas/gradio/examples/low-end-laptop.json new file mode 100644 index 0000000000000000000000000000000000000000..5d6af3e3fab375e9fcbdbe5e3ff615e31640b606 --- /dev/null +++ b/saas/gradio/examples/low-end-laptop.json @@ -0,0 +1,8 @@ +{ + "os": "windows", + "cpu_threads": 4, + "ram_gb": 8, + "vram_gb": 0, + "gpu_name": "Integrated graphics", + "disk": "USB 3.0" +} diff --git a/saas/gradio/examples/normal-laptop.json b/saas/gradio/examples/normal-laptop.json new file mode 100644 index 0000000000000000000000000000000000000000..9423ff1f92118a66ac80fab04dbf4b16d3465f81 --- /dev/null +++ b/saas/gradio/examples/normal-laptop.json @@ -0,0 +1,8 @@ +{ + "os": "windows", + "cpu_threads": 8, + "ram_gb": 16, + "vram_gb": 4, + "gpu_name": "Entry NVIDIA/AMD GPU", + "disk": "USB 3.2 SSD" +} diff --git a/saas/gradio/requirements.txt b/saas/gradio/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..82b11076759098c128ece92315805612025a9c66 --- /dev/null +++ b/saas/gradio/requirements.txt @@ -0,0 +1,4 @@ +gradio>=4.44.0 +requests +huggingface_hub>=0.34.0 +pydantic>=2.0.0 diff --git a/saas/worker/build_package.py b/saas/worker/build_package.py new file mode 100644 index 0000000000000000000000000000000000000000..cb9bb4e1e7341d3b9328f3344538afa130a5a018 --- /dev/null +++ b/saas/worker/build_package.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +"""Builds a downloadable JackAILocal customer bundle from a manifest.""" +import json, zipfile, sys, pathlib, hashlib + +def sha256(p): + h=hashlib.sha256(); h.update(pathlib.Path(p).read_bytes()); return h.hexdigest() + +def main(): + if len(sys.argv) != 4: + print("usage: build_package.py "); sys.exit(2) + seed=pathlib.Path(sys.argv[1]); manifest=pathlib.Path(sys.argv[2]); out=pathlib.Path(sys.argv[3]) + excluded_dirs={'.git','target','workspace','diagnostics','.jackailocal','.jackailocal-builder','__pycache__','license-keys'} + excluded_files={'config/update-private-key.xml'} + with zipfile.ZipFile(out, 'w', zipfile.ZIP_DEFLATED) as z: + for p in seed.rglob('*'): + rel=p.relative_to(seed).as_posix() + if p.is_file() and not any(part in excluded_dirs for part in p.parts) and rel not in excluded_files and not p.name.endswith('.pyc'): + z.write(p, p.relative_to(seed)) + z.write(manifest, 'manifest/build-manifest.json') + print(out, sha256(out)) +if __name__ == '__main__': main() diff --git a/scratch/chat_gemma.py b/scratch/chat_gemma.py new file mode 100644 index 0000000000000000000000000000000000000000..889896d5575df91412836049fb8277de0ceb6d4d --- /dev/null +++ b/scratch/chat_gemma.py @@ -0,0 +1,62 @@ +import sys +from transformers import AutoModelForCausalLM, AutoTokenizer, TextStreamer + +repo_id = "OBLITERATUS/Gemma-4-12B-OBLITERATED" + +print(f"Loading tokenizer for {repo_id}...") +tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True) +print(f"Loading model weights for {repo_id} (device_map='auto')...") +model = AutoModelForCausalLM.from_pretrained( + repo_id, + device_map="auto", + torch_dtype="auto", + trust_remote_code=True, +) +print("Model loaded successfully!") + +messages = [] + +print("\n--- CLI Chat with Gemma 4 ---") +print("Type 'exit' or 'quit' to end the chat.") + +while True: + try: + user_input = input("\nYou: ") + if not user_input.strip(): + continue + if user_input.strip().lower() in ["exit", "quit"]: + break + + messages.append({"role": "user", "content": user_input}) + + text = tokenizer.apply_chat_template( + messages, + tokenize=False, + add_generation_prompt=True, + enable_thinking=False, + ) + + inputs = tokenizer(text, return_tensors="pt").to(model.device) + + print("\nAssistant: ", end="", flush=True) + streamer = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True) + + output = model.generate( + **inputs, + max_new_tokens=1024, + temperature=0.7, + top_p=0.9, + top_k=40, + do_sample=True, + repetition_penalty=1.1, + streamer=streamer, + ) + + generated_text = tokenizer.decode(output[0][inputs["input_ids"].shape[-1]:], skip_special_tokens=True) + messages.append({"role": "assistant", "content": generated_text}) + + except KeyboardInterrupt: + print("\nExiting...") + break + except Exception as e: + print(f"\nError: {e}") diff --git a/scratch/download_shard.py b/scratch/download_shard.py new file mode 100644 index 0000000000000000000000000000000000000000..38b3a15d1c9a935ba16047f7d11f6f782ef52528 --- /dev/null +++ b/scratch/download_shard.py @@ -0,0 +1,20 @@ +import logging +import os +import sys +from huggingface_hub import hf_hub_download + +# Set up debug logging to stdout +logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) + +repo_id = "OBLITERATUS/Gemma-4-12B-OBLITERATED" +filename = "model-00010-of-00013.safetensors" + +print("Starting download with debug logging...") +try: + path = hf_hub_download( + repo_id=repo_id, + filename=filename, + ) + print(f"Success! Downloaded to {path}") +except Exception as e: + print(f"Failed with exception: {e}") diff --git a/scratch/serve_gemma.py b/scratch/serve_gemma.py new file mode 100644 index 0000000000000000000000000000000000000000..478c6d3932fbc6abf324ce12e85257115d2c9327 --- /dev/null +++ b/scratch/serve_gemma.py @@ -0,0 +1,286 @@ +import argparse +import asyncio +import json +import os +import sys +import threading +import time +from typing import List, Dict, Any, Union +from fastapi import FastAPI, Request +from fastapi.responses import StreamingResponse +from huggingface_hub import HfApi +from pydantic import BaseModel +import torch +import uvicorn +from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer + +app = FastAPI(title="Gemma-4 HF API Server") + +# Global model & tokenizer references +model = None +tokenizer = None +loaded_repo_id = None + +WEIGHT_FILENAMES = { + "model.safetensors", + "model.safetensors.index.json", + "pytorch_model.bin", + "pytorch_model.bin.index.json", +} + +class ChatMessage(BaseModel): + role: str + content: str + +class ChatCompletionRequest(BaseModel): + model: str = "OBLITERATUS/Gemma-4-12B-OBLITERATED" + messages: List[ChatMessage] + temperature: float = 0.7 + top_p: float = 0.9 + top_k: int = 40 + max_tokens: int = 512 + stream: bool = False + repetition_penalty: float = 1.1 + +def repo_has_transformers_weights(repo_id: str, token: str | None) -> bool: + files = HfApi(token=token).list_repo_files(repo_id=repo_id, repo_type="model") + return any( + filename in WEIGHT_FILENAMES + or filename.endswith(".safetensors") + or filename.endswith(".bin") + for filename in files + ) + + +def resolve_repo_id( + repo_id: str, + fallback_repo_id: str | None, + wait_for_weights: int, + poll_interval: int, + token: str | None, +) -> str: + deadline = time.monotonic() + wait_for_weights + + while True: + if repo_has_transformers_weights(repo_id, token): + return repo_id + + if time.monotonic() >= deadline: + break + + remaining = max(0, int(deadline - time.monotonic())) + print( + f"No Transformers weights are published for {repo_id} yet. " + f"Checking again in {poll_interval}s ({remaining}s remaining)...", + flush=True, + ) + time.sleep(min(poll_interval, remaining)) + + if fallback_repo_id: + if not repo_has_transformers_weights(fallback_repo_id, token): + raise RuntimeError( + f"Neither {repo_id} nor fallback {fallback_repo_id} contains " + "Transformers weights." + ) + print( + f"WARNING: {repo_id} has no Transformers weights. " + f"Using the explicitly requested fallback {fallback_repo_id}.", + flush=True, + ) + return fallback_repo_id + + raise RuntimeError( + f"{repo_id} does not currently contain model weights. Its Hugging Face " + "repository only publishes configuration/tokenizer files, so " + "AutoModelForCausalLM cannot load it.\n" + "Wait for the advertised weight files to finish publishing, or run an " + "explicit fallback, for example:\n" + " --fallback-repo-id google/gemma-4-12B-it\n" + "To wait for an in-progress upload, add:\n" + " --wait-for-weights 3600" + ) + + +def load_model( + repo_id: str, + fallback_repo_id: str | None = None, + wait_for_weights: int = 0, + poll_interval: int = 60, +): + global model, tokenizer, loaded_repo_id + token = os.environ.get("HF_TOKEN") or None + selected_repo_id = resolve_repo_id( + repo_id=repo_id, + fallback_repo_id=fallback_repo_id, + wait_for_weights=max(0, wait_for_weights), + poll_interval=max(5, poll_interval), + token=token, + ) + + if token is None: + print( + "HF_TOKEN is not set. Public downloads still work, but Hugging Face " + "applies lower rate limits.", + flush=True, + ) + + print(f"Loading tokenizer for {selected_repo_id}...") + tokenizer = AutoTokenizer.from_pretrained( + selected_repo_id, + trust_remote_code=True, + token=token, + ) + print(f"Loading model weights for {selected_repo_id} (device_map='auto')...") + model = AutoModelForCausalLM.from_pretrained( + selected_repo_id, + device_map="auto", + torch_dtype="auto", + trust_remote_code=True, + token=token, + ) + loaded_repo_id = selected_repo_id + print(f"Model loaded successfully: {selected_repo_id}") + + +@app.get("/health") +async def health(): + return { + "status": "ok" if model is not None and tokenizer is not None else "loading", + "model": loaded_repo_id, + } + +async def stream_generator(streamer: TextIteratorStreamer): + loop = asyncio.get_event_loop() + while True: + try: + token = await loop.run_in_executor(None, lambda: next(streamer, None)) + if token is None: + break + + chunk = { + "choices": [ + { + "delta": {"content": token}, + "finish_reason": None, + "index": 0 + } + ] + } + yield f"data: {json.dumps(chunk)}\n\n" + except Exception as e: + print(f"Error in stream: {e}") + break + + chunk_done = { + "choices": [ + { + "delta": {}, + "finish_reason": "stop", + "index": 0 + } + ] + } + yield f"data: {json.dumps(chunk_done)}\n\n" + yield "data: [DONE]\n\n" + +@app.post("/v1/chat/completions") +async def chat_completions(request: ChatCompletionRequest): + global model, tokenizer + if model is None or tokenizer is None: + return {"error": "Model not loaded"} + + messages_list = [{"role": msg.role, "content": msg.content} for msg in request.messages] + prompt = tokenizer.apply_chat_template( + messages_list, + tokenize=False, + add_generation_prompt=True, + enable_thinking=False, + ) + + inputs = tokenizer(prompt, return_tensors="pt").to(model.device) + + if request.stream: + streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True) + + generation_kwargs = dict( + **inputs, + max_new_tokens=request.max_tokens, + temperature=request.temperature, + top_p=request.top_p, + top_k=request.top_k, + do_sample=True, + repetition_penalty=request.repetition_penalty, + streamer=streamer, + ) + + thread = threading.Thread(target=model.generate, kwargs=generation_kwargs) + thread.start() + + return StreamingResponse(stream_generator(streamer), media_type="text/event-stream") + + else: + with torch.no_grad(): + output = model.generate( + **inputs, + max_new_tokens=request.max_tokens, + temperature=request.temperature, + top_p=request.top_p, + top_k=request.top_k, + do_sample=True, + repetition_penalty=request.repetition_penalty, + ) + + generated_text = tokenizer.decode(output[0][inputs["input_ids"].shape[-1]:], skip_special_tokens=True) + + return { + "choices": [ + { + "message": { + "role": "assistant", + "content": generated_text + }, + "finish_reason": "stop", + "index": 0 + } + ] + } + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--repo-id", type=str, default="OBLITERATUS/Gemma-4-12B-OBLITERATED") + parser.add_argument( + "--fallback-repo-id", + type=str, + default=None, + help="Explicit model to use only when --repo-id has no published weights.", + ) + parser.add_argument( + "--wait-for-weights", + type=int, + default=0, + metavar="SECONDS", + help="Wait for an in-progress Hugging Face upload before failing.", + ) + parser.add_argument( + "--poll-interval", + type=int, + default=60, + metavar="SECONDS", + help="Hugging Face polling interval used with --wait-for-weights.", + ) + parser.add_argument("--host", type=str, default="0.0.0.0") + parser.add_argument("--port", type=int, default=8000) + args = parser.parse_args() + + try: + load_model( + repo_id=args.repo_id, + fallback_repo_id=args.fallback_repo_id, + wait_for_weights=args.wait_for_weights, + poll_interval=args.poll_interval, + ) + except RuntimeError as exc: + print(f"\nERROR: {exc}", file=sys.stderr) + raise SystemExit(2) from None + + uvicorn.run(app, host=args.host, port=args.port) diff --git a/scratch/test_gemma.py b/scratch/test_gemma.py new file mode 100644 index 0000000000000000000000000000000000000000..647a8ea7cc1635771be166d03fa266d2ae4e0d53 --- /dev/null +++ b/scratch/test_gemma.py @@ -0,0 +1,35 @@ +from transformers import AutoModelForCausalLM, AutoTokenizer, TextStreamer + +repo_id = "OBLITERATUS/Gemma-4-12B-OBLITERATED" + +tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True) +model = AutoModelForCausalLM.from_pretrained( + repo_id, + device_map="auto", + torch_dtype="auto", + trust_remote_code=True, +) + +messages = [ + {"role": "user", "content": "Write a concise Python function that merges overlapping intervals."} +] +text = tokenizer.apply_chat_template( + messages, + tokenize=False, + add_generation_prompt=True, + enable_thinking=False, +) +inputs = tokenizer(text, return_tensors="pt").to(model.device) + +streamer = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True) + +output = model.generate( + **inputs, + max_new_tokens=50, + temperature=0.7, + top_p=0.9, + top_k=40, + do_sample=True, + repetition_penalty=1.1, + streamer=streamer, +) diff --git a/scratch/test_gradio_decision.py b/scratch/test_gradio_decision.py new file mode 100644 index 0000000000000000000000000000000000000000..afbbccf6f6993eb506ea4a2bbab82cb30e85b91b --- /dev/null +++ b/scratch/test_gradio_decision.py @@ -0,0 +1,37 @@ +import sys +import os +import json +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../saas/gradio"))) +import app +import decision_engine + +print("Provider status:", decision_engine.provider_status()) + +# Get the list of sample profiles +profiles = app.SAMPLE_PROFILES +print("Available profiles:", list(profiles.keys())) + +for name in profiles.keys(): + print(f"\n--- Testing profile: {name} ---") + plan = app.build_plan_object("EN", name, "", "Standard offline assistant") + app_state = app.build_decision_app_state( + "EN", + plan, + "", + simple_target="local_runtime", + target_mode="LocalFolder", + target_drive="C:/", + user_answers_json="{}", + operator_notes="", + decision_engine_required=True, + ) + + try: + decision = decision_engine.decide_internal_action(app_state) + print("Decision returned successfully!") + print("Decision:", decision.get("decision")) + print("Selected Model:", decision.get("selected_client_model_ref") or decision.get("selected_model")) + print("Selected Backend:", decision.get("selected_backend")) + print("Reasons:", decision.get("reasons")) + except Exception as e: + print(f"FAILED with exception: {type(e).__name__}: {e}") diff --git a/scratch/test_ollama_raw.py b/scratch/test_ollama_raw.py new file mode 100644 index 0000000000000000000000000000000000000000..d41923904dd02ed64027da6a214aa4c6fd5acbc6 --- /dev/null +++ b/scratch/test_ollama_raw.py @@ -0,0 +1,89 @@ +import sys +import os +import json +import urllib.request +import urllib.error + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../saas/gradio"))) +import decision_engine + +app_state = { + "language": "en", + "product": "JackAILocal", + "selector_version": "1.0.0", + "build_id": "test-build-123", + "package_goal": "Standard offline assistant", + "requested_action": "BUILD_LOCAL_FOLDER", + "simple_target": "local_runtime", + "target_mode": "LocalFolder", + "target_drive": "C:/", + "target_available": True, + "running_in_hosted_space": False, + "profile_source": "sample", + "hardware_profile": { + "os": "windows", + "cpu_threads": 8, + "ram_gb": 16, + "vram_gb": 0, + "gpu_name": "CPU-only office laptop", + "disk": "USB 3.2 SSD", + "usb_storage_gb": 128 + }, + "default_model": { + "id": "qwen35_smart_9b", + "model_ref": "qwen3.5:9b" + }, + "allowed_models": [ + {"id": "qwen35_fast_2b", "model_ref": "qwen3.5:2b", "task": "chat", "tier": "standard"}, + {"id": "qwen35_balanced_4b", "model_ref": "qwen3.5:4b", "task": "chat", "tier": "standard"}, + {"id": "qwen35_smart_9b", "model_ref": "qwen3.5:9b", "task": "chat", "tier": "standard"} + ], + "max_params_b": 32, + "constraints": { + "no_fake_or_simulated_backend": True, + "local_runtime_after_preload": True, + "max_params_b": 32, + "normal_runtime_local_only": True, + "backend_specific_models": True + }, + "user_answers": {}, + "operator_notes": "" +} + +messages = decision_engine.build_decision_prompt(app_state) + +url = "http://localhost:11434/v1/chat/completions" +request_json = { + "model": "gemma4:12b", + "messages": messages, + "temperature": 0.0, + "max_tokens": 1100, + "response_format": {"type": "json_object"} +} + +req = urllib.request.Request( + url, + data=json.dumps(request_json).encode("utf-8"), + headers={"Content-Type": "application/json"}, + method="POST" +) + +print(f"Sending request to Ollama at {url} using urllib...") +try: + with urllib.request.urlopen(req, timeout=120) as response: + status_code = response.getcode() + body = response.read().decode("utf-8") + print(f"Status Code: {status_code}") + print("Response Body:") + print(body) + + payload = json.loads(body) + content = payload.get("choices", [{}])[0].get("message", {}).get("content", "") + print(f"\nGenerated content string: '{content}'") + + parsed = decision_engine._parse_json_object_from_text(content) + print(f"Parsed JSON object: {parsed}") +except urllib.error.HTTPError as e: + print(f"HTTPError: {e.code} - {e.read().decode('utf-8')}") +except Exception as e: + print(f"Error: {e}") diff --git a/scratch/test_port.py b/scratch/test_port.py new file mode 100644 index 0000000000000000000000000000000000000000..18b20ad9863d935a201e8ee29c8b1781532720c5 --- /dev/null +++ b/scratch/test_port.py @@ -0,0 +1,15 @@ +import socket + +def check_port(host, port): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(2) + try: + s.connect((host, port)) + print(f"Port {port} is OPEN!") + s.close() + return True + except Exception as e: + print(f"Port {port} is CLOSED: {e}") + return False + +check_port("127.0.0.1", 7870) diff --git a/scratch/test_unified_audit.py b/scratch/test_unified_audit.py new file mode 100644 index 0000000000000000000000000000000000000000..637fd049238fde8307b7f656c1c5d391bca07cf3 --- /dev/null +++ b/scratch/test_unified_audit.py @@ -0,0 +1,30 @@ +import sys +import os +import json +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../saas/gradio"))) +import app + +print("Testing run_unified_ai_audit...") + +markdown_result, json_result = app.run_unified_ai_audit( + language="EN", + sample_name="Normal laptop", + raw_profile_json="", + package_goal="Standard offline assistant", + simple_target="local_runtime", + target_mode="LocalFolder", + target_drive="C:/", + decision_answers_json="{}", + operator_notes="Must support voice offline, target is somewhat limited hardware.", + agent_model_choice="auto" +) + +print("\n--- AUDIT MARKDOWN OUTPUT ---") +print(markdown_result) +print("\n--- AUDIT JSON OUTPUT ---") +try: + parsed = json.loads(json_result) + print(json.dumps(parsed, indent=2)) +except Exception as e: + print(f"Error parsing JSON output: {e}") + print(json_result) diff --git a/scripts/New-SvnDevBranch.ps1 b/scripts/New-SvnDevBranch.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..c9614c23b40b0e053715d8d3830cce36116a10bf --- /dev/null +++ b/scripts/New-SvnDevBranch.ps1 @@ -0,0 +1,178 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string]$BaseBranchUrl, + + [Parameter(Mandatory = $false)] + [string]$BranchName, + + [Parameter(Mandatory = $false)] + [string]$Ticket, + + [Parameter(Mandatory = $false)] + [string]$Description, + + [Parameter(Mandatory = $false)] + [string]$AuthorInitials, + + [Parameter(Mandatory = $false)] + [string]$CommitMessage, + + [Parameter(Mandatory = $false)] + [string]$CheckoutPath, + + [switch]$Force, + [switch]$WhatIfOnly +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +function Assert-SvnCli { + if (-not (Get-Command svn -ErrorAction SilentlyContinue)) { + throw "Le client svn n'est pas disponible dans le PATH." + } +} + +function Normalize-Slug { + param([string]$Value) + + if ([string]::IsNullOrWhiteSpace($Value)) { + return "modif" + } + + $normalized = $Value.ToLowerInvariant() + $normalized = $normalized -replace "[^a-z0-9]+", "-" + $normalized = $normalized.Trim("-") + + if ([string]::IsNullOrWhiteSpace($normalized)) { + return "modif" + } + + return $normalized +} + +function Get-ParentUrl { + param([string]$Url) + + $trimmed = $Url.TrimEnd("/") + $lastSlashIndex = $trimmed.LastIndexOf("/") + if ($lastSlashIndex -lt 0) { + throw "Impossible de determiner le parent de l'URL: $Url" + } + + return $trimmed.Substring(0, $lastSlashIndex) +} + +function Test-BranchExists { + param([string]$Url) + + & svn info "$Url" *> $null + return ($LASTEXITCODE -eq 0) +} + +function New-DefaultBranchName { + param( + [string]$Ticket, + [string]$Description, + [string]$AuthorInitials + ) + + $datePart = (Get-Date).ToString("yyyyMMdd") + $ticketPart = if ([string]::IsNullOrWhiteSpace($Ticket)) { "NO-TICKET" } else { $Ticket.Trim().ToUpperInvariant() } + $authorPart = if ([string]::IsNullOrWhiteSpace($AuthorInitials)) { "XX" } else { $AuthorInitials.Trim().ToUpperInvariant() } + $slugPart = Normalize-Slug -Value $Description + + return "dev_${ticketPart}_${authorPart}_${slugPart}_${datePart}" +} + +function Validate-BranchName { + param([string]$Name) + + $regex = "^dev_[A-Z0-9-]+_[A-Z]{2,5}_[a-z0-9-]+_[0-9]{8}$" + if ($Name -notmatch $regex) { + throw @" +Nom de branche invalide: $Name +Pattern attendu: dev____ +Exemple: dev_SPRF-3124_HBK_fix-journalisation-404_20260319 +"@ + } +} + +function New-DefaultCommitMessage { + param( + [string]$Ticket, + [string]$Description, + [string]$BranchName + ) + + $ticketPart = if ([string]::IsNullOrWhiteSpace($Ticket)) { "NO-TICKET" } else { $Ticket.Trim().ToUpperInvariant() } + $descriptionPart = if ([string]::IsNullOrWhiteSpace($Description)) { "Correctifs" } else { $Description.Trim() } + + return "$ticketPart - $descriptionPart | Branche: $BranchName" +} + +Assert-SvnCli + +if ([string]::IsNullOrWhiteSpace($BranchName)) { + $BranchName = New-DefaultBranchName -Ticket $Ticket -Description $Description -AuthorInitials $AuthorInitials +} + +Validate-BranchName -Name $BranchName + +$baseUrl = $BaseBranchUrl.TrimEnd("/") +$parentUrl = Get-ParentUrl -Url $baseUrl +$newBranchUrl = "$parentUrl/$BranchName" + +if ([string]::IsNullOrWhiteSpace($CommitMessage)) { + $CommitMessage = New-DefaultCommitMessage -Ticket $Ticket -Description $Description -BranchName $BranchName +} + +Write-Host "Base branch URL : $baseUrl" -ForegroundColor Cyan +Write-Host "New branch name : $BranchName" -ForegroundColor Cyan +Write-Host "New branch URL : $newBranchUrl" -ForegroundColor Cyan +Write-Host "Create message : $CommitMessage" -ForegroundColor Cyan +if (-not [string]::IsNullOrWhiteSpace($CheckoutPath)) { + Write-Host "Checkout path : $CheckoutPath" -ForegroundColor Cyan +} + +if ((Test-BranchExists -Url $newBranchUrl) -and -not $Force) { + throw "La branche existe deja: $newBranchUrl. Utilisez -Force pour continuer." +} + +if ($WhatIfOnly) { + Write-Host "Mode simulation: aucune commande svn copy/checkout executee." -ForegroundColor Yellow + return +} + +if (Test-BranchExists -Url $newBranchUrl) { + Write-Host "La branche existe deja et -Force est actif. Aucune creation effectuee." -ForegroundColor Yellow +} +else { + Write-Host "Creation de la branche..." -ForegroundColor Green + & svn copy "$baseUrl" "$newBranchUrl" -m "$CommitMessage" + if ($LASTEXITCODE -ne 0) { + throw "Echec de creation de branche SVN." + } +} + +if (-not [string]::IsNullOrWhiteSpace($CheckoutPath)) { + if (Test-Path -LiteralPath $CheckoutPath) { + if (-not $Force) { + throw "Le repertoire de checkout existe deja: $CheckoutPath. Utilisez -Force pour l'autoriser." + } + + Write-Host "Le repertoire existe deja, checkout ignore en mode -Force." -ForegroundColor Yellow + } + else { + Write-Host "Checkout de la nouvelle branche..." -ForegroundColor Green + & svn checkout "$newBranchUrl" "$CheckoutPath" + if ($LASTEXITCODE -ne 0) { + throw "Echec du checkout SVN." + } + } +} + +Write-Host "Termine." -ForegroundColor Green +Write-Host "Message de commit suggere pour les changements de code:" -ForegroundColor Cyan +Write-Host ("{0} - Correctifs journalisation (404 toleres + fallback reponse vide)" -f ($(if ([string]::IsNullOrWhiteSpace($Ticket)) { "NO-TICKET" } else { $Ticket.Trim().ToUpperInvariant() }))) -ForegroundColor Gray diff --git a/scripts/dev-start.ps1 b/scripts/dev-start.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..e324bf704ee2473e805f92f0e0b168dda9a5e956 --- /dev/null +++ b/scripts/dev-start.ps1 @@ -0,0 +1,45 @@ +# dev-start.ps1 — Lance API + UI en mode watch (Kestrel) sans IIS +# Usage : .\dev-start.ps1 (lance les deux) +# .\dev-start.ps1 -api (API seulement) +# .\dev-start.ps1 -ui (UI seulement) + +param( + [switch]$api, + [switch]$ui +) + +$projetApi = "" +$projetUi = "" +$urlApi = "https://localhost:5001" +$urlUi = "https://localhost:5002" + +# Si aucun flag, lancer les deux +if (-not $api -and -not $ui) { + $api = $true + $ui = $true +} + +if ($api) { + if ([string]::IsNullOrWhiteSpace($projetApi)) { + Write-Host "[ERREUR] Chemin API non configure dans ce depot." -ForegroundColor Red + exit 1 + } + Write-Host "[DEV] Demarrage API sur $urlApi ..." -ForegroundColor Cyan + Start-Process powershell -ArgumentList "-NoExit", "-Command", "cd '$PSScriptRoot'; dotnet watch run --project $projetApi --urls $urlApi" +} + +if ($ui) { + if ([string]::IsNullOrWhiteSpace($projetUi)) { + Write-Host "[ERREUR] Chemin UI non configure dans ce depot." -ForegroundColor Red + exit 1 + } + Write-Host "[DEV] Demarrage UI sur $urlUi ..." -ForegroundColor Green + Start-Process powershell -ArgumentList "-NoExit", "-Command", "cd '$PSScriptRoot'; dotnet watch run --project $projetUi --urls $urlUi" +} + +Write-Host "" +Write-Host "=== Serveurs de developpement ===" -ForegroundColor Yellow +if ($api) { Write-Host " API : $urlApi/swagger" -ForegroundColor Cyan } +if ($ui) { Write-Host " UI : $urlUi" -ForegroundColor Green } +Write-Host " Ctrl+C dans chaque fenetre pour arreter" -ForegroundColor DarkGray +Write-Host "" diff --git a/scripts/run-gradio-saas-preview.cmd b/scripts/run-gradio-saas-preview.cmd new file mode 100644 index 0000000000000000000000000000000000000000..3e3974cade5c7938a79aa4d8e454d1ef0873c129 --- /dev/null +++ b/scripts/run-gradio-saas-preview.cmd @@ -0,0 +1,10 @@ +@echo off +setlocal +cd /d "%~dp0..\saas\gradio" +if not exist .venv ( + python -m venv .venv +) +call .venv\Scripts\activate.bat +python -m pip install --upgrade pip +python -m pip install -r requirements.txt +python app.py diff --git a/scripts/support-bundle.ps1 b/scripts/support-bundle.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..3ca8f77a7059746c3c25241de5abb95cce5fc392 --- /dev/null +++ b/scripts/support-bundle.ps1 @@ -0,0 +1,10 @@ +param([string]$OutDir = ".\support") +$ErrorActionPreference = "Stop" +New-Item -ItemType Directory -Force -Path $OutDir | Out-Null +$root = Split-Path -Parent $PSScriptRoot +Copy-Item "$root\diagnostics\hardware.json" "$OutDir\hardware.json" -ErrorAction SilentlyContinue +Copy-Item "$root\manifest\sha256-manifest.json" "$OutDir\sha256-manifest.json" -ErrorAction SilentlyContinue +Copy-Item "$root\config\model-catalog.json" "$OutDir\model-catalog.json" -ErrorAction SilentlyContinue +Get-ChildItem "$root\logs" -ErrorAction SilentlyContinue | Copy-Item -Destination $OutDir -ErrorAction SilentlyContinue +Compress-Archive -Path "$OutDir\*" -DestinationPath "$OutDir\JackAILocal-support-bundle.zip" -Force +Write-Host "Created $OutDir\JackAILocal-support-bundle.zip" diff --git a/src/crypto_pack.rs b/src/crypto_pack.rs new file mode 100644 index 0000000000000000000000000000000000000000..526f2f6616e8eec3b1b4b96eba433d61add06a71 --- /dev/null +++ b/src/crypto_pack.rs @@ -0,0 +1,205 @@ +use crate::storage::{self, PackedDocument, ThreadRecord}; +use aes_gcm::{ + aead::{Aead, Payload}, + Aes256Gcm, KeyInit, Nonce, +}; +use anyhow::{bail, Context, Result}; +use base64::{engine::general_purpose::STANDARD as B64, Engine as _}; +use chrono::Utc; +use pbkdf2::pbkdf2_hmac; +use rand::{rngs::OsRng, RngCore}; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use sha2::Sha256; +use std::path::Path; + +const FORMAT: &str = "jackailocal-encrypted-pack"; +const VERSION: u32 = 1; +const PBKDF2_ITERATIONS: u32 = 210_000; +const AAD: &[u8] = b"JackAILocal encrypted transfer pack v1"; + +#[derive(Debug, Clone, Deserialize)] +pub struct ExportOptions { + pub passphrase: String, + pub include_threads: Option, + pub include_documents: Option, + pub include_settings: Option, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct ImportOptions { + pub passphrase: String, + pub data_base64: String, + pub mode: Option, +} + +#[derive(Debug, Clone, Serialize)] +pub struct ImportResult { + pub threads_imported: usize, + pub documents_imported: usize, + pub settings_imported: bool, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +struct PlainPack { + product: String, + format_version: u32, + exported_at: String, + threads: Vec, + documents: Vec, + settings: Option, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +struct EncryptedEnvelope { + format: String, + version: u32, + cipher: String, + kdf: String, + iterations: u32, + salt_base64: String, + nonce_base64: String, + ciphertext_base64: String, +} + +pub fn export_pack(root: &Path, options: &ExportOptions) -> Result> { + validate_passphrase(&options.passphrase)?; + let plain = PlainPack { + product: "JackAILocal".to_string(), + format_version: VERSION, + exported_at: Utc::now().to_rfc3339(), + threads: if options.include_threads.unwrap_or(true) { + storage::read_all_threads(root)? + } else { + Vec::new() + }, + documents: if options.include_documents.unwrap_or(true) { + storage::list_documents_with_content(root)? + } else { + Vec::new() + }, + settings: if options.include_settings.unwrap_or(false) { + Some(sanitized_settings(storage::read_settings(root))) + } else { + None + }, + }; + let plaintext = serde_json::to_vec(&plain)?; + + let mut salt = [0_u8; 16]; + let mut nonce = [0_u8; 12]; + OsRng.fill_bytes(&mut salt); + OsRng.fill_bytes(&mut nonce); + let key = derive_key(options.passphrase.as_bytes(), &salt, PBKDF2_ITERATIONS); + let cipher = Aes256Gcm::new_from_slice(&key).context("cannot initialize AES-256-GCM")?; + let ciphertext = cipher + .encrypt( + Nonce::from_slice(&nonce), + Payload { + msg: &plaintext, + aad: AAD, + }, + ) + .map_err(|_| anyhow::anyhow!("pack encryption failed"))?; + + let envelope = EncryptedEnvelope { + format: FORMAT.to_string(), + version: VERSION, + cipher: "AES-256-GCM".to_string(), + kdf: "PBKDF2-HMAC-SHA256".to_string(), + iterations: PBKDF2_ITERATIONS, + salt_base64: B64.encode(salt), + nonce_base64: B64.encode(nonce), + ciphertext_base64: B64.encode(ciphertext), + }; + Ok(serde_json::to_vec_pretty(&envelope)?) +} + +pub fn import_pack(root: &Path, options: &ImportOptions) -> Result { + validate_passphrase(&options.passphrase)?; + let envelope_bytes = B64 + .decode(options.data_base64.trim()) + .context("pack data is not valid base64")?; + let envelope: EncryptedEnvelope = + serde_json::from_slice(&envelope_bytes).context("pack envelope is not valid JSON")?; + if envelope.format != FORMAT || envelope.version != VERSION { + bail!("unsupported JackAILocal pack format or version"); + } + if envelope.cipher != "AES-256-GCM" || envelope.kdf != "PBKDF2-HMAC-SHA256" { + bail!("unsupported pack cryptography"); + } + if envelope.iterations < 100_000 { + bail!("pack key derivation settings are too weak"); + } + let salt = B64 + .decode(&envelope.salt_base64) + .context("invalid pack salt")?; + let nonce = B64 + .decode(&envelope.nonce_base64) + .context("invalid pack nonce")?; + let ciphertext = B64 + .decode(&envelope.ciphertext_base64) + .context("invalid pack ciphertext")?; + if salt.len() != 16 || nonce.len() != 12 { + bail!("invalid pack salt or nonce length"); + } + let key = derive_key(options.passphrase.as_bytes(), &salt, envelope.iterations); + let cipher = Aes256Gcm::new_from_slice(&key).context("cannot initialize AES-256-GCM")?; + let plaintext = cipher + .decrypt( + Nonce::from_slice(&nonce), + Payload { + msg: &ciphertext, + aad: AAD, + }, + ) + .map_err(|_| anyhow::anyhow!("wrong passphrase or corrupted pack"))?; + let pack: PlainPack = + serde_json::from_slice(&plaintext).context("decrypted pack is invalid")?; + if pack.product != "JackAILocal" || pack.format_version != VERSION { + bail!("decrypted data is not a compatible JackAILocal pack"); + } + + let replace = options.mode.as_deref() == Some("replace"); + let threads_imported = storage::import_threads(root, &pack.threads, replace)?; + let mut documents_imported = 0; + for document in pack.documents { + storage::save_document(root, &document.name, &document.content)?; + documents_imported += 1; + } + let settings_imported = if let Some(settings) = pack.settings { + storage::write_settings(root, &sanitized_settings(settings))?; + true + } else { + false + }; + Ok(ImportResult { + threads_imported, + documents_imported, + settings_imported, + }) +} + +fn validate_passphrase(passphrase: &str) -> Result<()> { + if passphrase.chars().count() < 8 { + bail!("passphrase must contain at least 8 characters"); + } + Ok(()) +} + +fn derive_key(passphrase: &[u8], salt: &[u8], iterations: u32) -> [u8; 32] { + let mut key = [0_u8; 32]; + pbkdf2_hmac::(passphrase, salt, iterations, &mut key); + key +} + +fn sanitized_settings(mut settings: Value) -> Value { + if !settings.is_object() { + settings = json!({}); + } + if let Some(object) = settings.as_object_mut() { + object.insert("phone_access_enabled".to_string(), json!(false)); + object.remove("phone_access_token"); + } + settings +} diff --git a/src/license.rs b/src/license.rs new file mode 100644 index 0000000000000000000000000000000000000000..f9c45b7c76c84a625a38a5e15b95793187fd2030 --- /dev/null +++ b/src/license.rs @@ -0,0 +1,345 @@ +use anyhow::{Context, Result}; +use base64::{engine::general_purpose::STANDARD as B64, Engine as _}; +use chrono::{DateTime, Utc}; +use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; +use rand::rngs::OsRng; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use std::path::{Path, PathBuf}; + +pub const LICENSE_RELATIVE_PATH: &str = "license/license.json"; +pub const PUBLIC_KEY_RELATIVE_PATH: &str = "config/license-public-key.txt"; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LicensePayload { + pub license_id: String, + pub product: String, + pub edition: String, + pub customer_name: String, + #[serde(default)] + pub customer_email: Option, + #[serde(default)] + pub customer_company: Option, + pub issued_at: DateTime, + #[serde(default)] + pub expires_at: Option>, + #[serde(default)] + pub max_devices: Option, + #[serde(default)] + pub features: Vec, + #[serde(default)] + pub notes: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +struct LicenseFile { + payload_b64: String, + signature_b64: String, +} + +#[derive(Debug, Clone)] +pub struct LicenseStatus { + pub state: &'static str, + pub reason: Option, + pub payload: Option, +} + +impl LicenseStatus { + pub fn to_json(&self) -> Value { + json!({ + "state": self.state, + "licensed": self.state == "licensed", + "reason": self.reason, + "license": self.payload.as_ref().map(|p| json!({ + "license_id": p.license_id, + "product": p.product, + "edition": p.edition, + "customer_name": p.customer_name, + "customer_company": p.customer_company, + "issued_at": p.issued_at.to_rfc3339(), + "expires_at": p.expires_at.map(|d| d.to_rfc3339()), + "features": p.features, + })), + }) + } +} + +pub fn status(root: &Path) -> LicenseStatus { + let verifying_key = match load_public_key(root) { + Ok(Some(key)) => key, + Ok(None) => { + return LicenseStatus { + state: "no_public_key", + reason: Some(format!("missing {PUBLIC_KEY_RELATIVE_PATH}")), + payload: None, + } + } + Err(err) => { + return LicenseStatus { + state: "no_public_key", + reason: Some(err.to_string()), + payload: None, + } + } + }; + let license_path = root.join(LICENSE_RELATIVE_PATH); + if !license_path.exists() { + return LicenseStatus { + state: "unlicensed", + reason: Some("no license file installed".to_string()), + payload: None, + }; + } + let text = match std::fs::read_to_string(&license_path) { + Ok(text) => text, + Err(err) => { + return LicenseStatus { + state: "invalid", + reason: Some(format!("cannot read license file: {err}")), + payload: None, + } + } + }; + match verify_text(&text, &verifying_key) { + Ok(payload) => evaluate_payload(payload), + Err(err) => LicenseStatus { + state: "invalid", + reason: Some(err.to_string()), + payload: None, + }, + } +} + +pub fn install(root: &Path, content: &str) -> Result { + let verifying_key = load_public_key(root)? + .with_context(|| format!("missing {PUBLIC_KEY_RELATIVE_PATH}"))?; + let payload = verify_text(content, &verifying_key)?; + let status = evaluate_payload(payload); + if status.state == "invalid" { + anyhow::bail!( + "license rejected: {}", + status.reason.as_deref().unwrap_or("unknown reason") + ); + } + let license_path = root.join(LICENSE_RELATIVE_PATH); + if let Some(parent) = license_path.parent() { + std::fs::create_dir_all(parent)?; + } + std::fs::write(&license_path, content)?; + Ok(status) +} + +pub fn keygen(out_dir: &Path) -> Result<(PathBuf, PathBuf)> { + std::fs::create_dir_all(out_dir)?; + let signing_key = SigningKey::generate(&mut OsRng); + let private_path = out_dir.join("license-signing-key.txt"); + let public_path = out_dir.join("license-public-key.txt"); + std::fs::write(&private_path, B64.encode(signing_key.to_bytes()))?; + std::fs::write( + &public_path, + B64.encode(signing_key.verifying_key().to_bytes()), + )?; + Ok((private_path, public_path)) +} + +#[allow(clippy::too_many_arguments)] +pub fn issue( + signing_key_path: &Path, + out_path: &Path, + product: &str, + edition: &str, + customer_name: &str, + customer_email: Option, + customer_company: Option, + expires_at: Option>, + max_devices: Option, + features: Vec, + notes: Option, +) -> Result { + let key_text = std::fs::read_to_string(signing_key_path) + .with_context(|| format!("cannot read signing key {}", signing_key_path.display()))?; + let key_bytes: [u8; 32] = B64 + .decode(key_text.trim()) + .context("signing key is not valid base64")? + .try_into() + .map_err(|_| anyhow::anyhow!("signing key must be 32 bytes"))?; + let signing_key = SigningKey::from_bytes(&key_bytes); + + let payload = LicensePayload { + license_id: uuid::Uuid::new_v4().to_string(), + product: product.to_string(), + edition: edition.to_string(), + customer_name: customer_name.to_string(), + customer_email, + customer_company, + issued_at: Utc::now(), + expires_at, + max_devices, + features, + notes, + }; + let payload_bytes = serde_json::to_vec(&payload)?; + let signature = signing_key.sign(&payload_bytes); + let file = LicenseFile { + payload_b64: B64.encode(&payload_bytes), + signature_b64: B64.encode(signature.to_bytes()), + }; + if let Some(parent) = out_path.parent() { + if !parent.as_os_str().is_empty() { + std::fs::create_dir_all(parent)?; + } + } + std::fs::write(out_path, serde_json::to_string_pretty(&file)?)?; + Ok(payload) +} + +pub fn verify_path(license_path: &Path, public_key_path: &Path) -> Result { + let key_text = std::fs::read_to_string(public_key_path) + .with_context(|| format!("cannot read public key {}", public_key_path.display()))?; + let verifying_key = parse_public_key(&key_text)?; + let text = std::fs::read_to_string(license_path) + .with_context(|| format!("cannot read license {}", license_path.display()))?; + let payload = verify_text(&text, &verifying_key)?; + Ok(evaluate_payload(payload)) +} + +fn evaluate_payload(payload: LicensePayload) -> LicenseStatus { + if let Some(expires_at) = payload.expires_at { + if Utc::now() > expires_at { + return LicenseStatus { + state: "expired", + reason: Some(format!("license expired {}", expires_at.to_rfc3339())), + payload: Some(payload), + }; + } + } + LicenseStatus { + state: "licensed", + reason: None, + payload: Some(payload), + } +} + +fn verify_text(text: &str, verifying_key: &VerifyingKey) -> Result { + let file: LicenseFile = + serde_json::from_str(text).context("license file is not valid JSON")?; + let payload_bytes = B64 + .decode(file.payload_b64.trim()) + .context("license payload is not valid base64")?; + let signature_bytes: [u8; 64] = B64 + .decode(file.signature_b64.trim()) + .context("license signature is not valid base64")? + .try_into() + .map_err(|_| anyhow::anyhow!("license signature must be 64 bytes"))?; + let signature = Signature::from_bytes(&signature_bytes); + verifying_key + .verify(&payload_bytes, &signature) + .map_err(|_| anyhow::anyhow!("license signature does not match"))?; + serde_json::from_slice(&payload_bytes).context("license payload is not valid JSON") +} + +fn load_public_key(root: &Path) -> Result> { + let path = root.join(PUBLIC_KEY_RELATIVE_PATH); + if !path.exists() { + return Ok(None); + } + let text = std::fs::read_to_string(&path) + .with_context(|| format!("cannot read public key {}", path.display()))?; + Ok(Some(parse_public_key(&text)?)) +} + +fn parse_public_key(text: &str) -> Result { + let bytes: [u8; 32] = B64 + .decode(text.trim()) + .context("public key is not valid base64")? + .try_into() + .map_err(|_| anyhow::anyhow!("public key must be 32 bytes"))?; + VerifyingKey::from_bytes(&bytes).context("public key is not a valid Ed25519 key") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn issued_license_verifies_and_round_trips() { + let dir = std::env::temp_dir().join(format!("jkl-license-test-{}", std::process::id())); + std::fs::create_dir_all(&dir).unwrap(); + let (private_path, public_path) = keygen(&dir).unwrap(); + let license_path = dir.join("license.json"); + issue( + &private_path, + &license_path, + "JackAILocal", + "pro", + "Test Customer", + Some("test@example.com".to_string()), + None, + None, + Some(3), + vec!["chat".to_string()], + None, + ) + .unwrap(); + let status = verify_path(&license_path, &public_path).unwrap(); + assert_eq!(status.state, "licensed"); + assert_eq!(status.payload.unwrap().customer_name, "Test Customer"); + std::fs::remove_dir_all(&dir).ok(); + } + + #[test] + fn tampered_license_is_rejected() { + let dir = std::env::temp_dir().join(format!("jkl-license-tamper-{}", std::process::id())); + std::fs::create_dir_all(&dir).unwrap(); + let (private_path, public_path) = keygen(&dir).unwrap(); + let license_path = dir.join("license.json"); + issue( + &private_path, + &license_path, + "JackAILocal", + "personal", + "Honest Customer", + None, + None, + None, + None, + vec![], + None, + ) + .unwrap(); + let text = std::fs::read_to_string(&license_path).unwrap(); + let mut file: LicenseFile = serde_json::from_str(&text).unwrap(); + let mut payload: LicensePayload = + serde_json::from_slice(&B64.decode(&file.payload_b64).unwrap()).unwrap(); + payload.edition = "whitelabel".to_string(); + file.payload_b64 = B64.encode(serde_json::to_vec(&payload).unwrap()); + std::fs::write(&license_path, serde_json::to_string(&file).unwrap()).unwrap(); + assert!(verify_path(&license_path, &public_path).is_err()); + std::fs::remove_dir_all(&dir).ok(); + } + + #[test] + fn expired_license_reports_expired() { + let dir = std::env::temp_dir().join(format!("jkl-license-exp-{}", std::process::id())); + std::fs::create_dir_all(&dir).unwrap(); + let (private_path, public_path) = keygen(&dir).unwrap(); + let license_path = dir.join("license.json"); + issue( + &private_path, + &license_path, + "JackAILocal", + "pro", + "Late Customer", + None, + None, + Some(Utc::now() - chrono::Duration::days(1)), + None, + vec![], + None, + ) + .unwrap(); + let status = verify_path(&license_path, &public_path).unwrap(); + assert_eq!(status.state, "expired"); + std::fs::remove_dir_all(&dir).ok(); + } +} diff --git a/src/local_ai.rs b/src/local_ai.rs new file mode 100644 index 0000000000000000000000000000000000000000..4936f350d35dd6a3070f24fea862b3c0649e7f67 --- /dev/null +++ b/src/local_ai.rs @@ -0,0 +1,696 @@ +use anyhow::{bail, Context, Result}; +use base64::{engine::general_purpose::STANDARD as B64, Engine as _}; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use std::{ + env, fs, + io::Write, + path::{Path, PathBuf}, + process::{Command, Stdio}, +}; +use uuid::Uuid; + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct AiMessage { + pub role: String, + pub content: Value, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct ChatInput { + pub model: Option, + pub messages: Vec, + pub temperature: Option, + pub max_tokens: Option, + pub think: Option, +} + +#[derive(Debug, Deserialize)] +pub struct VisionInput { + pub model: Option, + pub prompt: Option, + pub image_base64: String, +} + +#[derive(Debug, Deserialize)] +pub struct TranscriptionInput { + pub audio_base64: String, + pub format: Option, + pub language: Option, +} + +#[derive(Debug, Deserialize)] +pub struct SpeechInput { + pub text: String, +} + +#[derive(Debug, Clone)] +pub struct BackendConfig { + pub ollama_enabled: bool, + pub ollama_url: String, + pub llama_cpp_enabled: bool, + pub llama_cpp_url: String, +} + +impl BackendConfig { + pub fn new( + ollama_enabled: bool, + ollama_url: Option, + llama_cpp_enabled: bool, + llama_cpp_url: Option, + ) -> Self { + Self { + ollama_enabled, + ollama_url: normalize_url(ollama_url.unwrap_or_else(default_ollama_url)), + llama_cpp_enabled, + llama_cpp_url: normalize_url(llama_cpp_url.unwrap_or_else(default_llama_cpp_url)), + } + } +} + +pub async fn chat( + client: &reqwest::Client, + models: &Value, + root: &Path, + backend: &BackendConfig, + input: ChatInput, +) -> Result { + let hardware = hardware(root); + let installed = installed_ollama_models(client, backend).await; + let available_catalog = catalog_with_availability(models, &installed, root); + let selected = select_model( + &available_catalog, + input.model.as_deref(), + &hardware, + "chat", + ) + .context("no compatible chat model is configured")?; + let profile_id = selected + .get("id") + .and_then(|v| v.as_str()) + .unwrap_or("auto"); + let temperature = input.temperature.unwrap_or(0.4); + + if backend.ollama_enabled { + if let Some(ollama_model) = selected.get("ollama").and_then(|v| v.as_str()) { + let mut options = json!({ + "temperature": temperature, + "num_predict": input.max_tokens.unwrap_or(512), + "num_ctx": effective_num_ctx(selected, &hardware) + }); + merge_model_options(&mut options, selected); + let mut body = json!({ + "model": ollama_model, + "messages": input.messages, + "stream": false, + "keep_alive": model_keep_alive(selected), + "options": options + }); + if let Some(think) = input.think { + body["think"] = json!(think); + } + if let Ok(response) = client + .post(format!("{}/api/chat", backend.ollama_url)) + .json(&body) + .send() + .await + { + if response.status().is_success() { + let payload: Value = + response.json().await.context("invalid Ollama response")?; + let content = payload + .pointer("/message/content") + .and_then(|v| v.as_str()) + .unwrap_or(""); + return Ok(openai_response( + content, + profile_id, + "ollama", + payload.get("eval_count").cloned(), + )); + } + } + } + } + + if !backend.llama_cpp_enabled { + bail!("Ollama is unavailable and llama.cpp is disabled by runtime configuration"); + } + + let llama_body = json!({ + "messages": input.messages, + "stream": false, + "temperature": temperature, + "max_tokens": input.max_tokens.unwrap_or(512) + }); + let response = client + .post(format!("{}/v1/chat/completions", backend.llama_cpp_url)) + .json(&llama_body) + .send() + .await + .context("Ollama is unavailable and llama.cpp is not reachable")?; + if !response.status().is_success() { + bail!("llama.cpp returned HTTP {}", response.status()); + } + let payload: Value = response + .json() + .await + .context("invalid llama.cpp response")?; + Ok(payload) +} + +pub async fn analyze_image( + client: &reqwest::Client, + models: &Value, + root: &Path, + backend: &BackendConfig, + input: VisionInput, +) -> Result { + if !backend.ollama_enabled { + bail!("Ollama vision backend is disabled by runtime configuration"); + } + let hardware = hardware(root); + let installed = installed_ollama_models(client, backend).await; + let available_catalog = catalog_with_availability(models, &installed, root); + let selected = select_model( + &available_catalog, + input.model.as_deref(), + &hardware, + "vision", + ) + .context("no compatible vision model is configured")?; + let model_id = selected + .get("id") + .and_then(|v| v.as_str()) + .unwrap_or("vision"); + let ollama_model = selected + .get("ollama") + .and_then(|v| v.as_str()) + .context("the selected vision model has no Ollama model reference")?; + let image = strip_data_url(&input.image_base64); + B64.decode(image).context("image is not valid base64")?; + let prompt = input.prompt.unwrap_or_else(|| { + "Describe this image carefully and extract any readable text. State uncertainty explicitly.".to_string() + }); + let mut options = json!({ "num_ctx": effective_num_ctx(selected, &hardware) }); + merge_model_options(&mut options, selected); + let body = json!({ + "model": ollama_model, + "messages": [{ + "role": "user", + "content": prompt, + "images": [image] + }], + "stream": false, + "keep_alive": model_keep_alive(selected), + "options": options + }); + let response = client + .post(format!("{}/api/chat", backend.ollama_url)) + .json(&body) + .send() + .await + .context("Ollama is not reachable for vision analysis")?; + if !response.status().is_success() { + let status = response.status(); + let detail = response.text().await.unwrap_or_default(); + bail!( + "Ollama vision request returned HTTP {status}: {}", + detail.trim() + ); + } + let payload: Value = response + .json() + .await + .context("invalid Ollama vision response")?; + let content = payload + .pointer("/message/content") + .and_then(|v| v.as_str()) + .unwrap_or(""); + Ok(json!({ + "ok": true, + "model": model_id, + "backend_model": ollama_model, + "content": content, + "backend": "ollama" + })) +} + +pub async fn backend_status(client: &reqwest::Client, backend: &BackendConfig) -> Value { + let ollama = if backend.ollama_enabled { + match client + .get(format!("{}/api/tags", backend.ollama_url)) + .send() + .await + { + Ok(response) if response.status().is_success() => { + json!({"available": true, "enabled": true, "url": backend.ollama_url}) + } + Ok(response) => { + json!({"available": false, "enabled": true, "url": backend.ollama_url, "error": format!("HTTP {}", response.status())}) + } + Err(error) => { + json!({"available": false, "enabled": true, "url": backend.ollama_url, "error": error.to_string()}) + } + } + } else { + json!({"available": false, "enabled": false, "url": backend.ollama_url}) + }; + let llamacpp = if backend.llama_cpp_enabled { + match client + .get(format!("{}/v1/models", backend.llama_cpp_url)) + .send() + .await + { + Ok(response) if response.status().is_success() => { + json!({"available": true, "enabled": true, "url": backend.llama_cpp_url}) + } + Ok(response) => { + json!({"available": false, "enabled": true, "url": backend.llama_cpp_url, "error": format!("HTTP {}", response.status())}) + } + Err(error) => { + json!({"available": false, "enabled": true, "url": backend.llama_cpp_url, "error": error.to_string()}) + } + } + } else { + json!({"available": false, "enabled": false, "url": backend.llama_cpp_url}) + }; + json!({"ollama": ollama, "llama_cpp": llamacpp}) +} + +pub async fn installed_ollama_models( + client: &reqwest::Client, + backend: &BackendConfig, +) -> Vec { + if !backend.ollama_enabled { + return Vec::new(); + } + let response = match client + .get(format!("{}/api/tags", backend.ollama_url)) + .send() + .await + { + Ok(value) if value.status().is_success() => value, + _ => return Vec::new(), + }; + let payload: Value = match response.json().await { + Ok(value) => value, + Err(_) => return Vec::new(), + }; + payload + .get("models") + .and_then(|v| v.as_array()) + .into_iter() + .flatten() + .filter_map(|model| { + model + .get("name") + .and_then(|v| v.as_str()) + .map(str::to_string) + }) + .collect() +} + +pub fn catalog_with_availability(models: &Value, installed: &[String], root: &Path) -> Value { + let mut output = models.clone(); + if let Some(entries) = output.get_mut("models").and_then(|v| v.as_array_mut()) { + for model in entries { + let ollama = model.get("ollama").and_then(|v| v.as_str()); + let installed_ollama = ollama + .map(|name| model_name_matches(installed, name)) + .unwrap_or(false); + let gguf = model + .get("gguf") + .and_then(|v| v.as_str()) + .map(|path| root.join(path).is_file()) + .unwrap_or(false); + model["available"] = json!(installed_ollama || gguf); + model["installed_ollama"] = json!(installed_ollama); + model["installed_gguf"] = json!(gguf); + } + } + output +} + +pub fn voice_status(root: &Path) -> Value { + let whisper_binary = find_first(root, whisper_binary_candidates()); + let whisper_model = find_by_extension(&root.join("models/whisper"), &["bin"]); + let piper_binary = find_first(root, piper_binary_candidates()); + let piper_model = find_by_extension(&root.join("models/piper"), &["onnx"]); + json!({ + "stt": { + "available": whisper_binary.is_some() && whisper_model.is_some(), + "engine": "whisper.cpp", + "binary": display_option(&whisper_binary), + "model": display_option(&whisper_model) + }, + "tts": { + "available": piper_binary.is_some() && piper_model.is_some(), + "engine": "piper", + "binary": display_option(&piper_binary), + "model": display_option(&piper_model) + } + }) +} + +pub fn transcribe(root: &Path, input: &TranscriptionInput) -> Result { + let whisper_binary = find_first(root, whisper_binary_candidates()) + .context("whisper.cpp binary is not installed")?; + let whisper_model = find_by_extension(&root.join("models/whisper"), &["bin"]) + .context("whisper.cpp model is not installed")?; + let audio = B64 + .decode(strip_data_url(&input.audio_base64)) + .context("audio is not valid base64")?; + let extension = sanitize_audio_extension(input.format.as_deref().unwrap_or("wav")); + let id = Uuid::new_v4().to_string(); + let input_path = root + .join("workspace/voice") + .join(format!("{id}.{extension}")); + let output_prefix = root + .join("workspace/voice") + .join(format!("{id}-transcript")); + fs::write(&input_path, audio).context("cannot write temporary audio")?; + + let mut command = Command::new(&whisper_binary); + command + .arg("-m") + .arg(&whisper_model) + .arg("-f") + .arg(&input_path) + .arg("-otxt") + .arg("-of") + .arg(&output_prefix) + .arg("-nt"); + if let Some(language) = input.language.as_deref() { + if !language.trim().is_empty() { + command.arg("-l").arg(language.trim()); + } + } + let output = command.output().context("cannot run whisper.cpp")?; + let _ = fs::remove_file(&input_path); + if !output.status.success() { + bail!( + "whisper.cpp failed: {}", + String::from_utf8_lossy(&output.stderr).trim() + ); + } + let transcript_path = output_prefix.with_extension("txt"); + let text = + fs::read_to_string(&transcript_path).context("whisper.cpp did not produce a transcript")?; + let _ = fs::remove_file(transcript_path); + Ok(json!({"ok": true, "text": text.trim(), "engine": "whisper.cpp"})) +} + +pub fn synthesize(root: &Path, input: &SpeechInput) -> Result { + if input.text.trim().is_empty() { + bail!("text cannot be empty"); + } + let piper_binary = + find_first(root, piper_binary_candidates()).context("Piper binary is not installed")?; + let piper_model = find_by_extension(&root.join("models/piper"), &["onnx"]) + .context("Piper voice model is not installed")?; + let output_path = root + .join("workspace/voice") + .join(format!("{}.wav", Uuid::new_v4())); + let mut child = Command::new(&piper_binary) + .arg("--model") + .arg(&piper_model) + .arg("--output_file") + .arg(&output_path) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("cannot run Piper")?; + child + .stdin + .as_mut() + .context("cannot open Piper stdin")? + .write_all(input.text.as_bytes()) + .context("cannot send text to Piper")?; + let output = child.wait_with_output().context("cannot wait for Piper")?; + if !output.status.success() { + bail!( + "Piper failed: {}", + String::from_utf8_lossy(&output.stderr).trim() + ); + } + let audio = fs::read(&output_path).context("Piper did not produce audio")?; + let _ = fs::remove_file(output_path); + Ok(json!({ + "ok": true, + "engine": "piper", + "mime_type": "audio/wav", + "audio_base64": B64.encode(audio) + })) +} + +pub fn select_model<'a>( + models: &'a Value, + requested: Option<&str>, + hardware: &Value, + feature: &str, +) -> Option<&'a Value> { + let entries = models.get("models")?.as_array()?; + if let Some(requested) = requested.filter(|value| *value != "auto") { + if let Some(model) = entries.iter().find(|model| { + model.get("id").and_then(|v| v.as_str()) == Some(requested) + || model.get("ollama").and_then(|v| v.as_str()) == Some(requested) + }) { + return if has_feature(model, feature) + && model + .get("available") + .and_then(|v| v.as_bool()) + .unwrap_or(false) + { + Some(model) + } else { + None + }; + } + if let Some(default_id) = models + .get("profiles")? + .as_array()? + .iter() + .find(|profile| profile.get("id").and_then(|v| v.as_str()) == Some(requested)) + .and_then(|profile| profile.get("recommended_default").and_then(|v| v.as_str())) + { + if let Some(model) = entries + .iter() + .find(|model| model.get("id").and_then(|v| v.as_str()) == Some(default_id)) + { + return if has_feature(model, feature) + && model + .get("available") + .and_then(|v| v.as_bool()) + .unwrap_or(false) + { + Some(model) + } else { + None + }; + } + } + return None; + } + let ram = hardware + .get("ram_gb") + .and_then(|v| v.as_f64()) + .unwrap_or(8.0); + let vram = hardware + .get("vram_gb") + .and_then(|v| v.as_f64()) + .unwrap_or(0.0); + entries + .iter() + .filter(|model| has_feature(model, feature)) + .filter(|model| { + model + .get("available") + .and_then(|v| v.as_bool()) + .unwrap_or(false) + }) + .filter(|model| { + // 3% slack: physical "32 GB" machines report ~31.7 GB usable. + ram >= model + .get("min_ram_gb") + .and_then(|v| v.as_f64()) + .unwrap_or(0.0) + * 0.97 + }) + .filter(|model| { + vram >= model + .get("min_vram_gb") + .and_then(|v| v.as_f64()) + .unwrap_or(0.0) + * 0.97 + }) + .last() +} + +/// Context window for the request: the catalog's `default_context`, scaled to +/// the customer hardware so weak machines never thrash and strong machines +/// get the room they paid for. A per-model `options.num_ctx` overrides this. +pub fn effective_num_ctx(model: &Value, hardware: &Value) -> u64 { + let base = model + .get("default_context") + .and_then(|v| v.as_u64()) + .unwrap_or(4096); + let ram = hardware.get("ram_gb").and_then(|v| v.as_f64()).unwrap_or(8.0); + let vram = hardware.get("vram_gb").and_then(|v| v.as_f64()).unwrap_or(0.0); + if vram >= 16.0 || ram >= 32.0 { + base.max(8192) + } else if ram < 10.0 && vram < 4.0 { + base.min(4096) + } else { + base + } +} + +/// How long Ollama keeps the model loaded after a request. Reloading a model +/// between messages is the single biggest perceived slowdown on USB targets. +pub fn model_keep_alive(model: &Value) -> String { + model + .get("keep_alive") + .and_then(|v| v.as_str()) + .unwrap_or("30m") + .to_string() +} + +/// Merges the catalog model's optional `options` object (verbatim Ollama +/// options such as num_gpu, num_thread, top_p) over the computed defaults. +pub fn merge_model_options(options: &mut Value, model: &Value) { + if let Some(extra) = model.get("options").and_then(|v| v.as_object()) { + if let Some(target) = options.as_object_mut() { + for (key, value) in extra { + target.insert(key.clone(), value.clone()); + } + } + } +} + +pub fn hardware(root: &Path) -> Value { + let path = root.join("diagnostics/hardware.json"); + fs::read_to_string(path) + .ok() + .and_then(|text| serde_json::from_str(&text).ok()) + .unwrap_or_else(|| json!({"ram_gb": 8, "vram_gb": 0})) +} + +fn has_feature(model: &Value, feature: &str) -> bool { + model + .get("features") + .and_then(|v| v.as_array()) + .into_iter() + .flatten() + .any(|entry| entry.as_str() == Some(feature)) +} + +fn openai_response(content: &str, model: &str, backend: &str, eval_count: Option) -> Value { + json!({ + "id": format!("jackailocal-{}", Uuid::new_v4()), + "object": "chat.completion", + "model": model, + "backend": backend, + "choices": [{"index": 0, "message": {"role": "assistant", "content": content}, "finish_reason": "stop"}], + "usage": {"completion_tokens": eval_count} + }) +} + +fn model_name_matches(installed: &[String], expected: &str) -> bool { + installed.iter().any(|name| { + name == expected || name.trim_end_matches(":latest") == expected.trim_end_matches(":latest") + }) +} + +fn default_ollama_url() -> String { + env::var("OLLAMA_HOST").unwrap_or_else(|_| "http://127.0.0.1:11434".to_string()) +} + +fn default_llama_cpp_url() -> String { + env::var("LLAMA_CPP_URL").unwrap_or_else(|_| "http://127.0.0.1:8080".to_string()) +} + +fn normalize_url(value: String) -> String { + let value = value.trim().trim_end_matches('/').to_string(); + if value.starts_with("http://") || value.starts_with("https://") { + value + } else { + format!("http://{value}") + } +} + +fn strip_data_url(value: &str) -> &str { + value.split_once(',').map(|(_, data)| data).unwrap_or(value) +} + +fn sanitize_audio_extension(value: &str) -> String { + let extension = value.trim().trim_start_matches('.').to_ascii_lowercase(); + if matches!(extension.as_str(), "wav" | "mp3" | "flac" | "ogg") { + extension + } else { + "wav".to_string() + } +} + +fn find_first(root: &Path, candidates: &[&str]) -> Option { + candidates + .iter() + .map(|candidate| root.join(candidate)) + .find(|path| path.is_file()) +} + +fn find_by_extension(dir: &Path, extensions: &[&str]) -> Option { + fs::read_dir(dir) + .ok()? + .flatten() + .map(|entry| entry.path()) + .find(|path| { + path.is_file() + && path + .extension() + .and_then(|v| v.to_str()) + .map(|v| extensions.contains(&v)) + .unwrap_or(false) + }) +} + +fn display_option(path: &Option) -> Option { + path.as_ref() + .map(|value| value.to_string_lossy().to_string()) +} + +#[cfg(target_os = "windows")] +fn whisper_binary_candidates() -> &'static [&'static str] { + &[ + "backends/whisper.cpp/windows/whisper-cli.exe", + "backends/whisper.cpp/windows/main.exe", + ] +} +#[cfg(target_os = "macos")] +fn whisper_binary_candidates() -> &'static [&'static str] { + &[ + "backends/whisper.cpp/macos/whisper-cli", + "backends/whisper.cpp/macos/main", + ] +} +#[cfg(all(unix, not(target_os = "macos")))] +fn whisper_binary_candidates() -> &'static [&'static str] { + &[ + "backends/whisper.cpp/linux/whisper-cli", + "backends/whisper.cpp/linux/main", + ] +} + +#[cfg(target_os = "windows")] +fn piper_binary_candidates() -> &'static [&'static str] { + &["backends/piper/windows/piper.exe"] +} +#[cfg(target_os = "macos")] +fn piper_binary_candidates() -> &'static [&'static str] { + &["backends/piper/macos/piper"] +} +#[cfg(all(unix, not(target_os = "macos")))] +fn piper_binary_candidates() -> &'static [&'static str] { + &["backends/piper/linux/piper"] +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..e21ee5adf568b199e69b08627349968dd2c549dd --- /dev/null +++ b/src/main.rs @@ -0,0 +1,1289 @@ +mod crypto_pack; +mod license; +mod local_ai; +mod phone; +mod storage; + +use anyhow::Result; +use axum::{ + extract::{ConnectInfo, Path as AxumPath, Query, Request, State}, + http::StatusCode, + middleware::{self, Next}, + response::{IntoResponse, Response}, + routing::{get, post}, + Json, Router, +}; +use base64::{engine::general_purpose::STANDARD as B64, Engine as _}; +use clap::{Parser, Subcommand}; +use serde::Deserialize; +use serde_json::{json, Value}; +use std::{ + collections::HashMap, + net::SocketAddr, + path::{Path, PathBuf}, + process::{Command as ProcessCommand, Stdio}, + sync::Arc, + time::{Duration, Instant}, +}; +use tower_http::{cors::CorsLayer, services::ServeDir}; + +#[derive(Parser)] +#[command(name = "jackailocald")] +struct Cli { + #[command(subcommand)] + command: Command, +} + +#[derive(Subcommand)] +enum Command { + Serve { + #[arg(long, default_value = "config/jackailocal.windows.toml")] + config: String, + }, + Hwscan, + License { + #[command(subcommand)] + action: LicenseCommand, + }, +} + +#[derive(Subcommand)] +enum LicenseCommand { + Keygen { + #[arg(long, default_value = "license-keys")] + out_dir: String, + }, + Issue { + #[arg(long)] + key: String, + #[arg(long, default_value = "license.json")] + out: String, + #[arg(long, default_value = "JackAILocal")] + product: String, + #[arg(long, default_value = "personal")] + edition: String, + #[arg(long)] + customer_name: String, + #[arg(long)] + customer_email: Option, + #[arg(long)] + customer_company: Option, + #[arg(long)] + expires: Option, + #[arg(long)] + max_devices: Option, + #[arg(long, value_delimiter = ',')] + features: Vec, + #[arg(long)] + notes: Option, + }, + Verify { + #[arg(long)] + file: String, + #[arg(long)] + pubkey: String, + }, +} + +#[derive(Clone)] +struct AppState { + root: PathBuf, + models: Value, + client: reqwest::Client, + started: Instant, + runtime_config: phone::RuntimeConfig, + backend_config: local_ai::BackendConfig, + bind: SocketAddr, + phone_token: Option, +} + +#[derive(Debug, Deserialize)] +struct SaveDocument { + name: String, + content: String, +} +#[derive(Debug, Deserialize)] +struct AddModelRequest { + id: String, + label: String, +} +#[derive(Debug, Deserialize)] +struct SettingsRequest { + mode: Option, + offline_lock: Option, + tools_lock: Option, + phone_access_enabled: Option, +} +#[derive(Debug, Deserialize)] +struct BenchmarkRequest { + model: Option, +} +#[derive(Debug, Deserialize)] +struct CreateThreadRequest { + title: Option, + model: Option, +} +#[derive(Debug, Deserialize)] +struct RenameThreadRequest { + title: String, +} +#[derive(Debug, Deserialize)] +struct ThreadMessageRequest { + content: String, + model: Option, +} +#[derive(Debug, Deserialize)] +struct PhoneAccessRequest { + enabled: bool, +} +#[derive(Debug, Deserialize)] +struct InstallLicenseRequest { + content: String, +} +#[derive(Debug, Deserialize)] +struct AgentRecommendRequest { + language: Option, + package_goal: Option, + hardware: Option, + default_model: Option, + candidate_models: Option, + content_packs: Option, + current_config: Option, + customer_notes: Option, + agent_model: Option, + max_params_b: Option, + agent_max_tokens: Option, + agent_timeout_seconds: Option, +} + +#[tokio::main] +async fn main() -> Result<()> { + tracing_subscriber::fmt().init(); + let cli = Cli::parse(); + match cli.command { + Command::Serve { config } => serve(config).await, + Command::Hwscan => { + println!("{}", hwscan_json()); + Ok(()) + } + Command::License { action } => run_license_command(action), + } +} + +fn run_license_command(action: LicenseCommand) -> Result<()> { + match action { + LicenseCommand::Keygen { out_dir } => { + let (private_path, public_path) = license::keygen(Path::new(&out_dir))?; + println!("private signing key : {}", private_path.display()); + println!("public verify key : {}", public_path.display()); + println!( + "Ship the PUBLIC key as {} inside every package. Keep the private key offline and never distribute it.", + license::PUBLIC_KEY_RELATIVE_PATH + ); + Ok(()) + } + LicenseCommand::Issue { + key, + out, + product, + edition, + customer_name, + customer_email, + customer_company, + expires, + max_devices, + features, + notes, + } => { + let expires_at = expires.map(|text| parse_expiry(&text)).transpose()?; + let payload = license::issue( + Path::new(&key), + Path::new(&out), + &product, + &edition, + &customer_name, + customer_email, + customer_company, + expires_at, + max_devices, + features, + notes, + )?; + println!("issued license {} -> {}", payload.license_id, out); + Ok(()) + } + LicenseCommand::Verify { file, pubkey } => { + let status = license::verify_path(Path::new(&file), Path::new(&pubkey))?; + println!("{}", serde_json::to_string_pretty(&status.to_json())?); + if status.state == "licensed" { + Ok(()) + } else { + anyhow::bail!("license state: {}", status.state) + } + } + } +} + +fn parse_expiry(text: &str) -> Result> { + if let Ok(date_time) = chrono::DateTime::parse_from_rfc3339(text) { + return Ok(date_time.with_timezone(&chrono::Utc)); + } + let date = chrono::NaiveDate::parse_from_str(text, "%Y-%m-%d") + .map_err(|_| anyhow::anyhow!("expiry must be RFC3339 or YYYY-MM-DD: {text}"))?; + Ok(chrono::DateTime::from_naive_utc_and_offset( + date.and_hms_opt(23, 59, 59).unwrap(), + chrono::Utc, + )) +} + +async fn serve(config: String) -> Result<()> { + let root = std::env::current_dir()?; + let models_path = root.join("config/model-catalog.json"); + let models: Value = read_json(&models_path).unwrap_or_else(|_| json!({"profiles":[]})); + let ui_dir = root.join("webui"); + storage::ensure_workspace(&root)?; + let runtime_config = phone::load_runtime_config(&root, &config)?; + let backend_config = local_ai::BackendConfig::new( + runtime_config.ollama_enabled, + runtime_config.ollama_url.clone(), + runtime_config.llama_cpp_enabled, + runtime_config.llama_cpp_url.clone(), + ); + let addr = phone::effective_bind(&root, &runtime_config); + let phone_token = if !addr.ip().is_loopback() { + Some(phone::ensure_phone_token(&root)?) + } else { + phone::configured_token(&root) + }; + let state = Arc::new(AppState { + root: root.clone(), + models, + client: reqwest::Client::new(), + started: Instant::now(), + runtime_config, + backend_config, + bind: addr, + phone_token, + }); + let app = Router::new() + .route("/health", get(health)) + .route("/api/status", get(api_status)) + .route("/api/features", get(api_features)) + .route("/api/models", get(list_models)) + .route("/api/models/add", post(add_model)) + .route("/api/documents", get(list_documents).post(save_document)) + .route("/api/threads", get(list_threads).post(create_thread)) + .route( + "/api/threads/:id", + get(get_thread).patch(rename_thread).delete(delete_thread), + ) + .route("/api/threads/:id/messages", post(send_thread_message)) + .route("/api/packs/export", post(export_pack)) + .route("/api/packs/import", post(import_pack)) + .route("/api/vision/analyze", post(analyze_image)) + .route("/api/voice/status", get(voice_status)) + .route("/api/voice/transcribe", post(transcribe_audio)) + .route("/api/voice/synthesize", post(synthesize_speech)) + .route("/api/phone", get(phone_status).post(set_phone_access)) + .route("/api/runtime/restart", post(restart_runtime)) + .route("/api/content-packs", get(content_packs)) + .route("/api/field-manual", get(field_manual)) + .route("/api/settings", get(get_settings).post(save_settings)) + .route("/api/benchmark/run", post(run_benchmark)) + .route("/api/agent/recommend", post(agent_recommend)) + .route("/api/support/bundle", post(support_bundle)) + .route("/api/license", get(license_status).post(install_license)) + .route("/api/legal/eula", get(legal_eula)) + .route("/v1/models", get(list_models)) + .route("/v1/chat/completions", post(chat_completions)) + .nest_service( + "/", + ServeDir::new(ui_dir).append_index_html_on_directories(true), + ) + .layer(CorsLayer::permissive()) + .layer(middleware::from_fn_with_state( + state.clone(), + remote_api_auth, + )) + .with_state(state); + let listener = tokio::net::TcpListener::bind(addr).await?; + axum::serve( + listener, + app.into_make_service_with_connect_info::(), + ) + .await?; + Ok(()) +} + +async fn health(State(state): State>) -> impl IntoResponse { + Json( + json!({"product":"JackAILocal","status":"ok","offline":true,"bind":state.bind.to_string()}), + ) +} + +async fn api_status(State(state): State>) -> impl IntoResponse { + let hardware = read_json(&state.root.join("diagnostics/hardware.json")) + .unwrap_or_else(|_| serde_json::from_str(&hwscan_json()).unwrap_or(json!({}))); + let backends = local_ai::backend_status(&state.client, &state.backend_config).await; + let backend = if backends + .pointer("/ollama/available") + .and_then(|v| v.as_bool()) + == Some(true) + { + "ollama" + } else if backends + .pointer("/llama_cpp/available") + .and_then(|v| v.as_bool()) + == Some(true) + { + "llama.cpp" + } else { + "unavailable" + }; + let mut phone_access = phone::status(&state.root, &state.runtime_config, state.bind); + if let Some(object) = phone_access.as_object_mut() { + object.insert("qr_svg".to_string(), Value::Null); + } + Json(json!({ + "product":"JackAILocal", + "status":"ok", + "backend":backend, + "backends":backends, + "offline":true, + "bind":state.bind.to_string(), + "uptime_seconds":state.started.elapsed().as_secs(), + "hardware":hardware, + "phone_access":phone_access + })) +} + +async fn api_features(State(state): State>) -> impl IntoResponse { + let backends = local_ai::backend_status(&state.client, &state.backend_config).await; + let installed = local_ai::installed_ollama_models(&state.client, &state.backend_config).await; + let catalog = local_ai::catalog_with_availability(&state.models, &installed, &state.root); + let vision = catalog + .get("models") + .and_then(|v| v.as_array()) + .into_iter() + .flatten() + .any(|model| { + model.get("available").and_then(|v| v.as_bool()) == Some(true) + && model + .get("features") + .and_then(|v| v.as_array()) + .into_iter() + .flatten() + .any(|f| f.as_str() == Some("vision")) + }); + let voice = local_ai::voice_status(&state.root); + let chat = backends + .pointer("/ollama/available") + .and_then(|v| v.as_bool()) + == Some(true) + || backends + .pointer("/llama_cpp/available") + .and_then(|v| v.as_bool()) + == Some(true); + Json(json!({ + "chat": chat, + "threads": true, + "model_cookbook": true, + "documents": true, + "encrypted_import_export": true, + "scout_vision": vision, + "voice_stt": voice.pointer("/stt/available").and_then(|v| v.as_bool()).unwrap_or(false), + "voice_tts": voice.pointer("/tts/available").and_then(|v| v.as_bool()).unwrap_or(false), + "phone_access": state.runtime_config.phone_access_available, + "field_manual": state.root.join("content/field-manual/cards.json").is_file(), + "benchmark": true, + "settings": true, + "support_bundle": true, + "llm_config_agent": chat, + "offline_lock": true, + "shell_tools": false, + "email_calendar_tools": false, + "lan_exposure": !state.bind.ip().is_loopback() + })) +} + +async fn list_models(State(state): State>) -> impl IntoResponse { + let mut merged = state.models.clone(); + if let Ok(user) = read_json(&state.root.join("config/user-models.json")) { + if let (Some(base), Some(extra)) = ( + merged.get_mut("models").and_then(|v| v.as_array_mut()), + user.get("models").and_then(|v| v.as_array()), + ) { + for p in extra { + base.push(p.clone()); + } + } + } + let installed = local_ai::installed_ollama_models(&state.client, &state.backend_config).await; + Json(local_ai::catalog_with_availability( + &merged, + &installed, + &state.root, + )) +} + +async fn add_model( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + if !valid_id(&req.id) || req.label.trim().is_empty() { + return ( + StatusCode::BAD_REQUEST, + Json(json!({"ok":false,"error":"invalid id or label"})), + ) + .into_response(); + } + let installed = local_ai::installed_ollama_models(&state.client, &state.backend_config).await; + if !installed.iter().any(|name| { + name == &req.id || name.trim_end_matches(":latest") == req.id.trim_end_matches(":latest") + }) { + return (StatusCode::BAD_REQUEST, Json(json!({ + "ok": false, + "error": "model is not installed in the local Ollama store; install it with a builder before registering it" + }))).into_response(); + } + let path = state.root.join("config/user-models.json"); + let mut user = read_json(&path).unwrap_or_else(|_| json!({"models":[]})); + let model = json!({"id":req.id,"label":req.label,"ollama":req.id,"min_ram_gb":8,"min_vram_gb":0,"features":["chat"],"allowed_backends":["ollama"]}); + user["models"].as_array_mut().unwrap().push(model); + if let Err(e) = write_json_pretty(&path, &user) { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({"ok":false,"error":e.to_string()})), + ) + .into_response(); + } + Json(json!({"ok":true,"message":"installed local model registered"})).into_response() +} + +async fn chat_completions( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + match route_chat(state, req).await { + Ok(v) => (StatusCode::OK, Json(v)).into_response(), + Err(e) => ( + StatusCode::BAD_GATEWAY, + Json(json!({"error": e.to_string()})), + ) + .into_response(), + } +} + +async fn route_chat(state: Arc, req: local_ai::ChatInput) -> Result { + local_ai::chat( + &state.client, + &state.models, + &state.root, + &state.backend_config, + req, + ) + .await +} + +async fn list_documents(State(state): State>) -> impl IntoResponse { + match storage::list_documents_with_content(&state.root) { + Ok(documents) => Json(json!({"documents": documents})).into_response(), + Err(error) => error_response(StatusCode::INTERNAL_SERVER_ERROR, error), + } +} + +async fn save_document( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + match storage::save_document(&state.root, &req.name, &req.content) { + Ok(path) => { + Json(json!({"ok":true,"saved":path.file_name().unwrap_or_default().to_string_lossy()})) + .into_response() + } + Err(error) => error_response(StatusCode::BAD_REQUEST, error), + } +} + +async fn list_threads( + State(state): State>, + Query(query): Query>, +) -> impl IntoResponse { + match storage::list_threads(&state.root, query.get("q").map(String::as_str)) { + Ok(threads) => Json(json!({"threads": threads})).into_response(), + Err(error) => error_response(StatusCode::INTERNAL_SERVER_ERROR, error), + } +} + +async fn create_thread( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + match storage::create_thread(&state.root, req.title.as_deref(), req.model) { + Ok(thread) => (StatusCode::CREATED, Json(json!({"thread": thread}))).into_response(), + Err(error) => error_response(StatusCode::INTERNAL_SERVER_ERROR, error), + } +} + +async fn get_thread( + State(state): State>, + AxumPath(id): AxumPath, +) -> impl IntoResponse { + match storage::load_thread(&state.root, &id) { + Ok(thread) => Json(json!({"thread": thread})).into_response(), + Err(error) => error_response(StatusCode::NOT_FOUND, error), + } +} + +async fn rename_thread( + State(state): State>, + AxumPath(id): AxumPath, + Json(req): Json, +) -> impl IntoResponse { + match storage::rename_thread(&state.root, &id, &req.title) { + Ok(thread) => Json(json!({"thread": thread})).into_response(), + Err(error) => error_response(StatusCode::BAD_REQUEST, error), + } +} + +async fn delete_thread( + State(state): State>, + AxumPath(id): AxumPath, +) -> impl IntoResponse { + match storage::delete_thread(&state.root, &id) { + Ok(_) => Json(json!({"ok": true, "deleted": id})).into_response(), + Err(error) => error_response(StatusCode::BAD_REQUEST, error), + } +} + +async fn send_thread_message( + State(state): State>, + AxumPath(id): AxumPath, + Json(req): Json, +) -> impl IntoResponse { + if req.content.trim().is_empty() { + return error_response( + StatusCode::BAD_REQUEST, + anyhow::anyhow!("message cannot be empty"), + ); + } + let mut thread = match storage::load_thread(&state.root, &id) { + Ok(value) => value, + Err(error) => return error_response(StatusCode::NOT_FOUND, error), + }; + if thread.messages.is_empty() && thread.title == "New conversation" { + thread.title = req.content.trim().chars().take(72).collect(); + } + let model = req.model.or_else(|| thread.model.clone()); + thread.model = model.clone(); + storage::append_message(&mut thread, "user", json!(req.content)); + if let Err(error) = storage::save_thread(&state.root, &thread) { + return error_response(StatusCode::INTERNAL_SERVER_ERROR, error); + } + let messages = thread + .messages + .iter() + .map(|message| local_ai::AiMessage { + role: message.role.clone(), + content: message.content.clone(), + }) + .collect(); + let input = local_ai::ChatInput { + model, + messages, + temperature: Some(0.4), + max_tokens: Some(1024), + think: None, + }; + let response = match route_chat(state.clone(), input).await { + Ok(value) => value, + Err(error) => return error_response(StatusCode::BAD_GATEWAY, error), + }; + let content = response + .pointer("/choices/0/message/content") + .cloned() + .unwrap_or_else(|| json!("")); + storage::append_message(&mut thread, "assistant", content); + if let Err(error) = storage::save_thread(&state.root, &thread) { + return error_response(StatusCode::INTERNAL_SERVER_ERROR, error); + } + Json(json!({"thread": thread, "response": response})).into_response() +} + +async fn export_pack( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + match crypto_pack::export_pack(&state.root, &req) { + Ok(bytes) => Json(json!({ + "ok": true, + "filename": format!("jackailocal-backup-{}.jackaipack", chrono::Utc::now().format("%Y%m%d-%H%M%S")), + "mime_type": "application/vnd.jackailocal.encrypted-pack+json", + "data_base64": B64.encode(bytes), + "encryption": "AES-256-GCM", + "key_storage": "not stored" + })).into_response(), + Err(error) => error_response(StatusCode::BAD_REQUEST, error), + } +} + +async fn import_pack( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + match crypto_pack::import_pack(&state.root, &req) { + Ok(result) => Json(json!({"ok": true, "result": result})).into_response(), + Err(error) => error_response(StatusCode::BAD_REQUEST, error), + } +} + +async fn analyze_image( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + match local_ai::analyze_image( + &state.client, + &state.models, + &state.root, + &state.backend_config, + req, + ) + .await + { + Ok(result) => Json(result).into_response(), + Err(error) => error_response(StatusCode::BAD_GATEWAY, error), + } +} + +async fn voice_status(State(state): State>) -> impl IntoResponse { + Json(local_ai::voice_status(&state.root)) +} + +async fn transcribe_audio( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + match local_ai::transcribe(&state.root, &req) { + Ok(result) => Json(result).into_response(), + Err(error) => error_response(StatusCode::SERVICE_UNAVAILABLE, error), + } +} + +async fn synthesize_speech( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + match local_ai::synthesize(&state.root, &req) { + Ok(result) => Json(result).into_response(), + Err(error) => error_response(StatusCode::SERVICE_UNAVAILABLE, error), + } +} + +async fn license_status(State(state): State>) -> impl IntoResponse { + Json(license::status(&state.root).to_json()) +} + +async fn install_license( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + match license::install(&state.root, &req.content) { + Ok(status) => Json(status.to_json()).into_response(), + Err(err) => ( + StatusCode::BAD_REQUEST, + Json(json!({"error": err.to_string()})), + ) + .into_response(), + } +} + +async fn legal_eula( + State(state): State>, + Query(query): Query>, +) -> impl IntoResponse { + let lang = match query.get("lang").map(String::as_str) { + Some("fr") => "fr", + _ => "en", + }; + let relative = if lang == "fr" { + "legal/EULA_FR.md" + } else { + "legal/EULA_EN.md" + }; + match std::fs::read_to_string(state.root.join(relative)) { + Ok(markdown) => Json(json!({"lang": lang, "markdown": markdown})).into_response(), + Err(_) => ( + StatusCode::NOT_FOUND, + Json(json!({"error": format!("EULA file not found: {relative}")})), + ) + .into_response(), + } +} + +async fn phone_status(State(state): State>) -> impl IntoResponse { + Json(phone::status( + &state.root, + &state.runtime_config, + state.bind, + )) +} + +async fn set_phone_access( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + match phone::set_phone_access(&state.root, &state.runtime_config, req.enabled, state.bind) { + Ok(status) => Json(json!({"ok": true, "phone_access": status})).into_response(), + Err(error) => error_response(StatusCode::BAD_REQUEST, error), + } +} + +async fn restart_runtime(State(state): State>) -> impl IntoResponse { + let executable = match std::env::current_exe() { + Ok(path) => path, + Err(error) => return error_response(StatusCode::INTERNAL_SERVER_ERROR, error), + }; + let config = state.runtime_config.config_path.clone(); + let root = state.root.clone(); + if let Err(error) = spawn_delayed_restart(&executable, &config, &root) { + return error_response(StatusCode::INTERNAL_SERVER_ERROR, error); + } + tokio::spawn(async { + tokio::time::sleep(Duration::from_millis(300)).await; + std::process::exit(0); + }); + Json(json!({"ok": true, "message": "runtime restart scheduled"})).into_response() +} + +async fn content_packs(State(state): State>) -> impl IntoResponse { + let mut catalog = read_json(&state.root.join("config/content-packs.json")) + .unwrap_or_else(|_| json!({"packs":[]})); + if let Some(packs) = catalog + .get_mut("packs") + .and_then(|value| value.as_array_mut()) + { + for pack in packs { + let id = pack + .get("id") + .and_then(|value| value.as_str()) + .unwrap_or(""); + let configured_path = pack.get("storage_target").and_then(|value| value.as_str()); + let installed = configured_path + .map(|path| state.root.join(path).exists()) + .unwrap_or_else(|| match id { + "starter_prompt_packs" | "professional_prompt_packs" => { + state.root.join("content-packs/prompts").exists() + } + _ => false, + }); + pack["installed"] = json!(installed); + } + } + Json(catalog) +} + +async fn field_manual(State(state): State>) -> impl IntoResponse { + match read_json(&state.root.join("content/field-manual/cards.json")) { + Ok(cards) => Json(cards).into_response(), + Err(error) => error_response(StatusCode::NOT_FOUND, error), + } +} + +async fn get_settings(State(state): State>) -> impl IntoResponse { + Json(storage::read_settings(&state.root)) +} + +async fn save_settings( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + if let Some(enabled) = req.phone_access_enabled { + if let Err(error) = + phone::set_phone_access(&state.root, &state.runtime_config, enabled, state.bind) + { + return error_response(StatusCode::BAD_REQUEST, error); + } + } + let mut settings = storage::read_settings(&state.root); + if let Some(object) = settings.as_object_mut() { + if let Some(mode) = req.mode { + object.insert("mode".to_string(), json!(mode)); + } + if let Some(value) = req.offline_lock { + object.insert("offline_lock".to_string(), json!(value)); + } + if let Some(value) = req.tools_lock { + object.insert("tools_lock".to_string(), json!(value)); + } + } + match storage::write_settings(&state.root, &settings) { + Ok(_) => Json(json!({"ok":true,"settings":settings})).into_response(), + Err(error) => error_response(StatusCode::INTERNAL_SERVER_ERROR, error), + } +} + +async fn run_benchmark( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + let model = req.model.unwrap_or_else(|| "auto".to_string()); + let started = Instant::now(); + let chat = local_ai::ChatInput { + model: Some(model.clone()), + temperature: Some(0.1), + max_tokens: Some(96), + think: None, + messages: vec![local_ai::AiMessage { + role: "user".to_string(), + content: json!("Answer in one sentence: JackAILocal offline test"), + }], + }; + let result = route_chat(state, chat).await; + let elapsed_ms = started.elapsed().as_millis(); + match result { + Ok(v) => Json(json!({"ok":true,"model":model,"elapsed_ms":elapsed_ms,"sample":v})) + .into_response(), + Err(e) => { + Json(json!({"ok":false,"model":model,"elapsed_ms":elapsed_ms,"error":e.to_string()})) + .into_response() + } + } +} + +async fn agent_recommend( + State(state): State>, + Json(req): Json, +) -> impl IntoResponse { + let max_params_b = req.max_params_b.unwrap_or(32.0); + if !(0.0..=32.0).contains(&max_params_b) { + return error_response( + StatusCode::BAD_REQUEST, + anyhow::anyhow!("agent max_params_b must be greater than 0 and no more than 32"), + ); + } + + let installed = local_ai::installed_ollama_models(&state.client, &state.backend_config).await; + let catalog = local_ai::catalog_with_availability(&state.models, &installed, &state.root); + let agent_max_tokens = req.agent_max_tokens.unwrap_or(192).clamp(32, 512); + let agent_timeout_seconds = req.agent_timeout_seconds.unwrap_or(120).clamp(15, 300); + let requested_agent = req + .agent_model + .clone() + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| preferred_agent_model(&catalog).unwrap_or_else(|| "auto".to_string())); + + let resolved_agent = if requested_agent.eq_ignore_ascii_case("auto") { + match preferred_agent_model(&catalog) + .filter(|model| find_available_model(&catalog, model, "chat", max_params_b).is_some()) + { + Some(model) => model, + None => { + return error_response( + StatusCode::SERVICE_UNAVAILABLE, + anyhow::anyhow!( + "no installed local chat model <= {max_params_b}B is available for the LLM-in-the-loop agent; install gemma4:12b or another compatible catalog model" + ), + ); + } + } + } else { + if find_available_model(&catalog, &requested_agent, "chat", max_params_b).is_none() { + return error_response( + StatusCode::BAD_REQUEST, + anyhow::anyhow!( + "requested LLM-in-the-loop agent model is not installed or exceeds the configured limit: {}", + requested_agent + ), + ); + } + requested_agent.clone() + }; + + let policy_context = json!({ + "hard_constraints": { + "max_params_b": max_params_b, + "local_runtime_only": true, + "no_cloud_fallback": true, + "must_choose_from_candidate_models": true, + "must_not_claim_uninstalled_models": true, + "agent_max_tokens": agent_max_tokens, + "agent_timeout_seconds": agent_timeout_seconds + }, + "runtime": { + "backend": state.backend_config.ollama_url, + "installed_ollama_models": installed, + "requested_agent_model": requested_agent, + "resolved_agent_model": resolved_agent + }, + "hardware": req.hardware.unwrap_or_else(|| local_ai::hardware(&state.root)), + "package_goal": req.package_goal.unwrap_or_else(|| "standard offline assistant".to_string()), + "policy_default_model": req.default_model, + "candidate_models": req.candidate_models.unwrap_or_else(|| catalog.get("models").cloned().unwrap_or_else(|| json!([]))), + "content_packs": req.content_packs, + "current_config": req.current_config, + "customer_notes": req.customer_notes.unwrap_or_default(), + }); + + let language = req.language.unwrap_or_else(|| "en".to_string()); + let system = format!( + "You are JackAILocal Config Agent. You are part of the model-selection and client-configuration decision loop. \ + Use only the provided local catalog, installed model status, hardware profile, and policy constraints. \ + Never recommend a model above {max_params_b}B. Never claim a model is installed unless available=true or it appears in installed_ollama_models. \ + Prefer Gemma 4 12B (`gemma4:12b`) as the config-agent model when it is installed and hardware allows it, but do not invent availability. \ + Return only valid JSON with these keys: selected_model_id, selected_model_ref, agent_model_role, confidence, backend, content_packs, config_changes, risk_flags, human_summary, next_steps. \ + Answer language: {language}." + ); + let user = format!( + "Review this JackAILocal client build plan and make a constrained recommendation:\n{}", + serde_json::to_string_pretty(&policy_context).unwrap_or_else(|_| "{}".to_string()) + ); + let chat = local_ai::ChatInput { + model: Some(resolved_agent.clone()), + temperature: Some(0.1), + max_tokens: Some(agent_max_tokens), + think: Some(false), + messages: vec![ + local_ai::AiMessage { + role: "system".to_string(), + content: json!(system), + }, + local_ai::AiMessage { + role: "user".to_string(), + content: json!(user), + }, + ], + }; + + match tokio::time::timeout( + Duration::from_secs(agent_timeout_seconds), + route_chat(state, chat), + ) + .await + { + Ok(response) => { + let response = match response { + Ok(response) => response, + Err(error) => return error_response(StatusCode::BAD_GATEWAY, error), + }; + let content = response + .pointer("/choices/0/message/content") + .and_then(|value| value.as_str()) + .unwrap_or(""); + if content.trim().is_empty() { + return error_response( + StatusCode::BAD_GATEWAY, + anyhow::anyhow!( + "LLM-in-the-loop agent returned an empty response from the local backend; try a larger token budget or install gemma4:12b" + ), + ); + } + Json(json!({ + "ok": true, + "agent_model": resolved_agent, + "requested_agent_model": requested_agent, + "max_params_b": max_params_b, + "agent_max_tokens": agent_max_tokens, + "agent_timeout_seconds": agent_timeout_seconds, + "llm_response": response, + "agent_json": parse_json_object_from_text(content), + "raw_content": content, + "policy_context": policy_context + })) + .into_response() + } + Err(_) => error_response( + StatusCode::GATEWAY_TIMEOUT, + anyhow::anyhow!( + "LLM-in-the-loop agent timed out after {agent_timeout_seconds}s; install a faster local agent model or lower agent_max_tokens" + ), + ), + } +} + +async fn support_bundle(State(state): State>) -> impl IntoResponse { + match create_support_bundle(&state.root) { + Ok((path, bytes)) => Json(json!({ + "ok": true, + "filename": path.file_name().unwrap_or_default().to_string_lossy(), + "path": path.to_string_lossy(), + "mime_type": "application/zip", + "data_base64": B64.encode(bytes), + "privacy": "conversations and documents are not included" + })) + .into_response(), + Err(error) => error_response(StatusCode::INTERNAL_SERVER_ERROR, error), + } +} + +async fn remote_api_auth( + State(state): State>, + ConnectInfo(peer): ConnectInfo, + req: Request, + next: Next, +) -> Response { + let protected = req.uri().path().starts_with("/api/") + || req.uri().path().starts_with("/v1/") + || req.uri().path() == "/health"; + if !protected || peer.ip().is_loopback() { + return next.run(req).await; + } + let supplied = req + .headers() + .get("x-jackailocal-token") + .and_then(|value| value.to_str().ok()); + if supplied.is_some() && supplied == state.phone_token.as_deref() { + return next.run(req).await; + } + ( + StatusCode::UNAUTHORIZED, + Json(json!({ + "error": "phone pairing token required", + "header": "X-JackAILocal-Token" + })), + ) + .into_response() +} + +fn error_response(status: StatusCode, error: impl std::fmt::Display) -> Response { + ( + status, + Json(json!({"ok": false, "error": error.to_string()})), + ) + .into_response() +} + +#[cfg(target_os = "windows")] +fn spawn_delayed_restart(executable: &Path, config: &Path, root: &Path) -> Result<()> { + use std::os::windows::process::CommandExt; + let quote = |value: &Path| value.to_string_lossy().replace('\'', "''"); + let command = format!( + "Start-Sleep -Seconds 1; & '{}' serve --config '{}'", + quote(executable), + quote(config) + ); + ProcessCommand::new("powershell.exe") + .args(["-NoProfile", "-WindowStyle", "Hidden", "-Command", &command]) + .current_dir(root) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .creation_flags(0x08000000) + .spawn()?; + Ok(()) +} + +#[cfg(not(target_os = "windows"))] +fn spawn_delayed_restart(executable: &Path, config: &Path, root: &Path) -> Result<()> { + ProcessCommand::new("sh") + .args([ + "-c", + "sleep 1; exec \"$1\" serve --config \"$2\"", + "jackailocal-restart", + ]) + .arg(executable) + .arg(config) + .current_dir(root) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn()?; + Ok(()) +} + +fn create_support_bundle(root: &Path) -> Result<(PathBuf, Vec)> { + let filename = format!( + "JackAILocal-support-{}.zip", + chrono::Utc::now().format("%Y%m%d-%H%M%S") + ); + let path = root.join("workspace/exports").join(filename); + let file = std::fs::File::create(&path)?; + let mut archive = zip::ZipWriter::new(file); + let options = zip::write::SimpleFileOptions::default() + .compression_method(zip::CompressionMethod::Deflated); + let fixed_files = [ + ("diagnostics/hardware.json", "hardware.json"), + ("manifest/sha256-manifest.json", "sha256-manifest.json"), + ("manifest/build-manifest.json", "build-manifest.json"), + ("config/model-catalog.json", "model-catalog.json"), + ]; + for (relative, archive_name) in fixed_files { + let source = root.join(relative); + if source.is_file() { + archive.start_file(archive_name, options)?; + std::io::Write::write_all(&mut archive, &std::fs::read(source)?)?; + } + } + for (relative, archive_prefix) in [("logs", "logs"), (".jackailocal/logs", "runtime-logs")] { + let logs = root.join(relative); + if logs.is_dir() { + for entry in std::fs::read_dir(logs)? { + let entry = entry?; + if entry.path().is_file() { + let name = format!("{archive_prefix}/{}", entry.file_name().to_string_lossy()); + archive.start_file(name, options)?; + std::io::Write::write_all(&mut archive, &std::fs::read(entry.path())?)?; + } + } + } + } + archive.finish()?; + let bytes = std::fs::read(&path)?; + Ok((path, bytes)) +} + +fn preferred_agent_model(catalog: &Value) -> Option { + find_available_model(catalog, "gemma_config_agent_12b", "chat", 32.0) + .or_else(|| find_available_model(catalog, "gemma4:12b", "chat", 32.0)) + .or_else(|| { + catalog + .get("models")? + .as_array()? + .iter() + .find(|model| { + model.get("available").and_then(|value| value.as_bool()) == Some(true) + && model_params_b(model) <= 32.0 + && model_has_feature(model, "chat") + }) + .and_then(|model| { + model + .get("id") + .or_else(|| model.get("ollama")) + .and_then(|value| value.as_str()) + .map(str::to_string) + }) + }) +} + +fn find_available_model( + catalog: &Value, + requested: &str, + required_feature: &str, + max_params_b: f64, +) -> Option { + catalog + .get("models")? + .as_array()? + .iter() + .find(|model| { + (model.get("id").and_then(|value| value.as_str()) == Some(requested) + || model.get("ollama").and_then(|value| value.as_str()) == Some(requested)) + && model.get("available").and_then(|value| value.as_bool()) == Some(true) + && model_params_b(model) <= max_params_b + && model_has_feature(model, required_feature) + }) + .and_then(|model| { + model + .get("id") + .or_else(|| model.get("ollama")) + .and_then(|value| value.as_str()) + .map(str::to_string) + }) +} + +fn model_params_b(model: &Value) -> f64 { + model + .get("params_b") + .and_then(|value| value.as_f64()) + .unwrap_or(999.0) +} + +fn model_has_feature(model: &Value, feature: &str) -> bool { + model + .get("features") + .and_then(|value| value.as_array()) + .into_iter() + .flatten() + .any(|value| value.as_str() == Some(feature)) +} + +fn parse_json_object_from_text(text: &str) -> Value { + serde_json::from_str::(text) + .ok() + .or_else(|| { + let start = text.find('{')?; + let end = text.rfind('}')?; + serde_json::from_str::(&text[start..=end]).ok() + }) + .unwrap_or(Value::Null) +} + +fn read_json(path: &Path) -> Result { + let s = std::fs::read_to_string(path)?; + Ok(serde_json::from_str(&s)?) +} +fn write_json_pretty(path: &Path, value: &Value) -> Result<()> { + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent).ok(); + } + std::fs::write(path, serde_json::to_string_pretty(value)?.as_bytes())?; + Ok(()) +} +fn valid_id(s: &str) -> bool { + !s.is_empty() + && s.len() <= 80 + && s.chars().all(|c| { + c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.' || c == ':' || c == '/' + }) +} +fn hwscan_json() -> String { + json!({"ram_gb": sys_total_memory_gb(), "vram_gb": 0, "gpu_vendor":"unknown", "cpu_threads": std::thread::available_parallelism().map(|n|n.get()).unwrap_or(1)}).to_string() +} +#[cfg(target_os = "windows")] +#[repr(C)] +#[allow(non_snake_case)] +struct MEMORYSTATUSEX { + dwLength: u32, + dwMemoryLoad: u32, + ullTotalPhys: u64, + ullAvailPhys: u64, + ullTotalPageFile: u64, + ullAvailPageFile: u64, + ullTotalVirtual: u64, + ullAvailVirtual: u64, + ullAvailExtendedVirtual: u64, +} + +#[cfg(target_os = "windows")] +extern "system" { + fn GlobalMemoryStatusEx(lpBuffer: *mut MEMORYSTATUSEX) -> i32; +} + +#[cfg(target_os = "windows")] +fn sys_total_memory_gb() -> f64 { + let mut mem_info = MEMORYSTATUSEX { + dwLength: std::mem::size_of::() as u32, + dwMemoryLoad: 0, + ullTotalPhys: 0, + ullAvailPhys: 0, + ullTotalPageFile: 0, + ullAvailPageFile: 0, + ullTotalVirtual: 0, + ullAvailVirtual: 0, + ullAvailExtendedVirtual: 0, + }; + unsafe { + if GlobalMemoryStatusEx(&mut mem_info) != 0 { + let bytes = mem_info.ullTotalPhys as f64; + let gb = bytes / (1024.0 * 1024.0 * 1024.0); + (gb * 10.0).round() / 10.0 + } else { + 8.0 + } + } +} +#[cfg(not(target_os = "windows"))] +fn sys_total_memory_gb() -> f64 { + let meminfo = std::fs::read_to_string("/proc/meminfo").unwrap_or_default(); + for line in meminfo.lines() { + if line.starts_with("MemTotal:") { + let kb: f64 = line + .split_whitespace() + .nth(1) + .unwrap_or("0") + .parse() + .unwrap_or(0.0); + return (kb / 1024.0 / 1024.0 * 10.0).round() / 10.0; + } + } + 8.0 +} diff --git a/src/phone.rs b/src/phone.rs new file mode 100644 index 0000000000000000000000000000000000000000..05852015f0dcaefda47f2562e23a4c7ecfbb0de8 --- /dev/null +++ b/src/phone.rs @@ -0,0 +1,199 @@ +use crate::storage; +use anyhow::{Context, Result}; +use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; +use qrcode::{render::svg, QrCode}; +use rand::{rngs::OsRng, RngCore}; +use serde_json::{json, Value}; +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}, + path::{Path, PathBuf}, +}; + +#[derive(Debug, Clone)] +pub struct RuntimeConfig { + pub config_path: PathBuf, + pub default_bind: SocketAddr, + pub phone_access_available: bool, + pub ollama_enabled: bool, + pub ollama_url: Option, + pub llama_cpp_enabled: bool, + pub llama_cpp_url: Option, +} + +pub fn load_runtime_config(root: &Path, config_path: &str) -> Result { + let path = if Path::new(config_path).is_absolute() { + PathBuf::from(config_path) + } else { + root.join(config_path) + }; + let text = std::fs::read_to_string(&path) + .with_context(|| format!("cannot read config {}", path.display()))?; + let value: toml::Value = + toml::from_str(&text).with_context(|| format!("cannot parse config {}", path.display()))?; + + let bind_text = value + .get("bind") + .and_then(|v| v.as_str()) + .map(str::to_string) + .or_else(|| { + let host = value.get("server")?.get("host")?.as_str()?; + let port = value.get("server")?.get("port")?.as_integer()?; + Some(format!("{host}:{port}")) + }) + .unwrap_or_else(|| "127.0.0.1:4891".to_string()); + let default_bind = bind_text + .parse::() + .with_context(|| format!("invalid bind address: {bind_text}"))?; + let phone_access_available = value + .get("phone_access_available") + .and_then(|v| v.as_bool()) + .or_else(|| { + value + .get("security")? + .get("phone_access_available")? + .as_bool() + }) + .unwrap_or(false); + let (ollama_enabled, ollama_url) = backend_settings(&value, &["ollama"], true); + let (llama_cpp_enabled, llama_cpp_url) = + backend_settings(&value, &["llamacpp", "llama_cpp"], true); + Ok(RuntimeConfig { + config_path: path, + default_bind, + phone_access_available, + ollama_enabled, + ollama_url, + llama_cpp_enabled, + llama_cpp_url, + }) +} + +pub fn effective_bind(root: &Path, config: &RuntimeConfig) -> SocketAddr { + let settings = storage::read_settings(root); + let enabled = settings + .get("phone_access_enabled") + .and_then(|v| v.as_bool()) + .unwrap_or(false); + if enabled && config.phone_access_available { + SocketAddr::new( + IpAddr::V4(Ipv4Addr::UNSPECIFIED), + config.default_bind.port(), + ) + } else { + config.default_bind + } +} + +pub fn ensure_phone_token(root: &Path) -> Result { + let mut settings = storage::read_settings(root); + if let Some(token) = settings.get("phone_access_token").and_then(|v| v.as_str()) { + if token.len() >= 32 { + return Ok(token.to_string()); + } + } + let mut bytes = [0_u8; 24]; + OsRng.fill_bytes(&mut bytes); + let token = URL_SAFE_NO_PAD.encode(bytes); + if let Some(object) = settings.as_object_mut() { + object.insert("phone_access_token".to_string(), json!(token)); + } + storage::write_settings(root, &settings)?; + Ok(token) +} + +pub fn set_phone_access( + root: &Path, + config: &RuntimeConfig, + enabled: bool, + active_bind: SocketAddr, +) -> Result { + if enabled && !config.phone_access_available { + anyhow::bail!("phone access is disabled by the runtime configuration"); + } + let mut settings = storage::read_settings(root); + if let Some(object) = settings.as_object_mut() { + object.insert("phone_access_enabled".to_string(), json!(enabled)); + } + storage::write_settings(root, &settings)?; + if enabled { + ensure_phone_token(root)?; + } + Ok(status(root, config, active_bind)) +} + +pub fn status(root: &Path, config: &RuntimeConfig, active_bind: SocketAddr) -> Value { + let settings = storage::read_settings(root); + let enabled = settings + .get("phone_access_enabled") + .and_then(|v| v.as_bool()) + .unwrap_or(false); + let token = settings + .get("phone_access_token") + .and_then(|v| v.as_str()) + .unwrap_or(""); + let active = enabled && !active_bind.ip().is_loopback(); + let local_ip = local_ipv4(); + let url = if enabled && !token.is_empty() { + local_ip.map(|ip| format!("http://{ip}:{}?pair={token}", active_bind.port())) + } else { + None + }; + let qr_svg = url.as_deref().and_then(qr_svg); + json!({ + "available": config.phone_access_available, + "enabled": enabled, + "active": active, + "bind": active_bind.to_string(), + "url": url, + "qr_svg": qr_svg, + "pairing_required": true, + "restart_required": enabled != active, + "config": config.config_path.to_string_lossy() + }) +} + +pub fn configured_token(root: &Path) -> Option { + storage::read_settings(root) + .get("phone_access_token") + .and_then(|v| v.as_str()) + .map(str::to_string) +} + +fn local_ipv4() -> Option { + let socket = UdpSocket::bind("0.0.0.0:0").ok()?; + socket.connect("192.0.2.1:80").ok()?; + match socket.local_addr().ok()?.ip() { + IpAddr::V4(ip) if !ip.is_loopback() && !ip.is_unspecified() => Some(ip), + _ => None, + } +} + +fn qr_svg(value: &str) -> Option { + let code = QrCode::new(value.as_bytes()).ok()?; + Some( + code.render::() + .min_dimensions(220, 220) + .dark_color(svg::Color("#0b111a")) + .light_color(svg::Color("#ffffff")) + .build(), + ) +} + +fn backend_settings( + value: &toml::Value, + names: &[&str], + default_enabled: bool, +) -> (bool, Option) { + let backend = value + .get("backends") + .and_then(|backends| names.iter().find_map(|name| backends.get(*name))); + let enabled = backend + .and_then(|entry| entry.get("enabled")) + .and_then(|entry| entry.as_bool()) + .unwrap_or(default_enabled); + let url = backend + .and_then(|entry| entry.get("url").or_else(|| entry.get("base_url"))) + .and_then(|entry| entry.as_str()) + .map(str::to_string); + (enabled, url) +} diff --git a/src/storage.rs b/src/storage.rs new file mode 100644 index 0000000000000000000000000000000000000000..7644360ced958ebba17bc8f9269cc5bf3772c51b --- /dev/null +++ b/src/storage.rs @@ -0,0 +1,300 @@ +use anyhow::{bail, Context, Result}; +use chrono::Utc; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use std::path::{Path, PathBuf}; +use uuid::Uuid; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct StoredMessage { + pub role: String, + pub content: Value, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ThreadRecord { + pub id: String, + pub title: String, + pub model: Option, + pub created_at: String, + pub updated_at: String, + pub messages: Vec, +} + +#[derive(Debug, Clone, Serialize)] +pub struct ThreadSummary { + pub id: String, + pub title: String, + pub model: Option, + pub created_at: String, + pub updated_at: String, + pub message_count: usize, + pub preview: String, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct PackedDocument { + pub name: String, + pub content: String, +} + +pub fn ensure_workspace(root: &Path) -> Result<()> { + for relative in [ + "workspace/documents", + "workspace/settings", + "workspace/threads", + "workspace/exports", + "workspace/voice", + "workspace/scout", + "logs", + ] { + std::fs::create_dir_all(root.join(relative)) + .with_context(|| format!("cannot create {relative}"))?; + } + Ok(()) +} + +pub fn create_thread( + root: &Path, + title: Option<&str>, + model: Option, +) -> Result { + let now = Utc::now().to_rfc3339(); + let thread = ThreadRecord { + id: Uuid::new_v4().to_string(), + title: clean_title(title.unwrap_or("New conversation")), + model, + created_at: now.clone(), + updated_at: now, + messages: Vec::new(), + }; + save_thread(root, &thread)?; + Ok(thread) +} + +pub fn list_threads(root: &Path, filter: Option<&str>) -> Result> { + let filter = filter.unwrap_or("").trim().to_lowercase(); + let mut summaries = Vec::new(); + let dir = root.join("workspace/threads"); + if !dir.exists() { + return Ok(summaries); + } + for entry in + std::fs::read_dir(&dir).with_context(|| format!("cannot read {}", dir.display()))? + { + let entry = match entry { + Ok(value) => value, + Err(_) => continue, + }; + if !entry.path().is_file() + || entry.path().extension().and_then(|v| v.to_str()) != Some("json") + { + continue; + } + let thread: ThreadRecord = match read_json_typed(&entry.path()) { + Ok(value) => value, + Err(_) => continue, + }; + let preview = thread + .messages + .iter() + .rev() + .find_map(|message| message.content.as_str()) + .unwrap_or("") + .chars() + .take(160) + .collect::(); + if !filter.is_empty() + && !thread.title.to_lowercase().contains(&filter) + && !preview.to_lowercase().contains(&filter) + { + continue; + } + summaries.push(ThreadSummary { + id: thread.id, + title: thread.title, + model: thread.model, + created_at: thread.created_at, + updated_at: thread.updated_at, + message_count: thread.messages.len(), + preview, + }); + } + summaries.sort_by(|a, b| b.updated_at.cmp(&a.updated_at)); + Ok(summaries) +} + +pub fn load_thread(root: &Path, id: &str) -> Result { + validate_thread_id(id)?; + let path = thread_path(root, id); + read_json_typed(&path).with_context(|| format!("thread not found: {id}")) +} + +pub fn save_thread(root: &Path, thread: &ThreadRecord) -> Result<()> { + validate_thread_id(&thread.id)?; + write_json_pretty(&thread_path(root, &thread.id), thread) +} + +pub fn rename_thread(root: &Path, id: &str, title: &str) -> Result { + let mut thread = load_thread(root, id)?; + thread.title = clean_title(title); + thread.updated_at = Utc::now().to_rfc3339(); + save_thread(root, &thread)?; + Ok(thread) +} + +pub fn delete_thread(root: &Path, id: &str) -> Result<()> { + validate_thread_id(id)?; + let path = thread_path(root, id); + if path.exists() { + std::fs::remove_file(&path).with_context(|| format!("cannot delete {}", path.display()))?; + } + Ok(()) +} + +pub fn append_message(thread: &mut ThreadRecord, role: &str, content: Value) { + thread.messages.push(StoredMessage { + role: role.to_string(), + content, + }); + thread.updated_at = Utc::now().to_rfc3339(); +} + +pub fn list_documents_with_content(root: &Path) -> Result> { + let mut documents = Vec::new(); + let dir = root.join("workspace/documents"); + if !dir.exists() { + return Ok(documents); + } + for entry in + std::fs::read_dir(&dir).with_context(|| format!("cannot read {}", dir.display()))? + { + let entry = match entry { + Ok(value) => value, + Err(_) => continue, + }; + if !entry.path().is_file() { + continue; + } + let name = entry.file_name().to_string_lossy().to_string(); + if !safe_filename(&name) { + continue; + } + let content = match std::fs::read_to_string(entry.path()) { + Ok(value) => value, + Err(_) => continue, + }; + documents.push(PackedDocument { name, content }); + } + documents.sort_by(|a, b| a.name.cmp(&b.name)); + Ok(documents) +} + +pub fn save_document(root: &Path, name: &str, content: &str) -> Result { + if !safe_filename(name) { + bail!("unsafe filename"); + } + let path = root.join("workspace/documents").join(name); + std::fs::write(&path, content.as_bytes()) + .with_context(|| format!("cannot write {}", path.display()))?; + Ok(path) +} + +pub fn read_settings(root: &Path) -> Value { + let path = root.join("workspace/settings/settings.json"); + read_json_value(&path).unwrap_or_else(|_| { + json!({ + "mode": "simple", + "offline_lock": true, + "tools_lock": true, + "phone_access_enabled": false + }) + }) +} + +pub fn write_settings(root: &Path, settings: &Value) -> Result<()> { + write_json_pretty(&root.join("workspace/settings/settings.json"), settings) +} + +pub fn read_all_threads(root: &Path) -> Result> { + let mut threads = Vec::new(); + for summary in list_threads(root, None)? { + if let Ok(thread) = load_thread(root, &summary.id) { + threads.push(thread); + } + } + Ok(threads) +} + +pub fn import_threads(root: &Path, threads: &[ThreadRecord], replace: bool) -> Result { + let dir = root.join("workspace/threads"); + if replace && dir.exists() { + for entry in std::fs::read_dir(&dir)? { + let entry = entry?; + if entry.path().is_file() + && entry.path().extension().and_then(|v| v.to_str()) == Some("json") + { + std::fs::remove_file(entry.path())?; + } + } + } + let mut count = 0; + for thread in threads { + validate_thread_id(&thread.id)?; + save_thread(root, thread)?; + count += 1; + } + Ok(count) +} + +fn thread_path(root: &Path, id: &str) -> PathBuf { + root.join("workspace/threads").join(format!("{id}.json")) +} + +fn validate_thread_id(id: &str) -> Result<()> { + Uuid::parse_str(id).with_context(|| "invalid thread id")?; + Ok(()) +} + +fn clean_title(title: &str) -> String { + let cleaned = title + .trim() + .chars() + .filter(|c| !c.is_control()) + .take(120) + .collect::(); + if cleaned.is_empty() { + "New conversation".to_string() + } else { + cleaned + } +} + +fn safe_filename(name: &str) -> bool { + !name.is_empty() + && name.len() <= 120 + && !name.contains("..") + && !name.contains('/') + && !name.contains('\\') + && name + .chars() + .all(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '_' | '.' | ' ')) +} + +fn read_json_value(path: &Path) -> Result { + let text = std::fs::read_to_string(path)?; + Ok(serde_json::from_str(&text)?) +} + +fn read_json_typed Deserialize<'de>>(path: &Path) -> Result { + let text = std::fs::read_to_string(path)?; + Ok(serde_json::from_str(&text)?) +} + +fn write_json_pretty(path: &Path, value: &T) -> Result<()> { + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent)?; + } + std::fs::write(path, serde_json::to_vec_pretty(value)?)?; + Ok(()) +} diff --git a/tools/pm-hwscan.sh b/tools/pm-hwscan.sh new file mode 100644 index 0000000000000000000000000000000000000000..0146bb80b6147918de0490eb86b521c454014d21 --- /dev/null +++ b/tools/pm-hwscan.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -euo pipefail +OUT=/opt/JackAILocal/diagnostics/hardware.json +mkdir -p /opt/JackAILocal/diagnostics +RAM_GB=$(awk '/MemTotal/ {printf "%.1f", $2/1024/1024}' /proc/meminfo 2>/dev/null || echo 8) +CPU_THREADS=$(nproc 2>/dev/null || echo 1) +GPU_NAME="unknown"; GPU_VENDOR="unknown"; VRAM_GB=0 +if command -v nvidia-smi >/dev/null 2>&1; then + GPU_VENDOR=nvidia + GPU_NAME=$(nvidia-smi --query-gpu=name --format=csv,noheader | head -1 || echo nvidia) + VRAM_GB=$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits | awk 'NR==1 {printf "%.1f", $1/1024}' || echo 0) +elif command -v lspci >/dev/null 2>&1; then + GPU_NAME=$(lspci | grep -Ei 'vga|3d|display' | head -1 | sed 's/.*: //') + echo "$GPU_NAME" | grep -qi nvidia && GPU_VENDOR=nvidia || true + echo "$GPU_NAME" | grep -Eqi 'amd|radeon' && GPU_VENDOR=amd || true + echo "$GPU_NAME" | grep -qi intel && GPU_VENDOR=intel || true +fi +cat > "$OUT" <&2; exit 1; } +command -v python3 >/dev/null 2>&1 || { echo "python3 is required to install voice assets." >&2; exit 1; } +command -v curl >/dev/null 2>&1 || { echo "curl is required to install voice assets." >&2; exit 1; } + +case "$(uname -s)" in + Darwin) PLATFORM=macos ;; + Linux) PLATFORM=linux ;; + *) echo "Unsupported platform: $(uname -s)" >&2; exit 1 ;; +esac +case "$(uname -m)" in + x86_64|amd64) ARCH=x86_64 ;; + arm64|aarch64) ARCH=aarch64 ;; + *) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;; +esac + +ASSET_KEY="${PLATFORM}_${ARCH}" +if [ "$ASSET_KEY" = "linux_aarch64" ]; then + echo "No verified Piper Linux aarch64 asset is configured." >&2 + exit 1 +fi + +read_json() { + python3 - "$CONFIG" "$1" <<'PY' +import json, sys +value = json.load(open(sys.argv[1], encoding="utf-8")) +for key in sys.argv[2].split("."): + value = value[key] +print(value) +PY +} + +sha256_file() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$1" | awk '{print $1}' + else + shasum -a 256 "$1" | awk '{print $1}' + fi +} + +download_verified() { + url="$1"; expected="$2"; destination="$3" + if [ -f "$destination" ] && [ "$(sha256_file "$destination")" = "$expected" ]; then return 0; fi + rm -f "$destination" + echo "Downloading $url" + curl -fL "$url" -o "$destination" + actual="$(sha256_file "$destination")" + [ "$actual" = "$expected" ] || { rm -f "$destination"; echo "SHA256 verification failed for $url" >&2; exit 1; } +} + +CACHE="$TARGET_ROOT/.jackailocal-builder/voice-cache" +PIPER_DIR="$TARGET_ROOT/backends/piper/$PLATFORM" +WHISPER_DIR="$TARGET_ROOT/backends/whisper.cpp/$PLATFORM" +WHISPER_MODEL_DIR="$TARGET_ROOT/models/whisper" +PIPER_MODEL_DIR="$TARGET_ROOT/models/piper" +mkdir -p "$CACHE" "$PIPER_DIR" "$WHISPER_MODEL_DIR" "$PIPER_MODEL_DIR" + +[ -x "$WHISPER_DIR/whisper-cli" ] || { + echo "Required whisper.cpp binary is missing: $WHISPER_DIR/whisper-cli" >&2 + echo "Run factory/unix/build-whisper-backend.sh on the target platform before packaging." >&2 + exit 1 +} + +PIPER_ARCHIVE="$CACHE/piper.tar.gz" +download_verified "$(read_json "$ASSET_KEY.piper.archive_url")" "$(read_json "$ASSET_KEY.piper.archive_sha256")" "$PIPER_ARCHIVE" +EXTRACT="$CACHE/piper-extract" +rm -rf "$EXTRACT" +mkdir -p "$EXTRACT" +tar -xzf "$PIPER_ARCHIVE" -C "$EXTRACT" +PIPER_BINARY="$(find "$EXTRACT" -type f -name piper -perm -111 | head -n 1)" +[ -n "$PIPER_BINARY" ] || { echo "Piper binary was not found in $PIPER_ARCHIVE" >&2; exit 1; } +cp -R "$(dirname "$PIPER_BINARY")/." "$PIPER_DIR/" +chmod +x "$PIPER_DIR/piper" + +# Model files are platform-independent and live under the "shared" section. +download_verified "$(read_json shared.whisper.model_url)" "$(read_json shared.whisper.model_sha256)" "$WHISPER_MODEL_DIR/ggml-base.bin" +download_verified "$(read_json shared.piper.voice_url)" "$(read_json shared.piper.voice_sha256)" "$PIPER_MODEL_DIR/en_US-libritts_r-medium.onnx" +download_verified "$(read_json shared.piper.voice_config_url)" "$(read_json shared.piper.voice_config_sha256)" "$PIPER_MODEL_DIR/en_US-libritts_r-medium.onnx.json" + +echo "Voice assets installed and verified under $TARGET_ROOT" diff --git a/updates/Create-UpdatePackage.ps1 b/updates/Create-UpdatePackage.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..d7dffbbe9afb751924f9041e69342cd0d8376978 --- /dev/null +++ b/updates/Create-UpdatePackage.ps1 @@ -0,0 +1,62 @@ +# Vendor-side tool. Builds a signed offline update package that customers can +# apply from a USB drive or local folder without any internet access. +# +# The package layout is: +# \ +# update-manifest.json signed manifest (product, version, file hashes) +# update-manifest.json.sig RSA SHA-256 signature +# payload\ the actual replacement files +# +# Example: +# .\Create-UpdatePackage.ps1 -Version "v16" -Files "bin\jackailocald.exe","webui\app-v15.js" -OutputDir "E:\update-v16" +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$Version, + [Parameter(Mandatory=$true)][string[]]$Files, + [Parameter(Mandatory=$true)][string]$OutputDir, + [string]$SourceRoot = "", + [string]$Channel = "stable", + [string]$PrivateKeyXmlPath = "" +) +$ErrorActionPreference = "Stop" + +$scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path +if (-not $SourceRoot) { $SourceRoot = (Resolve-Path (Join-Path $scriptRoot "..")).Path } +$SourceRoot = (Resolve-Path $SourceRoot).Path + +New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null +$payloadDir = Join-Path $OutputDir "payload" +New-Item -ItemType Directory -Force -Path $payloadDir | Out-Null + +$entries = @() +foreach ($relative in $Files) { + $source = Join-Path $SourceRoot $relative + if (!(Test-Path $source -PathType Leaf)) { throw "Update file not found in source root: $source" } + $dest = Join-Path $payloadDir $relative + New-Item -ItemType Directory -Force -Path (Split-Path $dest) | Out-Null + Copy-Item $source $dest -Force + $hash = (Get-FileHash -Algorithm SHA256 $dest).Hash.ToLowerInvariant() + $entries += [pscustomobject]@{ + path = ($relative -replace "\\", "/") + sha256 = $hash + } +} + +$manifest = [pscustomobject]@{ + product = "JackAILocal" + version = $Version + channel = $Channel + created = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") + files = $entries +} +$manifestPath = Join-Path $OutputDir "update-manifest.json" +$manifest | ConvertTo-Json -Depth 5 | Set-Content -Encoding utf8 $manifestPath + +$signArgs = @{ ManifestPath = $manifestPath } +if ($PrivateKeyXmlPath) { $signArgs.PrivateKeyXmlPath = $PrivateKeyXmlPath } +& (Join-Path $scriptRoot "Sign-Manifest.ps1") @signArgs + +Write-Host "" +Write-Host "Offline update package created: $OutputDir" -ForegroundColor Green +Write-Host "Ship the whole folder (manifest + .sig + payload) to the customer." +Write-Host "The customer applies it with: updates\JackAILocal-Update-Offline.ps1 -PackagePath -Apply" diff --git a/updates/Find-And-Apply-Update.ps1 b/updates/Find-And-Apply-Update.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..9d2ddb9ee0e0faae0b2c99aa57878e9aa68e38c9 --- /dev/null +++ b/updates/Find-And-Apply-Update.ps1 @@ -0,0 +1,43 @@ +# Customer-friendly wrapper around JackAILocal-Update-Offline.ps1. +# Finds a signed update package on removable drives or in the local "update" +# folder, verifies it (dry run), then asks for confirmation before applying. +[CmdletBinding()] +param() +$ErrorActionPreference = "Stop" +$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path + +$candidates = @() +foreach ($disk in (Get-CimInstance Win32_LogicalDisk | Where-Object { $_.DriveType -eq 2 })) { + $driveRoot = $disk.DeviceID + "\" + if (Test-Path (Join-Path $driveRoot "update-manifest.json")) { + $candidates += Get-Item $driveRoot + } + $candidates += Get-ChildItem -Path $driveRoot -Directory -ErrorAction SilentlyContinue | + Where-Object { Test-Path (Join-Path $_.FullName "update-manifest.json") } +} +$localUpdate = Join-Path $Root "update" +if (Test-Path (Join-Path $localUpdate "update-manifest.json")) { + $candidates += Get-Item $localUpdate +} + +if (-not $candidates) { + Write-Host "" + Write-Host "Aucun package de mise a jour trouve. / No update package found." -ForegroundColor Yellow + Write-Host "Inserez la cle USB de mise a jour, ou placez le package dans le dossier 'update'." + Write-Host "Insert the update USB key, or put the package in the 'update' folder." + exit 1 +} + +$package = $candidates | Select-Object -First 1 +Write-Host ("Package trouve / Package found: {0}" -f $package.FullName) -ForegroundColor Cyan +Write-Host "" + +# Dry run: verifies the signature and every checksum without touching files. +& (Join-Path $PSScriptRoot "JackAILocal-Update-Offline.ps1") -PackagePath $package.FullName + +$answer = Read-Host "Appliquer cette mise a jour ? / Apply this update? (OUI/YES)" +if ($answer -in @("OUI", "YES", "oui", "yes", "Oui", "Yes")) { + & (Join-Path $PSScriptRoot "JackAILocal-Update-Offline.ps1") -PackagePath $package.FullName -Apply +} else { + Write-Host "Annule. / Cancelled." +} diff --git a/updates/Generate-UpdateKeys.ps1 b/updates/Generate-UpdateKeys.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..ad3ee67354da5028eb2bfb6f1c28ff089a5b151b --- /dev/null +++ b/updates/Generate-UpdateKeys.ps1 @@ -0,0 +1,37 @@ +[CmdletBinding()] +param( + [string]$ConfigDir = "" +) +$ErrorActionPreference = "Stop" + +if ([string]::IsNullOrWhiteSpace($ConfigDir)) { + $scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path + if ([string]::IsNullOrWhiteSpace($scriptRoot)) { + $scriptRoot = "." + } + $ConfigDir = (Join-Path $scriptRoot "..\config") +} + +if (-not (Test-Path $ConfigDir)) { + New-Item -ItemType Directory -Force -Path $ConfigDir | Out-Null +} + +$publicKeyPath = Join-Path $ConfigDir "update-public-key.xml" +$privateKeyPath = Join-Path $ConfigDir "update-private-key.xml" + +if (Test-Path $publicKeyPath) { + Write-Host "Keys already exist. Skipping generation." + exit 0 +} + +Write-Host "Generating new RSA key pair for update manifest signing..." +$rsa = [System.Security.Cryptography.RSA]::Create(2048) +$privateKeyXml = $rsa.ToXmlString($true) +$publicKeyXml = $rsa.ToXmlString($false) + +$publicKeyXml | Set-Content -Path $publicKeyPath -Encoding UTF8 +$privateKeyXml | Set-Content -Path $privateKeyPath -Encoding UTF8 + +Write-Host "Keys generated successfully." +Write-Host "Public Key: $publicKeyPath" +Write-Host "Private Key (KEEP SECRET): $privateKeyPath" diff --git a/updates/JackAILocal-Update-Offline.ps1 b/updates/JackAILocal-Update-Offline.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..e65f369c4e0d8fdfbb4bb4cfdf520be86e0b59d7 --- /dev/null +++ b/updates/JackAILocal-Update-Offline.ps1 @@ -0,0 +1,137 @@ +# Customer-side tool. Applies a signed offline update package produced by +# Create-UpdatePackage.ps1, with no internet access required. Designed for +# USB-delivered updates on air-gapped machines. +# +# Safety properties: +# - The manifest signature is verified against config\update-public-key.xml +# before anything is touched. +# - Every payload file must match its SHA-256 hash from the signed manifest. +# - Replaced files are backed up first; -Rollback restores the last backup. +# - Paths are confined to the installation root (no path traversal). +# +# Usage: +# .\JackAILocal-Update-Offline.ps1 -PackagePath D:\update-v16 (dry run) +# .\JackAILocal-Update-Offline.ps1 -PackagePath D:\update-v16 -Apply +# .\JackAILocal-Update-Offline.ps1 -Rollback +[CmdletBinding()] +param( + [string]$PackagePath = "", + [switch]$Apply, + [switch]$Force, + [switch]$Rollback +) +$ErrorActionPreference = "Stop" +$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path +$backupRoot = Join-Path $Root ".jackailocal\updates\backups" + +function Get-InstalledVersion { + $productPath = Join-Path $Root "manifest\product.json" + if (Test-Path $productPath) { + try { return (Get-Content $productPath -Raw | ConvertFrom-Json).version } catch { return "" } + } + return "" +} + +if ($Rollback) { + if (!(Test-Path $backupRoot)) { throw "No update backups found under $backupRoot" } + $latest = Get-ChildItem $backupRoot -Directory | Sort-Object Name -Descending | Select-Object -First 1 + if (-not $latest) { throw "No update backups found under $backupRoot" } + Write-Host "Rolling back from backup: $($latest.FullName)" + Get-ChildItem $latest.FullName -Recurse -File | ForEach-Object { + $relative = $_.FullName.Substring($latest.FullName.Length).TrimStart("\") + $dest = Join-Path $Root $relative + New-Item -ItemType Directory -Force -Path (Split-Path $dest) | Out-Null + Copy-Item $_.FullName $dest -Force + Write-Host "Restored: $relative" + } + Write-Host "Rollback complete. Restart JackAILocal." -ForegroundColor Green + exit 0 +} + +if (-not $PackagePath) { throw "Provide -PackagePath or -Rollback." } +$PackagePath = (Resolve-Path $PackagePath).Path +$manifestPath = Join-Path $PackagePath "update-manifest.json" +$sigPath = "$manifestPath.sig" +$payloadDir = Join-Path $PackagePath "payload" +$pubKeyPath = Join-Path $Root "config\update-public-key.xml" + +foreach ($required in @($manifestPath, $sigPath, $pubKeyPath)) { + if (!(Test-Path $required)) { throw "Required file not found: $required" } +} +if (!(Test-Path $payloadDir)) { throw "Update package has no payload folder: $payloadDir" } + +& (Join-Path $PSScriptRoot "Verify-SignedManifest.ps1") -ManifestPath $manifestPath -SignaturePath $sigPath -PublicKeyXmlPath $pubKeyPath + +$manifest = Get-Content $manifestPath -Raw | ConvertFrom-Json +if ($manifest.product -ne "JackAILocal") { throw "Wrong product in update manifest: $($manifest.product)" } + +$installed = Get-InstalledVersion +Write-Host "Installed version : $installed" +Write-Host "Update version : $($manifest.version)" +if ($installed -eq $manifest.version -and -not $Force) { + throw "This version is already installed. Use -Force to reapply." +} + +# Verify every payload file against the signed manifest before touching anything. +$verified = @() +foreach ($file in $manifest.files) { + $relative = $file.path -replace "/", "\" + $sourcePath = [System.IO.Path]::GetFullPath((Join-Path $payloadDir $relative)) + if (-not $sourcePath.StartsWith($payloadDir, [System.StringComparison]::OrdinalIgnoreCase)) { + throw "Security error: path traversal detected in payload path: $($file.path)" + } + $destPath = [System.IO.Path]::GetFullPath((Join-Path $Root $relative)) + if (-not $destPath.StartsWith($Root, [System.StringComparison]::OrdinalIgnoreCase)) { + throw "Security error: path traversal detected in target path: $($file.path)" + } + if (!(Test-Path $sourcePath -PathType Leaf)) { throw "Payload file missing from package: $($file.path)" } + $hash = (Get-FileHash -Algorithm SHA256 $sourcePath).Hash.ToLowerInvariant() + if ($hash -ne $file.sha256.ToLowerInvariant()) { throw "Checksum failed for: $($file.path)" } + $verified += [pscustomobject]@{ Relative = $relative; Source = $sourcePath; Dest = $destPath } + Write-Host "Verified: $($file.path)" +} + +if (-not $Apply) { + Write-Host "" + Write-Host "Dry run complete: signature and all checksums are valid." -ForegroundColor Green + Write-Host "Run again with -Apply to install update $($manifest.version)." + exit 0 +} + +# Backup current files (including the product manifest, so -Rollback also +# restores the previous version number), then apply. +$stamp = Get-Date -Format "yyyyMMdd-HHmmss" +$backupDir = Join-Path $backupRoot "$stamp-$($manifest.version)" +$productPath = Join-Path $Root "manifest\product.json" +if (Test-Path $productPath -PathType Leaf) { + $backupProduct = Join-Path $backupDir "manifest\product.json" + New-Item -ItemType Directory -Force -Path (Split-Path $backupProduct) | Out-Null + Copy-Item $productPath $backupProduct -Force +} +foreach ($entry in $verified) { + if (Test-Path $entry.Dest -PathType Leaf) { + $backupPath = Join-Path $backupDir $entry.Relative + New-Item -ItemType Directory -Force -Path (Split-Path $backupPath) | Out-Null + Copy-Item $entry.Dest $backupPath -Force + } +} +foreach ($entry in $verified) { + New-Item -ItemType Directory -Force -Path (Split-Path $entry.Dest) | Out-Null + Copy-Item $entry.Source $entry.Dest -Force + Write-Host "Updated: $($entry.Relative)" +} + +# Record the new version in the local product manifest. +if (Test-Path $productPath) { + try { + $product = Get-Content $productPath -Raw | ConvertFrom-Json + $product.version = $manifest.version + $product | ConvertTo-Json -Depth 10 | Set-Content -Encoding utf8 $productPath + } catch { + Write-Warning "Could not record the new version in manifest\product.json: $($_.Exception.Message)" + } +} + +Write-Host "" +Write-Host "Update $($manifest.version) applied. Backup saved to: $backupDir" -ForegroundColor Green +Write-Host "Restart JackAILocal to use the new version. Use -Rollback to undo." diff --git a/updates/JackAILocal-Update.ps1 b/updates/JackAILocal-Update.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..6e23a48aabba18cb6a0f1b897023dfea06c0ee5f --- /dev/null +++ b/updates/JackAILocal-Update.ps1 @@ -0,0 +1,46 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$ManifestUrl, + [switch]$Apply +) +$ErrorActionPreference = "Stop" +$Root = Resolve-Path (Join-Path $PSScriptRoot "..") +$tmp = Join-Path $Root ".jackailocal\updates" +New-Item -ItemType Directory -Force -Path $tmp | Out-Null +$manifestPath = Join-Path $tmp "update-manifest.json" +$sigPath = Join-Path $tmp "update-manifest.json.sig" +$pubKeyPath = Join-Path $Root "config\update-public-key.xml" + +if (!(Test-Path $pubKeyPath)) { + throw "Public key for update verification not found at: $pubKeyPath" +} + +# Download manifest and signature +Invoke-WebRequest -Uri $ManifestUrl -OutFile $manifestPath +Invoke-WebRequest -Uri "$ManifestUrl.sig" -OutFile $sigPath + +# Verify Signature +& (Join-Path $PSScriptRoot "Verify-SignedManifest.ps1") -ManifestPath $manifestPath -SignaturePath $sigPath -PublicKeyXmlPath $pubKeyPath + +$manifest = Get-Content $manifestPath -Raw | ConvertFrom-Json +if ($manifest.product -ne "JackAILocal") { throw "Wrong product." } + +$rootPath = [System.IO.Path]::GetFullPath($Root) + +foreach ($file in $manifest.files) { + # Avoid Path Traversal Vulnerability + $dest = [System.IO.Path]::GetFullPath((Join-Path $rootPath $file.path)) + if (-not $dest.StartsWith($rootPath, [System.StringComparison]::OrdinalIgnoreCase)) { + throw "Security error: Path traversal detected in file path: $($file.path)" + } + + $download = Join-Path $tmp ([IO.Path]::GetFileName($file.path)) + Invoke-WebRequest -Uri $file.url -OutFile $download + $h = (Get-FileHash -Algorithm SHA256 $download).Hash.ToLowerInvariant() + if ($h -ne $file.sha256.ToLowerInvariant()) { throw "Checksum failed: $($file.path)" } + if ($Apply) { + New-Item -ItemType Directory -Force -Path (Split-Path $dest) | Out-Null + Copy-Item $download $dest -Force + } +} +if (-not $Apply) { Write-Host "Dry-run update verified. Use -Apply to install." } diff --git a/updates/Sign-Manifest.ps1 b/updates/Sign-Manifest.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a59b928394fed11a385fa579b021b8ff90243e46 --- /dev/null +++ b/updates/Sign-Manifest.ps1 @@ -0,0 +1,28 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$ManifestPath, + [string]$PrivateKeyXmlPath = "" +) +$ErrorActionPreference = "Stop" + +$scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path +if ([string]::IsNullOrWhiteSpace($scriptRoot)) { $scriptRoot = "." } + +if ([string]::IsNullOrWhiteSpace($PrivateKeyXmlPath)) { + $PrivateKeyXmlPath = Join-Path $scriptRoot "..\config\update-private-key.xml" +} + +if (!(Test-Path $PrivateKeyXmlPath)) { + throw "Private key file not found: $PrivateKeyXmlPath" +} + +$manifestFullPath = (Resolve-Path $ManifestPath).Path +$signaturePath = $manifestFullPath + ".sig" + +$bytes = [IO.File]::ReadAllBytes($manifestFullPath) +$rsa = [System.Security.Cryptography.RSA]::Create() +$rsa.FromXmlString((Get-Content $PrivateKeyXmlPath -Raw)) +$sig = $rsa.SignData($bytes, [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1) + +[IO.File]::WriteAllBytes($signaturePath, $sig) +Write-Host "Manifest signed successfully. Signature saved to: $signaturePath" diff --git a/updates/Verify-SignedManifest.ps1 b/updates/Verify-SignedManifest.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..486f2509e26c2f637e939044705f5db70626cdf7 --- /dev/null +++ b/updates/Verify-SignedManifest.ps1 @@ -0,0 +1,13 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$ManifestPath, + [Parameter(Mandatory=$true)][string]$SignaturePath, + [Parameter(Mandatory=$true)][string]$PublicKeyXmlPath +) +$bytes = [IO.File]::ReadAllBytes((Resolve-Path $ManifestPath)) +$sig = [IO.File]::ReadAllBytes((Resolve-Path $SignaturePath)) +$rsa = [System.Security.Cryptography.RSA]::Create() +$rsa.FromXmlString((Get-Content $PublicKeyXmlPath -Raw)) +$ok = $rsa.VerifyData($bytes, $sig, [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1) +if (-not $ok) { throw "Signature verification failed." } +Write-Host "Signature OK" diff --git a/updates/update-manifest.example.json b/updates/update-manifest.example.json new file mode 100644 index 0000000000000000000000000000000000000000..5fd7444fa774e794d43134c464e9ed8347ea26df --- /dev/null +++ b/updates/update-manifest.example.json @@ -0,0 +1,12 @@ +{ + "product": "JackAILocal", + "version": "0.3.1", + "channel": "stable", + "files": [ + { + "path": "webui/index.html", + "url": "https://updates.example/jackailocal/0.3.1/index.html", + "sha256": "REPLACE" + } + ] +} \ No newline at end of file diff --git a/updates/update-manifest.example.json.sig b/updates/update-manifest.example.json.sig new file mode 100644 index 0000000000000000000000000000000000000000..4f8fd7bed9272f3e5f8ed0c67b3bfa631314f884 Binary files /dev/null and b/updates/update-manifest.example.json.sig differ diff --git a/usb-root/README-FIRST.txt b/usb-root/README-FIRST.txt new file mode 100644 index 0000000000000000000000000000000000000000..56dabe122275030ffe2fe77db5c3e71a1a88582e --- /dev/null +++ b/usb-root/README-FIRST.txt @@ -0,0 +1,14 @@ +JackAILocal + +Windows portable mode: +1. Open this drive. +2. Double-click START-HERE.cmd. +3. Wait for the local AI window to open. + +Linux boot mode: +1. Shut down the computer. +2. Insert the drive. +3. Open the boot menu. +4. Choose the USB/SSD drive. + +Models must be prepared before shipment or before offline use. diff --git a/usb-root/START-HERE.cmd b/usb-root/START-HERE.cmd new file mode 100644 index 0000000000000000000000000000000000000000..1ff185755ea82e389539a7620a53aa2cafc94b42 --- /dev/null +++ b/usb-root/START-HERE.cmd @@ -0,0 +1,17 @@ +@echo off +setlocal +cd /d "%~dp0" +where powershell.exe >nul 2>nul +if errorlevel 1 ( + echo PowerShell was not found. JackAILocal cannot start. + pause + exit /b 1 +) +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0windows\Start-JackAILocal.ps1" +if errorlevel 1 ( + echo. + echo JackAILocal did not start correctly. + echo Check .jackailocal\logs or run windows\Preflight-Windows.ps1 + pause + exit /b 1 +) diff --git a/usb-root/STOP-JACKAILOCAL.cmd b/usb-root/STOP-JACKAILOCAL.cmd new file mode 100644 index 0000000000000000000000000000000000000000..8d5dce3e49f37bbb9346b76f0030ff258b20a5a6 --- /dev/null +++ b/usb-root/STOP-JACKAILOCAL.cmd @@ -0,0 +1,5 @@ +@echo off +setlocal +cd /d "%~dp0" +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0windows\Stop-JackAILocal.ps1" +pause diff --git a/usb-root/autorun.inf b/usb-root/autorun.inf new file mode 100644 index 0000000000000000000000000000000000000000..cd84a20e2271180748bb74586dc8e4214a6baa02 --- /dev/null +++ b/usb-root/autorun.inf @@ -0,0 +1,5 @@ +[AutoRun] +label=JackAILocal +icon=branding\icon.ico +open=START-HERE.cmd +action=Start JackAILocal diff --git a/webui/app-v15.js b/webui/app-v15.js new file mode 100644 index 0000000000000000000000000000000000000000..27ff65fe33f29d2c40cbf8c8a792c5529f109199 --- /dev/null +++ b/webui/app-v15.js @@ -0,0 +1,873 @@ +"use strict"; + +const $ = (id) => document.getElementById(id); +const API_BASE = window.location.protocol === "file:" ? "http://127.0.0.1:4891" : ""; +const pairFromUrl = new URLSearchParams(window.location.search).get("pair"); +if (pairFromUrl) localStorage.setItem("jackailocal.phoneToken", pairFromUrl); + +const state = { + models: [], + status: {}, + features: {}, + threads: [], + currentThread: null, + apiOk: false, + currentView: "dashboard", + language: localStorage.getItem("jackailocal.language") || "en", + scoutImageBase64: "", + voice: {}, + audioContext: null, + audioStream: null, + audioProcessor: null, + audioSource: null, + audioChunks: [], + audioSampleRate: 44100, +}; + +const i18n = { + en: { + brandSubtitle: "Offline local AI", statusStarting: "Starting", statusLocalScan: "Local scan...", + navDashboard: "Dashboard", navChat: "Chat", navScout: "SCOUT Vision", navVoice: "Voice", + navFieldManual: "Field Manual", navModels: "Models", navDocuments: "Documents", + navTransfers: "Import / Export", navPhone: "Phone Access", navBenchmark: "Benchmark", + navSettings: "Settings", navSupport: "Support", networkMode: "Network mode", + loopbackOnly: "Local access only", eyebrow: "Portable private AI", refreshButton: "Refresh", + supportButton: "Support", heroKicker: "Ready for offline work", + heroTitle: "Local AI, loaded from your JackAILocal drive.", + heroSubtitle: "Runtime, models, conversations, documents, and UI stay on this device. No cloud fallback is used.", + startChatButton: "Start chat", checkModelsButton: "Check models", runtimeCard: "Runtime", + hardwareCard: "Hardware", modelCard: "Recommended model", privacyCard: "Privacy", + autoSelectHint: "Selected from installed local models", offlineLocked: "Offline locked", + localOnlyHint: "Loopback by default", setupTitle: "Preparation state", checkRuntimeTitle: "Local API", + checkRuntimeText: "jackailocald must be running.", checkModelsTitle: "Installed model", + checkModelsText: "At least one compatible local model must be installed.", checkSecurityTitle: "Security mode", + checkSecurityText: "Shell tools remain disabled and LAN access is explicit.", + quickActionsTitle: "Quick actions", runBenchmarkQuick: "Run benchmark", openDocsQuick: "Open documents", + createSupportQuick: "Create support bundle", chat: "Chat", dashboard: "Dashboard", scout: "SCOUT Vision", + voice: "Voice", "field-manual": "Field Manual", models: "Models", documents: "Documents", + transfers: "Import / Export", phone: "Phone Access", benchmark: "Benchmark", settings: "Settings", + support: "Support", threadsTitle: "Threads", newThreadButton: "New", threadFilterPlaceholder: "Filter threads", + newConversation: "New conversation", renameButton: "Rename", deleteButton: "Delete", modelLabel: "Model", + clearButton: "New thread", promptPlaceholder: "Write your request here. Enter = send, Shift+Enter = new line.", + sendButton: "Send", chatHelperTitle: "Assistant mode", + chatHelperText: "Use Auto unless you need a specific installed model. Threads are stored on this JackAILocal drive.", + offlineNoteTitle: "Offline note", offlineNoteText: "Answers come only from reachable local backends.", + scoutTitle: "SCOUT Vision", scoutSubtitle: "Analyze an image with an installed local vision model.", + scoutPromptPlaceholder: "Describe the image and extract readable text.", analyzeButton: "Analyze locally", + scoutResultTitle: "Analysis result", voiceTitle: "Voice Mode", + voiceSubtitle: "Local speech-to-text with whisper.cpp and text-to-speech with Piper.", + sttTitle: "Speech to text", recordButton: "Record", stopButton: "Stop", transcriptPlaceholder: "Transcript", + sendToChatButton: "Send to chat", ttsTitle: "Text to speech", ttsPlaceholder: "Text to speak", + speakButton: "Speak locally", fieldManualTitle: "Field Manual", + fieldManualSubtitle: "Offline reference cards with an Ask AI action.", askAiButton: "Ask AI about this", + modelsTitle: "Models", modelsSubtitle: "Availability is read from installed local model files and Ollama.", + agentTitle: "LLM Config Agent", + agentSubtitle: "A real installed local LLM can review hardware, model selection, and client configuration. Preferred model: gemma4:12b.", + agentModelLabel: "Agent model", + agentNotesPlaceholder: "Client constraints, use case, budget, privacy needs...", + agentRunButton: "Run local agent review", + agentRunning: "Local agent review running...", + addModelTitle: "Add a model to the catalog", + addModelSubtitle: "This registers a model reference. It does not claim the model is installed.", + addModelIdPlaceholder: "Ollama model reference, example: qwen3.5:4b", addModelLabelPlaceholder: "display name", + addButton: "Add", documentsTitle: "Documents", documentsSubtitle: "Local storage in workspace/documents.", + createNoteTitle: "Create a note", docNamePlaceholder: "document-name.txt", + docContentPlaceholder: "Text, notes, procedures, prompts...", saveLocalButton: "Save locally", + localDocsTitle: "Local documents", transfersTitle: "Encrypted Import / Export", + transfersSubtitle: "Transfer threads and documents with an AES-256-GCM encrypted pack.", + exportTitle: "Export", importTitle: "Import", passphrasePlaceholder: "Passphrase, minimum 8 characters", + includeThreads: "Include threads", includeDocuments: "Include documents", includeSettings: "Include safe settings", + exportButton: "Export encrypted pack", importMode: "Import mode", importButton: "Import encrypted pack", + phoneTitle: "Phone Access", phoneSubtitle: "Explicit local-network access with a pairing token. Disabled by default.", + enablePhoneButton: "Enable Phone Access", disablePhoneButton: "Disable Phone Access", + restartRuntimeButton: "Restart runtime", + phoneRestartNote: "A runtime restart is required after changing this mode.", + benchmarkTitle: "Benchmark", benchmarkSubtitle: "Short real inference test against the selected local backend.", + runTestButton: "Run test", settingsTitle: "Settings", settingsSubtitle: "Options stay simple. Dangerous tools remain disabled.", + modeTitle: "Mode", settingsModeHint: "Simple mode is the default for non-technical users.", + simpleMode: "Simple", simpleModeDesc: "Minimal choices and automatic local model selection.", + advancedMode: "Advanced", advancedModeDesc: "Shows more controls without enabling shell or system tools.", + offlineLock: "Offline lock", offlineLockDesc: "Keeps JackAILocal local-first.", + toolsLock: "Block shell/system tools", toolsLockDesc: "Prevents agent-style system actions.", + saveButton: "Save", activeFeaturesTitle: "Active features", + activeFeaturesSubtitle: "Capabilities are detected from the running local API.", + licenseTitle: "License", licenseSubtitle: "License status of this installation. Verified locally, no internet required.", + licenseStateLicensed: "Licensed", licenseStateUnlicensed: "No license installed", + licenseStateExpired: "License expired", licenseStateInvalid: "License invalid", + licenseStateNoKey: "Verification key missing", licenseEdition: "Edition", + licenseCustomer: "Licensed to", licenseExpires: "Expires", licensePerpetual: "Perpetual", + legalDocsHint: "Legal documents are in the legal/ and licenses/ folders of this installation.", + supportTitle: "Support", supportSubtitle: "Local diagnostics without conversations or documents.", + prepareReportButton: "Create report", auto: "Auto", runtimeUnavailable: "Runtime unavailable", + startWithCommand: "Start with START-HERE.cmd", apiNotResponding: "The local API is not responding:", + noModelsTitle: "No installed models", noModelsBody: "Use the builder to install a compatible local model.", + modelCatalogError: "Cannot load model catalog:", responsePending: "Generating local response...", + localError: "Local error:", backendHint: "Check that a local backend and model are installed.", + chatCleared: "New local thread created.", adding: "Adding...", saving: "Saving...", + noLocalDocuments: "No local documents.", apiUnavailable: "API unavailable.", benchmarkRunning: "Benchmark running...", + benchmarkFailed: "Benchmark failed:", savingSettings: "Saving...", preparing: "Preparing...", + readyMessage: "JackAILocal is ready. Create a thread and use Auto to select an installed model.", + recommended: "recommended", installed: "installed", unavailable: "not installed", bytes: "bytes", + unknown: "unknown", apiReady: "Ready", apiOffline: "Not running", pass: "Ready", fail: "Missing", + locked: "Locked", recording: "Recording...", processing: "Processing locally...", available: "Available", + notAvailable: "Not available", confirmDelete: "Delete this thread?", renamePrompt: "Thread name", + fileRequired: "Choose a file first.", imageRequired: "Choose an image first.", importComplete: "Import complete.", + on: "On", off: "Off", + eulaTitle: "End User License Agreement", eulaAcceptButton: "I accept", + eulaLoading: "Loading the license agreement...", + eulaFallback: "The license agreement could not be loaded from the local API. The full text is available in the legal/ folder of this installation (EULA_EN.md, EULA_FR.md). By continuing you accept its terms.", + installLicenseButton: "Install license file", licenseInstalling: "Installing license...", + }, + fr: { + brandSubtitle: "IA locale hors ligne", statusStarting: "Démarrage", statusLocalScan: "Analyse locale...", + navDashboard: "Tableau", navChat: "Chat", navScout: "Vision SCOUT", navVoice: "Voix", + navFieldManual: "Manuel terrain", navModels: "Modèles", navDocuments: "Documents", + navTransfers: "Import / Export", navPhone: "Accès téléphone", navBenchmark: "Benchmark", + navSettings: "Réglages", navSupport: "Support", networkMode: "Mode réseau", + loopbackOnly: "Accès local seulement", eyebrow: "IA privée portable", refreshButton: "Rafraîchir", + supportButton: "Support", heroKicker: "Prêt pour le travail hors ligne", + heroTitle: "IA locale, chargée depuis votre support JackAILocal.", + heroSubtitle: "Runtime, modèles, conversations, documents et UI restent sur cet appareil. Aucun repli cloud n'est utilisé.", + startChatButton: "Démarrer le chat", checkModelsButton: "Vérifier les modèles", runtimeCard: "Runtime", + hardwareCard: "Matériel", modelCard: "Modèle recommandé", privacyCard: "Confidentialité", + autoSelectHint: "Sélectionné parmi les modèles locaux installés", offlineLocked: "Hors ligne verrouillé", + localOnlyHint: "Loopback par défaut", setupTitle: "État de préparation", checkRuntimeTitle: "API locale", + checkRuntimeText: "jackailocald doit être démarré.", checkModelsTitle: "Modèle installé", + checkModelsText: "Au moins un modèle local compatible doit être installé.", checkSecurityTitle: "Mode sécurité", + checkSecurityText: "Les outils shell restent désactivés et l'accès LAN est explicite.", + quickActionsTitle: "Actions rapides", runBenchmarkQuick: "Lancer benchmark", openDocsQuick: "Ouvrir documents", + createSupportQuick: "Créer bundle support", chat: "Chat", dashboard: "Tableau", scout: "Vision SCOUT", + voice: "Voix", "field-manual": "Manuel terrain", models: "Modèles", documents: "Documents", + transfers: "Import / Export", phone: "Accès téléphone", benchmark: "Benchmark", settings: "Réglages", + support: "Support", threadsTitle: "Fils", newThreadButton: "Nouveau", threadFilterPlaceholder: "Filtrer les fils", + newConversation: "Nouvelle conversation", renameButton: "Renommer", deleteButton: "Supprimer", modelLabel: "Modèle", + clearButton: "Nouveau fil", promptPlaceholder: "Écrivez votre demande. Entrée = envoyer, Maj+Entrée = nouvelle ligne.", + sendButton: "Envoyer", chatHelperTitle: "Mode assistant", + chatHelperText: "Utilisez Auto sauf si vous voulez un modèle installé précis. Les fils sont stockés sur ce support JackAILocal.", + offlineNoteTitle: "Note hors ligne", offlineNoteText: "Les réponses viennent uniquement des backends locaux accessibles.", + scoutTitle: "Vision SCOUT", scoutSubtitle: "Analysez une image avec un modèle vision local installé.", + scoutPromptPlaceholder: "Décrivez l'image et extrayez le texte lisible.", analyzeButton: "Analyser localement", + scoutResultTitle: "Résultat d'analyse", voiceTitle: "Mode voix", + voiceSubtitle: "Reconnaissance vocale locale avec whisper.cpp et synthèse avec Piper.", + sttTitle: "Parole vers texte", recordButton: "Enregistrer", stopButton: "Arrêter", transcriptPlaceholder: "Transcription", + sendToChatButton: "Envoyer au chat", ttsTitle: "Texte vers parole", ttsPlaceholder: "Texte à prononcer", + speakButton: "Prononcer localement", fieldManualTitle: "Manuel terrain", + fieldManualSubtitle: "Cartes de référence hors ligne avec action Ask AI.", askAiButton: "Demander à l'IA", + modelsTitle: "Modèles", modelsSubtitle: "La disponibilité est lue depuis Ollama et les fichiers locaux installés.", + agentTitle: "Agent LLM config", + agentSubtitle: "Un vrai LLM local installe peut revoir le materiel, la selection des modeles et la configuration client. Modele prefere : gemma4:12b.", + agentModelLabel: "Modele agent", + agentNotesPlaceholder: "Contraintes client, usage, budget, besoins de confidentialite...", + agentRunButton: "Lancer la revue agent locale", + agentRunning: "Revue agent locale en cours...", + addModelTitle: "Ajouter un modèle au catalogue", + addModelSubtitle: "Cette action inscrit une référence. Elle ne prétend pas que le modèle est installé.", + addModelIdPlaceholder: "Référence Ollama, exemple : qwen3.5:4b", addModelLabelPlaceholder: "nom affiché", + addButton: "Ajouter", documentsTitle: "Documents", documentsSubtitle: "Stockage local dans workspace/documents.", + createNoteTitle: "Créer une note", docNamePlaceholder: "nom-du-document.txt", + docContentPlaceholder: "Texte, notes, procédures, prompts...", saveLocalButton: "Sauver localement", + localDocsTitle: "Documents locaux", transfersTitle: "Import / Export chiffré", + transfersSubtitle: "Transférez fils et documents avec un pack chiffré AES-256-GCM.", + exportTitle: "Exporter", importTitle: "Importer", passphrasePlaceholder: "Phrase secrète, minimum 8 caractères", + includeThreads: "Inclure les fils", includeDocuments: "Inclure les documents", includeSettings: "Inclure les réglages sûrs", + exportButton: "Exporter le pack chiffré", importMode: "Mode d'import", importButton: "Importer le pack chiffré", + phoneTitle: "Accès téléphone", phoneSubtitle: "Accès réseau local explicite avec jeton de pairage. Désactivé par défaut.", + enablePhoneButton: "Activer l'accès téléphone", disablePhoneButton: "Désactiver l'accès téléphone", + restartRuntimeButton: "Redémarrer le runtime", + phoneRestartNote: "Un redémarrage du runtime est requis après ce changement.", + benchmarkTitle: "Benchmark", benchmarkSubtitle: "Test réel court contre le backend local sélectionné.", + runTestButton: "Lancer test", settingsTitle: "Réglages", settingsSubtitle: "Les options restent simples. Les outils dangereux restent désactivés.", + modeTitle: "Mode", settingsModeHint: "Le mode simple est le défaut pour les utilisateurs non techniques.", + simpleMode: "Simple", simpleModeDesc: "Choix minimaux et sélection automatique du modèle local.", + advancedMode: "Avancé", advancedModeDesc: "Affiche plus de contrôles sans activer le shell ni les outils système.", + offlineLock: "Verrou hors ligne", offlineLockDesc: "Garde JackAILocal local-first.", + toolsLock: "Bloquer shell/outils système", toolsLockDesc: "Empêche les actions système de type agent.", + saveButton: "Enregistrer", activeFeaturesTitle: "Fonctions actives", + activeFeaturesSubtitle: "Les capacités sont détectées depuis l'API locale.", + licenseTitle: "Licence", licenseSubtitle: "État de la licence de cette installation. Vérifiée localement, sans Internet.", + licenseStateLicensed: "Sous licence", licenseStateUnlicensed: "Aucune licence installée", + licenseStateExpired: "Licence expirée", licenseStateInvalid: "Licence invalide", + licenseStateNoKey: "Clé de vérification absente", licenseEdition: "Édition", + licenseCustomer: "Licencié à", licenseExpires: "Expire", licensePerpetual: "Perpétuelle", + legalDocsHint: "Les documents légaux sont dans les dossiers legal/ et licenses/ de cette installation.", + supportTitle: "Support", supportSubtitle: "Diagnostic local sans conversations ni documents.", + prepareReportButton: "Créer rapport", auto: "Auto", runtimeUnavailable: "Runtime non disponible", + startWithCommand: "Démarrer avec START-HERE.cmd", apiNotResponding: "L'API locale ne répond pas :", + noModelsTitle: "Aucun modèle installé", noModelsBody: "Utilisez le builder pour installer un modèle local compatible.", + modelCatalogError: "Impossible de charger le catalogue :", responsePending: "Réponse locale en cours...", + localError: "Erreur locale :", backendHint: "Vérifiez qu'un backend local et un modèle sont installés.", + chatCleared: "Nouveau fil local créé.", adding: "Ajout...", saving: "Sauvegarde...", + noLocalDocuments: "Aucun document local.", apiUnavailable: "API non disponible.", benchmarkRunning: "Benchmark en cours...", + benchmarkFailed: "Benchmark échoué :", savingSettings: "Enregistrement...", preparing: "Préparation...", + readyMessage: "JackAILocal est prêt. Créez un fil et utilisez Auto pour sélectionner un modèle installé.", + recommended: "recommandé", installed: "installé", unavailable: "non installé", bytes: "octets", + unknown: "inconnu", apiReady: "Prêt", apiOffline: "Non démarré", pass: "Prêt", fail: "Manquant", + locked: "Verrouillé", recording: "Enregistrement...", processing: "Traitement local...", available: "Disponible", + notAvailable: "Non disponible", confirmDelete: "Supprimer ce fil ?", renamePrompt: "Nom du fil", + fileRequired: "Choisissez d'abord un fichier.", imageRequired: "Choisissez d'abord une image.", importComplete: "Import terminé.", + on: "Actif", off: "Inactif", + eulaTitle: "Contrat de licence utilisateur final", eulaAcceptButton: "J'accepte", + eulaLoading: "Chargement du contrat de licence...", + eulaFallback: "Le contrat de licence n'a pas pu être chargé depuis l'API locale. Le texte complet est disponible dans le dossier legal/ de cette installation (EULA_FR.md, EULA_EN.md). En continuant, vous en acceptez les termes.", + installLicenseButton: "Installer le fichier de licence", licenseInstalling: "Installation de la licence...", + }, +}; + +function t(key) { return i18n[state.language]?.[key] || i18n.en[key] || key; } +function apiUrl(path) { return /^https?:\/\//i.test(path) ? path : `${API_BASE}${path}`; } +function escapeHtml(value) { + return String(value ?? "").replace(/[&<>'"]/g, (character) => ({ + "&": "&", "<": "<", ">": ">", "'": "'", "\"": """, + }[character])); +} +function contentText(value) { return typeof value === "string" ? value : JSON.stringify(value, null, 2); } +function viewTitleKey(view) { return view; } + +async function requestJson(url, options = {}) { + const token = localStorage.getItem("jackailocal.phoneToken"); + const response = await fetch(apiUrl(url), { + headers: { + "Content-Type": "application/json", + ...(token ? { "X-JackAILocal-Token": token } : {}), + ...(options.headers || {}), + }, + ...options, + }); + const text = await response.text(); + let payload = {}; + try { payload = text ? JSON.parse(text) : {}; } catch { payload = { raw: text }; } + if (!response.ok) throw new Error(payload.error || payload.message || `${response.status} ${response.statusText}`); + return payload; +} +const getJson = (url) => requestJson(url); +const postJson = (url, body) => requestJson(url, { method: "POST", body: JSON.stringify(body || {}) }); +const patchJson = (url, body) => requestJson(url, { method: "PATCH", body: JSON.stringify(body || {}) }); +const deleteJson = (url) => requestJson(url, { method: "DELETE" }); + +function applyLanguage(language) { + state.language = language; + localStorage.setItem("jackailocal.language", language); + document.documentElement.lang = language; + document.querySelectorAll("[data-i18n]").forEach((element) => { element.textContent = t(element.dataset.i18n); }); + document.querySelectorAll("[data-i18n-placeholder]").forEach((element) => { element.placeholder = t(element.dataset.i18nPlaceholder); }); + document.querySelectorAll(".lang-btn").forEach((button) => button.classList.toggle("active", button.dataset.lang === language)); + $("viewTitle").textContent = t(viewTitleKey(state.currentView)); + renderModels(); + renderThreads(); + renderFeatures(); + renderLicense(); +} + +function switchView(name) { + state.currentView = name; + document.querySelectorAll(".nav").forEach((button) => button.classList.toggle("active", button.dataset.view === name)); + document.querySelectorAll(".view").forEach((view) => view.classList.remove("active")); + $(`view-${name}`)?.classList.add("active"); + $("viewTitle").textContent = t(viewTitleKey(name)); +} + +function showError(message) { + $("errorBanner").textContent = message; + $("errorBanner").classList.remove("hidden"); +} +function clearError() { $("errorBanner").classList.add("hidden"); $("errorBanner").textContent = ""; } +function setStatus(kind, title, subtitle) { + $("statusBox").className = `status-card ${kind === "ok" ? "state-ok" : kind === "error" ? "state-error" : "state-loading"}`; + $("runtimeStatus").textContent = title; + $("hardwareLine").textContent = subtitle || ""; +} +function updateCheck(id, ok) { $(id)?.classList.toggle("ok", Boolean(ok)); $(id)?.classList.toggle("error", !ok); } +function formatHardware(hardware = {}) { return `RAM ${hardware.ram_gb ?? "?"} GB · VRAM ${hardware.vram_gb ?? "?"} GB · CPU ${hardware.cpu_threads ?? "?"}`; } +function formatGpu(hardware = {}) { return `${hardware.gpu_vendor || t("unknown")}${hardware.gpu_name ? ` · ${hardware.gpu_name}` : ""}`; } + +async function loadStatus() { + try { + state.status = await getJson("/api/status"); + state.apiOk = true; + setStatus("ok", `${t("apiReady")} · ${state.status.backend}`, `${formatHardware(state.status.hardware)} · ${state.status.bind}`); + clearError(); + } catch (error) { + state.apiOk = false; + state.status = {}; + setStatus("error", t("runtimeUnavailable"), t("startWithCommand")); + showError(`${t("apiNotResponding")} ${error.message}`); + } + renderDashboard(); +} + +function selectRecommendedModel() { + const hardware = state.status.hardware || {}; + return state.models.filter((model) => model.available) + .filter((model) => Number(hardware.ram_gb ?? 0) >= Number(model.min_ram_gb ?? 0)) + .filter((model) => Number(hardware.vram_gb ?? 0) >= Number(model.min_vram_gb ?? 0)) + .filter((model) => (model.features || []).includes("chat")) + .at(-1) || null; +} + +function renderDashboard() { + const recommended = selectRecommendedModel(); + $("runtimeMetric").textContent = state.apiOk ? t("apiReady") : t("apiOffline"); + $("backendMetric").textContent = state.status.backend || "unavailable"; + $("hardwareMetric").textContent = formatHardware(state.status.hardware || {}); + $("gpuMetric").textContent = formatGpu(state.status.hardware || {}); + $("recommendedMetric").textContent = recommended?.label || recommended?.id || t("auto"); + updateCheck("checkRuntime", state.apiOk); + updateCheck("checkModels", state.models.some((model) => model.available)); + updateCheck("checkSecurity", state.features.shell_tools === false); + const bind = state.status.bind || "127.0.0.1:4891"; + document.querySelector(".security-pill").textContent = bind; + document.querySelector(".mini-copy").textContent = bind.startsWith("127.") ? t("loopbackOnly") : t("navPhone"); +} + +async function loadModels() { + try { + const payload = await getJson("/api/models"); + state.models = payload.models || []; + renderModels(); + } catch (error) { + state.models = []; + renderModels(); + showError(`${t("modelCatalogError")} ${error.message}`); + } +} + +function renderModels() { + const recommended = selectRecommendedModel(); + $("modelSelect").innerHTML = `` + state.models.map((model) => ( + `` + )).join(""); + const agentModels = state.models + .filter((model) => model.available) + .filter((model) => Number(model.params_b ?? 999) <= 32) + .filter((model) => (model.features || []).includes("chat")); + const preferredAgent = agentModels.find((model) => model.id === "gemma_config_agent_12b" || model.ollama === "gemma4:12b"); + $("agentModelSelect").innerHTML = `` + agentModels.map((model) => { + const preferred = preferredAgent?.id === model.id ? " - Gemma 4 12B" : ""; + return ``; + }).join(""); + if (preferredAgent) $("agentModelSelect").value = preferredAgent.id; + $("modelChips").innerHTML = state.models.filter((model) => model.available).map((model) => ( + `${escapeHtml(model.label || model.id)}` + )).join("") || `${escapeHtml(t("noModelsTitle"))}`; + $("modelCards").innerHTML = state.models.length ? state.models.map((model) => { + const availableClass = model.available ? "availability-on" : "availability-off"; + const availableText = model.available ? t("installed") : t("unavailable"); + const features = (model.features || []).map((feature) => `${escapeHtml(feature)}`).join(""); + return `
    + ${recommended?.id === model.id ? `${escapeHtml(t("recommended"))}` : ""} +

    ${escapeHtml(model.label || model.id)}

    +
    ${escapeHtml(model.id)}
    +

    ${escapeHtml(model.notes || model.description || "")}

    +
    ${escapeHtml(availableText)}RAM ${escapeHtml(model.min_ram_gb ?? 0)}+ GBVRAM ${escapeHtml(model.min_vram_gb ?? 0)}+ GB
    +
    ${features}
    +
    Ollama: ${escapeHtml(model.ollama || "n/a")}
    +
    `; + }).join("") : `

    ${escapeHtml(t("noModelsTitle"))}

    ${escapeHtml(t("noModelsBody"))}

    `; + renderDashboard(); +} + +async function loadFeatures() { + try { state.features = await getJson("/api/features"); } catch { state.features = {}; } + renderFeatures(); +} + +async function loadLicense() { + try { state.license = await getJson("/api/license"); } catch { state.license = null; } + renderLicense(); +} +function renderLicense() { + const target = $("licenseInfo"); + if (!target) return; + const payload = state.license; + const warning = $("licenseWarning"); + if (!payload) { + target.innerHTML = `
    ${escapeHtml(t("apiUnavailable"))}
    `; + warning?.classList.add("hidden"); + return; + } + const stateKeys = { + licensed: "licenseStateLicensed", + unlicensed: "licenseStateUnlicensed", + expired: "licenseStateExpired", + invalid: "licenseStateInvalid", + no_public_key: "licenseStateNoKey", + }; + const stateLabel = t(stateKeys[payload.state] || "licenseStateUnlicensed"); + if (warning) { + const showWarning = payload.state !== "licensed" && payload.state !== "no_public_key"; + warning.textContent = showWarning ? stateLabel : ""; + warning.classList.toggle("hidden", !showWarning); + } + const rows = [ + `
    ${escapeHtml(stateLabel)}${escapeHtml(payload.licensed ? t("on") : t("off"))}
    `, + ]; + const info = payload.license; + if (info) { + rows.push(`
    ${escapeHtml(t("licenseEdition"))}${escapeHtml(info.edition || "")}
    `); + rows.push(`
    ${escapeHtml(t("licenseCustomer"))}${escapeHtml(info.customer_name || "")}
    `); + const expires = info.expires_at ? new Date(info.expires_at).toLocaleDateString() : t("licensePerpetual"); + rows.push(`
    ${escapeHtml(t("licenseExpires"))}${escapeHtml(expires)}
    `); + } + target.innerHTML = rows.join(""); +} + +async function runAgentReview() { + const recommended = selectRecommendedModel(); + $("agentOut").textContent = t("agentRunning"); + const payload = await postJson("/api/agent/recommend", { + language: state.language, + package_goal: "Runtime WebUI client configuration review", + hardware: state.status.hardware || {}, + default_model: recommended || null, + candidate_models: state.models, + content_packs: null, + current_config: { + runtime_status: state.status, + features: state.features, + selected_chat_model: $("modelSelect").value, + phone_access: state.status.phone_access || null, + }, + customer_notes: $("agentNotes").value.trim(), + agent_model: $("agentModelSelect").value, + max_params_b: 32, + agent_max_tokens: 192, + agent_timeout_seconds: 180, + }); + const parsed = payload.agent_json && typeof payload.agent_json === "object" ? payload.agent_json : null; + $("agentOut").textContent = JSON.stringify({ + agent_model: payload.agent_model, + selected_model: parsed?.selected_model_ref || parsed?.selected_model_id || null, + confidence: parsed?.confidence || null, + summary: parsed?.human_summary || payload.raw_content, + risk_flags: parsed?.risk_flags || [], + next_steps: parsed?.next_steps || [], + raw: parsed ? undefined : payload.raw_content, + }, null, 2); +} +function renderFeatures() { + const entries = Object.entries(state.features || {}); + $("featureList").innerHTML = entries.length ? entries.map(([key, value]) => ( + `
    ${escapeHtml(key)}${escapeHtml(value ? t("on") : t("off"))}
    ` + )).join("") : `
    ${escapeHtml(t("apiUnavailable"))}
    `; +} + +async function loadThreads(filter = "") { + try { + const payload = await getJson(`/api/threads${filter ? `?q=${encodeURIComponent(filter)}` : ""}`); + state.threads = payload.threads || []; + renderThreads(); + if (!state.currentThread && state.threads.length) await selectThread(state.threads[0].id); + } catch (error) { $("threadList").innerHTML = `
    ${escapeHtml(error.message)}
    `; } +} +function renderThreads() { + $("threadList").innerHTML = state.threads.length ? state.threads.map((thread) => ( + `` + )).join("") : `
    ${escapeHtml(t("newConversation"))}
    `; + document.querySelectorAll("[data-thread-id]").forEach((button) => button.addEventListener("click", () => selectThread(button.dataset.threadId))); +} +async function createThread() { + const payload = await postJson("/api/threads", { title: t("newConversation"), model: $("modelSelect").value }); + state.currentThread = payload.thread; + await loadThreads($("threadFilter").value.trim()); + renderChat(); + return state.currentThread; +} +async function selectThread(id) { + const payload = await getJson(`/api/threads/${encodeURIComponent(id)}`); + state.currentThread = payload.thread; + if (state.currentThread.model && [...$("modelSelect").options].some((option) => option.value === state.currentThread.model && !option.disabled)) { + $("modelSelect").value = state.currentThread.model; + } + renderThreads(); + renderChat(); +} +function renderChat() { + $("chatLog").innerHTML = ""; + $("activeThreadTitle").textContent = state.currentThread?.title || t("newConversation"); + const messages = state.currentThread?.messages || []; + if (!messages.length) addMessage("system", t("readyMessage")); + messages.forEach((message) => addMessage(message.role, contentText(message.content))); +} +function addMessage(role, content) { + const wrapper = document.createElement("div"); + wrapper.className = `msg ${role}`; + const bubble = document.createElement("div"); + bubble.className = "bubble"; + bubble.textContent = content; + wrapper.appendChild(bubble); + $("chatLog").appendChild(wrapper); + $("chatLog").scrollTop = $("chatLog").scrollHeight; + return bubble; +} +async function sendTextToThread(text) { + if (!text.trim()) return; + if (!state.currentThread) await createThread(); + addMessage("user", text.trim()); + const pending = addMessage("assistant", t("responsePending")); + $("sendBtn").disabled = true; + try { + const payload = await postJson(`/api/threads/${encodeURIComponent(state.currentThread.id)}/messages`, { + content: text.trim(), model: $("modelSelect").value, + }); + state.currentThread = payload.thread; + renderChat(); + await loadThreads($("threadFilter").value.trim()); + } catch (error) { + pending.textContent = `${t("localError")} ${error.message}\n\n${t("backendHint")}`; + } finally { $("sendBtn").disabled = false; } +} +async function sendPrompt() { + const prompt = $("prompt").value; + if (!prompt.trim()) return; + $("prompt").value = ""; + await sendTextToThread(prompt); +} + +async function loadDocs() { + try { + const payload = await getJson("/api/documents"); + const documents = payload.documents || []; + $("docList").innerHTML = documents.length ? documents.map((doc) => ( + `
    ${escapeHtml(doc.name)}
    ${escapeHtml(doc.content.length)} ${escapeHtml(t("bytes"))}
    ` + )).join("") : `
    ${escapeHtml(t("noLocalDocuments"))}
    `; + } catch { $("docList").innerHTML = `
    ${escapeHtml(t("apiUnavailable"))}
    `; } +} + +async function loadVoiceStatus() { + try { + state.voice = await getJson("/api/voice/status"); + $("sttStatus").textContent = `${state.voice.stt.available ? t("available") : t("notAvailable")} · whisper.cpp`; + $("ttsStatus").textContent = `${state.voice.tts.available ? t("available") : t("notAvailable")} · Piper`; + $("startRecording").disabled = !state.voice.stt.available; + $("speakText").disabled = !state.voice.tts.available; + } catch (error) { $("voiceOut").textContent = error.message; } +} + +async function startRecording() { + try { + state.audioStream = await navigator.mediaDevices.getUserMedia({ audio: true }); + state.audioContext = new AudioContext(); + state.audioSampleRate = state.audioContext.sampleRate; + state.audioSource = state.audioContext.createMediaStreamSource(state.audioStream); + state.audioProcessor = state.audioContext.createScriptProcessor(4096, 1, 1); + state.audioChunks = []; + state.audioProcessor.onaudioprocess = (event) => state.audioChunks.push(new Float32Array(event.inputBuffer.getChannelData(0))); + state.audioSource.connect(state.audioProcessor); + state.audioProcessor.connect(state.audioContext.destination); + $("startRecording").disabled = true; + $("stopRecording").disabled = false; + $("voiceOut").textContent = t("recording"); + } catch (error) { $("voiceOut").textContent = error.message; } +} +async function stopRecording() { + $("stopRecording").disabled = true; + state.audioProcessor?.disconnect(); + state.audioSource?.disconnect(); + state.audioStream?.getTracks().forEach((track) => track.stop()); + await state.audioContext?.close(); + $("voiceOut").textContent = t("processing"); + try { + const wav = encodeWav(state.audioChunks, state.audioSampleRate); + const payload = await postJson("/api/voice/transcribe", { + audio_base64: arrayBufferToBase64(wav), format: "wav", language: state.language, + }); + $("voiceTranscript").value = payload.text || ""; + $("voiceOut").textContent = JSON.stringify(payload, null, 2); + } catch (error) { $("voiceOut").textContent = error.message; } + await loadVoiceStatus(); +} + +async function loadFieldManual() { + try { + const payload = await getJson("/api/field-manual"); + $("fieldManualWarning").textContent = state.language === "fr" ? payload.warning_fr : payload.warning_en; + $("fieldManualWarning").classList.remove("hidden"); + $("fieldManualCards").innerHTML = (payload.cards || []).map((card) => { + const title = state.language === "fr" ? card.title_fr : card.title_en; + const summary = state.language === "fr" ? card.summary_fr : card.summary_en; + const steps = state.language === "fr" ? card.steps_fr : card.steps_en; + const warnings = state.language === "fr" ? card.warnings_fr : card.warnings_en; + return `

    ${escapeHtml(title)}

    ${escapeHtml(summary)}

    +
      ${steps.map((step) => `
    1. ${escapeHtml(step)}
    2. `).join("")}
    +
      ${warnings.map((warning) => `
    • ${escapeHtml(warning)}
    • `).join("")}
    +
    `; + }).join(""); + document.querySelectorAll(".ask-manual").forEach((button) => button.addEventListener("click", async () => { + const card = payload.cards.find((item) => item.id === button.dataset.cardId); + const title = state.language === "fr" ? card.title_fr : card.title_en; + const summary = state.language === "fr" ? card.summary_fr : card.summary_en; + const steps = state.language === "fr" ? card.steps_fr : card.steps_en; + const warnings = state.language === "fr" ? card.warnings_fr : card.warnings_en; + const prompt = `${title}\n\n${summary}\n\n${steps.join("\n")}\n\nWarnings:\n${warnings.join("\n")}\n\nAnswer my question using this local reference card, remain cautious, and state when professional help is required.`; + switchView("chat"); + await sendTextToThread(prompt); + })); + } catch (error) { $("fieldManualCards").innerHTML = `
    ${escapeHtml(error.message)}
    `; } +} + +async function loadPhone() { + try { + const status = await getJson("/api/phone"); + const display = { ...status, qr_svg: status.qr_svg ? "[QR generated]" : null }; + $("phoneState").textContent = JSON.stringify(display, null, 2); + $("phoneQr").innerHTML = status.qr_svg || ""; + $("enablePhone").disabled = !status.available || status.enabled; + $("disablePhone").disabled = !status.enabled; + } catch (error) { $("phoneState").textContent = error.message; } +} + +async function createSupportBundle(outputId) { + $(outputId).textContent = t("preparing"); + try { + const payload = await postJson("/api/support/bundle", {}); + downloadBase64(payload.filename, payload.data_base64, payload.mime_type); + $(outputId).textContent = JSON.stringify({ ...payload, data_base64: "[downloaded]" }, null, 2); + } catch (error) { $(outputId).textContent = error.message; } +} + +async function refreshAll() { + await loadStatus(); + await Promise.all([loadModels(), loadFeatures(), loadLicense(), loadDocs(), loadThreads(), loadVoiceStatus(), loadFieldManual(), loadPhone()]); +} + +function bindEvents() { + document.querySelectorAll(".nav").forEach((button) => button.addEventListener("click", () => switchView(button.dataset.view))); + document.querySelectorAll("[data-action-view]").forEach((button) => button.addEventListener("click", () => switchView(button.dataset.actionView))); + document.querySelectorAll(".lang-btn").forEach((button) => button.addEventListener("click", () => { applyLanguage(button.dataset.lang); loadFieldManual(); })); + $("refreshAll").addEventListener("click", refreshAll); + $("openSupport").addEventListener("click", () => switchView("support")); + $("sendBtn").addEventListener("click", sendPrompt); + $("prompt").addEventListener("keydown", (event) => { if (event.key === "Enter" && !event.shiftKey) { event.preventDefault(); sendPrompt(); } }); + $("clearChat").addEventListener("click", async () => { + try { + await createThread(); + addMessage("system", t("chatCleared")); + } catch (error) { showError(error.message); } + }); + $("newThread").addEventListener("click", async () => { + try { await createThread(); } catch (error) { showError(error.message); } + }); + $("threadFilter").addEventListener("input", () => loadThreads($("threadFilter").value.trim())); + $("renameThread").addEventListener("click", async () => { + if (!state.currentThread) return; + const title = window.prompt(t("renamePrompt"), state.currentThread.title); + if (!title) return; + try { + const payload = await patchJson(`/api/threads/${encodeURIComponent(state.currentThread.id)}`, { title }); + state.currentThread = payload.thread; + await loadThreads($("threadFilter").value.trim()); + renderChat(); + } catch (error) { showError(error.message); } + }); + $("deleteThread").addEventListener("click", async () => { + if (!state.currentThread || !window.confirm(t("confirmDelete"))) return; + try { + await deleteJson(`/api/threads/${encodeURIComponent(state.currentThread.id)}`); + state.currentThread = null; + await loadThreads($("threadFilter").value.trim()); + if (!state.currentThread) renderChat(); + } catch (error) { showError(error.message); } + }); + $("refreshModels").addEventListener("click", loadModels); + $("runAgentReview").addEventListener("click", async () => { + const button = $("runAgentReview"); + button.disabled = true; + try { await runAgentReview(); } + catch (error) { $("agentOut").textContent = error.message; } + finally { button.disabled = false; } + }); + $("reloadDocs").addEventListener("click", loadDocs); + $("supportBundleQuick").addEventListener("click", () => createSupportBundle("dashboardOut")); + $("supportBundle").addEventListener("click", () => createSupportBundle("supportOut")); + $("addModelBtn").addEventListener("click", async () => { + $("addModelOut").textContent = t("adding"); + try { + $("addModelOut").textContent = JSON.stringify(await postJson("/api/models/add", { id: $("addModelId").value.trim(), label: $("addModelLabel").value.trim() }), null, 2); + await loadModels(); + } catch (error) { $("addModelOut").textContent = error.message; } + }); + $("saveDoc").addEventListener("click", async () => { + $("docOut").textContent = t("saving"); + try { + $("docOut").textContent = JSON.stringify(await postJson("/api/documents", { name: $("docName").value.trim(), content: $("docContent").value }), null, 2); + await loadDocs(); + } catch (error) { $("docOut").textContent = error.message; } + }); + $("runBenchmark").addEventListener("click", async () => { + const button = $("runBenchmark"); + button.disabled = true; + $("benchmarkOut").textContent = t("benchmarkRunning"); + try { $("benchmarkOut").textContent = JSON.stringify(await postJson("/api/benchmark/run", { model: $("modelSelect").value }), null, 2); } + catch (error) { $("benchmarkOut").textContent = `${t("benchmarkFailed")} ${error.message}`; } + finally { button.disabled = false; } + }); + $("saveSettings").addEventListener("click", async () => { + $("settingsOut").textContent = t("savingSettings"); + try { + $("settingsOut").textContent = JSON.stringify(await postJson("/api/settings", { + mode: document.querySelector("input[name=mode]:checked").value, + offline_lock: $("offlineLock").checked, tools_lock: $("toolsLock").checked, + }), null, 2); + } catch (error) { $("settingsOut").textContent = error.message; } + }); + $("scoutImage").addEventListener("change", async () => { + const file = $("scoutImage").files[0]; + if (!file) return; + state.scoutImageBase64 = await fileToDataUrl(file); + $("scoutPreview").src = state.scoutImageBase64; + $("scoutPreview").classList.remove("hidden"); + }); + $("runScout").addEventListener("click", async () => { + if (!state.scoutImageBase64) { $("scoutOut").textContent = t("imageRequired"); return; } + const button = $("runScout"); + button.disabled = true; + $("scoutOut").textContent = t("processing"); + try { + const payload = await postJson("/api/vision/analyze", { image_base64: state.scoutImageBase64, prompt: $("scoutPrompt").value, model: null }); + $("scoutOut").textContent = payload.content || JSON.stringify(payload, null, 2); + } catch (error) { $("scoutOut").textContent = error.message; } + finally { button.disabled = false; } + }); + $("refreshVoice").addEventListener("click", loadVoiceStatus); + $("startRecording").addEventListener("click", startRecording); + $("stopRecording").addEventListener("click", stopRecording); + $("sendTranscript").addEventListener("click", () => { $("prompt").value = $("voiceTranscript").value; switchView("chat"); }); + $("speakText").addEventListener("click", async () => { + $("voiceOut").textContent = t("processing"); + try { + const payload = await postJson("/api/voice/synthesize", { text: $("ttsText").value }); + $("ttsAudio").src = `data:${payload.mime_type};base64,${payload.audio_base64}`; + $("ttsAudio").classList.remove("hidden"); + await $("ttsAudio").play(); + $("voiceOut").textContent = JSON.stringify({ ...payload, audio_base64: "[audio loaded]" }, null, 2); + } catch (error) { $("voiceOut").textContent = error.message; } + }); + $("exportPack").addEventListener("click", async () => { + const button = $("exportPack"); + button.disabled = true; + $("transferOut").textContent = t("preparing"); + try { + const payload = await postJson("/api/packs/export", { + passphrase: $("exportPassphrase").value, include_threads: $("exportThreads").checked, + include_documents: $("exportDocuments").checked, include_settings: $("exportSettings").checked, + }); + downloadBase64(payload.filename, payload.data_base64, payload.mime_type); + $("transferOut").textContent = JSON.stringify({ ...payload, data_base64: "[downloaded]" }, null, 2); + } catch (error) { $("transferOut").textContent = error.message; } + finally { button.disabled = false; } + }); + $("importPack").addEventListener("click", async () => { + const file = $("importPackFile").files[0]; + if (!file) { $("transferOut").textContent = t("fileRequired"); return; } + const button = $("importPack"); + button.disabled = true; + $("transferOut").textContent = t("processing"); + try { + const data = arrayBufferToBase64(await file.arrayBuffer()); + const payload = await postJson("/api/packs/import", { passphrase: $("importPassphrase").value, data_base64: data, mode: $("importMode").value }); + $("transferOut").textContent = JSON.stringify(payload, null, 2); + state.currentThread = null; + await Promise.all([loadThreads(), loadDocs()]); + } catch (error) { $("transferOut").textContent = error.message; } + finally { button.disabled = false; } + }); + $("refreshPhone").addEventListener("click", loadPhone); + $("enablePhone").addEventListener("click", async () => { + try { + await postJson("/api/phone", { enabled: true }); + await loadPhone(); + } catch (error) { + $("phoneState").textContent = error.message; + showError(error.message); + } + }); + $("disablePhone").addEventListener("click", async () => { + try { + await postJson("/api/phone", { enabled: false }); + await loadPhone(); + } catch (error) { + $("phoneState").textContent = error.message; + showError(error.message); + } + }); + $("restartRuntime").addEventListener("click", async () => { + $("phoneState").textContent = t("processing"); + try { + await postJson("/api/runtime/restart", {}); + window.setTimeout(() => window.location.reload(), 2200); + } catch (error) { $("phoneState").textContent = error.message; } + }); + $("installLicense").addEventListener("click", async () => { + const button = $("installLicense"); + const message = $("licenseMessage"); + const file = $("licenseFile").files[0]; + if (!file) { message.textContent = t("fileRequired"); return; } + button.disabled = true; + message.textContent = t("licenseInstalling"); + try { + const content = await file.text(); + await postJson("/api/license", { content }); + message.textContent = ""; + $("licenseFile").value = ""; + await loadLicense(); + } catch (error) { message.textContent = error.message; } + finally { button.disabled = false; } + }); + $("eulaAccept").addEventListener("click", () => { + localStorage.setItem("jackailocal.eulaAccepted", new Date().toISOString()); + $("eulaOverlay").classList.add("hidden"); + }); +} + +async function showEulaIfNeeded() { + if (localStorage.getItem("jackailocal.eulaAccepted")) return; + const overlay = $("eulaOverlay"); + if (!overlay) return; + overlay.classList.remove("hidden"); + $("eulaText").textContent = t("eulaLoading"); + try { + const payload = await getJson(`/api/legal/eula?lang=${encodeURIComponent(state.language)}`); + $("eulaText").textContent = payload.markdown || t("eulaFallback"); + } catch { + $("eulaText").textContent = t("eulaFallback"); + } +} + +function fileToDataUrl(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(file); }); } +function arrayBufferToBase64(buffer) { + const bytes = new Uint8Array(buffer); let binary = ""; + for (let index = 0; index < bytes.length; index += 0x8000) binary += String.fromCharCode(...bytes.subarray(index, index + 0x8000)); + return btoa(binary); +} +function downloadBase64(filename, base64, mimeType) { + const link = document.createElement("a"); + link.href = `data:${mimeType || "application/octet-stream"};base64,${base64}`; + link.download = filename; + document.body.appendChild(link); link.click(); link.remove(); +} +function encodeWav(chunks, sampleRate) { + const length = chunks.reduce((sum, chunk) => sum + chunk.length, 0); + const samples = new Float32Array(length); let offset = 0; + chunks.forEach((chunk) => { samples.set(chunk, offset); offset += chunk.length; }); + const buffer = new ArrayBuffer(44 + samples.length * 2); + const view = new DataView(buffer); + const write = (at, text) => [...text].forEach((char, index) => view.setUint8(at + index, char.charCodeAt(0))); + write(0, "RIFF"); view.setUint32(4, 36 + samples.length * 2, true); write(8, "WAVE"); + write(12, "fmt "); view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, 1, true); + view.setUint32(24, sampleRate, true); view.setUint32(28, sampleRate * 2, true); view.setUint16(32, 2, true); view.setUint16(34, 16, true); + write(36, "data"); view.setUint32(40, samples.length * 2, true); + samples.forEach((sample, index) => view.setInt16(44 + index * 2, Math.max(-1, Math.min(1, sample)) * 0x7fff, true)); + return buffer; +} + +(async function init() { + bindEvents(); + applyLanguage(state.language); + setStatus("loading", t("statusStarting"), t("statusLocalScan")); + showEulaIfNeeded(); + await refreshAll(); + renderChat(); + setInterval(loadStatus, 15000); +})(); diff --git a/webui/index.html b/webui/index.html new file mode 100644 index 0000000000000000000000000000000000000000..33544193fd3c90e7b8b382bb29698ca7fd442cc7 --- /dev/null +++ b/webui/index.html @@ -0,0 +1,408 @@ + + + + + + + JackAILocal + + + +
    + + +
    +
    +
    +
    Portable private AI
    +

    Dashboard

    +
    +
    + + +
    +
    + + + +
    +
    +
    +
    Ready for offline work
    +

    Local AI, loaded from your USB drive.

    +

    JackAILocal runs the launcher, runtime, models, documents, and UI locally. No cloud is required after preparation.

    +
    +
    + + +
    +
    + +
    +
    + Runtime + ... + ... +
    +
    + Hardware + ... + ... +
    +
    + Recommended model + Auto + Selected by local policy +
    +
    + Privacy + Offline locked + Loopback only +
    +
    + +
    +
    +

    Preparation state

    +
    +
    Local APIjackailocald must be running.
    +
    Model catalogThe USB must contain configured model weights.
    +
    Security modeShell tools and LAN exposure remain disabled.
    +
    +
    +
    +

    Quick actions

    +
    + + + +
    +
    
    +          
    +
    +
    + +
    +
    + +
    +
    + New conversation +
    + + +
    +
    +
    +
    + +
    + + + +
    +
    +
    + +
    +
    + +
    +
    +

    SCOUT Vision

    Analyze an image with an installed local vision model.

    +
    +
    +
    + + + + +
    +
    +

    Analysis result

    +
    
    +          
    +
    +
    + +
    +
    +

    Voice Mode

    Local speech-to-text with whisper.cpp and text-to-speech with Piper.

    + +
    +
    +
    +

    Speech to text

    +

    +
    + + +
    + + +
    +
    +

    Text to speech

    +

    + + + +
    
    +          
    +
    +
    + +
    +
    +

    Field Manual

    Offline reference cards with an Ask AI action.

    +
    + +
    +
    + +
    +
    +

    Models

    The catalog shows what is configured. Model weights must be present in models/.

    + +
    +
    +
    +

    LLM Config Agent

    +

    A real installed local LLM can review hardware, model selection, and client configuration. Preferred model: gemma4:12b.

    +
    + + + +
    +
    
    +        
    +
    +

    Add a model to the catalog

    +

    This registers a model. It does not download weights and it does not bypass builder validation.

    +
    + + + +
    +
    
    +        
    +
    + +
    +
    +

    Documents

    Local storage in workspace/documents. No arbitrary disk access.

    + +
    +
    +
    +

    Create a note

    + + + +
    
    +          
    +
    +

    Local documents

    +
    +
    +
    +
    + +
    +
    +

    Encrypted Import / Export

    Transfer threads and documents with an AES-256-GCM encrypted pack.

    +
    +
    +
    +

    Export

    + +
    + + + +
    + +
    +
    +

    Import

    + + + + +
    +
    +
    
    +      
    + +
    +
    +

    Phone Access

    Explicit local-network access with a pairing token. Disabled by default.

    + +
    +
    +
    +
    +
    + + + +
    +

    A runtime restart is required after changing this mode.

    +
    +
    +
    +
    +
    +
    + +
    +
    +

    Benchmark

    Short test to validate loading, latency, and a real response.

    + +
    +
    
    +      
    + +
    +
    +
    +

    Settings

    +

    Options stay simple. Dangerous features remain disabled.

    +
    +
    +
    +
    +

    Mode

    +

    Simple mode is the default for non-technical users. Advanced mode keeps unsafe tools blocked.

    +
    + + + + +
    +
    + +
    +
    
    +          
    +
    +

    Active features

    +

    Runtime capabilities are read from the local API when it is running.

    +
    +
    +
    +

    License

    +

    License status of this installation. Verified locally, no internet required.

    +
    +
    + + +
    +
    +

    Legal documents are in the legal/ and licenses/ folders of this installation.

    +
    +
    +
    + +
    +
    +

    Support

    Local diagnostics without conversations by default.

    + +
    +
    
    +      
    +
    +
    + + + + + + diff --git a/webui/styles.css b/webui/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..78885a58dfd5c4c018b2a41274f263163dc8f398 --- /dev/null +++ b/webui/styles.css @@ -0,0 +1,535 @@ +:root { + --bg: #070a0f; + --bg-soft: #0b111a; + --panel: rgba(17, 24, 39, 0.88); + --panel-solid: #111827; + --panel-strong: #182235; + --line: rgba(148, 163, 184, 0.20); + --line-strong: rgba(148, 163, 184, 0.34); + --text: #edf2f7; + --muted: #9aa8bc; + --muted-2: #718096; + --accent: #bfff4f; + --accent-2: #6ee7f9; + --danger: #fb7185; + --ok: #34d399; + --warn: #fbbf24; + --button: #f8fafc; + --button-text: #0f172a; + --shadow: 0 24px 80px rgba(0, 0, 0, 0.34); + --radius-lg: 28px; + --radius-md: 18px; + --radius-sm: 12px; +} + +* { box-sizing: border-box; } +html, body { min-height: 100%; } +body { + margin: 0; + color: var(--text); + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif; + background: + radial-gradient(circle at 12% 0%, rgba(191, 255, 79, 0.14), transparent 27%), + radial-gradient(circle at 88% 9%, rgba(110, 231, 249, 0.10), transparent 30%), + linear-gradient(145deg, #05070b 0%, #0b111a 55%, #06080d 100%); +} + +button, input, textarea, select { font: inherit; } +*, *::before, *::after { box-sizing: border-box; } +button { user-select: none; } + +.app-shell { + display: grid; + grid-template-columns: 304px minmax(0, 1fr); + min-height: 100vh; +} + +.sidebar { + position: sticky; + top: 0; + height: 100vh; + display: flex; + flex-direction: column; + gap: 16px; + padding: 20px; + border-right: 1px solid var(--line); + background: rgba(7, 10, 15, 0.72); + backdrop-filter: blur(18px); +} + +.brand-card { + display: flex; + align-items: center; + gap: 13px; + padding: 12px; + border: 1px solid var(--line); + border-radius: 22px; + background: linear-gradient(180deg, rgba(255,255,255,0.055), rgba(255,255,255,0.025)); +} + +.brand-mark { + width: 48px; + height: 48px; + border-radius: 17px; + display: grid; + place-items: center; + font-size: 24px; + font-weight: 950; + color: #0a0f16; + background: linear-gradient(135deg, var(--accent), #ffffff 62%, var(--accent-2)); + box-shadow: 0 12px 30px rgba(191,255,79,0.16); +} + +.brand-name { font-weight: 900; letter-spacing: 0.2px; } +.brand-subtitle, .muted { color: var(--muted); } +.small, small { font-size: 12px; } + +.language-switch { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; +} + +.lang-btn, .nav, button, .security-pill { + border: 1px solid var(--line); + border-radius: var(--radius-sm); +} + +.lang-btn { + padding: 9px 10px; + color: var(--muted); + background: rgba(15, 23, 42, 0.76); + cursor: pointer; + font-weight: 800; +} + +.lang-btn.active { + color: var(--button-text); + background: var(--button); +} + +.status-card { + padding: 14px; + border: 1px solid var(--line); + border-radius: 18px; + background: var(--panel); +} + +.status-header { display: flex; align-items: center; gap: 9px; } +.status-dot { + width: 11px; + height: 11px; + border-radius: 99px; + background: var(--warn); + box-shadow: 0 0 18px rgba(251, 191, 36, 0.7); +} +.state-ok .status-dot { background: var(--ok); box-shadow: 0 0 18px rgba(52, 211, 153, 0.7); } +.state-error .status-dot { background: var(--danger); box-shadow: 0 0 18px rgba(251, 113, 133, 0.7); } +.status-title { font-weight: 850; } +.status-subtitle { margin-top: 6px; color: var(--muted); font-size: 12px; line-height: 1.35; } + +.nav-list { display: grid; gap: 7px; } +.nav { + appearance: none; + display: grid; + grid-template-columns: 24px 1fr; + align-items: center; + gap: 9px; + width: 100%; + padding: 12px 13px; + text-align: left; + background: transparent; + color: #dbe7f3; + cursor: pointer; + font-weight: 780; +} +.nav span:first-child { color: var(--muted); text-align: center; } +.nav:hover { background: rgba(148, 163, 184, 0.08); } +.nav.active { + background: linear-gradient(90deg, rgba(191,255,79,0.15), rgba(110,231,249,0.04)); + border-color: rgba(191,255,79,0.34); + box-shadow: inset 3px 0 0 var(--accent); +} + +.sidebar-footer { margin-top: auto; } +.mini-label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.14em; } +.security-pill { + display: inline-flex; + margin-top: 7px; + padding: 6px 10px; + background: rgba(52, 211, 153, 0.10); + border-color: rgba(52, 211, 153, 0.28); + color: #bbf7d0; + font-weight: 800; +} +.mini-copy { margin-top: 6px; color: var(--muted); font-size: 12px; } + +.main { min-width: 0; padding: 24px; overflow: auto; } +.app-topbar { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 18px; + margin-bottom: 18px; +} +.eyebrow, .hero-kicker { + color: var(--accent); + font-size: 12px; + font-weight: 900; + text-transform: uppercase; + letter-spacing: 0.13em; +} +#viewTitle { margin: 4px 0 0; font-size: clamp(30px, 4vw, 46px); letter-spacing: -0.04em; } +.top-actions { display: flex; gap: 10px; } + +.view { display: none; animation: enter 0.16s ease-out; } +.view.active { display: block; } +@keyframes enter { from { opacity: 0.75; transform: translateY(6px); } to { opacity: 1; transform: none; } } + +.banner { + margin-bottom: 16px; + padding: 13px 15px; + border-radius: 16px; + border: 1px solid rgba(251,113,133,0.42); + background: rgba(127, 29, 29, 0.22); + color: #fecdd3; + line-height: 1.45; +} +.hidden { display: none !important; } + +.hero-panel, .panel, .metric-card { + border: 1px solid var(--line); + background: var(--panel); + box-shadow: var(--shadow); +} +.hero-panel { + display: flex; + justify-content: space-between; + align-items: stretch; + gap: 24px; + padding: clamp(22px, 4vw, 34px); + border-radius: var(--radius-lg); + background: + linear-gradient(135deg, rgba(191,255,79,0.13), transparent 42%), + linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.025)); +} +.hero-panel h2 { margin: 8px 0 8px; font-size: clamp(30px, 4vw, 54px); line-height: 0.96; letter-spacing: -0.055em; max-width: 850px; } +.hero-panel p { max-width: 760px; margin: 0; color: var(--muted); line-height: 1.55; } +.hero-actions { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; } + +.metric-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 14px; + margin: 16px 0; +} +.metric-card { padding: 16px; border-radius: var(--radius-md); } +.metric-label { display: block; color: var(--muted); font-size: 12px; margin-bottom: 10px; } +.metric-card strong { display: block; font-size: 21px; line-height: 1.1; } +.metric-card small { display: block; margin-top: 7px; color: var(--muted); } + +.split-hero, .split, .chat-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(320px, 0.48fr); + gap: 14px; +} +.panel { padding: 18px; border-radius: var(--radius-md); } +.panel h2, .section-header h2 { margin: 0 0 7px; font-size: 22px; letter-spacing: -0.02em; } +.panel p, .section-header p { margin: 0; color: var(--muted); line-height: 1.5; } + +.checklist { display: grid; gap: 10px; } +.check-row { + display: grid; + grid-template-columns: 13px 1fr; + gap: 12px; + align-items: start; + padding: 12px; + border: 1px solid var(--line); + border-radius: 14px; + background: rgba(15, 23, 42, 0.42); +} +.check-row > span { width: 11px; height: 11px; margin-top: 5px; border-radius: 50%; background: var(--warn); box-shadow: 0 0 14px rgba(251,191,36,0.6); } +.check-row.ok > span { background: var(--ok); box-shadow: 0 0 14px rgba(52,211,153,0.6); } +.check-row.error > span { background: var(--danger); box-shadow: 0 0 14px rgba(251,113,133,0.6); } +.check-row strong { display: block; } +.check-row small { display: block; color: var(--muted); margin-top: 3px; } + +.quick-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; } + +.section-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 20px; + margin-bottom: 16px; +} + +button { + border: 0; + padding: 11px 15px; + border-radius: 13px; + cursor: pointer; + font-weight: 900; + transition: transform 0.11s ease, filter 0.11s ease, border-color 0.11s ease; +} +button:hover { transform: translateY(-1px); filter: brightness(1.04); } +button:active { transform: translateY(0); } +button:disabled { opacity: 0.55; cursor: not-allowed; transform: none; } +button:focus-visible, .nav:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } +.primary { color: #061016; background: linear-gradient(135deg, var(--accent), #ffffff); } +.secondary { color: var(--text); background: rgba(148, 163, 184, 0.10); border: 1px solid var(--line); } + +input:not([type="checkbox"]):not([type="radio"]), textarea, select { + width: 100%; + color: var(--text); + background: rgba(3, 7, 18, 0.72); + border: 1px solid var(--line-strong); + border-radius: 13px; + padding: 11px 12px; + outline: none; +} +input[type="checkbox"], input[type="radio"] { + width: 18px; + height: 18px; + min-width: 18px; + margin: 2px 0 0; + flex: 0 0 auto; + accent-color: var(--accent); +} +input:focus, textarea:focus, select:focus { border-color: rgba(191,255,79,0.55); box-shadow: 0 0 0 3px rgba(191,255,79,0.09); } +textarea { resize: vertical; } + +.chat-layout { grid-template-columns: minmax(0, 1fr) 320px; align-items: start; } +.chat-panel, .side-panel { + border: 1px solid var(--line); + border-radius: var(--radius-lg); + background: var(--panel); + box-shadow: var(--shadow); +} +.chat-panel { overflow: hidden; } +.chat-log { + height: calc(100vh - 285px); + min-height: 420px; + padding: 20px; + overflow: auto; + background: rgba(2, 6, 23, 0.35); +} +.msg { display: flex; margin: 12px 0; } +.msg.user { justify-content: flex-end; } +.bubble { + max-width: min(820px, 84%); + padding: 13px 15px; + border-radius: 17px; + line-height: 1.52; + white-space: pre-wrap; + word-break: break-word; +} +.user .bubble { color: #ffffff; background: linear-gradient(135deg, #2558a8, #1d4ed8); border-bottom-right-radius: 6px; } +.assistant .bubble { background: rgba(24, 34, 53, 0.96); border: 1px solid var(--line); border-bottom-left-radius: 6px; } +.system .bubble { background: rgba(191,255,79,0.09); border: 1px solid rgba(191,255,79,0.24); color: #eaffb3; } + +.composer-card { padding: 14px; border-top: 1px solid var(--line); } +.composer-card textarea { min-height: 96px; } +.composer-actions { display: grid; grid-template-columns: minmax(150px, 1fr) auto auto; gap: 10px; align-items: end; margin-top: 10px; } +.select-label { display: grid; gap: 5px; color: var(--muted); font-size: 12px; } + +.side-panel { padding: 18px; position: sticky; top: 24px; } +.side-panel h2 { margin: 0 0 8px; } +.side-panel p { color: var(--muted); line-height: 1.45; } +.chip-list { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0; } +.chip, .pill { + display: inline-flex; + align-items: center; + gap: 5px; + border: 1px solid var(--line); + border-radius: 999px; + padding: 5px 9px; + color: #dbe7f3; + background: rgba(15, 23, 42, 0.68); + font-size: 12px; + font-weight: 760; +} +.chip.recommended { border-color: rgba(191,255,79,0.44); color: #eaff99; } +.hint-box { display: grid; gap: 5px; margin-top: 16px; padding: 13px; border: 1px solid rgba(110,231,249,0.22); border-radius: 16px; background: rgba(110,231,249,0.075); } +.hint-box span { color: var(--muted); line-height: 1.4; } + +.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(265px, 1fr)); gap: 14px; margin-bottom: 16px; } +.model-card { position: relative; min-height: 220px; } +.model-card.recommended { border-color: rgba(191,255,79,0.45); } +.recommended-badge { position: absolute; right: 14px; top: 14px; color: #eaff99; border: 1px solid rgba(191,255,79,0.36); border-radius: 999px; padding: 4px 8px; font-size: 11px; font-weight: 900; background: rgba(191,255,79,0.08); } +.model-title { padding-right: 112px; } +.model-card .desc { min-height: 42px; color: var(--muted); } +.model-meta { display: flex; flex-wrap: wrap; gap: 7px; margin: 12px 0; } +.progress-track { height: 7px; border-radius: 999px; overflow: hidden; background: rgba(148,163,184,0.14); margin: 14px 0 8px; } +.progress-bar { height: 100%; width: 60%; background: linear-gradient(90deg, var(--accent), var(--accent-2)); border-radius: inherit; } + +.form-grid { display: grid; grid-template-columns: 1fr 1fr auto; gap: 10px; } +.doc-textarea { min-height: 220px; margin: 10px 0; } +.doc-list { display: grid; gap: 9px; } +.doc-item { padding: 13px; border: 1px solid var(--line); border-radius: 14px; background: rgba(15,23,42,0.44); } +.settings-grid { + display: grid; + grid-template-columns: minmax(0, 1.2fr) minmax(320px, 0.55fr); + gap: 14px; + align-items: start; +} +.settings-panel { min-height: 0; } +.option-stack { display: grid; gap: 10px; margin: 16px 0; } +.option-card { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 14px; + border: 1px solid var(--line); + border-radius: 16px; + background: rgba(15, 23, 42, 0.42); + cursor: pointer; +} +.option-card:hover { border-color: rgba(191,255,79,0.30); background: rgba(15, 23, 42, 0.62); } +.option-copy { display: grid; gap: 4px; min-width: 0; } +.option-copy strong { display: block; color: var(--text); } +.option-copy small { display: block; color: var(--muted); line-height: 1.35; } +.settings-actions { display: flex; gap: 10px; align-items: center; margin-bottom: 12px; } +.feature-list { display: grid; gap: 8px; margin-top: 16px; } +.feature-row { display: flex; justify-content: space-between; gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--line); align-items: center; } +.feature-row span:first-child { color: var(--muted); word-break: break-word; } + +.chat-workspace { + display: grid; + grid-template-columns: 260px minmax(0, 1fr) 300px; + gap: 14px; + align-items: start; +} +.thread-panel { + min-height: calc(100vh - 150px); + padding: 14px; + border: 1px solid var(--line); + border-radius: var(--radius-lg); + background: var(--panel); + box-shadow: var(--shadow); +} +.thread-header, .chat-thread-bar, .button-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; +} +.thread-header h2 { margin: 0; } +.thread-list { display: grid; gap: 8px; margin-top: 12px; max-height: calc(100vh - 260px); overflow: auto; } +.thread-item { + width: 100%; + display: grid; + gap: 4px; + padding: 11px; + text-align: left; + color: var(--text); + background: rgba(15, 23, 42, 0.42); + border: 1px solid var(--line); +} +.thread-item.active { border-color: rgba(191,255,79,0.45); background: rgba(191,255,79,0.08); } +.thread-item small { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.chat-thread-bar { padding: 12px 14px; border-bottom: 1px solid var(--line); } +.compact { padding: 7px 10px; font-size: 12px; } +.danger-button { color: #fecdd3; border-color: rgba(251,113,133,0.34); } +.image-preview { width: 100%; max-height: 420px; object-fit: contain; margin: 12px 0; border: 1px solid var(--line); border-radius: 14px; background: #020617; } +.audio-player { width: 100%; margin: 14px 0; } +.qr-panel { display: grid; place-items: center; min-height: 280px; background: #ffffff; } +.qr-panel svg { width: min(280px, 100%); height: auto; } +.compact-stack { margin: 12px 0; } +.compact-stack .option-card { padding: 10px 12px; } +.field-card .field-steps { padding-left: 20px; color: var(--muted); line-height: 1.5; } +.field-card .warning-list { color: #fbbf24; } +.availability-on { color: #bbf7d0; border-color: rgba(52,211,153,0.35); } +.availability-off { color: #fecdd3; border-color: rgba(251,113,133,0.35); } +.security-pill.availability-off { background: rgba(251, 113, 133, 0.10); } +.license-install { display: grid; gap: 10px; margin: 16px 0 12px; } + +.eula-overlay { + position: fixed; + inset: 0; + z-index: 1000; + display: grid; + place-items: center; + padding: 24px; + background: rgba(2, 6, 23, 0.82); + backdrop-filter: blur(8px); +} +.eula-modal { + width: min(720px, 100%); + max-height: min(82vh, 760px); + display: flex; + flex-direction: column; + gap: 14px; +} +.eula-modal h2 { margin: 0; } +.eula-text { + flex: 1; + min-height: 200px; + overflow: auto; + padding: 14px; + border: 1px solid var(--line); + border-radius: var(--radius-sm); + background: rgba(2, 6, 23, 0.55); + color: var(--text); + white-space: pre-wrap; + line-height: 1.55; + font-size: 13px; +} +.eula-actions { display: flex; justify-content: flex-end; } + +.terminal, pre { + color: #dbe7f3; + background: rgba(2, 6, 23, 0.72); + border: 1px solid var(--line); + border-radius: 16px; + padding: 14px; + white-space: pre-wrap; + overflow: auto; + line-height: 1.45; +} +.small-terminal { min-height: 60px; max-height: 220px; } +code { color: #eaff99; } + +@media (max-width: 1120px) { + .metric-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .chat-layout, .chat-workspace, .split-hero, .split, .settings-grid { grid-template-columns: 1fr; } + .side-panel { position: static; } + .thread-panel { min-height: auto; } + .thread-list { max-height: 240px; } +} + +@media (max-width: 820px) { + .app-shell { display: block; } + .sidebar { position: relative; height: auto; } + .main { padding: 16px; } + .app-topbar, .hero-panel, .section-header { display: block; } + .top-actions, .hero-actions { margin-top: 12px; } + .composer-actions, .form-grid { grid-template-columns: 1fr; } + .chat-log { height: 420px; min-height: 320px; } + .metric-grid { grid-template-columns: 1fr; } +} + +@media (prefers-color-scheme: light) { + :root { + --bg: #f5f7fb; + --bg-soft: #ffffff; + --panel: rgba(255, 255, 255, 0.90); + --panel-solid: #ffffff; + --panel-strong: #eef2f7; + --line: rgba(15, 23, 42, 0.13); + --line-strong: rgba(15, 23, 42, 0.22); + --text: #111827; + --muted: #64748b; + --muted-2: #7b8797; + --button: #111827; + --button-text: #ffffff; + --shadow: 0 24px 70px rgba(15, 23, 42, 0.10); + } + body { background: linear-gradient(145deg, #f7fafc, #eef4ff 60%, #f8fafc); } + .sidebar { background: rgba(255,255,255,0.76); } + input:not([type="checkbox"]):not([type="radio"]), textarea, select, .terminal, pre, .eula-text { background: rgba(255,255,255,0.86); color: var(--text); } + .assistant .bubble { background: #f1f5f9; } + .system .bubble { color: #3f4d08; } + .security-pill { color: #065f46; } + .nav { color: #1f2937; } +} diff --git a/windows/Add-Model.ps1 b/windows/Add-Model.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..79bdf41511460b748c3877c01e83310136e01b16 --- /dev/null +++ b/windows/Add-Model.ps1 @@ -0,0 +1,14 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$Model, + [ValidateSet("ollama","gguf")][string]$Backend="ollama" +) +$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path +if ($Backend -eq "ollama") { + $OllamaExe = Join-Path $Root "backends\ollama\windows\ollama.exe" + if (!(Test-Path $OllamaExe)) { throw "Missing Ollama binary." } + $env:OLLAMA_MODELS = Join-Path $Root "models\ollama" + & $OllamaExe pull $Model + exit $LASTEXITCODE +} +throw "GGUF add requires a signed model pack or a URL in manifest/model-sources.json." diff --git a/windows/Install-Voice.ps1 b/windows/Install-Voice.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..91841b61d6f8aa16f2017cede9a94ed192f2dc34 --- /dev/null +++ b/windows/Install-Voice.ps1 @@ -0,0 +1,68 @@ +[CmdletBinding()] +param( + [string]$TargetRoot = "" +) + +$ErrorActionPreference = "Stop" +if (-not $TargetRoot) { $TargetRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path } +if (!(Test-Path $TargetRoot)) { throw "Target root does not exist: $TargetRoot" } +$TargetRoot = (Resolve-Path $TargetRoot).Path +$assetConfigPath = Join-Path $TargetRoot "config\voice-assets.json" +if (!(Test-Path $assetConfigPath)) { throw "Voice asset configuration is missing: $assetConfigPath" } +$assetConfig = Get-Content -Raw $assetConfigPath | ConvertFrom-Json +$assets = $assetConfig.windows_x64 +$shared = $assetConfig.shared + +$cache = Join-Path $TargetRoot ".jackailocal-builder\voice-cache" +$whisperDir = Join-Path $TargetRoot "backends\whisper.cpp\windows" +$piperDir = Join-Path $TargetRoot "backends\piper\windows" +$whisperModelDir = Join-Path $TargetRoot "models\whisper" +$piperModelDir = Join-Path $TargetRoot "models\piper" +New-Item -ItemType Directory -Force -Path $cache,$whisperDir,$piperDir,$whisperModelDir,$piperModelDir | Out-Null + +function Download-Verified([string]$Url, [string]$Sha256, [string]$Destination) { + if (Test-Path $Destination) { + $existing = (Get-FileHash -Algorithm SHA256 -Path $Destination).Hash.ToLowerInvariant() + if ($existing -eq $Sha256.ToLowerInvariant()) { return } + Remove-Item -LiteralPath $Destination -Force + } + Write-Host "Downloading $Url" -ForegroundColor Cyan + Invoke-WebRequest -Uri $Url -OutFile $Destination -UseBasicParsing + $actual = (Get-FileHash -Algorithm SHA256 -Path $Destination).Hash.ToLowerInvariant() + if ($actual -ne $Sha256.ToLowerInvariant()) { + Remove-Item -LiteralPath $Destination -Force + throw "SHA256 verification failed for $Url. Expected $Sha256, got $actual." + } +} + +function Copy-ArchiveContents([string]$Archive, [string]$Destination, [string]$RequiredFile) { + $extract = Join-Path $cache ([IO.Path]::GetFileNameWithoutExtension($Archive)) + if (Test-Path $extract) { Remove-Item -LiteralPath $extract -Recurse -Force } + New-Item -ItemType Directory -Force -Path $extract | Out-Null + Expand-Archive -Path $Archive -DestinationPath $extract -Force + $required = Get-ChildItem -Path $extract -Recurse -File -Filter $RequiredFile | Select-Object -First 1 + if (-not $required) { throw "Required file $RequiredFile was not found in $Archive." } + $sourceDir = Split-Path $required.FullName -Parent + Copy-Item -Path (Join-Path $sourceDir "*") -Destination $Destination -Recurse -Force +} + +$whisperArchive = Join-Path $cache "whisper-bin-x64.zip" +$piperArchive = Join-Path $cache "piper-windows-amd64.zip" +$whisperModel = Join-Path $whisperModelDir "ggml-base.bin" +$piperVoice = Join-Path $piperModelDir "en_US-libritts_r-medium.onnx" +$piperVoiceConfig = Join-Path $piperModelDir "en_US-libritts_r-medium.onnx.json" + +if (!(Test-Path (Join-Path $whisperDir "whisper-cli.exe"))) { + Download-Verified $assets.whisper.archive_url $assets.whisper.archive_sha256 $whisperArchive + Copy-ArchiveContents $whisperArchive $whisperDir "whisper-cli.exe" +} +if (!(Test-Path (Join-Path $piperDir "piper.exe"))) { + Download-Verified $assets.piper.archive_url $assets.piper.archive_sha256 $piperArchive + Copy-ArchiveContents $piperArchive $piperDir "piper.exe" +} + +Download-Verified $shared.whisper.model_url $shared.whisper.model_sha256 $whisperModel +Download-Verified $shared.piper.voice_url $shared.piper.voice_sha256 $piperVoice +Download-Verified $shared.piper.voice_config_url $shared.piper.voice_config_sha256 $piperVoiceConfig + +Write-Host "Voice assets installed and verified under $TargetRoot" -ForegroundColor Green diff --git a/windows/Integrity-Check.ps1 b/windows/Integrity-Check.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..93b07f886d962d6e26d79071fc212449af64433d --- /dev/null +++ b/windows/Integrity-Check.ps1 @@ -0,0 +1,24 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$Root, + [ValidateSet("Warn","Fail")][string]$Mode="Fail" +) +$manifest = Join-Path $Root "manifest\sha256-manifest.json" +if (!(Test-Path $manifest)) { + if ($Mode -eq "Fail") { throw "Missing sha256 manifest." } + Write-Warning "Missing sha256 manifest." + exit 0 +} +$data = Get-Content $manifest -Raw | ConvertFrom-Json +$bad = @() +foreach ($entry in $data.files) { + $p = Join-Path $Root $entry.path + if (!(Test-Path $p)) { $bad += "missing: $($entry.path)"; continue } + $h = (Get-FileHash -Algorithm SHA256 $p).Hash.ToLowerInvariant() + if ($h -ne $entry.sha256.ToLowerInvariant()) { $bad += "hash mismatch: $($entry.path)" } +} +if ($bad.Count -gt 0) { + $msg = "Integrity check failed:`n" + ($bad -join "`n") + if ($Mode -eq "Fail") { throw $msg } + Write-Warning $msg +} diff --git a/windows/JackAILocal-USB-Builder.ps1 b/windows/JackAILocal-USB-Builder.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..6d73f974bdbd6888bab7fb717423ecdbb6f1c28c --- /dev/null +++ b/windows/JackAILocal-USB-Builder.ps1 @@ -0,0 +1,629 @@ +[CmdletBinding()] +param( + [string]$ManifestPath = "", + [string]$TargetDrive = "", + [ValidateSet("USB", "SSD", "LocalFolder")][string]$TargetMode = "USB", + [ValidateSet("DefaultOnly", "RecommendedSet", "AllAllowed")][string]$ModelInstallMode = "RecommendedSet", + [switch]$SkipModelDownload, + [switch]$CleanTarget +) + +$ErrorActionPreference = "Stop" + +# Force console and script output to UTF-8 to prevent Python decoding crashes +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 +$OutputEncoding = [System.Text.Encoding]::UTF8 + +function Write-Title([string]$Text) { + Write-Host "" + Write-Host "============================================================" -ForegroundColor DarkGray + Write-Host $Text -ForegroundColor Cyan + Write-Host "============================================================" -ForegroundColor DarkGray +} + +function Ensure-Directory([string]$Path) { + New-Item -ItemType Directory -Force -Path $Path | Out-Null +} + +function Resolve-PackageRoot { + $dir = $PSScriptRoot + while ($dir -and (Test-Path $dir)) { + if (Test-Path (Join-Path $dir "config")) { + return (Resolve-Path $dir).Path + } + $parent = Split-Path -Parent $dir + if ($parent -eq $dir) { break } + $dir = $parent + } + return (Resolve-Path (Join-Path $PSScriptRoot "..")).Path +} + +function Verify-ManifestSignature([string]$ManifestFile) { + $packageRoot = Resolve-PackageRoot + $fullManifestPath = [System.IO.Path]::GetFullPath($ManifestFile) + $fullPackageRoot = [System.IO.Path]::GetFullPath($packageRoot) + if ($fullManifestPath.StartsWith($fullPackageRoot, [System.StringComparison]::OrdinalIgnoreCase)) { + Write-Host "Manifest is local to the package. Skipping signature check." + return + } + + $sigPath = $ManifestFile + ".sig" + if (!(Test-Path $sigPath)) { + throw "Security error: Manifest is loaded from outside the package root ($ManifestFile), but no signature file was found at $sigPath. For security, external manifests must be signed." + } + + $pubKeyXmlPath = Join-Path $packageRoot "config\update-public-key.xml" + if (!(Test-Path $pubKeyXmlPath)) { + throw "Security error: Public key not found at $pubKeyXmlPath. Cannot verify signature." + } + + $bytes = [IO.File]::ReadAllBytes($fullManifestPath) + $sig = [IO.File]::ReadAllBytes((Resolve-Path $sigPath).Path) + $rsa = [System.Security.Cryptography.RSA]::Create() + $rsa.FromXmlString((Get-Content $pubKeyXmlPath -Raw)) + $ok = $rsa.VerifyData($bytes, $sig, [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1) + if (-not $ok) { + throw "Security error: Signature verification failed for external manifest $ManifestFile." + } + Write-Host "External manifest signature verified successfully." -ForegroundColor Green +} + + +function Get-FreeGb([string]$TargetPath) { + $fullPath = [System.IO.Path]::GetFullPath($TargetPath) + $root = [System.IO.Path]::GetPathRoot($fullPath) + if ([string]::IsNullOrWhiteSpace($root)) { throw "Could not determine a drive for target path: $TargetPath" } + $drive = [System.IO.DriveInfo]::new($root) + return [math]::Round($drive.AvailableFreeSpace / 1GB, 1) +} + +function Assert-SafeTarget([string]$SourceRoot, [string]$TargetRoot) { + $source = [System.IO.Path]::GetFullPath($SourceRoot).TrimEnd("\", "/") + $target = [System.IO.Path]::GetFullPath($TargetRoot).TrimEnd("\", "/") + $separator = [System.IO.Path]::DirectorySeparatorChar + $comparison = [System.StringComparison]::OrdinalIgnoreCase + if ($source.Equals($target, $comparison) -or + $source.StartsWith($target + $separator, $comparison) -or + $target.StartsWith($source + $separator, $comparison)) { + throw "Unsafe target path: source package '$source' and target '$target' overlap. Choose a separate folder or drive." + } +} + +function Get-DriveCandidates([string]$Mode) { + $logical = Get-CimInstance Win32_LogicalDisk | Where-Object { + $_.DeviceID -ne $env:SystemDrive -and $_.Size -gt 0 -and ( + ($Mode -eq "USB" -and $_.DriveType -eq 2) -or + ($Mode -eq "SSD" -and $_.DriveType -in @(2, 3)) + ) + } + $logical | ForEach-Object { + [pscustomobject]@{ + Drive = "$($_.DeviceID)\" + Label = $_.VolumeName + Type = if ($_.DriveType -eq 2) { "Removable" } else { "Fixed/External" } + SizeGb = [math]::Round($_.Size / 1GB, 1) + FreeGb = [math]::Round($_.FreeSpace / 1GB, 1) + } + } +} + +function Select-TargetDrive([string]$ExistingTarget, [string]$Mode) { + if ($ExistingTarget) { + $fullPath = [System.IO.Path]::GetFullPath($ExistingTarget) + if ($Mode -eq "LocalFolder") { + Ensure-Directory $fullPath + return (Resolve-Path $fullPath).Path + } + if (!(Test-Path $fullPath)) { throw "Target drive does not exist: $fullPath" } + $resolved = (Resolve-Path $fullPath).Path + $root = [System.IO.Path]::GetPathRoot($resolved) + if ($resolved.TrimEnd("\") -ne $root.TrimEnd("\")) { + throw "$Mode target must be a drive root such as E:\, not a subfolder: $resolved" + } + if ($root.TrimEnd("\") -eq $env:SystemDrive.TrimEnd("\")) { + throw "The Windows system drive cannot be used as a USB/SSD target." + } + if (-not $resolved.EndsWith("\")) { $resolved += "\" } + return $resolved + } + + if ($Mode -eq "LocalFolder") { + $defaultFolder = Join-Path $env:LOCALAPPDATA "JackAILocal" + Ensure-Directory $defaultFolder + return (Resolve-Path $defaultFolder).Path + } + + $candidates = @(Get-DriveCandidates -Mode $Mode) + if ($candidates.Count -eq 0) { + throw "No compatible $Mode drive was detected. Connect the target drive and run the builder again." + } + + Write-Host "Detected $Mode target drives:" -ForegroundColor Yellow + for ($i = 0; $i -lt $candidates.Count; $i++) { + $d = $candidates[$i] + Write-Host ("[{0}] {1} {2} Label='{3}' Size={4}GB Free={5}GB" -f ($i + 1), $d.Drive, $d.Type, $d.Label, $d.SizeGb, $d.FreeGb) + } + + if ($candidates.Count -eq 1) { + $answer = Read-Host "Use $($candidates[0].Drive) as the JackAILocal USB target? Type Y to continue" + if ($answer -notin @("Y", "y", "YES", "yes")) { throw "Cancelled by user." } + return $candidates[0].Drive + } + + $selection = Read-Host "Type the number of the target drive" + $index = 0 + if (-not [int]::TryParse($selection, [ref]$index)) { throw "Invalid drive selection." } + if ($index -lt 1 -or $index -gt $candidates.Count) { throw "Invalid drive selection." } + $chosen = $candidates[$index - 1].Drive + $confirm = Read-Host "Confirm target drive $chosen. Type BUILD to continue" + if ($confirm -ne "BUILD") { throw "Cancelled by user." } + return $chosen +} + +function Find-Manifest([string]$Root, [string]$ProvidedPath) { + if ($ProvidedPath) { + if (!(Test-Path $ProvidedPath)) { throw "Manifest path does not exist: $ProvidedPath" } + return (Resolve-Path $ProvidedPath).Path + } + + $searchRoots = New-Object System.Collections.Generic.List[string] + $searchRoots.Add($Root) + + $manifestInput = Join-Path $Root "manifest-input" + if (Test-Path $manifestInput) { $searchRoots.Add($manifestInput) } + + $downloads = Join-Path $env:USERPROFILE "Downloads" + if (Test-Path $downloads) { $searchRoots.Add($downloads) } + + $patterns = @("*build-manifest*.json", "*build-manifest*.zip", "jackailocal-build-manifest*.zip") + $candidates = @() + + foreach ($searchRoot in $searchRoots) { + foreach ($pattern in $patterns) { + try { + $recursive = ($searchRoot -eq $Root -or $searchRoot -eq $manifestInput) + if ($recursive) { + $candidates += Get-ChildItem -Path $searchRoot -Recurse -File -Filter $pattern -ErrorAction SilentlyContinue + } else { + $candidates += Get-ChildItem -Path $searchRoot -File -Filter $pattern -ErrorAction SilentlyContinue + } + } catch {} + } + } + + $candidates = @($candidates | Sort-Object FullName -Unique | Sort-Object LastWriteTime -Descending) + + if ($candidates.Count -eq 0) { + Write-Host "No Gradio build manifest ZIP/JSON was found." -ForegroundColor Yellow + Write-Host "Looked in:" -ForegroundColor Yellow + foreach ($searchRoot in $searchRoots) { Write-Host " $searchRoot" } + Write-Host "The builder will use the default Standard offline assistant profile." + return "" + } + + Write-Host "Detected build manifest files:" -ForegroundColor Yellow + for ($i = 0; $i -lt $candidates.Count; $i++) { + Write-Host ("[{0}] {1}" -f ($i + 1), $candidates[$i].FullName) + } + + if ($candidates.Count -eq 1) { + Write-Host "Using detected manifest: $($candidates[0].Name)" + return $candidates[0].FullName + } + + $selection = Read-Host "Type the number of the manifest to use, or press Enter for the newest" + if ([string]::IsNullOrWhiteSpace($selection)) { return $candidates[0].FullName } + $index = 0 + if (-not [int]::TryParse($selection, [ref]$index)) { throw "Invalid manifest selection." } + if ($index -lt 1 -or $index -gt $candidates.Count) { throw "Invalid manifest selection." } + return $candidates[$index - 1].FullName +} + +function Read-BuildManifest([string]$Root, [string]$ManifestFile) { + if (-not $ManifestFile) { + return [ordered]@{ + product = "JackAILocal" + package_goal = "Standard offline assistant" + hardware = @{ os = "windows"; ram_gb = 16; vram_gb = 0; gpu_name = "Default profile"; usb_storage_gb = 128 } + default_model = @{ id = "qwen35_smart_9b"; model_ref = "qwen3.5:9b"; provider = "ollama"; params_b = 9 } + allowed_models = @( + @{ id = "qwen35_fast_2b"; model_ref = "qwen3.5:2b"; provider = "ollama"; params_b = 2 }, + @{ id = "qwen35_balanced_4b"; model_ref = "qwen3.5:4b"; provider = "ollama"; params_b = 4 }, + @{ id = "qwen35_smart_9b"; model_ref = "qwen3.5:9b"; provider = "ollama"; params_b = 9 } + ) + content_packs = @() + build_id = "local-default" + } + } + + Verify-ManifestSignature -ManifestFile $ManifestFile + + $extension = [IO.Path]::GetExtension($ManifestFile).ToLowerInvariant() + if ($extension -eq ".zip") { + $extractDir = Join-Path $Root ".jackailocal-builder\manifest" + if (Test-Path $extractDir) { Remove-Item $extractDir -Recurse -Force } + Ensure-Directory $extractDir + Expand-Archive -Path $ManifestFile -DestinationPath $extractDir -Force + $json = Get-ChildItem -Path $extractDir -Recurse -File -Filter "*.json" | Where-Object { $_.Name -like "*manifest*" -or $_.FullName -like "*build-manifest*" } | Select-Object -First 1 + if (-not $json) { throw "No JSON build manifest was found inside $ManifestFile" } + return Get-Content -Raw -Path $json.FullName | ConvertFrom-Json + } + + return Get-Content -Raw -Path $ManifestFile | ConvertFrom-Json +} + +function Copy-RuntimePayload([string]$SourceRoot, [string]$TargetRoot, [bool]$Clean) { + if ($Clean) { + if ($env:AUTO_CONFIRM -ne "true") { + $confirm = Read-Host "Clean existing JackAILocal files on $TargetRoot? Type CLEAN to continue" + if ($confirm -ne "CLEAN") { throw "Clean target was requested but not confirmed." } + } + $paths = @("webui", "windows", "macos", "unix", "profiler", "config", "manifest", "models", "backends", "bin", "docs", "updates", "content", "content-packs", "licenses", "legal", "workspace", "diagnostics", ".jackailocal", "README-FIRST.txt", "START-HERE.cmd", "START-DESKTOP.cmd", "STOP-JACKAILOCAL.cmd", "START-HERE.command", "STOP-JACKAILOCAL.command", "autorun.inf") + foreach ($path in $paths) { + $target = Join-Path $TargetRoot $path + if (Test-Path $target) { Remove-Item $target -Recurse -Force } + } + } + + $items = @( + "START-HERE.cmd", "START-DESKTOP.cmd", "STOP-JACKAILOCAL.cmd", "UPDATE-FROM-USB.cmd", "START-HERE.command", "STOP-JACKAILOCAL.command", "README-FIRST.txt", "autorun.inf", + "webui", "windows", "macos", "unix", "profiler", "config", "manifest", "models", "backends", "bin", "docs", "updates", "content", "content-packs", "licenses", "legal" + ) + + foreach ($item in $items) { + $src = Join-Path $SourceRoot $item + $dst = Join-Path $TargetRoot $item + if (Test-Path $src) { + if ((Get-Item $src).PSIsContainer) { + $sizeGb = [math]::Round(((Get-ChildItem $src -Recurse -File -ErrorAction SilentlyContinue | Measure-Object Length -Sum).Sum) / 1GB, 2) + if ($sizeGb -ge 1) { + Write-Host ("Copying {0} ({1} GB)... this step can take several minutes." -f $item, $sizeGb) + } else { + Write-Host ("Copying {0}..." -f $item) + } + Ensure-Directory $dst + Copy-Item -Path (Join-Path $src "*") -Destination $dst -Recurse -Force -ErrorAction SilentlyContinue + } else { + Copy-Item -Path $src -Destination $dst -Force + } + } + } + Write-Host "Runtime payload copy finished." + + Ensure-Directory (Join-Path $TargetRoot "bin") + Ensure-Directory (Join-Path $TargetRoot "backends\ollama\windows") + Ensure-Directory (Join-Path $TargetRoot "backends\llama.cpp\windows") + Ensure-Directory (Join-Path $TargetRoot "models\ollama") + Ensure-Directory (Join-Path $TargetRoot "models\gguf") + Ensure-Directory (Join-Path $TargetRoot "models\hf") + Ensure-Directory (Join-Path $TargetRoot "workspace\documents") + Ensure-Directory (Join-Path $TargetRoot "workspace\threads") + Ensure-Directory (Join-Path $TargetRoot "workspace\exports") + Ensure-Directory (Join-Path $TargetRoot ".jackailocal\logs") + + $privateKey = Join-Path $TargetRoot "config\update-private-key.xml" + if (Test-Path $privateKey) { Remove-Item -LiteralPath $privateKey -Force } +} + +function Find-OllamaExe([string]$Root) { + $local = Join-Path $Root "backends\ollama\windows\ollama.exe" + if (Test-Path $local) { return (Resolve-Path $local).Path } + $package = Get-ChildItem -Path $Root -Recurse -File -Filter "ollama.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($package) { return $package.FullName } + $cmd = Get-Command ollama.exe -ErrorAction SilentlyContinue + if ($cmd) { return $cmd.Source } + return "" +} + +function Start-OllamaServer([string]$OllamaExe, [string]$ModelDir, [string]$LogDir) { + $builderHost = "127.0.0.1:11437" + $builderUrl = "http://$builderHost" + $busy = Get-NetTCPConnection -LocalAddress 127.0.0.1 -LocalPort 11437 -State Listen -ErrorAction SilentlyContinue + if ($busy) { + throw "Builder Ollama port 11437 is already in use. Stop that process before running the builder." + } + + $env:OLLAMA_HOST = $builderHost + $env:OLLAMA_MODELS = $ModelDir + $env:OLLAMA_NO_CLOUD = "true" + + $process = Start-Process -FilePath $OllamaExe ` + -ArgumentList "serve" ` + -WorkingDirectory (Split-Path -Parent $OllamaExe) ` + -WindowStyle Hidden ` + -PassThru ` + -RedirectStandardOutput (Join-Path $LogDir "ollama-builder.out.log") ` + -RedirectStandardError (Join-Path $LogDir "ollama-builder.err.log") + + for ($i = 0; $i -lt 40; $i++) { + Start-Sleep -Milliseconds 500 + try { + Invoke-RestMethod -Uri "$builderUrl/api/tags" -TimeoutSec 2 | Out-Null + return $process + } catch {} + } + + throw "Ollama server did not become ready." +} + +function Get-ModelsToInstall($Manifest, [string]$Mode) { + $models = New-Object System.Collections.Generic.List[string] + + function Add-ModelRef($candidate) { + if ($null -eq $candidate) { return } + $provider = [string]$candidate.provider + $ref = [string]$candidate.model_ref + if ($provider -eq "ollama" -and -not [string]::IsNullOrWhiteSpace($ref)) { + if (-not $models.Contains($ref)) { $models.Add($ref) } + } + } + + function Add-ModelString([string]$ref) { + if (-not [string]::IsNullOrWhiteSpace($ref)) { + if (-not $models.Contains($ref)) { $models.Add($ref) } + } + } + + if ($Manifest.backend_plan -and $Manifest.backend_plan.model_install_plan -and $Manifest.backend_plan.model_install_plan.ollama) { + foreach ($ref in @($Manifest.backend_plan.model_install_plan.ollama)) { + Add-ModelString ([string]$ref) + } + } + + Add-ModelRef $Manifest.default_model + Add-ModelRef $Manifest.agent_model + + if ($Mode -in @("RecommendedSet", "AllAllowed")) { + foreach ($m in @($Manifest.allowed_models)) { + if ($Mode -eq "RecommendedSet") { + $params = 0 + try { $params = [double]$m.params_b } catch { $params = 0 } + if ($params -le 9) { Add-ModelRef $m } + } else { + Add-ModelRef $m + } + } + } + + return @($models) +} + +function Pull-OllamaModels([string]$TargetRoot, $Manifest, [string]$Mode, [bool]$Skip) { + $modelDir = Join-Path $TargetRoot "models\ollama" + Ensure-Directory $modelDir + + $models = @(Get-ModelsToInstall -Manifest $Manifest -Mode $Mode) + if ($models.Count -eq 0) { + Write-Host "No Ollama model was selected for installation." + return + } + + $planPath = Join-Path $TargetRoot "manifest\models-to-install.txt" + $models | Set-Content -Encoding UTF8 $planPath + + if ($Skip) { + Write-Host "Model download skipped. Planned models were written to manifest\models-to-install.txt" -ForegroundColor Yellow + return + } + + $ollamaExe = Find-OllamaExe $TargetRoot + if (-not $ollamaExe) { + throw "ollama.exe was not found. This builder package must include ollama.exe, or Ollama must already be installed on this PC." + } + + $ollamaDest = Join-Path $TargetRoot "backends\ollama\windows\ollama.exe" + if ($ollamaExe -ne $ollamaDest) { + Ensure-Directory (Split-Path $ollamaDest) + Copy-Item $ollamaExe $ollamaDest -Force + $ollamaExe = $ollamaDest + } + + $logDir = Join-Path $TargetRoot ".jackailocal\logs" + Ensure-Directory $logDir + $server = Start-OllamaServer -OllamaExe $ollamaExe -ModelDir $modelDir -LogDir $logDir + + try { + foreach ($model in $models) { + Write-Host "Downloading model to USB: $model" -ForegroundColor Cyan + $env:OLLAMA_HOST = "127.0.0.1:11437" + $env:OLLAMA_MODELS = $modelDir + & $ollamaExe pull $model + if ($LASTEXITCODE -ne 0) { throw "Ollama pull failed for model: $model" } + } + + # Optional LoRA adapters: white-label customers ship fine-tuned models. + $loraManifest = Join-Path $TargetRoot "manifest\lora-adapters.json" + $loraScript = Join-Path (Resolve-PackageRoot) "factory\powershell\Install-LoraAdapters.ps1" + if ((Test-Path $loraManifest) -and (Test-Path $loraScript)) { + Write-Host "Installing LoRA adapters from manifest\lora-adapters.json" -ForegroundColor Cyan + & $loraScript -DriveRoot $TargetRoot -OllamaExe $ollamaExe + } + } finally { + if ($server -and -not $server.HasExited) { + Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue + } + } + + Write-Host "Models installed under $modelDir" +} + +function Write-TargetManifest([string]$TargetRoot, $Manifest) { + $manifestDir = Join-Path $TargetRoot "manifest" + Ensure-Directory $manifestDir + $Manifest | ConvertTo-Json -Depth 50 | Set-Content -Encoding UTF8 (Join-Path $manifestDir "build-manifest.json") + + $hashManifest = @() + Get-ChildItem -Path $TargetRoot -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object { + $rel = $_.FullName.Substring($TargetRoot.Length).TrimStart("\", "/") -replace "\\", "/" + if ($rel -eq "manifest/sha256-manifest.json") { return } + if ($rel -like ".jackailocal/*") { return } + if ($rel -like "workspace/*") { return } + if ($rel -like "diagnostics/*") { return } + + # Skip hidden/system files and common Windows system lockouts + if ($_.Attributes -match "Hidden" -or $_.Attributes -match "System") { return } + if ($rel -eq "DumpStack.log" -or $rel -like "System Volume Information/*" -or $rel -like "`$RECYCLE.BIN/*") { return } + + try { + $hash = (Get-FileHash -Algorithm SHA256 -Path $_.FullName -ErrorAction Stop).Hash.ToLowerInvariant() + $hashManifest += [ordered]@{ path = $rel; sha256 = $hash; bytes = $_.Length } + } catch { + Write-Host "Warning: Skipping locked or inaccessible file: $rel" -ForegroundColor Yellow + } + } + [ordered]@{ files = $hashManifest } | ConvertTo-Json -Depth 5 | Set-Content -Encoding UTF8 (Join-Path $manifestDir "sha256-manifest.json") +} + +function Ensure-LaunchFiles([string]$TargetRoot) { + $startPath = Join-Path $TargetRoot "START-HERE.cmd" + if (!(Test-Path $startPath)) { + @' +@echo off +cd /d "%~dp0" +powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0windows\Start-JackAILocal.ps1" +pause +'@ | Set-Content -Encoding ASCII $startPath + } + + $readmePath = Join-Path $TargetRoot "README-FIRST.txt" + @' +JackAILocal + +=== FRANCAIS === + +1. Ouvrez ce lecteur. +2. Double-cliquez sur START-HERE.cmd. +3. Attendez que l'interface locale s'ouvre. +4. Internet n'est pas requis une fois les modeles installes sur cette cle. + +Ne supprimez pas les dossiers models, backends, config, manifest, webui, windows, bin, legal ou licenses. + +Documents importants : legal\EULA_FR.md (contrat de licence), legal\PRIVACY_FR.md (confidentialite), +licenses\THIRD_PARTY_NOTICES.md (composants tiers). + +=== ENGLISH === + +1. Open this drive. +2. Double-click START-HERE.cmd. +3. Wait for the local interface to open. +4. Internet is not required after the models have been installed on this key. + +Do not delete the models, backends, config, manifest, webui, windows, bin, legal, or licenses folders. + +Important documents: legal\EULA_EN.md (license agreement), legal\PRIVACY_EN.md (privacy), +licenses\THIRD_PARTY_NOTICES.md (third-party components). +'@ | Set-Content -Encoding UTF8 $readmePath +} + +function Assert-TargetReady([string]$TargetRoot) { + $requiredFiles = @( + "bin\jackailocald.exe", + "backends\ollama\windows\ollama.exe", + "backends\whisper.cpp\windows\whisper-cli.exe", + "backends\piper\windows\piper.exe", + "models\whisper\ggml-base.bin", + "models\piper\en_US-libritts_r-medium.onnx", + "models\piper\en_US-libritts_r-medium.onnx.json", + "content\field-manual\cards.json", + "content-packs\prompts\general-assistant.json", + "webui\index.html", + "webui\app-v15.js", + "config\jackailocal.windows.toml", + "legal\EULA_EN.md", + "legal\EULA_FR.md", + "legal\PRIVACY_EN.md", + "legal\PRIVACY_FR.md", + "licenses\THIRD_PARTY_NOTICES.md" + ) + foreach ($relative in $requiredFiles) { + $path = Join-Path $TargetRoot $relative + if (!(Test-Path $path -PathType Leaf)) { throw "Target validation failed. Required file is missing: $path" } + } + $modelFiles = @(Get-ChildItem -Path (Join-Path $TargetRoot "models\ollama") -Recurse -File -ErrorAction SilentlyContinue | + Where-Object { $_.Name -notlike "*.txt" -and $_.Length -gt 0 }) + if ($modelFiles.Count -eq 0) { + throw "Target validation failed. No real Ollama model files were found under models\ollama." + } + $planPath = Join-Path $TargetRoot "manifest\models-to-install.txt" + if (Test-Path $planPath) { + foreach ($model in (Get-Content -Path $planPath | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })) { + $parts = $model.Trim().Split(":", 2) + $name = $parts[0] + $tag = if ($parts.Count -gt 1 -and $parts[1]) { $parts[1] } else { "latest" } + $segments = $name.Split("/", [System.StringSplitOptions]::RemoveEmptyEntries) + if ($segments.Count -eq 1) { + $manifestPath = Join-Path $TargetRoot ("models\ollama\manifests\registry.ollama.ai\library\{0}\{1}" -f $segments[0], $tag) + } elseif ($segments.Count -eq 2) { + $manifestPath = Join-Path $TargetRoot ("models\ollama\manifests\registry.ollama.ai\{0}\{1}\{2}" -f $segments[0], $segments[1], $tag) + } else { + throw "Target validation failed. Unsupported Ollama model reference in plan: $model" + } + if (!(Test-Path $manifestPath -PathType Leaf)) { + throw "Target validation failed. Planned Ollama model is missing from target store: $model" + } + } + } +} + +try { + Write-Title "JackAILocal Builder" + Write-Host "This wizard prepares a real JackAILocal $TargetMode target for a non-technical user." + Write-Host "It does not require the user to understand models, backends, RAM, VRAM, or quantization." + + $root = Resolve-PackageRoot + $daemonSource = Join-Path $root "bin\jackailocald.exe" + if (!(Test-Path $daemonSource)) { + throw "Required runtime binary is missing: $daemonSource. Build and stage jackailocald.exe before creating a target." + } + $ollamaSource = Find-OllamaExe $root + if (-not $ollamaSource) { + throw "Ollama was not found in the package or on this computer. Install or stage ollama.exe before creating a target." + } + + $logRoot = Join-Path $root ".jackailocal-builder\logs" + Ensure-Directory $logRoot + $logFile = Join-Path $logRoot ("builder-{0}.log" -f (Get-Date -Format "yyyyMMdd-HHmmss")) + Start-Transcript -Path $logFile -Force | Out-Null + + $manifestFile = Find-Manifest -Root $root -ProvidedPath $ManifestPath + $manifest = Read-BuildManifest -Root $root -ManifestFile $manifestFile + + $target = Select-TargetDrive -ExistingTarget $TargetDrive -Mode $TargetMode + Assert-SafeTarget -SourceRoot $root -TargetRoot $target + $freeGb = Get-FreeGb $target + if ($freeGb -lt 16) { throw "The target drive has only $freeGb GB free. Use a larger USB/SSD." } + + Write-Title "Build summary" + Write-Host "Target mode: $TargetMode" + Write-Host "Target path: $target" + Write-Host "Package goal: $($manifest.package_goal)" + Write-Host "Default model: $($manifest.default_model.model_ref)" + Write-Host "Install mode: $ModelInstallMode" + Write-Host "Skip model download: $SkipModelDownload" + + if ($env:AUTO_CONFIRM -ne "true") { + $confirm = Read-Host "Type YES to build this JackAILocal $TargetMode target" + if ($confirm -ne "YES") { throw "Cancelled by user." } + } + + Copy-RuntimePayload -SourceRoot $root -TargetRoot $target -Clean ([bool]$CleanTarget) + Write-TargetManifest -TargetRoot $target -Manifest $manifest + Pull-OllamaModels -TargetRoot $target -Manifest $manifest -Mode $ModelInstallMode -Skip ([bool]$SkipModelDownload) + & (Join-Path $target "windows\Install-Voice.ps1") -TargetRoot $target + Ensure-LaunchFiles -TargetRoot $target + Assert-TargetReady -TargetRoot $target + Write-TargetManifest -TargetRoot $target -Manifest $manifest + + Write-Title "Build complete" + Write-Host "The target is ready for use." -ForegroundColor Green + Write-Host ("Client launch file: {0}" -f (Join-Path $target "START-HERE.cmd")) + Write-Host "Build log: $logFile" + Stop-Transcript | Out-Null + exit 0 +} catch { + Write-Host "" + Write-Host "JackAILocal Builder failed:" -ForegroundColor Red + Write-Host $_.Exception.Message -ForegroundColor Red + try { Stop-Transcript | Out-Null } catch {} + exit 1 +} diff --git a/windows/Preflight-Windows.ps1 b/windows/Preflight-Windows.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..07bec6f43ca9257c1a6eb2621045eb2c5cb6e69b --- /dev/null +++ b/windows/Preflight-Windows.ps1 @@ -0,0 +1,19 @@ +[CmdletBinding()] +param() +$ErrorActionPreference = "Continue" +$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path +$checks = @() +function Add-Check($Name, $Ok, $Detail) { $script:checks += [ordered]@{ name=$Name; ok=[bool]$Ok; detail=$Detail } } +Add-Check "root" (Test-Path $Root) $Root +Add-Check "START-HERE.cmd" (Test-Path (Join-Path $Root "START-HERE.cmd")) "required client launcher" +Add-Check "jackailocald.exe" (Test-Path (Join-Path $Root "bin\jackailocald.exe")) "compile Rust or copy binary" +Add-Check "ollama.exe" (Test-Path (Join-Path $Root "backends\ollama\windows\ollama.exe")) "required for default backend" +Add-Check "llama-server.exe" (Test-Path (Join-Path $Root "backends\llama.cpp\windows\llama-server.exe")) "optional fallback" +Add-Check "models ollama" ((Test-Path (Join-Path $Root "models\ollama\manifests")) -or (Test-Path (Join-Path $Root "models\ollama\blobs"))) "models should be preloaded" +Add-Check "webui" (Test-Path (Join-Path $Root "webui\index.html")) "UI files" +Add-Check "model catalog" (Test-Path (Join-Path $Root "config\model-catalog.json")) "catalog" +Add-Check "manifest" (Test-Path (Join-Path $Root "manifest\sha256-manifest.json")) "integrity manifest" +$checks | ConvertTo-Json -Depth 4 +$failed = $checks | Where-Object { -not $_.ok } +if ($failed.Count -gt 0) { exit 1 } +exit 0 diff --git a/windows/Start-JackAILocal.ps1 b/windows/Start-JackAILocal.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..544740e278964e079249a418da70c879a0f07ecd --- /dev/null +++ b/windows/Start-JackAILocal.ps1 @@ -0,0 +1,148 @@ +[CmdletBinding()] +param( + [switch]$NoBrowser, + [switch]$AppMode +) +$ErrorActionPreference = "Stop" +$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path +$DiagDir = Join-Path $Root "diagnostics" +$LogDir = Join-Path $Root ".jackailocal\logs" +$CacheDir = Join-Path $Root ".jackailocal\cache" +$RunDir = Join-Path $Root ".jackailocal\run" +New-Item -ItemType Directory -Force -Path $DiagDir,$LogDir,$CacheDir,$RunDir | Out-Null + +& (Join-Path $PSScriptRoot "hwscan-windows.ps1") -OutputPath (Join-Path $DiagDir "hardware.json") | Out-Null +& (Join-Path $PSScriptRoot "Integrity-Check.ps1") -Root $Root -Mode Fail | Out-Null + +$env:JACKAILOCAL_ROOT = $Root +$env:JACKAILOCAL_CACHE = $CacheDir + +$OllamaExe = Join-Path $Root "backends\ollama\windows\ollama.exe" +$DaemonExe = Join-Path $Root "bin\jackailocald.exe" + +if (!(Test-Path $DaemonExe)) { throw "Missing $DaemonExe. Run factory build first." } + +$ollamaPidFile = Join-Path $RunDir "ollama.pid" +$daemonPidFile = Join-Path $RunDir "jackailocald.pid" + +$OllamaPort = "11434" +$OllamaHost = "127.0.0.1:11434" +$OllamaUrl = "http://127.0.0.1:11434" +$ollamaRunning = Get-NetTCPConnection -LocalAddress 127.0.0.1 -LocalPort 11434 -State Listen -ErrorAction SilentlyContinue +$reuseOllama = $false + +if ($ollamaRunning) { + # Query the running instance to check if it has the models from the USB + try { + $response = Invoke-RestMethod -Uri "$OllamaUrl/api/tags" -TimeoutSec 2 + $manifestPath = Join-Path $Root "manifest\build-manifest.json" + $defaultModelRef = "qwen3.5:4b" # fallback default + if (Test-Path $manifestPath) { + $manifest = Get-Content -Raw $manifestPath | ConvertFrom-Json + if ($manifest.default_model.model_ref) { + $defaultModelRef = $manifest.default_model.model_ref + } + } + + $found = $false + foreach ($m in $response.models) { + if ($m.name -eq $defaultModelRef -or $m.name.StartsWith($defaultModelRef + ":") -or $m.name -eq $defaultModelRef.Replace(":", "-")) { + $found = $true + break + } + } + if ($found) { + Write-Host "Ollama is already running on port 11434 and contains our preloaded models. Reusing instance." + $reuseOllama = $true + } + } catch {} +} + +if (-not $reuseOllama) { + if ($ollamaRunning) { + Write-Host "Host Ollama is running but does not have our preloaded models. Launching isolated instance on port 11435." + $OllamaPort = "11435" + $OllamaHost = "127.0.0.1:11435" + $OllamaUrl = "http://127.0.0.1:11435" + } + + if (!(Test-Path $OllamaExe)) { throw "Missing $OllamaExe. Run backend installer first." } + + # Isolate Ollama environment completely to the USB/local folder root + $env:OLLAMA_HOST = $OllamaHost + $env:OLLAMA_MODELS = Join-Path $Root "models\ollama" + $env:OLLAMA_NO_CLOUD = "true" + $env:OLLAMA_TMPDIR = $CacheDir + $env:HOME = $Root + $env:USERPROFILE = $Root + + # Also set for the Gradio agent decision engine + $env:OLLAMA_AGENT_ENDPOINT = "http://127.0.0.1:$($OllamaPort)/v1" + + $ollamaProcess = Start-Process -FilePath $OllamaExe ` + -ArgumentList "serve" ` + -WindowStyle Hidden ` + -WorkingDirectory (Split-Path $OllamaExe) ` + -PassThru ` + -RedirectStandardOutput (Join-Path $LogDir "ollama.out.log") ` + -RedirectStandardError (Join-Path $LogDir "ollama.err.log") + $ollamaProcess.Id | Set-Content -Encoding ASCII $ollamaPidFile + + for ($i = 0; $i -lt 30; $i++) { + Start-Sleep -Milliseconds 500 + try { + Invoke-RestMethod -Uri "$OllamaUrl/api/tags" -TimeoutSec 2 | Out-Null + break + } catch { + if ($i -eq 29) { throw "Ollama did not become ready on port $OllamaPort. See $LogDir." } + } + } +} else { + # Reusing running host instance on 11434 + $env:OLLAMA_HOST = $OllamaHost + $env:OLLAMA_AGENT_ENDPOINT = "http://127.0.0.1:$($OllamaPort)/v1" +} + +$daemonRunning = Get-NetTCPConnection -LocalAddress 127.0.0.1 -LocalPort 4891 -State Listen -ErrorAction SilentlyContinue +if ($daemonRunning -and !(Test-Path $daemonPidFile)) { + throw "Port 4891 is already in use by another process. Stop that process before launching JackAILocal." +} +if (-not $daemonRunning) { + $daemonProcess = Start-Process -FilePath $DaemonExe ` + -ArgumentList "serve --config config\jackailocal.windows.toml" ` + -WindowStyle Hidden ` + -WorkingDirectory $Root ` + -PassThru ` + -RedirectStandardOutput (Join-Path $LogDir "jackailocald.out.log") ` + -RedirectStandardError (Join-Path $LogDir "jackailocald.err.log") + $daemonProcess.Id | Set-Content -Encoding ASCII $daemonPidFile + for ($i = 0; $i -lt 30; $i++) { + Start-Sleep -Milliseconds 500 + try { + Invoke-RestMethod -Uri "http://127.0.0.1:4891/health" -TimeoutSec 2 | Out-Null + break + } catch { + if ($i -eq 29) { throw "JackAILocal runtime did not become ready. See $LogDir." } + } + } +} + +$Url = "http://127.0.0.1:4891" +if ($NoBrowser) { return } + +if ($AppMode) { + $browserCandidates = @( + (Join-Path ${env:ProgramFiles(x86)} "Microsoft\Edge\Application\msedge.exe"), + (Join-Path $env:ProgramFiles "Microsoft\Edge\Application\msedge.exe"), + (Join-Path $env:LOCALAPPDATA "Microsoft\Edge\Application\msedge.exe"), + (Join-Path $env:ProgramFiles "Google\Chrome\Application\chrome.exe"), + (Join-Path ${env:ProgramFiles(x86)} "Google\Chrome\Application\chrome.exe") + ) + $appBrowser = $browserCandidates | Where-Object { $_ -and (Test-Path $_) } | Select-Object -First 1 + if ($appBrowser) { + Start-Process -FilePath $appBrowser -ArgumentList "--app=$Url", "--start-maximized" + return + } +} + +Start-Process $Url diff --git a/windows/Start-LlamaCppFallback.ps1 b/windows/Start-LlamaCppFallback.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a011167d41f84f6c65aa1459fb79be420c151041 --- /dev/null +++ b/windows/Start-LlamaCppFallback.ps1 @@ -0,0 +1,7 @@ +$ErrorActionPreference = 'Stop' +$Root = Split-Path -Parent $PSScriptRoot +$Exe = "$Root\backends\llama.cpp\windows\llama-server.exe" +$Model = "$Root\models\gguf\qwen3.5-4b-q4_k_m.gguf" +if (!(Test-Path $Exe)) { throw "Missing llama-server.exe" } +if (!(Test-Path $Model)) { throw "Missing GGUF model $Model" } +Start-Process -FilePath $Exe -ArgumentList "-m `"$Model`" --host 127.0.0.1 --port 8080 -c 8192" -WorkingDirectory $Root -WindowStyle Hidden -RedirectStandardOutput "$Root\logs\llamacpp.out.log" -RedirectStandardError "$Root\logs\llamacpp.err.log" diff --git a/windows/Stop-JackAILocal.ps1 b/windows/Stop-JackAILocal.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..39b13a45efa3f15684bda0bc07c41afa68747cc3 --- /dev/null +++ b/windows/Stop-JackAILocal.ps1 @@ -0,0 +1,25 @@ +[CmdletBinding()] +param() +$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path +$RunDir = Join-Path $Root ".jackailocal\run" + +function Stop-PidFile([string]$Path) { + if (!(Test-Path $Path)) { return } + $pidText = (Get-Content -Raw -Path $Path).Trim() + $pidValue = 0 + if ([int]::TryParse($pidText, [ref]$pidValue)) { + $process = Get-Process -Id $pidValue -ErrorAction SilentlyContinue + if ($process) { + Stop-Process -Id $pidValue -Force -ErrorAction SilentlyContinue + } + } + Remove-Item -LiteralPath $Path -Force -ErrorAction SilentlyContinue +} + +Stop-PidFile (Join-Path $RunDir "jackailocald.pid") +Stop-PidFile (Join-Path $RunDir "ollama.pid") + +Get-Process jackailocald,ollama,llama-server -ErrorAction SilentlyContinue | + Where-Object { $_.Path -and $_.Path.StartsWith($Root, [System.StringComparison]::OrdinalIgnoreCase) } | + Stop-Process -Force -ErrorAction SilentlyContinue +Write-Host "JackAILocal stopped." diff --git a/windows/hwscan-windows.ps1 b/windows/hwscan-windows.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..0fb095624728f9f7283a0a94fd67995abe0d005a --- /dev/null +++ b/windows/hwscan-windows.ps1 @@ -0,0 +1,25 @@ +[CmdletBinding()] +param([string]$OutputPath = "diagnostics\hardware.json") +$ErrorActionPreference = "SilentlyContinue" +$cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 +$memBytes = (Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory +$gpus = Get-CimInstance Win32_VideoController | ForEach-Object { + [pscustomobject]@{ name=$_.Name; vram_gb=[math]::Round(($_.AdapterRAM / 1GB),1); driver=$_.DriverVersion } +} +$drive = Get-Item (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) +$profile = [ordered]@{ + collected_at = (Get-Date).ToString("o") + os = "windows" + cpu_name = $cpu.Name + cpu_threads = $cpu.NumberOfLogicalProcessors + ram_gb = [math]::Round($memBytes / 1GB, 1) + gpus = $gpus + vram_gb = [double](($gpus | Measure-Object -Property vram_gb -Maximum).Maximum) + whichllm = $null +} +if (Get-Command whichllm -ErrorAction SilentlyContinue) { + try { $profile.whichllm = (whichllm --top 10 --json | ConvertFrom-Json) } catch { $profile.whichllm_error = $_.Exception.Message } +} +New-Item -ItemType Directory -Force -Path (Split-Path $OutputPath) | Out-Null +$profile | ConvertTo-Json -Depth 20 | Set-Content -Encoding UTF8 $OutputPath +$profile | ConvertTo-Json -Depth 20 diff --git a/workspace/exports/JackAILocal-support-20260604-194958.zip b/workspace/exports/JackAILocal-support-20260604-194958.zip new file mode 100644 index 0000000000000000000000000000000000000000..18fe7778f941484fdb44f582b9a84582e81ba13b Binary files /dev/null and b/workspace/exports/JackAILocal-support-20260604-194958.zip differ diff --git a/workspace/exports/JackAILocal-support-20260604-205705.zip b/workspace/exports/JackAILocal-support-20260604-205705.zip new file mode 100644 index 0000000000000000000000000000000000000000..567518f2240e1c8a947fdfa3cba4ae1a9019e844 Binary files /dev/null and b/workspace/exports/JackAILocal-support-20260604-205705.zip differ diff --git a/workspace/exports/JackAILocal-support-20260604-205806.zip b/workspace/exports/JackAILocal-support-20260604-205806.zip new file mode 100644 index 0000000000000000000000000000000000000000..567518f2240e1c8a947fdfa3cba4ae1a9019e844 Binary files /dev/null and b/workspace/exports/JackAILocal-support-20260604-205806.zip differ diff --git a/workspace/settings/settings.json b/workspace/settings/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..06379432f6d1b386d0a5c1862e2081d4207149b2 --- /dev/null +++ b/workspace/settings/settings.json @@ -0,0 +1,7 @@ +{ + "mode": "simple", + "offline_lock": true, + "phone_access_enabled": false, + "phone_access_token": "P-OHrbfuDSu4ue9wGqJzjMUxkklhDF5w", + "tools_lock": true +} \ No newline at end of file diff --git a/workspace/test-manifest-qwen2b.json b/workspace/test-manifest-qwen2b.json new file mode 100644 index 0000000000000000000000000000000000000000..1c839e553617237e8eaddf37861702d7a8cece26 --- /dev/null +++ b/workspace/test-manifest-qwen2b.json @@ -0,0 +1,19 @@ +{ + "product": "JackAILocal", + "mode": "local-folder-real-test", + "selector_version": "v15-real-builder", + "build_id": "local-test-qwen2b", + "created_at": "2026-06-04T00:00:00Z", + "package_goal": "Real local-folder acceptance test", + "hardware": { "os": "windows", "cpu_threads": 8, "ram_gb": 16, "vram_gb": 0, "gpu_name": "local", "usb_storage_gb": 64 }, + "default_model": { "id": "qwen35_fast_2b", "label_en": "Fast 2B", "provider": "ollama", "model_ref": "qwen3.5:2b", "params_b": 2 }, + "allowed_models": [ + { "id": "qwen35_fast_2b", "label_en": "Fast 2B", "provider": "ollama", "model_ref": "qwen3.5:2b", "params_b": 2 } + ], + "content_packs": [ + { "id": "field_manual_core", "label_en": "Field Manual Core", "recommended": true, "min_storage_gb": 32 }, + { "id": "starter_prompt_packs", "label_en": "Starter Prompt Packs", "recommended": true, "min_storage_gb": 32 } + ], + "backends": ["ollama"], + "max_params_b": 32 +}