From 0fcee70833a9d058881711543d9c04a1a60c7d02 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 17 Dec 2025 15:25:55 +0000 Subject: [PATCH] prep repo for v1 --- .circleci/config.yml | 376 ----- .gitattributes | 2 - .github/bench.py | 401 ------ .github/bootstrap.sh | 330 ----- .github/optimize_performance.sh | 95 -- .github/workflows/bench_job.yml | 207 --- .github/workflows/benchmarks.yml | 71 - .gitignore | 175 --- .style.yapf | 19 - LICENSE | 675 --------- README.md | 285 +--- configure_mlx.sh | 43 - docs/exo-logo-black-bg.jpg | Bin 8089 -> 0 bytes docs/exo-logo-transparent-black-text.png | 3 - docs/exo-logo-transparent.png | 3 - docs/exo-rounded.png | 3 - docs/exo-screenshot.jpg | Bin 302470 -> 0 bytes examples/astra/README.md | 3 - .../astra/astra.xcodeproj/project.pbxproj | 653 --------- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/swiftpm/Package.resolved | 33 - .../AccentColor.colorset/Contents.json | 11 - .../AppIcon.appiconset/Contents.json | 63 - .../astra/astra/Assets.xcassets/Contents.json | 6 - examples/astra/astra/ContentView.swift | 729 ---------- .../Preview Assets.xcassets/Contents.json | 6 - examples/astra/astra/astra.entitlements | 22 - examples/astra/astra/astraApp.swift | 17 - examples/astra/astraTests/astraTests.swift | 35 - .../astra/astraUITests/astraUITests.swift | 41 - .../astraUITestsLaunchTests.swift | 32 - examples/chatgpt_api.sh | 39 - examples/function_calling.py | 111 -- exo/__init__.py | 1 - exo/api/__init__.py | 1 - exo/api/chatgpt_api.py | 645 --------- exo/apputil/__init__.py | 1 - exo/apputil/anim.py | 168 --- exo/apputil/baseimages/image1.png | 3 - exo/apputil/baseimages/image2.png | 3 - exo/apputil/baseimages/image3.png | 3 - exo/apputil/baseimages/image4.png | 3 - exo/download/__init__.py | 0 exo/download/download_progress.py | 65 - exo/download/hf/__init__.py | 0 exo/download/hf/hf_helpers.py | 98 -- exo/download/new_shard_download.py | 307 ----- exo/download/shard_download.py | 49 - exo/download/test_new_shard_download.py | 14 - exo/helpers.py | 372 ----- exo/inference/__init__.py | 0 exo/inference/debug_inference_engine.py | 58 - exo/inference/dummy_inference_engine.py | 37 - exo/inference/inference_engine.py | 77 -- exo/inference/mlx/__init__.py | 0 exo/inference/mlx/losses.py | 37 - .../mlx/models/StableDiffusionPipeline.py | 307 ----- exo/inference/mlx/models/__init__.py | 0 exo/inference/mlx/models/base.py | 9 - exo/inference/mlx/models/deepseek_v2.py | 127 -- exo/inference/mlx/models/deepseek_v3.py | 134 -- exo/inference/mlx/models/gemma2.py | 118 -- exo/inference/mlx/models/llama.py | 125 -- exo/inference/mlx/models/llava.py | 585 -------- exo/inference/mlx/models/phi3.py | 117 -- exo/inference/mlx/models/qwen2.py | 129 -- exo/inference/mlx/models/sd_models/clip.py | 191 --- .../mlx/models/sd_models/tokenizer.py | 131 -- exo/inference/mlx/models/sd_models/unet.py | 629 --------- exo/inference/mlx/models/sd_models/vae.py | 429 ------ exo/inference/mlx/perf_improvements.md | 7 - exo/inference/mlx/sharded_inference_engine.py | 179 --- exo/inference/mlx/sharded_utils.py | 257 ---- exo/inference/mlx/test_non_blocking.py | 81 -- exo/inference/mlx/test_sharded_model.py | 52 - exo/inference/shard.py | 39 - exo/inference/test_dummy_inference_engine.py | 47 - exo/inference/test_inference_engine.py | 54 - exo/inference/tinygrad/__init__.py | 0 exo/inference/tinygrad/inference.py | 157 --- exo/inference/tinygrad/losses.py | 14 - exo/inference/tinygrad/models/__init__.py | 0 exo/inference/tinygrad/models/llama.py | 327 ----- exo/inference/tinygrad/stateful_model.py | 22 - exo/inference/tinygrad/tinygrad_helpers.py | 52 - exo/inference/tokenizers.py | 63 - exo/main.py | 402 ------ exo/models.py | 273 ---- exo/networking/__init__.py | 5 - exo/networking/discovery.py | 17 - exo/networking/grpc/__init__.py | 0 exo/networking/grpc/grpc_peer_handle.py | 226 --- exo/networking/grpc/grpc_server.py | 173 --- exo/networking/grpc/node_service.proto | 116 -- exo/networking/grpc/node_service_pb2.py | 90 -- exo/networking/grpc/node_service_pb2_grpc.py | 355 ----- exo/networking/manual/__init__.py | 0 exo/networking/manual/manual_discovery.py | 101 -- .../manual/network_topology_config.py | 31 - .../manual/test_data/invalid_config.json | 17 - .../manual/test_data/invalid_json.json | 0 .../manual/test_data/test_config.json | 32 - .../test_data/test_config_single_node.json | 18 - .../manual/test_manual_discovery.py | 181 --- .../manual/test_network_topology_config.py | 49 - exo/networking/peer_handle.py | 56 - exo/networking/server.py | 11 - exo/networking/tailscale/__init__.py | 0 .../tailscale/tailscale_discovery.py | 178 --- exo/networking/tailscale/tailscale_helpers.py | 125 -- .../tailscale/test_tailscale_discovery.py | 43 - exo/networking/udp/__init__.py | 0 exo/networking/udp/test_udp_discovery.py | 77 -- exo/networking/udp/udp_discovery.py | 246 ---- exo/orchestration/__init__.py | 3 - exo/orchestration/node.py | 628 --------- exo/orchestration/test_node.py | 66 - exo/orchestration/tracing.py | 166 --- exo/test_callbacks.py | 50 - exo/tinychat/common.css | 130 -- exo/tinychat/favicon.svg | 25 - exo/tinychat/index.css | 846 ------------ exo/tinychat/index.html | 454 ------ exo/tinychat/index.js | 889 ------------ .../toolkit@1.0.2/dist/cdn.min.js | 1 - .../npm/@alpinejs/focus@3.x.x/dist/cdn.min.js | 15 - .../@alpinejs/intersect@3.x.x/dist/cdn.min.js | 1 - .../npm/purecss@3.0.0/build/base-min.css | 11 - .../libs/font-awesome/6.5.2/css/all.min.css | 9 - .../6.5.2/webfonts/fa-brands-400.ttf | Bin 209128 -> 0 bytes .../6.5.2/webfonts/fa-brands-400.woff2 | Bin 117852 -> 0 bytes .../6.5.2/webfonts/fa-regular-400.ttf | Bin 67860 -> 0 bytes .../6.5.2/webfonts/fa-regular-400.woff2 | Bin 25392 -> 0 bytes .../6.5.2/webfonts/fa-solid-900.ttf | Bin 420332 -> 0 bytes .../6.5.2/webfonts/fa-solid-900.woff2 | Bin 156400 -> 0 bytes .../6.5.2/webfonts/fa-v4compatibility.ttf | Bin 10832 -> 0 bytes .../6.5.2/webfonts/fa-v4compatibility.woff2 | Bin 4792 -> 0 bytes exo/tinychat/static/fonts.googleapis.com/css2 | 7 - .../cdn-assets@11.9.0/highlight.min.js | 1213 ----------------- .../cdn-assets@11.9.0/styles/vs2015.min.css | 1 - .../dist/alpine-autosize.min.js | 2 - .../unpkg.com/alpinejs@3.x.x/dist/cdn.min.js | 5 - .../dompurify@3.1.5/dist/purify.min.js | 3 - .../marked-highlight@2.1.2/lib/index.umd.js | 97 -- .../unpkg.com/marked@13.0.0/marked.min.js | 6 - exo/tinychat/update_deps.py | 93 -- exo/topology/__init__.py | 0 exo/topology/device_capabilities.py | 294 ---- exo/topology/partitioning_strategy.py | 42 - ...g_memory_weighted_partitioning_strategy.py | 18 - exo/topology/test_device_capabilities.py | 94 -- exo/topology/test_map_partitions.py | 81 -- ...g_memory_weighted_partitioning_strategy.py | 90 -- exo/topology/topology.py | 75 - exo/train/__init__.py | 0 exo/train/data/lora/test.jsonl | 100 -- exo/train/data/lora/train.jsonl | 1000 -------------- exo/train/data/lora/valid.jsonl | 100 -- exo/train/dataset.py | 80 -- exo/viz/__init__.py | 0 exo/viz/test_topology_viz.py | 129 -- exo/viz/topology_viz.py | 377 ----- extra/dashboard/dashboard.py | 1147 ---------------- extra/dashboard/requirements.txt | 5 - extra/dashboard/sounds/gta5_wasted.mp3 | 3 - extra/dashboard/sounds/pokemon_evolve.mp3 | 3 - extra/line_counter.py | 210 --- extra/pipsize.py | 113 -- extra/start_openwebui.sh | 3 - format.py | 33 - install.sh | 11 - scripts/build_exo.py | 64 - scripts/compile_grpc.sh | 7 - setup.py | 89 -- test/reconnect.sh | 21 - test/test_model_helpers.py | 121 -- test/test_tokenizers.py | 42 - 178 files changed, 2 insertions(+), 22721 deletions(-) delete mode 100644 .circleci/config.yml delete mode 100644 .gitattributes delete mode 100644 .github/bench.py delete mode 100755 .github/bootstrap.sh delete mode 100755 .github/optimize_performance.sh delete mode 100644 .github/workflows/bench_job.yml delete mode 100644 .github/workflows/benchmarks.yml delete mode 100644 .gitignore delete mode 100644 .style.yapf delete mode 100644 LICENSE delete mode 100755 configure_mlx.sh delete mode 100644 docs/exo-logo-black-bg.jpg delete mode 100644 docs/exo-logo-transparent-black-text.png delete mode 100644 docs/exo-logo-transparent.png delete mode 100644 docs/exo-rounded.png delete mode 100644 docs/exo-screenshot.jpg delete mode 100644 examples/astra/README.md delete mode 100644 examples/astra/astra.xcodeproj/project.pbxproj delete mode 100644 examples/astra/astra.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 examples/astra/astra.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 examples/astra/astra.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved delete mode 100644 examples/astra/astra/Assets.xcassets/AccentColor.colorset/Contents.json delete mode 100644 examples/astra/astra/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 examples/astra/astra/Assets.xcassets/Contents.json delete mode 100644 examples/astra/astra/ContentView.swift delete mode 100644 examples/astra/astra/Preview Content/Preview Assets.xcassets/Contents.json delete mode 100644 examples/astra/astra/astra.entitlements delete mode 100644 examples/astra/astra/astraApp.swift delete mode 100644 examples/astra/astraTests/astraTests.swift delete mode 100644 examples/astra/astraUITests/astraUITests.swift delete mode 100644 examples/astra/astraUITests/astraUITestsLaunchTests.swift delete mode 100755 examples/chatgpt_api.sh delete mode 100644 examples/function_calling.py delete mode 100644 exo/__init__.py delete mode 100644 exo/api/__init__.py delete mode 100644 exo/api/chatgpt_api.py delete mode 100644 exo/apputil/__init__.py delete mode 100644 exo/apputil/anim.py delete mode 100644 exo/apputil/baseimages/image1.png delete mode 100644 exo/apputil/baseimages/image2.png delete mode 100644 exo/apputil/baseimages/image3.png delete mode 100644 exo/apputil/baseimages/image4.png delete mode 100644 exo/download/__init__.py delete mode 100644 exo/download/download_progress.py delete mode 100644 exo/download/hf/__init__.py delete mode 100644 exo/download/hf/hf_helpers.py delete mode 100644 exo/download/new_shard_download.py delete mode 100644 exo/download/shard_download.py delete mode 100644 exo/download/test_new_shard_download.py delete mode 100644 exo/helpers.py delete mode 100644 exo/inference/__init__.py delete mode 100644 exo/inference/debug_inference_engine.py delete mode 100644 exo/inference/dummy_inference_engine.py delete mode 100644 exo/inference/inference_engine.py delete mode 100644 exo/inference/mlx/__init__.py delete mode 100644 exo/inference/mlx/losses.py delete mode 100644 exo/inference/mlx/models/StableDiffusionPipeline.py delete mode 100644 exo/inference/mlx/models/__init__.py delete mode 100644 exo/inference/mlx/models/base.py delete mode 100644 exo/inference/mlx/models/deepseek_v2.py delete mode 100644 exo/inference/mlx/models/deepseek_v3.py delete mode 100644 exo/inference/mlx/models/gemma2.py delete mode 100644 exo/inference/mlx/models/llama.py delete mode 100644 exo/inference/mlx/models/llava.py delete mode 100644 exo/inference/mlx/models/phi3.py delete mode 100644 exo/inference/mlx/models/qwen2.py delete mode 100644 exo/inference/mlx/models/sd_models/clip.py delete mode 100644 exo/inference/mlx/models/sd_models/tokenizer.py delete mode 100644 exo/inference/mlx/models/sd_models/unet.py delete mode 100644 exo/inference/mlx/models/sd_models/vae.py delete mode 100644 exo/inference/mlx/perf_improvements.md delete mode 100644 exo/inference/mlx/sharded_inference_engine.py delete mode 100644 exo/inference/mlx/sharded_utils.py delete mode 100644 exo/inference/mlx/test_non_blocking.py delete mode 100644 exo/inference/mlx/test_sharded_model.py delete mode 100644 exo/inference/shard.py delete mode 100644 exo/inference/test_dummy_inference_engine.py delete mode 100644 exo/inference/test_inference_engine.py delete mode 100644 exo/inference/tinygrad/__init__.py delete mode 100644 exo/inference/tinygrad/inference.py delete mode 100644 exo/inference/tinygrad/losses.py delete mode 100644 exo/inference/tinygrad/models/__init__.py delete mode 100644 exo/inference/tinygrad/models/llama.py delete mode 100644 exo/inference/tinygrad/stateful_model.py delete mode 100644 exo/inference/tinygrad/tinygrad_helpers.py delete mode 100644 exo/inference/tokenizers.py delete mode 100644 exo/main.py delete mode 100644 exo/models.py delete mode 100644 exo/networking/__init__.py delete mode 100644 exo/networking/discovery.py delete mode 100644 exo/networking/grpc/__init__.py delete mode 100644 exo/networking/grpc/grpc_peer_handle.py delete mode 100644 exo/networking/grpc/grpc_server.py delete mode 100644 exo/networking/grpc/node_service.proto delete mode 100644 exo/networking/grpc/node_service_pb2.py delete mode 100644 exo/networking/grpc/node_service_pb2_grpc.py delete mode 100644 exo/networking/manual/__init__.py delete mode 100644 exo/networking/manual/manual_discovery.py delete mode 100644 exo/networking/manual/network_topology_config.py delete mode 100644 exo/networking/manual/test_data/invalid_config.json delete mode 100644 exo/networking/manual/test_data/invalid_json.json delete mode 100644 exo/networking/manual/test_data/test_config.json delete mode 100644 exo/networking/manual/test_data/test_config_single_node.json delete mode 100644 exo/networking/manual/test_manual_discovery.py delete mode 100644 exo/networking/manual/test_network_topology_config.py delete mode 100644 exo/networking/peer_handle.py delete mode 100644 exo/networking/server.py delete mode 100644 exo/networking/tailscale/__init__.py delete mode 100644 exo/networking/tailscale/tailscale_discovery.py delete mode 100644 exo/networking/tailscale/tailscale_helpers.py delete mode 100644 exo/networking/tailscale/test_tailscale_discovery.py delete mode 100644 exo/networking/udp/__init__.py delete mode 100644 exo/networking/udp/test_udp_discovery.py delete mode 100644 exo/networking/udp/udp_discovery.py delete mode 100644 exo/orchestration/__init__.py delete mode 100644 exo/orchestration/node.py delete mode 100644 exo/orchestration/test_node.py delete mode 100644 exo/orchestration/tracing.py delete mode 100644 exo/test_callbacks.py delete mode 100644 exo/tinychat/common.css delete mode 100644 exo/tinychat/favicon.svg delete mode 100644 exo/tinychat/index.css delete mode 100644 exo/tinychat/index.html delete mode 100644 exo/tinychat/index.js delete mode 100644 exo/tinychat/static/cdn.jsdelivr.net/npm/@alpine-collective/toolkit@1.0.2/dist/cdn.min.js delete mode 100644 exo/tinychat/static/cdn.jsdelivr.net/npm/@alpinejs/focus@3.x.x/dist/cdn.min.js delete mode 100644 exo/tinychat/static/cdn.jsdelivr.net/npm/@alpinejs/intersect@3.x.x/dist/cdn.min.js delete mode 100644 exo/tinychat/static/cdn.jsdelivr.net/npm/purecss@3.0.0/build/base-min.css delete mode 100644 exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css delete mode 100644 exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-brands-400.ttf delete mode 100644 exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-brands-400.woff2 delete mode 100644 exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-regular-400.ttf delete mode 100644 exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-regular-400.woff2 delete mode 100644 exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-solid-900.ttf delete mode 100644 exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-solid-900.woff2 delete mode 100644 exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-v4compatibility.ttf delete mode 100644 exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-v4compatibility.woff2 delete mode 100644 exo/tinychat/static/fonts.googleapis.com/css2 delete mode 100644 exo/tinychat/static/unpkg.com/@highlightjs/cdn-assets@11.9.0/highlight.min.js delete mode 100644 exo/tinychat/static/unpkg.com/@highlightjs/cdn-assets@11.9.0/styles/vs2015.min.css delete mode 100644 exo/tinychat/static/unpkg.com/@marcreichel/alpine-autosize@1.3.x/dist/alpine-autosize.min.js delete mode 100644 exo/tinychat/static/unpkg.com/alpinejs@3.x.x/dist/cdn.min.js delete mode 100644 exo/tinychat/static/unpkg.com/dompurify@3.1.5/dist/purify.min.js delete mode 100644 exo/tinychat/static/unpkg.com/marked-highlight@2.1.2/lib/index.umd.js delete mode 100644 exo/tinychat/static/unpkg.com/marked@13.0.0/marked.min.js delete mode 100644 exo/tinychat/update_deps.py delete mode 100644 exo/topology/__init__.py delete mode 100644 exo/topology/device_capabilities.py delete mode 100644 exo/topology/partitioning_strategy.py delete mode 100644 exo/topology/ring_memory_weighted_partitioning_strategy.py delete mode 100644 exo/topology/test_device_capabilities.py delete mode 100644 exo/topology/test_map_partitions.py delete mode 100644 exo/topology/test_ring_memory_weighted_partitioning_strategy.py delete mode 100644 exo/topology/topology.py delete mode 100644 exo/train/__init__.py delete mode 100644 exo/train/data/lora/test.jsonl delete mode 100644 exo/train/data/lora/train.jsonl delete mode 100644 exo/train/data/lora/valid.jsonl delete mode 100644 exo/train/dataset.py delete mode 100644 exo/viz/__init__.py delete mode 100644 exo/viz/test_topology_viz.py delete mode 100644 exo/viz/topology_viz.py delete mode 100644 extra/dashboard/dashboard.py delete mode 100644 extra/dashboard/requirements.txt delete mode 100644 extra/dashboard/sounds/gta5_wasted.mp3 delete mode 100644 extra/dashboard/sounds/pokemon_evolve.mp3 delete mode 100644 extra/line_counter.py delete mode 100644 extra/pipsize.py delete mode 100755 extra/start_openwebui.sh delete mode 100644 format.py delete mode 100755 install.sh delete mode 100644 scripts/build_exo.py delete mode 100755 scripts/compile_grpc.sh delete mode 100644 setup.py delete mode 100755 test/reconnect.sh delete mode 100644 test/test_model_helpers.py delete mode 100644 test/test_tokenizers.py diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 0d80ff0c..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,376 +0,0 @@ -version: 2.1 - -orbs: - python: circleci/python@2 - -commands: - run_chatgpt_api_test: - parameters: - inference_engine: - type: string - model_id: - type: string - expected_output: - type: string - prompt: - type: string - steps: - - run: - name: Run chatgpt api integration test (<>, <>) - command: | - source env/bin/activate - - # Set CLANG=1 for tinygrad only - if [ "<>" = "tinygrad" ]; then - pip install llvmlite - export TOKENIZERS_PARALLELISM=true SUPPORT_BF16=0 CLANG=1 - fi - - # Start first instance - EXO_HOME="$(pwd)/.exo_cache_node1" DEBUG_DISCOVERY=7 DEBUG=7 exo --inference-engine <> \ - --node-id "node1" --listen-port 5678 --broadcast-port 5679 --chatgpt-api-port 8000 \ - --chatgpt-api-response-timeout 900 --disable-tui > output1.log & - PID1=$! - tail -f output1.log & - TAIL1=$! - - # Start second instance - EXO_HOME="$(pwd)/.exo_cache_node2" DEBUG_DISCOVERY=7 DEBUG=7 exo --inference-engine <> \ - --node-id "node2" --listen-port 5679 --broadcast-port 5678 --chatgpt-api-port 8001 \ - --chatgpt-api-response-timeout 900 --disable-tui > output2.log & - PID2=$! - tail -f output2.log & - TAIL2=$! - - # Remember to kill the tail processes at the end - trap 'kill $TAIL1 $TAIL2' EXIT - - # Wait for discovery - sleep 10 - - # Function to check if processes are still running - check_processes() { - if ! kill -0 $PID1 2>/dev/null; then - echo "First instance (PID $PID1) died unexpectedly. Log output:" - cat output1.log - exit 1 - fi - if ! kill -0 $PID2 2>/dev/null; then - echo "Second instance (PID $PID2) died unexpectedly. Log output:" - cat output2.log - exit 1 - fi - } - - # Check processes before proceeding - check_processes - - echo "Sending request to first instance..." - response_1=$(curl -s http://localhost:8000/v1/chat/completions \ - -H "Content-Type: application/json" \ - -d '{ - "model": "<>", - "messages": [{"role": "user", "content": "<>"}], - "temperature": 0.7 - }') - echo "Response 1: $response_1" - - # Check processes after first response - check_processes - - echo "Sending request to second instance..." - response_2=$(curl -s http://localhost:8001/v1/chat/completions \ - -H "Content-Type: application/json" \ - -d '{ - "model": "<>", - "messages": [{"role": "user", "content": "<>"}], - "temperature": 0.7 - }') - echo "Response 2: $response_2" - - # Check processes after second response - check_processes - - # Stop both instances - kill $PID1 $PID2 - - echo "" - # Extract content using jq and check if it contains expected output - content1=$(echo "$response_1" | jq -r '.choices[0].message.content') - content2=$(echo "$response_2" | jq -r '.choices[0].message.content') - - if [[ "$content1" != *"<>"* ]] || [[ "$content2" != *"<>"* ]]; then - echo "Test failed: Response does not match '<>'" - echo "Response 1 content: $content1" - echo "" - echo "Response 2 content: $content2" - echo "Output of first instance:" - cat output1.log - echo "Output of second instance:" - cat output2.log - exit 1 - else - echo "Test passed: Response from both nodes matches '<>'" - fi - -jobs: - unit_test: - macos: - xcode: "16.0.0" - resource_class: m2pro.large - steps: - - checkout - - run: - name: Set up Python - command: | - brew install python@3.12 - python3.12 -m venv env - source env/bin/activate - - run: - name: Install dependencies - command: | - source env/bin/activate - pip install --upgrade pip - pip install . - - run: - name: Run tests - command: | - source env/bin/activate - # set TEMPERATURE to 0 for deterministic sampling - echo "Running inference engine tests..." - METAL_DEVICE_WRAPPER_TYPE=1 METAL_DEBUG_ERROR_MODE=0 METAL_XCODE=1 TEMPERATURE=0 python3 -m exo.inference.test_inference_engine - echo "Running tokenizer tests..." - python3 ./test/test_tokenizers.py - python3 ./test/test_model_helpers.py - - discovery_integration_test: - macos: - xcode: "16.0.0" - steps: - - checkout - - run: - name: Set up Python - command: | - brew install python@3.12 - python3.12 -m venv env - source env/bin/activate - - run: - name: Install dependencies - command: | - source env/bin/activate - pip install --upgrade pip - pip install . - - run: - name: Run discovery integration test - command: | - source env/bin/activate - DEBUG_DISCOVERY=7 DEBUG=7 exo --node-id "node1" --listen-port 5678 --broadcast-port 5679 --chatgpt-api-port 8000 --disable-tui > output1.log 2>&1 & - PID1=$! - DEBUG_DISCOVERY=7 DEBUG=7 exo --node-id "node2" --listen-port 5679 --broadcast-port 5678 --chatgpt-api-port 8001 --disable-tui > output2.log 2>&1 & - PID2=$! - sleep 10 - kill $PID1 $PID2 - if grep -q "Peer statuses: {\\'node2\\': \\'is_connected=True, health_check=True" output1.log && ! grep -q "Failed to connect peers:" output1.log && grep -q "Peer statuses: {\\'node1\\': \\'is_connected=True, health_check=True" output2.log && ! grep -q "Failed to connect peers:" output2.log; then - echo "Test passed: Both instances discovered each other" - exit 0 - else - echo "Test failed: Devices did not discover each other" - echo "Output of first instance:" - cat output1.log - echo "Output of second instance:" - cat output2.log - exit 1 - fi - - chatgpt_api_integration_test_mlx: - macos: - xcode: "16.0.0" - resource_class: m2pro.large - steps: - - checkout - - run: - name: Set up Python - command: | - brew install python@3.12 - python3.12 -m venv env - source env/bin/activate - - run: - name: Install dependencies - command: | - source env/bin/activate - pip install --upgrade pip - pip install . - - run_chatgpt_api_test: - inference_engine: mlx - model_id: llama-3.2-1b - prompt: "Keep responses concise. Who was the king of pop?" - expected_output: "Michael Jackson" - - chatgpt_api_integration_test_dummy: - macos: - xcode: "16.0.0" - resource_class: m2pro.large - steps: - - checkout - - run: - name: Set up Python - command: | - brew install python@3.12 - python3.12 -m venv env - source env/bin/activate - - run: - name: Install dependencies - command: | - source env/bin/activate - pip install --upgrade pip - pip install . - - run_chatgpt_api_test: - inference_engine: dummy - model_id: dummy - prompt: "Dummy prompt." - expected_output: "dummy" - - chatgpt_api_integration_test_tinygrad: - macos: - xcode: "16.0.0" - resource_class: m2pro.large - steps: - - checkout - - run: - name: Set up Python - command: | - brew install python@3.12 - python3.12 -m venv env - source env/bin/activate - - run: - name: Install dependencies - command: | - source env/bin/activate - pip install --upgrade pip - pip install . - - run_chatgpt_api_test: - inference_engine: tinygrad - model_id: llama-3.2-1b - prompt: "Keep responses concise. Who was the king of pop?" - expected_output: "Michael Jackson" - - chatgpt_api_integration_test_tinygrad_linux: - machine: - image: ubuntu-2204:current - resource_class: xlarge - steps: - - checkout - - run: - name: Set up Python - command: | - export DEBIAN_FRONTEND=noninteractive - export DEBCONF_NONINTERACTIVE_SEEN=true - sudo apt-get update - sudo add-apt-repository -y ppa:deadsnakes/ppa - sudo apt-get update - sudo apt-get install -y python3.12 python3.12-venv clang - python3.12 -m venv env - source env/bin/activate - - run: - name: Install dependencies - command: | - source env/bin/activate - pip install --upgrade pip - pip install . - - run_chatgpt_api_test: - inference_engine: tinygrad - model_id: llama-3.2-1b - prompt: "Keep responses concise. Who was the king of pop?" - expected_output: "Michael Jackson" - - measure_pip_sizes: - macos: - xcode: "16.0.0" - steps: - - checkout - - run: - name: Set up Python - command: | - brew install python@3.12 - python3.12 -m venv env - source env/bin/activate - - run: - name: Install dependencies and measure sizes - command: | - source env/bin/activate - pip install --upgrade pip - pip install . - python ./extra/pipsize.py --json ./pipsize.json - - store_artifacts: - path: ./pipsize.json - destination: pip-sizes.json - - check_line_count: - docker: - - image: cimg/python:3.10 - steps: - - checkout - - - run: - name: Setup git for PR comparison - command: | - if [[ -n "$CIRCLE_PULL_REQUEST" ]]; then - PR_NUMBER=$(echo $CIRCLE_PULL_REQUEST | rev | cut -d'/' -f1 | rev) - BASE_BRANCH=$(curl -s -H "Circle-Token: $CIRCLE_TOKEN" \ - "https://circleci.com/api/v2/project/github/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/pipeline/$CIRCLE_WORKFLOW_ID" \ - | jq -r '.target_branch') - - git clone -b $BASE_BRANCH --single-branch \ - https://github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME.git \ - base_branch - fi - - - run: - name: Install dependencies - command: | - python -m pip install --upgrade pip - pip install tabulate - - - run: - name: Run line count check - command: | - if [[ -n "$CIRCLE_PULL_REQUEST" ]]; then - python extra/line_counter.py base_branch . - else - python extra/line_counter.py . - fi - - - store_artifacts: - path: line-count-snapshot.json - destination: line-count-snapshot.json - - - store_artifacts: - path: line-count-diff.json - destination: line-count-diff.json - - - run: - name: Create test results directory - command: | - mkdir -p test-results/line-count - cp line-count-*.json test-results/line-count/ - - - store_test_results: - path: test-results - -workflows: - version: 2 - build_and_test: - jobs: - - check_line_count: - filters: - branches: - only: /.*/ - tags: - only: /.*/ - - unit_test - - discovery_integration_test - - chatgpt_api_integration_test_mlx - - chatgpt_api_integration_test_tinygrad - - chatgpt_api_integration_test_tinygrad_linux - - chatgpt_api_integration_test_dummy - - measure_pip_sizes diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index ab26661d..00000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -*.mp3 filter=lfs diff=lfs merge=lfs -text -*.png filter=lfs diff=lfs merge=lfs -text diff --git a/.github/bench.py b/.github/bench.py deleted file mode 100644 index 9bc52e89..00000000 --- a/.github/bench.py +++ /dev/null @@ -1,401 +0,0 @@ -import aiohttp -import asyncio -import time -import json -import os -import boto3 -from typing import Dict, Any -from datetime import datetime -import subprocess -import psutil -import platform -from pathlib import Path - - -def check_system_state(): - print("\n=== System State Check ===", flush=True) - - # Add macOS-specific checks - try: - # Check powermetrics with sudo - try: - power_metrics = subprocess.run( - ['sudo', 'powermetrics', '-n', '1', '-i', '1000', '--samplers', 'cpu_power'], - capture_output=True, text=True - ) - print("\nPower Metrics:", power_metrics.stdout, flush=True) - except Exception as e: - print(f"Error getting power metrics: {e}", flush=True) - - # Check thermal state - thermal_state = subprocess.run(['pmset', '-g', 'therm'], capture_output=True, text=True) - print("\nThermal State:", thermal_state.stdout, flush=True) - - # Check if running under Rosetta - arch = subprocess.run(['arch'], capture_output=True, text=True) - print("\nArchitecture:", arch.stdout, flush=True) - - # Check MLX compilation mode - only if mlx is available - try: - import mlx.core as mx - if hasattr(mx, 'build_info'): - print("\nMLX Build Info:", mx.build_info(), flush=True) - else: - print("\nMLX Build Info: Not available in this version", flush=True) - except ImportError: - print("\nMLX: Not installed", flush=True) - except Exception as e: - print(f"\nError checking MLX: {e}", flush=True) - - except Exception as e: - print(f"Error in macOS checks: {e}", flush=True) - - # CPU Info - print("\nCPU Information:", flush=True) - try: - if platform.system() == 'Darwin' and platform.processor() == 'arm': - # Use sysctl for Apple Silicon Macs - cpu_info = subprocess.run(['sysctl', 'machdep.cpu'], capture_output=True, text=True) - if cpu_info.returncode == 0: - print(f"CPU Info (Apple Silicon):", cpu_info.stdout, flush=True) - - # Parse powermetrics output for clearer CPU frequency display - try: - power_metrics = subprocess.run( - ['sudo', 'powermetrics', '-n', '1', '-i', '100', '--samplers', 'cpu_power'], - capture_output=True, text=True - ) - if power_metrics.returncode == 0: - output = power_metrics.stdout - print("\nDetailed CPU Frequency Information:") - - # Extract cluster frequencies and max frequencies - current_cluster = None - max_freqs = {'E': 0, 'P0': 0, 'P1': 0} - - for line in output.split('\n'): - # Track which cluster we're processing - if "E-Cluster" in line: - current_cluster = 'E' - elif "P0-Cluster" in line: - current_cluster = 'P0' - elif "P1-Cluster" in line: - current_cluster = 'P1' - - # Get current frequencies - if "HW active frequency:" in line: - freq = line.split(':')[1].strip() - if freq != "0 MHz": - print(f"Current {current_cluster}-Cluster Frequency: {freq}") - - # Get max frequencies from residency lines - if current_cluster and "active residency:" in line and "MHz:" in line: - try: - # Extract all frequency values - freqs = [] - parts = line.split('MHz:')[:-1] # Skip last part as it's not a frequency - for part in parts: - freq_str = part.split()[-1] - try: - freq = float(freq_str) - freqs.append(freq) - except ValueError: - continue - if freqs: - max_freqs[current_cluster] = max(max_freqs[current_cluster], max(freqs)) - except Exception: - continue - - # Print max frequencies - print("\nMaximum Available Frequencies:") - for cluster, max_freq in max_freqs.items(): - if max_freq > 0: - print(f"{cluster}-Cluster Max: {max_freq:.0f} MHz") - - except Exception as e: - print(f"Error parsing powermetrics: {e}", flush=True) - else: - # Use psutil for other systems - cpu_freq = psutil.cpu_freq() - print(f"CPU Frequency - Current: {cpu_freq.current:.2f}MHz, Min: {cpu_freq.min:.2f}MHz, Max: {cpu_freq.max:.2f}MHz", flush=True) - - print(f"\nCPU Usage per Core: {psutil.cpu_percent(percpu=True)}%", flush=True) - - # Check if running in low power mode - power_mode = subprocess.run(['pmset', '-g'], capture_output=True, text=True) - print("\nPower Settings:", power_mode.stdout, flush=True) - except Exception as e: - print(f"Error getting CPU info: {e}", flush=True) - - # Memory Info - print("\nMemory Information:", flush=True) - try: - mem = psutil.virtual_memory() - print(f"Total: {mem.total/1024/1024/1024:.2f}GB", flush=True) - print(f"Available: {mem.available/1024/1024/1024:.2f}GB", flush=True) - print(f"Used: {mem.used/1024/1024/1024:.2f}GB ({mem.percent}%)", flush=True) - - # Check swap - swap = psutil.swap_memory() - print(f"Swap Used: {swap.used/1024/1024/1024:.2f}GB of {swap.total/1024/1024/1024:.2f}GB", flush=True) - except Exception as e: - print(f"Error getting memory info: {e}", flush=True) - - # GPU Info - print("\nGPU Information:", flush=True) - try: - # Check MLX GPU settings - print("MLX Environment Variables:", flush=True) - mlx_vars = {k: v for k, v in os.environ.items() if k.startswith('MLX')} - print(json.dumps(mlx_vars, indent=2), flush=True) - - # Check Metal GPU memory allocation - gpu_mem = subprocess.run(['sysctl', 'iogpu'], capture_output=True, text=True) - print("GPU Memory Settings:", gpu_mem.stdout, flush=True) - except Exception as e: - print(f"Error getting GPU info: {e}", flush=True) - - # Process Priority - print("\nProcess Priority Information:", flush=True) - try: - current_process = psutil.Process() - print(f"Process Nice Value: {current_process.nice()}", flush=True) - # Only try to get ionice if the platform supports it - if hasattr(current_process, 'ionice'): - print(f"Process IO Nice Value: {current_process.ionice()}", flush=True) - except Exception as e: - print(f"Error getting process priority info: {e}", flush=True) - - # System Load - print("\nSystem Load:", flush=True) - try: - load_avg = psutil.getloadavg() - print(f"Load Average: {load_avg}", flush=True) - - # Get top processes by CPU and Memory - print("\nTop Processes by CPU Usage:", flush=True) - processes = [] - for proc in psutil.process_iter(['pid', 'name', 'cpu_percent', 'memory_percent']): - try: - pinfo = proc.info - if pinfo['cpu_percent'] is not None and pinfo['memory_percent'] is not None: - processes.append(pinfo) - except (psutil.NoSuchProcess, psutil.AccessDenied): - continue - - # Sort and display top 5 CPU-consuming processes - sorted_by_cpu = sorted(processes, key=lambda x: x['cpu_percent'] or 0, reverse=True)[:5] - for proc in sorted_by_cpu: - print(f"PID: {proc['pid']}, Name: {proc['name']}, CPU: {proc['cpu_percent']}%, Memory: {proc['memory_percent']:.1f}%") - except Exception as e: - print(f"Error getting system load info: {e}", flush=True) - - print("\n=== End System State Check ===\n", flush=True) - - -def check_gpu_access(): - try: - # Check if MLX can see the GPU - import mlx.core as mx - print("MLX device info:", mx.default_device()) - - # Check Metal device availability - result = subprocess.run(['system_profiler', 'SPDisplaysDataType'], capture_output=True, text=True) - print("GPU Info:", result.stdout) - except Exception as e: - print(f"Failed to check GPU access: {e}") - - -async def measure_performance(api_endpoint: str, prompt: str, model: str) -> Dict[str, Any]: - """ - Measures the performance of an API endpoint by sending a prompt and recording metrics. - - Args: - api_endpoint (str): The API endpoint URL. - prompt (str): The prompt to send to the API. - - Returns: - Dict[str, Any]: A dictionary containing performance metrics or error information. - """ - - results = { - 'model': model, - 'run_id': os.environ.get('GITHUB_RUN_ID', 'unknown'), - 'branch': os.environ.get('GITHUB_REF_NAME', 'unknown'), - 'commit': os.environ.get('GITHUB_SHA', 'unknown'), - 'configuration': json.loads(os.environ.get('HARDWARE_CONFIG', '{}')) - } - - # Get token count - session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=600, connect=10, sock_read=600, sock_connect=10)) - try: - response = await session.post( - "http://localhost:52415/v1/chat/token/encode", - json={ - "model": model, - "messages": [{"role": "user", "content": prompt}] - } - ) - response.raise_for_status() - token_data = await response.json() - results['prompt_len'] = token_data['num_tokens'] - except Exception as e: - await session.close() - raise RuntimeError(f"Failed to get token count: {str(e)}") - - # Measure completion performance - try: - start_time = time.time() - response = await session.post( - api_endpoint, - json={ - "model": model, - "messages": [{"role": "user", "content": prompt}], - "temperature": 0, - "stream": True - } - ) - response.raise_for_status() - - first_token_time = None - total_tokens = 0 - - async for line in response.content.iter_chunks(): - line = line[0].decode('utf-8').strip() - if not line.startswith('data: '): - continue - - data = json.loads(line[6:]) # Skip 'data: ' prefix - if content := data.get('choices', [{}])[0].get('delta', {}).get('content'): - print(f"Received content: {content}", flush=True) - if first_token_time is None: - first_token_time = time.time() - ttft = first_token_time - start_time - results.update({ - 'ttft': ttft, - 'prompt_tps': results['prompt_len'] / ttft - }) - total_tokens += 1 - - total_time = time.time() - start_time - results.update({ - 'generation_tps': total_tokens / total_time, - 'response_len': total_tokens, - 'total_time': total_time - }) - - except Exception as e: - raise RuntimeError(f"Performance measurement failed: {str(e)}") - finally: - await session.close() - - return results - - -async def main() -> None: - api_endpoint = "http://localhost:52415/v1/chat/completions" - - # Define prompts - prompt_warmup = "what is the capital of France?" - prompt_essay = "write an essay about cats" - - model = os.environ.get('model', 'llama-3.2-1b') - # Warmup request - print("\nPerforming warmup request...", flush=True) - try: - warmup_results = await measure_performance(api_endpoint, prompt_warmup, model) - print("Warmup completed successfully", flush=True) - except Exception as e: - print(f"Warmup request failed: {e}", flush=True) - - # Measure performance for the essay prompt - print("\nMeasuring performance for the essay prompt...", flush=True) - results = await measure_performance(api_endpoint, prompt_essay, model) - - try: - s3_client = boto3.client( - 's3', - aws_access_key_id=os.environ.get('aws_access_key_id'), - aws_secret_access_key=os.environ.get('aws_secret_key') - ) - job_name = os.environ.get('GITHUB_JOB') - - # Create S3 key with timestamp and commit info - now = datetime.utcnow() - timestamp = now.strftime('%H-%M-%S') - commit_sha = os.environ.get('GITHUB_SHA', 'unknown')[:7] - s3_key = f"{job_name}/{model}/{now.year}/{now.month}/{now.day}/{timestamp}_{commit_sha}.json" - - # Upload to S3 - s3_client.put_object( - Bucket='exo-benchmarks', - Key=s3_key, - Body=json.dumps(results), - ContentType='application/json' - ) - print(f"Performance metrics uploaded to S3: s3://exo-benchmarks/{s3_key}", flush=True) - except Exception as e: - print(f"Failed to upload metrics to S3: {e}", flush=True) - - # Optionally print the metrics for visibility - print("Performance metrics:", flush=True) - print(json.dumps(results, indent=4), flush=True) - - -def optimize_system_performance(): - """Set optimal system performance settings before running benchmark.""" - try: - # Try to set high performance power mode - subprocess.run(['sudo', 'pmset', '-a', 'powermode', '2'], check=False) - - # Ensure MLX uses performance cores and GPU - os.environ['MLX_FORCE_P_CORES'] = '1' - os.environ['MLX_METAL_PREWARM'] = '1' - os.environ['MLX_USE_GPU'] = '1' - - # Set process priority - current_process = psutil.Process() - try: - # Set highest priority - subprocess.run(['sudo', 'renice', '-n', '-20', '-p', str(current_process.pid)], check=False) - - # Print current process state - print("\nProcess State Before Benchmark:", flush=True) - proc_info = subprocess.run( - ['ps', '-o', 'pid,ppid,user,%cpu,%mem,nice,stat,pri,command', '-p', str(current_process.pid)], - capture_output=True, text=True - ) - print(proc_info.stdout, flush=True) - - # Verify power mode - power_info = subprocess.run(['pmset', '-g'], capture_output=True, text=True) - if 'powermode 0' in power_info.stdout: - print("\nWarning: System still in normal power mode. Trying to set high performance mode again...", flush=True) - subprocess.run(['sudo', 'pmset', '-a', 'powermode', '2'], check=False) - - except Exception as e: - print(f"Warning: Could not set process priority: {e}", flush=True) - - except Exception as e: - print(f"Warning: Could not optimize system performance: {e}", flush=True) - - # Print optimization status - print("\nOptimization Settings:", flush=True) - print("MLX Environment Variables:", flush=True) - for var in ['MLX_FORCE_P_CORES', 'MLX_METAL_PREWARM', 'MLX_USE_GPU']: - print(f"{var}: {os.environ.get(var, 'Not set')}", flush=True) - - try: - nice_value = psutil.Process().nice() - print(f"Process Nice Value: {nice_value}", flush=True) - if nice_value != -20: - print("Warning: Process not running at highest priority", flush=True) - except Exception: - pass - - -if __name__ == "__main__": - check_system_state() - check_gpu_access() - optimize_system_performance() - asyncio.run(main()) diff --git a/.github/bootstrap.sh b/.github/bootstrap.sh deleted file mode 100755 index 03b13079..00000000 --- a/.github/bootstrap.sh +++ /dev/null @@ -1,330 +0,0 @@ -#!/bin/bash -set -e - -command_exists() { - command -v "$1" >/dev/null 2>&1 -} - -log() { - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" -} - -if [ "$EUID" -eq 0 ]; then - log "Please do not run as root. Run as regular user with sudo access." - exit 1 -fi - -# Check for required arguments -if [ -z "$1" ]; then - log "Error: Runner token is required" - log "Usage: $0 [tailscale-auth-key]" - exit 1 -fi - -RUNNER_TOKEN=$1 -TAILSCALE_AUTH_KEY=$2 -REPO="exo-explore/exo" - -# Add sudoers configuration -log "Configuring sudo access..." -SUDOERS_CONTENT="$(whoami) ALL=(ALL) NOPASSWD: ALL" -echo "$SUDOERS_CONTENT" | sudo tee /etc/sudoers.d/github-runner > /dev/null -sudo chmod 440 /etc/sudoers.d/github-runner - -log "Configuring privacy permissions..." -sudo tccutil reset All -sudo tccutil reset SystemPolicyAllFiles -sudo tccutil reset SystemPolicyNetworkVolumes - -# Configure power management for maximum performance -log "Configuring power management..." -sudo pmset -a powermode 2 # Force highest performance mode -sudo pmset -a gpuswitch 2 # Force discrete/high-performance GPU -sudo pmset -a lowpowermode 0 -sudo pmset -a lessbright 0 -sudo pmset -a disablesleep 1 -sudo pmset -a sleep 0 -sudo pmset -a hibernatemode 0 -sudo pmset -a autopoweroff 0 -sudo pmset -a standby 0 -sudo pmset -a powernap 0 - -# For Python specifically -PYTHON_PATH="/opt/homebrew/bin/python3.12" -sudo chmod 755 "$PYTHON_PATH" - -# Add to firewall -log "Configuring firewall access..." -sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add "$PYTHON_PATH" -sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblock "$PYTHON_PATH" - -# Set Homebrew paths based on architecture -if [ "$(uname -p)" = "arm" ]; then - BREW_PREFIX="/opt/homebrew" -else - BREW_PREFIX="/usr/local" -fi - -# Install Homebrew if not present -if ! command_exists brew; then - log "Installing Homebrew..." - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zshrc - eval "$(/opt/homebrew/bin/brew shellenv)" -fi - -# Install required packages -log "Installing required packages..." -export HOMEBREW_NO_AUTO_UPDATE=1 -brew install python@3.12 coreutils - -# Optional Tailscale setup if auth key is provided -if [ -n "$TAILSCALE_AUTH_KEY" ]; then - log "Installing and configuring Tailscale..." - brew install --quiet tailscale - sudo brew services stop tailscale 2>/dev/null || true - sudo rm -f /var/db/tailscale/tailscaled.state 2>/dev/null || true - sudo brew services start tailscale - sleep 2 - sudo tailscale up --authkey=$TAILSCALE_AUTH_KEY - - # Enable SSH and Screen Sharing - log "Enabling remote access services..." - sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist - sudo /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart \ - -activate \ - -configure -access -on \ - -configure -allowAccessFor -allUsers \ - -configure -restart -agent -privs -all - - # Create launch daemon for remote access - sudo bash -c 'cat > /Library/LaunchDaemons/com.remote.access.setup.plist' << 'EOL' - - - - - Label - com.remote.access.setup - ProgramArguments - - /bin/bash - -c - - launchctl load -w /System/Library/LaunchDaemons/ssh.plist; - /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -activate -configure -access -on - - - RunAtLoad - - - -EOL - - sudo chmod 644 /Library/LaunchDaemons/com.remote.access.setup.plist - sudo launchctl load -w /Library/LaunchDaemons/com.remote.access.setup.plist -fi - -# Configure GitHub Actions Runner -log "Gathering system metadata..." -MACHINE_NAME=$(scutil --get ComputerName) -MACHINE_NAME="runner-$(echo -n "$MACHINE_NAME" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]-')" - -# Enhanced Apple Silicon detection -MACHINE_INFO=$(system_profiler SPHardwareDataType) -CHIP_FULL=$(echo "$MACHINE_INFO" | grep "Chip" | cut -d: -f2 | xargs) -if [[ $CHIP_FULL =~ "Apple" ]]; then - CHIP_MODEL=$(echo "$CHIP_FULL" | sed 's/^Apple //' | tr -d ' ' | tr '[:lower:]' '[:upper:]') - GPU_CORES=$(ioreg -l | grep "gpu-core-count" | awk -F'= ' '{print $2}') - if [ -z "$GPU_CORES" ]; then - GPU_CORES="N/A" - fi -else - CHIP_MODEL="Intel" - GPU_CORES="N/A" -fi - -MEMORY=$(($(sysctl -n hw.memsize) / 1024 / 1024 / 1024)) - -# Set up GitHub Runner -RUNNER_DIR="$HOME/actions-runner" - -# Check if runner is already configured -if [ -f "$RUNNER_DIR/.runner" ]; then - log "Runner already configured. Stopping existing service..." - sudo launchctl unload /Library/LaunchDaemons/com.github.runner.plist 2>/dev/null || true -fi - -# Create runner directory if it doesn't exist -mkdir -p "$RUNNER_DIR" -cd "$RUNNER_DIR" - -CUSTOM_LABELS="self-hosted,macos,arm64,${CHIP_MODEL}_GPU${GPU_CORES}_${MEMORY}GB" - -# Only download and extract if not already present or if forced -if [ ! -f "$RUNNER_DIR/run.sh" ] || [ "${FORCE_SETUP:-false}" = "true" ]; then - log "Downloading GitHub Actions runner..." - RUNNER_VERSION=$(curl -s https://api.github.com/repos/actions/runner/releases/latest | grep '"tag_name":' | cut -d'"' -f4) - curl -o actions-runner.tar.gz -L "https://github.com/actions/runner/releases/download/${RUNNER_VERSION}/actions-runner-osx-arm64-${RUNNER_VERSION#v}.tar.gz" - tar xzf actions-runner.tar.gz - rm actions-runner.tar.gz -else - log "Runner already downloaded, skipping download step" -fi - -log "Configuring runner with labels: $CUSTOM_LABELS" -./config.sh --unattended \ - --url "https://github.com/${REPO}" \ - --token "${RUNNER_TOKEN}" \ - --name "${MACHINE_NAME}" \ - --labels "${CUSTOM_LABELS}" \ - --work "_work" - -# Set optimal performance settings -log "Configuring system for optimal performance..." - -# Configure CPU performance -log "Setting CPU performance controls..." -# Disable timer coalescing -sudo sysctl -w kern.timer.coalescing_enabled=0 -sudo sysctl -w kern.timer_coalesce_bg_scale=-5 -sudo sysctl -w kern.timer_resort_threshold_ns=0 -# Set minimum timer intervals -sudo sysctl -w kern.wq_max_timer_interval_usecs=1000 -sudo sysctl -w kern.timer_coalesce_bg_ns_max=1000 -# Set minimum timer coalescing for all tiers -sudo sysctl -w kern.timer_coalesce_tier0_scale=-5 -sudo sysctl -w kern.timer_coalesce_tier0_ns_max=1000 -sudo sysctl -w kern.timer_coalesce_tier1_scale=-5 -sudo sysctl -w kern.timer_coalesce_tier1_ns_max=1000 -sudo sysctl -w kern.timer_coalesce_tier2_scale=-5 -sudo sysctl -w kern.timer_coalesce_tier2_ns_max=1000 -sudo sysctl -w kern.timer_coalesce_tier3_scale=-5 -sudo sysctl -w kern.timer_coalesce_tier3_ns_max=1000 -sudo sysctl -w kern.timer_coalesce_tier4_scale=-5 -sudo sysctl -w kern.timer_coalesce_tier4_ns_max=1000 -# Disable QoS restrictions -sudo sysctl -w net.qos.policy.restricted=0 -sudo sysctl -w net.qos.policy.restrict_avapps=0 -sudo sysctl -w net.qos.policy.wifi_enabled=0 -sudo sysctl -w net.qos.policy.capable_enabled=0 -# Set scheduler parameters -sudo sysctl -w kern.sched_rt_avoid_cpu0=0 -sudo sysctl -w debug.sched=2 -sudo sysctl -w net.pktsched.netem.sched_output_ival_ms=1 - -# Clean up any existing runner services -log "Cleaning up existing runner services..." -for service in com.github.runner com.github.runner.monitor com.github.runner.cpuaffinity com.github.runner.affinity; do - sudo launchctl bootout system/$service 2>/dev/null || true - sudo rm -f /Library/LaunchDaemons/$service.plist -done - -# Create a simple runner service configuration -sudo tee /Library/LaunchDaemons/com.github.runner.plist > /dev/null << EOF - - - - - Label - com.github.runner - UserName - $(whoami) - GroupName - staff - WorkingDirectory - $RUNNER_DIR - ProgramArguments - - $RUNNER_DIR/run.sh - - RunAtLoad - - KeepAlive - - SuccessfulExit - - Crashed - - - ProcessType - Interactive - LowPriorityIO - - AbandonProcessGroup - - EnableTransactions - - ThrottleInterval - 0 - HardResourceLimits - - NumberOfFiles - 524288 - MemoryLock - -1 - - SoftResourceLimits - - NumberOfFiles - 524288 - MemoryLock - -1 - - QOSClass - User-Interactive - StandardOutPath - $RUNNER_DIR/_diag/runner.log - StandardErrorPath - $RUNNER_DIR/_diag/runner.err - EnvironmentVariables - - PATH - /usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin - - Nice - -20 - - -EOF - -# Set proper permissions for the LaunchDaemon -sudo chown root:wheel /Library/LaunchDaemons/com.github.runner.plist -sudo chmod 644 /Library/LaunchDaemons/com.github.runner.plist - -# Remove any existing service -sudo launchctl bootout system/com.github.runner 2>/dev/null || true - -# Load the new service using bootstrap -sudo launchctl bootstrap system /Library/LaunchDaemons/com.github.runner.plist - -# Add Runner.Listener permissions (after runner installation) -RUNNER_PATH="$RUNNER_DIR/bin/Runner.Listener" -sudo chmod 755 "$RUNNER_PATH" -sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add "$RUNNER_PATH" -sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblock "$RUNNER_PATH" - -# Create connection info file if Tailscale is configured -if [ -n "$TAILSCALE_AUTH_KEY" ]; then - TAILSCALE_IP=$(tailscale ip) - cat > "$HOME/remote_access_info.txt" << EOL -Mac Remote Access Information -============================ -Computer Name: $MACHINE_NAME -Username: $USER -Tailscale IP: $TAILSCALE_IP - -SSH Command: ssh $USER@$TAILSCALE_IP -Screen Sharing: vnc://$TAILSCALE_IP -EOL - chmod 600 "$HOME/remote_access_info.txt" -fi - -log "Verifying runner service status..." -if sudo launchctl list | grep com.github.runner > /dev/null; then - log "GitHub Actions runner service is running successfully!" - log "Runner labels: $CUSTOM_LABELS" - [ -n "$TAILSCALE_AUTH_KEY" ] && log "Remote access details saved to: $HOME/remote_access_info.txt" -else - log "Error: Failed to start GitHub Actions runner service" - exit 1 -fi \ No newline at end of file diff --git a/.github/optimize_performance.sh b/.github/optimize_performance.sh deleted file mode 100755 index 7b5c77be..00000000 --- a/.github/optimize_performance.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/bash -set -e - -# Function to log with timestamp -log() { - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" -} - -log "Applying comprehensive performance optimizations..." - -# System-wide power management -log "Configuring power management..." -sudo pmset -a lessbright 0 -sudo pmset -a disablesleep 1 -sudo pmset -a sleep 0 -sudo pmset -a hibernatemode 0 -sudo pmset -a autopoweroff 0 -sudo pmset -a standby 0 -sudo pmset -a powernap 0 -sudo pmset -a proximitywake 0 -sudo pmset -a tcpkeepalive 1 -sudo pmset -a powermode 2 -sudo pmset -a gpuswitch 2 -sudo pmset -a displaysleep 0 -sudo pmset -a disksleep 0 - -# Memory and kernel optimizations -log "Configuring memory and kernel settings..." -sudo sysctl -w kern.memorystatus_purge_on_warning=0 -sudo sysctl -w kern.memorystatus_purge_on_critical=0 -sudo sysctl -w kern.timer.coalescing_enabled=0 - -# Metal and GPU optimizations -log "Configuring Metal and GPU settings..." -defaults write com.apple.CoreML MPSEnableGPUValidation -bool false -defaults write com.apple.CoreML MPSEnableMetalValidation -bool false -defaults write com.apple.CoreML MPSEnableGPUDebug -bool false -defaults write com.apple.Metal GPUDebug -bool false -defaults write com.apple.Metal GPUValidation -bool false -defaults write com.apple.Metal MetalValidation -bool false -defaults write com.apple.Metal MetalCaptureEnabled -bool false -defaults write com.apple.Metal MTLValidationBehavior -string "Disabled" -defaults write com.apple.Metal EnableMTLDebugLayer -bool false -defaults write com.apple.Metal MTLDebugLevel -int 0 -defaults write com.apple.Metal PreferIntegratedGPU -bool false -defaults write com.apple.Metal ForceMaximumPerformance -bool true -defaults write com.apple.Metal MTLPreferredDeviceGPUFrame -bool true - -# Create MPS cache directory with proper permissions -sudo mkdir -p /tmp/mps_cache -sudo chmod 777 /tmp/mps_cache - -# Process and resource limits -log "Configuring process limits..." -sudo launchctl limit maxfiles 524288 524288 -ulimit -n 524288 || log "Warning: Could not set file descriptor limit" -ulimit -c 0 -ulimit -l unlimited || log "Warning: Could not set memory lock limit" - -# Export performance-related environment variables -cat << 'EOF' > /tmp/performance_env.sh -# Metal optimizations -export MTL_DEBUG_LAYER=0 -export METAL_DEVICE_WRAPPER_TYPE=1 -export METAL_DEBUG_ERROR_MODE=0 -export METAL_FORCE_PERFORMANCE_MODE=1 -export METAL_DEVICE_PRIORITY=high -export METAL_MAX_COMMAND_QUEUES=1024 -export METAL_LOAD_LIMIT=0 -export METAL_VALIDATION_ENABLED=0 -export METAL_ENABLE_VALIDATION_LAYER=0 -export OBJC_DEBUG_MISSING_POOLS=NO -export MPS_CACHEDIR=/tmp/mps_cache - -# MLX optimizations -export MLX_USE_GPU=1 -export MLX_METAL_COMPILE_ASYNC=1 -export MLX_METAL_PREALLOCATE=1 -export MLX_METAL_MEMORY_GUARD=0 -export MLX_METAL_CACHE_KERNELS=1 -export MLX_PLACEMENT_POLICY=metal -export MLX_METAL_VALIDATION=0 -export MLX_METAL_DEBUG=0 -export MLX_FORCE_P_CORES=1 -export MLX_METAL_MEMORY_BUDGET=0 -export MLX_METAL_PREWARM=1 - -# Python optimizations -export PYTHONUNBUFFERED=1 -export PYTHONOPTIMIZE=2 -export PYTHONHASHSEED=0 -export PYTHONDONTWRITEBYTECODE=1 -EOF - -log "Performance optimizations completed. Environment variables written to /tmp/performance_env.sh" \ No newline at end of file diff --git a/.github/workflows/bench_job.yml b/.github/workflows/bench_job.yml deleted file mode 100644 index 5e089283..00000000 --- a/.github/workflows/bench_job.yml +++ /dev/null @@ -1,207 +0,0 @@ -# This is the reusable workflow file -name: Distributed Job Runner - -on: - workflow_call: - inputs: - config: - required: true - type: string - model: - required: true - type: string - calling_job_name: - required: true - type: string - network_interface: - required: true - type: string -jobs: - generate-matrix: - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - steps: - - id: set-matrix - env: - CONFIG: ${{ inputs.config }} - run: | - MATRIX=$(echo $CONFIG | jq -c '{cpu: [to_entries | .[] | .key as $k | range(.value) | $k]}') - echo "matrix=$MATRIX" >> $GITHUB_OUTPUT - - run-distributed-job: - needs: generate-matrix - strategy: - matrix: ${{fromJson(needs.generate-matrix.outputs.matrix)}} - runs-on: ['self-hosted', 'macOS', '${{ matrix.cpu }}'] - env: - HARDWARE_CONFIG: ${{ inputs.config }} - model: ${{ inputs.model }} - # Add performance-related environment variables - MTL_DEBUG_LAYER: 0 - METAL_VALIDATION_ENABLED: 0 - MLX_METAL_VALIDATION: 0 - MLX_METAL_DEBUG: 0 - MLX_FORCE_P_CORES: 1 - MLX_METAL_PREWARM: 1 - PYTHONOPTIMIZE: 2 - steps: - - name: Cleanup workspace - run: | - sudo rm -rf "$GITHUB_WORKSPACE" - sudo mkdir -p "$GITHUB_WORKSPACE" - sudo chown -R $(whoami):$(id -g) "$GITHUB_WORKSPACE" - - - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - export PATH="/usr/local/bin:/opt/homebrew/bin:$PATH" - python3.12 -m venv .venv || { - echo "Failed to find python3.12. Checking installation locations:" - ls -l /usr/local/bin/python* /opt/homebrew/bin/python* 2>/dev/null || true - exit 1 - } - source .venv/bin/activate - pip install --upgrade pip - pip install -e . - pip install boto3==1.35.76 - - - name: Apply Performance Optimizations - run: | - # Export performance-related environment variables - cat << 'EOF' > /tmp/performance_env.sh - # MLX and Metal optimizations - export MTL_DEBUG_LAYER=0 - export METAL_VALIDATION_ENABLED=0 - export MLX_METAL_VALIDATION=0 - export MLX_METAL_DEBUG=0 - export MLX_FORCE_P_CORES=1 - export MLX_METAL_PREWARM=1 - export PYTHONOPTIMIZE=2 - EOF - - # Source the performance environment variables - source /tmp/performance_env.sh - - # MLX Memory Settings - ./configure_mlx.sh - - # Verify optimizations - echo "Verifying performance settings..." - env | grep -E "MLX_|METAL_|MTL_" - - - name: Run exo - env: - aws_access_key_id: ${{ secrets.S3_EXO_BENCHMARKS_AWS_ACCESS_KEY_ID }} - aws_secret_key: ${{ secrets.S3_EXO_BENCHMARKS_AWS_SECRET_ACCESS_KEY }} - run: | - # Source performance environment variables - source /tmp/performance_env.sh - - # Debug information - echo "Current commit SHA: $GITHUB_SHA" - git rev-parse HEAD - git status - - CALLING_JOB="${{ inputs.calling_job_name }}" - UNIQUE_JOB_ID="${CALLING_JOB}_${model}_${GITHUB_RUN_ID}" - ALL_NODE_IDS=$(for i in $(seq ${{ strategy.job-total }} -1 0); do echo -n "${UNIQUE_JOB_ID}_${i},"; done | sed 's/,$//') - MY_NODE_ID="${UNIQUE_JOB_ID}_${{ strategy.job-index }}" - - source .venv/bin/activate - export PATH="/usr/local/bin:/opt/homebrew/bin:$PATH" - - echo "=== Before starting exo ===" - ps -eo pid,ppid,user,%cpu,%mem,nice,state,pri,command | head -1 - ps -eo pid,ppid,user,%cpu,%mem,nice,state,pri,command | grep -i python - - echo "Starting exo daemon..." - - echo "Power mode settings:" - sudo pmset -g - - # Start exo with explicit process control - sudo taskpolicy -d default -g default -a -t 0 -l 0 .venv/bin/exo \ - --node-id="${MY_NODE_ID}" \ - --node-id-filter="${ALL_NODE_IDS}" \ - --interface-type-filter="${{ inputs.network_interface }}" \ - --disable-tui \ - --max-generate-tokens 250 \ - --chatgpt-api-response-timeout 900 \ - --chatgpt-api-port 52415 > output1.log 2>&1 & - PID1=$! - - echo "Exo process started with PID: $PID1" - tail -f output1.log & - TAIL1=$! - - # Give process time to start - sleep 2 - - # Set additional process priorities - sudo renice -n -20 -p $PID1 - sudo taskpolicy -t 4 -p $PID1 - - echo "=== After starting exo ===" - ps -eo pid,ppid,user,%cpu,%mem,nice,state,pri,command | head -1 - ps -eo pid,ppid,user,%cpu,%mem,nice,state,pri,command | grep $PID1 - - echo "Additional process details:" - sudo powermetrics -n 1 -i 1000 --show-process-energy | grep -A 5 $PID1 || true - - trap 'kill $TAIL1' EXIT - trap 'kill $PID1' EXIT - - echo "Waiting for all nodes to connect..." - for i in {1..20}; do - echo "Attempt $i: Checking node count..." - nodes=$(curl -s http://localhost:52415/topology | jq ".nodes | length") - echo "Current node count: $nodes" - if [ "$nodes" -eq "${{ strategy.job-total }}" ]; then - echo "All nodes connected successfully!" - break - fi - if [ $i -eq 20 ]; then - echo "ERROR: Failed to connect all nodes after 20 attempts. Expected ${{ strategy.job-total }} nodes, but got $nodes" - exit 1 - fi - sleep 5 - done - - if ! kill -0 $PID1 2>/dev/null; then - echo "ERROR: Instance (PID $PID1) died unexpectedly. Full log output:" - cat output1.log - exit 1 - fi - - if [ "${{ strategy.job-index }}" -eq "0" ]; then - sleep 10 - echo "This is the primary node (index 0). Running benchmark..." - GITHUB_JOB=$CALLING_JOB python .github/bench.py - else - echo "This is a secondary node (index ${{ strategy.job-index }}). Waiting for completion..." - sleep 10 - while true; do - echo "Checking if primary node is still running..." - nodes=$(curl -s http://localhost:52415/topology | jq ".nodes | length") - echo "Current node count: $nodes" - if [ "$nodes" -lt "${{ strategy.job-total }}" ]; then - echo "Primary node completed, exiting..." - break - fi - sleep 5 - done - fi - - - name: Check Final System State - if: always() - run: | - echo "=== Final System State ===" - sudo pmset -g - sudo powermetrics -n 1 -i 1000 --show-process-energy || true - system_profiler SPDisplaysDataType - sysctl iogpu - ps -eo pid,ppid,user,%cpu,%mem,nice,state,command | grep -i python - env | grep -E "MLX_|METAL_|MTL_" - echo "=== End Final System State ===" diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml deleted file mode 100644 index 2750ac4a..00000000 --- a/.github/workflows/benchmarks.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Build and Test - -on: - push: - branches: [ '*' ] - tags: [ '*' ] - pull_request: - branches: [ '*' ] - -jobs: - single-m4-pro: - strategy: - matrix: - model: ['llama-3.2-1b', 'llama-3.2-3b', 'llama-3.1-8b'] - uses: ./.github/workflows/bench_job.yml - with: - config: '{"M4PRO_GPU16_24GB": 1}' - model: ${{ matrix.model }} - calling_job_name: 'single-m4-pro' - network_interface: 'Ethernet' - secrets: inherit - - two-m4-pro-cluster: - strategy: - matrix: - model: ['llama-3.2-1b', 'llama-3.2-3b', 'llama-3.1-8b'] - uses: ./.github/workflows/bench_job.yml - with: - config: '{"M4PRO_GPU16_24GB": 2}' - model: ${{ matrix.model }} - calling_job_name: 'two-m4-pro-cluster' - network_interface: 'Ethernet' - secrets: inherit - - # two-m4-pro-cluster-thunderbolt: - # strategy: - # matrix: - # model: ['llama-3.2-1b', 'llama-3.2-3b', 'llama-3.1-8b'] - # uses: ./.github/workflows/bench_job.yml - # with: - # config: '{"M4PRO_GPU16_24GB": 2}' - # model: ${{ matrix.model }} - # calling_job_name: 'two-m4-pro-cluster-thunderbolt' - # network_interface: 'Thunderbolt' - # secrets: inherit - - three-m4-pro-cluster: - strategy: - matrix: - model: ['llama-3.2-1b', 'llama-3.2-3b', 'llama-3.1-8b', 'llama-3.3-70b'] - fail-fast: false - uses: ./.github/workflows/bench_job.yml - with: - config: '{"M4PRO_GPU16_24GB": 3}' - model: ${{ matrix.model }} - calling_job_name: 'three-m4-pro-cluster' - network_interface: 'Ethernet' - secrets: inherit - - # test-m3-single-node: - # strategy: - # matrix: - # model: ['llama-3.2-1b'] - # fail-fast: false - # uses: ./.github/workflows/bench_job.yml - # with: - # config: '{"M3MAX_GPU40_128GB": 1}' - # model: ${{ matrix.model }} - # calling_job_name: 'test-m3-cluster' - # network_interface: 'Ethernet' - # secrets: inherit \ No newline at end of file diff --git a/.gitignore b/.gitignore deleted file mode 100644 index bc6a38d7..00000000 --- a/.gitignore +++ /dev/null @@ -1,175 +0,0 @@ -__pycache__/ -.venv* -test_weights.npz -.exo_used_ports -.exo_node_id -.idea -.DS_Store - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -/.Python -/develop-eggs/ -/dist/ -/downloads/ -/eggs/ -/.eggs/ -/lib/ -/lib64/ -/parts/ -/sdist/ -/var/ -/wheels/ -/share/python-wheels/ -/*.egg-info/ -/.installed.cfg -/*.egg -/MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints -Untitled.ipynb - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/latest/usage/project/#working-with-version-control -.pdm.toml -.pdm-python -.pdm-build/ - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -**/*.xcodeproj/* -.aider* - -exo/tinychat/images/*.png diff --git a/.style.yapf b/.style.yapf deleted file mode 100644 index 7301910e..00000000 --- a/.style.yapf +++ /dev/null @@ -1,19 +0,0 @@ -[style] -based_on_style = pep8 -indent_width = 2 -column_limit = 200 -allow_split_before_dict_value = False -dedent_closing_brackets = True -split_before_first_argument = False -split_complex_comprehension = False -continuation_indent_width = 2 -indent_dictionary_value = True -allow_multiline_dictionary_keys = True -each_dict_entry_on_separate_line = False -allow_multiline_lambdas = True -blank_line_before_nested_class_or_def = False -arithmetic_precedence_indication = True -no_spaces_around_selected_binary_operators = "*,/" -coalesce_brackets = True -space_between_ending_comma_and_closing_bracket = False -split_before_expression_after_opening_paren = False \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 53d1f3d0..00000000 --- a/LICENSE +++ /dev/null @@ -1,675 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. - diff --git a/README.md b/README.md index 905bd88a..ef097f9b 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,7 @@ exo: Run your own AI cluster at home with everyday devices. Maintained by [exo l [![GitHub Repo stars](https://img.shields.io/github/stars/exo-explore/exo)](https://github.com/exo-explore/exo/stargazers) -[![Tests](https://dl.circleci.com/status-badge/img/circleci/TrkofJDoGzdQAeL6yVHKsg/4i5hJuafuwZYZQxbRAWS71/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/circleci/TrkofJDoGzdQAeL6yVHKsg/4i5hJuafuwZYZQxbRAWS71/tree/main) -[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) +[![License: GPL v3](https://img.shields.io/badge/License-Apache2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) exo-explore%2Fexo | Trendshift @@ -26,286 +25,6 @@ exo: Run your own AI cluster at home with everyday devices. Maintained by [exo l > **EXO** > -> EXO started out of a desire to run research experiments on large language models using the hardware we already owned. -> -> What began here is becoming part of something much larger. -> -> soon™ -> -> \- The EXO Team +> Coming soon. For legacy exo, see this repo's history or [exo-explore/ex-exo](https://github.com/exo-explore/ex-exo) for a snapshot. --- - -Unify your existing devices into one powerful GPU: iPhone, iPad, Android, Mac, NVIDIA, Raspberry Pi, pretty much any device! - -
-

Update: exo is hiring. See here for more details.

-
- -## Get Involved - -exo is **experimental** software. Expect bugs early on. Create issues so they can be fixed. The [exo labs](https://x.com/exolabs) team will strive to resolve issues quickly. - -We also welcome contributions from the community. We have a list of bounties in [this sheet](https://docs.google.com/spreadsheets/d/1cTCpTIp48UnnIvHeLEUNg1iMy_Q6lRybgECSFCoVJpE/edit?usp=sharing). - -## Features - -### Wide Model Support - -exo supports different models including LLaMA ([MLX](exo/inference/mlx/models/llama.py) and [tinygrad](exo/inference/tinygrad/models/llama.py)), Mistral, LlaVA, Qwen, and Deepseek. - -### Dynamic Model Partitioning - -exo [optimally splits up models](exo/topology/ring_memory_weighted_partitioning_strategy.py) based on the current network topology and device resources available. This enables you to run larger models than you would be able to on any single device. - -### Automatic Device Discovery - -exo will [automatically discover](https://github.com/exo-explore/exo/blob/945f90f676182a751d2ad7bcf20987ab7fe0181e/exo/orchestration/node.py#L154) other devices using the best method available. Zero manual configuration. - -### ChatGPT-compatible API - -exo provides a [ChatGPT-compatible API](exo/api/chatgpt_api.py) for running models. It's a [one-line change](examples/chatgpt_api.sh) in your application to run models on your own hardware using exo. - -### Device Equality - -Unlike other distributed inference frameworks, exo does not use a master-worker architecture. Instead, exo devices [connect p2p](https://github.com/exo-explore/exo/blob/945f90f676182a751d2ad7bcf20987ab7fe0181e/exo/orchestration/node.py#L161). As long as a device is connected somewhere in the network, it can be used to run models. - -Exo supports different [partitioning strategies](exo/topology/partitioning_strategy.py) to split up a model across devices. The default partitioning strategy is [ring memory weighted partitioning](exo/topology/ring_memory_weighted_partitioning_strategy.py). This runs an inference in a ring where each device runs a number of model layers proportional to the memory of the device. - -!["A screenshot of exo running 5 nodes](docs/exo-screenshot.jpg) - -## Installation - -The current recommended way to install exo is from source. - -### Prerequisites - -- Python>=3.12.0 is required because of [issues with asyncio](https://github.com/exo-explore/exo/issues/5) in previous versions. -- For Linux with NVIDIA GPU support (Linux-only, skip if not using Linux or NVIDIA): - - NVIDIA driver - verify with `nvidia-smi` - - CUDA toolkit - install from [NVIDIA CUDA guide](https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#cuda-cross-platform-installation), verify with `nvcc --version` - - cuDNN library - download from [NVIDIA cuDNN page](https://developer.nvidia.com/cudnn-downloads), verify installation by following [these steps](https://docs.nvidia.com/deeplearning/cudnn/latest/installation/linux.html#verifying-the-install-on-linux:~:text=at%20a%20time.-,Verifying%20the%20Install%20on%20Linux,Test%20passed!,-Upgrading%20From%20Older) - -### Hardware Requirements - -- The only requirement to run exo is to have enough memory across all your devices to fit the entire model into memory. For example, if you are running llama 3.1 8B (fp16), you need 16GB of memory across all devices. Any of the following configurations would work since they each have more than 16GB of memory in total: - - 2 x 8GB M3 MacBook Airs - - 1 x 16GB NVIDIA RTX 4070 Ti Laptop - - 2 x Raspberry Pi 400 with 4GB of RAM each (running on CPU) + 1 x 8GB Mac Mini -- exo is designed to run on devices with heterogeneous capabilities. For example, you can have some devices with powerful GPUs and others with integrated GPUs or even CPUs. Adding less capable devices will slow down individual inference latency but will increase the overall throughput of the cluster. - -### From source - - -```sh -git clone https://github.com/exo-explore/exo.git -cd exo -pip install -e . -# alternatively, with venv -source install.sh -``` - - -### Troubleshooting - -- If running on Mac, MLX has an [install guide](https://ml-explore.github.io/mlx/build/html/install.html) with troubleshooting steps. - -### Performance - -- There are a number of things users have empirically found to improve performance on Apple Silicon Macs: - -1. Upgrade to the latest version of macOS Sequoia. -2. Run `./configure_mlx.sh`. This runs commands to optimize GPU memory allocation on Apple Silicon Macs. - - -## Documentation - -### Example Usage on Multiple macOS Devices - -#### Device 1: - -```sh -exo -``` - -#### Device 2: -```sh -exo -``` - -That's it! No configuration required - exo will automatically discover the other device(s). - -exo starts a ChatGPT-like WebUI (powered by [tinygrad tinychat](https://github.com/tinygrad/tinygrad/tree/master/examples/tinychat)) on http://localhost:52415 - -For developers, exo also starts a ChatGPT-compatible API endpoint on http://localhost:52415/v1/chat/completions. Examples with curl: - -#### Llama 3.2 3B: - -```sh -curl http://localhost:52415/v1/chat/completions \ - -H "Content-Type: application/json" \ - -d '{ - "model": "llama-3.2-3b", - "messages": [{"role": "user", "content": "What is the meaning of exo?"}], - "temperature": 0.7 - }' -``` - -#### Llama 3.1 405B: - -```sh -curl http://localhost:52415/v1/chat/completions \ - -H "Content-Type: application/json" \ - -d '{ - "model": "llama-3.1-405b", - "messages": [{"role": "user", "content": "What is the meaning of exo?"}], - "temperature": 0.7 - }' -``` - -#### DeepSeek R1 (full 671B): - -```sh -curl http://localhost:52415/v1/chat/completions \ - -H "Content-Type: application/json" \ - -d '{ - "model": "deepseek-r1", - "messages": [{"role": "user", "content": "What is the meaning of exo?"}], - "temperature": 0.7 - }' -``` - -#### Llava 1.5 7B (Vision Language Model): - -```sh -curl http://localhost:52415/v1/chat/completions \ - -H "Content-Type: application/json" \ - -d '{ - "model": "llava-1.5-7b-hf", - "messages": [ - { - "role": "user", - "content": [ - { - "type": "text", - "text": "What are these?" - }, - { - "type": "image_url", - "image_url": { - "url": "http://images.cocodataset.org/val2017/000000039769.jpg" - } - } - ] - } - ], - "temperature": 0.0 - }' -``` - -### Example Usage on Multiple Heterogenous Devices (macOS + Linux) - -#### Device 1 (macOS): - -```sh -exo -``` - -Note: We don't need to explicitly tell exo to use the **tinygrad** inference engine. **MLX** and **tinygrad** are interoperable! - -#### Device 2 (Linux): -```sh -exo -``` - -Linux devices will automatically default to using the **tinygrad** inference engine. - -You can read about tinygrad-specific env vars [here](https://docs.tinygrad.org/env_vars/). For example, you can configure tinygrad to use the cpu by specifying `CLANG=1`. - -### Example Usage on a single device with "exo run" command - -```sh -exo run llama-3.2-3b -``` - -With a custom prompt: - -```sh -exo run llama-3.2-3b --prompt "What is the meaning of exo?" -``` - -### Model Storage - -Models by default are stored in `~/.cache/exo/downloads`. - -You can set a different model storage location by setting the `EXO_HOME` env var. - -## Model Downloading - -Models are downloaded from Hugging Face. If you are running exo in a country with strict internet censorship, you may need to download the models manually and put them in the `~/.cache/exo/downloads` directory. - -To download models from a proxy endpoint, set the `HF_ENDPOINT` environment variable. For example, to run exo with the huggingface mirror endpoint: - -```sh -HF_ENDPOINT=https://hf-mirror.com exo -``` - -## Debugging - -Enable debug logs with the DEBUG environment variable (0-9). - -```sh -DEBUG=9 exo -``` - -For the **tinygrad** inference engine specifically, there is a separate DEBUG flag `TINYGRAD_DEBUG` that can be used to enable debug logs (1-6). - -```sh -TINYGRAD_DEBUG=2 exo -``` - -## Formatting - -We use [yapf](https://github.com/google/yapf) to format the code. To format the code, first install the formatting requirements: - -```sh -pip3 install -e '.[formatting]' -``` - -Then run the formatting script: - -```sh -python3 format.py ./exo -``` - -## Known Issues - -- On certain versions of Python on macOS, certificates may not installed correctly, potentially causing SSL errors (e.g., when accessing huggingface.co). To resolve this, run the `Install Certificates` command, typicall as follows: - -```sh -/Applications/Python 3.x/Install Certificates.command -``` - -- 🚧 As the library is evolving so quickly, the iOS implementation has fallen behind Python. We have decided for now not to put out the buggy iOS version and receive a bunch of GitHub issues for outdated code. We are working on solving this properly and will make an announcement when it's ready. If you would like access to the iOS implementation now, please email alex@exolabs.net with your GitHub username explaining your use-case and you will be granted access on GitHub. - -## Inference Engines - -exo supports the following inference engines: - -- ✅ [MLX](exo/inference/mlx/sharded_inference_engine.py) -- ✅ [tinygrad](exo/inference/tinygrad/inference.py) -- 🚧 [PyTorch](https://github.com/exo-explore/exo/pull/139) -- 🚧 [llama.cpp](https://github.com/exo-explore/exo/issues/167) - -## Discovery Modules - -- ✅ [UDP](exo/networking/udp) -- ✅ [Manual](exo/networking/manual) -- ✅ [Tailscale](exo/networking/tailscale) -- 🚧 Radio -- 🚧 Bluetooth - -# Peer Networking Modules - -- ✅ [GRPC](exo/networking/grpc) -- 🚧 NCCL diff --git a/configure_mlx.sh b/configure_mlx.sh deleted file mode 100755 index f1cfe6e6..00000000 --- a/configure_mlx.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -# Get the total memory in MB -TOTAL_MEM_MB=$(($(sysctl -n hw.memsize) / 1024 / 1024)) - -# Calculate 80% and TOTAL_MEM_GB-5GB in MB -EIGHTY_PERCENT=$(($TOTAL_MEM_MB * 80 / 100)) -MINUS_5GB=$((($TOTAL_MEM_MB - 5120))) - -# Calculate 70% and TOTAL_MEM_GB-8GB in MB -SEVENTY_PERCENT=$(($TOTAL_MEM_MB * 70 / 100)) -MINUS_8GB=$((($TOTAL_MEM_MB - 8192))) - -# Set WIRED_LIMIT_MB to higher value -if [ $EIGHTY_PERCENT -gt $MINUS_5GB ]; then - WIRED_LIMIT_MB=$EIGHTY_PERCENT -else - WIRED_LIMIT_MB=$MINUS_5GB -fi - -# Set WIRED_LWM_MB to higher value -if [ $SEVENTY_PERCENT -gt $MINUS_8GB ]; then - WIRED_LWM_MB=$SEVENTY_PERCENT -else - WIRED_LWM_MB=$MINUS_8GB -fi - -# Display the calculated values -echo "Total memory: $TOTAL_MEM_MB MB" -echo "Maximum limit (iogpu.wired_limit_mb): $WIRED_LIMIT_MB MB" -echo "Lower bound (iogpu.wired_lwm_mb): $WIRED_LWM_MB MB" - -# Apply the values with sysctl, but check if we're already root -if [ "$EUID" -eq 0 ]; then - sysctl -w iogpu.wired_limit_mb=$WIRED_LIMIT_MB - sysctl -w iogpu.wired_lwm_mb=$WIRED_LWM_MB -else - # Try without sudo first, fall back to sudo if needed - sysctl -w iogpu.wired_limit_mb=$WIRED_LIMIT_MB 2>/dev/null || \ - sudo sysctl -w iogpu.wired_limit_mb=$WIRED_LIMIT_MB - sysctl -w iogpu.wired_lwm_mb=$WIRED_LWM_MB 2>/dev/null || \ - sudo sysctl -w iogpu.wired_lwm_mb=$WIRED_LWM_MB -fi \ No newline at end of file diff --git a/docs/exo-logo-black-bg.jpg b/docs/exo-logo-black-bg.jpg deleted file mode 100644 index c2905054557a369467205970b255b35864a0c187..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8089 zcmeHsdpML^-~VEqI|xZhO_2(r?GOsnVLK-|hnP-6l2oEFcafB6HbrHZ$!QB|${~de zIdyhOCWmsY95cfhW88D!@6z7S^Xy&E^}C+m`(FEc|9O||zT9&kzH5EI>+}7r&-&i@ zd%PPgur{|c2SP$Zz!Lreya#MI3-vn)fUPZ12LMO_p%_0P0?<p$HEE2)Bg*0sa@7 zy>?spA9sa(ZxjA;4p_4f;-kQDr*Ghiz~jDwM_1@-tp|o%t!zbRt-RMNH0quefs1LY=b-X%Z@1A5_UZG_h9c z?mHGKDYZyp@e)PV)oa$Osq5))FfcUQxb0^%a|=r=Yey$%7uQ{G?)wjTAN29{J9PX+ z@X3(SQ>UZOMW2tka4|OV%GIRgYu9h2q-SJi{g!YqJ- z(fqdMT`Ti_TYFD$U;n4iUj_z;*y9tNN$wPHnm_9ojOXue{nNAW{E~rw35$pjM8s$P z5)ux92SG+ebmdQCvRmxM_Xf!+>ztKXxHTc|L6xM6t^-Sc-?1jCMXU6BRN1qxee>); zb1d?|<=Hgwr)_Z$Uu55So=2}c;nw}zv*wb z-~2b*fBQGvZ~2$~K~ydvFs#o+`OTp?mlJR> zH)lp42aT7~k!~e2YD;9jpcRq@5~XV}p;{c&Fd6n1{OTL<2~Rt8KKV1zIRm}))#3~e zzU#%}`*G-xwb*0#KO-CkM;s8YO~<_R#ewon!gRz;Xt)SZt()AI;m+2MGS>Lb@ANrc zx4R*WTAS*XV;;rt65A>9Y!&ekDncLhz@!Xtu=)la2h3V3mSjPGZHw#AJp9Pg z3quaot~d8TpSLi@?fiUVKlvC*g3+DbB~_t_>fqoU5tIKM?VTXZV3bzE4q_fC9+BMXujc*G3#^(F+Ydbf# z{8F#qDXDRH-$yZtIl`M?%26HsITqLwN3K5((i9*pD-9XE6y6vnxeW)ah+a6jk23f+ z0pvT*sCMs;5NbwE=#W#8Zk!Xx+QG`VbTuXZYR}-yIkqn}j@18Wh;gm{D^SxW-AT_X z`jPcrB{AoWHM_~}WOo)bN{^@a#B}lD_Dx|2Wa_d;`wp&j0yya5aQ6#t zQiMZ)kreMxCGNVM?&bR)L^%8>sDq$i1VkinNZVTTef;oxE} zQb0m~)jo@Z5t%?7+-5d8V`1$15!b7AYo9L8$;`DIeK}3L!KWz9vucVu>Y;V%K{x5U zENl8955p$q`~DJ≧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 diff --git a/docs/exo-logo-transparent-black-text.png b/docs/exo-logo-transparent-black-text.png deleted file mode 100644 index 50ec690e..00000000 --- a/docs/exo-logo-transparent-black-text.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1c6f0b66b68ffc11a42cf25fbd43a6fbea99869ed4ba82e5f480d8213e9b7061 -size 1296 diff --git a/docs/exo-logo-transparent.png b/docs/exo-logo-transparent.png deleted file mode 100644 index 5cf5ea69..00000000 --- a/docs/exo-logo-transparent.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c7aeca6a876a195df706f3221f1bfd4792884e6042c2b355026f94cba0f7576d -size 1296 diff --git a/docs/exo-rounded.png b/docs/exo-rounded.png deleted file mode 100644 index a12c5650..00000000 --- a/docs/exo-rounded.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1316a53899f32ba6c33b083fca232b638aea4efbcf36bc99e640369169e6a1c9 -size 28651 diff --git a/docs/exo-screenshot.jpg b/docs/exo-screenshot.jpg deleted file mode 100644 index ace09a46033ae7578389ff304efcccdc98558e3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 302470 zcmb5VcTf}E8~1HNr79h%N++R730=IU1QJM)B$UvU5+I={z3VLkQUXdyKthueAV5HR z6_wsg2rYn0??(8|&^}YVg{rPx?8w!Si&zwDb z=FHi@+nGNLXN=FBKleZVZ-4%;ST3^sPplU&Ubx7{%FfQl%Erddd4-dm<1z;u8y64P zqDF#X9>x;QwD2FI`|gd!FSS8~fk(VD2+#F8q(2jg^n%0w*iWr88&G zooBglk>?UGt0bQqztnAOJJ*0hI-7vdRrPcNLr_>+#|UAAejFQ5DJ`pOeAhLl2{N$_ z3?|mrH%!WCxREktwTzMW9zkJGGK!Y9QSKP8(2~z`V6%|oZqGP*)9JsXivPv=KT!Xd z>MziRi)Syfvi)sF|9ybJ6c^63aQr2@c<#*qf#PA|mAt^GcH8=*lxx7_^ukO00@CUl z2xM&Sq>z>o%Em3QsHFZKT~J2Tm{8aC85Fd9$m$;aBx9<0<*J9QT(=445AzJ?`M)Fc zoaZ@n>&$=aZu%#}{Pw9b%{n_GaQ%%U$;vl;xgG8z&00CuK8bbL9fQ9gw~US8_{<&0 znmY!PWCz+8dqwJMzqBmR0E5(Q$f+)sR>ch3sRd@9fMQM*uhYvsSv!aDHua(2Gp?T- zyiu7a+`r{3P7*`fe6Dns&~7Y%(!YIHnJ6te(N`I}Q|t_BArdRo3gy@(hcj`$j0~9q zJNs4<+cq;BtMlw38eIk)%$(qP*7$!cMa(czIhZ=XbN4|+UnP&I`RVV8`R_@3StQbT z-VG1yReElLV**37e1M5HYsDFS84P0yEF0Ot#MN{{b6Tq-vr+LX>oEb2bA1mzU6i=z zm#;7SKdap1G$@Q`r0IDCOcWV-9Oi$|Q`E|CO!oKF@CYWWtK8Hw1v+hPr`8>kW%QyP z9lidsTClk7z#$Q{~`*0ggNl(}hzSOY8=vuMgi>#{5nz5P+pY%<) zm|H2Xga{u0MVIfho{ZB*F{4zpO@VaLlHR)8X$5_~H^i1w?xvtC$k!?vCGL}U; ziV7HLzAP<>AkPLP(EcAYleNCB9Ph|wVws+~1rHpa9OR{m^1wASv4I+j6gV!+EMS9^ z>~{<>T^W3|A$cE(7IPN1tH>JE68>=#dh-{wtj{EV;mf@}zd9AiPzipu+QO18xbhdE zXe0=61L9+s9*y}jFiE8Obxr2zPjt%_sdNZ{)tiyX6m1LfsHr!-Fp52OjL<1>NZ zZaW>?M>+?Ue)QfsE^{|C#syfQR|ytUY$o4)Ueq70iUr%Xs}camoLx2hkckthg7 zQNq_0#4(TpMi88SjYXuik{YxHKw1THmB|+_J_&wpjlC@E=Wl`Aq|5trNwet}BUAe6 z-BF7z_4ms1@~r9MbtTd>IG^43_Fz4bzoFT>LBTw0<=e`nLJPjfI9X}xSK@IzW14KjPF~ew&fp-FhI^#xk%+5a^;)7=u4M6O4&}}A?+$^&bc2MXLV>!{?l%Rx zfl)H?QCVzeOB*+HCT38bC#CmA^@e%K{0jU#k!vjhgY^r{uL}XwOA>l{3t}|q*MQbL zWdj3_JWAdTQr-rsFgQ)AQeB#yfA8WGL-LyPV4jtofY!U}Xp)gVWkd zW{$h((?4++s74u=mFb22{QU8bip=vvEw5bpAE)tl7`K%fZBvp}<#1uM*`B7RN2A9P zzQDc^AT|xPuF3!Oc(gahiLF5MdAFSB3jQ}{RoIIV$tlN6q{&NnKYSxcEj@-D6}>D0 z8uxj|-TO6}?zP9AuinO3{K&5`+u&jQ90m#bqUo-zc+9yu^$`nS)d0bfdv4jd5P_6$5c_68 zWQ<(9W03%4Yyj!^ayh*TQyj>Iir7yI$PQFA@o!29%?|tQ-gl6+J5z`#!Gd}ik+0`|ZUwKm ztRCpj;0*B8cghiYfKu`xr9H z0PY4ZdSs`0KoJqe^09_S{Efh=c}-c4rf~0BneCIAbji|G6~&7XE4w?Hz@E9h0%$x( zPpY}qsZSX`v`;>%q3qC{usszcyKOFQWDEYS`$cKB%^2`NEzsH_2LpYxJ8|EFsW|d3 zpnRX~AUC%G-9K!9FW`X<2wz&Hn~GY$**yV_w%;U2kTXpj-9MfdM1ygM{8YDMK&Q-o zTTdYDb|yV8liOwOm9xpsXI=7pOFDh(1Vy2wt`M1`!Su}+ug1o5(EgBWk*Z{UJ!56U|zvcgP5r4J&1o4!F9s5cR#e_{R2Y3 zbv7rL*7>tSiNrSUrcrhWm}$oD+dT2}eh;+31z|RRdDk0;2zLhIgg~qMH?8YD1GB3E zaMGj*4M&?>1;|fYAk1r--Teg*5)cy%WUd-lso8yzhKfa@M*>kHL`2uDXMctaR9%S83mx#}Z|6SvX}SrD6mU=TeTe@m>+z{-RZ2LDd}gOl6ln3#{nLgjXBy%aidb1gpIyz?QL5 z+E;j)I=u;tT5!phYo0R`M)2NOE;djgy1POY`ZYF!MK#V_x%b((bqpw<`XKKEd!=61 zMQX0A1|s4B5M;y;X{*+sQWX1YaE3Tk->5Vg_lS2-ECx|wq5*Bwa+f!h@_40Ql6r7X zzYQJ9KZSM7(38mmRS{fN+=n{2+Of#?Ek|nrqtDYgVb9C?IZ<%-P8Dw;dh=5%#+2GK zMuT+0uULHMvoEhkl^?3ew0*(8hZUmN)2i^qYtkZjC<0=~$I$~UIn^BqSeCp}67bz- zr(CM8xbn@cxCfm`HOT9OxcnZQB_rghj+!1KSz$%XMNqFi^H<)Zo%`;l=$TFBwC7FB z!m@wP6sL>j=4je84+>r*WcRn@4KLDFVa2YI%e4k}|^qTYb2b=dL^?X?xr_TqCdcXVb+|VEi*UTJOVBo&q zYfWBc-V)H0jo5=ENopQDFfX_Wg@cAkm=ce*cmIV5KKSSR)dA8xDMAxKQPCR(&Xufn zU>-YEGU$E|JUzxrNq!gisG#ek>(rj?Yzb3{HeX)<-jK@8_gY#%Eq(j$54*8Yuh23@ zY;6fYRJp;K9?_q!yjrX!sHPx7sJ+jRH# zB}-{+f7?GBxLDTrd`ANQjq|6Q_3DcRf5;AwWPqHhuRZI0w&FN8^VqPDK;k9lwJb(# z0A|AKJT>~qFE(SB1Z>-yV0O})rWwXEcfaGEy1n*onzDy+A3OktB|{kB#gmrj^9JeXYlt~A>ntJ%$(a@ud}UM$=O;| z^j4%i+XVqdW-~{p(QDJ7rwc8S_T*&swYvfkkf<&cR)f{SK_q0e`AUnF0@p`Oh5Rmi z1%Li?W`5}Ms{ckub#hg5B|H@B;Fv7W8xA#Z&lbkatj~>7ge_7)%!Vf>WzX%0P#*G+ zj}1gS_-=T9HS{drY?tb-_Cxts2Qmj3;D~qSZmZPQBqp2?#v}Z(Pvzp}e9x54r%R}8 zPrasjs4cM&3bopf<4tk8Ebs5SZdBJ}^6D5-cd{P3bJKy=IQu@?Y)xe@F=a2QP{`Yr#%v-2})2}0IVgHVHi*UY=2OvBI zd0h-nFX*KpGfB{vh{1JPQY5UIg0D{&**W2&B;g=pca`Q+;*~kvq8Fz>4Fx?)t0&#zxTUt`N1mB}Vj8 zLaLHp*+)l^@`K7Z?o$75n$W5;JZX8gWkZ092#Py_@=7)B{ZT7;D5ihWF|NMrnKTxz zE0A_Xz|7^_!mf1-=@%|@mc_;gAUGZPIG!qW@^f-^q6aHrYv)jVigPR{NEXfM_B8h(^_f%&x%l`S3 z&EYoZ!;G}A+S(droEWX|Jw!W2CFkIooN}-X49dnDaA7oNFY)7%J{b=Z{s?@Z^-eBR z;L1#tO4((9HiW1{ia{+ipT$37ZGLP&=4Gw@mdBcn4N$aacR{AUJ|aDOB0kYOl&EF_ zY-9A#ND(c<6hrFK`orv!fq4G|=PuZEuXiGH?300)wCmNmEqBf`HY+90oZ5`^1#yz> z%woTk^|f4Az}BQV#6?-ize1@(aJ?CczpE~XnEZ1_A<;+KD=2loMn$Ud+Kdf9nIZ?} zi#_QCcy-2z1j-ldFNrFr+i8WY*akX5i4lkQ9AhUwOeL)v)~emxPdi#|$wHV!MXebS zMylL(0DU7Zn*M8mjFQQ_NI$Wr(3b=49es6;V&q)^4{r~OMJhA&{G8Qlra^+@$~R_U zL_$EnBo$otx01{xgwb-M=P2 z5F=DB3K*U@lzCKe_Xr;$ap~1}1rPyY*}neEeg45sLR)PRLk~Ay*Hdodvn6%xFBFw| zNgIHnZ5lp$n$o>0n?n_tn!{L@n^z3)a}R^DpfDT4jwq+I4d`m#MMhP{6kZCP%-J4W zw{5Dt<1p$p-lt;ZSP`nFzjbdbYSSnv1e$?kPIzzIjq-z6hQp#q658^(PT+@QAH8XX zDI~}PMJyc-=BET5q*8!5vC9>ypi6~2#s9dyd`jOL1R90O^piYS-=+wlC+p3s^Zey3 z`Y?ct#31#9#0oc&B(CV7xL_x%iXqr7HN0o(FpE_8pv&Gf)<0+1ogfLxXe%c>O$_A4 zqE7lOy<=q>U(%W_G$-Me=}%{-5Mo1W!*{KA07(5s>& zmU@Vj!I9-LLq=<@i4jLYfSpoW8B%3J>FIZ#pw$F}b zuaZY5O7amNS`G4-L=EL9nw)(#w`B!riTlnc$_dXY9s_N0bK&z%zMo$;NyI7N1iB*OYW7+4cVb>_J;GrF1HBc*z1-B_@|?Ny(w-`R&N!Z2<`Sex zt(bz2P3}jEdp(-Sv}`>R8T>Z+WzV1)RCcj?z_W0PvEwI_Zqit)Q}kHUzg6K>Wa(xu z{VvYnY51G^&pcEFBG+82ZgvMWf1`v^pzWeFpxi&pP+fMxioPFrT{bgx5mJQp70ne7 z@~n-E+kfJEVyl;1`5E@ORz+O5*pV|-ZyjWd^spe7-dWaqD3dMxv(MPt*#VY&ajGzp zie7RK*2!J3MIi}5?K~(l4~tACC$_cTn+gBc>(hIvXgU*n2Sr;!WqrD*>m2q<(35VJ zC8czv5}h!`P7}Y}?ctSr`7lwH+<#l)Fs&vW@~+AGJ4iTU{v=^@`$_0&;rl_)+dA(g zb>*!huHAZcF5Y`n$FF?az}<5g>7%~iZWE#xB2^3Y7_#uzLcKeNk-0m{KdZqHYfHOr z2d$4m@Q);?)PG1Kh|B~zr6mNHzWKPpDf^H*0wx+Q6?bUulLWU!#z7%b1ViH(_>7(Y z4#Yc>#$4Ir6;ggceom3c22-Jlon49jjn_-7o~eanl9FRR?N21COWMpWlrtz#pdv=* zzuyU72z%15VBty+ggaTuBG#yO?#&YJ8{WRJi0StdrA@8FWQ$|V;xmZhd0(fj#E8^- z5TJO`G|kAsj*I;#7-H<=Ir@O=v>hVZGaQq+FQ<`1FZmMI_)P|2oHSxfvAO~qauQao zSk$t1zEY})bTsqeB@UWGQ)H?!EkAl}l=8;3ItAl4c0L$s*MJh%>spD)$mfOBv{Vz~ zag0>^W?C&dZ!g`;V=PTxNI>ZAbQUi~r?DkMN<*kTQ5BkAy(%53%2mYaV2}Z!{EwZ{VW+jneiz z%Iix^9u})h-+wtc1M5xS>;vD#V}g7*{LEjtQ@qcZ7l$?<*)9H{;x|n00jCRT$l(gP><416d6Wfx66!^r?}^~L}Xu* z;t0`hsA@#s}x&LCSB{GH;Pg|WemSXY)@MTy<#u|6GlyZ5#xsEitlr2|g< z#nBJb1KILXCx*(Dof!n4ABnckI(g6Xbg=tzh9NlWKQ}@b(D8rFOc44^{+YyUxnaB1B^GF+8<+s znO9W?rSSuoAM_z==T>ZQVzSI1XhvMOMgtNZ)`)wrvo!h7nhMI~; z%;K~i_issNW=mNUM6oPHnGO>p2MevzKWEN^^Ld6WQl!|iJ#BteIeP=P?M7u-7a?H8 z6RV;#DcO+!pwsztHNg%v*V1R_IWwOHdS=z+t|6cb#ysTfy0I^C}#djD@UGt<@bSFsCIr zRsS{o#$>ui`?kq$+S8Z>>tv__d(0Za7VKf-i5`r&7y`1@3cwUt7D6g-Q{M0zvms^B z@`8|^%_Wf^uj*O{W_p1jd0}al{fn>F;5ttM$&ht4NY*9#YTD77&#y+KEYZ_vhA;Ki z3)~R*bY47D%KW<4gx?>+|2bn$=6m3f1Iw}{;a<=Dw=St-v=FgI2o>HmcqRXoub~VE zkpp8&Qb+rjgsvZJ2U7~y)tTEoY~e*72PuLBx&_i-BF}pjOYgT?O(;xoIBq^C{j19u6AepkUg|-7BMdv zGhZIs%Ts=`gtDqQrw`zqV9FLS+Odb7EEPy*hAzQ8v@h#|Pf9^FrI__B!vXPYJnJ&uO!3|ed<{QzW z!*K8X)j#3C1gP~+qqenr!EdRhPEXl`FO)2z*9ufl6SH_@nW)UC$NlM-9S>0w7l6a`Bh>Zb|5Mrb?gzaU67IBGcHUMqdCz-Wxjp;Zd0@Fu^g?D$= za$C5k{R6O~IVMwscjcSo%EZe2UUa1b{=5!W>F>LW!|qm7~$D9x9Y&>BD9r@3yiOp7xo;m7;+=3^rKdenM1LBSS zN-?J=dl`R?ejoSe%#gd{r5U(Kys1_yjv36Lt2g!XY+1Fe)~NmVrH9{5#0Z5hDEM(Y z~0_4bq?9M;{y&c&&((sB&J) zx>IdlW@405j5jHUPAyvDN>=yQ+yQ`_TtZE2n|U|6!lq|)X?F#1IrJa!yf1R!RI_Cg z6FYh|N~=Oe_4)GCe4MHm5@2v_ZwH=t06%J^_a9ex#U`XzYCJvM7$=Mr#m|=&spd2l zeVAVASF=-bCnSvK`yNGq=dOg8e5$Dv^I&YO*m9va>>%a((-6i5c0d$!A6E)zPV|~{64GIp!Tqz1QHhVGG6}>BQ2|I z-q}^}3GI8-*6Esl$fXn`2F?%ZYr95OPvl0Vql0-8A)^%@5+I$Oj=Y;}Fx z-8Kw-Egom!X|JnSHxx(jB2sOFZoy*AHF*Og3?f+gQqwDYkTB;Qua^S)$sxsd4-3$l zOaQHTgDYr>V144zNi@x-6n>!o$x@GHNH2gPT=J&exm)~qSNBf`6@r8M>)`2D$HXJIBfTvqQ(OUk z{ZueeW;mM{6eP_qmQ88X(`a4_PXJWzplG)IhBpXzq^usRT{eqeuzS!G5?BvQt$~*4 z59CXxbO1xWLNzvb6Rfrdu-Z+eeRZ7&~pbinfC@y-C zwCW*Q^8=L#yNAi*8{ykdcDa4Ql%Up^ zb=J0PW}A(3N5olarrRc;57==4AiThiuUH>HM_vqyx!(ejk!d?!J;tLis~}RfiJxb& zn^4+t!tO;TGXSDl-uis(QUgVI3jM6YDNAbL{lX9y?1aaM)AR8ud~lE?Rx#&N7X^~Z z7gb@AVw_S+NP<`&+zJ_&9eG-dRg|Dsa5q@py{rwJcwgF|YS&VE<)2Ukg0cT-fVcEk zRy|hX^WA&5qv{OXLN}`3l`qqdZY1MWr zgjAELI`k*Dm4hmml(yREPb5e8o3gkJPVc4Y{;m@!J4eCcM(1j2znogO_Di#Mu~q+Q z$W$1t$S0=UGVXYGe@mM{ z4t`y#5-Bcq_|>#(F)hgosPnv8%U3o~^mE8)93O!;cCEc{=b~ z#Yaa5$aL+hBX6Xk z!otIiL#wOD`RbCaVe$Uf2V*d!5m&EZbiBTmx&an$RapfcTpRZYA3|sO45AGSSvH9^ zdfAX)@ZJoOu`)YhSF?EzO)r0qv=}#XWeogbCLLd^UM4-N+(t2q7cJ3dOWb?{sR4{9 z#)oi&Th@}Eri+VC zy~4|x&ca2s+8aktYM(w_p;^#K4jKni&|Gac>{?Yp@OTwQ1jo~ma^u}(+0)?C2BDCp z2exyTxL&vk?RCF6$o+%x@-~Jvc`*9+PWSjth36+BDI1?Zo*m@L4hcEAbhhw)xoVlP zO0C2R4{*|J(l{tWyW!~J_4nXegAd?YJr|5+)kG)QSZ7?lO(TvmJ$YO{Mu~m?aBZB$ zz(#+Dy(n;aW*yC)Ik8|#GArdxTt7Te~uX{A6W zol16}@a!retz+(M^A_DEaN?h1#s@%S$!5|9;_R9dE=pf^{07T1sv}c;NXWQ2gV$@Y zPms!hwc!p$6h&Cmr`(sZnH>Q7b_dw>UmN~O$-4mSRF+KC@-n&~eHz&}&T%nsa^AP& zCKMt!wEuAL^qyZjUFA}=DeY-?jBLw9k*z_(4e|*2230_(C8KxI2Hluw1D4RroV=cN z)59?!&BdBZCx_`4;OG=^@LElvaiEQ=uKH3H&|)~$=*G6pE0X-x9f5LNJcVj}-as)z z$G#Hxe69ZXogIE~qwp6|GDupUB@e2AlzMEN7;PmKA`tle~15f8e~5_RmSuQ51kf1mlhw@ z{TS;|ZH~*N3mIyR@I1;1k%@{qeA)W}G}*f^>{8hfB3J$DmrI5)4>0vchO{r8xNPvu zVAxO%=3<4+O0}Qt(Y6%=I?`F@uC&5KTlPv`Rq0WK?q zM)+tcO=??=*{he0YpMDIxA?$M%pS81&$dE4(Xg)@jr4tN>Ds*Dn3Y9iENIEg6&=MW zc-(TZebQpPblK!#X7z5ZD5xK6;$z5omM)|&5}9tO@A4WNPQ^r{Q8>$8Pa8P_m7IU{ zd=rmmo(3*cp(@T1SG!Gu7nEo46uE;c)05bA3AU+Fg^?_ti|8YcaI6FP!@U_I5Y^e3u+JxT<|;x!6K_4z{K;r2C`FeSBa?hL4(jz~Kvg z?4iX={Lo-s?uu$E1jI|Po>$bk74+B!YIcirsJ{8UMpZEL0(=R#p5xO&MM?r#5?CjQ zx((xjiEZ&eLZe|D>~*Ogqk09VWG4UaU-jKgB8WbhFGF*15E<=ed}h zZa74{T$jzL+`Uw}_%DLt#h9q#4;*sQ&(f1m*cIAz-J)rQm z>|h<288-?zH?ZF0ICij}6oTBW`&oK9x`Cv1M{FtIdjP%fc|X z5c;fJLlN2IRLhPye<(8lQ#cICzHxk`$JgP@lVZV;fE$Zs*&K;HV19nM6+CuvK3h1n z?7f-{c78tdtIMc;v=d?_Rh3YpFfjGYZBg1IMvja{6JYXBE|XUP zlQqK5#FiPN9geo|_|zI^P}%u1pwyuI!HtB8EG`*hS+*dnk0&$p>G-lsu3LS8TP^Ot z>~?0faRXs)Tzk(6S*;l$|KnMyk~ZK|~F^0!<8WIPzG*Zf)cDe1mP za7(nG0OYTaH62_&xj&Q=a<$TXR2Cnv9bsQ-CBXiD0BDMZs`EQHm+q~dV@Vqc8-_<@J30g zM-;{!4b+48?LYWZXDW?9gue*)uavI&=0K1I4^CR6sFP~P5`$$v4(aaa1AO!Npi)5eX{V$ixmCdkR- zj=%%3BGzn`3iI4)wH14S$uKGwNP67dUJ^^?L6g(Wv~t#+0?H;G-l^t$%l6J*Z49 zMB#Z}1=Na=(kD%BEo6Szdl5&Hcb`TuF0Gj=C|`fh4647h#Ip``{mSpEbQI?4OqOm` zHVkbQf#|JX#(YK25PrB{DoiCAVAAb74tZPhy*>BHaXNM~!U9r;y~b2+ z%eH~~k>7)l?sQpLkZKuWL=n^W2s-IL9NnG2%GfD_nwTa2IWyK&z5R=z2Cb;qS?7RA zbCm}}mG!#Z%53a@(yY<^p5Gqj_|!a_-{C`sX+??ZQ5U}um#Y#pVx?3Lzg-}vl``v) zW#S3z-;8k)>1L?DHe>Zc_pop?9b#?ekB-zl>Y|NE>Usdv=4xtn?xMV%DA&CS(`NSZ z=g8a;U9LqN$_TZb$ZBI$8Vcoo?;Ife!8&tNVBf0x<@o zS|ye1b+y*1>ZQ~bwu(ay`25Sn4?cdqywUFoC})=OHWScym6l~QqB!Y1G;C*iR&)-6j+=e|7&uMXlcXP&QKb3Qj8y zpNYTzJEcJC-Sr+nxoU5p$@lt3CUU=BcTl)M9O`Rc+4ybcW~RDzkjiQJkwK@0kXGne zp5os(rRWnoXD09pAi*}XmY7}E;sI8sM-~{(zi4TLYDAKLZZqp4`Ic>cU3z&Y5}4o! z!AF|wi+n09+hLHU7R-_8o$zPu@*L+BHw>{t~5uRJn+QXS@^5=|LK(p`61h zBF&zg0dD3?h&Q9^wczd#G;gO!@4uJ6t@xT@B-SQPe!J{$!DoGBlAPpa_6*Aa@?r|h z2!mBgW(OUy;r+(QL?*mbkH7exdO?8a-?)JDlyIMr{#L&Uw1Lt>SLxEWZZC4}=QW#d zl_f|FIvOc?h?URhopAk=zgCv`vG*O#>8r<&o`@>k%m78EWPY`1c8#HE*;GywJmHbx zTfOdNd8i{2?0r{FoJflN_(LuBeYm8Ocq&a>0IK?yIG19feN)1cv~L_}As1sh5?2kr zA|L{(KR%a@tjB$$`6(G`dGV_o*AY5o7B&ZrrWZV$M2+&L5OM$Yny(krr|og{nt!N0 zvIhYTG`U?&{#;_)&z1VLGLEGA+8(>Sc?fPA?yOgJ>^aJAByKJ9^&87`TgHMfEC)_n zK{iI_G_ol;;axA94tPc7&4?tIUCJuVsh+X3M0w6*fZdP{%m>KT-b(VK1V?nq+Ubio zgjN-*f&wkyGUdh5j7n4%tI~Xu?81GNoZ}&I+m}Gp^7L>)N^E?@HQ8$AiCI!rDB%Q6 z@|FA*4MU?YJ;vUFZ@kE-Y#dg$YDtep_tJHO&tB}|6&X~Y8%iZxq8hjEy_(}wh)5(2 z1I*O9EvJ!Hl}kJp^rp5(au1bGp{q9b;gb=Suo9W*7zc0TVH|k@ zk3y2(C)WB%-M-ye3CbLOFb~qsujR~Z1htHWC~0@!oW1bWx#%`vN?6J&Nc!q%bIrMG z4xfo0#vm_nXgq?qJvKsFQyU5SET^ThAf&q{gP#~&l~IK2X&5(B_0sWZQs7$14K|GU zrG;-DMkQ`O()C3<%*{Gxi&*|{RSrNkyTI=5U!(7=WWVnT6i_T6-0jp*(pm6k^=i?Q zIR(-@Q7axADcieeDq`!K9udLvU91>IVe^u2NGQ=~tIHmQNEyaV^7Ok=SQ$}u#^46b zc)4-RKy@vf&uK}9m3QoTaDUp`_ecR8EnMWVe9$kajVh?WB;`OT`TNKEl%n~)z;Zq~ z3?-EqOj4FJYr)9IaP?}IL}AtW8x5cun#bViIGnEI^&Wb%dBL_oN3-)oR7FsAVNbTC z{TdLJ5je5_(>fKk%%3gv8aq_B80fH-`i897sJ_EXpB7EcNc^yjY|ShLVCy%86|-AR z|C~vz-Q@jXA)tm+?*nfv**8YXQQhntL};Ku7nE&|Y%`NAG3PY$v$!X@zA~QX$b6Qf z=J1rFDTL#DQM42=^hje5twP{Dm0)fEua7saw4N`vEP2%8!;H=^`jP z=kTTROcq`W6#^HKVe-+F|f~GfZy4)TQREwR_tk zf%hAZ@;A^?3q#Kpu)nMY_PXqZ(wz5FYCi^Jm&8574e;c)o4B3+PSMu2>$%XzVs{W% z*Lz}t=xHTC?N0%^+ETI0$q0oaUgPDoGV5$w4pVk;; zqjC|4BWDs!hQ=GM?n*is9QZV4vXev@>z8HVd)fY#R!N!Exm>IAA2Cbg*K3U*znRB1 zH>(dnhP5oWt<=R#hI}>)E+%=0pPs;Zsz)XHou*0Te)`=$U^^4CI4h$Zc( zhDJ2gn9Ghn(~b6v^)Da%!Ugv&{Af1u+rTGz1MnWtUXQ#(a7_|H8xG{KQZ2t4RQjFt zD0;=4wG=4YB!P%wezC);EzK{^nsMER>5%USSkL7>n7zzKfl0aN@syQMWxUN)Z%R0I zBVFJ(PJXeoLG+N$&c7jx(CVDGF4-7QripJ|j-J;yZ+{{yZJ?!W>pV3;pCBydu4Z1U zs#O>)I-X+AJGQw?Q6)$M-$=?3S;N#Gd!`$kzVx$x$j^}5`RcRmLw1B_EPD6P8MO~r zd+#ESUph339apo@4wTL^C88G&NE^J^y z+($s>3%hqwwkf$Drj36yIW^1_>s3m^plO`UE66~;y4He3Wn~=V;VpE*GNm3~yHKvN z`+WY$Muu1Oy8$Lo~t&Y%IB-#?>`{sRixwd+d4d z&OlY2**tzpv{B5rb|xpqay;@PdQ}_!bjE#Sx(_)~+2v_72_k6sN~!H8&0k3z&-6Nr zG$pvqtt1J#Dd|j6sE>MSBz%#cUqdjP{;+=$9w@QIZ2n#k04_BYZ?Air3ZGb@1=(>o7`ppft3s;7CB<<7u z2ufkgK*$MHas_1dB8du1i zs%te!FInzC$RsHYzwH&#n4VE`>0Rt+ioTOr9nihnibD(f0pC9sAio~!Do88t|A!I6)R4hQ(R-8Drr3)k`c&<1rw;U!nH{qw<4}7+CQah)Hpdy*0j`}a(Y78ti?;HmyG zs+f=px&7@l5$K(xI9ifpI!Ga7tY1$`R@?*)S``W_2}%8}KV1vRK?>XEnO-H$6f$ip z!EO6m%{&1g2D4>;7Q4oirqhZsv+vizh0R zGf-=j-p(o`g`>}e+nj=%>TVZ8@82?t`Rf;)A}#1T$rL8yzudI)dACmJZA@17~NW!_V<<QlRolN+j1b1h8(V)^M0rL8UvqAljBcm(d}IG+CYgBWDq?xV;C&06t1MQ6NDJN*%6?V0ck zojDl5-0Wz>#|0nyBO8*5TGM(jNM_j==Bn#e3hUeS^z+hBb)Tq!4GK73pq7L7)$$%% znF_Nb@TWZ7hcJbud@sCf1TX)5<3YDm@4FC%+g@_%uobCnTIK{rSfRj{1jHTag*s|d z)R`NYrN!H}4Nk!;n3SF=)O!~#?XHQFCB3Y3Vsm@1xwlhQ0~+zdlAVq}pEv`q=}=N~ zcQr?D{tTVC{^pLSX`IEQvH(B?*JfMvk<{yX0Ru~l?Eu4~wQmEKdE`zhLW~UR^clXb zHD#LbeSPR%UTSw=-lx>`gJ{d^%hfpt1R%=6kp?zSln%g28Aku;W&e_U-yNA;>7oKR z;CVDrrFip&~qMaw6pQuY^b(9Ojvg)7hE_&twvtYlcI>|U)^YIGbP=V zEG@5nRcmA8SpnXwJ`qg0d^1P*s|#j!(Cf9gL3$=fo7PomW<^?!>&m*4Pl_#2d(IIm z6`(Wd(eWfWZ9^ULG4yuQe9G1jSVh0;)vlmHsyMEl5*jr|*w^h5BQQ5CSgwcE)7Z!P zD@%)_ArVm0co6qEei5U8)q08 zWvQbw_3^8Fa^ldE=F7x7+}pzT#vEPpE20nFwle|vqe)?#q!Jx=&E;tEZ%NtJR=6Kp z+O=MbxK<3~Pqm4w`_UBQZ@~3i!mn@Shl-%TwVm3^JMR%k2Bc9pJ#*2_DXomooFT5I zG)am>n6kbwZ+htmbw{}_VwlB)7m0_On1VncaA`pV7X>rWtm(lo@ju1Z<@)vIO3RWh z!lI-sK^9xSS`TGy)1IbfSUfB`=o_eA(%6yWwJm>{ausiaRkl#i@ujPvGXyVIeZE9GxuT;dgTU`C9Fe{B)dk z<_^q!8%a>v_NdaUpK4+}mpt|j6~2P&gTNy1k%YHA8@FYJZ$@O?2lW#e*HCk96| zG6Exe*w)8q`vDFZ;Igo+9`t@-qkrg{pqs$6<2eEVP|J$Cje8L*#}oz{z8x66Ro9FnvJSnc~G zj+!xTn)4)0e=&_)hjcbHWmUOEd&yz56$~i}UzchizA-OBW>Q%sOLuwW+!h)u`sd0o zB#g29^`sn9gnig7@XEh#N%e6SlGDJXwKlN!#Mpbcxj5mK$j-Bkoe!^61x?9mJ-74fEX_7IAp=@-@0;2p%m0^0-kIoM++0tLJ+S&kjb|IY4d!n}3n53MVi;qLf zTJs{2S9PDSdcv{XjfopI*aw= zix+jYA(l*QFL>c%{mq1zdbLrYdX8&+8V#FCcQed-P$yN&|JYu{$4-?uZRdSh%yn&% zU#)Wc&{iAL1?U)iLMLCKEsEax$EMdY_3I*?dZm)OO{%AMDh_MmnR1?8fDT^@ckIwz z)8|NhO;+H4f8ZNcr1toaij2pQ&BOgpU?{<+W8!#lLj@BjNEsh zx&8JA#7^~GYP^EnJ~>9~bP{-bIm1JCHX@|HDSatmM?P{{HTSCdS}auadFw0xq&nMV zt;XTJa_QZmH98k&Z)oGF#GcCP&^SL)Uo+ZT@VqGocd^&g)6;?(Z$E@#N}GQZeDXxP#=*&%tLQ9)h-eahKaQyn z2zGQT4R{Y@q+-|nxJ_3@KlV&QIRqF9oTNaFi7wKe_FStD8HGJ@SLb>EAzGwkYg8&g z?BJ|Ng*Q&cvc^YP>{~0JH_|isMnh@yka3RNyk$w8u00B+6Jt+P@oY~Wa3wwUVNpyF zA)+v{9wb1-_R2(?5F=yji6^Y>x2gfSQ(u3K!(2te&8mG4UWue00?N%auth1koQ=D( z$FivwtN0z&5UtFx`*P$lt>b~Cr_Z2LfqSh5k1LM-u4~@^**s}NIVQL@6+rz*1S5)X zwcVI{kgJ=pApd|P$I$aB5mEoq= z-h#7Abxu2LNDwL|m3|n=$$T?4N`2Pu)l~E#)^bF(GB=kqCcK-yz*EljJrZdHIA`Y> zDvTGER)k7Pigme{R`sn+UH_CXn&EB!>65TzMLSw@tgY~tE0~bKFEilEB!`4At?z zqqbrtFz4U#v`tfwJpRE*awVhxkWr19-+}9wn`SRNSz~k08%5SD zwRU5~1aOJ$S5B;TwrUk~wCRvARX0-2?d)y|AKSYqSn{}^va4+<_C}5UM!Ezt+mf*U z{Pu=EY&~HY)51}c1%lkXGL63L5*P`v>K+b~ZEsBJ6m(mo(h7w^#ea$=EXD!ys4Lr8sMJU#a;Psvx>ECdK#IgwlTZqEusfjn=7<%9Ixn}+u|Fcmdv zRcZM_vjy*Xr0VOzmump_-{D{|NtxJ>+JAH*O+z$L~ePZQ`M#Xqm zRZrY-*=7jJs@**+1p)J=F)VzEqLshl)oILx6OT*?t`gOfw$jV6NR4rw5rOoyX@Et%>Wm_#OWqSqZtr1p^Ld6Vw)Yeh;{`#Wv)iEtSNs}THrY)i zv&b}ix9Gkhj!E-sB9E(cPot8eBB$7p6z_!kdg&A2GL_IJ>mBZ$U*bjcyw(G%P6Chy zJgqVPbFaVL;GxHuIVj^|mMhhg&!0eTR0%J%00p?d+%DGB5Gf>Di zlc{I8#)I!YCwo4iZUY>=^g$h{1|=WqBiBK&h{L67ANOzbkJK2n@MP7d@n0=kdo4Z3c-FwZMuv z2C8ZQQ+qX@Gjny#)7%=(hmXpANq+ZCn$CY%mUb}B+`mH_NpG%ZV!F@_N1+dO(4q@gr+opDBiRJ;iT+^u9ehZ+Td#k z_O5^?$_ddEFBayZkuxMcV-JiWRN1~`SQ*Rrf|}Rehl~`m6X zi7rq22Qad9fcg8e?`?Y(z+Gz_e%oPX;4z-mYS*8c(=O~bOeMqx1&9WYOv-ZdVP%C! z_Jv3>{7bM!DK@BF0$RlKVXt&ks;;5;QS#Yp2NEghssV{`rXl@VD?hE`Q{x;QfRG;) zZJqVZfsKd!z>9pWvb8%0l59yq#RW~-npPt~1xI-?X7KgWEUsmbdlGpf2!SO(sDha*0ke@)&*90{?VmyhJKPP(W3#zz4KU~x35OV_0 zd!Q1yfP@EmryZM<9w)K#4%y=zy}iVNq&rCUR(=e)Ig9ylK^WDMr@T5!m17WH<4$K zMr*2X`lW0Xe6sR=t6;I_rTQ4w9zsL~*m~hU_9}81weRwdpsNbqxZ3D}Qu(mSnWaU; z-;nD@<@+|~8OaK7L#p;K4)_VK$0`N($d@5%VD~c*$`SS6b9CU0%~b8Izf7#zQnUUq z6@5$f1Lf>Gnn0>>H(ZrgO{YGgpo-V?QY%ZIM+r&>Sq2xb4cVBgrHX8sC5EZls#sZf z9(^A2om7?{(-0H9Me^hc=E%CpOfVvwI2%xIEdTGZFAV*{ef*?`W&9TgANWNjVr{4m z>NVq*P5UB1c`ePqc5X}bvm2O-M68`(JV3#ejyVAvdH{k{>c8zOnQ&_01lsTVN&0gI zDiH&ljd77k$qJSJ?7bp^cE8{5?Soc}1%LB}fOOj?6*iq=0d7z$pYX@uKD~x|KT@je zv0sl)QQWRT`3hFrD^||v%7|CN+BPNaCxFgDQLu*;=>OiKWCCU25|76C$2x(CW+F{e zgg<0SVP920OL_Hb_*r5DHW10`@bD?EzJ)x@KL5P=fd0SOKy`)-{u|Di7TO9|LI&X0 z6ylgR1;f4N^zUD|odW(ppCvJxkCD+I8cxod490!g5i!F2RH^vykJbCRmlpk+fcc+l zu1$DpLqP@)D>g+S*TLo58BoKUt{RW8pvq{G+=rZs;)k}`v1fH+Sx z0_+~<3h?ABmTOz#9dbHp8NP&r`u z`lf}*yp&!s9-H(fQ_TDmpVu9tjaniHTg8QE&&{0^ddBXcCuzUp^i6PPo;T68ls4ps z3;8xTXuov@>WUk@?!C)dtSL{I#MW61oa|H!mZr}&|4a)Mlc%$TZ5Ds1L|Bw2cV2Kg z=m(GBl)qtQ<(Twkf{m%z34%4 zv;)dr7*$n@!Ml9_sv4EhzF;W~YW3)g5=NzPyn znB7Xn?^dN32WlZhb$Qun@SfJOpiSWCFqRAmIPprc`?I~OouR|^KkqmDdh%V`_RYtZ z=Z2U8urClPr(WyhrI@~ccD`WS(9pcuIvGT6)RM6b0Q^8q22T_SD0G+jt@~6Hmo>f~ z@W|_pdrq9(il0N;bnwKkV$}wIVupth1%qm?m_@R1N@HCQQ&`^!al|i+W~2LNv}tgz zJuJrctSAO)w_kV=+K-9BuqOyxZ-+*MCNuow!8BXdXYKC*@Zd(9#H>CMv(u!iU%cl1 z&2^LBrHx@RRlQy%Ff8c!v5&<4!woDfmgV_GE^`<X{vMsy0*4+EQKSZmC zn}`yDg6NaD)$2+Kz_C{T9U<8oY0Za}2m#x`eyWlrq$&yKpaQ6+1B;{M2TTl89tm-8 zTQo!d6fdaK+RVEk6Ir^kim26Oef_jeR)gYU5YHj} zOi>k*<>I;ik^#&9=)96$xttVgvzXweqVZtz*#syXRSfsuW;f{4FNREM^x;@rs_ZdK z=qc)LAo5@0`Ir+Vc~W%MoQhn0yjgsc&g&mEf>w&DuLpBw;H#a8xs&RiAKl#3^rJf zkg{GLODY6c9NlWUV3f&@Y#r#Y3Pkgc?61J!fzwyIfr6AiDE3)Hm+Sf(2o+p&kme?4 z9X{CGn}ZQ|BkBF0C0Zd%G|?Eh>W+IiGlhTdQD_pnHH~{+RASCobqc+^y>IB~R>?cZ zA={pjS-@^G>a|`x@@yH|Sg?Z3;4H-Kg(hEM2g@6hA%b|5pCXD|GoM@Vj&h=rPqM@nD20;QqY8WH^%ZML z&94Q7gFk|#vzXE>81U%+dku}b0^tmTV&nay!es!lQJEv`y002MpXq1CdoeBhA3=Il z%eGJ#W*3|vLJj!0Filkk;5Ikt>!U=G;`iS$Az9UUjR5lRvR8B^j@;&bzh1dfOsrajIm6wD|Ed~wy&4YU2+*wLreB>C&+e}CA2wA4tsqA8vlqp-bK z4l)Rc%Q2wn>LgyN+y3c9o4ZUe!s@(*|KDC%7843F1D*3or=DEjOdRq#Y_-Ez4DxYc zZve?Mdd-EVA2CVUBkJpzo7Umz>?jaiGB6{=8-1cj^HuB)>-|pj=?^effDUwS3xs)2 z$|ao&7Mb%X!tKQbFnIFdy=}jppO@@4!qV??(Pe(RhZg6`pwsT};B1qbTzZ|V*&(=~ zVX;2%$HK-^Ulam0rI00#rxKp;lNwz9(Q%fxY*#8z#+1b%-QNIC z1Xzfp*mc~Np41E=nl9)G(w~&{uEPeG@@`y2?$cQTihecvC>k{sr;zbLGO}85MokP8o6TG&P?a4ih!>Y> zUsC@XN!6#e8FiXl;!VoEJ#5%P2&(X?vpQt7$AbLfrgWcH+S4`)$%I^^l;R765{xGq z4f1jv3!lBd+;{%mil|gRdI*Ow%MO3&g@p6U^hcPG-p$KgZVutZ4RiT|6ih0aSw(`6 z%wzk^v^ru7tPDpFgI6InE{{XhU|ZbQ)RJpY?nC{P7^~H+=VMV<7*W?H%GG{85U6BX zz*IIEELSG3`OFAyh-M4tPj1N3^ToxtTg-6&7J!{8RJxQaiF&+w_x&+7f+MUiI>Y8* z@l12b)&@ym1@TO}dJNJ__fm$#_Z<|Eu6wC5s!@S&FFiZ4GDFuw^e^CMjoZmwfjOHK z>aMu%n8Fku=dhrpdAhGEE!115Byz;eZ@Y;Yx%vF>ST&q&y5@0O)t8_Fh^o>YBJ~Jg zjj|zX8XoI&ny1emzjRa36w=lrAGYA+?S_RI&~C1Q*(L6z-B9KBYE;c{ot%XPYeU%AsH=#6-`u~LyQ+)9?A7F>@9 zeuOQ2d}W2chcPihFL6CnfU~O-duTwt0Lsnu+R%7{g@aaVRT%1#Gz*#Tbi6!lCcac_ z@v&+^x@pTNziXLKhjw(CwxIk7D%0MK4hFj$`N=c}TL;fs^ovuVZjr;F%r}FwfbbsA zB|=p;Wy+Q=f5nWfs#RriW3{i$_o16043c_7dF?BWO)eK1&0VB%kvSl@<+8+ID&N7CKb&wqu@CVcInuEU8#4kTwy^yv@tk=E-IDncaV?${nAN@<5XI?NskIGFEg3N zMM##pCJF=BQq|>u1OWz8h0P-L^8qc#Y`CB9x`Q|tdM}WTEsk9`&B`?)lRMb{U!15KZmf`L z%6DIhe=%c(q-;j&s8;l7n6G(}5v}qA#4p2_C6hiqBUz2M;9bmP;%%RxQHbFZ>)I%x zUtX3(p$0Mhj(&NRvJt7rD_O%xC#{J#LZ|i_hg*w6#RD$RC)(?6egOOhY0}{swh5uS8PBkt$-Q$c)BLUP+InOt!3a4^ zgY?#xvY4heMjtG|oErvPYdX7?hSPLFNuld?SC^Bz!+7I??ng!aS%aymFB_AmVSR>` z?w7Ny6cyHc_d>b6c99A42J-f|&Ff`k!Fi{TiXRPn!F{Z3b+bxB1kY6?c=y8eZR(Xf z9QZI!u7u9ad0wXH`bA)&FYjf~%$Td>mmsCbZ5+rDpF)#_%BhP0miyviQejlzTj9oR zurfnmv04VWF7v2zKH1(CcZ{JMwIqKq2%h$f?ubDpLrZ?kmuj>PpZ0s?T2H1 z3RZ25nKFv~|K>C%Z7LA!y~IJsq_0a`#rfJ}q02EhjH5uF&gMx)2DAbD#b)mbeka=i zp}dQX0O-l$VmvMNk>xjk2++IyCX2<|Eu$!eTpNYvoV3z%J|5^zSn0$!Zasc%ojO1` zAG?xXD%GuKvDNDpBG?$Z|5)T!N1RrW@rZ%YV0zxfnsMHLe~ctI0^RdJ|8HsCCFu9- zp1y}E;%AqY( zt-swUAPVSi!TYilygCja>$A8R-D&ssf5lp92ZJ9v<)Hd^(PW>bj_>jik4Ia8A`20BEpnZXRP z-2_?rYu?u|w_FSncEgaMX3@)OF&+h1KAfJRnm{2XwqIaTPC&PZ(NIyTjJqpX>mmluo0DlF=#x8J&_OI9QVq z?#@eixX~nFTt6KhUwms8IN3J;L8kk`A4?c1l`7TTcuo3^L{louk1Z~5iKDq$DJwW5 z=X%#WTEnsWZwe|~^W%5dioWHVm_SQ?%c{bVuA@R#UrDUY zmJ@8*3EZ%mtb8MZ$g#EesDp|fT=jSgdE4yY^UqVR`>9_WfY z3-glX2pRM*00742q6OKAqDd7@gfGSWRnbTl6tBnb+lY@muJ3+)lz%irn(RsKfVZ+U zZGv;49Jy`p>;7?!V`n@}BYjEa+b^)rayR$FppRawlZ4^b#B0N?4o;##d%9M)N=CHg_~rcLz+rS-Y%zsk6dRHxL%uTXczSwH_T=|?E+EiFpq8rCXVz++68!&`O>2`z+FI>Ir%|gDv1DaG)<|y)o$I>`Z`}5ggAERd#fpP+u|6|MmrvBn+!Y zx#r8%tXKtdhn@w&FbySlM`b@v&WF#AfA1*zvRzZcITrJMK7eJ=#3s}vJ8>~Wdyt~o zC^ye@&jhRwr_wc~Fx8TV>7HK#MJA^Wnt2X+z7+}^ExaprX5fahMII&Cg9yeQD{HTd zY3H}L`|P&^btFH8f)kligj7jw%p^*&9Hn9vd^%AM`axR;h#`&jZBQ3ct)YC7(fk4` zV|E0w2B{D#EWNQ_44bzP7icTt?sqEw7C~eTGZJ@yon>jVjG+{V>5|9AJjJ#V@ih$Z z)%jAPh80H@KhQ`^9(~=wH<|T~F^>Pu5%Moq(&5U-F;tPjPOZ2tfCzF|FqIxR0ZzNF z@s?{1oM-iP?YH3|GVCHOl5r?5*$*y3W)WBUZBkDzUqq6sZ8?_#Hhr8XA9Mp6;TPEv z#97!TLkW5$H`%=+_H#)ArA+Ta? zcPkBoM{X;W?1&<(zPM?aRL9pQ^*LPm;_P*hG(qN(=3a2HaFdi^!$Hk{FxKvMYXwmZ zhDcoEp^M|Cv(z%Qvfku)bk}znc18lJB~QB2|8JH5 z$t9`hYRqXd`pg@q{8^zcItwj2c3|?q(}q%kr1l8)B$qkh;N;+1vf*!MGgSJ~ z-e=053QCduNibhcv{!3Wi`w?mYfQ5?o_gg;v2-bR zaKD1%P|yrm70h!b%Zx)n5ShU*K&A_{7(OtNN!K3A{j0^Y(MruCL5y;OJwo0DItNpG zt4-<7p6WYec2O{=n?%YA7`l&{`3=!<($M>`P*>QXn~bMg@~9GeZbb5o8_HG0{P)Md zV`4;go3Q-a(#Phs9kWz1rdStVU{NcdO7?M<7+82;@!Z8lTDfA>4MG0e%rrp%&7a6& z4%UzM6Azx)2u&|T=eJJ;?JO##dH3Y-<0lGU{h5T<;GCj+LzmTgyws4PD)gP98c{>+ zCs!*Ow$|;v771U8K@$nRctGHy{c<)8rZ7G%9G9@(7`{cZ~J*>G57Pb%KsKYM~_K*A^bLIXK`7{59p3p@+RLv z%Pkkkow+PxYZGTq%&``~;~t6fX10}p_Oax=+x!tJBz|$B4)pEFsRT90;j?EhO&NX+ zx%%Xj4IVl+$FZVPIS}CMS9C6DXk6rr-0v}@axtI!R?r4-c5_DdYqFg7#xN`Xr(P?K z$em{$_e^Tq-rO==@5L7l1Bp=Od-4ayE~O!*ZJ5LFGB(w>u2K!-&Fah4{#@6JjWKNf znKGnb$gb6>pUyC?jwxF=H!$~j(IDAGtf-4J8g6y=Sy|!lz z>vz9?pq=6M^WrgSfK%SQebOpop&`cAMlNk0)(~5m29`vGL92RE8E+1?tbF>4_9AYH zyREJAb3OaK;HCFdwJ^tn(M`9&&qJlrIam4r5YQsTiO~@r6Pav_PUn$=c6rvWiZu; zS&t=j*2ZXlpx)k{Q0`}+4;Y}+RM+E2@X`+Xcp2I}BU`u!h_heJPdSy59%2w}xdcKi zRl}j?7q>EQ+4)N0twm=is~cXbinW!H*QH)IX=oh-kfBV0%3*+|jn+vHXwpRxxg=?| zsI;S??XKRpRi6|B>O1@lr&`Ritnnm3rm4hU@!GwhfV61yqO}nTwxLc^&{*K&&GbsF zk?mef?4UsVn9#4)MQ!Pkj{Il-0pztE4#K5@4BNP$iE+(4-0m~61GA{_w-#2#HWLW; z>@VM*+N_ak1o5y}ZetLfnE7=bTX+{*AkuHrovqGet;l%ESert#dNs8rXR)rg#mMsum5B*+^%?n;V+#gB;vxJyM;u3*146IJjg|7e6FkZ0!* zPHp@n($`I;>$Mdd2#PXF?U-8Omua_Iqds4?r)WmrP2I~+w2kP)nTmqM9-sB5sAWwQ z$uscv+|_abK9}V0yDuC|^_D*;+2V%FRre37q(5QnBO~mZMYkcIYFFguUv?el6R$=U zh#^GG8AyVeL4f)XiIj-&+<%Ma$DJ3jJE+qlrSg({o^pOt0i0QwW!S%^5}M!?l{=Rl zfxAm_TWf$~nE4NT|t;>Bth3^P>wWNU=S-VdKMnJ^v&{{fUT z(MHVrU|fi%)7qiGHI-P&9K>PWTh(ua%eDOaMNxh*D;Z}6u5>Z^8(&Ie<2}c*4~zeK z1Ms~+766>e5_PmJfGT<0daNj-%~>g|A#onHEH>Lb36aS!nJu@E)Rcubp&C?0bH^d1 zH-~KiL!uXZ5q-6+z9}Hr0{iZ%?04|$Z-4pCKrlnDDpJgqMru+CXGH0DbO6Yx}PU1}xR0u_F#Jd_zgQ_Lf{2PAKrNDYWD>|Fai_S(ZT@D{^&P@B3p3 zu_HZSk>h1m2I1qmP$KRdK;(|_uOJc2P}ym6IdriLY?Bye9~?@#OR4UU7cMh=%yHF7 zi!XSr~GKxwAjR-cR!;HD9^R|GZfZa3a0q?j!B z-4lJ*&UG;Pao{X78jzjIEXGZmlFeOB)OKi%ge~cVRlwjK*oO$p&lM zq8}ON)40!D6BK{@a*Y0= zGX&!*$deu93`vJA!&`%k)rs@UWa$k(-dZOV(hR_u6fM7hPqnpJh|RVQ zlo6~_iVbN&Hb9+gd$e;3jSdykZ2S~mV&|9<`0bG~@^Cssy&;*UW!OO!+c^s2ub8fV z+0UG_%n^^!@UXBs42RSpchXbWrQ0oA}S7}yG-GzgC@gU;e39&4jVKYyL_n8XKWpDu^$64s1D ze-s2b5|Xp5+HEp~+g3XUBKXzHKBtCjT{Rx92+sr1H7o&%V&*%xsb_|t6lCGSTLu`p zE2Rf0*7j0Do@Fnn@sBd?3XoKE$K4T#;FWpY`LOUPq++RP^bv=Y z(ul`@zaDaD54=CL!c$ix~$OVT-Sr82a|m^)!KH3G09%gec>x_ zV`KZkj1ruD$1ZdsJ2`deNqjZ*G2eXt3+rlh1#LuRqFMq0v__taB|fPzr;MWgc?7sR z%9=<|f8g32(GnN(aBweb(k+1q{UV>@ZSU7kUk~9isGgAW_VmR!88)R-bBr1Guj=Ra zB1fwQyCn3%*ZVtVdyC{A4V!a+X;;vKk86fu?G#F04HkX-hmM})Hj(X+Q8H1@y;uMR`1V1qOu0bJt5QY(ROOA= zAn)FVDPDYg;5^c1%Q!fu%zoClR1m*s#Th%uYnlGzh?C!m#Lx6I?q}`o7Eu)0gI_H$ z!6ioN7dCOvEsAhyJjyh(NOk>U=RwWHK%DQg>xqM+2r-D^CK+$z39(Y(vM^Lx=fHv^ z)-dp!HCJ{JQS`v=<@Z(Vw<*XP!m2k_VE0gz398)}_!h7yoNEIVO-~5Klpy$)uy<;!2oSR)81O z6(nXB-GfN44B4)#8X%v#H501g4NMwnijdK)^`kfM{ig2RIY4Zv%ab@IV#IP~#VjTx z8|vo}o#DJiX%_VRNP=xpS{0Vpcu7l2tjLH|wGt5JelxY!wnJolxx+K4ilcIR%~GsA z7Qu1iwHBS^XrjI|+@V?FF{Hf8B`6bryA(!JUz-*O+#haM+GPAJo6KsEGZaLop(O;1 z4wW~=`*LsWk))(fOsTnfQCcG+aPL8?W~jWcc&Yh*xSIHZ-O1cg=${`&`U z9LzVT3h%8fWx!N{g8N^IIlq<-90FqGZa08SKgZa;8OKTFiRgoK9rVZz8F*^GNMy&V z_{_T;n`wOHs95wa~w21;`AJp2g>M@#qdY0v1Vai$p?_f;`E!*`%-hHRa^ z(YKk^4wdMhSRkWlu;fWwRC!qrRSz!PqrPnBStbE8J`&sA<*BUkRK=lE-OXs0h2)>? z?%DUWa-$7C^f@vcu_bg0{dy4hS`K$C@LpJt6C1eEHoXTy;lVC$#IprS)Ru`Ou*dRS zy&WF9=`@PDmE3RPkR7>0<1CBx9Wkx~RL#a6z}PH@hF8$k?gZ>Wf0#UFDAtXAJ`hLm z3v|X&n9kn@Z7kegD9bNnMReXBHlG-z0ANC;cFbZb>muxTz3&0XEos|y}(J54SSow&C$dJnULQOvFzH(ac ztAIuXAm}VXDYc6z8#)k};vd!`CTAbYbrC1N(o3NO@}KMcqacG)Q@4h3;|TS~#!^Dz zc`$ap=sGUX0IeQ%@`n6E<$MXcd?8VosGdXdk&wde|Ko9uY=88QEE-{>+B-ma*GF1ypSJ}*dyZ4OGV=)zo z+cwPl0{VMJ%gXMi!hHCn4r9E9H2sE7rm+)|t2AIfVU72s815N%xoNO&w%w8{buHN3 z?356M^%dr19kopZlQlmFGV8)c7=1=VJzDuUj-{u^JDW_?Lj-steAQzzCxOPz#dNAz zyltS)i}?tm1Hq+?{_N9kc~3xrpKTRdn$Z?`oFqoI?Jz?+*@q1+%T8$C3mo2rNoJ_svo`tCArM&HA?PAly3!bZ@j2rEt!(kg;)e9}7W=hIJpHDR8(=-sX^F1d59INyawI z%=YrV+iL;iK-VMWq>q|bg%9t%m+=+i4#1^GCrj``D!URZ6g`x*+`F^`8tNx)yZy1eUvGymM;dTD$us;ZDk90{zMbkjzX|E^n*Tx-MWZ2CAc z%!uRFH~cYA1vx+!p23VUNg!-vBb_30$x5ewpJr=x?(^e7)+OWQ)>EZHzHHU%K>bF_ zMaXf-!~|RX!Bow;4`gA6?M9IdipTLg14lA+D!<&(8n!s_sp6Nvkg6PgpQlzTU7mN~ z4&y6Np4!RW&Kflekry@cIVy>I^HSNFRl>F(Z82|?9iwyJ2CB`aN7N)vXjq4mYP4JzL1$(v|xHpv~u3x7HfxZ zu8(a2RGb%)UIiY9_NTp8ew5Q(yge`%Pv?wGtlx0fhfZqM8|9SGqvgNKUk(aIxefSZ zj?jK5-TO#S3Ih1m#XWvC|MU)!X;W<4b#SX>%Xc$O;~%}ssZW@Id-3-dpbc^NfEwv8 zXa!W@mZvlQewF$$sK}fJAF6&r8<)@=bC!gS$4TIGkcOXjIYFLIJ=DG+D^Sav19R5i zVRuL=rJ*fS(F!zMcYG6)Dug+2WJHbjMX5GXnQs_MQ&0tR;EG{{%W$7;HkBncdRa>#P)(y3)QU7gKaP3npw>}OncIm_ zK2%g~d}t-ng*j@<|L>0{Da0T6(83+E@^?PH4%~Vrk9Ip;5~|Bi#$b}hus6)jpX+Yc zbvt@%w2$Q{gL8gkHD(;(74s}&MVeaiWPwN&tlPRBFNJ!GOtPGpE zxZtp>d~hCuSI47Nj=AD?XyN}ow=?um?7;%hH#UQH*~!>*7Yx2^7&~tb)!El=j^cEj zA1O=-mHvcw))52fOUd2w7~%=go(baNbskYPInYkLfD*LvT&{$saw!rg6jD7Ph2@E> z7+)EOt*`RzOd6B`Vqck>tA<%PiK+0;Z(DfiVxM?+uX-gl$eSC}C1$!KNbKNdeNMu( z;`k4U#^+-V^`(@7797 zLb7RDXXBcFTAR|{_H@|5%=rBG5a?!cE%v!ClkiJMiyK;MEM@Q0BAu$PbwfTIUf3*8 z^qdOH_=_~mX zYGN-OGqJs$^}1N*@R8jz>u4aI)D3p(lF?5Nc;X%8cgJ4ToVDs=vfuY833{(Ep&`Us zuri4I@Oz~45@?9QXw+{NR>VpsR2)G0Z1iR78D8$lv}(5O{qK{sDg1navaji_j{X6k z=4fO(D&!GIzyGremi<4M0(h4R2u6}E2`*chRw3;|-e@Zh=X@k!YX|v&F0jX2ys|H#Q+Lj$wIBM-Va% zYVQ%ul7eUX=(~PU(SmF7YMkaJ+>Nc8|Lks%t?pEiap~p_iQy~@)#-~cyGxS2U+VZ< zWF=SJX!0R7PLEPuD}1~I&}Z6+!4yA$tK2+eE#NxFWd`;E?;nFZi?@7m?X#0Nq9_@BJBH(C zUTC4u({5Ip^*xRqvofxFHv6hJ>>YO#4ZERA5k+z(G`Jyum-rhJci{mMCf$cyy$0Sl zwl{!%+1yHjX)K~lw1em*OalyOhp7GxE-`FMzN}KCz522|9+B0i-RU{a*u6^nGfXzA zJ_VAqhJ${AG{jH>cSJ^kQ=WYa;}b>3H5Wm>pHFymSCyd8VQfdhzBtsf?BH;{c71B zAWx83qOFU0N6t7C5Sl9q0g@W%Vg*fk(81-K{4Fv(-1j8IJy-(vEh-@?yCjZ^gDocU z+c4@*rxMHz(kIUGls8*O=C`rQ?$Zj3Jyqw)cHPH=$ij~isDl~7f8MnuV#>uf2D_&} zIA`Ts8bbJS!+jg9wG2sBML&dh{_Lo!zo#734*xDKakh}T6rizaI3jM{_;JL;V`bN5 z9UrXa(@MGRTO@azBZtkN$!USM3gMNp;drCI6MoQR=w6?nAu5kEFFe!xF1b#X^_kId z3o>A1S6?xK%6^XTmk` zQxY#|T2&0l2y`hMy@n@)-8S3}FJ1eRnw(G|8ec7g$%1nFJFPh}S+mSn8Sk|QP1iXD z>em4+zHEZBj}(JgyP=WyyXJ=d$30EoKlRQU6JOIsCuNOMMx7_a^VpUg>^;R zl(JCo<txKGf*Ru%Yv1<5iq>U5MfuH6@RPuxcF;4+O@h#MXW7=( z+2?Pkojv9etdFB!3ST%OUPMW8pdSY@BM`l_`B7j~UBA(E2T4*}aUJiOtHxF8!Fvih zL=+ZzTqR6CN=Tu&*Ox*?pN!KPE;z$1G&|kUL+Lebg#sV2qpu;Bdr9X9xl4$ql66x%H?utJW=Y1G*%Ia^-YaL{~#2X4Y|FbCaCHiSdkkL-IN869ISChl0?5er&`~Wl6 zH$lp!d1>PEvh6KF`!R=AR5Pub=wlz)|6}i~zuHRQIQyMWX-i8PTw0(=kYL4~4o(6= zf_rgy*G_Sl2q|6&0fH8H7~Cxchf-XMJJdFv@9v&G`xoqb32{Mh|O@R8LLU>7*C|sqqVN4A2St?K`!zTyks_6Y9%R+Wx z;t!1YzQ>Y$wrzsnzAIi5e!``H5m2G*n^6HHQRk@ZVoOBGA4pXzf6~hKQqK5oPRe7w zt9(`;ry-bMIKdEH6VgkyhD9$u2NJj0j!r%w|L9$+7TEr2>+v;A>F#0O-mcGA?x$Fs z>ZKO$yOrS5o+LY+J0EAC(_}-Tc#{gL^z!Qc*N}J4At~UrDP9oh^NO~&&|s2SD8!DN zBGK^uw5btA#ZvW~=_&o_Qz@#gHz|9XKkwC0TWU8Ou3A?6I*sXIPty9zs#})Dbj=FP zKqb}7o5_7p3omQxobZI?C9hG;9G z26hol4O~-hh$cAq+1Dm6@4_ipfSCF_5;Ho?BT!bf%IE}ObubQJt@&7!#KM77FjOW&f~?vgeP)*`UY!ONQdeKj@c=5Yvx#AEhLV|G%;&u#1F zd0zb;we^qbf=*j9GZz$!l$AryxK$EF=9L(x5ou`+C<&}FC%!?w>#emJl5Jfei~;euN{z>?$;jcR9Vy)wQ$gMR%2(J|Sr zkd{Fe272LONzCb~?QaW&U^Cr-hl@d%)UDLM*`kuGO6jBxe*ZlAfh0@f>=h}ubTA@= zTIQUReO%0yqr(UUJu-P_JRe0ee6;Bot5dX4_pGWyF$k#Ct=zdE~8Yp233t|k<6zFfZS{5CuU!=?d~&3QoR6lZ?}RhT_CxO#C z_1IL{QLeIJve{E^Oia{5YWbgON?1O&C>n6C6Uz~;5|zV{e$@C4DbEU_KhTzm=eoe= z@a5Wy-JEXu7g1KqD14<`)Qpl^bWUWzkbU#0UP;IRnIytV1Y&+r%@fK~d*ozPndein z4Q*>4oHSR@GlU_qRZl{b4)cCqV}`q|Vdd@tu$ZZ? zn(j+ehhh$vU)912e{kpQ5r}n)h40aCO;n+@-i5GKvU_N002Im*l-f=QFYL!|Y7Es9 z%TJr&%dCU|rUD_~BCNSYxTIU+Co0;x0g>BOdNE)3l0f+B^{0=GAt7P zS&O`&9YPc{QGT{4$pth#J=WWgJ2Ipq&Qh;s1ab9|d9!Vl+Z32o)DnElZ}i<`{Q>PWpcJBx;;i{F|m=}q;4 z5E*qjOpp8sv2*c5c#A6NnKL`3-7z`N+Z2fMVZ!+Yd~S_G-+r*Zkcwq{j|3u$j|FW--_7*lat zvJ%76PE|NC!-~G@I*J%F`XrUbThCDyJqrbCoh0$?YS%?=tY0nF<7cL6+g!Wb3(Q1q z>8fAXz;tHgULAaHbza>gSOrZ%wFJR^5k`rdzNXe)KfDeM@_x?r=y5?pTHLIeyu8BB z#a}XHTg|jdbtNMQu{%$D^|-k9$A_qBR#lXX5sre}Mw`-6JVmeaX{FAk0dW0;$9cbv z$b7Va{n&1UG|SFP>^bs_c9NbWcka)77Qki}t^`6*R5-q4I{!@5n&trrOn9%3AxnR% zB)+SkON^o-@4`iA1@5J|erlL>W;u>OG~VG@^xL3SI^hTk_889c>%)w1Pfz&Rg{}+z zU7Fh$o%Ee4!=A>M&7|j28Mn zmSm1(w{F@lC7Gy5^+Pz<%PYBr&Ws--2I1*rrTOZiQqcp9y9Bz`;? zi<(r40+>y0DQGwBfs4q&Jd1s}3gQY$Qsp>-Ej}S6T_LFkg=k3e42iiB%=<0 z4M>=uE#S)$?JVBaL;M*|`;$SDkpWSq(+d;X9N>e$H~7mq+EODX<^}(b8aL7|UpPky zxMh8gwdK~mB-$wJPcm)Ff#D{I0m2LRWqVKoI3g)}77bamo1<1`)8A4nn|&jGq&59S zkpJVW;)i)UIr+7COAY6$Q<0jY?LCe1)=KWX{>5yUlyb`r2ev!biB13kyUeU{B1uKl zK;hB}5)|tn`4G9`B zPxZApo${3_$d&0)>*$wh*um9U0q(wLHSl$EQ`~T!y`wH4M51($(37dmZ$7-Su#v&p z&l`KF7rw0+rUk`!#`Ic3N1m~_Q=&-jU?Fl|kpDhnJ)=rb z86Pv3ZJ+BoGs{sl-bfY;dV=8ZxSq7^oq6nI$v1FTSlbBy+s=m{jKPtROIlIeOGC-)gFK zDqjA1;Lh{Ky~{U2$OG2~4U);y>4qF)5F)`?N@>y(0`2drvRXwI2s5@* z6PoSNpsU}WFe+%rZdS?xpViST_1Q0uSNj3#<;YZ;nM0eT9)$LN#5jt-57{q~{ zz(s+e9j;G;Y?59V%)8)+KRGs$!=qKxez>aO?KjCiKReqZ+i(UBbCOi=sH;L)Y$Oka znY|?Cmz9lp@|+YH?S>AyMu_+Dad|U?lJ<}mjD)AI8ElP=td_F*!^ndPkKv)zgPl5H z2$-2hB+6~Hy4Wn;W(E{L9%QGhgsUHuo=G;O>oO`z`b0_=n+z2GLaF2SoDZNf3uZk} ziZDw4q$X`FNaYk`HzXF^t8`5LyA!}vhGDomwZ8DemuM7KH`4Is{E_p3eC~8{W`fd? z9=7YvR zc&XS#xWL4{#j8bu4PEjP%|FS4y028{&~mww5m#pF0ga(o2N9}dquy9Q@Ny9yhu4lO zI}0wN+%TK@jy+#C!{n*4+)~fm0Cd{DhFx-CbM2QSY1fKKhNNibCT{qc><2N~lY(i` zN;{IbM*Dm7BZFWaJ&EIEIuEfF-4Bih^k`$0u-LLsCfmoa$`%6J=_D$7e?-@5yrqln zT3af*$z17^A>t*%+@HO;m;|#r*dBZ>gsIj@M}kCw2_-XP`4w;gXCYQ)#Pi7oI)6zU z6JU5#Fkq(!qdrr=dO0%)5uGWwjnDp2P~bNZlnNV0^AO&KoF%dqP9<)bYsR_E&pj8I zcPwFG)ew#1Y^F(c+UZ;VeAV!qPFZd-)XdV9{+(%M%?h7)@7;=Uog>Ki=FE_CedU7F z+F>f6GQW4ePNn5=K1q*1>-+4It4uAg=wi0eF-?FWbYHjd%vtZ2#BtqT?|mC6UQbf! zus6~ET;;Ucesycmu;6@nKyTrjY~V4Uvs4U{?xuh9nC5=@NFKr_acX(?Ng!+<7POrRxe>tF(~VN5j1 zzNh3u3IhUVKKc>!qQ5B8Td#WP&Gq|4n-4ea`YFp)G!`Z+sW#5qxfFOY*|`{@%4A}g zzQe`eO+^@+m^s|byCJhF5>sK9W!17bA6h3lW&%>D-xSHe0;R51J-8s|v-qGzsLZwJ zk1o?$0X&N`2g~(~CY}U7_s-cCSqkYMCf)ExMm8Yy0?K6Kxqt5c1i46mZ>75;Lu$jXFI0TfC82D zmi%q0tv<#t8hI`oYBloNv`;S*E1EJ9>J_DqF@qVR0igIbjDvj3bZ!BKCDo>T!+q~! zt1?;p#H0*-ZUc*|$C9Aj)i4Xly8MhK8`okZgr)K^@e$)4QO-U9W6a%cN33D2x>Afp=$CWs|Y%I#UQO0jyEqHsxZE zz7rB+1z9u7ZE{eqj5_K^txS~q1SSo-0znycm!!ZZk^HT|>1?y6_;dS(n8=JbvDH^Z z;wlbLpH`^nI3lr(6~rG<8vr#^TOY82r#Z6$nOXA!J#TF?5|}o8!)P3qhYx0(VeS}6 zUbs5qX_eoZ6$r=xG3qTLY@aKw)W&ZHOHv40*TmSW>8s}6y|^B<)$n%p5$u{7(1Rf^ zQsn;wh^h&h4i~6RJZF%Rz_Nb0>$i#4PraD6ZrikJt_MUwgTvb$xJ10t*|RwS-s>5- zRxMkwG0bj_E-wJM_{w2|ES)k_+3B{ZSu+$viD-*~fro<(ys_EiODczjA*exK

WOM%b9!Gpa;f98P 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

0%@8Muo)BY%{GJnxUFwOn7NFq9?$2|qzOJ^ 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

QDbotZ%YLBS(xaD7N!GMCuvZAT;+OhG5 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 diff --git a/examples/astra/README.md b/examples/astra/README.md deleted file mode 100644 index 2f36bf50..00000000 --- a/examples/astra/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## Overview - -This example app is an open-source alternative to [Google's Project Astra](https://deepmind.google/technologies/gemini/project-astra/). It leverages the exo library to run on your own devices, providing a fully transparent and customizable experience compared to Google's closed-source API. diff --git a/examples/astra/astra.xcodeproj/project.pbxproj b/examples/astra/astra.xcodeproj/project.pbxproj deleted file mode 100644 index bcbf09ed..00000000 --- a/examples/astra/astra.xcodeproj/project.pbxproj +++ /dev/null @@ -1,653 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 56; - objects = { - -/* Begin PBXBuildFile section */ - FA3E988F2C725A0200E4E795 /* astraApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA3E988E2C725A0200E4E795 /* astraApp.swift */; }; - FA3E98912C725A0200E4E795 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA3E98902C725A0200E4E795 /* ContentView.swift */; }; - FA3E98932C725A0300E4E795 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FA3E98922C725A0300E4E795 /* Assets.xcassets */; }; - FA3E98972C725A0300E4E795 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FA3E98962C725A0300E4E795 /* Preview Assets.xcassets */; }; - FA3E98A12C725A0300E4E795 /* astraTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA3E98A02C725A0300E4E795 /* astraTests.swift */; }; - FA3E98AB2C725A0300E4E795 /* astraUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA3E98AA2C725A0300E4E795 /* astraUITests.swift */; }; - FA3E98AD2C725A0300E4E795 /* astraUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA3E98AC2C725A0300E4E795 /* astraUITestsLaunchTests.swift */; }; - FA3E98BB2C725BF800E4E795 /* WhisperKit in Frameworks */ = {isa = PBXBuildFile; productRef = FA3E98BA2C725BF800E4E795 /* WhisperKit */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - FA3E989D2C725A0300E4E795 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = FA3E98832C725A0200E4E795 /* Project object */; - proxyType = 1; - remoteGlobalIDString = FA3E988A2C725A0200E4E795; - remoteInfo = astra; - }; - FA3E98A72C725A0300E4E795 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = FA3E98832C725A0200E4E795 /* Project object */; - proxyType = 1; - remoteGlobalIDString = FA3E988A2C725A0200E4E795; - remoteInfo = astra; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - FA3E988B2C725A0200E4E795 /* astra.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = astra.app; sourceTree = BUILT_PRODUCTS_DIR; }; - FA3E988E2C725A0200E4E795 /* astraApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = astraApp.swift; sourceTree = ""; }; - FA3E98902C725A0200E4E795 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; - FA3E98922C725A0300E4E795 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - FA3E98942C725A0300E4E795 /* astra.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = astra.entitlements; sourceTree = ""; }; - FA3E98962C725A0300E4E795 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - FA3E989C2C725A0300E4E795 /* astraTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = astraTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - FA3E98A02C725A0300E4E795 /* astraTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = astraTests.swift; sourceTree = ""; }; - FA3E98A62C725A0300E4E795 /* astraUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = astraUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - FA3E98AA2C725A0300E4E795 /* astraUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = astraUITests.swift; sourceTree = ""; }; - FA3E98AC2C725A0300E4E795 /* astraUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = astraUITestsLaunchTests.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - FA3E98882C725A0200E4E795 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - FA3E98BB2C725BF800E4E795 /* WhisperKit in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - FA3E98992C725A0300E4E795 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - FA3E98A32C725A0300E4E795 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - FA3E98822C725A0200E4E795 = { - isa = PBXGroup; - children = ( - FA3E988D2C725A0200E4E795 /* astra */, - FA3E989F2C725A0300E4E795 /* astraTests */, - FA3E98A92C725A0300E4E795 /* astraUITests */, - FA3E988C2C725A0200E4E795 /* Products */, - ); - sourceTree = ""; - }; - FA3E988C2C725A0200E4E795 /* Products */ = { - isa = PBXGroup; - children = ( - FA3E988B2C725A0200E4E795 /* astra.app */, - FA3E989C2C725A0300E4E795 /* astraTests.xctest */, - FA3E98A62C725A0300E4E795 /* astraUITests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - FA3E988D2C725A0200E4E795 /* astra */ = { - isa = PBXGroup; - children = ( - FA3E988E2C725A0200E4E795 /* astraApp.swift */, - FA3E98902C725A0200E4E795 /* ContentView.swift */, - FA3E98922C725A0300E4E795 /* Assets.xcassets */, - FA3E98942C725A0300E4E795 /* astra.entitlements */, - FA3E98952C725A0300E4E795 /* Preview Content */, - ); - path = astra; - sourceTree = ""; - }; - FA3E98952C725A0300E4E795 /* Preview Content */ = { - isa = PBXGroup; - children = ( - FA3E98962C725A0300E4E795 /* Preview Assets.xcassets */, - ); - path = "Preview Content"; - sourceTree = ""; - }; - FA3E989F2C725A0300E4E795 /* astraTests */ = { - isa = PBXGroup; - children = ( - FA3E98A02C725A0300E4E795 /* astraTests.swift */, - ); - path = astraTests; - sourceTree = ""; - }; - FA3E98A92C725A0300E4E795 /* astraUITests */ = { - isa = PBXGroup; - children = ( - FA3E98AA2C725A0300E4E795 /* astraUITests.swift */, - FA3E98AC2C725A0300E4E795 /* astraUITestsLaunchTests.swift */, - ); - path = astraUITests; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - FA3E988A2C725A0200E4E795 /* astra */ = { - isa = PBXNativeTarget; - buildConfigurationList = FA3E98B02C725A0300E4E795 /* Build configuration list for PBXNativeTarget "astra" */; - buildPhases = ( - FA3E98872C725A0200E4E795 /* Sources */, - FA3E98882C725A0200E4E795 /* Frameworks */, - FA3E98892C725A0200E4E795 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = astra; - packageProductDependencies = ( - FA3E98BA2C725BF800E4E795 /* WhisperKit */, - ); - productName = astra; - productReference = FA3E988B2C725A0200E4E795 /* astra.app */; - productType = "com.apple.product-type.application"; - }; - FA3E989B2C725A0300E4E795 /* astraTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = FA3E98B32C725A0300E4E795 /* Build configuration list for PBXNativeTarget "astraTests" */; - buildPhases = ( - FA3E98982C725A0300E4E795 /* Sources */, - FA3E98992C725A0300E4E795 /* Frameworks */, - FA3E989A2C725A0300E4E795 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - FA3E989E2C725A0300E4E795 /* PBXTargetDependency */, - ); - name = astraTests; - productName = astraTests; - productReference = FA3E989C2C725A0300E4E795 /* astraTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - FA3E98A52C725A0300E4E795 /* astraUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = FA3E98B62C725A0300E4E795 /* Build configuration list for PBXNativeTarget "astraUITests" */; - buildPhases = ( - FA3E98A22C725A0300E4E795 /* Sources */, - FA3E98A32C725A0300E4E795 /* Frameworks */, - FA3E98A42C725A0300E4E795 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - FA3E98A82C725A0300E4E795 /* PBXTargetDependency */, - ); - name = astraUITests; - productName = astraUITests; - productReference = FA3E98A62C725A0300E4E795 /* astraUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - FA3E98832C725A0200E4E795 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1540; - LastUpgradeCheck = 1540; - TargetAttributes = { - FA3E988A2C725A0200E4E795 = { - CreatedOnToolsVersion = 15.4; - }; - FA3E989B2C725A0300E4E795 = { - CreatedOnToolsVersion = 15.4; - TestTargetID = FA3E988A2C725A0200E4E795; - }; - FA3E98A52C725A0300E4E795 = { - CreatedOnToolsVersion = 15.4; - TestTargetID = FA3E988A2C725A0200E4E795; - }; - }; - }; - buildConfigurationList = FA3E98862C725A0200E4E795 /* Build configuration list for PBXProject "astra" */; - compatibilityVersion = "Xcode 14.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = FA3E98822C725A0200E4E795; - packageReferences = ( - FA3E98B92C725BF800E4E795 /* XCRemoteSwiftPackageReference "whisperkit" */, - ); - productRefGroup = FA3E988C2C725A0200E4E795 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - FA3E988A2C725A0200E4E795 /* astra */, - FA3E989B2C725A0300E4E795 /* astraTests */, - FA3E98A52C725A0300E4E795 /* astraUITests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - FA3E98892C725A0200E4E795 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - FA3E98972C725A0300E4E795 /* Preview Assets.xcassets in Resources */, - FA3E98932C725A0300E4E795 /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - FA3E989A2C725A0300E4E795 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - FA3E98A42C725A0300E4E795 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - FA3E98872C725A0200E4E795 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - FA3E98912C725A0200E4E795 /* ContentView.swift in Sources */, - FA3E988F2C725A0200E4E795 /* astraApp.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - FA3E98982C725A0300E4E795 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - FA3E98A12C725A0300E4E795 /* astraTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - FA3E98A22C725A0300E4E795 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - FA3E98AD2C725A0300E4E795 /* astraUITestsLaunchTests.swift in Sources */, - FA3E98AB2C725A0300E4E795 /* astraUITests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - FA3E989E2C725A0300E4E795 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = FA3E988A2C725A0200E4E795 /* astra */; - targetProxy = FA3E989D2C725A0300E4E795 /* PBXContainerItemProxy */; - }; - FA3E98A82C725A0300E4E795 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = FA3E988A2C725A0200E4E795 /* astra */; - targetProxy = FA3E98A72C725A0300E4E795 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - FA3E98AE2C725A0300E4E795 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - FA3E98AF2C725A0300E4E795 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SWIFT_COMPILATION_MODE = wholemodule; - }; - name = Release; - }; - FA3E98B12C725A0300E4E795 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = astra/astra.entitlements; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"astra/Preview Content\""; - DEVELOPMENT_TEAM = 8NFAS2P4ND; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSCameraUsageDescription = "Capture from camera to send to vision model"; - INFOPLIST_KEY_NSMicrophoneUsageDescription = "Uses your microphone for transcribing audio"; - "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; - "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; - "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; - "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; - "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; - "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; - "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; - "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 17.5; - LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 14.4; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = net.exolabs.astra; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - FA3E98B22C725A0300E4E795 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = astra/astra.entitlements; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"astra/Preview Content\""; - DEVELOPMENT_TEAM = 8NFAS2P4ND; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSCameraUsageDescription = "Capture from camera to send to vision model"; - INFOPLIST_KEY_NSMicrophoneUsageDescription = "Uses your microphone for transcribing audio"; - "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; - "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; - "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; - "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; - "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; - "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; - "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; - "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 17.5; - LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; - "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 14.4; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = net.exolabs.astra; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - FA3E98B42C725A0300E4E795 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 8NFAS2P4ND; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.5; - MACOSX_DEPLOYMENT_TARGET = 14.4; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = net.exolabs.astraTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/astra.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/astra"; - }; - name = Debug; - }; - FA3E98B52C725A0300E4E795 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 8NFAS2P4ND; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.5; - MACOSX_DEPLOYMENT_TARGET = 14.4; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = net.exolabs.astraTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/astra.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/astra"; - }; - name = Release; - }; - FA3E98B72C725A0300E4E795 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 8NFAS2P4ND; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.5; - MACOSX_DEPLOYMENT_TARGET = 14.4; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = net.exolabs.astraUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = astra; - }; - name = Debug; - }; - FA3E98B82C725A0300E4E795 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 8NFAS2P4ND; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.5; - MACOSX_DEPLOYMENT_TARGET = 14.4; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = net.exolabs.astraUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = auto; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = astra; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - FA3E98862C725A0200E4E795 /* Build configuration list for PBXProject "astra" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - FA3E98AE2C725A0300E4E795 /* Debug */, - FA3E98AF2C725A0300E4E795 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - FA3E98B02C725A0300E4E795 /* Build configuration list for PBXNativeTarget "astra" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - FA3E98B12C725A0300E4E795 /* Debug */, - FA3E98B22C725A0300E4E795 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - FA3E98B32C725A0300E4E795 /* Build configuration list for PBXNativeTarget "astraTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - FA3E98B42C725A0300E4E795 /* Debug */, - FA3E98B52C725A0300E4E795 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - FA3E98B62C725A0300E4E795 /* Build configuration list for PBXNativeTarget "astraUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - FA3E98B72C725A0300E4E795 /* Debug */, - FA3E98B82C725A0300E4E795 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - FA3E98B92C725BF800E4E795 /* XCRemoteSwiftPackageReference "whisperkit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/argmaxinc/whisperkit"; - requirement = { - branch = main; - kind = branch; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - FA3E98BA2C725BF800E4E795 /* WhisperKit */ = { - isa = XCSwiftPackageProductDependency; - package = FA3E98B92C725BF800E4E795 /* XCRemoteSwiftPackageReference "whisperkit" */; - productName = WhisperKit; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = FA3E98832C725A0200E4E795 /* Project object */; -} diff --git a/examples/astra/astra.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/astra/astra.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6..00000000 --- a/examples/astra/astra.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/examples/astra/astra.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/astra/astra.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/examples/astra/astra.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/examples/astra/astra.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/examples/astra/astra.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index edc15da2..00000000 --- a/examples/astra/astra.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,33 +0,0 @@ -{ - "originHash" : "8f61689e55c5551e76f2c686d145061dc1fa621a58cbca576565ebfabc15c894", - "pins" : [ - { - "identity" : "swift-argument-parser", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser.git", - "state" : { - "revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41", - "version" : "1.3.0" - } - }, - { - "identity" : "swift-transformers", - "kind" : "remoteSourceControl", - "location" : "https://github.com/huggingface/swift-transformers.git", - "state" : { - "revision" : "74b94211bdc741694ed7e700a1104c72e5ba68fe", - "version" : "0.1.7" - } - }, - { - "identity" : "whisperkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/argmaxinc/whisperkit", - "state" : { - "branch" : "main", - "revision" : "59aaa4e5f211622f9a5e133440220d9974641d3b" - } - } - ], - "version" : 3 -} diff --git a/examples/astra/astra/Assets.xcassets/AccentColor.colorset/Contents.json b/examples/astra/astra/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb878970..00000000 --- a/examples/astra/astra/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/examples/astra/astra/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/astra/astra/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 532cd729..00000000 --- a/examples/astra/astra/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "16x16" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "16x16" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "32x32" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "32x32" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "128x128" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "128x128" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "256x256" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "256x256" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "512x512" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "512x512" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/examples/astra/astra/Assets.xcassets/Contents.json b/examples/astra/astra/Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/examples/astra/astra/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/examples/astra/astra/ContentView.swift b/examples/astra/astra/ContentView.swift deleted file mode 100644 index 52327fc2..00000000 --- a/examples/astra/astra/ContentView.swift +++ /dev/null @@ -1,729 +0,0 @@ -import SwiftUI -import WhisperKit -import AVFoundation -import Foundation -import Combine -import Vision -import AVFAudio - -actor CameraActor { - let captureSession = AVCaptureSession() - private let photoOutput = AVCapturePhotoOutput() - private var isConfigured = false - private var currentPhotoCaptureDelegate: PhotoCaptureDelegate? - - func configure() throws { - guard !isConfigured else { - print("Camera already configured") - return - } - - print("Starting camera configuration") - - guard let camera = AVCaptureDevice.default(for: .video) else { - print("No camera device available") - throw CameraError.cameraUnavailable - } - - do { - let input = try AVCaptureDeviceInput(device: camera) - print("Camera input created successfully") - - guard captureSession.canAddInput(input) else { - print("Cannot add camera input to session") - throw CameraError.cannotAddInputOutput - } - - guard captureSession.canAddOutput(photoOutput) else { - print("Cannot add photo output to session") - throw CameraError.cannotAddInputOutput - } - - captureSession.beginConfiguration() - captureSession.addInput(input) - captureSession.addOutput(photoOutput) - captureSession.commitConfiguration() - - print("Camera session configured successfully") - - Task.detached { [weak self] in - self?.captureSession.startRunning() - print("Camera session started running") - } - - isConfigured = true - print("Camera fully configured and ready") - } catch { - print("Error during camera configuration: \(error)") - throw error - } - } - - func capturePhoto() async throws -> String { - guard isConfigured else { - throw CameraError.notConfigured - } - - return try await withCheckedThrowingContinuation { continuation in - let photoSettings = AVCapturePhotoSettings() - - let delegate = PhotoCaptureDelegate { result in - self.currentPhotoCaptureDelegate = nil - continuation.resume(with: result) - } - - self.currentPhotoCaptureDelegate = delegate - - Task { @MainActor in - self.photoOutput.capturePhoto(with: photoSettings, delegate: delegate) - } - } - } -} - -class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate { - private let completionHandler: (Result) -> Void - - init(completionHandler: @escaping (Result) -> Void) { - self.completionHandler = completionHandler - } - - func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { - if let error = error { - completionHandler(.failure(error)) - return - } - - guard let imageData = photo.fileDataRepresentation() else { - completionHandler(.failure(CameraError.imageProcessingFailed)) - return - } - - let base64String = imageData.base64EncodedString() - completionHandler(.success(base64String)) - } -} - -enum CameraError: Error { - case cameraUnavailable - case cannotAddInputOutput - case notConfigured - case imageProcessingFailed -} - -struct CameraPreview: UIViewControllerRepresentable { - let cameraActor: CameraActor - - func makeUIViewController(context: Context) -> UIViewController { - let viewController = UIViewController() - let previewLayer = AVCaptureVideoPreviewLayer(session: cameraActor.captureSession) - previewLayer.videoGravity = .resizeAspectFill - viewController.view.layer.addSublayer(previewLayer) - previewLayer.frame = viewController.view.bounds - return viewController - } - - func updateUIViewController(_ uiViewController: UIViewController, context: Context) { - if let previewLayer = uiViewController.view.layer.sublayers?.first as? AVCaptureVideoPreviewLayer { - previewLayer.frame = uiViewController.view.bounds - } - } -} - -struct ContentView: View { - @State private var whisperKit: WhisperKit? - @State private var isListening = false - @State private var currentText = "" - @State private var bufferSeconds: Double = 0.5 // or whatever the actual buffer size is - @State private var modelState: ModelState = .unloaded - - @AppStorage("selectedModel") private var selectedModel: String = "large-v3" - @AppStorage("selectedLanguage") private var selectedLanguage: String = "english" - @AppStorage("selectedTask") private var selectedTask: String = "transcribe" - - @State private var isRecordingMemo = false - @State private var currentMemo = "" - @State private var lastVoiceActivityTime = Date() - @State private var silenceTimer: Timer? - @State private var voiceActivityThreshold: Float = 0.40 - @State private var silenceTimeThreshold = 1.0 - @State private var debugText = "" - @State private var apiEndpoint = "http://192.168.212.74:52415/v1/chat/completions" - @State private var audioBuffer: [Float] = [] - @State private var bufferDuration: Double = 0.5 // 0.5 seconds buffer - @State private var isInitialTranscription = true - @State private var streamingResponse = "" - @State private var cancellables = Set() - - @State private var cameraActor: CameraActor? - @State private var showLiveCamera = false - @State private var capturedImageBase64: String? - @State private var errorMessage: String? - @State private var isCameraReady = false - - @State private var speechSynthesizer = AVSpeechSynthesizer() - @State private var speechBuffer = "" - @State private var wordCount = 0 - let maxWords = 12 - @State private var originalSilenceThreshold: Float = 0.40 - @State private var isTTSActive: Bool = false - @State private var canRecordAudio: Bool = true - @State private var ttsFinishTime: Date? - - @State private var isRequestInProgress = false - @State private var isFirst3WordsOfResponse = true - - var body: some View { - ZStack { - if showLiveCamera, isCameraReady, let actor = cameraActor { - CameraPreview(cameraActor: actor) - .edgesIgnoringSafeArea(.all) - } - - ScrollView { - VStack { - Text(currentText) - .padding() - - Text(isListening ? "Listening..." : "Not listening") - .foregroundColor(isListening ? .green : .red) - - if isRecordingMemo { - Text("Recording...") - .foregroundColor(.blue) - } - - Picker("Model", selection: $selectedModel) { - Text("large-v3").tag("large-v3") - Text("base").tag("base") - Text("small").tag("small") - } - .pickerStyle(SegmentedPickerStyle()) - .padding() - - Button("Load Model") { - loadModel(selectedModel) - } - .disabled(modelState == .loaded) - .padding() - - Text("Model State: \(modelState.description)") - - Text(debugText) - .font(.caption) - .foregroundColor(.gray) - - Text("TTS Active: \(isTTSActive ? "Yes" : "No")") - .font(.caption) - .foregroundColor(isTTSActive ? .green : .red) - - Text("Current Silence Threshold: \(voiceActivityThreshold, specifier: "%.2f")") - .font(.caption) - .foregroundColor(.blue) - - Text("Original Silence Threshold: \(originalSilenceThreshold, specifier: "%.2f")") - .font(.caption) - .foregroundColor(.orange) - - Slider(value: $voiceActivityThreshold, in: 0.01...1.0) { - Text("Voice Activity Threshold: \(voiceActivityThreshold, specifier: "%.2f")") - } - - Text("API Response:") - .font(.headline) - .padding(.top) - - ScrollView { - Text(streamingResponse) - .padding() - } - .frame(height: 200) - .border(Color.gray, width: 1) - - Toggle("Show Live Camera", isOn: $showLiveCamera) - .padding() - .onChange(of: showLiveCamera) { newValue in - if newValue { - Task { - await setupCamera() - } - } else { - cameraActor = nil - isCameraReady = false - print("Camera disabled") - } - } - - if !showLiveCamera { - Text("Camera Ready: \(isCameraReady ? "Yes" : "No")") - .padding() - - if let errorMessage = errorMessage { - Text("Error: \(errorMessage)") - .foregroundColor(.red) - .padding() - } - } - } - } - .opacity(showLiveCamera ? 0.7 : 1) - } - .onAppear { - setupWhisperKit() - startTTSMonitoring() - } - } - - private func setupWhisperKit() { - Task { - do { - whisperKit = try await WhisperKit(verbose: true) - print("WhisperKit initialized successfully") - startListening() - startAudioBuffering() - } catch { - print("Error initializing WhisperKit: \(error)") - } - } - } - - private func startTTSMonitoring() { - Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in - let newTTSActive = speechSynthesizer.isSpeaking - if newTTSActive != isTTSActive { - isTTSActive = newTTSActive - canRecordAudio = !newTTSActive - if isTTSActive { - voiceActivityThreshold = 1.0 // Set to max to prevent recording - whisperKit?.audioProcessor.purgeAudioSamples(keepingLast: 0) // Flush audio buffer - print("TTS Started - Audio recording paused") - } else { - ttsFinishTime = Date() - print("TTS Finished - Waiting 0.5 seconds before resuming audio recording") - } - updateDebugText() - } - - if !isTTSActive, let finishTime = ttsFinishTime, Date().timeIntervalSince(finishTime) >= 0.5 { - whisperKit?.audioProcessor.purgeAudioSamples(keepingLast: 0) // Flush audio buffer - voiceActivityThreshold = originalSilenceThreshold - canRecordAudio = true - ttsFinishTime = nil - print("Audio recording resumed after TTS delay") - updateDebugText() - } - } - } - - private func updateDebugText() { - debugText += "\nTTS Active: \(isTTSActive)" - debugText += "\nCurrent Silence Threshold: \(voiceActivityThreshold)" - debugText += "\nOriginal Silence Threshold: \(originalSilenceThreshold)" - debugText += "\n---" - } - - private func startAudioBuffering() { - Task { - while true { - if let samples = whisperKit?.audioProcessor.audioSamples { - let bufferSize = Int(Double(WhisperKit.sampleRate) * bufferDuration) - audioBuffer = Array(samples.suffix(bufferSize)) - } - try await Task.sleep(nanoseconds: 100_000_000) // Update every 0.1 seconds - } - } - } - - private func loadModel(_ model: String) { - Task { - let success = try await loadModel(selectedModel) - if success { - startListening() - } else { - print("Model failed to load, cannot start listening") - } - } - } - - private func startListening() { - guard let audioProcessor = whisperKit?.audioProcessor else { - print("AudioProcessor not available") - return - } - - do { - try audioProcessor.startRecordingLive { buffer in - DispatchQueue.main.async { - checkVoiceActivity() - } - } - isListening = true - } catch { - print("Error starting listening: \(error)") - } - } - - private func checkVoiceActivity() { - guard canRecordAudio, let audioProcessor = whisperKit?.audioProcessor else { return } - - let voiceDetected = AudioProcessor.isVoiceDetected( - in: audioProcessor.relativeEnergy, - nextBufferInSeconds: Float(bufferSeconds), - silenceThreshold: Float(voiceActivityThreshold) - ) - - let energyValuesToConsider = Int(Float(bufferSeconds) / 0.1) - let nextBufferEnergies = audioProcessor.relativeEnergy.suffix(energyValuesToConsider) - let numberOfValuesToCheck = max(10, nextBufferEnergies.count - 10) - let relevantEnergies = Array(nextBufferEnergies.prefix(numberOfValuesToCheck)) - - debugText = """ - Buffer seconds: \(bufferSeconds) - Energy values to consider: \(energyValuesToConsider) - Number of values to check: \(numberOfValuesToCheck) - Silence threshold: \(voiceActivityThreshold) - Relevant energies: \(relevantEnergies) - Max energy: \(relevantEnergies.max() ?? 0) - Voice detected: \(voiceDetected) - """ - - if voiceDetected { - lastVoiceActivityTime = Date() - if !isRecordingMemo { - startNewMemo() - } - } else { - checkSilence() - } - } - - private func checkSilence() { - let silenceDuration = Date().timeIntervalSince(lastVoiceActivityTime) - debugText += "\nSilence duration: \(silenceDuration)" - - if silenceDuration > silenceTimeThreshold { - endCurrentMemo() - } - } - - private func endCurrentMemo() { - if isRecordingMemo { - isRecordingMemo = false - silenceTimer?.invalidate() - silenceTimer = nil - if !currentMemo.isEmpty { - saveMemoToFile(currentMemo) - currentMemo = "" - } - currentText = "" - whisperKit?.audioProcessor.purgeAudioSamples(keepingLast: 0) - print("Ended memo") - debugText += "\nMemo ended" - } - } - - private func startNewMemo() { - isRecordingMemo = true - currentMemo = "" - isInitialTranscription = true - silenceTimer?.invalidate() - silenceTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in - checkSilence() - } - transcribeInRealTime() - print("Started new memo") - } - - private func transcribeInRealTime() { - Task { - while isRecordingMemo { - if canRecordAudio, let samples = whisperKit?.audioProcessor.audioSamples, samples.count > WhisperKit.sampleRate { - do { - let samplesToTranscribe: [Float] - if isInitialTranscription { - samplesToTranscribe = audioBuffer + samples - isInitialTranscription = false - } else { - samplesToTranscribe = Array(samples) - } - - let result = try await whisperKit?.transcribe(audioArray: samplesToTranscribe) - await MainActor.run { - let newText = result?.first?.text ?? "" - if !newText.isEmpty { - currentMemo = newText - currentText = newText - } - } - } catch { - print("Transcription error: \(error)") - } - } - try await Task.sleep(nanoseconds: 500_000_000) // Sleep for 0.5 seconds - } - } - } - - private func saveMemoToFile(_ memo: String) { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy-MM-dd_HH-mm-ss" - let fileName = "memo_\(dateFormatter.string(from: Date())).txt" - - guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { - print("Unable to access documents directory") - return - } - - let fileURL = documentsDirectory.appendingPathComponent(fileName) - - do { - try memo.write(to: fileURL, atomically: true, encoding: .utf8) - print("Memo saved to: \(fileURL.path)") - } catch { - print("Error saving memo: \(error)") - } - - Task { - if !isCameraReady { - print("Camera not ready, initializing...") - await setupCamera() - } - - if let imageBase64 = await capturePhotoBase64() { - sendMemoToAPI(memo, imageBase64: imageBase64) - } else { - sendMemoToAPI(memo) - } - } - } - - private func setupCamera() async { - print("Setting up camera...") - do { - let actor = CameraActor() - print("CameraActor instance created") - try await actor.configure() - print("Camera configured successfully") - await MainActor.run { - self.cameraActor = actor - self.errorMessage = nil - self.isCameraReady = true - print("Camera setup complete, UI updated") - } - } catch { - print("Camera setup failed: \(error)") - await MainActor.run { - self.errorMessage = "Failed to initialize camera: \(error.localizedDescription)" - self.isCameraReady = false - print("Camera setup failure reflected in UI") - } - } - } - - private func capturePhotoBase64() async -> String? { - print("Attempting to capture photo...") - if !isCameraReady { - print("Camera not ready, attempting to initialize...") - await setupCamera() - } - - guard let actor = cameraActor, isCameraReady else { - print("Camera not initialized or not ready, cannot capture photo") - await MainActor.run { - self.errorMessage = "Camera not initialized or not ready" - } - return nil - } - - do { - let base64String = try await actor.capturePhoto() - print("Photo captured successfully") - await MainActor.run { - self.errorMessage = nil - } - return base64String - } catch { - print("Error capturing photo: \(error)") - await MainActor.run { - self.errorMessage = "Failed to capture photo: \(error.localizedDescription)" - } - return nil - } - } - - private func sendMemoToAPI(_ memo: String, imageBase64: String? = nil) { - Task { - guard !isRequestInProgress else { - print("A request is already in progress. Skipping this one.") - return - } - - isRequestInProgress = true - isFirst3WordsOfResponse = true // Reset for new request - defer { isRequestInProgress = false } - - do { - print("Starting API request for memo: \(memo.prefix(50))...") - - guard let url = URL(string: apiEndpoint) else { - print("Invalid API endpoint URL: \(apiEndpoint)") - return - } - - var payload: [String: Any] = [ - "model": "llava-1.5-7b-hf", - "messages": [ - ["role": "user", "content": [ - ["type": "text", "text": "You are a helpful conversational assistant chatting with a Gen Z user using their iPhone for voice transcription and sending images to you with their iPhone camera. Be conversational and concise, with a laid back attitude and be cheerful with humour. User said: " + memo], - ]] - ], - "temperature": 0.7, - "stream": true - ] - - if let imageBase64 = imageBase64 { - if var userMessage = (payload["messages"] as? [[String: Any]])?.last, - var content = userMessage["content"] as? [[String: Any]] { - content.append(["type": "image_url", "image_url": ["url": "data:image/jpeg;base64,\(imageBase64)"]]) - userMessage["content"] = content - payload["messages"] = [userMessage] - } - } - - guard let jsonData = try? JSONSerialization.data(withJSONObject: payload) else { - print("Failed to serialize JSON payload") - return - } - - var request = URLRequest(url: url) - request.httpMethod = "POST" - request.setValue("application/json", forHTTPHeaderField: "Content-Type") - request.httpBody = jsonData - - print("Sending request to \(url.absoluteString)") - - await MainActor.run { - self.streamingResponse = "" - } - - let (bytes, response) = try await URLSession.shared.bytes(for: request) - - guard let httpResponse = response as? HTTPURLResponse else { - print("Invalid response") - return - } - - print("Response status code: \(httpResponse.statusCode)") - - for try await line in bytes.lines { - print("Received line: \(line)") - await processStreamLine(line) - } - - print("Stream completed") - } catch { - print("Error: \(error.localizedDescription)") - } - } - } - - private func processStreamLine(_ line: String) async { - let jsonString: String - if line.hasPrefix("data: ") { - jsonString = String(line.dropFirst(6)) - } else { - jsonString = line - } - - if jsonString.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - return - } - - if let jsonData = jsonString.data(using: .utf8), - let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any], - let choices = json["choices"] as? [[String: Any]], - let firstChoice = choices.first, - let delta = firstChoice["delta"] as? [String: String], - let content = delta["content"] { - print("Extracted content: \(content)") - await MainActor.run { - self.streamingResponse += content - bufferContent(content) - } - } - } - - private func bufferContent(_ content: String) { - speechBuffer += content - let words = speechBuffer.split(separator: " ") - wordCount = words.count - - if isFirst3WordsOfResponse && wordCount >= 3 { - isFirst3WordsOfResponse = false - speakBufferedContent() - } else if content.contains(".") || content.contains("!") || content.contains("?") || wordCount >= maxWords { - speakBufferedContent() - } - } - - private func speakBufferedContent() { - guard !speechBuffer.isEmpty else { return } - speakContent(speechBuffer) - speechBuffer = "" - wordCount = 0 - } - - private func speakContent(_ content: String) { - let utterance = AVSpeechUtterance(string: content) - utterance.voice = AVSpeechSynthesisVoice(language: "en-US") - utterance.rate = 0.5 - speechSynthesizer.speak(utterance) - } - - private func loadModel(_ model: String) async throws -> Bool { - guard let whisperKit = whisperKit else { - print("WhisperKit instance not initialized") - return false - } - modelState = .loading - do { - print("Starting to load model: \(model)") - try await whisperKit.loadModels() - await MainActor.run { - modelState = .loaded - print("Model loaded successfully: \(model)") - } - return true - } catch { - print("Error loading model: \(error)") - await MainActor.run { modelState = .unloaded } - return false - } - } - - private func capturePhoto() async { - print("Attempting to capture photo...") - print("Camera ready: \(isCameraReady), CameraActor exists: \(cameraActor != nil)") - guard let actor = cameraActor, isCameraReady else { - print("Camera not initialized or not ready, cannot capture photo") - await MainActor.run { - self.errorMessage = "Camera not initialized or not ready" - } - return - } - - do { - let base64String = try await actor.capturePhoto() - print("Photo captured successfully") - await MainActor.run { - self.capturedImageBase64 = base64String - self.errorMessage = nil - } - } catch { - print("Error capturing photo: \(error)") - await MainActor.run { - self.errorMessage = "Failed to capture photo: \(error.localizedDescription)" - } - } - } -} diff --git a/examples/astra/astra/Preview Content/Preview Assets.xcassets/Contents.json b/examples/astra/astra/Preview Content/Preview Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/examples/astra/astra/Preview Content/Preview Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/examples/astra/astra/astra.entitlements b/examples/astra/astra/astra.entitlements deleted file mode 100644 index ee23443d..00000000 --- a/examples/astra/astra/astra.entitlements +++ /dev/null @@ -1,22 +0,0 @@ - - - - - com.apple.developer.kernel.increased-memory-limit - - com.apple.security.app-sandbox - - com.apple.security.device.audio-input - - com.apple.security.files.downloads.read-only - - com.apple.security.files.user-selected.read-write - - com.apple.security.network.client - - com.apple.security.network.server - - com.apple.security.device.camera - - - diff --git a/examples/astra/astra/astraApp.swift b/examples/astra/astra/astraApp.swift deleted file mode 100644 index 79cfe707..00000000 --- a/examples/astra/astra/astraApp.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// astraApp.swift -// astra -// -// Created by Alex on 18/08/2024. -// - -import SwiftUI - -@main -struct astraApp: App { - var body: some Scene { - WindowGroup { - ContentView() - } - } -} diff --git a/examples/astra/astraTests/astraTests.swift b/examples/astra/astraTests/astraTests.swift deleted file mode 100644 index 982e0c91..00000000 --- a/examples/astra/astraTests/astraTests.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// astraTests.swift -// astraTests -// -// Created by Alex on 18/08/2024. -// - -import XCTest - -final class astraTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - // Any test you write for XCTest can be annotated as throws and async. - // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. - // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - measure { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/examples/astra/astraUITests/astraUITests.swift b/examples/astra/astraUITests/astraUITests.swift deleted file mode 100644 index 97a293d9..00000000 --- a/examples/astra/astraUITests/astraUITests.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// astraUITests.swift -// astraUITests -// -// Created by Alex on 18/08/2024. -// - -import XCTest - -final class astraUITests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // UI tests must launch the application that they test. - let app = XCUIApplication() - app.launch() - - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testLaunchPerformance() throws { - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { - // This measures how long it takes to launch your application. - measure(metrics: [XCTApplicationLaunchMetric()]) { - XCUIApplication().launch() - } - } - } -} diff --git a/examples/astra/astraUITests/astraUITestsLaunchTests.swift b/examples/astra/astraUITests/astraUITestsLaunchTests.swift deleted file mode 100644 index d05a1484..00000000 --- a/examples/astra/astraUITests/astraUITestsLaunchTests.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// astraUITestsLaunchTests.swift -// astraUITests -// -// Created by Alex on 18/08/2024. -// - -import XCTest - -final class astraUITestsLaunchTests: XCTestCase { - - override class var runsForEachTargetApplicationUIConfiguration: Bool { - true - } - - override func setUpWithError() throws { - continueAfterFailure = false - } - - func testLaunch() throws { - let app = XCUIApplication() - app.launch() - - // Insert steps here to perform after app launch but before taking a screenshot, - // such as logging into a test account or navigating somewhere in the app - - let attachment = XCTAttachment(screenshot: app.screenshot()) - attachment.name = "Launch Screen" - attachment.lifetime = .keepAlways - add(attachment) - } -} diff --git a/examples/chatgpt_api.sh b/examples/chatgpt_api.sh deleted file mode 100755 index 44401836..00000000 --- a/examples/chatgpt_api.sh +++ /dev/null @@ -1,39 +0,0 @@ -# exo provides an API that aims to be a drop-in replacements for the ChatGPT-API. -# This example shows how you can use the API first without streaming and second with streaming. -# This works the same in a single-node set up and in a multi-node setup. -# You need to start exo before running this by running `python3 main.py`. - -API_ENDPOINT="http://${API_ENDPOINT:-$(ifconfig | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | head -n 1):52415}" -MODEL="llama-3.1-8b" -PROMPT="What is the meaning of exo?" -TEMPERATURE=0.7 - -echo "" -echo "" -echo "--- Output without streaming:" -echo "" -curl "${API_ENDPOINT}/v1/chat/completions" --silent \ - -H "Content-Type: application/json" \ - -d '{ - "model": "'"${MODEL}"'", - "messages": [{"role": "user", "content": "'"${PROMPT}"'"}], - "temperature": '"${TEMPERATURE}"' - }' - -echo "" -echo "" -echo "--- Output with streaming:" -echo "" -curl "${API_ENDPOINT}/v1/chat/completions" --silent \ - -H "Content-Type: application/json" \ - -d '{ - "model": "'"${MODEL}"'", - "messages": [{"role": "user", "content": "'"${PROMPT}"'"}], - "temperature": '"${TEMPERATURE}"', - "stream": true - }' | while read -r line; do - if [[ $line == data:* ]]; then - content=$(echo "$line" | sed 's/^data: //') - echo "$content" | jq -r '.choices[].delta.content' --unbuffered | tr -d '\n' - fi - done \ No newline at end of file diff --git a/examples/function_calling.py b/examples/function_calling.py deleted file mode 100644 index dcab2a8d..00000000 --- a/examples/function_calling.py +++ /dev/null @@ -1,111 +0,0 @@ -import json -import re -import requests - -def get_current_weather(location: str, unit: str = "celsius"): - """Mock weather data function""" - # Hardcoded response for demo purposes - return { - "location": location, - "temperature": 22 if unit == "celsius" else 72, - "unit": unit, - "forecast": "Sunny with light clouds" - } - -def try_parse_tool_calls(content: str): - """Try parse the tool calls.""" - tool_calls = [] - offset = 0 - for i, m in enumerate(re.finditer(r"\n(.+)?\n", content)): - if i == 0: - offset = m.start() - try: - func = json.loads(m.group(1)) - tool_calls.append({"type": "function", "function": func}) - if isinstance(func["arguments"], str): - func["arguments"] = json.loads(func["arguments"]) - except json.JSONDecodeError as e: - print(f"Failed to parse tool calls: the content is {m.group(1)} and {e}") - pass - if tool_calls: - if offset > 0 and content[:offset].strip(): - c = content[:offset] - else: - c = "" - return {"role": "assistant", "content": c, "tool_calls": tool_calls} - return {"role": "assistant", "content": re.sub(r"<\|im_end\|>$", "", content)} - -def chat_completion(messages): - """Send chat completion request to local server""" - response = requests.post( - "http://localhost:52415/v1/chat/completions", - json={ - "model": "qwen-2.5-1.5b", - "messages": messages, - "tools": [{ - "type": "function", - "function": { - "name": "get_current_weather", - "description": "Get the current weather in a given location", - "parameters": { - "type": "object", - "properties": { - "location": { - "type": "string", - "description": "The city and state, e.g. San Francisco, CA" - }, - "unit": { - "type": "string", - "enum": ["celsius", "fahrenheit"] - } - }, - "required": ["location"] - } - } - }], - "tool_choice": "auto" - } - ) - return response.json() - -def main(): - # Initial conversation - messages = [{ - "role": "user", - "content": "Hi there, what's the weather in Boston?" - }] - - # Get initial response - response = chat_completion(messages) - print(f"First response: {response}") - assistant_message = try_parse_tool_calls(response["choices"][0]["message"]["content"]) - messages.append(assistant_message) - - # If there are tool calls, execute them and continue conversation - if "tool_calls" in assistant_message: - for tool_call in assistant_message["tool_calls"]: - if tool_call["function"]["name"] == "get_current_weather": - args = tool_call["function"]["arguments"] - weather_data = get_current_weather(**args) - - # Add tool response to messages - messages.append({ - "role": "tool", - "content": json.dumps(weather_data), - "name": tool_call["function"]["name"] - }) - - # Get final response with weather data - response = chat_completion(messages) - print(f"Final response: {response}") - messages.append({ - "role": "assistant", - "content": response["choices"][0]["message"]["content"] - }) - - # Print full conversation - for msg in messages: - print(f"\n{msg['role'].upper()}: {msg['content']}") - -if __name__ == "__main__": - main() diff --git a/exo/__init__.py b/exo/__init__.py deleted file mode 100644 index 596559f3..00000000 --- a/exo/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from exo.helpers import DEBUG as DEBUG, DEBUG_DISCOVERY as DEBUG_DISCOVERY, VERSION as VERSION \ No newline at end of file diff --git a/exo/api/__init__.py b/exo/api/__init__.py deleted file mode 100644 index 660e7507..00000000 --- a/exo/api/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from exo.api.chatgpt_api import ChatGPTAPI as ChatGPTAPI diff --git a/exo/api/chatgpt_api.py b/exo/api/chatgpt_api.py deleted file mode 100644 index 1020fdbc..00000000 --- a/exo/api/chatgpt_api.py +++ /dev/null @@ -1,645 +0,0 @@ -import uuid -import time -import asyncio -import json -import os -from pathlib import Path -from transformers import AutoTokenizer -from typing import List, Literal, Union, Dict, Optional -from aiohttp import web -import aiohttp_cors -import traceback -import signal -from exo import DEBUG, VERSION -from exo.helpers import PrefixDict, shutdown, get_exo_images_dir -from exo.inference.tokenizers import resolve_tokenizer -from exo.orchestration import Node -from exo.models import build_base_shard, build_full_shard, model_cards, get_repo, get_supported_models, get_pretty_name -from typing import Callable, Optional -from PIL import Image -import numpy as np -import base64 -from io import BytesIO -import platform -from exo.download.download_progress import RepoProgressEvent -from exo.download.new_shard_download import delete_model -import tempfile -from exo.apputil import create_animation_mp4 -from collections import defaultdict - -if platform.system().lower() == "darwin" and platform.machine().lower() == "arm64": - import mlx.core as mx -else: - import numpy as mx - - -class Message: - def __init__(self, role: str, content: Union[str, List[Dict[str, Union[str, Dict[str, str]]]]], tools: Optional[List[Dict]] = None): - self.role = role - self.content = content - self.tools = tools - - def to_dict(self): - data = {"role": self.role, "content": self.content} - if self.tools: - data["tools"] = self.tools - return data - - -class ChatCompletionRequest: - def __init__(self, model: str, messages: List[Message], temperature: float, tools: Optional[List[Dict]] = None): - self.model = model - self.messages = messages - self.temperature = temperature - self.tools = tools - - def to_dict(self): - return {"model": self.model, "messages": [message.to_dict() for message in self.messages], "temperature": self.temperature, "tools": self.tools} - - -def generate_completion( - chat_request: ChatCompletionRequest, - tokenizer, - prompt: str, - request_id: str, - tokens: List[int], - stream: bool, - finish_reason: Union[Literal["length", "stop"], None], - object_type: Literal["chat.completion", "text_completion"], -) -> dict: - completion = { - "id": f"chatcmpl-{request_id}", - "object": object_type, - "created": int(time.time()), - "model": chat_request.model, - "system_fingerprint": f"exo_{VERSION}", - "choices": [{ - "index": 0, - "message": {"role": "assistant", "content": tokenizer.decode(tokens)}, - "logprobs": None, - "finish_reason": finish_reason, - }], - } - - if not stream: - completion["usage"] = { - "prompt_tokens": len(tokenizer.encode(prompt)), - "completion_tokens": len(tokens), - "total_tokens": len(tokenizer.encode(prompt)) + len(tokens), - } - - choice = completion["choices"][0] - if object_type.startswith("chat.completion"): - key_name = "delta" if stream else "message" - choice[key_name] = {"role": "assistant", "content": tokenizer.decode(tokens)} - elif object_type == "text_completion": - choice["text"] = tokenizer.decode(tokens) - else: - ValueError(f"Unsupported response type: {object_type}") - - return completion - - -def remap_messages(messages: List[Message]) -> List[Message]: - remapped_messages = [] - last_image = None - for message in messages: - if not isinstance(message.content, list): - remapped_messages.append(message) - continue - - remapped_content = [] - for content in message.content: - if isinstance(content, dict): - if content.get("type") in ["image_url", "image"]: - image_url = content.get("image_url", {}).get("url") or content.get("image") - if image_url: - last_image = {"type": "image", "image": image_url} - remapped_content.append({"type": "text", "text": "[An image was uploaded but is not displayed here]"}) - else: - remapped_content.append(content) - else: - remapped_content.append(content) - remapped_messages.append(Message(role=message.role, content=remapped_content)) - - if last_image: - # Replace the last image placeholder with the actual image content - for message in reversed(remapped_messages): - for i, content in enumerate(message.content): - if isinstance(content, dict): - if content.get("type") == "text" and content.get("text") == "[An image was uploaded but is not displayed here]": - message.content[i] = last_image - return remapped_messages - - return remapped_messages - - -def build_prompt(tokenizer, _messages: List[Message], tools: Optional[List[Dict]] = None): - messages = remap_messages(_messages) - chat_template_args = {"conversation": [m.to_dict() for m in messages], "tokenize": False, "add_generation_prompt": True} - if tools: - chat_template_args["tools"] = tools - - try: - prompt = tokenizer.apply_chat_template(**chat_template_args) - if DEBUG >= 3: print(f"!!! Prompt: {prompt}") - return prompt - except UnicodeEncodeError: - # Handle Unicode encoding by ensuring everything is UTF-8 - chat_template_args["conversation"] = [ - {k: v.encode('utf-8').decode('utf-8') if isinstance(v, str) else v - for k, v in m.to_dict().items()} - for m in messages - ] - prompt = tokenizer.apply_chat_template(**chat_template_args) - if DEBUG >= 3: print(f"!!! Prompt (UTF-8 encoded): {prompt}") - return prompt - - -def parse_message(data: dict): - if "role" not in data or "content" not in data: - raise ValueError(f"Invalid message: {data}. Must have 'role' and 'content'") - return Message(data["role"], data["content"], data.get("tools")) - - -def parse_chat_request(data: dict, default_model: str): - return ChatCompletionRequest( - data.get("model", default_model), - [parse_message(msg) for msg in data["messages"]], - data.get("temperature", 0.0), - data.get("tools", None), - ) - - -class PromptSession: - def __init__(self, request_id: str, timestamp: int, prompt: str): - self.request_id = request_id - self.timestamp = timestamp - self.prompt = prompt - - -class ChatGPTAPI: - def __init__( - self, - node: Node, - inference_engine_classname: str, - response_timeout: int = 90, - on_chat_completion_request: Callable[[str, ChatCompletionRequest, str], None] = None, - default_model: Optional[str] = None, - system_prompt: Optional[str] = None - ): - self.node = node - self.inference_engine_classname = inference_engine_classname - self.response_timeout = response_timeout - self.on_chat_completion_request = on_chat_completion_request - self.app = web.Application(client_max_size=100*1024*1024) # 100MB to support image upload - self.prompts: PrefixDict[str, PromptSession] = PrefixDict() - self.prev_token_lens: Dict[str, int] = {} - self.stream_tasks: Dict[str, asyncio.Task] = {} - self.default_model = default_model or "llama-3.2-1b" - self.token_queues = defaultdict(asyncio.Queue) - - # Get the callback system and register our handler - self.token_callback = node.on_token.register("chatgpt-api-token-handler") - self.token_callback.on_next(lambda _request_id, tokens, is_finished: asyncio.create_task(self.handle_tokens(_request_id, tokens, is_finished))) - self.system_prompt = system_prompt - - cors = aiohttp_cors.setup(self.app) - cors_options = aiohttp_cors.ResourceOptions( - allow_credentials=True, - expose_headers="*", - allow_headers="*", - allow_methods="*", - ) - cors.add(self.app.router.add_get("/models", self.handle_get_models), {"*": cors_options}) - cors.add(self.app.router.add_get("/v1/models", self.handle_get_models), {"*": cors_options}) - cors.add(self.app.router.add_post("/chat/token/encode", self.handle_post_chat_token_encode), {"*": cors_options}) - cors.add(self.app.router.add_post("/v1/chat/token/encode", self.handle_post_chat_token_encode), {"*": cors_options}) - cors.add(self.app.router.add_post("/chat/completions", self.handle_post_chat_completions), {"*": cors_options}) - cors.add(self.app.router.add_post("/v1/chat/completions", self.handle_post_chat_completions), {"*": cors_options}) - cors.add(self.app.router.add_post("/v1/image/generations", self.handle_post_image_generations), {"*": cors_options}) - cors.add(self.app.router.add_get("/v1/download/progress", self.handle_get_download_progress), {"*": cors_options}) - cors.add(self.app.router.add_get("/modelpool", self.handle_model_support), {"*": cors_options}) - cors.add(self.app.router.add_get("/healthcheck", self.handle_healthcheck), {"*": cors_options}) - cors.add(self.app.router.add_post("/quit", self.handle_quit), {"*": cors_options}) - cors.add(self.app.router.add_delete("/models/{model_name}", self.handle_delete_model), {"*": cors_options}) - cors.add(self.app.router.add_get("/initial_models", self.handle_get_initial_models), {"*": cors_options}) - cors.add(self.app.router.add_post("/create_animation", self.handle_create_animation), {"*": cors_options}) - cors.add(self.app.router.add_post("/download", self.handle_post_download), {"*": cors_options}) - cors.add(self.app.router.add_get("/v1/topology", self.handle_get_topology), {"*": cors_options}) - cors.add(self.app.router.add_get("/topology", self.handle_get_topology), {"*": cors_options}) - - # Add static routes - if "__compiled__" not in globals(): - self.static_dir = Path(__file__).parent.parent/"tinychat" - self.app.router.add_get("/", self.handle_root) - self.app.router.add_static("/", self.static_dir, name="static") - - # Always add images route, regardless of compilation status - self.images_dir = get_exo_images_dir() - self.images_dir.mkdir(parents=True, exist_ok=True) - self.app.router.add_static('/images/', self.images_dir, name='static_images') - - self.app.middlewares.append(self.timeout_middleware) - self.app.middlewares.append(self.log_request) - - async def handle_quit(self, request): - if DEBUG >= 1: print("Received quit signal") - response = web.json_response({"detail": "Quit signal received"}, status=200) - await response.prepare(request) - await response.write_eof() - await shutdown(signal.SIGINT, asyncio.get_event_loop(), self.node.server) - - async def timeout_middleware(self, app, handler): - async def middleware(request): - try: - return await asyncio.wait_for(handler(request), timeout=self.response_timeout) - except asyncio.TimeoutError: - return web.json_response({"detail": "Request timed out"}, status=408) - - return middleware - - async def log_request(self, app, handler): - async def middleware(request): - if DEBUG >= 2: print(f"Received request: {request.method} {request.path}") - return await handler(request) - - return middleware - - async def handle_root(self, request): - return web.FileResponse(self.static_dir/"index.html") - - async def handle_healthcheck(self, request): - return web.json_response({"status": "ok"}) - - async def handle_model_support(self, request): - try: - response = web.StreamResponse(status=200, reason='OK', headers={ 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }) - await response.prepare(request) - async for path, s in self.node.shard_downloader.get_shard_download_status(self.inference_engine_classname): - model_data = { s.shard.model_id: { "downloaded": s.downloaded_bytes == s.total_bytes, "download_percentage": 100 if s.downloaded_bytes == s.total_bytes else 100 * float(s.downloaded_bytes) / float(s.total_bytes), "total_size": s.total_bytes, "total_downloaded": s.downloaded_bytes } } - await response.write(f"data: {json.dumps(model_data)}\n\n".encode()) - await response.write(b"data: [DONE]\n\n") - return response - - except Exception as e: - print(f"Error in handle_model_support: {str(e)}") - traceback.print_exc() - return web.json_response({"detail": f"Server error: {str(e)}"}, status=500) - - async def handle_get_models(self, request): - models_list = [{"id": model_name, "object": "model", "owned_by": "exo", "ready": True} for model_name, _ in model_cards.items()] - return web.json_response({"object": "list", "data": models_list}) - - async def handle_post_chat_token_encode(self, request): - data = await request.json() - model = data.get("model", self.default_model) - if model and model.startswith("gpt-"): # Handle gpt- model requests - model = self.default_model - if not model or model not in model_cards: - if DEBUG >= 1: print(f"Invalid model: {model}. Supported: {list(model_cards.keys())}. Defaulting to {self.default_model}") - model = self.default_model - shard = build_base_shard(model, self.inference_engine_classname) - messages = [parse_message(msg) for msg in data.get("messages", [])] - tokenizer = await resolve_tokenizer(get_repo(shard.model_id, self.inference_engine_classname)) - prompt = build_prompt(tokenizer, messages, data.get("tools", None)) - tokens = tokenizer.encode(prompt) - return web.json_response({ - "length": len(prompt), - "num_tokens": len(tokens), - "encoded_tokens": tokens, - "encoded_prompt": prompt, - }) - - async def handle_get_download_progress(self, request): - progress_data = {} - for node_id, progress_event in self.node.node_download_progress.items(): - if isinstance(progress_event, RepoProgressEvent): - if progress_event.status != "in_progress": continue - progress_data[node_id] = progress_event.to_dict() - else: - print(f"Unknown progress event type: {type(progress_event)}. {progress_event}") - return web.json_response(progress_data) - - async def handle_post_chat_completions(self, request): - data = await request.json() - if DEBUG >= 2: print(f"[ChatGPTAPI] Handling chat completions request from {request.remote}: {data}") - stream = data.get("stream", False) - chat_request = parse_chat_request(data, self.default_model) - if chat_request.model and chat_request.model.startswith("gpt-"): # to be compatible with ChatGPT tools, point all gpt- model requests to default model - chat_request.model = self.default_model - if not chat_request.model or chat_request.model not in model_cards: - if DEBUG >= 1: print(f"[ChatGPTAPI] Invalid model: {chat_request.model}. Supported: {list(model_cards.keys())}. Defaulting to {self.default_model}") - chat_request.model = self.default_model - shard = build_base_shard(chat_request.model, self.inference_engine_classname) - if not shard: - supported_models = [model for model, info in model_cards.items() if self.inference_engine_classname in info.get("repo", {})] - return web.json_response( - {"detail": f"Unsupported model: {chat_request.model} with inference engine {self.inference_engine_classname}. Supported models for this engine: {supported_models}"}, - status=400, - ) - - tokenizer = await resolve_tokenizer(get_repo(shard.model_id, self.inference_engine_classname)) - if DEBUG >= 4: print(f"[ChatGPTAPI] Resolved tokenizer: {tokenizer}") - - # Add system prompt if set - if self.system_prompt and not any(msg.role == "system" for msg in chat_request.messages): - chat_request.messages.insert(0, Message("system", self.system_prompt)) - - prompt = build_prompt(tokenizer, chat_request.messages, chat_request.tools) - request_id = str(uuid.uuid4()) - if self.on_chat_completion_request: - try: - self.on_chat_completion_request(request_id, chat_request, prompt) - except Exception as e: - if DEBUG >= 2: traceback.print_exc() - - if DEBUG >= 2: print(f"[ChatGPTAPI] Processing prompt: {request_id=} {shard=} {prompt=}") - - try: - await asyncio.wait_for(asyncio.shield(asyncio.create_task(self.node.process_prompt(shard, prompt, request_id=request_id))), timeout=self.response_timeout) - - if DEBUG >= 2: print(f"[ChatGPTAPI] Waiting for response to finish. timeout={self.response_timeout}s") - - if stream: - response = web.StreamResponse( - status=200, - reason="OK", - headers={ - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - }, - ) - await response.prepare(request) - - try: - # Stream tokens while waiting for inference to complete - while True: - if DEBUG >= 2: print(f"[ChatGPTAPI] Waiting for token from queue: {request_id=}") - tokens, is_finished = await asyncio.wait_for( - self.token_queues[request_id].get(), - timeout=self.response_timeout - ) - if DEBUG >= 2: print(f"[ChatGPTAPI] Got token from queue: {request_id=} {tokens=} {is_finished=}") - - eos_token_id = None - if not eos_token_id and hasattr(tokenizer, "eos_token_id"): eos_token_id = tokenizer.eos_token_id - if not eos_token_id and hasattr(tokenizer, "_tokenizer"): eos_token_id = tokenizer.special_tokens_map.get("eos_token_id") - - finish_reason = None - if is_finished: finish_reason = "stop" if tokens[-1] == eos_token_id else "length" - if DEBUG >= 2: print(f"{eos_token_id=} {tokens[-1]=} {finish_reason=}") - - completion = generate_completion( - chat_request, - tokenizer, - prompt, - request_id, - tokens, - stream, - finish_reason, - "chat.completion", - ) - - await response.write(f"data: {json.dumps(completion)}\n\n".encode()) - - if is_finished: - break - - await response.write_eof() - return response - - except asyncio.TimeoutError: - if DEBUG >= 2: print(f"[ChatGPTAPI] Timeout waiting for token: {request_id=}") - return web.json_response({"detail": "Response generation timed out"}, status=408) - - except Exception as e: - if DEBUG >= 2: - print(f"[ChatGPTAPI] Error processing prompt: {e}") - traceback.print_exc() - return web.json_response( - {"detail": f"Error processing prompt: {str(e)}"}, - status=500 - ) - - finally: - # Clean up the queue for this request - if request_id in self.token_queues: - if DEBUG >= 2: print(f"[ChatGPTAPI] Cleaning up token queue: {request_id=}") - del self.token_queues[request_id] - else: - tokens = [] - while True: - _tokens, is_finished = await asyncio.wait_for(self.token_queues[request_id].get(), timeout=self.response_timeout) - tokens.extend(_tokens) - if is_finished: - break - finish_reason = "length" - eos_token_id = None - if not eos_token_id and hasattr(tokenizer, "eos_token_id"): eos_token_id = tokenizer.eos_token_id - if not eos_token_id and hasattr(tokenizer, "_tokenizer"): eos_token_id = tokenizer.special_tokens_map.get("eos_token_id") - if DEBUG >= 2: print(f"Checking if end of tokens result {tokens[-1]=} is {eos_token_id=}") - if tokens[-1] == eos_token_id: - finish_reason = "stop" - - return web.json_response(generate_completion(chat_request, tokenizer, prompt, request_id, tokens, stream, finish_reason, "chat.completion")) - except asyncio.TimeoutError: - return web.json_response({"detail": "Response generation timed out"}, status=408) - except Exception as e: - if DEBUG >= 2: traceback.print_exc() - return web.json_response({"detail": f"Error processing prompt (see logs with DEBUG>=2): {str(e)}"}, status=500) - - async def handle_post_image_generations(self, request): - data = await request.json() - - if DEBUG >= 2: print(f"Handling chat completions request from {request.remote}: {data}") - stream = data.get("stream", False) - model = data.get("model", "") - prompt = data.get("prompt", "") - image_url = data.get("image_url", "") - if DEBUG >= 2: print(f"model: {model}, prompt: {prompt}, stream: {stream}") - shard = build_base_shard(model, self.inference_engine_classname) - if DEBUG >= 2: print(f"shard: {shard}") - if not shard: - return web.json_response({"error": f"Unsupported model: {model} with inference engine {self.inference_engine_classname}"}, status=400) - - request_id = str(uuid.uuid4()) - callback_id = f"chatgpt-api-wait-response-{request_id}" - callback = self.node.on_token.register(callback_id) - try: - if image_url != "" and image_url != None: - img = self.base64_decode(image_url) - else: - img = None - await asyncio.wait_for(asyncio.shield(asyncio.create_task(self.node.process_prompt(shard, prompt, request_id=request_id, inference_state={"image": img}))), timeout=self.response_timeout) - - response = web.StreamResponse(status=200, reason='OK', headers={ - 'Content-Type': 'application/octet-stream', - "Cache-Control": "no-cache", - }) - await response.prepare(request) - - def get_progress_bar(current_step, total_steps, bar_length=50): - # Calculate the percentage of completion - percent = float(current_step)/total_steps - # Calculate the number of hashes to display - arrow = '-'*int(round(percent*bar_length) - 1) + '>' - spaces = ' '*(bar_length - len(arrow)) - - # Create the progress bar string - progress_bar = f'Progress: [{arrow}{spaces}] {int(percent * 100)}% ({current_step}/{total_steps})' - return progress_bar - - async def stream_image(_request_id: str, result, is_finished: bool): - if isinstance(result, list): - await response.write(json.dumps({'progress': get_progress_bar((result[0]), (result[1]))}).encode('utf-8') + b'\n') - - elif isinstance(result, np.ndarray): - try: - im = Image.fromarray(np.array(result)) - # Save the image to a file - image_filename = f"{_request_id}.png" - image_path = self.images_dir/image_filename - im.save(image_path) - - # Get URL for the saved image - try: - image_url = request.app.router['static_images'].url_for(filename=image_filename) - base_url = f"{request.scheme}://{request.host}" - full_image_url = base_url + str(image_url) - - await response.write(json.dumps({'images': [{'url': str(full_image_url), 'content_type': 'image/png'}]}).encode('utf-8') + b'\n') - except KeyError as e: - if DEBUG >= 2: print(f"Error getting image URL: {e}") - # Fallback to direct file path if URL generation fails - await response.write(json.dumps({'images': [{'url': str(image_path), 'content_type': 'image/png'}]}).encode('utf-8') + b'\n') - - if is_finished: - await response.write_eof() - - except Exception as e: - if DEBUG >= 2: print(f"Error processing image: {e}") - if DEBUG >= 2: traceback.print_exc() - await response.write(json.dumps({'error': str(e)}).encode('utf-8') + b'\n') - - stream_task = None - - def on_result(_request_id: str, result, is_finished: bool): - nonlocal stream_task - stream_task = asyncio.create_task(stream_image(_request_id, result, is_finished)) - return _request_id == request_id and is_finished - - await callback.wait(on_result, timeout=self.response_timeout*10) - - if stream_task: - # Wait for the stream task to complete before returning - await stream_task - - return response - - except Exception as e: - if DEBUG >= 2: traceback.print_exc() - return web.json_response({"detail": f"Error processing prompt (see logs with DEBUG>=2): {str(e)}"}, status=500) - - async def handle_delete_model(self, request): - model_id = request.match_info.get('model_name') - try: - if await delete_model(model_id, self.inference_engine_classname): return web.json_response({"status": "success", "message": f"Model {model_id} deleted successfully"}) - else: return web.json_response({"detail": f"Model {model_id} files not found"}, status=404) - except Exception as e: - if DEBUG >= 2: traceback.print_exc() - return web.json_response({"detail": f"Error deleting model: {str(e)}"}, status=500) - - async def handle_get_initial_models(self, request): - model_data = {} - for model_id in get_supported_models([[self.inference_engine_classname]]): - model_data[model_id] = { - "name": get_pretty_name(model_id), - "downloaded": None, # Initially unknown - "download_percentage": None, # Change from 0 to null - "total_size": None, - "total_downloaded": None, - "loading": True # Add loading state - } - return web.json_response(model_data) - - async def handle_create_animation(self, request): - try: - data = await request.json() - replacement_image_path = data.get("replacement_image_path") - device_name = data.get("device_name", "Local Device") - prompt_text = data.get("prompt", "") - - if DEBUG >= 2: print(f"Creating animation with params: replacement_image={replacement_image_path}, device={device_name}, prompt={prompt_text}") - - if not replacement_image_path: - return web.json_response({"error": "replacement_image_path is required"}, status=400) - - # Create temp directory if it doesn't exist - tmp_dir = Path(tempfile.gettempdir())/"exo_animations" - tmp_dir.mkdir(parents=True, exist_ok=True) - - # Generate unique output filename in temp directory - output_filename = f"animation_{uuid.uuid4()}.mp4" - output_path = str(tmp_dir/output_filename) - - if DEBUG >= 2: print(f"Animation temp directory: {tmp_dir}, output file: {output_path}, directory exists: {tmp_dir.exists()}, directory permissions: {oct(tmp_dir.stat().st_mode)[-3:]}") - - # Create the animation - create_animation_mp4(replacement_image_path, output_path, device_name, prompt_text) - - return web.json_response({"status": "success", "output_path": output_path}) - - except Exception as e: - if DEBUG >= 2: traceback.print_exc() - return web.json_response({"error": str(e)}, status=500) - - async def handle_post_download(self, request): - try: - data = await request.json() - model_name = data.get("model") - if not model_name: return web.json_response({"error": "model parameter is required"}, status=400) - if model_name not in model_cards: return web.json_response({"error": f"Invalid model: {model_name}. Supported models: {list(model_cards.keys())}"}, status=400) - shard = build_full_shard(model_name, self.inference_engine_classname) - if not shard: return web.json_response({"error": f"Could not build shard for model {model_name}"}, status=400) - asyncio.create_task(self.node.inference_engine.shard_downloader.ensure_shard(shard, self.inference_engine_classname)) - - return web.json_response({"status": "success", "message": f"Download started for model: {model_name}"}) - except Exception as e: - if DEBUG >= 2: traceback.print_exc() - return web.json_response({"error": str(e)}, status=500) - - async def handle_get_topology(self, request): - try: - topology = self.node.current_topology - if topology: - return web.json_response(topology.to_json()) - else: - return web.json_response({}) - except Exception as e: - if DEBUG >= 2: traceback.print_exc() - return web.json_response({"detail": f"Error getting topology: {str(e)}"}, status=500) - - async def handle_tokens(self, request_id: str, tokens: List[int], is_finished: bool): - await self.token_queues[request_id].put((tokens, is_finished)) - - async def run(self, host: str = "0.0.0.0", port: int = 52415): - runner = web.AppRunner(self.app) - await runner.setup() - site = web.TCPSite(runner, host, port) - await site.start() - - def base64_decode(self, base64_string): - #decode and reshape image - if base64_string.startswith('data:image'): - base64_string = base64_string.split(',')[1] - image_data = base64.b64decode(base64_string) - img = Image.open(BytesIO(image_data)) - W, H = (dim - dim%64 for dim in (img.width, img.height)) - if W != img.width or H != img.height: - if DEBUG >= 2: print(f"Warning: image shape is not divisible by 64, downsampling to {W}x{H}") - img = img.resize((W, H), Image.NEAREST) # use desired downsampling filter - img = mx.array(np.array(img)) - img = (img[:, :, :3].astype(mx.float32)/255)*2 - 1 - img = img[None] - return img diff --git a/exo/apputil/__init__.py b/exo/apputil/__init__.py deleted file mode 100644 index b6ac6e25..00000000 --- a/exo/apputil/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from exo.apputil.anim import create_animation_mp4 \ No newline at end of file diff --git a/exo/apputil/anim.py b/exo/apputil/anim.py deleted file mode 100644 index b64aace3..00000000 --- a/exo/apputil/anim.py +++ /dev/null @@ -1,168 +0,0 @@ -from PIL import Image, ImageDraw, ImageFont, ImageFilter -import os -import numpy as np -import cv2 -import sys - -def draw_rounded_rectangle(draw, coords, radius, fill): - left, top, right, bottom = coords - diameter = radius * 2 - draw.rectangle([left + radius, top, right - radius, bottom], fill=fill) - draw.rectangle([left, top + radius, right, bottom - radius], fill=fill) - draw.pieslice([left, top, left + diameter, top + diameter], 180, 270, fill=fill) - draw.pieslice([right - diameter, top, right, top + diameter], 270, 360, fill=fill) - draw.pieslice([left, bottom - diameter, left + diameter, bottom], 90, 180, fill=fill) - draw.pieslice([right - diameter, bottom - diameter, right, bottom], 0, 90, fill=fill) - -def draw_centered_text_rounded(draw, text, font, rect_coords, radius=10, text_color="yellow", bg_color=(43,33,44)): - bbox = font.getbbox(text) - text_width = bbox[2] - bbox[0] - text_height = bbox[3] - bbox[1] - rect_left, rect_top, rect_right, rect_bottom = rect_coords - rect_width = rect_right - rect_left - rect_height = rect_bottom - rect_top - text_x = rect_left + (rect_width - text_width) // 2 - text_y = rect_top + (rect_height - text_height) // 2 - draw_rounded_rectangle(draw, rect_coords, radius, bg_color) - draw.text((text_x, text_y), text, fill=text_color, font=font) - -def draw_left_aligned_text_rounded(draw, text, font, rect_coords, padding_left=20, radius=10, text_color="yellow", bg_color=(43,33,44)): - bbox = font.getbbox(text) - text_height = bbox[3] - bbox[1] - rect_left, rect_top, rect_right, rect_bottom = rect_coords - rect_height = rect_bottom - rect_top - text_y = rect_top + (rect_height - text_height) // 2 - text_x = rect_left + padding_left - draw_rounded_rectangle(draw, rect_coords, radius, bg_color) - draw.text((text_x, text_y), text, fill=text_color, font=font) - -def draw_right_text_dynamic_width_rounded(draw, text, font, base_coords, padding=20, radius=10, text_color="yellow", bg_color=(43,33,44)): - bbox = font.getbbox(text) - text_width = bbox[2] - bbox[0] - text_height = bbox[3] - bbox[1] - _, rect_top, rect_right, rect_bottom = base_coords - rect_height = rect_bottom - rect_top - new_rect_left = rect_right - (text_width + (padding * 2)) - text_y = rect_top + (rect_height - text_height) // 2 - text_x = new_rect_left + padding - draw_rounded_rectangle(draw, (new_rect_left, rect_top, rect_right, rect_bottom), radius, bg_color) - draw.text((text_x, text_y), text, fill=text_color, font=font) - return new_rect_left - -def draw_progress_bar(draw, progress, coords, color="yellow", bg_color=(70, 70, 70)): - left, top, right, bottom = coords - total_width = right - left - draw.rectangle(coords, fill=bg_color) - progress_width = int(total_width * progress) - if progress_width > 0: - draw.rectangle((left, top, left + progress_width, bottom), fill=color) - -def crop_image(image, top_crop=70): - width, height = image.size - return image.crop((0, top_crop, width, height)) - -def create_animation_mp4( - replacement_image_path, - output_path, - device_name, - prompt_text, - fps=30, - target_size=(512, 512), - target_position=(139, 755), - progress_coords=(139, 1285, 655, 1295), - device_coords=(1240, 370, 1640, 416), - prompt_coords=(332, 1702, 2662, 1745) -): - frames = [] - try: - font = ImageFont.truetype("/System/Library/Fonts/SFNSMono.ttf", 20) - promptfont = ImageFont.truetype("/System/Library/Fonts/SFNSMono.ttf", 24) - except: - font = ImageFont.load_default() - promptfont = ImageFont.load_default() - - # Get the base directory for images when running as a bundled app - if hasattr(sys, '_MEIPASS'): - base_dir = os.path.join(sys._MEIPASS, "exo", "apputil", "baseimages") - else: - base_dir = os.path.join(os.path.dirname(__file__), "baseimages") - - # Process first frame - base_img = Image.open(os.path.join(base_dir, "image1.png")) - draw = ImageDraw.Draw(base_img) - draw_centered_text_rounded(draw, device_name, font, device_coords) - frames.extend([crop_image(base_img)] * 30) # 1 second at 30fps - - # Process second frame with typing animation - base_img2 = Image.open(os.path.join(base_dir, "image2.png")) - for i in range(len(prompt_text) + 1): - current_frame = base_img2.copy() - draw = ImageDraw.Draw(current_frame) - draw_centered_text_rounded(draw, device_name, font, device_coords) - if i > 0: # Only draw if we have at least one character - draw_left_aligned_text_rounded(draw, prompt_text[:i], promptfont, prompt_coords) - frames.extend([crop_image(current_frame)] * 2) # 2 frames per character for smooth typing - - # Hold the complete prompt for a moment - frames.extend([frames[-1]] * 30) # Hold for 1 second - - # Create blur sequence - replacement_img = Image.open(replacement_image_path) - base_img = Image.open(os.path.join(base_dir, "image3.png")) - blur_steps = [int(80 * (1 - i/8)) for i in range(9)] - - for i, blur_amount in enumerate(blur_steps): - new_frame = base_img.copy() - draw = ImageDraw.Draw(new_frame) - - replacement_copy = replacement_img.copy() - replacement_copy.thumbnail(target_size, Image.Resampling.LANCZOS) - if blur_amount > 0: - replacement_copy = replacement_copy.filter(ImageFilter.GaussianBlur(radius=blur_amount)) - - mask = replacement_copy.split()[-1] if replacement_copy.mode in ('RGBA', 'LA') else None - new_frame.paste(replacement_copy, target_position, mask) - - draw_progress_bar(draw, (i + 1) / 9, progress_coords) - draw_centered_text_rounded(draw, device_name, font, device_coords) - draw_right_text_dynamic_width_rounded(draw, prompt_text, promptfont, (None, 590, 2850, 685), padding=30) - - frames.extend([crop_image(new_frame)] * 15) # 0.5 seconds at 30fps - - # Create and add final frame (image4) - final_base = Image.open(os.path.join(base_dir, "image4.png")) - draw = ImageDraw.Draw(final_base) - - draw_centered_text_rounded(draw, device_name, font, device_coords) - draw_right_text_dynamic_width_rounded(draw, prompt_text, promptfont, (None, 590, 2850, 685), padding=30) - - replacement_copy = replacement_img.copy() - replacement_copy.thumbnail(target_size, Image.Resampling.LANCZOS) - mask = replacement_copy.split()[-1] if replacement_copy.mode in ('RGBA', 'LA') else None - final_base.paste(replacement_copy, target_position, mask) - - frames.extend([crop_image(final_base)] * 30) # 1 second at 30fps - - # Convert frames to video using H.264 codec - if frames: - first_frame = np.array(frames[0]) - height, width = first_frame.shape[:2] - fourcc = cv2.VideoWriter_fourcc(*'avc1') - out = cv2.VideoWriter( - output_path, - fourcc, - fps, - (width, height), - isColor=True - ) - - if not out.isOpened(): - print("Error: VideoWriter failed to open") - return - - for frame in frames: - frame_array = cv2.cvtColor(np.array(frame), cv2.COLOR_RGB2BGR) - out.write(frame_array) - - out.release() - print(f"Video saved successfully to {output_path}") diff --git a/exo/apputil/baseimages/image1.png b/exo/apputil/baseimages/image1.png deleted file mode 100644 index b8375e7e..00000000 --- a/exo/apputil/baseimages/image1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:361fdadd67c277d45cd18b0bfc8c5ceea5fd89f2d65aef157fd915ce9cbb8599 -size 814460 diff --git a/exo/apputil/baseimages/image2.png b/exo/apputil/baseimages/image2.png deleted file mode 100644 index b2d4f31c..00000000 --- a/exo/apputil/baseimages/image2.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f0e3891bc6b4f4dfa7444af53fcaa4b3ba06b0549546202be3243f08a0e6bd7e -size 814235 diff --git a/exo/apputil/baseimages/image3.png b/exo/apputil/baseimages/image3.png deleted file mode 100644 index 9ee38a57..00000000 --- a/exo/apputil/baseimages/image3.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a2dc5b3378aef397d60fd1252da8a1c578ad97e202a859590ffa416b49551d19 -size 146633 diff --git a/exo/apputil/baseimages/image4.png b/exo/apputil/baseimages/image4.png deleted file mode 100644 index e0959d7b..00000000 --- a/exo/apputil/baseimages/image4.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dbc6883e2a3c5233ec7b844c98646922bdc4f5e42e1f424857eaff56f785dbcd -size 668550 diff --git a/exo/download/__init__.py b/exo/download/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/exo/download/download_progress.py b/exo/download/download_progress.py deleted file mode 100644 index c02733a3..00000000 --- a/exo/download/download_progress.py +++ /dev/null @@ -1,65 +0,0 @@ -from typing import Dict, Callable, Coroutine, Any, Literal -from exo.inference.shard import Shard -from dataclasses import dataclass -from datetime import timedelta - - -@dataclass -class RepoFileProgressEvent: - repo_id: str - repo_revision: str - file_path: str - downloaded: int - downloaded_this_session: int - total: int - speed: int - eta: timedelta - status: Literal["not_started", "in_progress", "complete"] - start_time: float - - def to_dict(self): - return { - "repo_id": self.repo_id, "repo_revision": self.repo_revision, "file_path": self.file_path, "downloaded": self.downloaded, "downloaded_this_session": self.downloaded_this_session, - "total": self.total, "speed": self.speed, "eta": self.eta.total_seconds(), "status": self.status, "start_time": self.start_time - } - - @classmethod - def from_dict(cls, data): - if 'eta' in data: data['eta'] = timedelta(seconds=data['eta']) - return cls(**data) - - -@dataclass -class RepoProgressEvent: - shard: Shard - repo_id: str - repo_revision: str - completed_files: int - total_files: int - downloaded_bytes: int - downloaded_bytes_this_session: int - total_bytes: int - overall_speed: int - overall_eta: timedelta - file_progress: Dict[str, RepoFileProgressEvent] - status: Literal["not_started", "in_progress", "complete"] - - def to_dict(self): - return { - "shard": self.shard.to_dict(), "repo_id": self.repo_id, "repo_revision": self.repo_revision, "completed_files": self.completed_files, "total_files": self.total_files, "downloaded_bytes": self.downloaded_bytes, - "downloaded_bytes_this_session": self.downloaded_bytes_this_session, "total_bytes": self.total_bytes, "overall_speed": self.overall_speed, "overall_eta": self.overall_eta.total_seconds(), - "file_progress": {k: v.to_dict() - for k, v in self.file_progress.items()}, "status": self.status - } - - @classmethod - def from_dict(cls, data): - if 'overall_eta' in data: data['overall_eta'] = timedelta(seconds=data['overall_eta']) - if 'file_progress' in data: data['file_progress'] = {k: RepoFileProgressEvent.from_dict(v) for k, v in data['file_progress'].items()} - if 'shard' in data: data['shard'] = Shard.from_dict(data['shard']) - - return cls(**data) - - -RepoFileProgressCallback = Callable[[RepoFileProgressEvent], Coroutine[Any, Any, None]] -RepoProgressCallback = Callable[[RepoProgressEvent], Coroutine[Any, Any, None]] diff --git a/exo/download/hf/__init__.py b/exo/download/hf/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/exo/download/hf/hf_helpers.py b/exo/download/hf/hf_helpers.py deleted file mode 100644 index 01cd8650..00000000 --- a/exo/download/hf/hf_helpers.py +++ /dev/null @@ -1,98 +0,0 @@ -import aiofiles.os as aios -from typing import Union -import os -from typing import Callable, Optional, Dict, List, Union -from fnmatch import fnmatch -from pathlib import Path -from typing import Generator, Iterable, TypeVar -from exo.helpers import DEBUG -from exo.inference.shard import Shard -import aiofiles - -T = TypeVar("T") - -def filter_repo_objects( - items: Iterable[T], - *, - allow_patterns: Optional[Union[List[str], str]] = None, - ignore_patterns: Optional[Union[List[str], str]] = None, - key: Optional[Callable[[T], str]] = None, -) -> Generator[T, None, None]: - if isinstance(allow_patterns, str): - allow_patterns = [allow_patterns] - if isinstance(ignore_patterns, str): - ignore_patterns = [ignore_patterns] - if allow_patterns is not None: - allow_patterns = [_add_wildcard_to_directories(p) for p in allow_patterns] - if ignore_patterns is not None: - ignore_patterns = [_add_wildcard_to_directories(p) for p in ignore_patterns] - - if key is None: - def _identity(item: T) -> str: - if isinstance(item, str): - return item - if isinstance(item, Path): - return str(item) - raise ValueError(f"Please provide `key` argument in `filter_repo_objects`: `{item}` is not a string.") - key = _identity - - for item in items: - path = key(item) - if allow_patterns is not None and not any(fnmatch(path, r) for r in allow_patterns): - continue - if ignore_patterns is not None and any(fnmatch(path, r) for r in ignore_patterns): - continue - yield item - -def _add_wildcard_to_directories(pattern: str) -> str: - if pattern[-1] == "/": - return pattern + "*" - return pattern - -def get_hf_endpoint() -> str: - return os.environ.get('HF_ENDPOINT', "https://huggingface.co") - -def get_hf_home() -> Path: - """Get the Hugging Face home directory.""" - return Path(os.environ.get("HF_HOME", Path.home()/".cache"/"huggingface")) - -async def get_hf_token(): - """Retrieve the Hugging Face token from the user's HF_HOME directory.""" - token_path = get_hf_home()/"token" - if await aios.path.exists(token_path): - async with aiofiles.open(token_path, 'r') as f: - return (await f.read()).strip() - return None - -async def get_auth_headers(): - """Get authentication headers if a token is available.""" - token = await get_hf_token() - if token: - return {"Authorization": f"Bearer {token}"} - return {} - -def extract_layer_num(tensor_name: str) -> Optional[int]: - # This is a simple example and might need to be adjusted based on the actual naming convention - parts = tensor_name.split('.') - for part in parts: - if part.isdigit(): - return int(part) - return None - -def get_allow_patterns(weight_map: Dict[str, str], shard: Shard) -> List[str]: - default_patterns = set(["*.json", "*.py", "tokenizer.model", "*.tiktoken", "*.txt"]) - shard_specific_patterns = set() - if weight_map: - for tensor_name, filename in weight_map.items(): - layer_num = extract_layer_num(tensor_name) - if layer_num is not None and shard.start_layer <= layer_num <= shard.end_layer: - shard_specific_patterns.add(filename) - sorted_file_names = sorted(weight_map.values()) - if shard.is_first_layer(): - shard_specific_patterns.add(sorted_file_names[0]) - elif shard.is_last_layer(): - shard_specific_patterns.add(sorted_file_names[-1]) - else: - shard_specific_patterns = set(["*.safetensors"]) - if DEBUG >= 3: print(f"get_allow_patterns {weight_map=} {shard=} {shard_specific_patterns=}") - return list(default_patterns | shard_specific_patterns) diff --git a/exo/download/new_shard_download.py b/exo/download/new_shard_download.py deleted file mode 100644 index 02e9f7bf..00000000 --- a/exo/download/new_shard_download.py +++ /dev/null @@ -1,307 +0,0 @@ -from exo.inference.shard import Shard -from exo.models import get_repo -from pathlib import Path -from exo.download.hf.hf_helpers import get_hf_endpoint, get_auth_headers, filter_repo_objects, get_allow_patterns -from exo.download.shard_download import ShardDownloader -from exo.download.download_progress import RepoProgressEvent, RepoFileProgressEvent -from exo.helpers import AsyncCallbackSystem, DEBUG -from exo.models import get_supported_models, build_full_shard -import os -import aiofiles.os as aios -import aiohttp -import aiofiles -from urllib.parse import urljoin -from typing import Callable, Union, Tuple, Dict, List, Optional, Literal, AsyncIterator -import time -from datetime import timedelta -import asyncio -import json -import traceback -import shutil -import tempfile -import hashlib - -def exo_home() -> Path: - return Path(os.environ.get("EXO_HOME", Path.home()/".cache"/"exo")) - -def exo_tmp() -> Path: - return Path(tempfile.gettempdir())/"exo" - -async def ensure_exo_home() -> Path: - await aios.makedirs(exo_home(), exist_ok=True) - return exo_home() - -async def ensure_exo_tmp() -> Path: - await aios.makedirs(exo_tmp(), exist_ok=True) - return exo_tmp() - -async def has_exo_home_read_access() -> bool: - try: return await aios.access(exo_home(), os.R_OK) - except OSError: return False - -async def has_exo_home_write_access() -> bool: - try: return await aios.access(exo_home(), os.W_OK) - except OSError: return False - -async def ensure_downloads_dir() -> Path: - downloads_dir = exo_home()/"downloads" - await aios.makedirs(downloads_dir, exist_ok=True) - return downloads_dir - -async def delete_model(model_id: str, inference_engine_name: str) -> bool: - repo_id = get_repo(model_id, inference_engine_name) - model_dir = await ensure_downloads_dir()/repo_id.replace("/", "--") - if not await aios.path.exists(model_dir): return False - await asyncio.to_thread(shutil.rmtree, model_dir, ignore_errors=False) - return True - -async def seed_models(seed_dir: Union[str, Path]): - """Move model in resources folder of app to .cache/huggingface/hub""" - source_dir = Path(seed_dir) - dest_dir = await ensure_downloads_dir() - for path in source_dir.iterdir(): - if path.is_dir() and path.name.startswith("models--"): - dest_path = dest_dir/path.name - if await aios.path.exists(dest_path): print('Skipping moving model to .cache directory') - else: - try: await aios.rename(str(path), str(dest_path)) - except: - print(f"Error seeding model {path} to {dest_path}") - traceback.print_exc() - -async def fetch_file_list_with_cache(repo_id: str, revision: str = "main") -> List[Dict[str, Union[str, int]]]: - cache_file = (await ensure_exo_tmp())/f"{repo_id.replace('/', '--')}--{revision}--file_list.json" - if await aios.path.exists(cache_file): - async with aiofiles.open(cache_file, 'r') as f: return json.loads(await f.read()) - file_list = await fetch_file_list_with_retry(repo_id, revision) - await aios.makedirs(cache_file.parent, exist_ok=True) - async with aiofiles.open(cache_file, 'w') as f: await f.write(json.dumps(file_list)) - return file_list - -async def fetch_file_list_with_retry(repo_id: str, revision: str = "main", path: str = "") -> List[Dict[str, Union[str, int]]]: - n_attempts = 30 - for attempt in range(n_attempts): - try: return await _fetch_file_list(repo_id, revision, path) - except Exception as e: - if attempt == n_attempts - 1: raise e - await asyncio.sleep(min(8, 0.1 * (2 ** attempt))) - -async def _fetch_file_list(repo_id: str, revision: str = "main", path: str = "") -> List[Dict[str, Union[str, int]]]: - api_url = f"{get_hf_endpoint()}/api/models/{repo_id}/tree/{revision}" - url = f"{api_url}/{path}" if path else api_url - - headers = await get_auth_headers() - async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=30, connect=10, sock_read=30, sock_connect=10)) as session: - async with session.get(url, headers=headers) as response: - if response.status == 200: - data = await response.json() - files = [] - for item in data: - if item["type"] == "file": - files.append({"path": item["path"], "size": item["size"]}) - elif item["type"] == "directory": - subfiles = await _fetch_file_list(repo_id, revision, item["path"]) - files.extend(subfiles) - return files - else: - raise Exception(f"Failed to fetch file list: {response.status}") - -async def calc_hash(path: Path, type: Literal["sha1", "sha256"] = "sha1") -> str: - hash = hashlib.sha1() if type == "sha1" else hashlib.sha256() - if type == "sha1": - header = f"blob {(await aios.stat(path)).st_size}\0".encode() - hash.update(header) - async with aiofiles.open(path, 'rb') as f: - while chunk := await f.read(8 * 1024 * 1024): - hash.update(chunk) - return hash.hexdigest() - -async def file_meta(repo_id: str, revision: str, path: str) -> Tuple[int, str]: - url = urljoin(f"{get_hf_endpoint()}/{repo_id}/resolve/{revision}/", path) - headers = await get_auth_headers() - async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=1800, connect=60, sock_read=1800, sock_connect=60)) as session: - async with session.head(url, headers=headers) as r: - content_length = int(r.headers.get('x-linked-size') or r.headers.get('content-length') or 0) - etag = r.headers.get('X-Linked-ETag') or r.headers.get('ETag') or r.headers.get('Etag') - assert content_length > 0, f"No content length for {url}" - assert etag is not None, f"No remote hash for {url}" - if (etag[0] == '"' and etag[-1] == '"') or (etag[0] == "'" and etag[-1] == "'"): etag = etag[1:-1] - return content_length, etag - -async def download_file_with_retry(repo_id: str, revision: str, path: str, target_dir: Path, on_progress: Callable[[int, int], None] = lambda _, __: None) -> Path: - n_attempts = 30 - for attempt in range(n_attempts): - try: return await _download_file(repo_id, revision, path, target_dir, on_progress) - except Exception as e: - if isinstance(e, FileNotFoundError) or attempt == n_attempts - 1: raise e - print(f"Download error on attempt {attempt}/{n_attempts} for {repo_id=} {revision=} {path=} {target_dir=}") - traceback.print_exc() - await asyncio.sleep(min(8, 0.1 * (2 ** attempt))) - -async def _download_file(repo_id: str, revision: str, path: str, target_dir: Path, on_progress: Callable[[int, int], None] = lambda _, __: None) -> Path: - if await aios.path.exists(target_dir/path): return target_dir/path - await aios.makedirs((target_dir/path).parent, exist_ok=True) - length, etag = await file_meta(repo_id, revision, path) - remote_hash = etag[:-5] if etag.endswith("-gzip") else etag - partial_path = target_dir/f"{path}.partial" - resume_byte_pos = (await aios.stat(partial_path)).st_size if (await aios.path.exists(partial_path)) else None - if resume_byte_pos != length: - url = urljoin(f"{get_hf_endpoint()}/{repo_id}/resolve/{revision}/", path) - headers = await get_auth_headers() - if resume_byte_pos: headers['Range'] = f'bytes={resume_byte_pos}-' - n_read = resume_byte_pos or 0 - async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=1800, connect=60, sock_read=1800, sock_connect=60)) as session: - async with session.get(url, headers=headers, timeout=aiohttp.ClientTimeout(total=1800, connect=60, sock_read=1800, sock_connect=60)) as r: - if r.status == 404: raise FileNotFoundError(f"File not found: {url}") - assert r.status in [200, 206], f"Failed to download {path} from {url}: {r.status}" - async with aiofiles.open(partial_path, 'ab' if resume_byte_pos else 'wb') as f: - while chunk := await r.content.read(8 * 1024 * 1024): on_progress(n_read := n_read + await f.write(chunk), length) - - final_hash = await calc_hash(partial_path, type="sha256" if len(remote_hash) == 64 else "sha1") - integrity = final_hash == remote_hash - if not integrity: - try: await aios.remove(partial_path) - except Exception as e: print(f"Error removing partial file {partial_path}: {e}") - raise Exception(f"Downloaded file {target_dir/path} has hash {final_hash} but remote hash is {remote_hash}") - await aios.rename(partial_path, target_dir/path) - return target_dir/path - - -def calculate_repo_progress(shard: Shard, repo_id: str, revision: str, file_progress: Dict[str, RepoFileProgressEvent], all_start_time: float) -> RepoProgressEvent: - all_total_bytes = sum([p.total for p in file_progress.values()]) - all_downloaded_bytes = sum([p.downloaded for p in file_progress.values()]) - all_downloaded_bytes_this_session = sum([p.downloaded_this_session for p in file_progress.values()]) - elapsed_time = time.time() - all_start_time - all_speed = all_downloaded_bytes_this_session / elapsed_time if elapsed_time > 0 else 0 - all_eta = timedelta(seconds=(all_total_bytes - all_downloaded_bytes) / all_speed) if all_speed > 0 else timedelta(seconds=0) - status = "complete" if all(p.status == "complete" for p in file_progress.values()) else "in_progress" if any(p.status == "in_progress" for p in file_progress.values()) else "not_started" - return RepoProgressEvent(shard, repo_id, revision, len([p for p in file_progress.values() if p.downloaded == p.total]), len(file_progress), all_downloaded_bytes, all_downloaded_bytes_this_session, all_total_bytes, all_speed, all_eta, file_progress, status) - -async def get_weight_map(repo_id: str, revision: str = "main") -> Dict[str, str]: - target_dir = (await ensure_exo_tmp())/repo_id.replace("/", "--") - index_file = await download_file_with_retry(repo_id, revision, "model.safetensors.index.json", target_dir) - async with aiofiles.open(index_file, 'r') as f: index_data = json.loads(await f.read()) - return index_data.get("weight_map") - -async def resolve_allow_patterns(shard: Shard, inference_engine_classname: str) -> List[str]: - try: - weight_map = await get_weight_map(get_repo(shard.model_id, inference_engine_classname)) - return get_allow_patterns(weight_map, shard) - except: - if DEBUG >= 1: print(f"Error getting weight map for {shard.model_id=} and inference engine {inference_engine_classname}") - if DEBUG >= 1: traceback.print_exc() - return ["*"] - -async def get_downloaded_size(path: Path) -> int: - partial_path = path.with_suffix(path.suffix + ".partial") - if await aios.path.exists(path): return (await aios.stat(path)).st_size - if await aios.path.exists(partial_path): return (await aios.stat(partial_path)).st_size - return 0 - -async def download_shard(shard: Shard, inference_engine_classname: str, on_progress: AsyncCallbackSystem[str, Tuple[Shard, RepoProgressEvent]], max_parallel_downloads: int = 8, skip_download: bool = False) -> tuple[Path, RepoProgressEvent]: - if DEBUG >= 2 and not skip_download: print(f"Downloading {shard.model_id=} for {inference_engine_classname}") - repo_id = get_repo(shard.model_id, inference_engine_classname) - revision = "main" - target_dir = await ensure_downloads_dir()/repo_id.replace("/", "--") - if not skip_download: await aios.makedirs(target_dir, exist_ok=True) - - if repo_id is None: - raise ValueError(f"No repo found for {shard.model_id=} and inference engine {inference_engine_classname}") - - allow_patterns = await resolve_allow_patterns(shard, inference_engine_classname) - if DEBUG >= 2: print(f"Downloading {shard.model_id=} with {allow_patterns=}") - - all_start_time = time.time() - file_list = await fetch_file_list_with_cache(repo_id, revision) - filtered_file_list = list(filter_repo_objects(file_list, allow_patterns=allow_patterns, key=lambda x: x["path"])) - file_progress: Dict[str, RepoFileProgressEvent] = {} - def on_progress_wrapper(file: dict, curr_bytes: int, total_bytes: int): - start_time = file_progress[file["path"]].start_time if file["path"] in file_progress else time.time() - downloaded_this_session = file_progress[file["path"]].downloaded_this_session + (curr_bytes - file_progress[file["path"]].downloaded) if file["path"] in file_progress else curr_bytes - speed = downloaded_this_session / (time.time() - start_time) if time.time() - start_time > 0 else 0 - eta = timedelta(seconds=(total_bytes - curr_bytes) / speed) if speed > 0 else timedelta(seconds=0) - file_progress[file["path"]] = RepoFileProgressEvent(repo_id, revision, file["path"], curr_bytes, downloaded_this_session, total_bytes, speed, eta, "complete" if curr_bytes == total_bytes else "in_progress", start_time) - on_progress.trigger_all(shard, calculate_repo_progress(shard, repo_id, revision, file_progress, all_start_time)) - if DEBUG >= 6: print(f"Downloading {file['path']} {curr_bytes}/{total_bytes} {speed} {eta}") - for file in filtered_file_list: - downloaded_bytes = await get_downloaded_size(target_dir/file["path"]) - file_progress[file["path"]] = RepoFileProgressEvent(repo_id, revision, file["path"], downloaded_bytes, 0, file["size"], 0, timedelta(0), "complete" if downloaded_bytes == file["size"] else "not_started", time.time()) - - semaphore = asyncio.Semaphore(max_parallel_downloads) - async def download_with_semaphore(file): - async with semaphore: - await download_file_with_retry(repo_id, revision, file["path"], target_dir, lambda curr_bytes, total_bytes: on_progress_wrapper(file, curr_bytes, total_bytes)) - if not skip_download: await asyncio.gather(*[download_with_semaphore(file) for file in filtered_file_list]) - final_repo_progress = calculate_repo_progress(shard, repo_id, revision, file_progress, all_start_time) - on_progress.trigger_all(shard, final_repo_progress) - if gguf := next((f for f in filtered_file_list if f["path"].endswith(".gguf")), None): - return target_dir/gguf["path"], final_repo_progress - else: - return target_dir, final_repo_progress - -def new_shard_downloader(max_parallel_downloads: int = 8) -> ShardDownloader: - return SingletonShardDownloader(CachedShardDownloader(NewShardDownloader(max_parallel_downloads))) - -class SingletonShardDownloader(ShardDownloader): - def __init__(self, shard_downloader: ShardDownloader): - self.shard_downloader = shard_downloader - self.active_downloads: Dict[Shard, asyncio.Task] = {} - - @property - def on_progress(self) -> AsyncCallbackSystem[str, Tuple[Shard, RepoProgressEvent]]: - return self.shard_downloader.on_progress - - async def ensure_shard(self, shard: Shard, inference_engine_name: str) -> Path: - if shard not in self.active_downloads: self.active_downloads[shard] = asyncio.create_task(self.shard_downloader.ensure_shard(shard, inference_engine_name)) - try: return await self.active_downloads[shard] - finally: - if shard in self.active_downloads and self.active_downloads[shard].done(): del self.active_downloads[shard] - - async def get_shard_download_status(self, inference_engine_name: str) -> AsyncIterator[tuple[Path, RepoProgressEvent]]: - async for path, status in self.shard_downloader.get_shard_download_status(inference_engine_name): - yield path, status - -class CachedShardDownloader(ShardDownloader): - def __init__(self, shard_downloader: ShardDownloader): - self.shard_downloader = shard_downloader - self.cache: Dict[tuple[str, Shard], Path] = {} - - @property - def on_progress(self) -> AsyncCallbackSystem[str, Tuple[Shard, RepoProgressEvent]]: - return self.shard_downloader.on_progress - - async def ensure_shard(self, shard: Shard, inference_engine_name: str) -> Path: - if (inference_engine_name, shard) in self.cache: - if DEBUG >= 2: print(f"ensure_shard cache hit {shard=} for {inference_engine_name}") - return self.cache[(inference_engine_name, shard)] - if DEBUG >= 2: print(f"ensure_shard cache miss {shard=} for {inference_engine_name}") - target_dir = await self.shard_downloader.ensure_shard(shard, inference_engine_name) - self.cache[(inference_engine_name, shard)] = target_dir - return target_dir - - async def get_shard_download_status(self, inference_engine_name: str) -> AsyncIterator[tuple[Path, RepoProgressEvent]]: - async for path, status in self.shard_downloader.get_shard_download_status(inference_engine_name): - yield path, status - -class NewShardDownloader(ShardDownloader): - def __init__(self, max_parallel_downloads: int = 8): - self.max_parallel_downloads = max_parallel_downloads - self._on_progress = AsyncCallbackSystem[str, Tuple[Shard, RepoProgressEvent]]() - - @property - def on_progress(self) -> AsyncCallbackSystem[str, Tuple[Shard, RepoProgressEvent]]: - return self._on_progress - - async def ensure_shard(self, shard: Shard, inference_engine_name: str) -> Path: - target_dir, _ = await download_shard(shard, inference_engine_name, self.on_progress, max_parallel_downloads=self.max_parallel_downloads) - return target_dir - - async def get_shard_download_status(self, inference_engine_name: str) -> AsyncIterator[tuple[Path, RepoProgressEvent]]: - if DEBUG >= 2: print("Getting shard download status for", inference_engine_name) - tasks = [download_shard(build_full_shard(model_id, inference_engine_name), inference_engine_name, self.on_progress, skip_download=True) for model_id in get_supported_models([[inference_engine_name]])] - for task in asyncio.as_completed(tasks): - try: - path, progress = await task - yield (path, progress) - except Exception as e: - print("Error downloading shard:", e) diff --git a/exo/download/shard_download.py b/exo/download/shard_download.py deleted file mode 100644 index 9e06e40a..00000000 --- a/exo/download/shard_download.py +++ /dev/null @@ -1,49 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Optional, Tuple, Dict, AsyncIterator -from pathlib import Path -from exo.inference.shard import Shard -from exo.download.download_progress import RepoProgressEvent -from exo.helpers import AsyncCallbackSystem - - -class ShardDownloader(ABC): - @abstractmethod - async def ensure_shard(self, shard: Shard, inference_engine_name: str) -> Path: - """ - Ensures that the shard is downloaded. - Does not allow multiple overlapping downloads at once. - If you try to download a Shard which overlaps a Shard that is already being downloaded, - the download will be cancelled and a new download will start. - - Args: - shard (Shard): The shard to download. - inference_engine_name (str): The inference engine used on the node hosting the shard - """ - pass - - @property - @abstractmethod - def on_progress(self) -> AsyncCallbackSystem[str, Tuple[Shard, RepoProgressEvent]]: - pass - - @abstractmethod - async def get_shard_download_status(self, inference_engine_name: str) -> AsyncIterator[tuple[Path, RepoProgressEvent]]: - """Get the download status of shards. - - Returns: - Optional[Dict[str, float]]: A dictionary mapping shard IDs to their download percentage (0-100), - or None if status cannot be determined - """ - pass - - -class NoopShardDownloader(ShardDownloader): - async def ensure_shard(self, shard: Shard, inference_engine_name: str) -> Path: - return Path("/tmp/noop_shard") - - @property - def on_progress(self) -> AsyncCallbackSystem[str, Tuple[Shard, RepoProgressEvent]]: - return AsyncCallbackSystem() - - async def get_shard_download_status(self, inference_engine_name: str) -> AsyncIterator[tuple[Path, RepoProgressEvent]]: - if False: yield diff --git a/exo/download/test_new_shard_download.py b/exo/download/test_new_shard_download.py deleted file mode 100644 index 57b33c65..00000000 --- a/exo/download/test_new_shard_download.py +++ /dev/null @@ -1,14 +0,0 @@ -from exo.download.new_shard_download import NewShardDownloader -from exo.inference.shard import Shard -import asyncio - -async def test_new_shard_download(): - shard_downloader = NewShardDownloader() - shard_downloader.on_progress.register("test").on_next(lambda shard, event: print(shard, event)) - await shard_downloader.ensure_shard(Shard(model_id="llama-3.2-1b", start_layer=0, end_layer=0, n_layers=16), "MLXDynamicShardInferenceEngine") - async for path, shard_status in shard_downloader.get_shard_download_status("MLXDynamicShardInferenceEngine"): - print("Shard download status:", path, shard_status) - -if __name__ == "__main__": - asyncio.run(test_new_shard_download()) - diff --git a/exo/helpers.py b/exo/helpers.py deleted file mode 100644 index ff0205f0..00000000 --- a/exo/helpers.py +++ /dev/null @@ -1,372 +0,0 @@ -import os -import sys -import asyncio -from typing import Callable, TypeVar, Optional, Dict, Generic, Tuple, List -import socket -import random -import platform -import psutil -import uuid -from scapy.all import get_if_addr, get_if_list -import re -import subprocess -from pathlib import Path -import tempfile -import json -from concurrent.futures import ThreadPoolExecutor -import traceback - -DEBUG = int(os.getenv("DEBUG", default="0")) -DEBUG_DISCOVERY = int(os.getenv("DEBUG_DISCOVERY", default="0")) -VERSION = "0.0.1" - -exo_text = r""" - _____ _____ - / _ \ \/ / _ \ -| __/> < (_) | - \___/_/\_\___/ - """ - -# Single shared thread pool for subprocess operations -subprocess_pool = ThreadPoolExecutor(max_workers=4, thread_name_prefix="subprocess_worker") - - -def get_system_info(): - if psutil.MACOS: - if platform.machine() == "arm64": - return "Apple Silicon Mac" - if platform.machine() in ["x86_64", "i386"]: - return "Intel Mac" - return "Unknown Mac architecture" - if psutil.LINUX: - return "Linux" - return "Non-Mac, non-Linux system" - - -def find_available_port(host: str = "", min_port: int = 49152, max_port: int = 65535) -> int: - used_ports_file = os.path.join(tempfile.gettempdir(), "exo_used_ports") - - def read_used_ports(): - if os.path.exists(used_ports_file): - with open(used_ports_file, "r") as f: - return [int(line.strip()) for line in f if line.strip().isdigit()] - return [] - - def write_used_port(port, used_ports): - with open(used_ports_file, "w") as f: - print(used_ports[-19:]) - for p in used_ports[-19:] + [port]: - f.write(f"{p}\n") - - used_ports = read_used_ports() - available_ports = set(range(min_port, max_port + 1)) - set(used_ports) - - while available_ports: - port = random.choice(list(available_ports)) - if DEBUG >= 2: print(f"Trying to find available port {port=}") - try: - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - s.bind((host, port)) - write_used_port(port, used_ports) - return port - except socket.error: - available_ports.remove(port) - - raise RuntimeError("No available ports in the specified range") - - -def print_exo(): - print(exo_text) - - -def print_yellow_exo(): - yellow = "\033[93m" # ANSI escape code for yellow - reset = "\033[0m" # ANSI escape code to reset color - print(f"{yellow}{exo_text}{reset}") - - -def terminal_link(uri, label=None): - if label is None: - label = uri - parameters = "" - - # OSC 8 ; params ; URI ST OSC 8 ;; ST - escape_mask = "\033]8;{};{}\033\\{}\033]8;;\033\\" - - return escape_mask.format(parameters, uri, label) - - -T = TypeVar("T") -K = TypeVar("K") - - -class AsyncCallback(Generic[T]): - def __init__(self) -> None: - self.condition: asyncio.Condition = asyncio.Condition() - self.result: Optional[Tuple[T, ...]] = None - self.observers: list[Callable[..., None]] = [] - - async def wait(self, check_condition: Callable[..., bool], timeout: Optional[float] = None) -> Tuple[T, ...]: - async with self.condition: - await asyncio.wait_for(self.condition.wait_for(lambda: self.result is not None and check_condition(*self.result)), timeout) - assert self.result is not None # for type checking - return self.result - - def on_next(self, callback: Callable[..., None]) -> None: - self.observers.append(callback) - - def set(self, *args: T) -> None: - self.result = args - for observer in self.observers: - observer(*args) - asyncio.create_task(self.notify()) - - async def notify(self) -> None: - async with self.condition: - self.condition.notify_all() - - -class AsyncCallbackSystem(Generic[K, T]): - def __init__(self) -> None: - self.callbacks: Dict[K, AsyncCallback[T]] = {} - - def register(self, name: K) -> AsyncCallback[T]: - if name not in self.callbacks: - self.callbacks[name] = AsyncCallback[T]() - return self.callbacks[name] - - def deregister(self, name: K) -> None: - if name in self.callbacks: - del self.callbacks[name] - - def trigger(self, name: K, *args: T) -> None: - if name in self.callbacks: - self.callbacks[name].set(*args) - - def trigger_all(self, *args: T) -> None: - for callback in self.callbacks.values(): - callback.set(*args) - - -K = TypeVar('K', bound=str) -V = TypeVar('V') - - -class PrefixDict(Generic[K, V]): - def __init__(self): - self.items: Dict[K, V] = {} - - def add(self, key: K, value: V) -> None: - self.items[key] = value - - def find_prefix(self, argument: str) -> List[Tuple[K, V]]: - return [(key, value) for key, value in self.items.items() if argument.startswith(key)] - - def find_longest_prefix(self, argument: str) -> Optional[Tuple[K, V]]: - matches = self.find_prefix(argument) - if len(matches) == 0: - return None - - return max(matches, key=lambda x: len(x[0])) - - -def is_valid_uuid(val): - try: - uuid.UUID(str(val)) - return True - except ValueError: - return False - - -def get_or_create_node_id(): - NODE_ID_FILE = Path(tempfile.gettempdir())/".exo_node_id" - try: - if NODE_ID_FILE.is_file(): - with open(NODE_ID_FILE, "r") as f: - stored_id = f.read().strip() - if is_valid_uuid(stored_id): - if DEBUG >= 2: print(f"Retrieved existing node ID: {stored_id}") - return stored_id - else: - if DEBUG >= 2: print("Stored ID is not a valid UUID. Generating a new one.") - - new_id = str(uuid.uuid4()) - with open(NODE_ID_FILE, "w") as f: - f.write(new_id) - - if DEBUG >= 2: print(f"Generated and stored new node ID: {new_id}") - return new_id - except IOError as e: - if DEBUG >= 2: print(f"IO error creating node_id: {e}") - return str(uuid.uuid4()) - except Exception as e: - if DEBUG >= 2: print(f"Unexpected error creating node_id: {e}") - return str(uuid.uuid4()) - - -def pretty_print_bytes(size_in_bytes: int) -> str: - if size_in_bytes < 1024: - return f"{size_in_bytes} B" - elif size_in_bytes < 1024**2: - return f"{size_in_bytes / 1024:.2f} KB" - elif size_in_bytes < 1024**3: - return f"{size_in_bytes / (1024 ** 2):.2f} MB" - elif size_in_bytes < 1024**4: - return f"{size_in_bytes / (1024 ** 3):.2f} GB" - else: - return f"{size_in_bytes / (1024 ** 4):.2f} TB" - - -def pretty_print_bytes_per_second(bytes_per_second: int) -> str: - if bytes_per_second < 1024: - return f"{bytes_per_second} B/s" - elif bytes_per_second < 1024**2: - return f"{bytes_per_second / 1024:.2f} KB/s" - elif bytes_per_second < 1024**3: - return f"{bytes_per_second / (1024 ** 2):.2f} MB/s" - elif bytes_per_second < 1024**4: - return f"{bytes_per_second / (1024 ** 3):.2f} GB/s" - else: - return f"{bytes_per_second / (1024 ** 4):.2f} TB/s" - - -def get_all_ip_addresses_and_interfaces(): - ip_addresses = [] - for interface in get_if_list(): - try: - ip = get_if_addr(interface) - if ip.startswith("0.0."): continue - simplified_interface = re.sub(r'^\\Device\\NPF_', '', interface) - ip_addresses.append((ip, simplified_interface)) - except: - if DEBUG >= 1: print(f"Failed to get IP address for interface {interface}") - if DEBUG >= 1: traceback.print_exc() - if not ip_addresses: - if DEBUG >= 1: print("Failed to get any IP addresses. Defaulting to localhost.") - return [("localhost", "lo")] - return list(set(ip_addresses)) - - - -async def get_macos_interface_type(ifname: str) -> Optional[Tuple[int, str]]: - try: - # Use the shared subprocess_pool - output = await asyncio.get_running_loop().run_in_executor( - subprocess_pool, lambda: subprocess.run(['system_profiler', 'SPNetworkDataType', '-json'], capture_output=True, text=True, close_fds=True).stdout - ) - - data = json.loads(output) - - for interface in data.get('SPNetworkDataType', []): - if interface.get('interface') == ifname: - hardware = interface.get('hardware', '').lower() - type_name = interface.get('type', '').lower() - name = interface.get('_name', '').lower() - - if 'thunderbolt' in name: - return (5, "Thunderbolt") - if hardware == 'ethernet' or type_name == 'ethernet': - if 'usb' in name: - return (4, "Ethernet [USB]") - return (4, "Ethernet") - if hardware == 'airport' or type_name == 'airport' or 'wi-fi' in name: - return (3, "WiFi") - if type_name == 'vpn': - return (1, "External Virtual") - - except Exception as e: - if DEBUG >= 2: print(f"Error detecting macOS interface type: {e}") - - return None - - -async def get_interface_priority_and_type(ifname: str) -> Tuple[int, str]: - # On macOS, try to get interface type using networksetup - if psutil.MACOS: - macos_type = await get_macos_interface_type(ifname) - if macos_type is not None: return macos_type - - # Local container/virtual interfaces - if (ifname.startswith(('docker', 'br-', 'veth', 'cni', 'flannel', 'calico', 'weave')) or 'bridge' in ifname): - return (7, "Container Virtual") - - # Loopback interface - if ifname.startswith('lo'): - return (6, "Loopback") - - # Traditional detection for non-macOS systems or fallback - if ifname.startswith(('tb', 'nx', 'ten')): - return (5, "Thunderbolt") - - # Regular ethernet detection - if ifname.startswith(('eth', 'en')) and not ifname.startswith(('en1', 'en0')): - return (4, "Ethernet") - - # WiFi detection - if ifname.startswith(('wlan', 'wifi', 'wl')) or ifname in ['en0', 'en1']: - return (3, "WiFi") - - # Non-local virtual interfaces (VPNs, tunnels) - if ifname.startswith(('tun', 'tap', 'vtun', 'utun', 'gif', 'stf', 'awdl', 'llw')): - return (1, "External Virtual") - - # Other physical interfaces - return (2, "Other") - - -async def shutdown(signal, loop, server): - """Gracefully shutdown the server and close the asyncio loop.""" - print(f"Received exit signal {signal.name}...") - print("Thank you for using exo.") - print_yellow_exo() - server_tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()] - [task.cancel() for task in server_tasks] - print(f"Cancelling {len(server_tasks)} outstanding tasks") - await asyncio.gather(*server_tasks, return_exceptions=True) - await server.stop() - - -def is_frozen(): - return getattr(sys, 'frozen', False) or os.path.basename(sys.executable) == "exo" \ - or ('Contents/MacOS' in str(os.path.dirname(sys.executable))) \ - or '__nuitka__' in globals() or getattr(sys, '__compiled__', False) - -async def get_mac_system_info() -> Tuple[str, str, int]: - """Get Mac system information using system_profiler.""" - try: - output = await asyncio.get_running_loop().run_in_executor( - subprocess_pool, - lambda: subprocess.check_output(["system_profiler", "SPHardwareDataType"]).decode("utf-8") - ) - - model_line = next((line for line in output.split("\n") if "Model Name" in line), None) - model_id = model_line.split(": ")[1] if model_line else "Unknown Model" - - chip_line = next((line for line in output.split("\n") if "Chip" in line), None) - chip_id = chip_line.split(": ")[1] if chip_line else "Unknown Chip" - - memory_line = next((line for line in output.split("\n") if "Memory" in line), None) - memory_str = memory_line.split(": ")[1] if memory_line else "Unknown Memory" - memory_units = memory_str.split() - memory_value = int(memory_units[0]) - memory = memory_value * 1024 if memory_units[1] == "GB" else memory_value - - return model_id, chip_id, memory - except Exception as e: - if DEBUG >= 2: print(f"Error getting Mac system info: {e}") - return "Unknown Model", "Unknown Chip", 0 - -def get_exo_home() -> Path: - if psutil.WINDOWS: docs_folder = Path(os.environ["USERPROFILE"])/"Documents" - else: docs_folder = Path.home()/"Documents" - if not docs_folder.exists(): docs_folder.mkdir(exist_ok=True) - exo_folder = docs_folder/"Exo" - if not exo_folder.exists(): exo_folder.mkdir(exist_ok=True) - return exo_folder - - -def get_exo_images_dir() -> Path: - exo_home = get_exo_home() - images_dir = exo_home/"Images" - if not images_dir.exists(): images_dir.mkdir(exist_ok=True) - return images_dir diff --git a/exo/inference/__init__.py b/exo/inference/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/exo/inference/debug_inference_engine.py b/exo/inference/debug_inference_engine.py deleted file mode 100644 index d81ac023..00000000 --- a/exo/inference/debug_inference_engine.py +++ /dev/null @@ -1,58 +0,0 @@ -from exo.inference.inference_engine import InferenceEngine -from exo.inference.shard import Shard -from exo.inference.tinygrad.inference import TinygradDynamicShardInferenceEngine -import asyncio -import numpy as np - - -# An inference engine should work the same for any number of Shards, as long as the Shards are continuous. -async def test_inference_engine(inference_engine_1: InferenceEngine, inference_engine_2: InferenceEngine, model_id: str): - from exo.inference.tinygrad.inference import Tokenizer - from pathlib import Path - - _tokenizer = Tokenizer(str(Path(model_id)/"tokenizer.model")) - - prompt = "In a single word only, what is the last name of the president of the United States? " - resp_full = await inference_engine_1.infer_prompt("A", shard=Shard(model_id=model_id, start_layer=0, end_layer=31, n_layers=32), prompt=prompt) - token_full = await inference_engine_1.sample(resp_full) - - next_resp_full, _ = await inference_engine_1.infer_tensor( - "A", - shard=Shard(model_id=model_id, start_layer=0, end_layer=31, n_layers=32), - input_data=token_full, - ) - - resp1, _ = await inference_engine_1.infer_prompt("B", shard=Shard(model_id=model_id, start_layer=0, end_layer=30, n_layers=32), prompt=prompt) - resp2, _ = await inference_engine_2.infer_tensor( - "B", - shard=Shard(model_id=model_id, start_layer=31, end_layer=31, n_layers=32), - input_data=resp1, - ) - token2 = await inference_engine_2.sample(resp2) - resp3, _ = await inference_engine_1.infer_tensor( - "B", - shard=Shard(model_id=model_id, start_layer=0, end_layer=30, n_layers=32), - input_data=token2, - ) - resp4, _ = await inference_engine_2.infer_tensor( - "B", - shard=Shard(model_id=model_id, start_layer=31, end_layer=31, n_layers=32), - input_data=resp3, - ) - - print(f"{resp2=}") - print(f"full: {_tokenizer.decode(resp_full)}") - print(f"next full: {_tokenizer.decode(next_resp_full)}") - print(f"resp2: {_tokenizer.decode(resp2)}") - print(f"{resp4=}") - print(f"resp4: {_tokenizer.decode(resp4)}") - - assert np.array_equal(resp_full, resp2) - assert np.array_equal(next_resp_full, resp4) - - -asyncio.run(test_inference_engine( - TinygradDynamicShardInferenceEngine(), - TinygradDynamicShardInferenceEngine(), - "llama3-8b-sfr", -)) diff --git a/exo/inference/dummy_inference_engine.py b/exo/inference/dummy_inference_engine.py deleted file mode 100644 index 98cf8894..00000000 --- a/exo/inference/dummy_inference_engine.py +++ /dev/null @@ -1,37 +0,0 @@ -from typing import Optional, Tuple, TYPE_CHECKING -import numpy as np -from exo.inference.inference_engine import InferenceEngine -from exo.inference.shard import Shard -from exo.inference.tokenizers import DummyTokenizer - -class DummyInferenceEngine(InferenceEngine): - def __init__(self): - self.shard = None - self.vocab_size = 1000 - self.hidden_size = 256 - self.eos_token_id = 0 - self.latency_mean = 0.1 - self.latency_stddev = 0.02 - self.num_generate_dummy_tokens = 10 - self.tokenizer = DummyTokenizer() - - async def encode(self, shard: Shard, prompt: str) -> np.ndarray: - return np.array(self.tokenizer.encode(prompt)) - - async def sample(self, x: np.ndarray, temp: float = 0.0, top_p: float = 1.0) -> np.ndarray: - if x[0] > self.num_generate_dummy_tokens: return np.array([self.tokenizer.eos_token_id]) - return x - - async def decode(self, shard: Shard, tokens: np.ndarray) -> str: - return self.tokenizer.decode(tokens) - - async def infer_tensor(self, request_id: str, shard: Shard, input_data: np.ndarray, inference_state: Optional[dict] = None) -> tuple[np.ndarray, Optional[dict]]: - await self.ensure_shard(shard) - return input_data + 1 if self.shard.is_last_layer() else input_data, None - - async def ensure_shard(self, shard: Shard): - if self.shard == shard: return - self.shard = shard - - async def load_checkpoint(self, shard: Shard, path: str): - await self.ensure_shard(shard) diff --git a/exo/inference/inference_engine.py b/exo/inference/inference_engine.py deleted file mode 100644 index b6200037..00000000 --- a/exo/inference/inference_engine.py +++ /dev/null @@ -1,77 +0,0 @@ -import numpy as np -import os -from exo.helpers import DEBUG # Make sure to import DEBUG - -from typing import Tuple, Optional -from abc import ABC, abstractmethod -from .shard import Shard -from exo.download.shard_download import ShardDownloader - - -class InferenceEngine(ABC): - session = {} - - @abstractmethod - async def encode(self, shard: Shard, prompt: str) -> np.ndarray: - pass - - @abstractmethod - async def sample(self, x: np.ndarray) -> np.ndarray: - pass - - @abstractmethod - async def decode(self, shard: Shard, tokens: np.ndarray) -> str: - pass - - @abstractmethod - async def infer_tensor(self, request_id: str, shard: Shard, input_data: np.ndarray, inference_state: Optional[dict] = None) -> tuple[np.ndarray, Optional[dict]]: - pass - - @abstractmethod - async def load_checkpoint(self, shard: Shard, path: str): - pass - - async def save_checkpoint(self, shard: Shard, path: str): - pass - - async def save_session(self, key, value): - self.session[key] = value - - async def clear_session(self): - self.session.empty() - - async def infer_prompt(self, request_id: str, shard: Shard, prompt: str, inference_state: Optional[dict] = None) -> tuple[np.ndarray, Optional[dict]]: - tokens = await self.encode(shard, prompt) - if shard.model_id != 'stable-diffusion-2-1-base': - x = tokens.reshape(1, -1) - else: - x = tokens - output_data, inference_state = await self.infer_tensor(request_id, shard, x, inference_state) - - return output_data, inference_state - - -inference_engine_classes = { - "mlx": "MLXDynamicShardInferenceEngine", - "tinygrad": "TinygradDynamicShardInferenceEngine", - "dummy": "DummyInferenceEngine", -} - - -def get_inference_engine(inference_engine_name: str, shard_downloader: ShardDownloader): - if DEBUG >= 2: - print(f"get_inference_engine called with: {inference_engine_name}") - if inference_engine_name == "mlx": - from exo.inference.mlx.sharded_inference_engine import MLXDynamicShardInferenceEngine - - return MLXDynamicShardInferenceEngine(shard_downloader) - elif inference_engine_name == "tinygrad": - from exo.inference.tinygrad.inference import TinygradDynamicShardInferenceEngine - import tinygrad.helpers - tinygrad.helpers.DEBUG.value = int(os.getenv("TINYGRAD_DEBUG", default="0")) - - return TinygradDynamicShardInferenceEngine(shard_downloader) - elif inference_engine_name == "dummy": - from exo.inference.dummy_inference_engine import DummyInferenceEngine - return DummyInferenceEngine() - raise ValueError(f"Unsupported inference engine: {inference_engine_name}") diff --git a/exo/inference/mlx/__init__.py b/exo/inference/mlx/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/exo/inference/mlx/losses.py b/exo/inference/mlx/losses.py deleted file mode 100644 index 1052caa7..00000000 --- a/exo/inference/mlx/losses.py +++ /dev/null @@ -1,37 +0,0 @@ -import mlx.core as mx -import mlx.nn as nn -def length_masked_ce_loss(model, inputs, targets, lengths): - # Run model on inputs - logits = model(inputs).astype(mx.float32) - - # Mask padding tokens - length_mask = mx.arange(inputs.shape[1])[None, :] < lengths[:, None] - - # Calculate the loss - ce = nn.losses.cross_entropy(logits, targets) * length_mask - loss = ce.sum() / length_mask.sum() -# print(f"| {inputs=}\n| ==>{logits=}\n| ~^~{ce=}\n| == {loss=}") - return loss - -#Naive intermediate layer loss, where we replace the targets with gradients and just multiply the output by the gradients to derive the loss. This is naive and may warrant some further iteration, but will do the job for now -def back_gradient_loss(model, inputs, gradients, lengths): - out = model(inputs).astype(mx.float32) - grad = gradients.astype(mx.float32) - - # Mask padding tokens - length_mask = mx.repeat(mx.arange(inputs.shape[1])[None, :] < lengths[:, None], out.shape[-1]).reshape(out.shape) - - masked_sum = (out * length_mask).sum(axis=1) - gradient_lens = mx.abs(grad * masked_sum) - loss = gradient_lens.sum() / length_mask.sum() -# print(f"| {inputs=}\n" -# + f"| ==>{out=}\n" -# + f"| ~^~{masked_sum=}\n" -# + f"| <~>{gradient_lens=}\n" -# + f"| == {loss=}") - return loss - -loss_fns = { - "back_gradient": back_gradient_loss, - "length_masked_ce": length_masked_ce_loss, -} diff --git a/exo/inference/mlx/models/StableDiffusionPipeline.py b/exo/inference/mlx/models/StableDiffusionPipeline.py deleted file mode 100644 index f8e7c054..00000000 --- a/exo/inference/mlx/models/StableDiffusionPipeline.py +++ /dev/null @@ -1,307 +0,0 @@ -# Adapted from https://github.com/ml-explore/mlx-examples/blob/main/stable_diffusion/stable_diffusion/__init__.py - -import time -from typing import Optional, Tuple -import inspect - -import mlx.core as mx -import mlx.nn as nn -from pathlib import Path - -from tqdm import tqdm - -from .sd_models.vae import ModelArgs as VAEArgs -from .sd_models.vae import Autoencoder -from .sd_models.tokenizer import load_tokenizer -from .sd_models.clip import CLIPTextModel -from .sd_models.clip import ModelArgs as CLIPArgs -from .sd_models.unet import UNetConfig, UNetModel - -from dataclasses import dataclass, field -from exo.inference.shard import Shard - -@dataclass -class DiffusionConfig: - beta_schedule: str = "scaled_linear" - beta_start: float = 0.00085 - beta_end: float = 0.012 - num_train_steps: int = 1000 - - @classmethod - def from_dict(cls, params): - return cls(**{k: v for k, v in params.items() if k in inspect.signature(cls).parameters}) - - -#Sampler -def _linspace(a, b, num): - x = mx.arange(0, num) / (num - 1) - return (b - a) * x + a - - -def _interp(y, x_new): - """Interpolate the function defined by (arange(0, len(y)), y) at positions x_new.""" - x_low = x_new.astype(mx.int32) - x_high = mx.minimum(x_low + 1, len(y) - 1) - - y_low = y[x_low] - y_high = y[x_high] - delta_x = x_new - x_low - y_new = y_low * (1 - delta_x) + delta_x * y_high - - return y_new - -class SimpleEulerSampler: - """A simple Euler integrator that can be used to sample from our diffusion models. - - The method ``step()`` performs one Euler step from x_t to x_t_prev. - """ - - def __init__(self, config: DiffusionConfig): - # Compute the noise schedule - if config.beta_schedule == "linear": - betas = _linspace( - config.beta_start, config.beta_end, config.num_train_steps - ) - elif config.beta_schedule == "scaled_linear": - betas = _linspace( - config.beta_start**0.5, config.beta_end**0.5, config.num_train_steps - ).square() - else: - raise NotImplementedError(f"{config.beta_schedule} is not implemented.") - - alphas = 1 - betas - alphas_cumprod = mx.cumprod(alphas) - - self._sigmas = mx.concatenate( - [mx.zeros(1), ((1 - alphas_cumprod) / alphas_cumprod).sqrt()] - ) - - @property - def max_time(self): - return len(self._sigmas) - 1 - - def sample_prior(self, shape, dtype=mx.float32, key=None): - noise = mx.random.normal(shape, key=key) - return ( - noise * self._sigmas[-1] * (self._sigmas[-1].square() + 1).rsqrt() - ).astype(dtype) - - def add_noise(self, x, t, key=None): - noise = mx.random.normal(x.shape, key=key) - s = self.sigmas(t) - return (x + noise * s) * (s.square() + 1).rsqrt() - - def sigmas(self, t): - return _interp(self._sigmas, t) - - def timesteps(self, num_steps: int, start_time=None, dtype=mx.float32): - start_time = start_time or (len(self._sigmas) - 1) - assert 0 < start_time <= (len(self._sigmas) - 1) - steps = _linspace(start_time, 0, num_steps + 1).astype(dtype) - return list(zip(steps, steps[1:])) - - def current_timestep(self, step, total_steps, start_time=None): - if step < total_steps: - steps = self.timesteps(total_steps, start_time) - return steps[step] - else: - return mx.array(0),mx.array(0) - - def step(self, eps_pred, x_t, t, t_prev): - sigma = self.sigmas(t).astype(eps_pred.dtype) - sigma_prev = self.sigmas(t_prev).astype(eps_pred.dtype) - - dt = sigma_prev - sigma - x_t_prev = (sigma.square() + 1).sqrt() * x_t + eps_pred * dt - - x_t_prev = x_t_prev * (sigma_prev.square() + 1).rsqrt() - - return x_t_prev - -@dataclass -class ShardConfig: - model_id:str - start_layer:int - end_layer:int - n_layers:int - -@dataclass -class StableDiffusionConfig: - model_type:str - vae:VAEArgs - text_encoder:CLIPArgs - scheduler:DiffusionConfig - unet:UNetConfig - shard:ShardConfig - - @classmethod - def from_dict(cls, params): - return cls(**{k: v for k, v in params.items() if k in inspect.signature(cls).parameters}) - -@dataclass -class ModelArgs(StableDiffusionConfig): - shard:Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - if isinstance(self.shard, dict): - self.shard = Shard(**self.shard) - - if not isinstance(self.shard, Shard): - raise TypeError(f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead") - - -class Model(nn.Module): - def __init__(self, config): - super().__init__() - self.model_type = config.model_type - self.config = config - self.model_path = config.vae['path'].split('/vae')[0] - self.shard = config.shard - self.shard_clip, self.shard_encoder, self.shard_unet, self.shard_decoder = model_shards(config.shard) - self.config_clip=CLIPArgs.from_dict(config.text_encoder['config']) - if self.shard_clip.start_layer != -1: - self.text_encoder = CLIPTextModel(self.config_clip, shard=self.shard_clip) - else: - self.text_encoder = nn.Identity() - self.tokenizer = load_tokenizer(Path(self.model_path), "vocab.json", "merges.txt") - self.diffusion_config = DiffusionConfig.from_dict(config.scheduler['config']) - self.sampler = SimpleEulerSampler(self.diffusion_config) - if self.shard_unet.start_layer!=-1: - self.config_unet = UNetConfig.from_dict(config.unet['config']) - self.unet = UNetModel(self.config_unet, self.shard_unet) - else: - self.unet = nn.Identity() - self.config_vae=VAEArgs.from_dict(config.vae['config']) - if self.shard_encoder.start_layer != -1: - self.encoder=Autoencoder(self.config_vae, self.shard_encoder, "vae_encoder") - else: - self.encoder = nn.Identity() - if self.shard_decoder.start_layer != -1: - self.decoder=Autoencoder(self.config_vae, self.shard_decoder, "vae_decoder") - else: - self.decoder = nn.Identity() - - def __call__(self,x, step= 0, cfg_weight: float = 7.5,total_steps=50,conditioning=None,mask=None,residual=None,x_t_prev=None,is_finished=False,is_step_finished=False, image=None, strength=0.65, start_step=None): - t, t_prev = self.sampler.current_timestep(step=step, total_steps=total_steps, start_time=start_step) - is_finished = False - is_step_finished = False - if t.item()==1000: - if self.shard_clip.start_layer == 0: - conditioning = x - if self.shard_clip.start_layer != -1: - conditioning, mask= self.text_encoder(conditioning,mask) - seed = int(time.time()) - mx.random.seed(seed) - if image is None: - if self.shard_encoder.is_last_layer(): - x = self.sampler.sample_prior((1, *(64, 64), self.config_vae.latent_channels_in), dtype=mx.float32) - x_t_prev=x - start_step = self.sampler.max_time - else: - if self.shard_encoder.start_layer != -1: - image= self.encoder.encode(image) - if self.shard_encoder.is_last_layer(): - start_step = self.sampler.max_time*strength - total_steps = int(total_steps*strength) - image = mx.broadcast_to(image, (1,) + image.shape[1:]) - x_t_prev=self.sampler.add_noise(image, mx.array(start_step)) - image = None - t, t_prev = self.sampler.current_timestep(step=step, total_steps=total_steps, start_time=start_step) - # Perform the denoising loop - if self.shard_unet.start_layer != -1: - with tqdm(total=total_steps,initial=step+1) as pbar: - if step 1 else x - else: - x_t_unet = x - t_unet = mx.broadcast_to(t, [len(x_t_unet)]) - x, residual= self.unet(x_t_unet, t_unet, encoder_x=conditioning, residuals=residual) - if self.shard_unet.is_last_layer(): - if cfg_weight > 1: - eps_text, eps_neg = x.split(2) - eps_pred = eps_neg + cfg_weight * (eps_text - eps_neg) - x = self.sampler.step(eps_pred, x_t_prev, t, t_prev) - x_t_prev=x - mx.eval(x) - - if self.shard_decoder.is_last_layer(): - is_step_finished=True - if self.shard_decoder.start_layer != -1: - x=self.decoder.decode(x) - if self.shard_decoder.is_last_layer(): - x = mx.clip(x / 2 + 0.5, 0, 1) - B, H, W, C = x.shape - x = x.reshape(1, B // 1, H, W, C).transpose(0, 2, 1, 3, 4) - x = x.reshape(1 * H, B // 1 * W, C) - x = (x * 255).astype(mx.uint8) - if t_prev.item() ==0: - is_finished=True - mx.eval(x) - - return x, {'conditioning':conditioning, 'mask':mask,'residual':residual,'x_t_prev':x_t_prev,'is_finished':is_finished,'is_step_finished':is_step_finished, 'step':step, 'total_steps':total_steps, 'start_step':start_step, 'image':image} - - - def load(self): - if self.shard_encoder.start_layer != -1: - vae_weights = mx.load(self.config_vae.weight_files[0]) - vae_weights = self.encoder.sanitize(vae_weights) - self.encoder.load_weights(list(vae_weights.items()), strict=True) - if self.shard_decoder.start_layer != -1: - vae_weights = mx.load(self.config_vae.weight_files[0]) - vae_weights = self.decoder.sanitize(vae_weights) - self.decoder.load_weights(list(vae_weights.items()), strict=True) - if self.shard_clip.start_layer != -1: - clip_weights = mx.load(self.config_clip.weight_files[0]) - clip_weights = self.text_encoder.sanitize(clip_weights) - self.text_encoder.load_weights(list(clip_weights.items()), strict=True) - if self.shard_unet.start_layer !=-1: - unet_weights = mx.load(self.config_unet.weight_files[0]) - unet_weights = self.unet.sanitize(unet_weights) - self.unet.load_weights(list(unet_weights.items()), strict=True) - -def model_shards(shard:ShardConfig): - def create_shard(shard, model_ranges): - start_layer = shard.start_layer - end_layer = shard.end_layer - - shards = {} - - for model_name, (range_start, range_end) in model_ranges.items(): - if start_layer < range_end and end_layer >= range_start: - # Calculate the overlap with the model range - overlap_start = max(start_layer, range_start) - overlap_end = min(end_layer, range_end - 1) - - # Adjust the layers relative to the model's range - relative_start = overlap_start - range_start - relative_end = overlap_end - range_start - shards[model_name] = Shard(model_name, relative_start, relative_end, range_end - range_start) - else: - # If no overlap, create a zero-layer shard - shards[model_name] = Shard(model_name, -1, -1, range_end - range_start) - - return shards - - # Define the ranges for different models - model_ranges = { - 'clip': (0, 12), - 'vae_encoder':(12,17), - 'unet':(17,26), - 'vae_decoder': (26, 31) # Example range for unet - } - - # Call the function and get the shards for all models - shards = create_shard(shard, model_ranges) - - # Access individual shards - shard_clip = shards['clip'] - shard_encoder = shards['vae_encoder'] - shard_unet = shards['unet'] - shard_decoder = shards['vae_decoder'] - - return shard_clip, shard_encoder, shard_unet, shard_decoder - - - diff --git a/exo/inference/mlx/models/__init__.py b/exo/inference/mlx/models/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/exo/inference/mlx/models/base.py b/exo/inference/mlx/models/base.py deleted file mode 100644 index 5181adfd..00000000 --- a/exo/inference/mlx/models/base.py +++ /dev/null @@ -1,9 +0,0 @@ -from typing import Optional -import mlx.core as mx -import mlx.nn as nn -from mlx_lm.models.cache import KVCache - - -class IdentityBlock(nn.Module): - def __call__(self, x: mx.array, mask: Optional[mx.array] = None, cache: Optional[KVCache] = None) -> mx.array: - return x diff --git a/exo/inference/mlx/models/deepseek_v2.py b/exo/inference/mlx/models/deepseek_v2.py deleted file mode 100644 index 8952e9e6..00000000 --- a/exo/inference/mlx/models/deepseek_v2.py +++ /dev/null @@ -1,127 +0,0 @@ -from dataclasses import dataclass, field -from typing import Optional - -import mlx.core as mx -import mlx.nn as nn - -from mlx_lm.models.cache import KVCache -from mlx_lm.models.deepseek_v2 import ModelArgs, DeepseekV2DecoderLayer -from .base import IdentityBlock -from exo.inference.shard import Shard - - -@dataclass -class ModelArgs(ModelArgs): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - if isinstance(self.shard, Shard): - return - if not isinstance(self.shard, dict): - raise TypeError(f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead") - - self.shard = Shard(**self.shard) - - -class DeepseekV2Model(nn.Module): - def __init__(self, config: ModelArgs): - super().__init__() - self.args = config - self.num_hidden_layers = config.num_hidden_layers - self.vocab_size = config.vocab_size - if self.args.shard.is_first_layer(): - self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) - - self.layers = [] - for i in range(self.num_hidden_layers): - if self.args.shard.start_layer <= i <= self.args.shard.end_layer: - self.layers.append(DeepseekV2DecoderLayer(config, i)) - else: - self.layers.append(IdentityBlock()) - - if self.args.shard.is_last_layer(): - self.norm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps) - - def __call__( - self, - x: mx.array, - cache: Optional[KVCache] = None, - ) -> mx.array: - if self.args.shard.is_first_layer(): - h = self.embed_tokens(x) - else: - h = x - - mask = None - T = h.shape[1] - if T > 1: - mask = nn.MultiHeadAttention.create_additive_causal_mask(T) - mask = mask.astype(h.dtype) - - if cache is None: - cache = [None]*len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, c) - - if self.args.shard.is_last_layer(): - h = self.norm(h) - return h - - -class Model(nn.Module): - def __init__(self, config: ModelArgs): - super().__init__() - self.args = config - self.model_type = config.model_type - self.model = DeepseekV2Model(config) - if self.args.shard.is_last_layer(): - self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) - - def __call__( - self, - inputs: mx.array, - cache: Optional[KVCache] = None, - ): - out = self.model(inputs, cache) - if self.args.shard.is_last_layer(): - return self.lm_head(out) - return out - - def sanitize(self, weights): - shard_state_dict = {} - - for key, value in weights.items(): - if key.startswith('model.layers.'): - layer_num = int(key.split('.')[2]) - if self.args.shard.start_layer <= layer_num <= self.args.shard.end_layer: - shard_state_dict[key] = value - elif self.args.shard.is_first_layer() and key.startswith('model.embed_tokens'): - shard_state_dict[key] = value - elif self.args.shard.is_last_layer() and (key.startswith('model.norm') or key.startswith('lm_head')): - shard_state_dict[key] = value - - for l in range(self.args.num_hidden_layers): - prefix = f"model.layers.{l}" - for n, m in [("w1", "gate_proj"), ("w2", "down_proj"), ("w3", "up_proj")]: - for k in ["weight", "scales", "biases"]: - if f"{prefix}.mlp.experts.0.{m}.{k}" in shard_state_dict: - to_join = [shard_state_dict.pop(f"{prefix}.mlp.experts.{e}.{m}.{k}") for e in range(self.args.n_routed_experts)] - shard_state_dict[f"{prefix}.mlp.switch_mlp.{m}.{k}"] = mx.stack(to_join) - - return shard_state_dict - - @property - def layers(self): - return self.model.layers - - @property - def head_dim(self): - return ( - self.args.qk_nope_head_dim + self.args.qk_rope_head_dim, - self.args.v_head_dim, - ) - - @property - def n_kv_heads(self): - return self.args.num_key_value_heads diff --git a/exo/inference/mlx/models/deepseek_v3.py b/exo/inference/mlx/models/deepseek_v3.py deleted file mode 100644 index bf36a86b..00000000 --- a/exo/inference/mlx/models/deepseek_v3.py +++ /dev/null @@ -1,134 +0,0 @@ -from dataclasses import dataclass, field -from typing import Optional - -import mlx.core as mx -import mlx.nn as nn - -from mlx_lm.models.cache import KVCache -from mlx_lm.models.deepseek_v3 import ( - ModelArgs as V3ModelArgs, - DeepseekV3DecoderLayer, -) -from .base import IdentityBlock -from exo.inference.shard import Shard - - -@dataclass -class ModelArgs(V3ModelArgs): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - if isinstance(self.shard, Shard): - return - if not isinstance(self.shard, dict): - raise TypeError(f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead") - - self.shard = Shard(**self.shard) - - -class DeepseekV3Model(nn.Module): - def __init__(self, config: ModelArgs): - super().__init__() - self.args = config - self.num_hidden_layers = config.num_hidden_layers - self.vocab_size = config.vocab_size - if self.args.shard.is_first_layer(): - self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) - - self.layers = [] - for i in range(self.num_hidden_layers): - if self.args.shard.start_layer <= i <= self.args.shard.end_layer: - self.layers.append(DeepseekV3DecoderLayer(config, i)) - else: - self.layers.append(IdentityBlock()) - - if self.args.shard.is_last_layer(): - self.norm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps) - - def __call__( - self, - x: mx.array, - cache: Optional[KVCache] = None, - ) -> mx.array: - if self.args.shard.is_first_layer(): - h = self.embed_tokens(x) - else: - h = x - - mask = None - T = h.shape[1] - if T > 1: - mask = nn.MultiHeadAttention.create_additive_causal_mask(T) - mask = mask.astype(h.dtype) - - if cache is None: - cache = [None]*len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, c) - - if self.args.shard.is_last_layer(): - h = self.norm(h) - return h - - -class Model(nn.Module): - def __init__(self, config: ModelArgs): - super().__init__() - self.args = config - self.model_type = config.model_type - self.model = DeepseekV3Model(config) - if self.args.shard.is_last_layer(): - self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) - - def __call__( - self, - inputs: mx.array, - cache: Optional[KVCache] = None, - ): - out = self.model(inputs, cache) - if self.args.shard.is_last_layer(): - return self.lm_head(out) - return out - - def sanitize(self, weights): - shard_state_dict = {} - - for key, value in weights.items(): - if key.startswith('model.layers.'): - layer_num = int(key.split('.')[2]) - if self.args.shard.start_layer <= layer_num <= self.args.shard.end_layer: - shard_state_dict[key] = value - elif self.args.shard.is_first_layer() and key.startswith('model.embed_tokens'): - shard_state_dict[key] = value - elif self.args.shard.is_last_layer() and (key.startswith('model.norm') or key.startswith('lm_head')): - shard_state_dict[key] = value - - for l in range(self.args.num_hidden_layers): - prefix = f"model.layers.{l}" - for n, m in [("w1", "gate_proj"), ("w2", "down_proj"), ("w3", "up_proj")]: - for k in ["weight", "scales", "biases"]: - expert_key = f"{prefix}.mlp.experts.0.{m}.{k}" - if expert_key in shard_state_dict: - to_join = [ - shard_state_dict.pop(f"{prefix}.mlp.experts.{e}.{m}.{k}") - for e in range(self.args.n_routed_experts) - ] - shard_state_dict[f"{prefix}.mlp.switch_mlp.{m}.{k}"] = mx.stack(to_join) - - return shard_state_dict - - @property - def layers(self): - return self.model.layers - - @property - def head_dim(self): - return ( - self.args.qk_nope_head_dim + self.args.qk_rope_head_dim, - self.args.v_head_dim, - ) - - @property - def n_kv_heads(self): - return self.args.num_key_value_heads diff --git a/exo/inference/mlx/models/gemma2.py b/exo/inference/mlx/models/gemma2.py deleted file mode 100644 index 88957fd9..00000000 --- a/exo/inference/mlx/models/gemma2.py +++ /dev/null @@ -1,118 +0,0 @@ -from dataclasses import dataclass, field - -import mlx.core as mx -import mlx.nn as nn - -from mlx_lm.models.base import create_attention_mask -from mlx_lm.models.gemma2 import TransformerBlock, ModelArgs, RMSNorm - -from ...shard import Shard -from .base import IdentityBlock - - -@dataclass -class ModelArgs(ModelArgs): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - if isinstance(self.shard, Shard): - return - if not isinstance(self.shard, dict): - raise TypeError(f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead") - - self.shard = Shard(**self.shard) - - -class GemmaModel(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.vocab_size = args.vocab_size - self.num_hidden_layers = args.num_hidden_layers - assert self.vocab_size > 0 - if args.shard.is_first_layer() or args.shard.is_last_layer(): - self.embed_tokens = nn.Embedding(args.vocab_size, args.hidden_size) - self.layers = [] - for i in range(self.num_hidden_layers): - if args.shard.start_layer <= i <= args.shard.end_layer: - self.layers.append(TransformerBlock(args=args)) - else: - self.layers.append(IdentityBlock()) - if args.shard.is_last_layer(): - self.norm = RMSNorm(args.hidden_size, eps=args.rms_norm_eps) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - if self.args.shard.is_first_layer(): - h = self.embed_tokens(inputs) - h = h * (self.args.hidden_size**0.5) - else: - h = inputs - - mask = None - if h.ndim > 1 and h.shape[1] > 1: - mask = create_attention_mask(h, cache) - - if cache is None: - cache = [None]*len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, cache=c) - - if self.args.shard.is_last_layer(): - h = self.norm(h) - return h - - -class Model(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.model_type = args.model_type - self.model = GemmaModel(args) - if args.shard.is_last_layer(): - self.final_logit_softcapping = args.final_logit_softcapping - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - out = self.model(inputs, cache) - if self.args.shard.is_last_layer(): - out = self.model.embed_tokens.as_linear(out) - out = mx.tanh(out / self.final_logit_softcapping) - out = out * self.final_logit_softcapping - return out - - def sanitize(self, weights): - shard_state_dict = {} - - for key, value in weights.items(): - if "self_attn.rotary_emb.inv_freq" in key: - continue - if key.startswith('model.layers.'): - layer_num = int(key.split('.')[2]) - if self.args.shard.start_layer <= layer_num <= self.args.shard.end_layer: - shard_state_dict[key] = value - elif (self.args.shard.is_first_layer() or self.args.shard.is_last_layer()) and key.startswith('model.embed_tokens'): - shard_state_dict[key] = value - elif self.args.shard.is_last_layer() and (key.startswith('model.norm')): - shard_state_dict[key] = value - - return shard_state_dict - - @property - def layers(self): - return self.model.layers - - @property - def head_dim(self): - return self.args.head_dim - - @property - def n_kv_heads(self): - return self.args.num_key_value_heads diff --git a/exo/inference/mlx/models/llama.py b/exo/inference/mlx/models/llama.py deleted file mode 100644 index 8b069ecc..00000000 --- a/exo/inference/mlx/models/llama.py +++ /dev/null @@ -1,125 +0,0 @@ -from dataclasses import dataclass, field - -import mlx.core as mx -import mlx.nn as nn - -from mlx_lm.models.base import create_attention_mask -from mlx_lm.models.llama import TransformerBlock, ModelArgs - -from ...shard import Shard -from .base import IdentityBlock - - -@dataclass -class ModelArgs(ModelArgs): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - super().__post_init__() # Ensure parent initializations are respected - - if isinstance(self.shard, Shard): - return - if not isinstance(self.shard, dict): - raise TypeError(f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead") - - self.shard = Shard(**self.shard) - - -class LlamaModel(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.vocab_size = args.vocab_size - self.num_hidden_layers = args.num_hidden_layers - assert self.vocab_size > 0 - if args.shard.is_first_layer() or (args.shard.is_last_layer() and args.tie_word_embeddings): - self.embed_tokens = nn.Embedding(args.vocab_size, args.hidden_size) - self.layers = [] - for i in range(self.num_hidden_layers): - if args.shard.start_layer <= i <= args.shard.end_layer: - self.layers.append(TransformerBlock(args=args)) - else: - self.layers.append(IdentityBlock()) - if args.shard.is_last_layer(): - self.norm = nn.RMSNorm(args.hidden_size, eps=args.rms_norm_eps) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - if self.args.shard.is_first_layer(): - h = self.embed_tokens(inputs) - else: - h = inputs - - mask = None - if h.ndim > 1 and h.shape[1] > 1: - mask = create_attention_mask(h, cache) - - if cache is None: - cache = [None]*len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, cache=c) - - if self.args.shard.is_last_layer(): - h = self.norm(h) - return h - - -class Model(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.model_type = args.model_type - self.model = LlamaModel(args) - if args.shard.is_last_layer(): - if not args.tie_word_embeddings: - self.lm_head = nn.Linear(args.hidden_size, args.vocab_size, bias=False) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - out = self.model(inputs, cache) - if self.args.shard.is_last_layer(): - if self.args.tie_word_embeddings: - out = self.model.embed_tokens.as_linear(out) - else: - out = self.lm_head(out) - return out - - def sanitize(self, weights): - shard_state_dict = {} - - for key, value in weights.items(): - if "self_attn.rotary_emb.inv_freq" in key: - continue - if key.startswith('model.layers.'): - layer_num = int(key.split('.')[2]) - if self.args.shard.start_layer <= layer_num <= self.args.shard.end_layer: - shard_state_dict[key] = value - elif self.args.shard.is_first_layer() and key.startswith('model.embed_tokens'): - shard_state_dict[key] = value - elif (self.args.shard.is_last_layer() and self.args.tie_word_embeddings) and key.startswith('model.embed_tokens'): - shard_state_dict[key] = value - elif (self.args.shard.is_last_layer() and not self.args.tie_word_embeddings) and key.startswith('lm_head'): - shard_state_dict[key] = value - elif self.args.shard.is_last_layer() and (key.startswith('model.norm')): - shard_state_dict[key] = value - - return shard_state_dict - - @property - def layers(self): - return self.model.layers - - @property - def head_dim(self): - return (self.args.head_dim or self.args.hidden_size // self.args.num_attention_heads) - - @property - def n_kv_heads(self): - return self.args.num_key_value_heads diff --git a/exo/inference/mlx/models/llava.py b/exo/inference/mlx/models/llava.py deleted file mode 100644 index b734b09b..00000000 --- a/exo/inference/mlx/models/llava.py +++ /dev/null @@ -1,585 +0,0 @@ -# Copyright © 2024 Apple Inc. - -import math -import inspect -from dataclasses import dataclass, field -from typing import Optional, Dict, Union - -import mlx.core as mx -import mlx.nn as nn -from mlx_lm.models.base import BaseModelArgs, KVCache -from exo.inference.shard import Shard -from .base import IdentityBlock -import numpy as np - - -@dataclass -class VisionConfig: - model_type: str - num_hidden_layers: int = 24 - hidden_size: int = 1024 - intermediate_size: int = 4096 - num_attention_heads: int = 16 - image_size: int = 336 - patch_size: int = 14 - projection_dim: int = 768 - vocab_size: int = 32000 - num_channels: int = 3 - layer_norm_eps: float = 1e-5 - - @classmethod - def from_dict(cls, params): - return cls(**{k: v for k, v in params.items() if k in inspect.signature(cls).parameters}) - - -class VisionAttention(nn.Module): - def __init__( - self, - dims: int, - num_heads: int, - query_input_dims: Optional[int] = None, - key_input_dims: Optional[int] = None, - value_input_dims: Optional[int] = None, - value_dims: Optional[int] = None, - value_output_dims: Optional[int] = None, - bias: bool = False, - ): - super().__init__() - - if (dims % num_heads) != 0: - raise ValueError("The input feature dimensions should be divisible by the " - f"number of heads ({dims} % {num_heads}) != 0") - - query_input_dims = query_input_dims or dims - key_input_dims = key_input_dims or dims - value_input_dims = value_input_dims or key_input_dims - value_dims = value_dims or dims - value_output_dims = value_output_dims or dims - - self.num_heads = num_heads - self.q_proj = nn.Linear(query_input_dims, dims, bias=bias) - self.k_proj = nn.Linear(key_input_dims, dims, bias=bias) - self.v_proj = nn.Linear(value_input_dims, value_dims, bias=bias) - self.out_proj = nn.Linear(value_dims, value_output_dims, bias=bias) - - def __call__(self, queries, keys, values, mask=None): - queries = self.q_proj(queries) - keys = self.k_proj(keys) - values = self.v_proj(values) - - num_heads = self.num_heads - B, L, D = queries.shape - _, S, _ = keys.shape - queries = queries.reshape(B, L, num_heads, -1).transpose(0, 2, 1, 3) - keys = keys.reshape(B, S, num_heads, -1).transpose(0, 2, 3, 1) - values = values.reshape(B, S, num_heads, -1).transpose(0, 2, 1, 3) - - scale = math.sqrt(1/queries.shape[-1]) - scores = (queries*scale) @ keys - if mask is not None: - scores = scores + mask.astype(scores.dtype) - scores = mx.softmax(scores, axis=-1) - values_hat = (scores @ values).transpose(0, 2, 1, 3).reshape(B, L, -1) - - return self.out_proj(values_hat) - - -class VisionMLP(nn.Module): - def __init__(self, config: VisionConfig): - super().__init__() - self.activation_fn = nn.GELU(approx="fast") - self.fc1 = nn.Linear(config.hidden_size, config.intermediate_size) - self.fc2 = nn.Linear(config.intermediate_size, config.hidden_size) - - def __call__(self, x: mx.array) -> mx.array: - x = self.activation_fn(self.fc1(x)) - x = self.fc2(x) - return x - - -class VisionEncoderLayer(nn.Module): - def __init__(self, config: VisionConfig): - super().__init__() - self.embed_dim = config.hidden_size - self.self_attn = VisionAttention(config.hidden_size, config.num_attention_heads, bias=True) - self.layer_norm1 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps) - self.mlp = VisionMLP(config) - self.layer_norm2 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps) - - def __call__(self, x: mx.array, mask: Optional[mx.array] = None) -> mx.array: - y = self.layer_norm1(x) - y = self.self_attn(y, y, y, mask) - x = x + y - y = self.layer_norm2(x) - y = self.mlp(y) - return x + y - - -class VisionEncoder(nn.Module): - def __init__(self, config: VisionConfig): - super().__init__() - self.layers = [VisionEncoderLayer(config) for _ in range(config.num_hidden_layers)] - - -class VisionEmbeddings(nn.Module): - def __init__(self, config: VisionConfig): - super().__init__() - self.config = config - self.embed_dim = config.hidden_size - self.image_size = config.image_size - self.patch_size = config.patch_size - - self.class_embedding = mx.zeros((config.hidden_size,)) - - self.patch_embedding = nn.Conv2d( - in_channels=config.num_channels, - out_channels=self.embed_dim, - kernel_size=self.patch_size, - stride=self.patch_size, - bias=False, - ) - - self.num_patches = (self.image_size // self.patch_size)**2 - self.num_positions = self.num_patches + 1 - self.position_embedding = nn.Embedding(self.num_positions, self.embed_dim) - - def __call__(self, x: mx.array) -> mx.array: - batch_size = x.shape[0] - patch_embeddings = self.patch_embedding(x) - patch_embeddings = mx.flatten(patch_embeddings, start_axis=1, end_axis=2) - embed_dim = patch_embeddings.shape[-1] - cls_embeddings = mx.broadcast_to(self.class_embedding, (batch_size, 1, embed_dim)) - embeddings = mx.concatenate((cls_embeddings, patch_embeddings), axis=1) - embeddings += self.position_embedding.weight - return embeddings - - -class ClipVisionModel(nn.Module): - def __init__(self, config: VisionConfig): - super().__init__() - self.embeddings = VisionEmbeddings(config) - self.pre_layrnorm = nn.LayerNorm(config.hidden_size) - self.encoder = VisionEncoder(config) - self.post_layernorm = nn.LayerNorm(config.hidden_size) - - def __call__( - self, - x: mx.array, - output_hidden_states: Optional[bool] = None, - ) -> mx.array: - x = self.embeddings(x) - x = self.pre_layrnorm(x) - - encoder_states = (x,) if output_hidden_states else None - - for l in self.encoder.layers: - x = l(x, mask=None) - if output_hidden_states: - encoder_states = encoder_states + (x,) - - pooler_output = self.post_layernorm(x[:, 0, :]) - return pooler_output, x, encoder_states - - -class VisionModel(nn.Module): - def __init__(self, config: VisionConfig): - super().__init__() - - self.model_type = config.model_type - if self.model_type != "clip_vision_model": - raise ValueError(f"Unsupported model type: {self.model_type}") - - self.vision_model = ClipVisionModel(config) - - def __call__(self, x: mx.array, output_hidden_states: Optional[bool] = None) -> mx.array: - return self.vision_model(x, output_hidden_states) - - def sanitize(self, weights): - sanitized_weights = {} - for k, v in weights.items(): - if "position_ids" in k: - # Remove unused position_ids - continue - elif "patch_embedding.weight" in k: - # PyTorch conv2d weight tensors have shape: - # [out_channels, in_channels, kH, KW] - # MLX conv2d expects the weight be of shape: - # [out_channels, kH, KW, in_channels] - sanitized_weights[k] = v.transpose(0, 2, 3, 1) - else: - sanitized_weights[k] = v - - return sanitized_weights - - -@dataclass -class TextConfig: - model_type: str - hidden_size: int = 4096 - num_hidden_layers: int = 32 - intermediate_size: int = 11008 - num_attention_heads: int = 32 - head_dim: int = None - rms_norm_eps: float = 1e-6 - vocab_size: int = 32000 - num_key_value_heads: int = None - rope_theta: float = 10000 - rope_traditional: bool = False - rope_scaling: Optional[Dict[str, Union[float, str]]] = None - - @classmethod - def from_dict(cls, params): - return cls(**{k: v for k, v in params.items() if k in inspect.signature(cls).parameters}) - - def __post_init__(self): - if self.num_key_value_heads is None: - self.num_key_value_heads = self.num_attention_heads - - if self.head_dim is None: - self.head_dim = self.hidden_size // self.num_attention_heads - - if self.model_type is None: - self.model_type = "llama" - - if self.rope_scaling: - required_keys = {"factor", "type"} - if not all(key in self.rope_scaling for key in required_keys): - raise ValueError(f"rope_scaling must contain keys {required_keys}") - - if self.rope_scaling["type"] != "linear": - raise ValueError("rope_scaling 'type' currently only supports 'linear'") - - -class TextAttention(nn.Module): - def __init__(self, config: TextConfig): - super().__init__() - - dim = config.hidden_size - self.n_heads = n_heads = config.num_attention_heads - self.n_kv_heads = n_kv_heads = config.num_key_value_heads - - self.repeats = n_heads // n_kv_heads - - head_dim = config.hidden_size // n_heads - self.scale = head_dim**-0.5 - - self.q_proj = nn.Linear(dim, n_heads*head_dim, bias=False) - self.k_proj = nn.Linear(dim, n_kv_heads*head_dim, bias=False) - self.v_proj = nn.Linear(dim, n_kv_heads*head_dim, bias=False) - self.o_proj = nn.Linear(n_heads*head_dim, dim, bias=False) - - rope_scale = (1/config.rope_scaling["factor"] if config.rope_scaling is not None and config.rope_scaling["type"] == "linear" else 1) - self.rope = nn.RoPE( - head_dim, - traditional=config.rope_traditional, - base=config.rope_theta, - scale=rope_scale, - ) - - def __call__( - self, - x: mx.array, - mask: Optional[mx.array] = None, - cache: Optional[KVCache] = None, - ) -> mx.array: - B, L, D = x.shape - - queries, keys, values = self.q_proj(x), self.k_proj(x), self.v_proj(x) - - # Prepare the queries, keys and values for the attention computation - queries = queries.reshape(B, L, self.n_heads, -1).transpose(0, 2, 1, 3) - keys = keys.reshape(B, L, self.n_kv_heads, -1).transpose(0, 2, 1, 3) - values = values.reshape(B, L, self.n_kv_heads, -1).transpose(0, 2, 1, 3) - - if cache is not None: - queries = self.rope(queries, offset=cache.offset) - keys = self.rope(keys, offset=cache.offset) - keys, values = cache.update_and_fetch(keys, values) - else: - queries = self.rope(queries) - keys = self.rope(keys) - - output = mx.fast.scaled_dot_product_attention(queries, keys, values, scale=self.scale, mask=mask) - output = output.transpose(0, 2, 1, 3).reshape(B, L, -1) - return self.o_proj(output) - - -class TextMLP(nn.Module): - def __init__(self, dim, hidden_dim): - super().__init__() - self.gate_proj = nn.Linear(dim, hidden_dim, bias=False) - self.down_proj = nn.Linear(hidden_dim, dim, bias=False) - self.up_proj = nn.Linear(dim, hidden_dim, bias=False) - - def __call__(self, x) -> mx.array: - return self.down_proj(nn.silu(self.gate_proj(x))*self.up_proj(x)) - - -class TransformerBlock(nn.Module): - def __init__(self, config: TextConfig): - super().__init__() - self.num_attention_heads = config.num_attention_heads - self.hidden_size = config.hidden_size - self.self_attn = TextAttention(config) - self.mlp = TextMLP(config.hidden_size, config.intermediate_size) - self.input_layernorm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps) - self.post_attention_layernorm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps) - self.config = config - - def __call__( - self, - x: mx.array, - mask: Optional[mx.array] = None, - cache: Optional[KVCache] = None, - ) -> mx.array: - r = self.self_attn(self.input_layernorm(x), mask, cache) - h = x + r - r = self.mlp(self.post_attention_layernorm(h)) - out = h + r - return out - - -class Llama(nn.Module): - def __init__(self, config: TextConfig, shard: Shard): - super().__init__() - self.config = config - self.shard = shard - self.vocab_size = config.vocab_size - self.model_type = config.model_type - self.num_hidden_layers = config.num_hidden_layers - self.num_key_value_heads = config.num_key_value_heads - self.head_dim = config.head_dim - assert self.vocab_size > 0 - if self.shard.is_first_layer(): - self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) - self.layers = [] - for i in range(self.num_hidden_layers): - if self.shard.start_layer <= i <= self.shard.end_layer: - self.layers.append(TransformerBlock(config=config)) - else: - self.layers.append(IdentityBlock()) - if self.shard.is_last_layer(): - self.norm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps) - - def __call__( - self, - inputs: mx.array, - cache=None, - inputs_embeds=None, - ): - # for passing merged input embeddings - if inputs_embeds is None: - if self.shard.is_first_layer(): - h = self.embed_tokens(inputs) - else: - h = inputs - else: - h = inputs_embeds - - mask = None - if h.shape[1] > 1: - mask = nn.MultiHeadAttention.create_additive_causal_mask(h.shape[1]) - mask = mask.astype(h.dtype) - - if cache is None: - cache = [None]*len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, c) - - if self.shard.is_last_layer(): - h = self.norm(h) - return h - - -class LanguageModel(nn.Module): - def __init__(self, config: TextConfig, shard: Shard): - super().__init__() - self.model_type = config.model_type - if self.model_type != "llama": - raise ValueError(f"Model type {self.model_type} not supported. Currently only 'llama' is supported") - self.shard = shard - self.model = Llama(config, shard) - if self.shard.is_last_layer(): - self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) - - def __call__( - self, - inputs: mx.array, - cache=None, - inputs_embeds=None, - ): - out = self.model(inputs, cache, inputs_embeds) - if self.shard.is_last_layer(): - out = self.lm_head(out) - return out - - def sanitize(self, weights): - shard_state_dict = {} - for key, value in weights.items(): - if "self_attn.rotary_emb.inv_freq" in key: - continue - - if key.startswith('language_model.model.layers.'): - layer_num = int(key.split('.')[3]) - if layer_num < self.shard.start_layer or layer_num > self.shard.end_layer: - continue - if not self.shard.is_first_layer() and key.startswith('language_model.model.embed_tokens'): - continue - elif not self.shard.is_last_layer() and (key.startswith('language_model.model.norm') or key.startswith('language_model.lm_head')): - continue - - shard_state_dict[key] = value - - return shard_state_dict - - -@dataclass -class LlaVAConfig(BaseModelArgs): - text_config: TextConfig - vision_config: VisionConfig = None - model_type: str = "llava" - ignore_index: int = -100 - image_token_index: int = 32000 - vision_feature_select_strategy: str = "default" - vision_feature_layer: int = -2 - vocab_size: int = 32000 - - @classmethod - def from_dict(cls, params): - updated_params = {} - class_params = inspect.signature(cls).parameters - for k, v in params.items(): - if k in class_params: - if k in ["text_config", "vision_config"]: - v = class_params[k].annotation.from_dict(v) - updated_params.update({k: v}) - - return cls(**updated_params) - - -@dataclass -class ModelArgs(LlaVAConfig): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - if isinstance(self.shard, dict): - self.shard = Shard(**self.shard) - - if not isinstance(self.shard, Shard): - raise TypeError(f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead") - - if not self.shard.is_first_layer(): - self.vision_config = None - - -class LlavaMultiModalProjector(nn.Module): - def __init__(self, config: LlaVAConfig): - super().__init__() - self.linear_1 = nn.Linear(config.vision_config.hidden_size, config.text_config.hidden_size, bias=True) - self.gelu = nn.GELU() - self.linear_2 = nn.Linear(config.text_config.hidden_size, config.text_config.hidden_size, bias=True) - - def __call__(self, x: mx.array) -> mx.array: - x = self.linear_1(x) - x = self.gelu(x) - x = self.linear_2(x) - return x - - -class Model(nn.Module): - def __init__(self, config: ModelArgs): - super().__init__() - self.config = config - self.model_type = config.model_type - if config.vision_config: - self.vision_tower = VisionModel(config.vision_config) - self.multi_modal_projector = LlavaMultiModalProjector(config) - self.vision_feature_layer = config.vision_feature_layer - self.vision_feature_select_strategy = config.vision_feature_select_strategy - self.language_model = LanguageModel(config.text_config, config.shard) - - def get_input_embeddings( - self, - input_ids: Optional[mx.array] = None, - pixel_values: Optional[mx.array] = None, - ): - if pixel_values is None: - return self.language_model(input_ids) - - # Get the input embeddings from the language model - inputs_embeds = self.language_model.model.embed_tokens(input_ids) - - # Get the ouptut hidden states from the vision model - *_, hidden_states = self.vision_tower(pixel_values.transpose(0, 2, 3, 1), output_hidden_states=True) - - # Select the hidden states from the desired layer - selected_image_feature = hidden_states[self.vision_feature_layer] - - if self.vision_feature_select_strategy == "default": - selected_image_feature = selected_image_feature[:, 1:] - elif self.vision_feature_select_strategy == "full": - selected_image_feature = selected_image_feature - else: - raise ValueError("Unexpected feature selection strategy: " - f"{self.vision_feature_select_strategy}") - - # Pass image features through the multi-modal projector - image_features = self.multi_modal_projector(selected_image_feature) - - # Insert special image tokens in the input_ids - final_inputs_embeds = self._merge_input_ids_with_image_features(image_features, inputs_embeds, input_ids) - return final_inputs_embeds - - def _merge_input_ids_with_image_features(self, image_features, inputs_embeds, input_ids): - image_token_index = self.config.image_token_index - num_images, num_image_patches, embed_dim = image_features.shape - - # Positions of tokens in input_ids, assuming batch size is 1 - image_positions = np.where(input_ids[0] == image_token_index)[0].tolist() - - if len(image_positions) != num_images: - raise ValueError(f"The number of image tokens ({len(image_positions)}) does not " - f" match the number of image inputs ({num_images}).") - - text_segments = [] - start_idx = 0 - - for position in image_positions: - text_segments.append(inputs_embeds[:, start_idx:position]) - start_idx = position + 1 - - image_embeddings = mx.split(image_features, image_features.shape[0]) - final_embeddings = [v for p in zip(text_segments, image_embeddings) for v in p] - final_embeddings += [inputs_embeds[:, start_idx:]] - - # Create a final embedding of shape - # (1, num_image_patches*num_images + sequence_len, embed_dim) - return mx.concatenate(final_embeddings, axis=1) - - def __call__(self, input_ids: mx.array, pixel_values: mx.array = None, cache=None): - input_embddings = None - if pixel_values is not None: - input_embddings = self.get_input_embeddings(input_ids, pixel_values) - logits = self.language_model(input_ids, cache=cache, inputs_embeds=input_embddings) - return logits - - def sanitize(self, weights): - if self.config.vision_config: - weights = self.vision_tower.sanitize(weights) - else: - weights = {k: v for k, v in weights.items() if not k.startswith(('vision_tower', 'multi_modal_projector', 'vision_feature_layer', 'vision_feature_select_strategy'))} - weights = self.language_model.sanitize(weights) - return weights - - @property - def layers(self): - return self.language_model.model.layers - - @property - def head_dim(self): - return (self.language_model.model.head_dim or self.language_model.model.hidden_size // self.language_model.model.num_attention_heads) - - @property - def n_kv_heads(self): - return self.language_model.model.num_key_value_heads diff --git a/exo/inference/mlx/models/phi3.py b/exo/inference/mlx/models/phi3.py deleted file mode 100644 index acd4114a..00000000 --- a/exo/inference/mlx/models/phi3.py +++ /dev/null @@ -1,117 +0,0 @@ -from dataclasses import dataclass, field - -import mlx.core as mx -import mlx.nn as nn - -from mlx_lm.models.base import create_attention_mask -from mlx_lm.models.phi3 import TransformerBlock, ModelArgs - -from ...shard import Shard -from .base import IdentityBlock - -@dataclass -class ModelArgs(ModelArgs): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - super().__post_init__() - - if isinstance(self.shard, Shard): - return - if not isinstance(self.shard, dict): - raise TypeError(f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead") - - self.shard = Shard(**self.shard) - -class Phi3Model(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.vocab_size = args.vocab_size - self.num_hidden_layers = args.num_hidden_layers - assert self.vocab_size > 0 - - if self.args.shard.is_first_layer(): - self.embed_tokens = nn.Embedding(args.vocab_size, args.hidden_size) - - self.layers = [] - for i in range(self.num_hidden_layers): - if self.args.shard.start_layer <= i <= self.args.shard.end_layer: - self.layers.append(TransformerBlock(args=args)) - else: - self.layers.append(IdentityBlock()) - - if self.args.shard.is_last_layer(): - self.norm = nn.RMSNorm(args.hidden_size, eps=args.rms_norm_eps) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - if self.args.shard.is_first_layer(): - h = self.embed_tokens(inputs) - else: - h = inputs - - mask = None - if h.shape[1] > 1: - mask = create_attention_mask(h, cache) - - if cache is None: - cache = [None] * len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, c) - - if self.args.shard.is_last_layer(): - h = self.norm(h) - return h - -class Model(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.model_type = args.model_type - self.model = Phi3Model(args) - if self.args.shard.is_last_layer(): - self.lm_head = nn.Linear(args.hidden_size, args.vocab_size, bias=False) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - out = self.model(inputs, cache) - if self.args.shard.is_last_layer(): - out = self.lm_head(out) - return out - - def sanitize(self, weights): - shard_state_dict = {} - - for key, value in weights.items(): - if "self_attn.rope.inv_freq" in key: - continue - if key.startswith('model.layers.'): - layer_num = int(key.split('.')[2]) - if self.args.shard.start_layer <= layer_num <= self.args.shard.end_layer: - shard_state_dict[key] = value - elif self.args.shard.is_first_layer() and key.startswith('model.embed_tokens'): - shard_state_dict[key] = value - elif self.args.shard.is_last_layer() and (key.startswith('lm_head') or key.startswith('model.norm')): - shard_state_dict[key] = value - - return shard_state_dict - - @property - def layers(self): - return self.model.layers - - @property - def head_dim(self): - return self.args.hidden_size // self.args.num_attention_heads - - @property - def n_kv_heads(self): - return self.args.num_key_value_heads diff --git a/exo/inference/mlx/models/qwen2.py b/exo/inference/mlx/models/qwen2.py deleted file mode 100644 index 492fbb27..00000000 --- a/exo/inference/mlx/models/qwen2.py +++ /dev/null @@ -1,129 +0,0 @@ -from dataclasses import dataclass, field - -import mlx.core as mx -import mlx.nn as nn - -from mlx_lm.models.base import create_attention_mask -from mlx_lm.models.qwen2 import TransformerBlock, ModelArgs - -from ...shard import Shard -from .base import IdentityBlock - -@dataclass -class ModelArgs(ModelArgs): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - super().__post_init__() - - if isinstance(self.shard, Shard): - return - if not isinstance(self.shard, dict): - raise TypeError(f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead") - - self.shard = Shard(**self.shard) - -class Qwen2Model(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.vocab_size = args.vocab_size - self.num_hidden_layers = args.num_hidden_layers - assert self.vocab_size > 0 - - if self.args.shard.is_first_layer() or (self.args.shard.is_last_layer() and args.tie_word_embeddings): - self.embed_tokens = nn.Embedding(args.vocab_size, args.hidden_size) - - self.layers = [] - for i in range(self.num_hidden_layers): - if self.args.shard.start_layer <= i <= self.args.shard.end_layer: - self.layers.append(TransformerBlock(args=args)) - else: - self.layers.append(IdentityBlock()) - - if self.args.shard.is_last_layer(): - self.norm = nn.RMSNorm(args.hidden_size, eps=args.rms_norm_eps) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - if self.args.shard.is_first_layer(): - h = self.embed_tokens(inputs) - else: - h = inputs - - mask = None - if h.shape[1] > 1: - mask = create_attention_mask(h, cache) - - if cache is None: - cache = [None]*len(self.layers) - - for layer, c in zip(self.layers, cache): - h = layer(h, mask, c) - - if self.args.shard.is_last_layer(): - h = self.norm(h) - return h - - -class Model(nn.Module): - def __init__(self, args: ModelArgs): - super().__init__() - self.args = args - self.model_type = args.model_type - self.model = Qwen2Model(args) - if self.args.shard.is_last_layer(): - if not args.tie_word_embeddings: - self.lm_head = nn.Linear(args.hidden_size, args.vocab_size, bias=False) - - def __call__( - self, - inputs: mx.array, - cache=None, - ): - out = self.model(inputs, cache) - if self.args.shard.is_last_layer(): - if self.args.tie_word_embeddings: - out = self.model.embed_tokens.as_linear(out) - else: - out = self.lm_head(out) - return out - - def sanitize(self, weights): - shard_state_dict = {} - - for key, value in weights.items(): - if "self_attn.rotary_emb.inv_freq" in key: - continue - if key.startswith('model.layers.'): - layer_num = int(key.split('.')[2]) - if self.args.shard.start_layer <= layer_num <= self.args.shard.end_layer: - shard_state_dict[key] = value - elif self.args.shard.is_first_layer() and key.startswith('model.embed_tokens'): - shard_state_dict[key] = value - elif (self.args.shard.is_last_layer() and self.args.tie_word_embeddings) and key.startswith('model.embed_tokens'): - shard_state_dict[key] = value - elif (self.args.shard.is_last_layer() and not self.args.tie_word_embeddings) and key.startswith('lm_head'): - shard_state_dict[key] = value - elif self.args.shard.is_last_layer() and (key.startswith('model.norm')): - shard_state_dict[key] = value - - if self.args.tie_word_embeddings: - shard_state_dict.pop("lm_head.weight", None) - - return shard_state_dict - - @property - def layers(self): - return self.model.layers - - @property - def head_dim(self): - return self.args.hidden_size // self.args.num_attention_heads - - @property - def n_kv_heads(self): - return self.args.num_key_value_heads diff --git a/exo/inference/mlx/models/sd_models/clip.py b/exo/inference/mlx/models/sd_models/clip.py deleted file mode 100644 index 849460f4..00000000 --- a/exo/inference/mlx/models/sd_models/clip.py +++ /dev/null @@ -1,191 +0,0 @@ -# Adapted from https://github.com/ml-explore/mlx-examples/blob/main/stable_diffusion/stable_diffusion/clip.py - -import math -from dataclasses import dataclass -from typing import List, Optional - -import mlx.core as mx -import mlx.nn as nn -from dataclasses import field, dataclass -from exo.inference.shard import Shard -from exo.inference.mlx.models.base import IdentityBlock - -_ACTIVATIONS = {"quick_gelu": nn.gelu_fast_approx, "gelu": nn.gelu} - - - -@dataclass -class CLIPTextModelConfig: - num_layers: int = 23 - model_dims: int = 1024 - num_heads: int = 16 - max_length: int = 77 - vocab_size: int = 49408 - projection_dim: Optional[int] = None - hidden_act: str = "quick_gelu" - - @classmethod - def from_dict(cls, config): - return ModelArgs( - num_layers=config["num_hidden_layers"], - model_dims=config["hidden_size"], - num_heads=config["num_attention_heads"], - max_length=config["max_position_embeddings"], - vocab_size=config["vocab_size"], - projection_dim=config["projection_dim"] if "WithProjection" in config['architectures'][0] else None, - hidden_act=config.get("hidden_act", "quick_gelu"), - weight_files=config.get("weight_files", []) - ) - -@dataclass -class ModelArgs(CLIPTextModelConfig): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - weight_files: List[str] = field(default_factory=lambda: []) - def __post_init__(self): - if isinstance(self.shard, dict): - self.shard = Shard(**self.shard) - - if not isinstance(self.shard, Shard): - raise TypeError(f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead") - - if not self.shard.is_first_layer(): - self.vision_config = None - - -@dataclass -class CLIPOutput: - pooled_output: Optional[mx.array] = None - last_hidden_state: Optional[mx.array] = None - hidden_states: Optional[List[mx.array]] = None - - -class CLIPEncoderLayer(nn.Module): - """The transformer encoder layer from CLIP.""" - - def __init__(self, model_dims: int, num_heads: int, activation: str): - super().__init__() - - self.layer_norm1 = nn.LayerNorm(model_dims) - self.layer_norm2 = nn.LayerNorm(model_dims) - - self.attention = nn.MultiHeadAttention(model_dims, num_heads) - self.attention.query_proj.bias = mx.zeros(model_dims) - self.attention.key_proj.bias = mx.zeros(model_dims) - self.attention.value_proj.bias = mx.zeros(model_dims) - self.attention.out_proj.bias = mx.zeros(model_dims) - - self.linear1 = nn.Linear(model_dims, 4 * model_dims) - self.linear2 = nn.Linear(4 * model_dims, model_dims) - - self.act = _ACTIVATIONS[activation] - - def __call__(self, x, attn_mask=None): - - y = self.layer_norm1(x) - y = self.attention(y, y, y, attn_mask) - x = y + x - - y = self.layer_norm2(x) - y = self.linear1(y) - y = self.act(y) - y = self.linear2(y) - x = y + x - return x - - -class CLIPTextModel(nn.Module): - """Implements the text encoder transformer from CLIP.""" - - def __init__(self, config: CLIPTextModelConfig, shard: Shard): - super().__init__() - - self.shard = shard - self.layers_range = range(self.shard.start_layer*2, self.shard.end_layer*2+2) - if self.shard.is_first_layer(): - self.token_embedding = nn.Embedding(config.vocab_size, config.model_dims) - self.position_embedding = nn.Embedding(config.max_length, config.model_dims) - self.layers = [] - for i in range(math.ceil(config.num_layers/2)): - if 2*i in self.layers_range: - self.layers.append(CLIPEncoderLayer(config.model_dims, config.num_heads, config.hidden_act)) - if 2*i+1 in self.layers_range and 2*i+1 < config.num_layers: - self.layers.append(CLIPEncoderLayer(config.model_dims, config.num_heads, config.hidden_act)) - else: - self.layers.append(IdentityBlock()) - if self.shard.is_last_layer(): - self.final_layer_norm = nn.LayerNorm(config.model_dims) - - if config.projection_dim is not None: - self.text_projection = nn.Linear( - config.model_dims, config.projection_dim, bias=False - ) - - def _get_mask(self, N, dtype): - indices = mx.arange(N) - mask = indices[:, None] < indices[None] - mask = mask.astype(dtype) * (-6e4 if dtype == mx.float16 else -1e9) - return mask - - def __call__(self, x, mask=None): - # Extract some shapes - if self.shard.is_first_layer(): - B, N = x.shape - eos_tokens = x.argmax(-1) - - # Compute the embeddings - x = self.token_embedding(x) - - x = x + self.position_embedding.weight[:N] - # Compute the features from the transformer - mask = self._get_mask(N, x.dtype) - - for l in self.layers: - x = l(x, mask) - # Apply the final layernorm and return - - if self.shard.is_last_layer(): - x = self.final_layer_norm(x) - - - - return x, mask - def sanitize(self, weights): - sanitized_weights = {} - for key, value in weights.items(): - if "position_ids" in key: - continue - if key.startswith("text_model."): - key = key[11:] - if key.startswith("embeddings."): - key = key[11:] - if key.startswith("encoder."): - key = key[8:] - - # Map attention layers - if "self_attn." in key: - key = key.replace("self_attn.", "attention.") - if "q_proj." in key: - key = key.replace("q_proj.", "query_proj.") - if "k_proj." in key: - key = key.replace("k_proj.", "key_proj.") - if "v_proj." in key: - key = key.replace("v_proj.", "value_proj.") - - # Map ffn layers - if "mlp.fc1" in key: - key = key.replace("mlp.fc1", "linear1") - if "mlp.fc2" in key: - key = key.replace("mlp.fc2", "linear2") - - if key.startswith("layers."): - layer_num = int(key.split(".")[1]) - if layer_num not in self.layers_range: - continue - if not self.shard.is_first_layer() and "embedding" in key: - continue - if not self.shard.is_last_layer() and key.startswith("final_layer_norm"): - continue - if not self.shard.is_last_layer() and key.startswith("text_projection"): - continue - sanitized_weights[key] = value - return sanitized_weights diff --git a/exo/inference/mlx/models/sd_models/tokenizer.py b/exo/inference/mlx/models/sd_models/tokenizer.py deleted file mode 100644 index 4987bb90..00000000 --- a/exo/inference/mlx/models/sd_models/tokenizer.py +++ /dev/null @@ -1,131 +0,0 @@ -# adapted from https://github.com/ml-explore/mlx-examples/blob/main/stable_diffusion/stable_diffusion/tokenizer.py - -import regex -import json -import glob - - -class Tokenizer: - """A simple port of CLIPTokenizer from https://github.com/huggingface/transformers/ .""" - - def __init__(self, bpe_ranks, vocab): - self.bpe_ranks = bpe_ranks - self.vocab = vocab - self.pat = regex.compile( - r"""<\|startoftext\|>|<\|endoftext\|>|'s|'t|'re|'ve|'m|'ll|'d|[\p{L}]+|[\p{N}]|[^\s\p{L}\p{N}]+""", - regex.IGNORECASE, - ) - - self._cache = {self.bos: self.bos, self.eos: self.eos} - - @property - def bos(self): - return "<|startoftext|>" - - @property - def bos_token(self): - return self.vocab[self.bos] - - @property - def eos(self): - return "<|endoftext|>" - - @property - def eos_token(self): - return self.vocab[self.eos] - - def bpe(self, text): - if text in self._cache: - return self._cache[text] - - unigrams = list(text[:-1]) + [text[-1] + ""] - unique_bigrams = set(zip(unigrams, unigrams[1:])) - - if not unique_bigrams: - return unigrams - - # In every iteration try to merge the two most likely bigrams. If none - # was merged we are done. - # - # Ported from https://github.com/huggingface/transformers/blob/main/src/transformers/models/clip/tokenization_clip.py - while unique_bigrams: - bigram = min( - unique_bigrams, key=lambda pair: self.bpe_ranks.get(pair, float("inf")) - ) - if bigram not in self.bpe_ranks: - break - - new_unigrams = [] - skip = False - for a, b in zip(unigrams, unigrams[1:]): - if skip: - skip = False - continue - - if (a, b) == bigram: - new_unigrams.append(a + b) - skip = True - - else: - new_unigrams.append(a) - - if not skip: - new_unigrams.append(b) - - unigrams = new_unigrams - unique_bigrams = set(zip(unigrams, unigrams[1:])) - - self._cache[text] = unigrams - - return unigrams - - def tokenize(self, text, prepend_bos=True, append_eos=True): - if isinstance(text, list): - return [self.tokenize(t, prepend_bos, append_eos) for t in text] - - # Lower case cleanup and split according to self.pat. Hugging Face does - # a much more thorough job here but this should suffice for 95% of - # cases. - clean_text = regex.sub(r"\s+", " ", text.lower()) - tokens = regex.findall(self.pat, clean_text) - - # Split the tokens according to the byte-pair merge file - bpe_tokens = [ti for t in tokens for ti in self.bpe(t)] - - # Map to token ids and return - tokens = [self.vocab[t] for t in bpe_tokens] - if prepend_bos: - tokens = [self.bos_token] + tokens - if append_eos: - tokens.append(self.eos_token) - - return tokens - - def encode(self, prompt): - tokens = [self.tokenize(prompt)] - negative_text = "" - if negative_text is not None: - tokens += [self.tokenize(negative_text)] - lengths = [len(t) for t in tokens] - N = max(lengths) - tokens = [t + [0] * (N - len(t)) for t in tokens] - return tokens - -def load_tokenizer( - model_path: str, - vocab_key: str = "tokenizer_vocab", - merges_key: str = "tokenizer_merges", -): - - vocab_file = glob.glob(str(model_path/"tokenizer"/vocab_key))[0] - with open(vocab_file, encoding="utf-8") as f: - vocab = json.load(f) - - merges_file = glob.glob(str(model_path/"tokenizer"/merges_key))[0] - with open(merges_file, encoding="utf-8") as f: - bpe_merges = f.read().strip().split("\n")[1 : 49152 - 256 - 2 + 1] - bpe_merges = [tuple(m.split()) for m in bpe_merges] - bpe_ranks = dict(map(reversed, enumerate(bpe_merges))) - - return Tokenizer(bpe_ranks, vocab) - diff --git a/exo/inference/mlx/models/sd_models/unet.py b/exo/inference/mlx/models/sd_models/unet.py deleted file mode 100644 index 3fe44b86..00000000 --- a/exo/inference/mlx/models/sd_models/unet.py +++ /dev/null @@ -1,629 +0,0 @@ -# Adapted from https://github.com/ml-explore/mlx-examples/blob/main/stable_diffusion/stable_diffusion/unet.py - -import math -from typing import Optional - -import mlx.core as mx -import mlx.nn as nn - -from dataclasses import dataclass, field -from typing import Tuple, Optional, List -from exo.inference.shard import Shard - -@dataclass -class UNetConfig: - in_channels: int = 4 - out_channels: int = 4 - conv_in_kernel: int = 3 - conv_out_kernel: int = 3 - block_out_channels: Tuple[int] = (320, 640, 1280, 1280) - layers_per_block: Tuple[int] = (2, 2, 2, 2) - mid_block_layers: int = 2 - transformer_layers_per_block: Tuple[int] = (1, 1, 1, 1) - num_attention_heads: Tuple[int] = (5, 10, 20, 20) - cross_attention_dim: Tuple[int] = (1024,) * 4 - norm_num_groups: int = 32 - down_block_types: Tuple[str] = ( - "CrossAttnDownBlock2D", - "CrossAttnDownBlock2D", - "CrossAttnDownBlock2D", - "DownBlock2D", - ) - up_block_types: Tuple[str] = ( - "UpBlock2D", - "CrossAttnUpBlock2D", - "CrossAttnUpBlock2D", - "CrossAttnUpBlock2D", - ) - addition_embed_type: Optional[str] = None - addition_time_embed_dim: Optional[int] = None - projection_class_embeddings_input_dim: Optional[int] = None - weight_files: List[str] = field(default_factory=lambda: []) - - - - @classmethod - def from_dict(cls,config): - n_blocks = len(config['block_out_channels']) - return UNetConfig( - in_channels=config["in_channels"], - out_channels=config["out_channels"], - block_out_channels=config["block_out_channels"], - layers_per_block=[config["layers_per_block"]] * n_blocks, - transformer_layers_per_block=config.get( - "transformer_layers_per_block", (1,) * 4 - ), - num_attention_heads=( - [config["attention_head_dim"]] * n_blocks - if isinstance(config["attention_head_dim"], int) - else config["attention_head_dim"] - ), - cross_attention_dim=[config["cross_attention_dim"]] * n_blocks, - norm_num_groups=config["norm_num_groups"], - down_block_types=config["down_block_types"], - up_block_types=config["up_block_types"][::-1], - addition_embed_type=config.get("addition_embed_type", None), - addition_time_embed_dim=config.get("addition_time_embed_dim", None), - projection_class_embeddings_input_dim=config.get( - "projection_class_embeddings_input_dim", None - ), - weight_files=config.get("weight_files", []) - - ) - - -def upsample_nearest(x, scale: int = 2): - B, H, W, C = x.shape - x = mx.broadcast_to(x[:, :, None, :, None, :], (B, H, scale, W, scale, C)) - x = x.reshape(B, H * scale, W * scale, C) - - return x - - -class TimestepEmbedding(nn.Module): - def __init__(self, in_channels: int, time_embed_dim: int): - super().__init__() - - self.linear_1 = nn.Linear(in_channels, time_embed_dim) - self.linear_2 = nn.Linear(time_embed_dim, time_embed_dim) - - def __call__(self, x): - x = self.linear_1(x) - x = nn.silu(x) - x = self.linear_2(x) - - return x - - -class TransformerBlock(nn.Module): - def __init__( - self, - model_dims: int, - num_heads: int, - hidden_dims: Optional[int] = None, - memory_dims: Optional[int] = None, - ): - super().__init__() - - self.norm1 = nn.LayerNorm(model_dims) - self.attn1 = nn.MultiHeadAttention(model_dims, num_heads) - self.attn1.out_proj.bias = mx.zeros(model_dims) - - memory_dims = memory_dims or model_dims - self.norm2 = nn.LayerNorm(model_dims) - self.attn2 = nn.MultiHeadAttention( - model_dims, num_heads, key_input_dims=memory_dims - ) - self.attn2.out_proj.bias = mx.zeros(model_dims) - - hidden_dims = hidden_dims or 4 * model_dims - self.norm3 = nn.LayerNorm(model_dims) - self.linear1 = nn.Linear(model_dims, hidden_dims) - self.linear2 = nn.Linear(model_dims, hidden_dims) - self.linear3 = nn.Linear(hidden_dims, model_dims) - - def __call__(self, x, memory, attn_mask, memory_mask): - # Self attention - y = self.norm1(x) - y = self.attn1(y, y, y, attn_mask) - x = x + y - - # Cross attention - y = self.norm2(x) - y = self.attn2(y, memory, memory, memory_mask) - x = x + y - - # FFN - y = self.norm3(x) - y_a = self.linear1(y) - y_b = self.linear2(y) - y = y_a * nn.gelu(y_b) - y = self.linear3(y) - x = x + y - - return x - - -class Transformer2D(nn.Module): - """A transformer model for inputs with 2 spatial dimensions.""" - - def __init__( - self, - in_channels: int, - model_dims: int, - encoder_dims: int, - num_heads: int, - num_layers: int = 1, - norm_num_groups: int = 32, - ): - super().__init__() - - self.norm = nn.GroupNorm(norm_num_groups, in_channels, pytorch_compatible=True) - self.proj_in = nn.Linear(in_channels, model_dims) - self.transformer_blocks = [ - TransformerBlock(model_dims, num_heads, memory_dims=encoder_dims) - for i in range(num_layers) - ] - self.proj_out = nn.Linear(model_dims, in_channels) - - def __call__(self, x, encoder_x, attn_mask, encoder_attn_mask): - # Save the input to add to the output - input_x = x - dtype = x.dtype - - # Perform the input norm and projection - B, H, W, C = x.shape - x = self.norm(x.astype(mx.float32)).astype(dtype).reshape(B, -1, C) - x = self.proj_in(x) - - # Apply the transformer - for block in self.transformer_blocks: - x = block(x, encoder_x, attn_mask, encoder_attn_mask) - - # Apply the output projection and reshape - x = self.proj_out(x) - x = x.reshape(B, H, W, C) - - return x + input_x - - -class ResnetBlock2D(nn.Module): - def __init__( - self, - in_channels: int, - out_channels: Optional[int] = None, - groups: int = 32, - temb_channels: Optional[int] = None, - ): - super().__init__() - - out_channels = out_channels or in_channels - - self.norm1 = nn.GroupNorm(groups, in_channels, pytorch_compatible=True) - self.conv1 = nn.Conv2d( - in_channels, out_channels, kernel_size=3, stride=1, padding=1 - ) - if temb_channels is not None: - self.time_emb_proj = nn.Linear(temb_channels, out_channels) - self.norm2 = nn.GroupNorm(groups, out_channels, pytorch_compatible=True) - self.conv2 = nn.Conv2d( - out_channels, out_channels, kernel_size=3, stride=1, padding=1 - ) - - if in_channels != out_channels: - self.conv_shortcut = nn.Linear(in_channels, out_channels) - - def __call__(self, x, temb=None): - dtype = x.dtype - - if temb is not None: - temb = self.time_emb_proj(nn.silu(temb)) - y = self.norm1(x.astype(mx.float32)).astype(dtype) - - y = nn.silu(y) - - y = self.conv1(y) - - - if temb is not None: - y = y + temb[:, None, None, :] - y = self.norm2(y.astype(mx.float32)).astype(dtype) - y = nn.silu(y) - y = self.conv2(y) - - x = y + (x if "conv_shortcut" not in self else self.conv_shortcut(x)) - return x - - -class UNetBlock2D(nn.Module): - def __init__( - self, - in_channels: int, - out_channels: int, - temb_channels: int, - prev_out_channels: Optional[int] = None, - num_layers: int = 1, - transformer_layers_per_block: int = 1, - num_attention_heads: int = 8, - cross_attention_dim=1280, - resnet_groups: int = 32, - add_downsample=True, - add_upsample=True, - add_cross_attention=True, - ): - super().__init__() - - # Prepare the in channels list for the resnets - if prev_out_channels is None: - in_channels_list = [in_channels] + [out_channels] * (num_layers - 1) - else: - in_channels_list = [prev_out_channels] + [out_channels] * (num_layers - 1) - res_channels_list = [out_channels] * (num_layers - 1) + [in_channels] - in_channels_list = [ - a + b for a, b in zip(in_channels_list, res_channels_list) - ] - - # Add resnet blocks that also process the time embedding - self.resnets = [ - ResnetBlock2D( - in_channels=ic, - out_channels=out_channels, - temb_channels=temb_channels, - groups=resnet_groups, - ) - for ic in in_channels_list - ] - - # Add optional cross attention layers - if add_cross_attention: - self.attentions = [ - Transformer2D( - in_channels=out_channels, - model_dims=out_channels, - num_heads=num_attention_heads, - num_layers=transformer_layers_per_block, - encoder_dims=cross_attention_dim, - ) - for i in range(num_layers) - ] - - # Add an optional downsampling layer - if add_downsample: - self.downsample = nn.Conv2d( - out_channels, out_channels, kernel_size=3, stride=2, padding=1 - ) - - # or upsampling layer - if add_upsample: - self.upsample = nn.Conv2d( - out_channels, out_channels, kernel_size=3, stride=1, padding=1 - ) - - def __call__( - self, - x, - encoder_x=None, - temb=None, - attn_mask=None, - encoder_attn_mask=None, - residual_hidden_states=None, - ): - output_states = [] - - for i in range(len(self.resnets)): - if residual_hidden_states is not None: - x = mx.concatenate([x, residual_hidden_states.pop()], axis=-1) - - x = self.resnets[i](x, temb) - - if "attentions" in self: - x = self.attentions[i](x, encoder_x, attn_mask, encoder_attn_mask) - - output_states.append(x) - - if "downsample" in self: - x = self.downsample(x) - output_states.append(x) - - if "upsample" in self: - x = self.upsample(upsample_nearest(x)) - output_states.append(x) - - return x, output_states - - -class UNetModel(nn.Module): - """The conditional 2D UNet model that actually performs the denoising.""" - - def __init__(self, config: UNetConfig, shard: Shard): - super().__init__() - self.shard = shard - self.start_layer = shard.start_layer - self.end_layer = shard.end_layer - self.layers_range = list(range(self.start_layer, self.end_layer+1)) - if shard.is_first_layer(): - self.conv_in = nn.Conv2d( - config.in_channels, - config.block_out_channels[0], - config.conv_in_kernel, - padding=(config.conv_in_kernel - 1) // 2, - ) - - self.timesteps = nn.SinusoidalPositionalEncoding( - config.block_out_channels[0], - max_freq=1, - min_freq=math.exp( - -math.log(10000) + 2 * math.log(10000) / config.block_out_channels[0] - ), - scale=1.0, - cos_first=True, - full_turns=False, - ) - self.time_embedding = TimestepEmbedding( - config.block_out_channels[0], - config.block_out_channels[0] * 4, - ) - - if config.addition_embed_type == "text_time": - self.add_time_proj = nn.SinusoidalPositionalEncoding( - config.addition_time_embed_dim, - max_freq=1, - min_freq=math.exp( - -math.log(10000) - + 2 * math.log(10000) / config.addition_time_embed_dim - ), - scale=1.0, - cos_first=True, - full_turns=False, - ) - self.add_embedding = TimestepEmbedding( - config.projection_class_embeddings_input_dim, - config.block_out_channels[0] * 4, - ) - - # Make the downsampling blocks - block_channels = [config.block_out_channels[0]] + list( - config.block_out_channels - ) - self.down_blocks = [] - - for i, (in_channels, out_channels) in enumerate(zip(block_channels, block_channels[1:])): - if i in self.layers_range: - self.down_blocks.append( - UNetBlock2D( - in_channels=in_channels, - out_channels=out_channels, - temb_channels=config.block_out_channels[0] * 4, - num_layers=config.layers_per_block[i], - transformer_layers_per_block=config.transformer_layers_per_block[i], - num_attention_heads=config.num_attention_heads[i], - cross_attention_dim=config.cross_attention_dim[i], - resnet_groups=config.norm_num_groups, - add_downsample=(i < len(config.block_out_channels) - 1), - add_upsample=False, - add_cross_attention="CrossAttn" in config.down_block_types[i], - ) - ) - else: - self.down_blocks.append(nn.Identity()) - - - # Make the middle block - if 4 in self.layers_range: - self.mid_blocks = [ - ResnetBlock2D( - in_channels=config.block_out_channels[-1], - out_channels=config.block_out_channels[-1], - temb_channels=config.block_out_channels[0] * 4, - groups=config.norm_num_groups, - ), - Transformer2D( - in_channels=config.block_out_channels[-1], - model_dims=config.block_out_channels[-1], - num_heads=config.num_attention_heads[-1], - num_layers=config.transformer_layers_per_block[-1], - encoder_dims=config.cross_attention_dim[-1], - ), - ResnetBlock2D( - in_channels=config.block_out_channels[-1], - out_channels=config.block_out_channels[-1], - temb_channels=config.block_out_channels[0] * 4, - groups=config.norm_num_groups, - ), - ] - - # Make the upsampling blocks - block_channels = ( - [config.block_out_channels[0]] - + list(config.block_out_channels) - + [config.block_out_channels[-1]] - ) - - total_items = len(block_channels) - 3 - reversed_channels = list(reversed(list(zip(block_channels, block_channels[1:], block_channels[2:])))) - - self.up_blocks = [] - for rev_i, (in_channels, out_channels, prev_out_channels) in enumerate(reversed_channels): - i = total_items - rev_i - if rev_i+5 in self.layers_range: - self.up_blocks.append( - UNetBlock2D( - in_channels=in_channels, - out_channels=out_channels, - temb_channels=config.block_out_channels[0] * 4, - prev_out_channels=prev_out_channels, - num_layers=config.layers_per_block[i] + 1, - transformer_layers_per_block=config.transformer_layers_per_block[i], - num_attention_heads=config.num_attention_heads[i], - cross_attention_dim=config.cross_attention_dim[i], - resnet_groups=config.norm_num_groups, - add_downsample=False, - add_upsample=(i > 0), - add_cross_attention="CrossAttn" in config.up_block_types[i], - ) - ) - else: - self.up_blocks.append(nn.Identity()) - - - if shard.is_last_layer(): - self.conv_norm_out = nn.GroupNorm( - config.norm_num_groups, - config.block_out_channels[0], - pytorch_compatible=True, - ) - self.conv_out = nn.Conv2d( - config.block_out_channels[0], - config.out_channels, - config.conv_out_kernel, - padding=(config.conv_out_kernel - 1) // 2, - ) - - def __call__( - self, - x, - timestep, - encoder_x, - attn_mask=None, - encoder_attn_mask=None, - text_time=None, - residuals=None, - ): - # Compute the time embeddings - - temb = self.timesteps(timestep).astype(x.dtype) - temb = self.time_embedding(temb) - - # Add the extra text_time conditioning - if text_time is not None: - text_emb, time_ids = text_time - emb = self.add_time_proj(time_ids).flatten(1).astype(x.dtype) - emb = mx.concatenate([text_emb, emb], axis=-1) - emb = self.add_embedding(emb) - temb = temb + emb - - if self.shard.is_first_layer(): - # Preprocess the input - x = self.conv_in(x) - residuals = [x] - # Run the downsampling part of the unet - - for i in range(len(self.down_blocks)): - if i in self.layers_range: - x, res = self.down_blocks[i]( - x, - encoder_x=encoder_x, - temb=temb, - attn_mask=attn_mask, - encoder_attn_mask=encoder_attn_mask, - ) - residuals.extend(res) - else: - x= self.down_blocks[i](x) - - if 4 in self.layers_range: - # Run the middle part of the unet - x = self.mid_blocks[0](x, temb) - x = self.mid_blocks[1](x, encoder_x, attn_mask, encoder_attn_mask) - x = self.mid_blocks[2](x, temb) - - # Run the upsampling part of the unet - for i in range(len(self.up_blocks)): - if i+5 in self.layers_range: - x, _ = self.up_blocks[i]( - x, - encoder_x=encoder_x, - temb=temb, - attn_mask=attn_mask, - encoder_attn_mask=encoder_attn_mask, - residual_hidden_states=residuals, - ) - else: - x= self.up_blocks[i](x) - - # Postprocess the output - if self.shard.is_last_layer(): - dtype = x.dtype - x = self.conv_norm_out(x.astype(mx.float32)).astype(dtype) - x = nn.silu(x) - x = self.conv_out(x) - - return x, residuals - def sanitize(self, weights): - sanitized_weights = {} - for key, value in weights.items(): - k1="" - k2="" - if "downsamplers" in key: - key = key.replace("downsamplers.0.conv", "downsample") - if "upsamplers" in key: - key = key.replace("upsamplers.0.conv", "upsample") - - # Map the mid block - if "mid_block.resnets.0" in key: - key = key.replace("mid_block.resnets.0", "mid_blocks.0") - if "mid_block.attentions.0" in key: - key = key.replace("mid_block.attentions.0", "mid_blocks.1") - if "mid_block.resnets.1" in key: - key = key.replace("mid_block.resnets.1", "mid_blocks.2") - - # Map attention layers - if "to_k" in key: - key = key.replace("to_k", "key_proj") - if "to_out.0" in key: - key = key.replace("to_out.0", "out_proj") - if "to_q" in key: - key = key.replace("to_q", "query_proj") - if "to_v" in key: - key = key.replace("to_v", "value_proj") - - # Map transformer ffn - if "ff.net.2" in key: - key = key.replace("ff.net.2", "linear3") - if "ff.net.0" in key: - k1 = key.replace("ff.net.0.proj", "linear1") - k2 = key.replace("ff.net.0.proj", "linear2") - v1, v2 = mx.split(value, 2) - - - if "conv_shortcut.weight" in key: - value = value.squeeze() - - # Transform the weights from 1x1 convs to linear - if len(value.shape) == 4 and ("proj_in" in key or "proj_out" in key): - value = value.squeeze() - - if len(value.shape) == 4: - value = value.transpose(0, 2, 3, 1) - value = value.reshape(-1).reshape(value.shape) - - if key.startswith("conv_in") : - if 0 not in self.layers_range: - continue - - if key.startswith("down_blocks"): - layer_num = int(key.split(".")[1]) - if layer_num not in self.layers_range: - continue - - if key.startswith("mid_block"): - if 4 not in self.layers_range: - continue - - if key.startswith("up_blocks"): - layer_num = int(key.split(".")[1]) - if (layer_num+5) not in self.layers_range: - continue - - if key.startswith("conv_out") or key.startswith("conv_norm_out"): - if 8 not in self.layers_range: - continue - - if len(k1)>0: - sanitized_weights[k1] = v1 - sanitized_weights[k2] = v2 - else: - sanitized_weights[key] = value - - - return sanitized_weights diff --git a/exo/inference/mlx/models/sd_models/vae.py b/exo/inference/mlx/models/sd_models/vae.py deleted file mode 100644 index 0f148517..00000000 --- a/exo/inference/mlx/models/sd_models/vae.py +++ /dev/null @@ -1,429 +0,0 @@ -# Adapted from https://github.com/ml-explore/mlx-examples/blob/main/stable_diffusion/stable_diffusion/vae.py - -import math -from typing import List - -import mlx.core as mx -import mlx.nn as nn - -from .unet import ResnetBlock2D, upsample_nearest -from dataclasses import dataclass, field -from exo.inference.shard import Shard -from typing import Tuple -import inspect -from ..base import IdentityBlock - -@dataclass -class AutoencoderConfig: - in_channels: int = 3 - out_channels: int = 3 - latent_channels_out: int = 8 - latent_channels_in: int = 4 - block_out_channels: Tuple[int] = (128, 256, 512, 512) - layers_per_block: int = 2 - norm_num_groups: int = 32 - scaling_factor: float = 0.18215 - weight_files: List[str] = field(default_factory=lambda: []) - @classmethod - def from_dict(cls, params): - return cls(**{k: v for k, v in params.items() if k in inspect.signature(cls).parameters}) - - -@dataclass -class ModelArgs(AutoencoderConfig): - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - - def __post_init__(self): - if isinstance(self.shard, dict): - self.shard = Shard(**self.shard) - - if not isinstance(self.shard, Shard): - raise TypeError(f"Expected shard to be a Shard instance or a dict, got {type(self.shard)} instead") - - if not self.shard.is_first_layer(): - self.vision_config = None - - -class Attention(nn.Module): - """A single head unmasked attention for use with the VAE.""" - - def __init__(self, dims: int, norm_groups: int = 32): - super().__init__() - - self.group_norm = nn.GroupNorm(norm_groups, dims, pytorch_compatible=True) - self.query_proj = nn.Linear(dims, dims) - self.key_proj = nn.Linear(dims, dims) - self.value_proj = nn.Linear(dims, dims) - self.out_proj = nn.Linear(dims, dims) - - def __call__(self, x): - B, H, W, C = x.shape - - y = self.group_norm(x) - - queries = self.query_proj(y).reshape(B, H * W, C) - keys = self.key_proj(y).reshape(B, H * W, C) - values = self.value_proj(y).reshape(B, H * W, C) - - scale = 1 / math.sqrt(queries.shape[-1]) - scores = (queries * scale) @ keys.transpose(0, 2, 1) - attn = mx.softmax(scores, axis=-1) - y = (attn @ values).reshape(B, H, W, C) - - y = self.out_proj(y) - x = x + y - - return x - - -class EncoderDecoderBlock2D(nn.Module): - def __init__( - self, - in_channels: int, - out_channels: int, - num_layers: int = 1, - resnet_groups: int = 32, - add_downsample=True, - add_upsample=True, - ): - super().__init__() - - # Add the resnet blocks - self.resnets = [ - ResnetBlock2D( - in_channels=in_channels if i == 0 else out_channels, - out_channels=out_channels, - groups=resnet_groups, - ) - for i in range(num_layers) - ] - - # Add an optional downsampling layer - if add_downsample: - self.downsample = nn.Conv2d( - out_channels, out_channels, kernel_size=3, stride=2, padding=0 - ) - - # or upsampling layer - if add_upsample: - self.upsample = nn.Conv2d( - out_channels, out_channels, kernel_size=3, stride=1, padding=1 - ) - - def __call__(self, x): - for resnet in self.resnets: - x = resnet(x) - if "downsample" in self: - x = mx.pad(x, [(0, 0), (0, 1), (0, 1), (0, 0)]) - x = self.downsample(x) - - if "upsample" in self: - x = self.upsample(upsample_nearest(x)) - return x - - -class Encoder(nn.Module): - """Implements the encoder side of the Autoencoder.""" - - def __init__( - self, - in_channels: int, - latent_channels_out: int, - block_out_channels: List[int] = [64], - layers_per_block: int = 2, - resnet_groups: int = 32, - layers_range: List[int] = [], - shard: Shard = field(default_factory=lambda: Shard("", 0, 0, 0)) - ): - super().__init__() - self.layers_range = layers_range - self.shard = shard - if self.shard.is_first_layer(): - self.conv_in = nn.Conv2d( - in_channels, block_out_channels[0], kernel_size=3, stride=1, padding=1 - ) - - channels = [block_out_channels[0]] + list(block_out_channels) - self.down_blocks = [] - current_layer = 1 - for i, (in_channels, out_channels) in enumerate(zip(channels, channels[1:])): - if current_layer in self.layers_range: - self.down_blocks.append( - EncoderDecoderBlock2D( - in_channels, - out_channels, - num_layers=layers_per_block, - resnet_groups=resnet_groups, - add_downsample=i < len(block_out_channels) - 1, - add_upsample=False, - ) - ) - else: - self.down_blocks.append(IdentityBlock()) - current_layer += 1 - - if self.shard.is_last_layer(): - self.mid_blocks = [ - ResnetBlock2D( - in_channels=block_out_channels[-1], - out_channels=block_out_channels[-1], - groups=resnet_groups, - ), - Attention(block_out_channels[-1], resnet_groups), - ResnetBlock2D( - in_channels=block_out_channels[-1], - out_channels=block_out_channels[-1], - groups=resnet_groups, - ), - ] - - self.conv_norm_out = nn.GroupNorm( - resnet_groups, block_out_channels[-1], pytorch_compatible=True - ) - self.conv_out = nn.Conv2d(block_out_channels[-1], latent_channels_out, 3, padding=1) - - def __call__(self, x): - if self.shard.is_first_layer(): - x = self.conv_in(x) - - for l in self.down_blocks: - x = l(x) - - if self.shard.is_last_layer(): - x = self.mid_blocks[0](x) - x = self.mid_blocks[1](x) - x = self.mid_blocks[2](x) - - x = self.conv_norm_out(x) - x = nn.silu(x) - x = self.conv_out(x) - - return x - - -class Decoder(nn.Module): - """Implements the decoder side of the Autoencoder.""" - - def __init__( - self, - in_channels: int, - out_channels: int, - shard: Shard, - layer_range: List[int], - block_out_channels: List[int] = [64], - layers_per_block: int = 2, - resnet_groups: int = 32, - ): - super().__init__() - self.out_channels = out_channels - self.layers_range = layer_range - if 0 in layer_range: - self.conv_in = nn.Conv2d( - in_channels, block_out_channels[-1], kernel_size=3, stride=1, padding=1 - ) - - if 0 in layer_range: - self.mid_blocks = [ - ResnetBlock2D( - in_channels=block_out_channels[-1], - out_channels=block_out_channels[-1], - groups=resnet_groups, - ), - Attention(block_out_channels[-1], resnet_groups), - ResnetBlock2D( - in_channels=block_out_channels[-1], - out_channels=block_out_channels[-1], - groups=resnet_groups, - ), - ] - - channels = list(reversed(block_out_channels)) - channels = [channels[0]] + channels - - self.up_blocks = [] - current_layer = 1 - - for i, (in_channels, out_channels) in enumerate(zip(channels, channels[1:])): - if current_layer in layer_range: - self.up_blocks.append( - EncoderDecoderBlock2D( - in_channels, - out_channels, - num_layers=layers_per_block, - resnet_groups=resnet_groups, - add_downsample=False, - add_upsample=i < len(block_out_channels) - 1, - ) - ) - else: - self.up_blocks.append(IdentityBlock()) - current_layer += 1 - if 4 in layer_range: - self.conv_norm_out = nn.GroupNorm( - resnet_groups, block_out_channels[0], pytorch_compatible=True - ) - self.conv_out = nn.Conv2d(block_out_channels[0], self.out_channels, 3, padding=1) - - - def __call__(self, x): - if 0 in self.layers_range: - x = self.conv_in(x) - x = self.mid_blocks[0](x) - x = self.mid_blocks[1](x) - x = self.mid_blocks[2](x) - - for l in self.up_blocks: - x = l(x) - if 4 in self.layers_range: - x = self.conv_norm_out(x) - x = nn.silu(x) - x = self.conv_out(x) - return x - - -class Autoencoder(nn.Module): - """The autoencoder that allows us to perform diffusion in the latent space.""" - - def __init__(self, config: AutoencoderConfig, shard: Shard, model_shard: str): - super().__init__() - self.shard = shard - self.start_layer = shard.start_layer - self.end_layer = shard.end_layer - self.layers_range = list(range(self.start_layer, self.end_layer+1)) - self.latent_channels = config.latent_channels_in - self.scaling_factor = config.scaling_factor - self.model_shard = model_shard - if self.model_shard == "vae_encoder": - self.encoder = Encoder( - config.in_channels, - config.latent_channels_out, - config.block_out_channels, - config.layers_per_block, - resnet_groups=config.norm_num_groups, - layers_range=self.layers_range, - shard=shard - ) - if self.shard.is_last_layer(): - self.quant_proj = nn.Linear( - config.latent_channels_out, config.latent_channels_out - ) - if self.model_shard == "vae_decoder": - self.decoder = Decoder( - config.latent_channels_in, - config.out_channels, - shard, - self.layers_range, - config.block_out_channels, - config.layers_per_block + 1, - resnet_groups=config.norm_num_groups, - ) - if self.shard.is_first_layer(): - self.post_quant_proj = nn.Linear( - config.latent_channels_in, config.latent_channels_in - ) - - def decode(self, z): - if self.shard.is_first_layer(): - z = z / self.scaling_factor - z=self.post_quant_proj(z) - return self.decoder(z) - - def encode(self, x): - x = self.encoder(x) - if self.shard.is_last_layer(): - x = self.quant_proj(x) - mean, logvar = x.split(2, axis=-1) - mean = mean * self.scaling_factor - logvar = logvar + 2 * math.log(self.scaling_factor) - x = mean - return x - - def __call__(self, x, key=None): - mean, logvar = self.encode(x) - z = mx.random.normal(mean.shape, key=key) * mx.exp(0.5 * logvar) + mean - x_hat = self.decode(z) - - return dict(x_hat=x_hat, z=z, mean=mean, logvar=logvar) - - def sanitize(self, weights): - shard = self.shard - layers = self.layers_range - sanitized_weights = {} - for key, value in weights.items(): - - if "downsamplers" in key: - key = key.replace("downsamplers.0.conv", "downsample") - if "upsamplers" in key: - key = key.replace("upsamplers.0.conv", "upsample") - - # Map attention layers - if "key" in key: - key = key.replace("key", "key_proj") - if "proj_attn" in key: - key = key.replace("proj_attn", "out_proj") - if "query" in key: - key = key.replace("query", "query_proj") - if "value" in key: - key = key.replace("value", "value_proj") - - # Map the mid block - if "mid_block.resnets.0" in key: - key = key.replace("mid_block.resnets.0", "mid_blocks.0") - if "mid_block.attentions.0" in key: - key = key.replace("mid_block.attentions.0", "mid_blocks.1") - if "mid_block.resnets.1" in key: - key = key.replace("mid_block.resnets.1", "mid_blocks.2") - - # Map the quant/post_quant layers - if "quant_conv" in key: - key = key.replace("quant_conv", "quant_proj") - value = value.squeeze() - - # Map the conv_shortcut to linear - if "conv_shortcut.weight" in key: - value = value.squeeze() - - if len(value.shape) == 4: - value = value.transpose(0, 2, 3, 1) - value = value.reshape(-1).reshape(value.shape) - - - if "post_quant_conv" in key : - key = key.replace("quant_conv", "quant_proj") - value = value.squeeze() - - if 'decoder' in key and self.model_shard == "vae_decoder": - if key.startswith("decoder.mid_blocks."): - if 0 in layers: - sanitized_weights[key] = value - if "conv_in" in key and 0 in layers: - sanitized_weights[key] = value - if key.startswith("decoder.up_blocks."): - layer_num = int(key.split(".")[2])+1 - if layer_num in layers: - sanitized_weights[key] = value - if key.startswith("decoder.conv_norm_out") and 4 in layers: - sanitized_weights[key] = value - if key.startswith("decoder.conv_out") and 4 in layers: - sanitized_weights[key] = value - if self.model_shard == "vae_decoder": - if key.startswith("post_quant_proj") and 0 in layers: - sanitized_weights[key] = value - if self.model_shard == "vae_encoder": - if key.startswith("encoder."): - if "conv_in" in key and shard.is_first_layer(): - sanitized_weights[key] = value - if key.startswith("encoder.down_blocks."): - layer_num = int(key.split(".")[2])+1 - if layer_num in layers: - sanitized_weights[key] = value - if key.startswith("encoder.mid_blocks.") and shard.is_last_layer(): - sanitized_weights[key] = value - if "conv_norm_out" in key and shard.is_last_layer(): - sanitized_weights[key] = value - if "conv_out" in key and shard.is_last_layer(): - sanitized_weights[key] = value - if key.startswith("quant_proj") and shard.is_last_layer(): - sanitized_weights[key] = value - return sanitized_weights - diff --git a/exo/inference/mlx/perf_improvements.md b/exo/inference/mlx/perf_improvements.md deleted file mode 100644 index aa9869c8..00000000 --- a/exo/inference/mlx/perf_improvements.md +++ /dev/null @@ -1,7 +0,0 @@ -# Perf improvements - -Target: 460 tok/sec -- removing sample goes from 369 -> 402 -- performance degrades as we generate more tokens -- make mlx inference engien synchronous, removing thread pool executor: 402 -> 413 -- remove self.on_opaque_status.trigger_all: 413 -> 418 diff --git a/exo/inference/mlx/sharded_inference_engine.py b/exo/inference/mlx/sharded_inference_engine.py deleted file mode 100644 index cb730321..00000000 --- a/exo/inference/mlx/sharded_inference_engine.py +++ /dev/null @@ -1,179 +0,0 @@ -import numpy as np -import mlx.core as mx -import mlx.nn as nn -from mlx_lm.sample_utils import make_sampler -import mlx.optimizers as optim -from ..inference_engine import InferenceEngine -from .sharded_utils import load_model_shard, resolve_tokenizer -from .losses import loss_fns -from ..shard import Shard -from typing import Dict, Optional, Tuple -from exo.download.shard_download import ShardDownloader -import asyncio -from collections import OrderedDict -from mlx_lm.models.cache import make_prompt_cache -from concurrent.futures import ThreadPoolExecutor - -class MLXDynamicShardInferenceEngine(InferenceEngine): - def __init__(self, shard_downloader: ShardDownloader): - self.shard = None - self.shard_downloader = shard_downloader - self.caches = OrderedDict() - self.sampler_params: tuple[float, float] = (0.0, 0.0, 0.0, 1) - self.sampler = make_sampler(*self.sampler_params) - self._mlx_thread = ThreadPoolExecutor(max_workers=1, thread_name_prefix="mlx") - self._tokenizer_thread = ThreadPoolExecutor(max_workers=1, thread_name_prefix="tokenizer") - self.session = {} - self._shard_lock = asyncio.Lock() - - async def _eval_mlx(self, *args): - await asyncio.get_running_loop().run_in_executor(self._mlx_thread, mx.eval, *args) - - async def poll_state(self, request_id: str, max_caches=2): - if request_id in self.caches: - self.caches.move_to_end(request_id) - else: - newcache = make_prompt_cache(self.model) - if len(self.caches) > max_caches: - self.caches.popitem(last=False) - self.caches[request_id] = newcache - return {"cache": self.caches[request_id]} - - async def sample(self, x: np.ndarray, temp: float = 0.0, top_p: float = 1.0) -> np.ndarray: - if (temp, top_p, 0.0, 1) != self.sampler_params: - self.sampler_params = (temp, top_p, 0.0, 1) - self.sampler = make_sampler(*self.sampler_params) - logits = mx.array(x) - logits = logits[:, -1, :] - logprobs = logits - mx.logsumexp(logits, keepdims=True) - result = self.sampler(logprobs) - await self._eval_mlx(result) - return np.asarray(result, dtype=int) - - async def encode(self, shard: Shard, prompt: str) -> np.ndarray: - await self.ensure_shard(shard) - return np.asarray( - await asyncio.get_running_loop().run_in_executor( - self._tokenizer_thread, - self.tokenizer.encode, - prompt - ) - ) - - async def decode(self, shard: Shard, tokens) -> str: - await self.ensure_shard(shard) - return await asyncio.get_running_loop().run_in_executor( - self._tokenizer_thread, - self.tokenizer.decode, - tokens - ) - - async def save_checkpoint(self, shard: Shard, path: str): - await self.ensure_shard(shard) - await asyncio.get_running_loop().run_in_executor(self._mlx_thread, lambda: self.model.save_weights(path)) - - async def load_checkpoint(self, shard: Shard, path: str): - await self.ensure_shard(shard) - await asyncio.get_running_loop().run_in_executor(self._mlx_thread, lambda: self.model.load_weights(path)) - - async def infer_tensor(self, request_id: str, shard: Shard, input_data: np.ndarray, inference_state: Optional[dict] = None) -> tuple[np.ndarray, Optional[dict]]: - await self.ensure_shard(shard) - state = await self.poll_state(request_id) if self.model.model_type != 'StableDiffusionPipeline' else {} - x = mx.array(input_data) - - if self.model.model_type != 'StableDiffusionPipeline': - output_data = await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, - lambda: self.model(x, **state, **(inference_state or {})) - ) - inference_state = None - else: - result = await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, - lambda: self.model(x, **state, **(inference_state or {})) - ) - output_data, inference_state = result - - await self._eval_mlx(output_data) - output_data = await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, - lambda: np.array(output_data, copy=False) - ) - return output_data, inference_state - - async def evaluate(self, request_id: str, shard: Shard, inputs, targets, lengths, loss: str = "length_masked_ce"): - await self.ensure_shard(shard) - await self.save_session('loss', loss_fns[loss]) - x = mx.array(inputs) - y = mx.array(targets) - l = mx.array(lengths) - - score = await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, - lambda: self.session['loss'](self.model, x, y, l) - ) - return score - - async def ensure_train(self, shard: Shard, loss: str, opt=optim.SGD, lr=1e-5, trainable_layers=['input_layernorm', 'gate_proj']): - await self.ensure_shard(shard) - - if 'train_layers' not in self.session or self.session['train_layers'] != trainable_layers: - await self.save_session('train_layers', trainable_layers) - def freeze_unfreeze(): - self.model.freeze() - self.model.apply_to_modules( - lambda k, v: v.unfreeze() if any(k.endswith(layer_name) for layer_name in trainable_layers) else None - ) - await asyncio.get_running_loop().run_in_executor(self._mlx_thread, freeze_unfreeze) - - if 'lossname' not in self.session or 'LVaG' not in self.session or self.session['lossname'] != loss: - await self.save_session('lossname', loss) - await self.save_session('LVaG', nn.value_and_grad(self.model, loss_fns[loss])) - - if 'opt' not in self.session: - await self.save_session('opt', opt(lr)) - return True - - async def train(self, request_id: str, shard: Shard, inputs, targets, lengths, loss: str = "length_masked_ce", opt=optim.SGD, lr=1e-5): - await self.ensure_train(shard, loss, opt, lr) - - def train_step(inp, tar, lng): - lval, grad = self.session['LVaG'](self.model, inp, tar, lng) - gradlayers = grad['model']['layers'] - self.session['opt'].update(self.model, grad) - return lval, gradlayers, (self.model.parameters(), self.session['opt'].state, lval) - - x = mx.array(inputs) - y = mx.array(targets) - l = mx.array(lengths) - score, gradients, eval_args = await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, - lambda: train_step(x, y, l) - ) - await self._eval_mlx(*eval_args) - - layers = [{k: v["weight"] for k, v in layer.items() if 'weight' in v} for layer in gradients if layer] - first_layer = np.array(layers[0]['input_layernorm'], copy=False) - await self._eval_mlx(first_layer) - return score, first_layer - - async def ensure_shard(self, shard: Shard): - async with self._shard_lock: - if self.shard == shard: return - model_path = await self.shard_downloader.ensure_shard(shard, self.__class__.__name__) - if self.shard != shard: - model_shard = await asyncio.get_running_loop().run_in_executor( - self._mlx_thread, - lambda: load_model_shard(model_path, shard, lazy=False) - ) - if hasattr(model_shard, "tokenizer"): - self.tokenizer = model_shard.tokenizer - else: - self.tokenizer = await resolve_tokenizer(model_path) - self.shard = shard - self.model = model_shard - self.caches = OrderedDict() - self.session = {} - - async def cleanup(self): - self._mlx_thread.shutdown(wait=True) diff --git a/exo/inference/mlx/sharded_utils.py b/exo/inference/mlx/sharded_utils.py deleted file mode 100644 index 34f29604..00000000 --- a/exo/inference/mlx/sharded_utils.py +++ /dev/null @@ -1,257 +0,0 @@ -# Adapted from https://github.com/ml-explore/mlx-examples/blob/main/llms/mlx_lm/utils.py - -import glob -import importlib -import json -import logging -import asyncio -import aiohttp -from functools import partial -from pathlib import Path -from typing import Optional, Tuple, Union, List, Callable -from PIL import Image -from io import BytesIO -import base64 -import traceback - -import mlx.core as mx -import mlx.nn as nn -from transformers import AutoProcessor - -from mlx_lm.tokenizer_utils import load_tokenizer, TokenizerWrapper - -from exo import DEBUG -from exo.inference.tokenizers import resolve_tokenizer -from ..shard import Shard - - -class ModelNotFoundError(Exception): - def __init__(self, message): - self.message = message - super().__init__(self.message) - - -MODEL_REMAPPING = { - "mistral": "llama", # mistral is compatible with llama - "phi-msft": "phixtral", -} - - -def _get_classes(config: dict): - """ - Retrieve the model and model args classes based on the configuration. - - Args: - config (dict): The model configuration. - - Returns: - A tuple containing the Model class and the ModelArgs class. - """ - model_type = config["model_type"] - model_type = MODEL_REMAPPING.get(model_type, model_type) - try: - arch = importlib.import_module(f"exo.inference.mlx.models.{model_type}") - except ImportError: - msg = f"Model type {model_type} not supported." - logging.error(msg) - traceback.print_exc() - raise ValueError(msg) - - return arch.Model, arch.ModelArgs - - -def load_config(model_path: Path) -> dict: - try: - config_path = model_path / "config.json" - if config_path.exists(): - with open(config_path, "r") as f: - config = json.load(f) - return config - - model_index_path = model_path / "model_index.json" - if model_index_path.exists(): - config = load_model_index(model_path, model_index_path) - return config - except FileNotFoundError: - logging.error(f"Config file not found in {model_path}") - raise - return config - -def load_model_shard( - model_path: Path, - shard: Shard, - lazy: bool = False, - model_config: dict = {}, -) -> nn.Module: - """ - Load and initialize the model from a given path. - - Args: - model_path (Path): The path to load the model from. - lazy (bool): If False eval the model parameters to make sure they are - loaded in memory before returning, otherwise they will be loaded - when needed. Default: ``False`` - model_config(dict, optional): Configuration parameters for the model. - Defaults to an empty dictionary. - - Returns: - nn.Module: The loaded and initialized model. - - Raises: - FileNotFoundError: If the weight files (.safetensors) are not found. - ValueError: If the model class or args class are not found or cannot be instantiated. - """ - config = load_config(model_path) - config.update(model_config) - - # TODO hack - config["shard"] = { - "model_id": model_path.name, - "start_layer": shard.start_layer, - "end_layer": shard.end_layer, - "n_layers": shard.n_layers, - } - - weight_files = glob.glob(str(model_path/"model*.safetensors")) - - if not weight_files: - # Try weight for back-compat - weight_files = glob.glob(str(model_path/"weight*.safetensors")) - - model_class, model_args_class = _get_classes(config=config) - - class ShardedModel(model_class): - def __init__(self, args): - super().__init__(args) - self.shard = Shard(args.shard.model_id, args.shard.start_layer, args.shard.end_layer, args.shard.n_layers) - - def __call__(self, x, *args, **kwargs): - y = super().__call__(x, *args, **kwargs) - return y - - model_args = model_args_class.from_dict(config) - model = ShardedModel(model_args) - - if config.get("model_index", False): - model.load() - return model - - if not weight_files: - logging.error(f"No safetensors found in {model_path}") - raise FileNotFoundError(f"No safetensors found in {model_path}") - - weights = {} - for wf in sorted(weight_files): - if DEBUG >= 8: - layer_nums = set() - for k in mx.load(wf): - if k.startswith("model.layers."): - layer_num = int(k.split(".")[2]) - layer_nums.add(layer_num) - if k.startswith("language_model.model.layers."): - layer_num = int(k.split(".")[3]) - layer_nums.add(layer_num) - print(f"\"{wf.split('/')[-1]}\": {sorted(layer_nums)},") - - weights.update(mx.load(wf)) - - - - if hasattr(model, "sanitize"): - weights = model.sanitize(weights) - if DEBUG >= 8: - print(f"\n|| {config=} ||\n") - - if (quantization := config.get("quantization", None)) is not None: - # Handle legacy models which may not have everything quantized - def class_predicate(p, m): - if not hasattr(m, "to_quantized"): - return False - return f"{p}.scales" in weights - - - nn.quantize( - model, - **quantization, - class_predicate=class_predicate, - ) - - model.load_weights(list(weights.items()), strict=True) - - if not lazy: - mx.eval(model.parameters()) - - model.eval() - return model - -async def load_shard( - model_path: str, - shard: Shard, - tokenizer_config={}, - model_config={}, - adapter_path: Optional[str] = None, - lazy: bool = False, -) -> Tuple[nn.Module, TokenizerWrapper]: - model = load_model_shard(model_path, shard, lazy, model_config) - - # TODO: figure out a generic solution - if model.model_type == "llava": - processor = AutoProcessor.from_pretrained(model_path) - processor.eos_token_id = processor.tokenizer.eos_token_id - processor.encode = processor.tokenizer.encode - return model, processor - elif hasattr(model, "tokenizer"): - tokenizer = model.tokenizer - return model, tokenizer - else: - tokenizer = await resolve_tokenizer(model_path) - return model, tokenizer - - -async def get_image_from_str(_image_str: str): - image_str = _image_str.strip() - - if image_str.startswith("http"): - async with aiohttp.ClientSession() as session: - async with session.get(image_str, timeout=10) as response: - content = await response.read() - return Image.open(BytesIO(content)).convert("RGB") - elif image_str.startswith("data:image/"): - # Extract the image format and base64 data - format_prefix, base64_data = image_str.split(";base64,") - image_format = format_prefix.split("/")[1].lower() - if DEBUG >= 2: print(f"{image_str=} {image_format=}") - imgdata = base64.b64decode(base64_data) - img = Image.open(BytesIO(imgdata)) - - # Convert to RGB if not already - if img.mode != "RGB": - img = img.convert("RGB") - - return img - else: - raise ValueError("Invalid image_str format. Must be a URL or a base64 encoded image.") - -# loading a combined config for all models in the index -def load_model_index(model_path: Path, model_index_path: Path): - models_config = {} - with open(model_index_path, "r") as f: - model_index = json.load(f) - models_config["model_index"] = True - models_config["model_type"] = model_index["_class_name"] - models_config["models"] = {} - for model in model_index.keys(): - model_config_path = glob.glob(str(model_path / model / "*config.json")) - if len(model_config_path)>0: - with open(model_config_path[0], "r") as f: - model_config = { } - model_config["model_type"] = model - model_config["config"] = json.load(f) - model_config["path"] = model_path / model - if model_config["path"]/"*model.safetensors": - model_config["config"].update({"weight_files": list(glob.glob(str(model_config["path"]/"*model.safetensors")))}) - model_config["path"] = str(model_path / model) - m = {} - m[model] = model_config - models_config.update(m) - return models_config diff --git a/exo/inference/mlx/test_non_blocking.py b/exo/inference/mlx/test_non_blocking.py deleted file mode 100644 index 4eb2e9fb..00000000 --- a/exo/inference/mlx/test_non_blocking.py +++ /dev/null @@ -1,81 +0,0 @@ -import asyncio -import time -import numpy as np -from exo.inference.mlx.sharded_inference_engine import MLXDynamicShardInferenceEngine -from exo.download.new_shard_download import NewShardDownloader -from exo.inference.shard import Shard -from exo.models import build_base_shard -from collections import deque -from statistics import mean, median - -async def test_non_blocking(): - # Setup - shard_downloader = NewShardDownloader() - engine = MLXDynamicShardInferenceEngine(shard_downloader) - _shard = build_base_shard("llama-3.1-8b", "MLXDynamicShardInferenceEngine") - shard = Shard(_shard.model_id, _shard.start_layer, _shard.n_layers - 1, _shard.n_layers) - await engine.ensure_shard(shard) - - queue = asyncio.Queue() - measurements = deque(maxlen=1000000) - running = True - - async def mlx_worker(): - try: - start_time = time.time() - count = 0 - while running and (time.time() - start_time) < 5: # Hard time limit - start = time.perf_counter_ns() - await engine.infer_prompt("req1", shard, "test prompt") - duration = (time.perf_counter_ns() - start) / 1_000_000 # Convert to ms - count += 1 - print(f"MLX operation {count} took: {duration:.3f}ms") - except asyncio.CancelledError: - pass - finally: - print(f"\nTotal MLX operations completed: {count}") - print(f"Average rate: {count/5:.1f} ops/second") - - async def latency_producer(): - try: - start_time = time.perf_counter_ns() - count = 0 - while running: - await queue.put(time.perf_counter_ns()) - count += 1 - await asyncio.sleep(0) # Yield to event loop without delay - duration = (time.perf_counter_ns() - start_time) / 1e9 # Convert to seconds - print(f"\nProducer iterations: {count}") - print(f"Producer rate: {count/duration:.1f} iterations/second") - except asyncio.CancelledError: - pass - - async def latency_consumer(): - try: - while running: - timestamp = await queue.get() - latency = (time.perf_counter_ns() - timestamp) / 1_000_000 # Convert to ms - measurements.append(latency) - queue.task_done() - except asyncio.CancelledError: - pass - - tasks = [ - asyncio.create_task(mlx_worker()), - asyncio.create_task(latency_producer()), - asyncio.create_task(latency_consumer()) - ] - - try: - await asyncio.wait_for(asyncio.gather(*tasks), timeout=6) - except asyncio.TimeoutError: - print("\nTest timed out") - finally: - running = False - for task in tasks: - task.cancel() - await asyncio.gather(*tasks, return_exceptions=True) - print(f"\nFinal measurement count: {len(measurements)}") - -if __name__ == "__main__": - asyncio.run(test_non_blocking()) diff --git a/exo/inference/mlx/test_sharded_model.py b/exo/inference/mlx/test_sharded_model.py deleted file mode 100644 index c9743d07..00000000 --- a/exo/inference/mlx/test_sharded_model.py +++ /dev/null @@ -1,52 +0,0 @@ -from exo.inference.shard import Shard -import mlx.core as mx -import mlx.nn as nn -from typing import Optional -import numpy as np - - -class DummyModel(nn.Module): - def __init__(self, shard: Optional[Shard] = None): - self.shard = shard - self.layers = [ - nn.Linear(8, 128), - nn.Linear(128, 128), - nn.Linear(128, 128), - nn.Linear(128, 128), - nn.Linear(128, 8), - ] - - self.n_kv_heads = 4 - self.head_dim = 4 - - def __call__(self, x, cache=None): - if self.shard: - for layer in self.layers[self.shard.start_layer:self.shard.end_layer + 1]: - x = layer(x) - if self.shard.is_last_layer(): - x = x.reshape((1, 2, 4)) - else: - for layer in self.layers: - x = layer(x) - x = x.reshape((1, 2, 4)) - - return x - - -model = DummyModel() -model.save_weights("./test_weights.npz") -n_layers = 5 -shard1 = Shard("test", 0, n_layers // 2, n_layers) -sharded_model1 = DummyModel(shard1) -shard2 = Shard("test", n_layers//2 + 1, n_layers - 1, n_layers) -sharded_model2 = DummyModel(shard2) - -model.load_weights("./test_weights.npz") -sharded_model1.load_weights("./test_weights.npz") -sharded_model2.load_weights("./test_weights.npz") - -fullresp = model(mx.array([1, 2, 3, 4, 5, 6, 7, 8])) -resp1 = sharded_model1(mx.array([1, 2, 3, 4, 5, 6, 7, 8])) -resp2 = sharded_model2(resp1) - -assert np.all(np.array(fullresp) == np.array(resp2)) diff --git a/exo/inference/shard.py b/exo/inference/shard.py deleted file mode 100644 index 21b662f6..00000000 --- a/exo/inference/shard.py +++ /dev/null @@ -1,39 +0,0 @@ -from dataclasses import dataclass, field - - -@dataclass(frozen=True) -class Shard: - model_id: str - start_layer: int - end_layer: int - n_layers: int - - def __hash__(self): - return hash((self.model_id, self.start_layer, self.end_layer, self.n_layers)) - - def is_first_layer(self) -> bool: - return self.start_layer == 0 - - def is_last_layer(self) -> bool: - return self.end_layer == self.n_layers - 1 - - def get_layer_count(self) -> int: - return self.end_layer - self.start_layer + 1 - - def to_dict(self) -> dict: - return { - "model_id": self.model_id, - "start_layer": self.start_layer, - "end_layer": self.end_layer, - "n_layers": self.n_layers, - } - - def from_dict(data: dict) -> 'Shard': - return Shard(**data) - - def overlaps(self, other: 'Shard') -> bool: - return shards_overlap(self, other) - - -def shards_overlap(shard1: Shard, shard2: Shard) -> bool: - return (shard1.model_id == shard2.model_id and max(shard1.start_layer, shard2.start_layer) <= min(shard1.end_layer, shard2.end_layer)) diff --git a/exo/inference/test_dummy_inference_engine.py b/exo/inference/test_dummy_inference_engine.py deleted file mode 100644 index fad5178e..00000000 --- a/exo/inference/test_dummy_inference_engine.py +++ /dev/null @@ -1,47 +0,0 @@ -import pytest -import numpy as np -from exo.inference.dummy_inference_engine import DummyInferenceEngine -from exo.inference.shard import Shard - - -@pytest.mark.asyncio -async def test_dummy_inference_specific(): - engine = DummyInferenceEngine() - test_shard = Shard(model_id="test_model", start_layer=0, end_layer=1, n_layers=1) - test_prompt = "This is a test prompt" - - result, _ = await engine.infer_prompt("test_request", test_shard, test_prompt) - - print(f"Inference result shape: {result.shape}") - - assert result.shape[0] == 1, "Result should be a 2D array with first dimension 1" - - -@pytest.mark.asyncio -async def test_dummy_inference_engine(): - # Initialize the DummyInferenceEngine - engine = DummyInferenceEngine() - - # Create a test shard - shard = Shard(model_id="test_model", start_layer=0, end_layer=1, n_layers=1) - - # Test infer_prompt - output, _ = await engine.infer_prompt("test_id", shard, "Test prompt") - - assert isinstance(output, np.ndarray), "Output should be a numpy array" - assert output.ndim == 2, "Output should be 2-dimensional" - - # Test infer_tensor - input_tensor = np.array([[1, 2, 3]]) - output, _ = await engine.infer_tensor("test_id", shard, input_tensor) - - assert isinstance(output, np.ndarray), "Output should be a numpy array" - assert output.ndim == 2, "Output should be 2-dimensional" - - print("All tests passed!") - - -if __name__ == "__main__": - import asyncio - asyncio.run(test_dummy_inference_engine()) - asyncio.run(test_dummy_inference_specific()) diff --git a/exo/inference/test_inference_engine.py b/exo/inference/test_inference_engine.py deleted file mode 100644 index 956a1162..00000000 --- a/exo/inference/test_inference_engine.py +++ /dev/null @@ -1,54 +0,0 @@ -from exo.inference.mlx.sharded_inference_engine import MLXDynamicShardInferenceEngine -from exo.inference.inference_engine import InferenceEngine -from exo.download.new_shard_download import NewShardDownloader -from exo.inference.shard import Shard -from exo.helpers import DEBUG -import os -import asyncio -import numpy as np - - -# An inference engine should work the same for any number of Shards, as long as the Shards are continuous. -async def test_inference_engine(inference_engine_1: InferenceEngine, inference_engine_2: InferenceEngine, model_id: str, n_layers: int): - prompt = "In a single word only, what is the last name of the current president of the USA?" - resp_full, _ = await inference_engine_1.infer_prompt("A", shard=Shard(model_id=model_id, start_layer=0, end_layer=n_layers - 1, n_layers=n_layers), prompt=prompt) - token_full = await inference_engine_1.sample(resp_full) - token_full = token_full.reshape(1, -1) - next_resp_full, _ = await inference_engine_1.infer_tensor( - "A", - shard=Shard(model_id=model_id, start_layer=0, end_layer=n_layers - 1, n_layers=n_layers), - input_data=token_full, - ) - - pp = n_layers // 2 - resp1, _ = await inference_engine_1.infer_prompt("B", shard=Shard(model_id=model_id, start_layer=0, end_layer=pp, n_layers=n_layers), prompt=prompt) - resp2, _ = await inference_engine_2.infer_tensor( - "B", - shard=Shard(model_id=model_id, start_layer=pp + 1, end_layer=n_layers - 1, n_layers=n_layers), - input_data=resp1, - ) - tokens2 = await inference_engine_1.sample(resp2) - tokens2 = tokens2.reshape(1, -1) - resp3, _ = await inference_engine_1.infer_tensor( - "B", - shard=Shard(model_id=model_id, start_layer=0, end_layer=pp, n_layers=n_layers), - input_data=tokens2, - ) - resp4, _ = await inference_engine_2.infer_tensor( - "B", - shard=Shard(model_id=model_id, start_layer=pp + 1, end_layer=n_layers - 1, n_layers=n_layers), - input_data=resp3, - ) - - assert np.array_equal(resp_full, resp2) - assert np.array_equal(next_resp_full, resp4) - - -asyncio.run(test_inference_engine(MLXDynamicShardInferenceEngine(NewShardDownloader()), MLXDynamicShardInferenceEngine(NewShardDownloader()), "llama-3.2-1b", 16)) - -if os.getenv("RUN_TINYGRAD", default="0") == "1": - import tinygrad - import os - from exo.inference.tinygrad.inference import TinygradDynamicShardInferenceEngine - tinygrad.helpers.DEBUG.value = int(os.getenv("TINYGRAD_DEBUG", default="0")) - asyncio.run(test_inference_engine(TinygradDynamicShardInferenceEngine(NewShardDownloader()), TinygradDynamicShardInferenceEngine(NewShardDownloader()), "llama-3.2-1b", 32)) diff --git a/exo/inference/tinygrad/__init__.py b/exo/inference/tinygrad/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/exo/inference/tinygrad/inference.py b/exo/inference/tinygrad/inference.py deleted file mode 100644 index 8e336dce..00000000 --- a/exo/inference/tinygrad/inference.py +++ /dev/null @@ -1,157 +0,0 @@ -from pathlib import Path -import json -import os -from exo.inference.tinygrad.models.llama import Transformer, TransformerShard, convert_from_huggingface, fix_bf16, sample_logits -from exo.inference.shard import Shard -from exo.inference.tokenizers import resolve_tokenizer -from tinygrad.nn.state import safe_save, safe_load, get_state_dict, load_state_dict -from tinygrad import Tensor, nn, Context, TinyJit -from exo.inference.inference_engine import InferenceEngine -import numpy as np -from exo.inference.tinygrad.tinygrad_helpers import concat_weights, load -from exo.download.shard_download import ShardDownloader -from concurrent.futures import ThreadPoolExecutor -from .stateful_model import make_prompt_state -from .losses import length_masked_ce_loss -from collections import OrderedDict -import asyncio -from typing import Optional -Tensor.no_grad = True -# default settings -TEMPERATURE = int(os.getenv("TEMPERATURE", 0.85)) -TOP_K = 25 -TOP_P = 0.9 -ALPHA_F = 0.1 -ALPHA_P = 0.0 -MODEL_PARAMS = { - "1B": { - "args": { - "dim": 2048, "n_heads": 32, "n_kv_heads": 8, "n_layers": 16, "norm_eps": 1e-5, "rope_theta": 500000, "vocab_size": 128256, "hidden_dim": 8192, - "rope_scaling": {"factor": 32.0, "high_freq_factor": 4.0, "low_freq_factor": 1.0, "original_max_position_embeddings": 8192, "rope_type": "llama3"}, "tie_word_embeddings": True - }, "files": 1 - }, "3B": { - "args": { - "dim": 3072, "n_heads": 24, "n_kv_heads": 8, "n_layers": 28, "norm_eps": 1e-5, "rope_theta": 500000, "vocab_size": 128256, "hidden_dim": 8192, - "rope_scaling": {"factor": 32.0, "high_freq_factor": 4.0, "low_freq_factor": 1.0, "original_max_position_embeddings": 8192, "rope_type": "llama3"}, "tie_word_embeddings": True - }, "files": 1 - }, "8B": {"args": {"dim": 4096, "n_heads": 32, "n_kv_heads": 8, "n_layers": 32, "norm_eps": 1e-5, "rope_theta": 500000, "vocab_size": 128256, "hidden_dim": 14336}, "files": 1}, - "70B": {"args": {"dim": 8192, "n_heads": 64, "n_kv_heads": 8, "n_layers": 80, "norm_eps": 1e-5, "rope_theta": 500000, "vocab_size": 128256, "hidden_dim": 28672}, "files": 8} -} - - -def build_transformer(model_path: Path, shard: Shard, model_size="8B", device=None): - # build model - linear = nn.Linear - model = Transformer(**MODEL_PARAMS[model_size]["args"], linear=linear, max_context=8192, jit=True, shard=shard) - - # load weights - if model_path.is_dir(): - if (model_path/"model.safetensors.index.json").exists(): weights = load(str(model_path/"model.safetensors.index.json"), shard) - elif (model_path/"model.safetensors").exists(): weights = load(str(model_path/"model.safetensors"), shard) - else: weights = concat_weights([load(str(model_path/f"consolidated.{i:02d}.pth"), shard) for i in range(MODEL_PARAMS[model_size]["files"])], device[0] if isinstance(device, tuple) else device) - else: - weights = load(str(model_path), shard) - weights = convert_from_huggingface(weights, model, MODEL_PARAMS[model_size]["args"]["n_heads"], MODEL_PARAMS[model_size]["args"]["n_kv_heads"]) - weights = fix_bf16(weights) - - with Context(BEAM=0): - # replace weights in model - load_state_dict(model, weights, strict=False, consume=False) # consume=True - model = TransformerShard(shard, model) - - return model - -_executor = ThreadPoolExecutor(max_workers=1) # singleton so tinygrad always runs on the same thread -class TinygradDynamicShardInferenceEngine(InferenceEngine): - def __init__(self, shard_downloader: ShardDownloader): - self.shard = None - self.shard_downloader = shard_downloader - self.states = OrderedDict() - self.executor = _executor - - def poll_state(self, x, request_id: str, max_states=2): - if request_id not in self.states: - if len(self.states) >= max_states: - self.states.popitem(last=False) - self.states[request_id] = make_prompt_state(x, self.model) - else: - self.states.move_to_end(request_id) - state = self.states[request_id] - return {"start_pos": state.start, "cache": state.cache} - - async def sample(self, x: np.ndarray, temp=TEMPERATURE, top_p: float = 0.0) -> np.ndarray: - def sample_wrapper(): - logits = x[:, -1, :] - return sample_logits(Tensor(logits).flatten(), temp, 0, 0.8, top_p, 0.0).realize().numpy().astype(int) - return await asyncio.get_running_loop().run_in_executor(self.executor, sample_wrapper) - - async def encode(self, shard: Shard, prompt: str) -> np.ndarray: - await self.ensure_shard(shard) - tokens = await asyncio.get_running_loop().run_in_executor(self.executor, self.tokenizer.encode, prompt) - return await asyncio.get_running_loop().run_in_executor(self.executor, np.array, tokens) - - async def decode(self, shard: Shard, tokens) -> str: - await self.ensure_shard(shard) - tokens = await asyncio.get_running_loop().run_in_executor(self.executor, self.tokenizer.decode, tokens) - return tokens - - async def load_checkpoint(self, shard: Shard, path: str): - await self.ensure_shard(shard) - state_dict = safe_load(path) - await asyncio.get_running_loop().run_in_executor(self.executor, load_state_dict, self.model, state_dict) - - async def save_checkpoint(self, shard: Shard, path: str): - await self.ensure_shard(shard) - state_dict = await asyncio.get_running_loop().run_in_executor(self.executor, get_state_dict, self.model) - safe_save(state_dict, path) - - async def infer_tensor(self, request_id: str, shard: Shard, input_data: np.ndarray, inference_state: Optional[dict] = None) -> tuple[np.ndarray, Optional[dict]]: - await self.ensure_shard(shard) - def wrap_infer(): - x = Tensor(input_data) - h = self.model.embed(x) - state = self.poll_state(h, request_id) - out = self.model.forward(h, **state) - self.states[request_id].start += x.shape[1] - return out.numpy() - output_data = await asyncio.get_running_loop().run_in_executor(self.executor, wrap_infer) - return output_data, inference_state - - async def evaluate(self, request_id: str, shard: Shard, inputs, targets, lengths, loss=length_masked_ce_loss): - def step(x, y, l): - Tensor.training = False - return self.session['loss'](self.model, x, y, l) - await self.ensure_shard(shard) - score = await asyncio.get_running_loop().run_in_executor(self.executor, lambda: self.session['jit'](Tensor(inputs), targets, lengths)) - out = score.numpy() - return out - - async def train(self, request_id: str, shard: Shard, inputs, targets, lengths, loss=length_masked_ce_loss, opt=nn.optim.Adam, lr=1e-5): - def step(x, y, l): - Tensor.training = True - score = self.session['loss'](self.model, x, y, l) - self.session['opt'].zero_grad() - score.backward() - self.session['opt'].step() - return score - await self.ensure_shard(shard) - - score = await asyncio.get_running_loop().run_in_executor(self.executor, lambda: self.session['jit'](Tensor(inputs), targets, lengths).realize()) - - return loss.numpy(), loss.numpy() - - async def ensure_shard(self, shard: Shard): - if self.shard == shard: - return - - model_path = await self.shard_downloader.ensure_shard(shard, self.__class__.__name__) - - if self.shard != shard: - loop = asyncio.get_running_loop() - parameters = "1B" if "1b" in shard.model_id.lower() else "3B" if "3b" in shard.model_id.lower() else "8B" if "8b" in shard.model_id.lower() else "70B" - model_shard = await loop.run_in_executor(self.executor, build_transformer, model_path, shard, parameters) - - tokenizer_path = str((model_path if model_path.is_dir() else model_path.parent)) - self.tokenizer = await resolve_tokenizer(tokenizer_path) - self.shard = shard - self.model = model_shard diff --git a/exo/inference/tinygrad/losses.py b/exo/inference/tinygrad/losses.py deleted file mode 100644 index 02f54231..00000000 --- a/exo/inference/tinygrad/losses.py +++ /dev/null @@ -1,14 +0,0 @@ -from tinygrad import Tensor, dtypes -import numpy as np -def length_masked_ce_loss(model, inputs, targets, lengths): - # Run model on inputs - logits = model(inputs).cast(dtypes.float32).contiguous() - - # Mask padding tokens - length_mask = Tensor(np.arange(inputs.shape[1])[None, :] < lengths[:, None], requires_grad=False) - - # Calculate the loss - ce = logits.sparse_categorical_crossentropy(Tensor(targets, requires_grad=False)).mul(length_mask) - loss = ce.sum() / length_mask.sum() - return loss - diff --git a/exo/inference/tinygrad/models/__init__.py b/exo/inference/tinygrad/models/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/exo/inference/tinygrad/models/llama.py b/exo/inference/tinygrad/models/llama.py deleted file mode 100644 index fbe0d69f..00000000 --- a/exo/inference/tinygrad/models/llama.py +++ /dev/null @@ -1,327 +0,0 @@ -from typing import Tuple, Union, Optional, Dict, Any, List -from tinygrad import Tensor, Variable, TinyJit, dtypes, nn, Device -from tinygrad.helpers import getenv -from collections import OrderedDict - - -# https://github.com/facebookresearch/llama/blob/1076b9c51c77ad06e9d7ba8a4c6df775741732bd/llama/model.py#L47 -def precompute_freqs_cis(dim: int, end: int, theta: float = 10000.0, dtype=dtypes.half, rope_scaling: Optional[Dict[str, float]] = None) -> Tensor: - freqs = 1.0/(theta**(Tensor.arange(0, dim, 2)[:(dim // 2)]/dim)) - - if rope_scaling: - factor = rope_scaling.get('factor', 1.0) - low_freq_factor = rope_scaling.get('low_freq_factor', 1.0) - high_freq_factor = rope_scaling.get('high_freq_factor', 1.0) - original_max_pos_emb = rope_scaling.get('original_max_position_embeddings', end) - - freqs[:dim // 4] *= low_freq_factor - freqs[dim // 4:] = freqs[dim // 4:].contiguous()*high_freq_factor - freqs *= (original_max_pos_emb/end)**(1.0/factor) - - freqs = Tensor.arange(end).unsqueeze(dim=1)*freqs.unsqueeze(dim=0) - # TODO: move dtype outside this - return Tensor.stack(freqs.cos().cast(dtype), freqs.sin().cast(dtype), dim=-1).reshape(1, end, 1, dim // 2, 2) - - -# (a+i*b) * (c+i*d) = (ac-bd) + i*(ad+bc) -def complex_mult(A, c, d): - a, b = A[..., 0:1], A[..., 1:2] - ro = a*c - b*d - co = a*d + b*c - return ro.cat(co, dim=-1) - - -def apply_rotary_emb(xq: Tensor, xk: Tensor, freqs_cis: Tensor) -> Tuple[Tensor, Tensor]: - assert freqs_cis.shape[1] == xq.shape[1] == xk.shape[1], f"freqs_cis shape mismatch {freqs_cis.shape} xq:{xq.shape} xk:{xk.shape}" - xq = xq.reshape(*xq.shape[0:-1], -1, 2) - xk = xk.reshape(*xk.shape[0:-1], -1, 2) - assert len(xq.shape) == len(xk.shape) == len(freqs_cis.shape) == 5 - c, d = freqs_cis[..., 0:1], freqs_cis[..., 1:2] - xq_out = complex_mult(xq, c, d) - xk_out = complex_mult(xk, c, d) - return xq_out.flatten(3), xk_out.flatten(3) - - -def repeat_kv(x: Tensor, n_rep: int) -> Tensor: - bs, seqlen, n_kv_heads, head_dim = x.shape - if n_rep == 1: return x - # NOTE: this is different from x.repeat((1, 1, n_rep, 1)) - return x.repeat((1, 1, 1, n_rep)).reshape(bs, seqlen, n_kv_heads*n_rep, head_dim) - -class Attention: - def __init__(self, dim, n_heads, n_kv_heads, max_context, linear=nn.Linear): - self.n_heads = n_heads - self.n_kv_heads = n_kv_heads if n_kv_heads is not None else n_heads # n_kv_heads != n_heads implies MQA [arxiv/2307.09288, A.2.1] - self.head_dim = dim // n_heads - self.n_rep = self.n_heads // self.n_kv_heads - self.max_context = max_context - - self.wq = linear(dim, self.n_heads*self.head_dim, bias=False) - self.wk = linear(dim, self.n_kv_heads*self.head_dim, bias=False) - self.wv = linear(dim, self.n_kv_heads*self.head_dim, bias=False) - self.wo = linear(self.n_heads*self.head_dim, dim, bias=False) - - def __call__(self, x: Tensor, start_pos: Union[Variable, int], freqs_cis: Tensor, mask: Optional[Tensor], cache: Optional[Tensor]=None) -> Tensor: - if getenv("WQKV"): - if not hasattr(self, 'wqkv'): self.wqkv = Tensor.cat(self.wq.weight, self.wk.weight, self.wv.weight) - xqkv = x @ self.wqkv.T - xq, xk, xv = xqkv.split([self.wq.weight.shape[0], self.wk.weight.shape[0], self.wv.weight.shape[0]], dim=2) - else: - xq, xk, xv = self.wq(x), self.wk(x), self.wv(x) - - xq = xq.reshape(xq.shape[0], xq.shape[1], self.n_heads, self.head_dim) - xk = xk.reshape(xk.shape[0], xk.shape[1], self.n_kv_heads, self.head_dim) - xv = xv.reshape(xv.shape[0], xv.shape[1], self.n_kv_heads, self.head_dim) - - xq, xk = apply_rotary_emb(xq, xk, freqs_cis) - bsz, seqlen, _, _ = xq.shape - - if cache is not None: - # update the cache - assert xk.dtype == xv.dtype == cache.dtype, f"{xk.dtype=}, {xv.dtype=}, {cache.dtype=}" - cache.shrink((None, None, (start_pos, start_pos + seqlen), None, None)).assign(Tensor.stack(xk, xv)).realize() - - keys = cache[0].shrink((None, (0, start_pos + seqlen), None, None)) if start_pos > 0 else xk - values = cache[1].shrink((None, (0, start_pos + seqlen), None, None)) if start_pos > 0 else xv - else: - keys = xk - values = xv - - keys, values = repeat_kv(keys, self.n_rep), repeat_kv(values, self.n_rep) - xq, keys, values = xq.transpose(1, 2), keys.transpose(1, 2), values.transpose(1, 2) - attn = xq.scaled_dot_product_attention(keys, values, mask).transpose(1, 2) - attn = attn.reshape(bsz, seqlen, -1) - return self.wo(attn) - - -class FeedForward: - def __init__(self, dim: int, hidden_dim: int, linear=nn.Linear): - self.w1 = linear(dim, hidden_dim, bias=False) - self.w2 = linear(hidden_dim, dim, bias=False) - self.w3 = linear(dim, hidden_dim, bias=False) # the gate in Gated Linear Unit - - def __call__(self, x: Tensor) -> Tensor: - return self.w2(self.w1(x).silu()*self.w3(x)) # SwiGLU [arxiv/2002.05202, eq (5)] - - -class TransformerBlock: - def __init__(self, dim: int, hidden_dim: int, n_heads: int, n_kv_heads: int, norm_eps: float, max_context: int, linear=nn.Linear, feed_forward=FeedForward): - self.attention = Attention(dim, n_heads, n_kv_heads, max_context, linear) - self.feed_forward = feed_forward(dim, hidden_dim, linear) - self.attention_norm = nn.RMSNorm(dim, norm_eps) - self.ffn_norm = nn.RMSNorm(dim, norm_eps) - - def __call__(self, x: Tensor, start_pos: Union[Variable, int], freqs_cis: Tensor, mask: Optional[Tensor], cache: Optional[Tensor]=None): - h = x + self.attention(self.attention_norm(x), start_pos, freqs_cis, mask, cache=cache) - return (h + self.feed_forward(self.ffn_norm(h))).contiguous() - - -# standard openai sampling -def sample_logits(logits: Tensor, temp: float, k: int, p: float, af: float, ap: float): - assert logits.ndim == 1, "only works on 1d tensors" - assert 0 <= p <= 1, "p must be between 0 and 1" - assert 0 <= k <= logits.numel(), "k must be between 0 and numel" - - # if temperature is very low just use argmax - if temp < 1e-6: return logits.argmax().reshape(1) - - # alpha sampling - if af or ap: - if not hasattr(sample, "alpha_counter"): - setattr(sample, "alpha_counter", Tensor.zeros_like(logits, dtype=dtypes.int32).contiguous()) - logits = logits - (sample.alpha_counter*af + (sample.alpha_counter > 0)*ap) - - # replace NaNs with -inf - logits = (logits != logits).where(-float("inf"), logits) - - # softmax - t = (logits/temp).softmax() - - counter, counter2 = Tensor.arange(t.numel(), device=logits.device).contiguous(), Tensor.arange(t.numel() - 1, -1, -1, device=logits.device).contiguous() - # top k - if k: - output, output_indices = Tensor.zeros(k, device=logits.device).contiguous(), Tensor.zeros(k, device=logits.device, dtype=dtypes.int32).contiguous() - for i in range(k): - t_argmax = (t.numel() - ((t == (t_max := t.max()))*counter2).max() - 1).cast(dtypes.default_int) - output = output + t_max.unsqueeze(0).pad(((i, k - i - 1),)) - output_indices = output_indices + t_argmax.unsqueeze(0).pad(((i, k - i - 1),)) - t = (counter == t_argmax).where(0, t) - - # approximate top p - # because we are already limited to top k elements we can do top p "without sorting" - output_cumsum = output[::-1]._cumsum()[::-1] + t.sum() - output = (output_cumsum >= (1 - p))*output - output_indices = (output_cumsum >= (1 - p))*output_indices - - # sample - output_idx = output.multinomial() - output_token = output_indices[output_idx] - else: - output_token = t.multinomial() - - # increase alpha counter - if af or ap: - sample.alpha_counter = (counter == output_token).where(sample.alpha_counter + 1, sample.alpha_counter) - - return output_token - - -from exo.inference.shard import Shard - - -class Transformer: - def __init__( - self, - dim: int, - hidden_dim: int, - n_heads: int, - n_layers: int, - norm_eps: float, - vocab_size, - shard: Shard = None, - linear=nn.Linear, - n_kv_heads=None, - rope_theta=10000, - max_context=1024, - jit=True, - feed_forward=FeedForward, - rope_scaling: Optional[Dict[str, float]] = None, - tie_word_embeddings=False, - ): - self.layers = [TransformerBlock(dim, hidden_dim, n_heads, n_kv_heads, norm_eps, max_context, linear, feed_forward=feed_forward) for _ in range(n_layers)] - self.norm = nn.RMSNorm(dim, norm_eps) - self.tok_embeddings = nn.Embedding(vocab_size, dim) - self.output = nn.Linear(dim, vocab_size, bias=False) - if tie_word_embeddings: - self.output.weight = self.tok_embeddings.weight - self.max_context = max_context - self.freqs_cis = precompute_freqs_cis(dim // n_heads, self.max_context*2, rope_theta, rope_scaling=rope_scaling).contiguous() - self.forward_jit = TinyJit(self.forward_base) if jit else None - self.shard = shard - - def forward_base(self, x: Tensor, start_pos: Union[Variable, int], cache: Optional[List[Tensor]] = None): - seqlen = x.shape[1] - freqs_cis = self.freqs_cis.shrink((None, (start_pos, start_pos + seqlen), None, None, None)) - mask = Tensor.full((1, 1, seqlen, start_pos + seqlen), float("-100000000"), dtype=x.dtype, device=x.device).triu(start_pos + 1).realize() if seqlen > 1 else None - - h = x - - if cache is None: - cache = [None for _ in range(self.shard.start_layer, self.shard.end_layer + 1)] - for i, c in zip(range(self.shard.start_layer, self.shard.end_layer + 1), cache): - layer = self.layers[i] - h = layer(h, start_pos, freqs_cis, mask, cache=c) - - if self.shard.is_last_layer(): - logits = self.output(self.norm(h)).float().realize() - return logits - else: - return h - - def embed(self, inputs: Tensor): - if self.shard.is_first_layer(): - h = self.tok_embeddings(inputs) - else: - h = inputs - return h - - def forward(self, x: Tensor, start_pos: int, cache: Optional[List[Tensor]] = None): - if x.shape[0:2] == (1, 1) and self.forward_jit is not None and start_pos != 0: - return self.forward_jit(x, Variable("start_pos", 1, self.max_context).bind(start_pos), cache=cache) - return self.forward_base(x, start_pos, cache=cache) - - def __call__(self, x: Tensor, start_pos: Variable, cache: Optional[List[Tensor]] = None): - # TODO: better way to handle the first call v.s. the rest? - h = self.embed(x) - return self.forward(h, start_pos, cache=cache) - -class TransformerShard: - def __init__( - self, - shard: Shard, - base, - jit: bool = True, - ): - shardrange = range(shard.start_layer, shard.end_layer + 1) - self.layers = [layer for layer, n in zip(base.layers, range(shard.n_layers)) if n in shardrange] - self.norm = base.norm - self.tok_embeddings = base.tok_embeddings - self.embed = (lambda x: self.tok_embeddings(x)) if shard.is_first_layer() else (lambda x: x) - self.output = base.output - self.post = (lambda x: self.output(x)) if shard.is_last_layer() else (lambda x: x) - self.max_context = base.max_context - self.null_cache = [None for _ in shardrange] - self.freqs_cis = base.freqs_cis - self.forward_jit = TinyJit(self.forward_base) if jit else None - - def forward_base(self, x: Tensor, start_pos: Union[Variable, int], cache): - seqlen = x.shape[1] - freqs_cis = self.freqs_cis.shrink((None, (start_pos, start_pos + seqlen), None, None, None)) - mask = Tensor.full((1, 1, seqlen, start_pos + seqlen), float("-100000000"), dtype=x.dtype, device=x.device).triu(start_pos + 1).realize() if seqlen > 1 else None - - for layer, c in zip(self.layers, cache): - x = layer(x, start_pos, freqs_cis, mask, cache=c) - - out = self.post(x) - return out - - def forward(self, x: Tensor, start_pos: int, cache: Optional[List[Tensor]] = None): - if x.shape[0:2] == (1, 1) and self.forward_jit is not None and start_pos != 0: - return self.forward_jit(x, Variable("start_pos", 1, self.max_context).bind(start_pos), cache=cache) - return self.forward_base(x, start_pos, cache=cache) - - def __call__(self, x: Tensor, start_pos: Variable, cache: Optional[List[Tensor]] = None): - # TODO: better way to handle the first call v.s. the rest? - h = self.embed(x) - return self.forward(h, start_pos, cache=self.null_cache if cache is None else cache) - -# *** helpers *** - - -def convert_from_huggingface(weights: Dict[str, Tensor], model: Transformer, n_heads: int, n_kv_heads: int): - def permute(v: Tensor, n_heads: int): - return v.reshape(n_heads, 2, v.shape[0] // n_heads // 2, v.shape[1]).transpose(1, 2).reshape(*v.shape[:2]) - - keymap = { - "model.embed_tokens.weight": "tok_embeddings.weight", - **{f"model.layers.{l}.input_layernorm.weight": f"layers.{l}.attention_norm.weight" - for l in range(len(model.layers))}, - **{f"model.layers.{l}.self_attn.{x}_proj.weight": f"layers.{l}.attention.w{x}.weight" - for x in ["q", "k", "v", "o"] - for l in range(len(model.layers))}, - **{f"model.layers.{l}.post_attention_layernorm.weight": f"layers.{l}.ffn_norm.weight" - for l in range(len(model.layers))}, - **{f"model.layers.{l}.mlp.{x}_proj.weight": f"layers.{l}.feed_forward.w{y}.weight" - for x, y in {"gate": "1", "down": "2", "up": "3"}.items() - for l in range(len(model.layers))}, - "model.norm.weight": "norm.weight", - "lm_head.weight": "output.weight", - } - sd = {} - for k, v in weights.items(): - if ".rotary_emb." in k: continue - v = v.to(Device.DEFAULT) - if "model.layers" in k: - if "q_proj" in k: - v = permute(v, n_heads) - elif "k_proj" in k: - v = permute(v, n_kv_heads) - if k in keymap: - sd[keymap[k]] = v - else: - sd[k] = v - return sd - - -def fix_bf16(weights: Dict[Any, Tensor]): - if Device.DEFAULT == "CLANG": - # TODO: without casting to float16, 70B llama OOM on tinybox. - return { - k: (v.llvm_bf16_cast(dtypes.float32).to(v.device) if v.dtype == dtypes.bfloat16 else v) - for k, v in weights.items() - } - if getenv("SUPPORT_BF16", 1): - # TODO: without casting to float16, 70B llama OOM on tinybox. - return {k: v.cast(dtypes.float32).cast(dtypes.float16) if v.dtype == dtypes.bfloat16 else v for k, v in weights.items()} - # TODO: check if device supports bf16 - return {k: v.llvm_bf16_cast(dtypes.half).to(v.device) if v.dtype == dtypes.bfloat16 else v for k, v in weights.items()} diff --git a/exo/inference/tinygrad/stateful_model.py b/exo/inference/tinygrad/stateful_model.py deleted file mode 100644 index 1d9b605d..00000000 --- a/exo/inference/tinygrad/stateful_model.py +++ /dev/null @@ -1,22 +0,0 @@ -from tinygrad import Tensor, Variable -from collections import OrderedDict -from typing import List, Optional - -def create_kv_cache(x: Tensor, layer): - cache_kv = Tensor.zeros(2, x.shape[0], layer.max_context, layer.n_kv_heads, layer.head_dim, dtype=x.dtype).contiguous().realize() - if isinstance(x.device, tuple): - # TODO: instead of specifying how to shard, it can follow how xk and xv are being sharded - cache_kv.shard_((x.device), axis=3 if getenv("SHARD_KVCACHE") else None).realize() - return cache_kv.realize() - -class ModelState: - cache: List[Tensor] - start: int - def __init__(self, cache: List[Tensor], start: int = 0): - self.cache = cache - self.start = start - -def make_prompt_state(x: Tensor, model): - cache = [create_kv_cache(x, l.attention) for l in model.layers] - - return ModelState(cache) diff --git a/exo/inference/tinygrad/tinygrad_helpers.py b/exo/inference/tinygrad/tinygrad_helpers.py deleted file mode 100644 index a33b01ae..00000000 --- a/exo/inference/tinygrad/tinygrad_helpers.py +++ /dev/null @@ -1,52 +0,0 @@ -from tinygrad.nn.state import safe_load, torch_load -from tinygrad import Tensor -from pathlib import Path -import json -from typing import List -from exo.inference.shard import Shard -from exo.helpers import DEBUG -from exo.download.hf.hf_helpers import get_allow_patterns -from fnmatch import fnmatch -import re - - -# **** helper functions **** -def concat_weights(models, device=None): - def convert(name) -> Tensor: - disk_tensors: List[Tensor] = [model[name] for model in models] - if len(disk_tensors) == 1 or len(disk_tensors[0].shape) == 1: - return disk_tensors[0].to(device=device) - axis = 1 if name.endswith(".attention.wo.weight") or name.endswith(".feed_forward.w2.weight") else 0 - lazy_tensors = [data.to(device=device) for data in disk_tensors] - return lazy_tensors[0].cat(*lazy_tensors[1:], dim=axis) - - return {name: convert(name) for name in {name: None for model in models for name in model}} - - -def load(fn: str, shard: Shard): - if fn.endswith('.index.json'): - with open(fn) as fp: - weight_map = json.load(fp)['weight_map'] - parts = {} - filtered_weight_map = {} - allow_patterns = get_allow_patterns(weight_map, shard) - for k, n in weight_map.items(): - if allow_patterns is not None and not any(fnmatch(n, r) for r in allow_patterns): - continue - if k.startswith("model.layers."): - layer_num = int(k.split('.')[2]) - if layer_num < shard.start_layer or layer_num > shard.end_layer: - continue - - parts[n] = load(str(Path(fn).parent/Path(n).name), shard) - filtered_weight_map[k] = n - if DEBUG >= 2: print(f"Excluded model param keys for {shard=}: {sorted(set(weight_map.keys()) - set(filtered_weight_map.keys()))}") - return {k: parts[n][k] for k, n in filtered_weight_map.items()} - elif fn.endswith(".safetensors"): - weight_map = safe_load(fn) - for k in list(weight_map): - if (n := re.search(r"\.(\d+)\.", k)) and not (shard.start_layer <= int(n.group(1)) <= shard.end_layer): - del weight_map[k] - return weight_map - else: - return torch_load(fn) diff --git a/exo/inference/tokenizers.py b/exo/inference/tokenizers.py deleted file mode 100644 index 865765d5..00000000 --- a/exo/inference/tokenizers.py +++ /dev/null @@ -1,63 +0,0 @@ -import traceback -from os import PathLike -from aiofiles import os as aios -from typing import Union -from transformers import AutoTokenizer, AutoProcessor -import numpy as np -from exo.helpers import DEBUG -from exo.download.new_shard_download import ensure_downloads_dir - - -class DummyTokenizer: - def __init__(self): - self.eos_token_id = 69 - self.vocab_size = 1000 - - def apply_chat_template(self, conversation, tokenize=True, add_generation_prompt=True, tools=None, **kwargs): - return "dummy_tokenized_prompt" - - def encode(self, text): - return np.array([1]) - - def decode(self, tokens): - return "dummy" * len(tokens) - - -async def resolve_tokenizer(repo_id: Union[str, PathLike]): - if repo_id == "dummy": - return DummyTokenizer() - local_path = await ensure_downloads_dir()/str(repo_id).replace("/", "--") - if DEBUG >= 2: print(f"Checking if local path exists to load tokenizer from local {local_path=}") - try: - if local_path and await aios.path.exists(local_path): - if DEBUG >= 2: print(f"Resolving tokenizer for {repo_id=} from {local_path=}") - return await _resolve_tokenizer(local_path) - except: - if DEBUG >= 5: print(f"Local check for {local_path=} failed. Resolving tokenizer for {repo_id=} normally...") - if DEBUG >= 5: traceback.print_exc() - return await _resolve_tokenizer(repo_id) - - -async def _resolve_tokenizer(repo_id_or_local_path: Union[str, PathLike]): - try: - if DEBUG >= 4: print(f"Trying AutoProcessor for {repo_id_or_local_path}") - processor = AutoProcessor.from_pretrained(repo_id_or_local_path, use_fast=True if "Mistral-Large" in f"{repo_id_or_local_path}" else False, trust_remote_code=True) - if not hasattr(processor, 'eos_token_id'): - processor.eos_token_id = getattr(processor, 'tokenizer', getattr(processor, '_tokenizer', processor)).eos_token_id - if not hasattr(processor, 'encode'): - processor.encode = getattr(processor, 'tokenizer', getattr(processor, '_tokenizer', processor)).encode - if not hasattr(processor, 'decode'): - processor.decode = getattr(processor, 'tokenizer', getattr(processor, '_tokenizer', processor)).decode - return processor - except Exception as e: - if DEBUG >= 4: print(f"Failed to load processor for {repo_id_or_local_path}. Error: {e}") - if DEBUG >= 4: print(traceback.format_exc()) - - try: - if DEBUG >= 4: print(f"Trying AutoTokenizer for {repo_id_or_local_path}") - return AutoTokenizer.from_pretrained(repo_id_or_local_path, trust_remote_code=True) - except Exception as e: - if DEBUG >= 4: print(f"Failed to load tokenizer for {repo_id_or_local_path}. Falling back to tinygrad tokenizer. Error: {e}") - if DEBUG >= 4: print(traceback.format_exc()) - - raise ValueError(f"[TODO] Unsupported model: {repo_id_or_local_path}") diff --git a/exo/main.py b/exo/main.py deleted file mode 100644 index 1ac5672d..00000000 --- a/exo/main.py +++ /dev/null @@ -1,402 +0,0 @@ -import argparse -import asyncio -import atexit -import signal -import json -import platform -import os -import time -import traceback -import uuid -import numpy as np -from tqdm import tqdm -from exo.train.dataset import load_dataset, iterate_batches -from exo.networking.manual.manual_discovery import ManualDiscovery -from exo.orchestration.node import Node -from exo.networking.grpc.grpc_server import GRPCServer -from exo.networking.udp.udp_discovery import UDPDiscovery -from exo.networking.tailscale.tailscale_discovery import TailscaleDiscovery -from exo.networking.grpc.grpc_peer_handle import GRPCPeerHandle -from exo.topology.ring_memory_weighted_partitioning_strategy import RingMemoryWeightedPartitioningStrategy -from exo.api import ChatGPTAPI -from exo.download.shard_download import ShardDownloader, NoopShardDownloader -from exo.download.download_progress import RepoProgressEvent -from exo.download.new_shard_download import new_shard_downloader, has_exo_home_read_access, has_exo_home_write_access, ensure_exo_home, seed_models -from exo.helpers import print_yellow_exo, find_available_port, DEBUG, get_system_info, get_or_create_node_id, get_all_ip_addresses_and_interfaces, terminal_link, shutdown -from exo.inference.shard import Shard -from exo.inference.inference_engine import get_inference_engine -from exo.inference.tokenizers import resolve_tokenizer -from exo.models import build_base_shard, get_repo -from exo.viz.topology_viz import TopologyViz -import uvloop -import concurrent.futures -import resource -import psutil - -# TODO: figure out why this is happening -os.environ["GRPC_VERBOSITY"] = "error" -os.environ["TRANSFORMERS_VERBOSITY"] = "error" -os.environ["TOKENIZERS_PARALLELISM"] = "true" - -# Configure uvloop for maximum performance -def configure_uvloop(): - uvloop.install() - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - - # Increase file descriptor limits on Unix systems - if not psutil.WINDOWS: - soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE) - try: resource.setrlimit(resource.RLIMIT_NOFILE, (hard, hard)) - except ValueError: - try: resource.setrlimit(resource.RLIMIT_NOFILE, (8192, hard)) - except ValueError: pass - - loop.set_default_executor(concurrent.futures.ThreadPoolExecutor(max_workers=min(32, (os.cpu_count() or 1) * 4))) - return loop - -# parse args -parser = argparse.ArgumentParser(description="Initialize GRPC Discovery") -parser.add_argument("command", nargs="?", choices=["run", "eval", "train"], help="Command to run") -parser.add_argument("model_name", nargs="?", help="Model name to run") -parser.add_argument("--default-model", type=str, default=None, help="Default model") -parser.add_argument("--iters", type=int, default=100, help="Training iterations") -parser.add_argument("--save-every", type=int, default=5, help="Save the model every N iterations.") -parser.add_argument("--data", type=str, default="exo/train/data/lora", help="Directory where training data lives") -parser.add_argument("--batch-size", type=int, default=1, help="Minibatch size.") -parser.add_argument("--resume-checkpoint", type=str, default=None, help="Path to a custom checkpoint to load") -parser.add_argument("--save-checkpoint-dir", type=str, default="checkpoints", help="Path to a folder where checkpoints are stored") -parser.add_argument("--node-id", type=str, default=None, help="Node ID") -parser.add_argument("--node-host", type=str, default="0.0.0.0", help="Node host") -parser.add_argument("--node-port", type=int, default=None, help="Node port") -parser.add_argument("--models-seed-dir", type=str, default=None, help="Model seed directory") -parser.add_argument("--listen-port", type=int, default=5678, help="Listening port for discovery") -parser.add_argument("--download-quick-check", action="store_true", help="Quick check local path for model shards download") -parser.add_argument("--max-parallel-downloads", type=int, default=8, help="Max parallel downloads for model shards download") -parser.add_argument("--broadcast-port", type=int, default=5678, help="Broadcast port for discovery") -parser.add_argument("--discovery-module", type=str, choices=["udp", "tailscale", "manual"], default="udp", help="Discovery module to use") -parser.add_argument("--discovery-timeout", type=int, default=30, help="Discovery timeout in seconds") -parser.add_argument("--discovery-config-path", type=str, default=None, help="Path to discovery config json file") -parser.add_argument("--wait-for-peers", type=int, default=0, help="Number of peers to wait to connect to before starting") -parser.add_argument("--chatgpt-api-port", type=int, default=52415, help="ChatGPT API port") -parser.add_argument("--chatgpt-api-response-timeout", type=int, default=900, help="ChatGPT API response timeout in seconds") -parser.add_argument("--max-generate-tokens", type=int, default=10000, help="Max tokens to generate in each request") -parser.add_argument("--inference-engine", type=str, default=None, help="Inference engine to use (mlx, tinygrad, or dummy)") -parser.add_argument("--disable-tui", action=argparse.BooleanOptionalAction, help="Disable TUI") -parser.add_argument("--run-model", type=str, help="Specify a model to run directly") -parser.add_argument("--prompt", type=str, help="Prompt for the model when using --run-model", default="Who are you?") -parser.add_argument("--default-temp", type=float, help="Default token sampling temperature", default=0.0) -parser.add_argument("--tailscale-api-key", type=str, default=None, help="Tailscale API key") -parser.add_argument("--tailnet-name", type=str, default=None, help="Tailnet name") -parser.add_argument("--node-id-filter", type=str, default=None, help="Comma separated list of allowed node IDs (only for UDP and Tailscale discovery)") -parser.add_argument("--interface-type-filter", type=str, default=None, help="Comma separated list of allowed interface types (only for UDP discovery)") -parser.add_argument("--system-prompt", type=str, default=None, help="System prompt for the ChatGPT API") -args = parser.parse_args() -print(f"Selected inference engine: {args.inference_engine}") - -print_yellow_exo() - -print("\n" + "="*80) -print("EXO") -print("="*80) -print("\nEXO started out of a desire to run research experiments on large language") -print("models using the hardware we already owned.") -print("\nWhat began here is becoming part of something much larger.") -print("\nsoon™") -print("\n- The EXO Team") -print("="*80 + "\n") - -system_info = get_system_info() -print(f"Detected system: {system_info}") - -shard_downloader: ShardDownloader = new_shard_downloader(args.max_parallel_downloads) if args.inference_engine != "dummy" else NoopShardDownloader() -inference_engine_name = args.inference_engine or ("mlx" if system_info == "Apple Silicon Mac" else "tinygrad") -print(f"Inference engine name after selection: {inference_engine_name}") - -inference_engine = get_inference_engine(inference_engine_name, shard_downloader) -print(f"Using inference engine: {inference_engine.__class__.__name__} with shard downloader: {shard_downloader.__class__.__name__}") - -if args.node_port is None: - args.node_port = find_available_port(args.node_host) - if DEBUG >= 1: print(f"Using available port: {args.node_port}") - -args.node_id = args.node_id or get_or_create_node_id() -chatgpt_api_endpoints = [f"http://{ip}:{args.chatgpt_api_port}/v1/chat/completions" for ip, _ in get_all_ip_addresses_and_interfaces()] -web_chat_urls = [f"http://{ip}:{args.chatgpt_api_port}" for ip, _ in get_all_ip_addresses_and_interfaces()] -if DEBUG >= 0: - print("Chat interface started:") - for web_chat_url in web_chat_urls: - print(f" - {terminal_link(web_chat_url)}") - print("ChatGPT API endpoint served at:") - for chatgpt_api_endpoint in chatgpt_api_endpoints: - print(f" - {terminal_link(chatgpt_api_endpoint)}") - -# Convert node-id-filter and interface-type-filter to lists if provided -allowed_node_ids = args.node_id_filter.split(',') if args.node_id_filter else None -allowed_interface_types = args.interface_type_filter.split(',') if args.interface_type_filter else None - -if args.discovery_module == "udp": - discovery = UDPDiscovery( - args.node_id, - args.node_port, - args.listen_port, - args.broadcast_port, - lambda peer_id, address, description, device_capabilities: GRPCPeerHandle(peer_id, address, description, device_capabilities), - discovery_timeout=args.discovery_timeout, - allowed_node_ids=allowed_node_ids, - allowed_interface_types=allowed_interface_types - ) -elif args.discovery_module == "tailscale": - discovery = TailscaleDiscovery( - args.node_id, - args.node_port, - lambda peer_id, address, description, device_capabilities: GRPCPeerHandle(peer_id, address, description, device_capabilities), - discovery_timeout=args.discovery_timeout, - tailscale_api_key=args.tailscale_api_key, - tailnet=args.tailnet_name, - allowed_node_ids=allowed_node_ids - ) -elif args.discovery_module == "manual": - if not args.discovery_config_path: - raise ValueError(f"--discovery-config-path is required when using manual discovery. Please provide a path to a config json file.") - discovery = ManualDiscovery(args.discovery_config_path, args.node_id, create_peer_handle=lambda peer_id, address, description, device_capabilities: GRPCPeerHandle(peer_id, address, description, device_capabilities)) -topology_viz = TopologyViz(chatgpt_api_endpoints=chatgpt_api_endpoints, web_chat_urls=web_chat_urls) if not args.disable_tui else None -node = Node( - args.node_id, - None, - inference_engine, - discovery, - shard_downloader, - partitioning_strategy=RingMemoryWeightedPartitioningStrategy(), - max_generate_tokens=args.max_generate_tokens, - topology_viz=topology_viz, - default_sample_temperature=args.default_temp -) -server = GRPCServer(node, args.node_host, args.node_port) -node.server = server -api = ChatGPTAPI( - node, - node.inference_engine.__class__.__name__, - response_timeout=args.chatgpt_api_response_timeout, - on_chat_completion_request=lambda req_id, __, prompt: topology_viz.update_prompt(req_id, prompt) if topology_viz else None, - default_model=args.default_model, - system_prompt=args.system_prompt -) -buffered_token_output = {} -def update_topology_viz(req_id, tokens, __): - if not topology_viz: return - if not node.inference_engine.shard: return - if node.inference_engine.shard.model_id == 'stable-diffusion-2-1-base': return - if req_id in buffered_token_output: buffered_token_output[req_id].extend(tokens) - else: buffered_token_output[req_id] = tokens - topology_viz.update_prompt_output(req_id, node.inference_engine.tokenizer.decode(buffered_token_output[req_id])) -node.on_token.register("update_topology_viz").on_next(update_topology_viz) -def update_prompt_viz(request_id, opaque_status: str): - if not topology_viz: return - try: - status = json.loads(opaque_status) - if status.get("type") != "node_status" or status.get("status") != "start_process_prompt": return - topology_viz.update_prompt(request_id, status.get("prompt", "corrupted prompt (this should never happen)")) - except Exception as e: - if DEBUG >= 2: - print(f"Failed to update prompt viz: {e}") - traceback.print_exc() -node.on_opaque_status.register("update_prompt_viz").on_next(update_prompt_viz) - -def preemptively_load_shard(request_id: str, opaque_status: str): - try: - status = json.loads(opaque_status) - if status.get("type") != "node_status" or status.get("status") != "start_process_prompt": return - current_shard = node.get_current_shard(Shard.from_dict(status.get("shard"))) - if DEBUG >= 2: print(f"Preemptively starting download for {current_shard}") - asyncio.create_task(node.inference_engine.ensure_shard(current_shard)) - except Exception as e: - if DEBUG >= 2: - print(f"Failed to preemptively start download: {e}") - traceback.print_exc() -node.on_opaque_status.register("preemptively_load_shard").on_next(preemptively_load_shard) - -last_events: dict[str, tuple[float, RepoProgressEvent]] = {} -def throttled_broadcast(shard: Shard, event: RepoProgressEvent): - global last_events - current_time = time.time() - if event.status == "not_started": return - last_event = last_events.get(shard.model_id) - if last_event and last_event[1].status == "complete" and event.status == "complete": return - if last_event and last_event[0] == event.status and current_time - last_event[0] < 0.2: return - last_events[shard.model_id] = (current_time, event) - asyncio.create_task(node.broadcast_opaque_status("", json.dumps({"type": "download_progress", "node_id": node.id, "progress": event.to_dict()}))) -shard_downloader.on_progress.register("broadcast").on_next(throttled_broadcast) - -async def run_model_cli(node: Node, model_name: str, prompt: str): - inference_class = node.inference_engine.__class__.__name__ - shard = build_base_shard(model_name, inference_class) - if not shard: - print(f"Error: Unsupported model '{model_name}' for inference engine {inference_class}") - return - tokenizer = await resolve_tokenizer(get_repo(shard.model_id, inference_class)) - request_id = str(uuid.uuid4()) - callback_id = f"cli-wait-response-{request_id}" - callback = node.on_token.register(callback_id) - if topology_viz: - topology_viz.update_prompt(request_id, prompt) - prompt = tokenizer.apply_chat_template([{"role": "user", "content": prompt}], tokenize=False, add_generation_prompt=True) - - try: - print(f"Processing prompt: {prompt}") - await node.process_prompt(shard, prompt, request_id=request_id) - - tokens = [] - def on_token(_request_id, _tokens, _is_finished): - tokens.extend(_tokens) - return _request_id == request_id and _is_finished - await callback.wait(on_token, timeout=300) - - print("\nGenerated response:") - print(tokenizer.decode(tokens)) - except Exception as e: - print(f"Error processing prompt: {str(e)}") - traceback.print_exc() - finally: - node.on_token.deregister(callback_id) - -def clean_path(path): - """Clean and resolve path""" - if path.startswith("Optional("): - path = path.strip('Optional("').rstrip('")') - return os.path.expanduser(path) - -async def hold_outstanding(node: Node): - while node.outstanding_requests: - await asyncio.sleep(.5) - return - -async def run_iter(node: Node, shard: Shard, train: bool, data, batch_size=1): - losses = [] - tokens = [] - for batch in tqdm(iterate_batches(data, batch_size), total=len(data) // batch_size): - _, _, lengths = batch - losses.append(np.sum(lengths * await node.enqueue_example(shard, *batch, train=train))) - tokens.append(np.sum(lengths)) - total_tokens = np.sum(tokens) - total_loss = np.sum(losses) / total_tokens - - return total_loss, total_tokens - -async def eval_model_cli(node: Node, model_name, dataloader, batch_size, num_batches=-1): - inference_class = node.inference_engine.__class__.__name__ - shard = build_base_shard(model_name, inference_class) - if not shard: - print(f"Error: Unsupported model '{model_name}' for inference engine {inference_class}") - return - tokenizer = await resolve_tokenizer(get_repo(shard.model_id, inference_class)) - train, val, test = dataloader(tokenizer.encode) - print(f"Evaluating {len(test)} examples with batch_size {batch_size}") - loss, tokens = await run_iter(node, shard, False, test, batch_size) - print(f"total | {loss=}, {tokens=}") - print("Waiting for outstanding tasks") - await hold_outstanding(node) - -async def train_model_cli(node: Node, model_name, dataloader, batch_size, iters, save_interval=0, checkpoint_dir=None): - inference_class = node.inference_engine.__class__.__name__ - shard = build_base_shard(model_name, inference_class) - if not shard: - print(f"Error: Unsupported model '{model_name}' for inference engine {inference_class}") - return - tokenizer = await resolve_tokenizer(get_repo(shard.model_id, inference_class)) - train, val, test = dataloader(tokenizer.encode) - print(f"Training on {len(train)} examples with batch_size {batch_size} for {iters} epochs") - for i in tqdm(range(3)): - await asyncio.sleep(1) - for epoch in range(iters): - loss, tokens = await run_iter(node, shard, True, train, batch_size) - print(f"epoch {epoch + 1}/{iters}\t| loss: {loss}, tokens: {tokens}") - if save_interval > 0 and epoch > 0 and (epoch % save_interval) == 0 and checkpoint_dir is not None: - await node.coordinate_save(shard, epoch, checkpoint_dir) - await hold_outstanding(node) - await hold_outstanding(node) - -async def check_exo_home(): - home, has_read, has_write = await ensure_exo_home(), await has_exo_home_read_access(), await has_exo_home_write_access() - if DEBUG >= 1: print(f"exo home directory: {home}") - print(f"{has_read=}, {has_write=}") - if not has_read or not has_write: - print(f""" - WARNING: Limited permissions for exo home directory: {home}. - This may prevent model downloads from working correctly. - {"❌ No read access" if not has_read else ""} - {"❌ No write access" if not has_write else ""} - """) - -async def main(): - loop = asyncio.get_running_loop() - - try: await check_exo_home() - except Exception as e: print(f"Error checking exo home directory: {e}") - - if not args.models_seed_dir is None: - try: - models_seed_dir = clean_path(args.models_seed_dir) - await seed_models(models_seed_dir) - except Exception as e: - print(f"Error seeding models: {e}") - - def restore_cursor(): - if platform.system() != "Windows": - os.system("tput cnorm") # Show cursor - - # Restore the cursor when the program exits - atexit.register(restore_cursor) - - # Use a more direct approach to handle signals - def handle_exit(): - asyncio.ensure_future(shutdown(signal.SIGTERM, loop, node.server)) - - if platform.system() != "Windows": - for s in [signal.SIGINT, signal.SIGTERM]: - loop.add_signal_handler(s, handle_exit) - - await node.start(wait_for_peers=args.wait_for_peers) - - if args.command == "run" or args.run_model: - model_name = args.model_name or args.run_model - if not model_name: - print("Error: Model name is required when using 'run' command or --run-model") - return - await run_model_cli(node, model_name, args.prompt) - elif args.command == "eval" or args.command == 'train': - model_name = args.model_name - dataloader = lambda tok: load_dataset(args.data, preprocess=lambda item: tok(item) - , loadline=lambda line: json.loads(line).get("text","")) - if args.command == 'eval': - if not model_name: - print("Error: Much like a human, I can't evaluate anything without a model") - return - await eval_model_cli(node, model_name, dataloader, args.batch_size) - else: - if not model_name: - print("Error: This train ain't leaving the station without a model") - return - await train_model_cli(node, model_name, dataloader, args.batch_size, args.iters, save_interval=args.save_every, checkpoint_dir=args.save_checkpoint_dir) - - else: - asyncio.create_task(api.run(port=args.chatgpt_api_port)) # Start the API server as a non-blocking task - await asyncio.Event().wait() - - if args.wait_for_peers > 0: - print("Cooldown to allow peers to exit gracefully") - for i in tqdm(range(50)): - await asyncio.sleep(.1) - -def run(): - loop = None - try: - loop = configure_uvloop() - loop.run_until_complete(main()) - except KeyboardInterrupt: - print("\nShutdown requested... exiting") - finally: - if loop: loop.close() - -if __name__ == "__main__": - run() diff --git a/exo/models.py b/exo/models.py deleted file mode 100644 index b64bcca1..00000000 --- a/exo/models.py +++ /dev/null @@ -1,273 +0,0 @@ -from exo.inference.shard import Shard -from typing import Optional, List - -model_cards = { - ### llama - "llama-3.3-70b": { - "layers": 80, - "repo": { - "MLXDynamicShardInferenceEngine": "mlx-community/Llama-3.3-70B-Instruct-4bit", - "TinygradDynamicShardInferenceEngine": "unsloth/Llama-3.3-70B-Instruct", - }, - }, - "llama-3.2-1b": { - "layers": 16, - "repo": { - "MLXDynamicShardInferenceEngine": "mlx-community/Llama-3.2-1B-Instruct-4bit", - "TinygradDynamicShardInferenceEngine": "unsloth/Llama-3.2-1B-Instruct", - }, - }, - "llama-3.2-1b-8bit": { - "layers": 16, - "repo": { - "MLXDynamicShardInferenceEngine": "mlx-community/Llama-3.2-1B-Instruct-8bit", - "TinygradDynamicShardInferenceEngine": "unsloth/Llama-3.2-1B-Instruct", - }, - }, - "llama-3.2-3b": { - "layers": 28, - "repo": { - "MLXDynamicShardInferenceEngine": "mlx-community/Llama-3.2-3B-Instruct-4bit", - "TinygradDynamicShardInferenceEngine": "unsloth/Llama-3.2-3B-Instruct", - }, - }, - "llama-3.2-3b-8bit": { - "layers": 28, - "repo": { - "MLXDynamicShardInferenceEngine": "mlx-community/Llama-3.2-3B-Instruct-8bit", - "TinygradDynamicShardInferenceEngine": "unsloth/Llama-3.2-3B-Instruct", - }, - }, - "llama-3.2-3b-bf16": { - "layers": 28, - "repo": { - "MLXDynamicShardInferenceEngine": "mlx-community/Llama-3.2-3B-Instruct", - "TinygradDynamicShardInferenceEngine": "unsloth/Llama-3.2-3B-Instruct", - }, - }, - "llama-3.1-8b": { - "layers": 32, - "repo": { - "MLXDynamicShardInferenceEngine": "mlx-community/Meta-Llama-3.1-8B-Instruct-4bit", - "TinygradDynamicShardInferenceEngine": "mlabonne/Meta-Llama-3.1-8B-Instruct-abliterated", - }, - }, - "llama-3.1-70b": { - "layers": 80, - "repo": { - "MLXDynamicShardInferenceEngine": "mlx-community/Meta-Llama-3.1-70B-Instruct-4bit", - "TinygradDynamicShardInferenceEngine": "NousResearch/Meta-Llama-3.1-70B-Instruct", - }, - }, - "llama-3.1-70b-bf16": { - "layers": 80, - "repo": { - "MLXDynamicShardInferenceEngine": "mlx-community/Meta-Llama-3.1-70B-Instruct-bf16-CORRECTED", - "TinygradDynamicShardInferenceEngine": "NousResearch/Meta-Llama-3.1-70B-Instruct", - }, - }, - "llama-3-8b": { - "layers": 32, - "repo": { - "MLXDynamicShardInferenceEngine": "mlx-community/Meta-Llama-3-8B-Instruct-4bit", - "TinygradDynamicShardInferenceEngine": "TriAiExperiments/SFR-Iterative-DPO-LLaMA-3-8B-R", - }, - }, - "llama-3-70b": { - "layers": 80, - "repo": { - "MLXDynamicShardInferenceEngine": "mlx-community/Meta-Llama-3-70B-Instruct-4bit", - "TinygradDynamicShardInferenceEngine": "TriAiExperiments/SFR-Iterative-DPO-LLaMA-3-70B-R", - }, - }, - "llama-3.1-405b": { "layers": 126, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Meta-Llama-3.1-405B-4bit", }, }, - "llama-3.1-405b-8bit": { "layers": 126, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Meta-Llama-3.1-405B-Instruct-8bit", }, }, - ### mistral - "mistral-nemo": { "layers": 40, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Mistral-Nemo-Instruct-2407-4bit", }, }, - "mistral-large": { "layers": 88, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Mistral-Large-Instruct-2407-4bit", }, }, - ### deepseek - "deepseek-coder-v2-lite": { "layers": 27, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-Coder-V2-Lite-Instruct-4bit-mlx", }, }, - "deepseek-coder-v2.5": { "layers": 60, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-V2.5-MLX-AQ4_1_64", }, }, - "deepseek-v3": { "layers": 61, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-V3-4bit", }, }, - "deepseek-v3-3bit": { "layers": 61, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-V3-3bit", }, }, - "deepseek-r1": { "layers": 61, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-4bit", }, }, - "deepseek-r1-3bit": { "layers": 61, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-3bit", }, }, - ### deepseek distills - "deepseek-r1-distill-qwen-1.5b": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/deepseek-r1-distill-qwen-1.5b", }, }, - "deepseek-r1-distill-qwen-1.5b-3bit": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-1.5B-3bit", }, }, - "deepseek-r1-distill-qwen-1.5b-6bit": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-1.5B-6bit", }, }, - "deepseek-r1-distill-qwen-1.5b-8bit": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-1.5B-8bit", }, }, - "deepseek-r1-distill-qwen-1.5b-bf16": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-1.5B-bf16", }, }, - "deepseek-r1-distill-qwen-7b": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-7B-4bit", }, }, - "deepseek-r1-distill-qwen-7b-3bit": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-7B-3bit", }, }, - "deepseek-r1-distill-qwen-7b-6bit": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-7B-6bit", }, }, - "deepseek-r1-distill-qwen-7b-8bit": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-7B-8bit", }, }, - "deepseek-r1-distill-qwen-7b-bf16": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-7B-bf16", }, }, - "deepseek-r1-distill-qwen-14b": { "layers": 48, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-14B-4bit", }, }, - "deepseek-r1-distill-qwen-14b-3bit": { "layers": 48, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-14B-3bit", }, }, - "deepseek-r1-distill-qwen-14b-6bit": { "layers": 48, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-14B-6bit", }, }, - "deepseek-r1-distill-qwen-14b-8bit": { "layers": 48, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-14B-8bit", }, }, - "deepseek-r1-distill-qwen-14b-bf16": { "layers": 48, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-14B-bf16", }, }, - "deepseek-r1-distill-qwen-32b": { "layers": 64, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-32B-4bit", }, }, - "deepseek-r1-distill-qwen-32b-3bit": { "layers": 64, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-32B-3bit", }, }, - "deepseek-r1-distill-qwen-32b-6bit": { "layers": 64, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-32B-6bit", }, }, - "deepseek-r1-distill-qwen-32b-8bit": { "layers": 64, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-32B-MLX-8Bit", }, }, - "deepseek-r1-distill-qwen-32b-bf16": { "layers": 64, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Qwen-32B-bf16", }, }, - "deepseek-r1-distill-llama-8b": { "layers": 32, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Llama-8B-4bit", }, }, - "deepseek-r1-distill-llama-8b-3bit": { "layers": 32, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Llama-8B-3bit", }, }, - "deepseek-r1-distill-llama-8b-6bit": { "layers": 32, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Llama-8B-6bit", }, }, - "deepseek-r1-distill-llama-8b-8bit": { "layers": 32, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Llama-8B-8bit", }, }, - "deepseek-r1-distill-llama-8b-bf16": { "layers": 32, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Llama-8B-bf16", }, }, - "deepseek-r1-distill-llama-70b": { "layers": 80, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Llama-70B-4bit", }, }, - "deepseek-r1-distill-llama-70b-3bit": { "layers": 80, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Llama-70B-3bit", }, }, - "deepseek-r1-distill-llama-70b-6bit": { "layers": 80, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Llama-70B-6bit", }, }, - "deepseek-r1-distill-llama-70b-8bit": { "layers": 80, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/DeepSeek-R1-Distill-Llama-70B-8bit", }, }, - ### llava - "llava-1.5-7b-hf": { "layers": 32, "repo": { "MLXDynamicShardInferenceEngine": "llava-hf/llava-1.5-7b-hf", }, }, - ### qwen - "qwen-2.5-0.5b": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-0.5B-Instruct-4bit", }, }, - "qwen-2.5-1.5b": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-1.5B-Instruct-4bit", }, }, - "qwen-2.5-coder-1.5b": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-Coder-1.5B-Instruct-4bit", }, }, - "qwen-2.5-3b": { "layers": 36, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-3B-Instruct-4bit", }, }, - "qwen-2.5-coder-3b": { "layers": 36, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-Coder-3B-Instruct-4bit", }, }, - "qwen-2.5-7b": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-7B-Instruct-4bit", }, }, - "qwen-2.5-coder-7b": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-Coder-7B-Instruct-4bit", }, }, - "qwen-2.5-math-7b": { "layers": 28, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-Math-7B-Instruct-4bit", }, }, - "qwen-2.5-14b": { "layers": 48, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-14B-Instruct-4bit", }, }, - "qwen-2.5-coder-14b": { "layers": 48, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-Coder-14B-Instruct-4bit", }, }, - "qwen-2.5-32b": { "layers": 64, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-32B-Instruct-4bit", }, }, - "qwen-2.5-coder-32b": { "layers": 64, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-Coder-32B-Instruct-4bit", }, }, - "qwen-2.5-72b": { "layers": 80, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-72B-Instruct-4bit", }, }, - "qwen-2.5-math-72b": { "layers": 80, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Qwen2.5-Math-72B-Instruct-4bit", }, }, - ### nemotron - "nemotron-70b": { "layers": 80, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/nvidia_Llama-3.1-Nemotron-70B-Instruct-HF_4bit", }, }, - "nemotron-70b-bf16": { "layers": 80, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Llama-3.1-Nemotron-70B-Instruct-HF-bf16", }, }, - # gemma - "gemma2-9b": { "layers": 42, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/gemma-2-9b-it-4bit", }, }, - "gemma2-27b": { "layers": 46, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/gemma-2-27b-it-4bit", }, }, - # stable diffusion - "stable-diffusion-2-1-base": { "layers": 31, "repo": { "MLXDynamicShardInferenceEngine": "stabilityai/stable-diffusion-2-1-base" } }, - # phi - "phi-3.5-mini": { "layers": 32, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/Phi-3.5-mini-instruct-4bit", }, }, - "phi-4": { "layers": 40, "repo": { "MLXDynamicShardInferenceEngine": "mlx-community/phi-4-4bit", }, }, - # dummy - "dummy": { "layers": 8, "repo": { "DummyInferenceEngine": "dummy", }, }, -} - -pretty_name = { - "llama-3.3-70b": "Llama 3.3 70B", - "llama-3.2-1b": "Llama 3.2 1B", - "llama-3.2-1b-8bit": "Llama 3.2 1B (8-bit)", - "llama-3.2-3b": "Llama 3.2 3B", - "llama-3.2-3b-8bit": "Llama 3.2 3B (8-bit)", - "llama-3.2-3b-bf16": "Llama 3.2 3B (BF16)", - "llama-3.1-8b": "Llama 3.1 8B", - "llama-3.1-70b": "Llama 3.1 70B", - "llama-3.1-70b-bf16": "Llama 3.1 70B (BF16)", - "llama-3.1-405b": "Llama 3.1 405B", - "llama-3.1-405b-8bit": "Llama 3.1 405B (8-bit)", - "gemma2-9b": "Gemma2 9B", - "gemma2-27b": "Gemma2 27B", - "nemotron-70b": "Nemotron 70B", - "nemotron-70b-bf16": "Nemotron 70B (BF16)", - "mistral-nemo": "Mistral Nemo", - "mistral-large": "Mistral Large", - "deepseek-coder-v2-lite": "Deepseek Coder V2 Lite", - "deepseek-coder-v2.5": "Deepseek Coder V2.5", - "deepseek-v3": "Deepseek V3 (4-bit)", - "deepseek-v3-3bit": "Deepseek V3 (3-bit)", - "deepseek-r1": "Deepseek R1 (4-bit)", - "deepseek-r1-3bit": "Deepseek R1 (3-bit)", - "llava-1.5-7b-hf": "LLaVa 1.5 7B (Vision Model)", - "qwen-2.5-0.5b": "Qwen 2.5 0.5B", - "qwen-2.5-1.5b": "Qwen 2.5 1.5B", - "qwen-2.5-coder-1.5b": "Qwen 2.5 Coder 1.5B", - "qwen-2.5-3b": "Qwen 2.5 3B", - "qwen-2.5-coder-3b": "Qwen 2.5 Coder 3B", - "qwen-2.5-7b": "Qwen 2.5 7B", - "qwen-2.5-coder-7b": "Qwen 2.5 Coder 7B", - "qwen-2.5-math-7b": "Qwen 2.5 7B (Math)", - "qwen-2.5-14b": "Qwen 2.5 14B", - "qwen-2.5-coder-14b": "Qwen 2.5 Coder 14B", - "qwen-2.5-32b": "Qwen 2.5 32B", - "qwen-2.5-coder-32b": "Qwen 2.5 Coder 32B", - "qwen-2.5-72b": "Qwen 2.5 72B", - "qwen-2.5-math-72b": "Qwen 2.5 72B (Math)", - "phi-3.5-mini": "Phi-3.5 Mini", - "phi-4": "Phi-4", - "llama-3-8b": "Llama 3 8B", - "llama-3-70b": "Llama 3 70B", - "stable-diffusion-2-1-base": "Stable Diffusion 2.1", - "deepseek-r1-distill-qwen-1.5b": "DeepSeek R1 Distill Qwen 1.5B", - "deepseek-r1-distill-qwen-1.5b-3bit": "DeepSeek R1 Distill Qwen 1.5B (3-bit)", - "deepseek-r1-distill-qwen-1.5b-6bit": "DeepSeek R1 Distill Qwen 1.5B (6-bit)", - "deepseek-r1-distill-qwen-1.5b-8bit": "DeepSeek R1 Distill Qwen 1.5B (8-bit)", - "deepseek-r1-distill-qwen-1.5b-bf16": "DeepSeek R1 Distill Qwen 1.5B (BF16)", - "deepseek-r1-distill-qwen-7b": "DeepSeek R1 Distill Qwen 7B", - "deepseek-r1-distill-qwen-7b-3bit": "DeepSeek R1 Distill Qwen 7B (3-bit)", - "deepseek-r1-distill-qwen-7b-6bit": "DeepSeek R1 Distill Qwen 7B (6-bit)", - "deepseek-r1-distill-qwen-7b-8bit": "DeepSeek R1 Distill Qwen 7B (8-bit)", - "deepseek-r1-distill-qwen-7b-bf16": "DeepSeek R1 Distill Qwen 7B (BF16)", - "deepseek-r1-distill-qwen-14b": "DeepSeek R1 Distill Qwen 14B", - "deepseek-r1-distill-qwen-14b-3bit": "DeepSeek R1 Distill Qwen 14B (3-bit)", - "deepseek-r1-distill-qwen-14b-6bit": "DeepSeek R1 Distill Qwen 14B (6-bit)", - "deepseek-r1-distill-qwen-14b-8bit": "DeepSeek R1 Distill Qwen 14B (8-bit)", - "deepseek-r1-distill-qwen-14b-bf16": "DeepSeek R1 Distill Qwen 14B (BF16)", - "deepseek-r1-distill-qwen-32b": "DeepSeek R1 Distill Qwen 32B", - "deepseek-r1-distill-qwen-32b-3bit": "DeepSeek R1 Distill Qwen 32B (3-bit)", - "deepseek-r1-distill-qwen-32b-8bit": "DeepSeek R1 Distill Qwen 32B (8-bit)", - "deepseek-r1-distill-qwen-32b-bf16": "DeepSeek R1 Distill Qwen 32B (BF16)", - "deepseek-r1-distill-llama-8b-8bit": "DeepSeek R1 Distill Llama 8B (8-bit)", - "deepseek-r1-distill-llama-70b-6bit": "DeepSeek R1 Distill Llama 70B (6-bit)", - "deepseek-r1-distill-llama-70b-8bit": "DeepSeek R1 Distill Llama 70B (8-bit)", - "deepseek-r1-distill-llama-8b": "DeepSeek R1 Distill Llama 8B", - "deepseek-r1-distill-llama-8b-3bit": "DeepSeek R1 Distill Llama 8B (3-bit)", - "deepseek-r1-distill-llama-8b-6bit": "DeepSeek R1 Distill Llama 8B (6-bit)", - "deepseek-r1-distill-llama-8b-8bit": "DeepSeek R1 Distill Llama 8B (8-bit)", - "deepseek-r1-distill-llama-8b-bf16": "DeepSeek R1 Distill Llama 8B (BF16)", - "deepseek-r1-distill-llama-70b": "DeepSeek R1 Distill Llama 70B", - "deepseek-r1-distill-llama-70b-3bit": "DeepSeek R1 Distill Llama 70B (3-bit)", - "deepseek-r1-distill-llama-70b-6bit": "DeepSeek R1 Distill Llama 70B (6-bit)", - "deepseek-r1-distill-llama-70b-8bit": "DeepSeek R1 Distill Llama 70B (8-bit)", - "deepseek-r1-distill-qwen-32b-6bit": "DeepSeek R1 Distill Qwen 32B (6-bit)", -} - -def get_repo(model_id: str, inference_engine_classname: str) -> Optional[str]: - return model_cards.get(model_id, {}).get("repo", {}).get(inference_engine_classname, None) - -def get_pretty_name(model_id: str) -> Optional[str]: - return pretty_name.get(model_id, None) - -def build_base_shard(model_id: str, inference_engine_classname: str) -> Optional[Shard]: - repo = get_repo(model_id, inference_engine_classname) - n_layers = model_cards.get(model_id, {}).get("layers", 0) - if repo is None or n_layers < 1: - return None - return Shard(model_id, 0, 0, n_layers) - -def build_full_shard(model_id: str, inference_engine_classname: str) -> Optional[Shard]: - base_shard = build_base_shard(model_id, inference_engine_classname) - if base_shard is None: return None - return Shard(base_shard.model_id, 0, base_shard.n_layers - 1, base_shard.n_layers) - -def get_supported_models(supported_inference_engine_lists: Optional[List[List[str]]] = None) -> List[str]: - if not supported_inference_engine_lists: - return list(model_cards.keys()) - - from exo.inference.inference_engine import inference_engine_classes - supported_inference_engine_lists = [ - [inference_engine_classes[engine] if engine in inference_engine_classes else engine for engine in engine_list] - for engine_list in supported_inference_engine_lists - ] - - def has_any_engine(model_info: dict, engine_list: List[str]) -> bool: - return any(engine in model_info.get("repo", {}) for engine in engine_list) - - def supports_all_engine_lists(model_info: dict) -> bool: - return all(has_any_engine(model_info, engine_list) - for engine_list in supported_inference_engine_lists) - - return [ - model_id for model_id, model_info in model_cards.items() - if supports_all_engine_lists(model_info) - ] diff --git a/exo/networking/__init__.py b/exo/networking/__init__.py deleted file mode 100644 index 44a10a30..00000000 --- a/exo/networking/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .discovery import Discovery -from .peer_handle import PeerHandle -from .server import Server - -__all__ = ["Discovery", "PeerHandle", "Server"] diff --git a/exo/networking/discovery.py b/exo/networking/discovery.py deleted file mode 100644 index cdcbfabc..00000000 --- a/exo/networking/discovery.py +++ /dev/null @@ -1,17 +0,0 @@ -from abc import ABC, abstractmethod -from typing import List -from .peer_handle import PeerHandle - - -class Discovery(ABC): - @abstractmethod - async def start(self) -> None: - pass - - @abstractmethod - async def stop(self) -> None: - pass - - @abstractmethod - async def discover_peers(self, wait_for_peers: int = 0) -> List[PeerHandle]: - pass diff --git a/exo/networking/grpc/__init__.py b/exo/networking/grpc/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/exo/networking/grpc/grpc_peer_handle.py b/exo/networking/grpc/grpc_peer_handle.py deleted file mode 100644 index 997d43ef..00000000 --- a/exo/networking/grpc/grpc_peer_handle.py +++ /dev/null @@ -1,226 +0,0 @@ -import grpc -import numpy as np -import asyncio -from typing import Optional, Tuple, List - -from . import node_service_pb2 -from . import node_service_pb2_grpc - -from ..peer_handle import PeerHandle -from exo.inference.shard import Shard -from exo.topology.topology import Topology -from exo.topology.device_capabilities import DeviceCapabilities, DeviceFlops -from exo.helpers import DEBUG -import json -import platform - -if platform.system().lower() == "darwin" and platform.machine().lower() == "arm64": - import mlx.core as mx -else: - import numpy as mx - - -class GRPCPeerHandle(PeerHandle): - def __init__(self, _id: str, address: str, desc: str, device_capabilities: DeviceCapabilities): - self._id = _id - self.address = address - self.desc = desc - self._device_capabilities = device_capabilities - self.channel = None - self.stub = None - self.channel_options = [ - ("grpc.max_metadata_size", 32 * 1024 * 1024), - ("grpc.max_receive_message_length", 256 * 1024 * 1024), - ("grpc.max_send_message_length", 256 * 1024 * 1024), - ("grpc.max_concurrent_streams", 100), - ("grpc.http2.min_time_between_pings_ms", 10000), - ("grpc.keepalive_time_ms", 10000), - ("grpc.keepalive_timeout_ms", 5000), - ("grpc.keepalive_permit_without_calls", 1), - ("grpc.http2.max_pings_without_data", 0), - ("grpc.http2.min_ping_interval_without_data_ms", 5000), - ("grpc.tcp_nodelay", 1), - ("grpc.optimization_target", "throughput"), - ] - - def id(self) -> str: - return self._id - - def addr(self) -> str: - return self.address - - def description(self) -> str: - return self.desc - - def device_capabilities(self) -> DeviceCapabilities: - return self._device_capabilities - - async def connect(self): - self.channel = grpc.aio.insecure_channel( - self.address, - options=self.channel_options, - compression=grpc.Compression.Gzip - ) - self.stub = node_service_pb2_grpc.NodeServiceStub(self.channel) - await asyncio.wait_for(self.channel.channel_ready(), timeout=10.0) - - async def is_connected(self) -> bool: - return self.channel is not None and self.channel.get_state() == grpc.ChannelConnectivity.READY - - async def disconnect(self): - if self.channel: - await self.channel.close() - self.channel = None - self.stub = None - - async def _ensure_connected(self): - if not (await self.is_connected()): - try: - await asyncio.wait_for(self.connect(), timeout=10.0) - except asyncio.TimeoutError: - if DEBUG >= 2: print(f"Connection timeout for {self._id}@{self.address}") - await self.disconnect() - raise - - async def health_check(self) -> bool: - try: - await self._ensure_connected() - request = node_service_pb2.HealthCheckRequest() - response = await asyncio.wait_for(self.stub.HealthCheck(request), timeout=5) - return response.is_healthy - except asyncio.TimeoutError: - return False - except Exception: - if DEBUG >= 4: - print(f"Health check failed for {self._id}@{self.address}.") - import traceback - traceback.print_exc() - return False - - async def send_prompt(self, shard: Shard, prompt: str, inference_state: Optional[dict] = None, request_id: Optional[str] = None) -> Optional[np.array]: - await self._ensure_connected() - request = node_service_pb2.PromptRequest( - prompt=prompt, - shard=node_service_pb2.Shard( - model_id=shard.model_id, - start_layer=shard.start_layer, - end_layer=shard.end_layer, - n_layers=shard.n_layers, - ), - request_id=request_id, - inference_state=None if inference_state is None else self.serialize_inference_state(inference_state) - ) - await self.stub.SendPrompt(request) - - async def send_tensor(self, shard: Shard, tensor: np.ndarray, inference_state: Optional[dict] = None, request_id: Optional[str] = None) -> Optional[np.array]: - await self._ensure_connected() - request = node_service_pb2.TensorRequest( - shard=node_service_pb2.Shard( - model_id=shard.model_id, - start_layer=shard.start_layer, - end_layer=shard.end_layer, - n_layers=shard.n_layers, - ), - tensor=node_service_pb2.Tensor(tensor_data=tensor.tobytes(), shape=tensor.shape, dtype=str(tensor.dtype)), - request_id=request_id, - inference_state=None if inference_state is None else self.serialize_inference_state(inference_state) - ) - response = await self.stub.SendTensor(request) - - if not response.tensor_data or not response.shape or not response.dtype: - return None - - return np.frombuffer(response.tensor_data, dtype=np.dtype(response.dtype)).reshape(response.shape) - - async def send_example(self, shard: Shard, example: np.ndarray, target: np.ndarray, length: np.ndarray, train: bool, request_id: Optional[str] = None) -> Optional[np.array]: - await self._ensure_connected() - request = node_service_pb2.ExampleRequest( - shard=node_service_pb2.Shard( - model_id=shard.model_id, - start_layer=shard.start_layer, - end_layer=shard.end_layer, - n_layers=shard.n_layers, - ), - example=node_service_pb2.Tensor(tensor_data=example.tobytes(), shape=example.shape, dtype=str(example.dtype)), - target=node_service_pb2.Tensor(tensor_data=target.tobytes(), shape=target.shape, dtype=str(target.dtype)), - length=node_service_pb2.Tensor(tensor_data=length.tobytes(), shape=length.shape, dtype=str(length.dtype)), - train=train, - request_id=request_id, - ) - response = await self.stub.SendExample(request) - loss = response.loss - if train and not shard.is_first_layer(): - grads = np.frombuffer(response.grads.tensor_data, dtype=np.dtype(response.grads.dtype)).reshape(response.grads.shape) - return loss, grads - else: - return loss - - async def send_loss(self, shard: Shard, tensor: np.ndarray, request_id: Optional[str] = None) -> Optional[np.array]: - await self._ensure_connected() - request = node_service_pb2.TensorRequest( - shard=node_service_pb2.Shard( - model_id=shard.model_id, - start_layer=shard.start_layer, - end_layer=shard.end_layer, - n_layers=shard.n_layers, - ), - tensor=node_service_pb2.Tensor(tensor_data=tensor.tobytes(), shape=tensor.shape, dtype=str(tensor.dtype)), - request_id=request_id, - ) - response = await self.stub.SendLoss(request) - - if not response.tensor_data or not response.shape or not response.dtype: - return None - - return np.frombuffer(response.tensor_data, dtype=np.dtype(response.dtype)).reshape(response.shape) - - async def collect_topology(self, visited: set[str], max_depth: int) -> Topology: - await self._ensure_connected() - request = node_service_pb2.CollectTopologyRequest(visited=visited, max_depth=max_depth) - response = await self.stub.CollectTopology(request) - topology = Topology() - for node_id, capabilities in response.nodes.items(): - device_capabilities = DeviceCapabilities( - model=capabilities.model, chip=capabilities.chip, memory=capabilities.memory, flops=DeviceFlops(fp16=capabilities.flops.fp16, fp32=capabilities.flops.fp32, int8=capabilities.flops.int8) - ) - topology.update_node(node_id, device_capabilities) - for node_id, peer_connections in response.peer_graph.items(): - for conn in peer_connections.connections: - topology.add_edge(node_id, conn.to_id, conn.description) - return topology - - async def send_result(self, request_id: str, result: List[int], is_finished: bool) -> None: - await self._ensure_connected() - tensor = None - if isinstance(result, np.ndarray): - tensor = node_service_pb2.Tensor(tensor_data=result.tobytes(), shape=result.shape, dtype=str(result.dtype)) - result = [] - request = node_service_pb2.SendResultRequest(request_id=request_id, result=result, tensor=tensor, is_finished=is_finished) - await self.stub.SendResult(request) - - async def send_opaque_status(self, request_id: str, status: str) -> None: - await self._ensure_connected() - request = node_service_pb2.SendOpaqueStatusRequest(request_id=request_id, status=status) - await asyncio.wait_for(self.stub.SendOpaqueStatus(request), timeout=10.0) - - def serialize_inference_state(self, inference_state: dict) -> node_service_pb2.InferenceState: - proto_inference_state = node_service_pb2.InferenceState() - other_data = {} - for k, v in inference_state.items(): - if isinstance(v, mx.array): - np_array = np.array(v) - tensor_data = node_service_pb2.Tensor(tensor_data=np_array.tobytes(), shape=list(np_array.shape), dtype=str(np_array.dtype)) - proto_inference_state.tensor_data[k].CopyFrom(tensor_data) - elif isinstance(v, list) and all(isinstance(item, mx.array) for item in v): - tensor_list = node_service_pb2.TensorList() - for tensor in v: - np_array = np.array(tensor) - tensor_data = node_service_pb2.Tensor(tensor_data=np_array.tobytes(), shape=list(np_array.shape), dtype=str(np_array.dtype)) - tensor_list.tensors.append(tensor_data) - proto_inference_state.tensor_list_data[k].CopyFrom(tensor_list) - else: - # For non-tensor data, we'll still use JSON - other_data[k] = v - if other_data: - proto_inference_state.other_data_json = json.dumps(other_data) - return proto_inference_state diff --git a/exo/networking/grpc/grpc_server.py b/exo/networking/grpc/grpc_server.py deleted file mode 100644 index cbc33f94..00000000 --- a/exo/networking/grpc/grpc_server.py +++ /dev/null @@ -1,173 +0,0 @@ -import grpc -from concurrent import futures -import numpy as np -from asyncio import CancelledError - -import platform - -from . import node_service_pb2 -from . import node_service_pb2_grpc -from exo import DEBUG -from exo.inference.shard import Shard -from exo.orchestration import Node -import json - -if platform.system().lower() == "darwin" and platform.machine().lower() == "arm64": - import mlx.core as mx -else: - import numpy as mx - - -class GRPCServer(node_service_pb2_grpc.NodeServiceServicer): - def __init__(self, node: Node, host: str, port: int): - self.node = node - self.host = host - self.port = port - self.server = None - - async def start(self) -> None: - self.server = grpc.aio.server( - futures.ThreadPoolExecutor(max_workers=32), - options=[ - ("grpc.max_metadata_size", 32*1024*1024), - ("grpc.max_send_message_length", 256*1024*1024), - ("grpc.max_receive_message_length", 256*1024*1024), - ("grpc.keepalive_time_ms", 10000), - ("grpc.keepalive_timeout_ms", 5000), - ("grpc.http2.max_pings_without_data", 0), - ("grpc.http2.min_time_between_pings_ms", 10000), - ("grpc.http2.min_ping_interval_without_data_ms", 5000), - ("grpc.max_concurrent_streams", 100), - ("grpc.tcp_nodelay", 1), - ("grpc.optimization_target", "throughput"), - ("grpc.keepalive_permit_without_calls", 1), - ("grpc.http2.max_concurrent_streams", 0), # Unlimited concurrent streams - ], - ) - node_service_pb2_grpc.add_NodeServiceServicer_to_server(self, self.server) - listen_addr = f"{self.host}:{self.port}" - self.server.add_insecure_port(listen_addr) - await self.server.start() - if DEBUG >= 1: print(f"Server started, listening on {listen_addr}") - - async def stop(self) -> None: - if self.server: - try: - await self.server.stop(grace=5) - await self.server.wait_for_termination() - except CancelledError: - pass - if DEBUG >= 1: print("Server stopped and all connections are closed") - - async def SendPrompt(self, request, context): - shard = Shard( - model_id=request.shard.model_id, - start_layer=request.shard.start_layer, - end_layer=request.shard.end_layer, - n_layers=request.shard.n_layers, - ) - prompt = request.prompt - request_id = request.request_id - inference_state = None if request.inference_state is None else self.deserialize_inference_state(request.inference_state) - result = await self.node.process_prompt(shard, prompt, request_id, inference_state) - if DEBUG >= 5: print(f"SendPrompt {shard=} {prompt=} {request_id=} result: {result}") - tensor_data = result.tobytes() if result is not None else None - return node_service_pb2.Tensor(tensor_data=tensor_data, shape=result.shape, dtype=str(result.dtype)) if result is not None else node_service_pb2.Tensor() - - async def SendTensor(self, request, context): - shard = Shard( - model_id=request.shard.model_id, - start_layer=request.shard.start_layer, - end_layer=request.shard.end_layer, - n_layers=request.shard.n_layers, - ) - tensor = np.frombuffer(request.tensor.tensor_data, dtype=np.dtype(request.tensor.dtype)).reshape(request.tensor.shape) - request_id = request.request_id - - inference_state = None if request.inference_state is None else self.deserialize_inference_state(request.inference_state) - - result = await self.node.process_tensor(shard, tensor, request_id, inference_state) - if DEBUG >= 5: print(f"SendTensor tensor {shard=} {tensor=} {request_id=} result: {result}") - tensor_data = result.tobytes() if result is not None else None - return node_service_pb2.Tensor(tensor_data=tensor_data, shape=result.shape, dtype=str(result.dtype)) if result is not None else node_service_pb2.Tensor() - - async def SendExample(self, request, context): - shard = Shard( - model_id=request.shard.model_id, - start_layer=request.shard.start_layer, - end_layer=request.shard.end_layer, - n_layers=request.shard.n_layers, - ) - example = np.frombuffer(request.example.tensor_data, dtype=np.dtype(request.example.dtype)).reshape(request.example.shape) - target = np.frombuffer(request.target.tensor_data, dtype=np.dtype(request.target.dtype)).reshape(request.target.shape) - length = np.frombuffer(request.length.tensor_data, dtype=np.dtype(request.length.dtype)).reshape(request.length.shape) - train = request.train - request_id = request.request_id - - if train and not shard.is_first_layer(): - loss, grad = await self.node.process_example(shard, example, target, length, train, request_id) - tensor_data = grad.tobytes() - grad_tensor = node_service_pb2.Tensor(tensor_data=tensor_data, shape=grad.shape, dtype=str(grad.dtype)) - return node_service_pb2.Loss(loss=loss, grads=grad_tensor) - else: - loss = await self.node.process_example(shard, example, target, length, train, request_id) - return node_service_pb2.Loss(loss=loss, grads=None) - - async def CollectTopology(self, request, context): - max_depth = request.max_depth - visited = set(request.visited) - topology = self.node.current_topology - nodes = { - node_id: - node_service_pb2.DeviceCapabilities( - model=cap.model, - chip=cap.chip, - memory=cap.memory, - flops=node_service_pb2.DeviceFlops(fp32=cap.flops.fp32, fp16=cap.flops.fp16, int8=cap.flops.int8), - ) - for node_id, cap in topology.nodes.items() - } - peer_graph = { - node_id: node_service_pb2.PeerConnections(connections=[node_service_pb2.PeerConnection(to_id=conn.to_id, description=conn.description) for conn in connections]) - for node_id, connections in topology.peer_graph.items() - } - if DEBUG >= 5: print(f"CollectTopology {max_depth=} {visited=} {nodes=} {peer_graph=}") - return node_service_pb2.Topology(nodes=nodes, peer_graph=peer_graph) - - async def SendResult(self, request, context): - request_id = request.request_id - result = request.result - is_finished = request.is_finished - img = request.tensor - if DEBUG >= 5: print(f"Received SendResult request: {request_id=} {result=} {is_finished=}") - result = list(result) - if len(img.tensor_data) > 0: - result = np.frombuffer(img.tensor_data, dtype=np.dtype(img.dtype)).reshape(img.shape) - self.node.on_token.trigger_all(request_id, result, is_finished) - return node_service_pb2.Empty() - - async def SendOpaqueStatus(self, request, context): - request_id = request.request_id - status = request.status - if DEBUG >= 8: print(f"Received SendOpaqueStatus request: {request_id=} {status=}") - self.node.on_opaque_status.trigger_all(request_id, status) - return node_service_pb2.Empty() - - async def HealthCheck(self, request, context): - return node_service_pb2.HealthCheckResponse(is_healthy=True) - - def deserialize_inference_state(self, inference_state_proto: node_service_pb2.InferenceState) -> dict: - inference_state = {} - - for k, tensor_data in inference_state_proto.tensor_data.items(): - np_array = np.frombuffer(tensor_data.tensor_data, dtype=tensor_data.dtype).reshape(tensor_data.shape) - inference_state[k] = mx.array(np_array) - - for k, tensor_list in inference_state_proto.tensor_list_data.items(): - inference_state[k] = [mx.array(np.frombuffer(tensor.tensor_data, dtype=tensor.dtype).reshape(tensor.shape)) for tensor in tensor_list.tensors] - - if inference_state_proto.other_data_json: - other_data = json.loads(inference_state_proto.other_data_json) - inference_state.update(other_data) - - return inference_state diff --git a/exo/networking/grpc/node_service.proto b/exo/networking/grpc/node_service.proto deleted file mode 100644 index 882a5247..00000000 --- a/exo/networking/grpc/node_service.proto +++ /dev/null @@ -1,116 +0,0 @@ -syntax = "proto3"; - -package node_service; - -service NodeService { - rpc SendPrompt (PromptRequest) returns (Tensor) {} - rpc SendTensor (TensorRequest) returns (Tensor) {} - rpc SendExample (ExampleRequest) returns (Loss) {} - rpc CollectTopology (CollectTopologyRequest) returns (Topology) {} - rpc SendResult (SendResultRequest) returns (Empty) {} - rpc SendOpaqueStatus (SendOpaqueStatusRequest) returns (Empty) {} - rpc HealthCheck (HealthCheckRequest) returns (HealthCheckResponse) {} -} - -message Shard { - string model_id = 1; - int32 start_layer = 2; - int32 end_layer = 3; - int32 n_layers = 4; -} - -message PromptRequest { - Shard shard = 1; - string prompt = 2; - optional string request_id = 3; - optional InferenceState inference_state = 4; -} - -message TensorRequest { - Shard shard = 1; - Tensor tensor = 2; - optional string request_id = 3; - optional InferenceState inference_state = 4; -} - -message ExampleRequest { - Shard shard = 1; - Tensor example = 2; - Tensor target = 3; - Tensor length = 4; - bool train = 5; - optional string request_id = 6; -} - -message Loss { - float loss = 1; - optional Tensor grads = 2; -} - -message Tensor { - bytes tensor_data = 1; - repeated int32 shape = 2; - string dtype = 3; -} - -message TensorList { - repeated Tensor tensors = 1; -} - -message InferenceState { - map tensor_data = 1; - map tensor_list_data = 2; - string other_data_json = 3; -} - -message CollectTopologyRequest { - repeated string visited = 1; - int32 max_depth = 2; -} - -message Topology { - map nodes = 1; - map peer_graph = 2; -} - -message PeerConnection { - string to_id = 1; - optional string description = 2; -} - -message PeerConnections { - repeated PeerConnection connections = 1; -} - -message DeviceFlops { - double fp32 = 1; - double fp16 = 2; - double int8 = 3; -} - -message DeviceCapabilities { - string model = 1; - string chip = 2; - int32 memory = 3; - DeviceFlops flops = 4; -} - -message SendResultRequest { - string request_id = 1; - repeated int32 result = 2; - optional Tensor tensor = 3; - bool is_finished = 4; -} - -message SendOpaqueStatusRequest { - string request_id = 1; - string status = 2; -} - -message HealthCheckRequest {} - -message HealthCheckResponse { - bool is_healthy = 1; -} - -message Empty {} diff --git a/exo/networking/grpc/node_service_pb2.py b/exo/networking/grpc/node_service_pb2.py deleted file mode 100644 index 9a83380d..00000000 --- a/exo/networking/grpc/node_service_pb2.py +++ /dev/null @@ -1,90 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE -# source: node_service.proto -# Protobuf Python Version: 5.27.2 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 5, - 27, - 2, - '', - 'node_service.proto' -) -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12node_service.proto\x12\x0cnode_service\"S\n\x05Shard\x12\x10\n\x08model_id\x18\x01 \x01(\t\x12\x13\n\x0bstart_layer\x18\x02 \x01(\x05\x12\x11\n\tend_layer\x18\x03 \x01(\x05\x12\x10\n\x08n_layers\x18\x04 \x01(\x05\"\xbb\x01\n\rPromptRequest\x12\"\n\x05shard\x18\x01 \x01(\x0b\x32\x13.node_service.Shard\x12\x0e\n\x06prompt\x18\x02 \x01(\t\x12\x17\n\nrequest_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12:\n\x0finference_state\x18\x04 \x01(\x0b\x32\x1c.node_service.InferenceStateH\x01\x88\x01\x01\x42\r\n\x0b_request_idB\x12\n\x10_inference_state\"\xd1\x01\n\rTensorRequest\x12\"\n\x05shard\x18\x01 \x01(\x0b\x32\x13.node_service.Shard\x12$\n\x06tensor\x18\x02 \x01(\x0b\x32\x14.node_service.Tensor\x12\x17\n\nrequest_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12:\n\x0finference_state\x18\x04 \x01(\x0b\x32\x1c.node_service.InferenceStateH\x01\x88\x01\x01\x42\r\n\x0b_request_idB\x12\n\x10_inference_state\"\xde\x01\n\x0e\x45xampleRequest\x12\"\n\x05shard\x18\x01 \x01(\x0b\x32\x13.node_service.Shard\x12%\n\x07\x65xample\x18\x02 \x01(\x0b\x32\x14.node_service.Tensor\x12$\n\x06target\x18\x03 \x01(\x0b\x32\x14.node_service.Tensor\x12$\n\x06length\x18\x04 \x01(\x0b\x32\x14.node_service.Tensor\x12\r\n\x05train\x18\x05 \x01(\x08\x12\x17\n\nrequest_id\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\r\n\x0b_request_id\"H\n\x04Loss\x12\x0c\n\x04loss\x18\x01 \x01(\x02\x12(\n\x05grads\x18\x02 \x01(\x0b\x32\x14.node_service.TensorH\x00\x88\x01\x01\x42\x08\n\x06_grads\";\n\x06Tensor\x12\x13\n\x0btensor_data\x18\x01 \x01(\x0c\x12\r\n\x05shape\x18\x02 \x03(\x05\x12\r\n\x05\x64type\x18\x03 \x01(\t\"3\n\nTensorList\x12%\n\x07tensors\x18\x01 \x03(\x0b\x32\x14.node_service.Tensor\"\xd2\x02\n\x0eInferenceState\x12\x41\n\x0btensor_data\x18\x01 \x03(\x0b\x32,.node_service.InferenceState.TensorDataEntry\x12J\n\x10tensor_list_data\x18\x02 \x03(\x0b\x32\x30.node_service.InferenceState.TensorListDataEntry\x12\x17\n\x0fother_data_json\x18\x03 \x01(\t\x1aG\n\x0fTensorDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12#\n\x05value\x18\x02 \x01(\x0b\x32\x14.node_service.Tensor:\x02\x38\x01\x1aO\n\x13TensorListDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\'\n\x05value\x18\x02 \x01(\x0b\x32\x18.node_service.TensorList:\x02\x38\x01\"<\n\x16\x43ollectTopologyRequest\x12\x0f\n\x07visited\x18\x01 \x03(\t\x12\x11\n\tmax_depth\x18\x02 \x01(\x05\"\x98\x02\n\x08Topology\x12\x30\n\x05nodes\x18\x01 \x03(\x0b\x32!.node_service.Topology.NodesEntry\x12\x39\n\npeer_graph\x18\x02 \x03(\x0b\x32%.node_service.Topology.PeerGraphEntry\x1aN\n\nNodesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12/\n\x05value\x18\x02 \x01(\x0b\x32 .node_service.DeviceCapabilities:\x02\x38\x01\x1aO\n\x0ePeerGraphEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12,\n\x05value\x18\x02 \x01(\x0b\x32\x1d.node_service.PeerConnections:\x02\x38\x01\"I\n\x0ePeerConnection\x12\r\n\x05to_id\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"D\n\x0fPeerConnections\x12\x31\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x1c.node_service.PeerConnection\"7\n\x0b\x44\x65viceFlops\x12\x0c\n\x04\x66p32\x18\x01 \x01(\x01\x12\x0c\n\x04\x66p16\x18\x02 \x01(\x01\x12\x0c\n\x04int8\x18\x03 \x01(\x01\"k\n\x12\x44\x65viceCapabilities\x12\r\n\x05model\x18\x01 \x01(\t\x12\x0c\n\x04\x63hip\x18\x02 \x01(\t\x12\x0e\n\x06memory\x18\x03 \x01(\x05\x12(\n\x05\x66lops\x18\x04 \x01(\x0b\x32\x19.node_service.DeviceFlops\"\x82\x01\n\x11SendResultRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x0e\n\x06result\x18\x02 \x03(\x05\x12)\n\x06tensor\x18\x03 \x01(\x0b\x32\x14.node_service.TensorH\x00\x88\x01\x01\x12\x13\n\x0bis_finished\x18\x04 \x01(\x08\x42\t\n\x07_tensor\"=\n\x17SendOpaqueStatusRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x0e\n\x06status\x18\x02 \x01(\t\"\x14\n\x12HealthCheckRequest\")\n\x13HealthCheckResponse\x12\x12\n\nis_healthy\x18\x01 \x01(\x08\"\x07\n\x05\x45mpty2\x97\x04\n\x0bNodeService\x12\x41\n\nSendPrompt\x12\x1b.node_service.PromptRequest\x1a\x14.node_service.Tensor\"\x00\x12\x41\n\nSendTensor\x12\x1b.node_service.TensorRequest\x1a\x14.node_service.Tensor\"\x00\x12\x41\n\x0bSendExample\x12\x1c.node_service.ExampleRequest\x1a\x12.node_service.Loss\"\x00\x12Q\n\x0f\x43ollectTopology\x12$.node_service.CollectTopologyRequest\x1a\x16.node_service.Topology\"\x00\x12\x44\n\nSendResult\x12\x1f.node_service.SendResultRequest\x1a\x13.node_service.Empty\"\x00\x12P\n\x10SendOpaqueStatus\x12%.node_service.SendOpaqueStatusRequest\x1a\x13.node_service.Empty\"\x00\x12T\n\x0bHealthCheck\x12 .node_service.HealthCheckRequest\x1a!.node_service.HealthCheckResponse\"\x00\x62\x06proto3') - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'node_service_pb2', _globals) -if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None - _globals['_INFERENCESTATE_TENSORDATAENTRY']._loaded_options = None - _globals['_INFERENCESTATE_TENSORDATAENTRY']._serialized_options = b'8\001' - _globals['_INFERENCESTATE_TENSORLISTDATAENTRY']._loaded_options = None - _globals['_INFERENCESTATE_TENSORLISTDATAENTRY']._serialized_options = b'8\001' - _globals['_TOPOLOGY_NODESENTRY']._loaded_options = None - _globals['_TOPOLOGY_NODESENTRY']._serialized_options = b'8\001' - _globals['_TOPOLOGY_PEERGRAPHENTRY']._loaded_options = None - _globals['_TOPOLOGY_PEERGRAPHENTRY']._serialized_options = b'8\001' - _globals['_SHARD']._serialized_start=36 - _globals['_SHARD']._serialized_end=119 - _globals['_PROMPTREQUEST']._serialized_start=122 - _globals['_PROMPTREQUEST']._serialized_end=309 - _globals['_TENSORREQUEST']._serialized_start=312 - _globals['_TENSORREQUEST']._serialized_end=521 - _globals['_EXAMPLEREQUEST']._serialized_start=524 - _globals['_EXAMPLEREQUEST']._serialized_end=746 - _globals['_LOSS']._serialized_start=748 - _globals['_LOSS']._serialized_end=820 - _globals['_TENSOR']._serialized_start=822 - _globals['_TENSOR']._serialized_end=881 - _globals['_TENSORLIST']._serialized_start=883 - _globals['_TENSORLIST']._serialized_end=934 - _globals['_INFERENCESTATE']._serialized_start=937 - _globals['_INFERENCESTATE']._serialized_end=1275 - _globals['_INFERENCESTATE_TENSORDATAENTRY']._serialized_start=1123 - _globals['_INFERENCESTATE_TENSORDATAENTRY']._serialized_end=1194 - _globals['_INFERENCESTATE_TENSORLISTDATAENTRY']._serialized_start=1196 - _globals['_INFERENCESTATE_TENSORLISTDATAENTRY']._serialized_end=1275 - _globals['_COLLECTTOPOLOGYREQUEST']._serialized_start=1277 - _globals['_COLLECTTOPOLOGYREQUEST']._serialized_end=1337 - _globals['_TOPOLOGY']._serialized_start=1340 - _globals['_TOPOLOGY']._serialized_end=1620 - _globals['_TOPOLOGY_NODESENTRY']._serialized_start=1461 - _globals['_TOPOLOGY_NODESENTRY']._serialized_end=1539 - _globals['_TOPOLOGY_PEERGRAPHENTRY']._serialized_start=1541 - _globals['_TOPOLOGY_PEERGRAPHENTRY']._serialized_end=1620 - _globals['_PEERCONNECTION']._serialized_start=1622 - _globals['_PEERCONNECTION']._serialized_end=1695 - _globals['_PEERCONNECTIONS']._serialized_start=1697 - _globals['_PEERCONNECTIONS']._serialized_end=1765 - _globals['_DEVICEFLOPS']._serialized_start=1767 - _globals['_DEVICEFLOPS']._serialized_end=1822 - _globals['_DEVICECAPABILITIES']._serialized_start=1824 - _globals['_DEVICECAPABILITIES']._serialized_end=1931 - _globals['_SENDRESULTREQUEST']._serialized_start=1934 - _globals['_SENDRESULTREQUEST']._serialized_end=2064 - _globals['_SENDOPAQUESTATUSREQUEST']._serialized_start=2066 - _globals['_SENDOPAQUESTATUSREQUEST']._serialized_end=2127 - _globals['_HEALTHCHECKREQUEST']._serialized_start=2129 - _globals['_HEALTHCHECKREQUEST']._serialized_end=2149 - _globals['_HEALTHCHECKRESPONSE']._serialized_start=2151 - _globals['_HEALTHCHECKRESPONSE']._serialized_end=2192 - _globals['_EMPTY']._serialized_start=2194 - _globals['_EMPTY']._serialized_end=2201 - _globals['_NODESERVICE']._serialized_start=2204 - _globals['_NODESERVICE']._serialized_end=2739 -# @@protoc_insertion_point(module_scope) diff --git a/exo/networking/grpc/node_service_pb2_grpc.py b/exo/networking/grpc/node_service_pb2_grpc.py deleted file mode 100644 index ac12e2f7..00000000 --- a/exo/networking/grpc/node_service_pb2_grpc.py +++ /dev/null @@ -1,355 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -"""Client and server classes corresponding to protobuf-defined services.""" -import grpc -import warnings - -from . import node_service_pb2 as node__service__pb2 - -GRPC_GENERATED_VERSION = '1.67.0' -GRPC_VERSION = grpc.__version__ -_version_not_supported = False - -try: - from grpc._utilities import first_version_is_lower - _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) -except ImportError: - _version_not_supported = True - -if _version_not_supported: - raise RuntimeError( - f'The grpc package installed is at version {GRPC_VERSION},' - + f' but the generated code in node_service_pb2_grpc.py depends on' - + f' grpcio>={GRPC_GENERATED_VERSION}.' - + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' - + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' - ) - - -class NodeServiceStub(object): - """Missing associated documentation comment in .proto file.""" - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.SendPrompt = channel.unary_unary( - '/node_service.NodeService/SendPrompt', - request_serializer=node__service__pb2.PromptRequest.SerializeToString, - response_deserializer=node__service__pb2.Tensor.FromString, - _registered_method=True) - self.SendTensor = channel.unary_unary( - '/node_service.NodeService/SendTensor', - request_serializer=node__service__pb2.TensorRequest.SerializeToString, - response_deserializer=node__service__pb2.Tensor.FromString, - _registered_method=True) - self.SendExample = channel.unary_unary( - '/node_service.NodeService/SendExample', - request_serializer=node__service__pb2.ExampleRequest.SerializeToString, - response_deserializer=node__service__pb2.Loss.FromString, - _registered_method=True) - self.CollectTopology = channel.unary_unary( - '/node_service.NodeService/CollectTopology', - request_serializer=node__service__pb2.CollectTopologyRequest.SerializeToString, - response_deserializer=node__service__pb2.Topology.FromString, - _registered_method=True) - self.SendResult = channel.unary_unary( - '/node_service.NodeService/SendResult', - request_serializer=node__service__pb2.SendResultRequest.SerializeToString, - response_deserializer=node__service__pb2.Empty.FromString, - _registered_method=True) - self.SendOpaqueStatus = channel.unary_unary( - '/node_service.NodeService/SendOpaqueStatus', - request_serializer=node__service__pb2.SendOpaqueStatusRequest.SerializeToString, - response_deserializer=node__service__pb2.Empty.FromString, - _registered_method=True) - self.HealthCheck = channel.unary_unary( - '/node_service.NodeService/HealthCheck', - request_serializer=node__service__pb2.HealthCheckRequest.SerializeToString, - response_deserializer=node__service__pb2.HealthCheckResponse.FromString, - _registered_method=True) - - -class NodeServiceServicer(object): - """Missing associated documentation comment in .proto file.""" - - def SendPrompt(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SendTensor(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SendExample(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def CollectTopology(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SendResult(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SendOpaqueStatus(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def HealthCheck(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_NodeServiceServicer_to_server(servicer, server): - rpc_method_handlers = { - 'SendPrompt': grpc.unary_unary_rpc_method_handler( - servicer.SendPrompt, - request_deserializer=node__service__pb2.PromptRequest.FromString, - response_serializer=node__service__pb2.Tensor.SerializeToString, - ), - 'SendTensor': grpc.unary_unary_rpc_method_handler( - servicer.SendTensor, - request_deserializer=node__service__pb2.TensorRequest.FromString, - response_serializer=node__service__pb2.Tensor.SerializeToString, - ), - 'SendExample': grpc.unary_unary_rpc_method_handler( - servicer.SendExample, - request_deserializer=node__service__pb2.ExampleRequest.FromString, - response_serializer=node__service__pb2.Loss.SerializeToString, - ), - 'CollectTopology': grpc.unary_unary_rpc_method_handler( - servicer.CollectTopology, - request_deserializer=node__service__pb2.CollectTopologyRequest.FromString, - response_serializer=node__service__pb2.Topology.SerializeToString, - ), - 'SendResult': grpc.unary_unary_rpc_method_handler( - servicer.SendResult, - request_deserializer=node__service__pb2.SendResultRequest.FromString, - response_serializer=node__service__pb2.Empty.SerializeToString, - ), - 'SendOpaqueStatus': grpc.unary_unary_rpc_method_handler( - servicer.SendOpaqueStatus, - request_deserializer=node__service__pb2.SendOpaqueStatusRequest.FromString, - response_serializer=node__service__pb2.Empty.SerializeToString, - ), - 'HealthCheck': grpc.unary_unary_rpc_method_handler( - servicer.HealthCheck, - request_deserializer=node__service__pb2.HealthCheckRequest.FromString, - response_serializer=node__service__pb2.HealthCheckResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'node_service.NodeService', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) - server.add_registered_method_handlers('node_service.NodeService', rpc_method_handlers) - - - # This class is part of an EXPERIMENTAL API. -class NodeService(object): - """Missing associated documentation comment in .proto file.""" - - @staticmethod - def SendPrompt(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/node_service.NodeService/SendPrompt', - node__service__pb2.PromptRequest.SerializeToString, - node__service__pb2.Tensor.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) - - @staticmethod - def SendTensor(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/node_service.NodeService/SendTensor', - node__service__pb2.TensorRequest.SerializeToString, - node__service__pb2.Tensor.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) - - @staticmethod - def SendExample(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/node_service.NodeService/SendExample', - node__service__pb2.ExampleRequest.SerializeToString, - node__service__pb2.Loss.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) - - @staticmethod - def CollectTopology(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/node_service.NodeService/CollectTopology', - node__service__pb2.CollectTopologyRequest.SerializeToString, - node__service__pb2.Topology.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) - - @staticmethod - def SendResult(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/node_service.NodeService/SendResult', - node__service__pb2.SendResultRequest.SerializeToString, - node__service__pb2.Empty.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) - - @staticmethod - def SendOpaqueStatus(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/node_service.NodeService/SendOpaqueStatus', - node__service__pb2.SendOpaqueStatusRequest.SerializeToString, - node__service__pb2.Empty.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) - - @staticmethod - def HealthCheck(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary( - request, - target, - '/node_service.NodeService/HealthCheck', - node__service__pb2.HealthCheckRequest.SerializeToString, - node__service__pb2.HealthCheckResponse.FromString, - options, - channel_credentials, - insecure, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - _registered_method=True) diff --git a/exo/networking/manual/__init__.py b/exo/networking/manual/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/exo/networking/manual/manual_discovery.py b/exo/networking/manual/manual_discovery.py deleted file mode 100644 index 041eafa6..00000000 --- a/exo/networking/manual/manual_discovery.py +++ /dev/null @@ -1,101 +0,0 @@ -import os -import asyncio -from typing import Dict, List, Callable, Optional -from concurrent.futures import ThreadPoolExecutor - -from exo.networking.discovery import Discovery -from exo.topology.device_capabilities import DeviceCapabilities -from exo.networking.manual.network_topology_config import NetworkTopology, PeerConfig -from exo.helpers import DEBUG_DISCOVERY -from exo.networking.peer_handle import PeerHandle - - -class ManualDiscovery(Discovery): - def __init__( - self, - network_config_path: str, - node_id: str, - create_peer_handle: Callable[[str, str, str, DeviceCapabilities], PeerHandle], - ): - self.network_config_path = network_config_path - self.node_id = node_id - self.create_peer_handle = create_peer_handle - - self.listen_task = None - self.known_peers: Dict[str, PeerHandle] = {} - - self._cached_peers: Dict[str, PeerConfig] = {} - self._last_modified_time: Optional[float] = None - self._file_executor = ThreadPoolExecutor(max_workers=1) - - async def start(self) -> None: - self.listen_task = asyncio.create_task(self.task_find_peers_from_config()) - - async def stop(self) -> None: - if self.listen_task: self.listen_task.cancel() - self._file_executor.shutdown(wait=True) - - async def discover_peers(self, wait_for_peers: int = 0) -> List[PeerHandle]: - if wait_for_peers > 0: - while len(self.known_peers) < wait_for_peers: - if DEBUG_DISCOVERY >= 2: print(f"Current peers: {len(self.known_peers)}/{wait_for_peers}. Waiting for more peers...") - await asyncio.sleep(0.1) - if DEBUG_DISCOVERY >= 2: print(f"Discovered peers: {[peer.id() for peer in self.known_peers.values()]}") - return list(self.known_peers.values()) - - async def task_find_peers_from_config(self): - if DEBUG_DISCOVERY >= 2: print("Starting task to find peers from config...") - while True: - peers_from_config = await self._get_peers() - new_known_peers = {} - for peer_id, peer_config in peers_from_config.items(): - try: - if DEBUG_DISCOVERY >= 2: print(f"Checking peer {peer_id=} at {peer_config.address}:{peer_config.port}") - peer = self.known_peers.get(peer_id) - if not peer: - if DEBUG_DISCOVERY >= 2: print(f"{peer_id=} not found in known peers. Adding.") - peer = self.create_peer_handle(peer_id, f"{peer_config.address}:{peer_config.port}", "MAN", peer_config.device_capabilities) - is_healthy = await peer.health_check() - if is_healthy: - if DEBUG_DISCOVERY >= 2: print(f"{peer_id=} at {peer_config.address}:{peer_config.port} is healthy.") - new_known_peers[peer_id] = peer - elif DEBUG_DISCOVERY >= 2: - print(f"{peer_id=} at {peer_config.address}:{peer_config.port} is not healthy. Removing.") - except Exception as e: - if DEBUG_DISCOVERY >= 2: print(f"Exception occurred when attempting to add {peer_id=}: {e}") - self.known_peers = new_known_peers - await asyncio.sleep(5.0) - - if DEBUG_DISCOVERY >= 2: print(f"Current known peers: {[peer.id() for peer in self.known_peers.values()]}") - - async def _get_peers(self): - try: - loop = asyncio.get_running_loop() - current_mtime = await loop.run_in_executor(self._file_executor, os.path.getmtime, self.network_config_path) - - if (self._cached_peers is not None and self._last_modified_time is not None and current_mtime <= self._last_modified_time): - return self._cached_peers - - topology = await loop.run_in_executor(self._file_executor, NetworkTopology.from_path, self.network_config_path) - - if self.node_id not in topology.peers: - raise ValueError( - f"Node ID {self.node_id} not found in network config file " - f"{self.network_config_path}. Please run with `node_id` set to " - f"one of the keys in the config file: {[k for k, _ in topology.peers]}" - ) - - peers_in_network = topology.peers - peers_in_network.pop(self.node_id) - - self._cached_peers = peers_in_network - self._last_modified_time = current_mtime - - return peers_in_network - - except Exception as e: - if DEBUG_DISCOVERY >= 2: - print(f"Error when loading network config file from {self.network_config_path}. " - f"Please update the config file in order to successfully discover peers. " - f"Exception: {e}") - return self._cached_peers diff --git a/exo/networking/manual/network_topology_config.py b/exo/networking/manual/network_topology_config.py deleted file mode 100644 index c0246ec3..00000000 --- a/exo/networking/manual/network_topology_config.py +++ /dev/null @@ -1,31 +0,0 @@ -from typing import Dict -from pydantic import BaseModel, ValidationError - -from exo.topology.device_capabilities import DeviceCapabilities - - -class PeerConfig(BaseModel): - address: str - port: int - device_capabilities: DeviceCapabilities - - -class NetworkTopology(BaseModel): - """Configuration of the network. A collection outlining all nodes in the network, including the node this is running from.""" - - peers: Dict[str, PeerConfig] - """ - node_id to PeerConfig. The node_id is used to identify the peer in the discovery process. The node that this is running from should be included in this dict. - """ - @classmethod - def from_path(cls, path: str) -> "NetworkTopology": - try: - with open(path, "r") as f: - config_data = f.read() - except FileNotFoundError as e: - raise FileNotFoundError(f"Config file not found at {path}") from e - - try: - return cls.model_validate_json(config_data) - except ValidationError as e: - raise ValueError(f"Error validating network topology config from {path}: {e}") from e diff --git a/exo/networking/manual/test_data/invalid_config.json b/exo/networking/manual/test_data/invalid_config.json deleted file mode 100644 index 283feadf..00000000 --- a/exo/networking/manual/test_data/invalid_config.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "peers": { - "node1": { - "address": "localhost", - "device_capabilities": { - "model": "Unknown Model", - "chip": "Unknown Chip", - "memory": 0, - "flops": { - "fp32": 0, - "fp16": 0, - "int8": 0 - } - } - } - } -} diff --git a/exo/networking/manual/test_data/invalid_json.json b/exo/networking/manual/test_data/invalid_json.json deleted file mode 100644 index e69de29b..00000000 diff --git a/exo/networking/manual/test_data/test_config.json b/exo/networking/manual/test_data/test_config.json deleted file mode 100644 index 54eced72..00000000 --- a/exo/networking/manual/test_data/test_config.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "peers": { - "node1": { - "address": "localhost", - "port": 50051, - "device_capabilities": { - "model": "Unknown Model", - "chip": "Unknown Chip", - "memory": 0, - "flops": { - "fp32": 0, - "fp16": 0, - "int8": 0 - } - } - }, - "node2": { - "address": "localhost", - "port": 50052, - "device_capabilities": { - "model": "Unknown Model", - "chip": "Unknown Chip", - "memory": 0, - "flops": { - "fp32": 0, - "fp16": 0, - "int8": 0 - } - } - } - } -} \ No newline at end of file diff --git a/exo/networking/manual/test_data/test_config_single_node.json b/exo/networking/manual/test_data/test_config_single_node.json deleted file mode 100644 index 81a0670f..00000000 --- a/exo/networking/manual/test_data/test_config_single_node.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "peers": { - "node1": { - "address": "localhost", - "port": 50051, - "device_capabilities": { - "model": "Unknown Model", - "chip": "Unknown Chip", - "memory": 0, - "flops": { - "fp32": 0, - "fp16": 0, - "int8": 0 - } - } - } - } -} diff --git a/exo/networking/manual/test_manual_discovery.py b/exo/networking/manual/test_manual_discovery.py deleted file mode 100644 index 317fba9d..00000000 --- a/exo/networking/manual/test_manual_discovery.py +++ /dev/null @@ -1,181 +0,0 @@ -import json -import asyncio -import unittest -from unittest import mock -from exo.networking.manual.manual_discovery import ManualDiscovery -from exo.networking.manual.network_topology_config import NetworkTopology -from exo.networking.grpc.grpc_peer_handle import GRPCPeerHandle -from exo.networking.grpc.grpc_server import GRPCServer -from exo.orchestration.node import Node - -root_path = "./exo/networking/manual/test_data/test_config.json" - - -class TestSingleNodeManualDiscovery(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - self.peer1 = mock.AsyncMock() - self.peer1.connect = mock.AsyncMock() - self.discovery1 = ManualDiscovery( - root_path, - "node1", - create_peer_handle=lambda peer_id, address, description, device_capabilities: self.peer1, - ) - await self.discovery1.start() - - async def asyncTearDown(self): - await self.discovery1.stop() - - async def test_discovery(self): - peers1 = await self.discovery1.discover_peers(wait_for_peers=0) - assert len(peers1) == 0 - - self.peer1.connect.assert_not_called() - - -class TestManualDiscovery(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - self.peer1 = mock.AsyncMock() - self.peer2 = mock.AsyncMock() - self.peer1.connect = mock.AsyncMock() - self.peer2.connect = mock.AsyncMock() - self.discovery1 = ManualDiscovery( - root_path, - "node1", - create_peer_handle=lambda peer_id, address, description, device_capabilities: self.peer1, - ) - self.discovery2 = ManualDiscovery( - root_path, - "node2", - create_peer_handle=lambda peer_id, address, description, device_capabilities: self.peer2, - ) - await self.discovery1.start() - await self.discovery2.start() - - async def asyncTearDown(self): - await self.discovery1.stop() - await self.discovery2.stop() - - async def test_discovery(self): - peers1 = await self.discovery1.discover_peers(wait_for_peers=1) - assert len(peers1) == 1 - peers2 = await self.discovery2.discover_peers(wait_for_peers=1) - assert len(peers2) == 1 - - # connect has to be explicitly called after discovery - self.peer1.connect.assert_not_called() - self.peer2.connect.assert_not_called() - - -class TestManualDiscoveryWithGRPCPeerHandle(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - config = NetworkTopology.from_path(root_path) - - self.node1 = mock.AsyncMock(spec=Node) - self.node2 = mock.AsyncMock(spec=Node) - self.server1 = GRPCServer(self.node1, config.peers["node1"].address, config.peers["node1"].port) - self.server2 = GRPCServer(self.node2, config.peers["node2"].address, config.peers["node2"].port) - await self.server1.start() - await self.server2.start() - self.discovery1 = ManualDiscovery( - root_path, - "node1", - create_peer_handle=lambda peer_id, address, description, device_capabilities: GRPCPeerHandle(peer_id, address, description, device_capabilities), - ) - self.discovery2 = ManualDiscovery( - root_path, - "node2", - create_peer_handle=lambda peer_id, address, description, device_capabilities: GRPCPeerHandle(peer_id, address, description, device_capabilities), - ) - await self.discovery1.start() - await self.discovery2.start() - - async def asyncTearDown(self): - await self.discovery1.stop() - await self.discovery2.stop() - await self.server1.stop() - await self.server2.stop() - - async def test_grpc_discovery(self): - peers1 = await self.discovery1.discover_peers(wait_for_peers=1) - assert len(peers1) == 1 - peers2 = await self.discovery2.discover_peers(wait_for_peers=1) - assert len(peers2) == 1 - - # Connect - await peers1[0].connect() - await peers2[0].connect() - self.assertTrue(await peers1[0].is_connected()) - self.assertTrue(await peers2[0].is_connected()) - - # Kill server1 - await self.server1.stop() - - self.assertTrue(await peers1[0].is_connected()) - self.assertFalse(await peers2[0].is_connected()) - - # Kill server2 - await self.server2.stop() - - self.assertFalse(await peers1[0].is_connected()) - self.assertFalse(await peers2[0].is_connected()) - - async def test_dynamic_config_update(self): - initial_peers = await self.discovery1.discover_peers(wait_for_peers=1) - self.assertEqual(len(initial_peers), 1) - - # Save original config for cleanup - with open(root_path, "r") as f: - original_config = json.load(f) - - try: - updated_config = { - "peers": { - **original_config["peers"], - "node3": { - "address": "localhost", - "port": 50053, - "device_capabilities": { - "model": "Unknown Model", - "chip": "Unknown Chip", - "memory": 0, - "flops": {"fp32": 0, "fp16": 0, "int8": 0}, - }, - }, - } - } - - with open(root_path, "w") as f: - json.dump(updated_config, f, indent=2) - - node3 = mock.AsyncMock(spec=Node) - server3 = GRPCServer(node3, "localhost", 50053) - await server3.start() - - try: - # Wait for the config to be reloaded - await asyncio.sleep(1.5) - - updated_peers = await self.discovery1.discover_peers(wait_for_peers=2) - self.assertEqual(len(updated_peers), 2) - - for peer in updated_peers: - await peer.connect() - self.assertTrue(await peer.is_connected()) - - finally: - await server3.stop() - - finally: - # Restore the original config file - with open(root_path, "w") as f: - json.dump(original_config, f, indent=2) - - # Wait for the config to be reloaded again - await asyncio.sleep(1.5) - - updated_peers = await self.discovery1.discover_peers(wait_for_peers=1) - self.assertEqual(len(updated_peers), 1) - - -if __name__ == "__main__": - asyncio.run(unittest.main()) diff --git a/exo/networking/manual/test_network_topology_config.py b/exo/networking/manual/test_network_topology_config.py deleted file mode 100644 index b5b2fbd5..00000000 --- a/exo/networking/manual/test_network_topology_config.py +++ /dev/null @@ -1,49 +0,0 @@ -import unittest - -from exo.networking.manual.network_topology_config import NetworkTopology - -root_path = "./exo/networking/manual/test_data/" - - -class TestNetworkTopologyConfig(unittest.TestCase): - def test_from_path_invalid_path(self): - with self.assertRaises(FileNotFoundError) as e: - NetworkTopology.from_path("invalid_path") - self.assertEqual(str(e.exception), "Config file not found at invalid_path") - - def test_from_path_invalid_json(self): - with self.assertRaises(ValueError) as e: - NetworkTopology.from_path(root_path + "invalid_json.json") - self.assertIn("Error validating network topology config from", str(e.exception)) - self.assertIn("1 validation error for NetworkTopology\n Invalid JSON: EOF while parsing a value at line 1 column 0", str(e.exception)) - - def test_from_path_invalid_config(self): - with self.assertRaises(ValueError) as e: - NetworkTopology.from_path(root_path + "invalid_config.json") - self.assertIn("Error validating network topology config from", str(e.exception)) - self.assertIn("port\n Field required", str(e.exception)) - - def test_from_path_valid(self): - config = NetworkTopology.from_path(root_path + "test_config.json") - - self.assertEqual(config.peers["node1"].port, 50051) - self.assertEqual(config.peers["node1"].device_capabilities.model, "Unknown Model") - self.assertEqual(config.peers["node1"].address, "localhost") - self.assertEqual(config.peers["node1"].device_capabilities.chip, "Unknown Chip") - self.assertEqual(config.peers["node1"].device_capabilities.memory, 0) - self.assertEqual(config.peers["node1"].device_capabilities.flops.fp32, 0) - self.assertEqual(config.peers["node1"].device_capabilities.flops.fp16, 0) - self.assertEqual(config.peers["node1"].device_capabilities.flops.int8, 0) - - self.assertEqual(config.peers["node2"].port, 50052) - self.assertEqual(config.peers["node2"].device_capabilities.model, "Unknown Model") - self.assertEqual(config.peers["node2"].address, "localhost") - self.assertEqual(config.peers["node2"].device_capabilities.chip, "Unknown Chip") - self.assertEqual(config.peers["node2"].device_capabilities.memory, 0) - self.assertEqual(config.peers["node2"].device_capabilities.flops.fp32, 0) - self.assertEqual(config.peers["node2"].device_capabilities.flops.fp16, 0) - self.assertEqual(config.peers["node2"].device_capabilities.flops.int8, 0) - - -if __name__ == "__main__": - unittest.main() diff --git a/exo/networking/peer_handle.py b/exo/networking/peer_handle.py deleted file mode 100644 index d75318ef..00000000 --- a/exo/networking/peer_handle.py +++ /dev/null @@ -1,56 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Optional, Tuple, List -import numpy as np -from exo.inference.shard import Shard -from exo.topology.device_capabilities import DeviceCapabilities -from exo.topology.topology import Topology - - -class PeerHandle(ABC): - @abstractmethod - def id(self) -> str: - pass - - @abstractmethod - def addr(self) -> str: - pass - - @abstractmethod - def description(self) -> str: - pass - - @abstractmethod - def device_capabilities(self) -> DeviceCapabilities: - pass - - @abstractmethod - async def connect(self) -> None: - pass - - @abstractmethod - async def is_connected(self) -> bool: - pass - - @abstractmethod - async def disconnect(self) -> None: - pass - - @abstractmethod - async def health_check(self) -> bool: - pass - - @abstractmethod - async def send_prompt(self, shard: Shard, prompt: str, request_id: Optional[str] = None) -> Optional[np.array]: - pass - - @abstractmethod - async def send_tensor(self, shard: Shard, tensor: np.array, request_id: Optional[str] = None) -> Optional[np.array]: - pass - - @abstractmethod - async def send_result(self, request_id: str, result: List[int], is_finished: bool) -> None: - pass - - @abstractmethod - async def collect_topology(self, visited: set[str], max_depth: int) -> Topology: - pass diff --git a/exo/networking/server.py b/exo/networking/server.py deleted file mode 100644 index 8e7f9812..00000000 --- a/exo/networking/server.py +++ /dev/null @@ -1,11 +0,0 @@ -from abc import ABC, abstractmethod - - -class Server(ABC): - @abstractmethod - async def start(self) -> None: - pass - - @abstractmethod - async def stop(self) -> None: - pass diff --git a/exo/networking/tailscale/__init__.py b/exo/networking/tailscale/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/exo/networking/tailscale/tailscale_discovery.py b/exo/networking/tailscale/tailscale_discovery.py deleted file mode 100644 index 61a13507..00000000 --- a/exo/networking/tailscale/tailscale_discovery.py +++ /dev/null @@ -1,178 +0,0 @@ -import asyncio -import time -import traceback -from typing import List, Dict, Callable, Tuple -from exo.networking.discovery import Discovery -from exo.networking.peer_handle import PeerHandle -from exo.topology.device_capabilities import DeviceCapabilities, device_capabilities, UNKNOWN_DEVICE_CAPABILITIES -from exo.helpers import DEBUG, DEBUG_DISCOVERY -from .tailscale_helpers import get_device_id, update_device_attributes, get_device_attributes, get_tailscale_devices, Device - - -class TailscaleDiscovery(Discovery): - def __init__( - self, - node_id: str, - node_port: int, - create_peer_handle: Callable[[str, str, str, DeviceCapabilities], PeerHandle], - discovery_interval: int = 5, - discovery_timeout: int = 30, - update_interval: int = 15, - device_capabilities: DeviceCapabilities = UNKNOWN_DEVICE_CAPABILITIES, - tailscale_api_key: str = None, - tailnet: str = None, - allowed_node_ids: List[str] = None, - ): - self.node_id = node_id - self.node_port = node_port - self.create_peer_handle = create_peer_handle - self.discovery_interval = discovery_interval - self.discovery_timeout = discovery_timeout - self.update_interval = update_interval - self.device_capabilities = device_capabilities - self.known_peers: Dict[str, Tuple[PeerHandle, float, float]] = {} - self.discovery_task = None - self.cleanup_task = None - self.tailscale_api_key = tailscale_api_key - self.tailnet = tailnet - self.allowed_node_ids = allowed_node_ids - self._device_id = None - self.update_task = None - - async def start(self): - self.device_capabilities = await device_capabilities() - self.discovery_task = asyncio.create_task(self.task_discover_peers()) - self.cleanup_task = asyncio.create_task(self.task_cleanup_peers()) - self.update_task = asyncio.create_task(self.task_update_device_posture_attributes()) - - async def task_update_device_posture_attributes(self): - while True: - try: - await self.update_device_posture_attributes() - if DEBUG_DISCOVERY >= 2: - print(f"Updated device posture attributes") - except Exception as e: - print(f"Error updating device posture attributes: {e}") - print(traceback.format_exc()) - finally: - await asyncio.sleep(self.update_interval) - - async def get_device_id(self): - if self._device_id: - return self._device_id - self._device_id = await get_device_id() - return self._device_id - - async def update_device_posture_attributes(self): - await update_device_attributes(await self.get_device_id(), self.tailscale_api_key, self.node_id, self.node_port, self.device_capabilities) - - async def task_discover_peers(self): - while True: - try: - devices: dict[str, Device] = await get_tailscale_devices(self.tailscale_api_key, self.tailnet) - current_time = time.time() - - active_devices = {name: device for name, device in devices.items() if device.last_seen is not None and (current_time - device.last_seen.timestamp()) < 30} - - if DEBUG_DISCOVERY >= 4: print(f"Found tailscale devices: {devices}") - if DEBUG_DISCOVERY >= 2: print(f"Active tailscale devices: {len(active_devices)}/{len(devices)}") - if DEBUG_DISCOVERY >= 2: print("Time since last seen tailscale devices", [(current_time - device.last_seen.timestamp()) for device in devices.values()]) - - for device in active_devices.values(): - if device.name == self.node_id: continue - peer_host = device.addresses[0] - peer_id, peer_port, device_capabilities = await get_device_attributes(device.device_id, self.tailscale_api_key) - if not peer_id: - if DEBUG_DISCOVERY >= 4: print(f"{device.device_id} does not have exo node attributes. skipping.") - continue - - if self.allowed_node_ids and peer_id not in self.allowed_node_ids: - if DEBUG_DISCOVERY >= 2: print(f"Ignoring peer {peer_id} as it's not in the allowed node IDs list") - continue - - if peer_id not in self.known_peers or self.known_peers[peer_id][0].addr() != f"{peer_host}:{peer_port}": - new_peer_handle = self.create_peer_handle(peer_id, f"{peer_host}:{peer_port}", "TS", device_capabilities) - if not await new_peer_handle.health_check(): - if DEBUG >= 1: print(f"Peer {peer_id} at {peer_host}:{peer_port} is not healthy. Skipping.") - continue - - if DEBUG >= 1: print(f"Adding {peer_id=} at {peer_host}:{peer_port}. Replace existing peer_id: {peer_id in self.known_peers}") - self.known_peers[peer_id] = ( - new_peer_handle, - current_time, - current_time, - ) - else: - if not await self.known_peers[peer_id][0].health_check(): - if DEBUG >= 1: print(f"Peer {peer_id} at {peer_host}:{peer_port} is not healthy. Removing.") - if peer_id in self.known_peers: del self.known_peers[peer_id] - continue - self.known_peers[peer_id] = (self.known_peers[peer_id][0], self.known_peers[peer_id][1], current_time) - - except Exception as e: - print(f"Error in discover peers: {e}") - print(traceback.format_exc()) - finally: - await asyncio.sleep(self.discovery_interval) - - async def stop(self): - if self.discovery_task: - self.discovery_task.cancel() - if self.cleanup_task: - self.cleanup_task.cancel() - if self.update_task: - self.update_task.cancel() - if self.discovery_task or self.cleanup_task or self.update_task: - await asyncio.gather(self.discovery_task, self.cleanup_task, self.update_task, return_exceptions=True) - - async def discover_peers(self, wait_for_peers: int = 0) -> List[PeerHandle]: - if wait_for_peers > 0: - while len(self.known_peers) < wait_for_peers: - if DEBUG_DISCOVERY >= 2: - print(f"Current peers: {len(self.known_peers)}/{wait_for_peers}. Waiting for more peers...") - await asyncio.sleep(0.1) - return [peer_handle for peer_handle, _, _ in self.known_peers.values()] - - async def task_cleanup_peers(self): - while True: - try: - current_time = time.time() - peers_to_remove = [] - - peer_ids = list(self.known_peers.keys()) - results = await asyncio.gather(*[self.check_peer(peer_id, current_time) for peer_id in peer_ids], return_exceptions=True) - - for peer_id, should_remove in zip(peer_ids, results): - if should_remove: peers_to_remove.append(peer_id) - - if DEBUG_DISCOVERY >= 2: - print( - "Peer statuses:", { - peer_handle.id(): f"is_connected={await peer_handle.is_connected()}, health_check={await peer_handle.health_check()}, connected_at={connected_at}, last_seen={last_seen}" - for peer_handle, connected_at, last_seen in self.known_peers.values() - } - ) - - for peer_id in peers_to_remove: - if peer_id in self.known_peers: - del self.known_peers[peer_id] - if DEBUG_DISCOVERY >= 2: print(f"Removed peer {peer_id} due to inactivity or failed health check.") - except Exception as e: - print(f"Error in cleanup peers: {e}") - print(traceback.format_exc()) - finally: - await asyncio.sleep(self.discovery_interval) - - async def check_peer(self, peer_id: str, current_time: float) -> bool: - peer_handle, connected_at, last_seen = self.known_peers.get(peer_id, (None, None, None)) - if peer_handle is None: return False - - try: - is_connected = await peer_handle.is_connected() - health_ok = await peer_handle.health_check() - except Exception as e: - if DEBUG_DISCOVERY >= 2: print(f"Error checking peer {peer_id}: {e}") - return True - - should_remove = ((not is_connected and current_time - connected_at > self.discovery_timeout) or (current_time - last_seen > self.discovery_timeout) or (not health_ok)) - return should_remove diff --git a/exo/networking/tailscale/tailscale_helpers.py b/exo/networking/tailscale/tailscale_helpers.py deleted file mode 100644 index 969a72da..00000000 --- a/exo/networking/tailscale/tailscale_helpers.py +++ /dev/null @@ -1,125 +0,0 @@ -import json -import asyncio -import aiohttp -import re -from typing import Dict, Any, Tuple, List, Optional -from exo.helpers import DEBUG_DISCOVERY -from exo.topology.device_capabilities import DeviceCapabilities, DeviceFlops -from datetime import datetime, timezone - - -class Device: - def __init__(self, device_id: str, name: str, addresses: List[str], last_seen: Optional[datetime] = None): - self.device_id = device_id - self.name = name - self.addresses = addresses - self.last_seen = last_seen - - @classmethod - def from_dict(cls, data: Dict[str, Any]) -> 'Device': - return cls(device_id=data.get('id', ''), name=data.get('name', ''), addresses=data.get('addresses', []), last_seen=cls.parse_datetime(data.get('lastSeen'))) - - @staticmethod - def parse_datetime(date_string: Optional[str]) -> Optional[datetime]: - if not date_string: - return None - return datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc) - - -async def get_device_id() -> str: - try: - process = await asyncio.create_subprocess_exec('tailscale', 'status', '--json', stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) - stdout, stderr = await process.communicate() - if process.returncode != 0: - raise Exception(f"Command failed with exit code {process.returncode}: {stderr.decode().strip()}.") - if DEBUG_DISCOVERY >= 4: print(f"tailscale status: {stdout.decode()}") - data = json.loads(stdout.decode()) - return data['Self']['ID'] - except Exception as e: - raise Exception(f"{str(e)} Do you have the tailscale cli installed? See: https://tailscale.com/kb/1080/cli") - - -async def update_device_attributes(device_id: str, api_key: str, node_id: str, node_port: int, device_capabilities: DeviceCapabilities): - async with aiohttp.ClientSession() as session: - base_url = f"https://api.tailscale.com/api/v2/device/{device_id}/attributes" - headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'} - - attributes = { - "custom:exo_node_id": node_id.replace('-', '_'), "custom:exo_node_port": node_port, "custom:exo_device_capability_chip": sanitize_attribute(device_capabilities.chip), - "custom:exo_device_capability_model": sanitize_attribute(device_capabilities.model), "custom:exo_device_capability_memory": str(device_capabilities.memory), - "custom:exo_device_capability_flops_fp16": str(device_capabilities.flops.fp16), "custom:exo_device_capability_flops_fp32": str(device_capabilities.flops.fp32), - "custom:exo_device_capability_flops_int8": str(device_capabilities.flops.int8) - } - - for attr_name, attr_value in attributes.items(): - url = f"{base_url}/{attr_name}" - data = {"value": str(attr_value).replace(' ', '_')} # Ensure all values are strings for JSON - async with session.post(url, headers=headers, json=data) as response: - if response.status == 200: - if DEBUG_DISCOVERY >= 1: print(f"Updated device posture attribute {attr_name} for device {device_id}") - else: - print(f"Failed to update device posture attribute {attr_name}: {response.status} {await response.text()}") - - -async def get_device_attributes(device_id: str, api_key: str) -> Tuple[str, int, DeviceCapabilities]: - async with aiohttp.ClientSession() as session: - url = f"https://api.tailscale.com/api/v2/device/{device_id}/attributes" - headers = {'Authorization': f'Bearer {api_key}'} - async with session.get(url, headers=headers) as response: - if response.status == 200: - data = await response.json() - attributes = data.get("attributes", {}) - node_id = attributes.get("custom:exo_node_id", "").replace('_', '-') - node_port = int(attributes.get("custom:exo_node_port", 0)) - device_capabilities = DeviceCapabilities( - model=attributes.get("custom:exo_device_capability_model", "").replace('_', ' '), - chip=attributes.get("custom:exo_device_capability_chip", "").replace('_', ' '), - memory=int(attributes.get("custom:exo_device_capability_memory", 0)), - flops=DeviceFlops( - fp16=float(attributes.get("custom:exo_device_capability_flops_fp16", 0)), - fp32=float(attributes.get("custom:exo_device_capability_flops_fp32", 0)), - int8=float(attributes.get("custom:exo_device_capability_flops_int8", 0)) - ) - ) - return node_id, node_port, device_capabilities - else: - print(f"Failed to fetch posture attributes for {device_id}: {response.status}") - return "", 0, DeviceCapabilities(model="", chip="", memory=0, flops=DeviceFlops(fp16=0, fp32=0, int8=0)) - - -def parse_device_attributes(data: Dict[str, str]) -> Dict[str, Any]: - result = {} - prefix = "custom:exo_" - for key, value in data.items(): - if key.startswith(prefix): - attr_name = key.replace(prefix, "") - if attr_name in ["node_id", "node_port", "device_capability_chip", "device_capability_model"]: - result[attr_name] = value.replace('_', ' ') - elif attr_name in ["device_capability_memory", "device_capability_flops_fp16", "device_capability_flops_fp32", "device_capability_flops_int8"]: - result[attr_name] = float(value) - return result - - -def sanitize_attribute(value: str) -> str: - # Replace invalid characters with underscores - sanitized_value = re.sub(r'[^a-zA-Z0-9_.]', '_', value) - # Truncate to 50 characters - return sanitized_value[:50] - - -async def get_tailscale_devices(api_key: str, tailnet: str) -> Dict[str, Device]: - async with aiohttp.ClientSession() as session: - url = f"https://api.tailscale.com/api/v2/tailnet/{tailnet}/devices" - headers = {"Authorization": f"Bearer {api_key}"} - - async with session.get(url, headers=headers) as response: - response.raise_for_status() - data = await response.json() - - devices = {} - for device_data in data.get("devices", []): - print("Device data: ", device_data) - device = Device.from_dict(device_data) - devices[device.name] = device - - return devices diff --git a/exo/networking/tailscale/test_tailscale_discovery.py b/exo/networking/tailscale/test_tailscale_discovery.py deleted file mode 100644 index 63cc9d04..00000000 --- a/exo/networking/tailscale/test_tailscale_discovery.py +++ /dev/null @@ -1,43 +0,0 @@ -import os -import asyncio -import unittest -from unittest import mock -from exo.networking.tailscale.tailscale_discovery import TailscaleDiscovery -from exo.networking.peer_handle import PeerHandle - - -class TestTailscaleDiscovery(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - self.tailscale_api_key = os.environ.get("TAILSCALE_API_KEY", "") - self.tailnet = os.environ.get("TAILSCALE_TAILNET", "") - self.discovery = TailscaleDiscovery( - node_id="test_node", - node_port=50051, - create_peer_handle=lambda peer_id, address, description, device_capabilities: unittest.mock.Mock(spec=PeerHandle, id=lambda: peer_id), - tailscale_api_key=self.tailscale_api_key, - tailnet=self.tailnet - ) - await self.discovery.start() - - async def asyncTearDown(self): - await self.discovery.stop() - - async def test_discovery(self): - # Wait for a short period to allow discovery to happen - await asyncio.sleep(15) - - # Get discovered peers - peers = await self.discovery.discover_peers() - - # Check if any peers were discovered - self.assertGreater(len(peers), 0, "No peers were discovered") - - # Print discovered peers for debugging - print(f"Discovered peers: {[peer.id() for peer in peers]}") - - # Check if discovered peers are instances of GRPCPeerHandle - print(peers) - - -if __name__ == '__main__': - unittest.main() diff --git a/exo/networking/udp/__init__.py b/exo/networking/udp/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/exo/networking/udp/test_udp_discovery.py b/exo/networking/udp/test_udp_discovery.py deleted file mode 100644 index ff1dc5c2..00000000 --- a/exo/networking/udp/test_udp_discovery.py +++ /dev/null @@ -1,77 +0,0 @@ -import asyncio -import unittest -from unittest import mock -from exo.networking.udp.udp_discovery import UDPDiscovery -from exo.networking.grpc.grpc_peer_handle import GRPCPeerHandle -from exo.networking.grpc.grpc_server import GRPCServer -from exo.orchestration.node import Node - - -class TestUDPDiscovery(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - self.peer1 = mock.AsyncMock() - self.peer2 = mock.AsyncMock() - self.peer1.connect = mock.AsyncMock() - self.peer2.connect = mock.AsyncMock() - self.discovery1 = UDPDiscovery("discovery1", 50051, 5678, 5679, create_peer_handle=lambda peer_id, address, description, device_capabilities: self.peer1) - self.discovery2 = UDPDiscovery("discovery2", 50052, 5679, 5678, create_peer_handle=lambda peer_id, address, description, device_capabilities: self.peer2) - await self.discovery1.start() - await self.discovery2.start() - - async def asyncTearDown(self): - await self.discovery1.stop() - await self.discovery2.stop() - - async def test_discovery(self): - peers1 = await self.discovery1.discover_peers(wait_for_peers=1) - assert len(peers1) == 1 - peers2 = await self.discovery2.discover_peers(wait_for_peers=1) - assert len(peers2) == 1 - - # connect has to be explicitly called after discovery - self.peer1.connect.assert_not_called() - self.peer2.connect.assert_not_called() - - -class TestUDPDiscoveryWithGRPCPeerHandle(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - self.node1 = mock.AsyncMock(spec=Node) - self.node2 = mock.AsyncMock(spec=Node) - self.server1 = GRPCServer(self.node1, "localhost", 50053) - self.server2 = GRPCServer(self.node2, "localhost", 50054) - await self.server1.start() - await self.server2.start() - self.discovery1 = UDPDiscovery("discovery1", 50053, 5678, 5679, lambda peer_id, address, description, device_capabilities: GRPCPeerHandle(peer_id, address, description, device_capabilities)) - self.discovery2 = UDPDiscovery("discovery2", 50054, 5679, 5678, lambda peer_id, address, description, device_capabilities: GRPCPeerHandle(peer_id, address, description, device_capabilities)) - await self.discovery1.start() - await self.discovery2.start() - - async def asyncTearDown(self): - await self.discovery1.stop() - await self.discovery2.stop() - await self.server1.stop() - await self.server2.stop() - - async def test_grpc_discovery(self): - peers1 = await self.discovery1.discover_peers(wait_for_peers=1) - assert len(peers1) == 1 - peers2 = await self.discovery2.discover_peers(wait_for_peers=1) - assert len(peers2) == 1 - assert not await peers1[0].is_connected() - assert not await peers2[0].is_connected() - - # Connect - await peers1[0].connect() - await peers2[0].connect() - assert await peers1[0].is_connected() - assert await peers2[0].is_connected() - - # Kill server1 - await self.server1.stop() - - assert await peers1[0].is_connected() - assert not await peers2[0].is_connected() - - -if __name__ == "__main__": - asyncio.run(unittest.main()) diff --git a/exo/networking/udp/udp_discovery.py b/exo/networking/udp/udp_discovery.py deleted file mode 100644 index 7117c7cd..00000000 --- a/exo/networking/udp/udp_discovery.py +++ /dev/null @@ -1,246 +0,0 @@ -import asyncio -import json -import socket -import time -import traceback -from typing import List, Dict, Callable, Tuple, Coroutine, Optional -from exo.networking.discovery import Discovery -from exo.networking.peer_handle import PeerHandle -from exo.topology.device_capabilities import DeviceCapabilities, device_capabilities, UNKNOWN_DEVICE_CAPABILITIES -from exo.helpers import DEBUG, DEBUG_DISCOVERY, get_all_ip_addresses_and_interfaces, get_interface_priority_and_type - - -class ListenProtocol(asyncio.DatagramProtocol): - def __init__(self, on_message: Callable[[bytes, Tuple[str, int]], Coroutine]): - super().__init__() - self.on_message = on_message - self.loop = asyncio.get_event_loop() - - def connection_made(self, transport): - self.transport = transport - - def datagram_received(self, data, addr): - asyncio.create_task(self.on_message(data, addr)) - - -def get_broadcast_address(ip_addr: str) -> str: - try: - # Split IP into octets and create broadcast address for the subnet - ip_parts = ip_addr.split('.') - return f"{ip_parts[0]}.{ip_parts[1]}.{ip_parts[2]}.255" - except: - return "255.255.255.255" - - -class BroadcastProtocol(asyncio.DatagramProtocol): - def __init__(self, message: str, broadcast_port: int, source_ip: str): - self.message = message - self.broadcast_port = broadcast_port - self.source_ip = source_ip - - def connection_made(self, transport): - sock = transport.get_extra_info("socket") - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - # Try both subnet-specific and global broadcast - broadcast_addr = get_broadcast_address(self.source_ip) - transport.sendto(self.message.encode("utf-8"), (broadcast_addr, self.broadcast_port)) - if broadcast_addr != "255.255.255.255": - transport.sendto(self.message.encode("utf-8"), ("255.255.255.255", self.broadcast_port)) - - -class UDPDiscovery(Discovery): - def __init__( - self, - node_id: str, - node_port: int, - listen_port: int, - broadcast_port: int, - create_peer_handle: Callable[[str, str, str, DeviceCapabilities], PeerHandle], - broadcast_interval: int = 2.5, - discovery_timeout: int = 30, - device_capabilities: DeviceCapabilities = UNKNOWN_DEVICE_CAPABILITIES, - allowed_node_ids: Optional[List[str]] = None, - allowed_interface_types: Optional[List[str]] = None, - ): - self.node_id = node_id - self.node_port = node_port - self.listen_port = listen_port - self.broadcast_port = broadcast_port - self.create_peer_handle = create_peer_handle - self.broadcast_interval = broadcast_interval - self.discovery_timeout = discovery_timeout - self.device_capabilities = device_capabilities - self.allowed_node_ids = allowed_node_ids - self.allowed_interface_types = allowed_interface_types - self.known_peers: Dict[str, Tuple[PeerHandle, float, float, int]] = {} - self.broadcast_task = None - self.listen_task = None - self.cleanup_task = None - - async def start(self): - self.device_capabilities = await device_capabilities() - self.broadcast_task = asyncio.create_task(self.task_broadcast_presence()) - self.listen_task = asyncio.create_task(self.task_listen_for_peers()) - self.cleanup_task = asyncio.create_task(self.task_cleanup_peers()) - - async def stop(self): - if self.broadcast_task: self.broadcast_task.cancel() - if self.listen_task: self.listen_task.cancel() - if self.cleanup_task: self.cleanup_task.cancel() - if self.broadcast_task or self.listen_task or self.cleanup_task: - await asyncio.gather(self.broadcast_task, self.listen_task, self.cleanup_task, return_exceptions=True) - - async def discover_peers(self, wait_for_peers: int = 0) -> List[PeerHandle]: - if wait_for_peers > 0: - while len(self.known_peers) < wait_for_peers: - if DEBUG_DISCOVERY >= 2: print(f"Current peers: {len(self.known_peers)}/{wait_for_peers}. Waiting for more peers...") - await asyncio.sleep(0.1) - return [peer_handle for peer_handle, _, _, _ in self.known_peers.values()] - - async def task_broadcast_presence(self): - while True: - for addr, interface_name in get_all_ip_addresses_and_interfaces(): - interface_priority, interface_type = await get_interface_priority_and_type(interface_name) - message = json.dumps({ - "type": "discovery", - "node_id": self.node_id, - "grpc_port": self.node_port, - "device_capabilities": self.device_capabilities.to_dict(), - "priority": interface_priority, - "interface_name": interface_name, - "interface_type": interface_type, - }) - - transport = None - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - try: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - except AttributeError: - pass - sock.bind((addr, 0)) - - transport, _ = await asyncio.get_event_loop().create_datagram_endpoint( - lambda: BroadcastProtocol(message, self.broadcast_port, addr), - sock=sock - ) - except Exception as e: - print(f"Error in broadcast presence ({addr} - {interface_name} - {interface_priority}): {e}") - finally: - if transport: - try: transport.close() - except Exception as e: - if DEBUG_DISCOVERY >= 2: print(f"Error closing transport: {e}") - - await asyncio.sleep(self.broadcast_interval) - - async def on_listen_message(self, data, addr): - if not data: - return - - decoded_data = data.decode("utf-8", errors="ignore") - - # Check if the decoded data starts with a valid JSON character - if not (decoded_data.strip() and decoded_data.strip()[0] in "{["): - if DEBUG_DISCOVERY >= 2: print(f"Received invalid JSON data from {addr}: {decoded_data[:100]}") - return - - try: - decoder = json.JSONDecoder(strict=False) - message = decoder.decode(decoded_data) - except json.JSONDecodeError as e: - if DEBUG_DISCOVERY >= 2: print(f"Error decoding JSON data from {addr}: {e}") - return - - if DEBUG_DISCOVERY >= 2: print(f"received from peer {addr}: {message}") - - if message["type"] == "discovery" and message["node_id"] != self.node_id: - peer_id = message["node_id"] - - # Skip if peer_id is not in allowed list - if self.allowed_node_ids and peer_id not in self.allowed_node_ids: - if DEBUG_DISCOVERY >= 2: print(f"Ignoring peer {peer_id} as it's not in the allowed node IDs list") - return - - peer_host = addr[0] - peer_port = message["grpc_port"] - peer_prio = message["priority"] - peer_interface_name = message["interface_name"] - peer_interface_type = message["interface_type"] - - # Skip if interface type is not in allowed list - if self.allowed_interface_types and peer_interface_type not in self.allowed_interface_types: - if DEBUG_DISCOVERY >= 2: print(f"Ignoring peer {peer_id} as its interface type {peer_interface_type} is not in the allowed interface types list") - return - - device_capabilities = DeviceCapabilities(**message["device_capabilities"]) - - if peer_id not in self.known_peers or self.known_peers[peer_id][0].addr() != f"{peer_host}:{peer_port}": - if peer_id in self.known_peers: - existing_peer_prio = self.known_peers[peer_id][3] - if existing_peer_prio >= peer_prio: - if DEBUG >= 1: - print(f"Ignoring peer {peer_id} at {peer_host}:{peer_port} with priority {peer_prio} because we already know about a peer with higher or equal priority: {existing_peer_prio}") - return - new_peer_handle = self.create_peer_handle(peer_id, f"{peer_host}:{peer_port}", f"{peer_interface_type} ({peer_interface_name})", device_capabilities) - if not await new_peer_handle.health_check(): - if DEBUG >= 1: print(f"Peer {peer_id} at {peer_host}:{peer_port} is not healthy. Skipping.") - return - if DEBUG >= 1: print(f"Adding {peer_id=} at {peer_host}:{peer_port}. Replace existing peer_id: {peer_id in self.known_peers}") - self.known_peers[peer_id] = (new_peer_handle, time.time(), time.time(), peer_prio) - else: - if not await self.known_peers[peer_id][0].health_check(): - if DEBUG >= 1: print(f"Peer {peer_id} at {peer_host}:{peer_port} is not healthy. Removing.") - if peer_id in self.known_peers: del self.known_peers[peer_id] - return - if peer_id in self.known_peers: self.known_peers[peer_id] = (self.known_peers[peer_id][0], self.known_peers[peer_id][1], time.time(), peer_prio) - - async def task_listen_for_peers(self): - await asyncio.get_event_loop().create_datagram_endpoint(lambda: ListenProtocol(self.on_listen_message), local_addr=("0.0.0.0", self.listen_port)) - if DEBUG_DISCOVERY >= 2: print("Started listen task") - - async def task_cleanup_peers(self): - while True: - try: - current_time = time.time() - peers_to_remove = [] - - peer_ids = list(self.known_peers.keys()) - results = await asyncio.gather(*[self.check_peer(peer_id, current_time) for peer_id in peer_ids], return_exceptions=True) - - for peer_id, should_remove in zip(peer_ids, results): - if should_remove: peers_to_remove.append(peer_id) - - if DEBUG_DISCOVERY >= 2: - print( - "Peer statuses:", { - peer_handle.id(): f"is_connected={await peer_handle.is_connected()}, health_check={await peer_handle.health_check()}, connected_at={connected_at}, last_seen={last_seen}, prio={prio}" - for peer_handle, connected_at, last_seen, prio in self.known_peers.values() - } - ) - - for peer_id in peers_to_remove: - if peer_id in self.known_peers: - del self.known_peers[peer_id] - if DEBUG_DISCOVERY >= 2: print(f"Removed peer {peer_id} due to inactivity or failed health check.") - except Exception as e: - print(f"Error in cleanup peers: {e}") - print(traceback.format_exc()) - finally: - await asyncio.sleep(self.broadcast_interval) - - async def check_peer(self, peer_id: str, current_time: float) -> bool: - peer_handle, connected_at, last_seen, prio = self.known_peers.get(peer_id, (None, None, None, None)) - if peer_handle is None: return False - - try: - is_connected = await peer_handle.is_connected() - health_ok = await peer_handle.health_check() - except Exception as e: - if DEBUG_DISCOVERY >= 2: print(f"Error checking peer {peer_id}: {e}") - return True - - should_remove = ((not is_connected and current_time - connected_at > self.discovery_timeout) or (current_time - last_seen > self.discovery_timeout) or (not health_ok)) - return should_remove diff --git a/exo/orchestration/__init__.py b/exo/orchestration/__init__.py deleted file mode 100644 index 022a9160..00000000 --- a/exo/orchestration/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .node import Node - -__all__ = ["Node"] diff --git a/exo/orchestration/node.py b/exo/orchestration/node.py deleted file mode 100644 index 1281aa8a..00000000 --- a/exo/orchestration/node.py +++ /dev/null @@ -1,628 +0,0 @@ -import numpy as np -import json -import asyncio -import uuid -import time -import traceback -from typing import List, Dict, Optional, Tuple, Union, Set -from exo.networking import Discovery, PeerHandle, Server -from exo.inference.inference_engine import InferenceEngine, Shard -from exo.topology.topology import Topology -from exo.topology.device_capabilities import device_capabilities, UNKNOWN_DEVICE_CAPABILITIES -from exo.topology.partitioning_strategy import Partition, PartitioningStrategy, map_partitions_to_shards -from exo import DEBUG -from exo.helpers import AsyncCallbackSystem -from exo.viz.topology_viz import TopologyViz -from exo.download.download_progress import RepoProgressEvent -from exo.inference.inference_engine import get_inference_engine, InferenceEngine -from exo.download.shard_download import ShardDownloader - -class Node: - def __init__( - self, - _id: str, - server: Server, - inference_engine: InferenceEngine, - discovery: Discovery, - shard_downloader: ShardDownloader, - partitioning_strategy: PartitioningStrategy = None, - max_generate_tokens: int = 1024, - default_sample_temperature: float = 0.0, - topology_viz: Optional[TopologyViz] = None, - ): - self.id = _id - self.inference_engine = inference_engine - self.server = server - self.discovery = discovery - self.shard_downloader = shard_downloader - self.partitioning_strategy = partitioning_strategy - self.peers: List[PeerHandle] = {} - self.topology: Topology = Topology() - self.device_capabilities = UNKNOWN_DEVICE_CAPABILITIES - self.buffered_token_output: Dict[str, Tuple[List[int], bool]] = {} - self.buffered_logits: Dict[str, List[np.ndarray]] = {} - self.buffered_inputs: Dict[str, List[np.ndarray]] = {} - self.buffered_partials: Dict[str, List[np.ndarray]] = {} - self.checkpoints: Dict[str, Dict[str, int]] = {} - - self.max_generate_tokens = max_generate_tokens - self.topology_viz = topology_viz - self.default_sample_temperature = default_sample_temperature - self._on_token = AsyncCallbackSystem[str, Tuple[str, List[int], bool]]() - self._on_opaque_status = AsyncCallbackSystem[str, Tuple[str, str]]() - self._on_opaque_status.register("node_status").on_next(self.on_node_status) - self.node_download_progress: Dict[str, RepoProgressEvent] = {} - self.topology_inference_engines_pool: List[List[str]] = [] - self.outstanding_requests = {} - - async def start(self, wait_for_peers: int = 0) -> None: - self.device_capabilities = await device_capabilities() - await self.server.start() - await self.discovery.start() - await self.update_peers(wait_for_peers) - await self.collect_topology(set()) - if DEBUG >= 2: print(f"Collected topology: {self.topology}") - asyncio.create_task(self.periodic_topology_collection(2.0)) - - async def stop(self) -> None: - await self.discovery.stop() - await self.server.stop() - - def on_node_status(self, request_id, opaque_status): - try: - status_data = json.loads(opaque_status) - status_type = status_data.get("type", "") - if status_type == "supported_inference_engines": - node_id = status_data.get("node_id") - engines = status_data.get("engines", []) - self.topology_inference_engines_pool.append(engines) - elif status_type == "node_status": - if status_data.get("status", "").startswith("start_"): - self.current_topology.active_node_id = status_data.get("node_id") - elif status_data.get("status", "").startswith("end_"): - if status_data.get("node_id") == self.current_topology.active_node_id: - self.current_topology.active_node_id = None - - download_progress = None - if status_type == "download_progress": - if DEBUG >= 8: print(f"Download progress from {status_data.get('node_id')}: {status_data.get('progress')}") - download_progress = RepoProgressEvent.from_dict(status_data.get('progress')) - self.node_download_progress[status_data.get('node_id')] = download_progress - - if self.topology_viz: - self.topology_viz.update_visualization(self.topology, self.partitioning_strategy.partition(self.topology), self.id, self.node_download_progress) - except Exception as e: - if DEBUG >= 1: print(f"Error on_node_status: {e}") - if DEBUG >= 1: traceback.print_exc() - - def get_supported_inference_engines(self): - supported_engine_names = [] - if self.inference_engine.__class__.__name__ == 'MLXDynamicShardInferenceEngine': - supported_engine_names.append('mlx') - supported_engine_names.append('tinygrad') - else: - supported_engine_names.append('tinygrad') - return supported_engine_names - - async def broadcast_supported_engines(self, supported_engines_names: List[str]): - status_message = json.dumps({"type": "supported_inference_engines", "node_id": self.id, "engines": supported_engines_names}) - await self.broadcast_opaque_status("", status_message) - - def get_topology_inference_engines(self) -> List[List[str]]: - return self.topology_inference_engines_pool - - token_count = 0 - first_token_time = 0 - async def process_inference_result( - self, - shard, - result: np.ndarray, - request_id: Optional[str] = None, - inference_state: Optional[dict] = None, - ): - if shard.model_id != 'stable-diffusion-2-1-base': - if request_id not in self.buffered_token_output: - self.buffered_token_output[request_id] = ([], False) - is_finished = len(self.buffered_token_output[request_id][0]) >= self.max_generate_tokens - if shard.is_last_layer() and not is_finished: - token = await self.inference_engine.sample(result, temp=self.default_sample_temperature) - await self.inference_engine.ensure_shard(shard) - self.buffered_token_output[request_id][0].append(token.item()) - is_finished = token.item() == self.inference_engine.tokenizer.eos_token_id or is_finished or len(self.buffered_token_output[request_id][0]) >= self.max_generate_tokens - if DEBUG >= 2: print(f"[{request_id}] result size: {result.size}, is finished: {is_finished}, buffered tokens: {len(self.buffered_token_output[request_id][0])}") - forward = token.reshape(1, -1) - intermediate_result = [self.buffered_token_output[request_id][0][-1]] - else: - forward = result - else: - await self.inference_engine.ensure_shard(shard) - is_finished = inference_state.get("is_finished", False) - intermediate_result, inference_state = self.handle_stable_diffusion(inference_state, result) - forward = result - if shard.is_last_layer(): - self.trigger_on_token_callbacks(request_id, intermediate_result, is_finished) - asyncio.create_task(self.broadcast_result(request_id, intermediate_result, is_finished)) - - if is_finished: - if shard.model_id != 'stable-diffusion-2-1-base': - self.buffered_token_output[request_id] = (self.buffered_token_output[request_id][0], True) - self.outstanding_requests.pop(request_id) - else: - self.outstanding_requests[request_id] = "waiting" - asyncio.create_task(self.forward_tensor(shard, forward, request_id, self.get_partition_index(offset = 1), inference_state)) - - return np.array(self.buffered_token_output[request_id][0]) if shard.model_id != 'stable-diffusion-2-1-base' else intermediate_result - - - async def process_prompt( - self, - base_shard: Shard, - prompt: str, - request_id: Optional[str] = None, - inference_state: Optional[dict] = {}, - ) -> Optional[np.ndarray]: - shard = self.get_current_shard(base_shard) - start_time = time.perf_counter_ns() - asyncio.create_task( - self.broadcast_opaque_status( - request_id, - json.dumps({ - "type": "node_status", - "node_id": self.id, - "status": "start_process_prompt", - "base_shard": base_shard.to_dict(), - "shard": shard.to_dict(), - "prompt": prompt, - "request_id": request_id, - }), - ) - ) - start_time = time.perf_counter_ns() - resp = await self._process_prompt(base_shard, prompt, request_id, inference_state) - end_time = time.perf_counter_ns() - elapsed_time_ns = end_time - start_time - asyncio.create_task( - self.broadcast_opaque_status( - request_id, - json.dumps({ - "type": "node_status", - "node_id": self.id, - "status": "end_process_prompt", - "base_shard": base_shard.to_dict(), - "shard": shard.to_dict(), - "prompt": prompt, - "request_id": request_id, - "elapsed_time_ns": elapsed_time_ns, - }), - ) - ) - if DEBUG >= 2: print(f"[{request_id}] process prompt: {base_shard=} {shard=} {prompt=} {elapsed_time_ns=}") - - async def _process_prompt(self, base_shard: Shard, prompt: str, request_id: Optional[str] = None, inference_state: Optional[dict] = None) -> Optional[np.ndarray]: - if request_id is None: - request_id = str(uuid.uuid4()) - shard = self.get_current_shard(base_shard) - if DEBUG >= 2: print(f"[{request_id}] process prompt: {base_shard=} {shard=} {prompt=}") - - if not shard.is_first_layer(): - if DEBUG >= 2: print(f"[{request_id}] forwarding to next shard: {base_shard=} {shard=} {prompt=}") - self.outstanding_requests[request_id] = "waiting" - resp = await self.forward_prompt(shard, prompt, request_id, 0, inference_state) - return None - else: - self.outstanding_requests[request_id] = "processing" - result, inference_state = await self.inference_engine.infer_prompt(request_id, shard, prompt, inference_state) - ret = await self.process_inference_result(shard, result, request_id, inference_state) - return result - - async def enqueue_example( - self, - base_shard: Shard, - example: np.ndarray, - target: np.ndarray, - length: np.ndarray, - request_id: Optional[str] = None, - train: bool = False, - ): - shard = self.get_current_shard(base_shard) - if shard.is_first_layer(): - loss = await self.process_example(shard, example, target, length, train, request_id) - return loss - else: - if request_id is None: - request_id = str(uuid.uuid4()) - self.outstanding_requests[request_id] = "waiting" - loss = await self.forward_example(shard, example, target, length, train, request_id, 0) - return loss - - async def coordinate_save( - self, - base_shard: Shard, - iteration: int, - destination: str, - ): - shard = self.get_current_shard(base_shard) - model = shard.model_id - sid = shard.__hash__() - path = f"{destination}/{model}/{sid}-{iteration}.safetensors" - self.outstanding_requests[f"{sid}::{iteration}"] = "Checking" - if model not in self.checkpoints: - self.checkpoints[model] = {} - if sid not in self.checkpoints[model]: - self.checkpoints[model][sid] = [] - if len(self.checkpoints[model][sid]) < 1 or self.checkpoints[model][sid][-1] < iteration: - print(f"Saving checkpoint to {path}") - self.outstanding_requests[f"{sid}::{iteration}"] = "Saving" - import os - os.makedirs("/".join(path.split("/")[:-1]), exist_ok=True) - await self.inference_engine.save_checkpoint(shard, path) - self.checkpoints[model][sid] = sorted(self.checkpoints[model][sid] + [iteration]) - self.outstanding_requests.pop(f"{sid}::{iteration}") - - async def process_example( - self, - base_shard: Shard, - example: np.ndarray, - target: np.ndarray, - length: np.ndarray, - train: bool = False, - request_id: Optional[str] = None, - ): - shard = self.get_current_shard(base_shard) - asyncio.create_task( - self.broadcast_opaque_status( - request_id, - json.dumps({ - "type": "node_status", - "node_id": self.id, - "status": f"start_{'train' if train else 'eval'}_example", - "base_shard": base_shard.to_dict(), - "shard": shard.to_dict(), - "example_size": example.size, - "example_shape": example.shape, - "request_id": request_id, - }), - ) - ) - start_time = time.perf_counter_ns() - resp = await self._process_example(shard, example, target, length, train, request_id) - end_time = time.perf_counter_ns() - elapsed_time_ns = end_time - start_time - asyncio.create_task( - self.broadcast_opaque_status( - request_id, - json.dumps({ - "type": "node_status", - "node_id": self.id, - "status": f"end_{'train' if train else 'eval'}_example", - "base_shard": base_shard.to_dict(), - "shard": shard.to_dict(), - "request_id": request_id, - "elapsed_time_ns": elapsed_time_ns, - }), - ) - ) - return resp - - async def _process_example( - self, - base_shard: Shard, - example: np.ndarray, - target: np.ndarray, - length: np.ndarray, - train: bool = False, - request_id: Optional[str] = None, - ) -> Optional[np.ndarray]: - if request_id is None: - request_id = str(uuid.uuid4()) - shard = self.get_current_shard(base_shard) - if DEBUG >= 1: print(f"[{request_id}] process_example: {example.shape=}") - try: - target = target.astype(int) - if train: - if shard.is_last_layer(): - self.outstanding_requests[request_id] = "training" - loss, grad = await self.inference_engine.train(request_id, shard, example, target, length) - else: - self.outstanding_requests[request_id] = "preprocessing" - step, _ = await self.inference_engine.infer_tensor(request_id, shard, example) - self.outstanding_requests[request_id] = "waiting" - loss, backgrad = await self.forward_example(shard, step, target, length, train, request_id, self.get_partition_index(offset = 1)) - self.outstanding_requests[request_id] = "training" - partial_loss, grad = await self.inference_engine.train(request_id, shard, example, backgrad, length, loss="back_gradient") - self.outstanding_requests.pop(request_id) - if shard.is_first_layer(): - return loss - else: - return loss, grad - else: - if shard.is_last_layer(): - self.outstanding_requests[request_id] = "evaluating" - loss = await self.inference_engine.evaluate(request_id, shard, example, target, length) - else: - self.outstanding_requests[request_id] = "preprocessing" - step, _ = await self.inference_engine.infer_tensor(request_id, shard, example) - self.outstanding_requests[request_id] = "waiting" - loss = await self.forward_example(shard, step, target, length, train, request_id, self.get_partition_index(offset = 1)) - self.outstanding_requests.pop(request_id) - return loss - except Exception as e: - self.outstanding_requests.pop(request_id) - print(f"Error processing example for shard {shard}: {e}") - traceback.print_exc() - return None - - async def process_tensor( - self, - base_shard: Shard, - tensor: np.ndarray, - request_id: Optional[str] = None, - inference_state: Optional[dict] = None, - ) -> Optional[np.ndarray]: - shard = self.get_current_shard(base_shard) - start_time = time.perf_counter_ns() - resp = await self._process_tensor(shard, tensor, request_id, inference_state) - end_time = time.perf_counter_ns() - elapsed_time_ns = end_time - start_time - if DEBUG >= 2: print(f"[{request_id}] process_tensor: {base_shard=} {shard=} {tensor.size=} {tensor.shape=} {elapsed_time_ns=}") - - async def _process_tensor( - self, - base_shard: Shard, - tensor: np.ndarray, - request_id: Optional[str] = None, - inference_state: Optional[dict] = None, - ) -> Optional[np.ndarray]: - if request_id is None: - request_id = str(uuid.uuid4()) - shard = self.get_current_shard(base_shard) - - try: - self.outstanding_requests[request_id] = "processing" - result, inference_state = await self.inference_engine.infer_tensor(request_id, shard, tensor, inference_state) - ret = await self.process_inference_result(shard, result, request_id, inference_state) - return ret - except Exception as e: - self.outstanding_requests.pop(request_id) - print(f"Error processing tensor for shard {shard}: {e}") - traceback.print_exc() - - async def forward_example( - self, - base_shard: Shard, - step: np.ndarray, - target: np.ndarray, - length: np.ndarray, - train: bool, - request_id: str, - target_index: int, - ) -> None: - if DEBUG >= 1: print(f"target partition index: {target_index}") - target_id = self.partitioning_strategy.partition(self.topology)[target_index].node_id - target_shard = self.get_current_shard(base_shard, target_index) - if DEBUG >= 2: print(f"computed target from: {base_shard} {target_index}, {self.topology}. target shard: {target_shard}") - target_peer = next((p for p in self.peers if p.id() == target_id), None) - if not target_peer: - raise ValueError(f"peer for {target_index} not found") - if DEBUG >= 1: print(f"sending example to {target_peer.id()}: {step} => {target} ({length})") - resp = await target_peer.send_example(target_shard, step, target, length, request_id=request_id, train=train) - return resp - - async def forward_prompt( - self, - base_shard: Shard, - prompt: str, - request_id: str, - target_index: int, - inference_state: Optional[dict] = None, - ) -> None: - if DEBUG >= 1: print(f"target partition index: {target_index}") - target_id = self.partitioning_strategy.partition(self.topology)[target_index].node_id - next_shard = self.get_current_shard(base_shard, target_index) - if DEBUG >= 2: print(f"Computed target from: {base_shard} {target_index}, {self.topology}. next shard: {next_shard}") - if target_id == self.id: - await self.process_prompt(next_shard, prompt, request_id, inference_state) - else: - target_peer = next((p for p in self.peers if p.id() == target_id), None) - if not target_peer: - raise ValueError(f"Peer for {target_index} not found") - if DEBUG >= 1: print(f"Sending prompt to {target_peer.id()}: {prompt}") - await target_peer.send_prompt(next_shard, prompt, request_id=request_id, inference_state=inference_state) - - async def forward_tensor( - self, - base_shard: Shard, - tensor: np.ndarray, - request_id: str, - target_index: int, - inference_state: Optional[dict] = None, - ) -> None: - if DEBUG >= 1: print(f"target partition index: {target_index}") - target_id = self.partitioning_strategy.partition(self.topology)[target_index].node_id - next_shard = self.get_current_shard(base_shard, target_index) - if DEBUG >= 2: print(f"Computed target from: {base_shard} {target_index}, {self.topology}. target shard: {next_shard}") - if target_id == self.id: - await self.process_tensor(next_shard, tensor, request_id, inference_state) - else: - target_peer = next((p for p in self.peers if p.id() == target_id), None) - if not target_peer: - raise ValueError(f"Peer for {target_index} not found") - if DEBUG >= 1: print(f"Sending tensor to {target_peer.id()}: {tensor}") - await target_peer.send_tensor(next_shard, tensor, request_id=request_id, inference_state=inference_state) - - def get_partition_index(self, offset: int = 0): - if not self.partitioning_strategy: - if DEBUG >= 1: print("No partitioning strategy found. Skipping forward.") - return None - partitions = self.partitioning_strategy.partition(self.topology) - current_partition_index = next((i for i, p in enumerate(partitions) if p.node_id == self.id), None) - if current_partition_index is None: - raise ValueError(f"No current partition found for node: {self.id}") - return (current_partition_index + offset) % len(partitions) - - def get_current_shard(self, base_shard: Shard, index: Optional[int] = None) -> Shard: - if index is None: - index = self.get_partition_index() - partitions = self.partitioning_strategy.partition(self.topology) - shards = map_partitions_to_shards(partitions, base_shard.n_layers, base_shard.model_id) - return shards[index] - - async def update_peers(self, wait_for_peers: int = 0) -> bool: - next_peers = await self.discovery.discover_peers(wait_for_peers) - current_peer_ids = {peer.id() for peer in self.peers} - next_peer_ids = {peer.id() for peer in next_peers} - peers_added = [peer for peer in next_peers if peer.id() not in current_peer_ids] - peers_removed = [peer for peer in self.peers if peer.id() not in next_peer_ids] - peers_updated = [peer for peer in next_peers if peer.id() in current_peer_ids and any(p.addr() != peer.addr() for p in self.peers if p.id() == peer.id())] - peers_unchanged = [peer for peer in next_peers if peer.id() in current_peer_ids and all(p.addr() == peer.addr() for p in self.peers if p.id() == peer.id())] - peers_to_disconnect = [peer for peer in peers_removed if await peer.is_connected()] - peers_to_connect = [peer for peer in peers_added + peers_updated + peers_unchanged if not await peer.is_connected()] - - def _pretty(peers: List[PeerHandle]) -> List[str]: - return [f"{peer.id()}@{peer.addr()}" for peer in peers] - - if DEBUG >= 2: - print(f"update_peers: added={peers_added} removed={peers_removed} updated={peers_updated} unchanged={peers_unchanged} to_disconnect={peers_to_disconnect} to_connect={peers_to_connect}") - - async def disconnect_with_timeout(peer, timeout=5): - try: - await asyncio.wait_for(peer.disconnect(), timeout) - return True - except Exception as e: - print(f"Error disconnecting peer {peer.id()}@{peer.addr()}: {e}") - traceback.print_exc() - return False - - async def connect_with_timeout(peer, timeout=5): - try: - await asyncio.wait_for(peer.connect(), timeout) - return True - except Exception as e: - print(f"Error connecting peer {peer.id()}@{peer.addr()}: {e}") - traceback.print_exc() - return False - - disconnect_results = await asyncio.gather(*(disconnect_with_timeout(peer) for peer in peers_to_disconnect), return_exceptions=True) - connect_results = await asyncio.gather(*(connect_with_timeout(peer) for peer in peers_to_connect), return_exceptions=True) - - successful_disconnects = [peer for peer, result in zip(peers_to_disconnect, disconnect_results) if result is True] - failed_disconnects = [peer for peer, result in zip(peers_to_disconnect, disconnect_results) if result is False] - successful_connects = [peer for peer, result in zip(peers_to_connect, connect_results) if result is True] - failed_connects = [peer for peer, result in zip(peers_to_connect, connect_results) if result is False] - if DEBUG >= 1: - if successful_disconnects: print(f"Successfully disconnected peers: {_pretty(successful_disconnects)}") - if failed_disconnects: print(f"Failed to disconnect peers: {_pretty(failed_disconnects)}") - if successful_connects: print(f"Successfully connected peers: {_pretty(successful_connects)}") - if failed_connects: print(f"Failed to connect peers: {_pretty(failed_connects)}") - - self.peers = next_peers - return len(peers_added) > 0 or len(peers_removed) > 0 or len(peers_updated) > 0 - - async def select_best_inference_engine(self): - if self.inference_engine.__class__.__name__ == 'DummyInferenceEngine': return - supported_engines = self.get_supported_inference_engines() - await self.broadcast_supported_engines(supported_engines) - if len(self.get_topology_inference_engines()): - self.inference_engine = get_inference_engine(supported_engines[0], self.shard_downloader) - - async def periodic_topology_collection(self, interval: int): - while True: - await asyncio.sleep(interval) - try: - did_peers_change = await self.update_peers() - if DEBUG >= 2: print(f"{did_peers_change=}") - await self.collect_topology(set()) - if did_peers_change: - await self.select_best_inference_engine() - except Exception as e: - print(f"Error collecting topology: {e}") - traceback.print_exc() - - async def collect_topology(self, visited: set[str], max_depth: int = 4) -> Topology: - next_topology = Topology() - next_topology.update_node(self.id, self.device_capabilities) - - if DEBUG >= 2: print(f"Collecting topology {max_depth=} {visited=}") - - prev_visited = visited.copy() - visited.add(self.id) - visited.update(p.id() for p in self.peers) - - for peer in self.peers: - next_topology.update_node(peer.id(), peer.device_capabilities()) - next_topology.add_edge(self.id, peer.id(), peer.description()) - - if peer.id() in prev_visited: - continue - - if max_depth <= 0: - if DEBUG >= 2: print("Max depth reached. Skipping...") - continue - - try: - other_topology = await asyncio.wait_for(peer.collect_topology(visited, max_depth=max_depth - 1), timeout=5.0) - if DEBUG >= 2: print(f"Collected topology from: {peer.id()}: {other_topology}") - next_topology.merge(peer.id(), other_topology) - except Exception as e: - print(f"Error collecting topology from {peer.id()}: {e}") - traceback.print_exc() - - next_topology.active_node_id = self.topology.active_node_id - self.topology = next_topology - if self.topology_viz: - self.topology_viz.update_visualization(self.topology, self.partitioning_strategy.partition(self.topology), self.id) - return self.topology - - @property - def on_token(self) -> AsyncCallbackSystem[str, Tuple[str, List[int], bool]]: - return self._on_token - - @property - def on_opaque_status(self) -> AsyncCallbackSystem[str, Tuple[str, str]]: - return self._on_opaque_status - - def trigger_on_token_callbacks(self, request_id: str, tokens: List[int], is_finished: bool) -> None: - if DEBUG >= 2: print(f"Triggering all on_token callbacks with {request_id=} {tokens=} {is_finished=}") - self.on_token.trigger_all(request_id, tokens, is_finished) - - async def broadcast_result(self, request_id: str, result: List[int], is_finished: bool) -> None: - if DEBUG >= 2: print(f"Broadcasting result: {request_id=} {result=} {is_finished=}") - async def send_result_to_peer(peer): - try: - await asyncio.wait_for(peer.send_result(request_id, result, is_finished), timeout=15.0) - except asyncio.TimeoutError: - print(f"Timeout broadcasting result to {peer.id()}") - except Exception as e: - print(f"Error broadcasting result to {peer.id()}: {e}") - traceback.print_exc() - - await asyncio.gather(*[send_result_to_peer(peer) for peer in self.peers], return_exceptions=True) - - async def broadcast_opaque_status(self, request_id: str, status: str) -> None: - if DEBUG >= 8: print(f"Broadcasting opaque status: {request_id=} {status=}") - - async def send_status_to_peer(peer): - try: - await asyncio.wait_for(peer.send_opaque_status(request_id, status), timeout=15.0) - except asyncio.TimeoutError: - print(f"Timeout sending opaque status to {peer.id()}") - except Exception as e: - print(f"Error sending opaque status to {peer.id()}: {e}") - traceback.print_exc() - - await asyncio.gather(*[send_status_to_peer(peer) for peer in self.peers], return_exceptions=True) - # in the case of opaque status, we also want to receive our own opaque statuses - self.on_opaque_status.trigger_all(request_id, status) - - @property - def current_topology(self) -> Topology: - return self.topology - - def handle_stable_diffusion(self, inference_state, result): - if inference_state['is_step_finished']: - inference_state['step']+=1 - progress = [inference_state['step'],inference_state['total_steps']] - intermediate_result = result - if progress[0] == progress[1]: - intermediate_result = result - return intermediate_result, inference_state diff --git a/exo/orchestration/test_node.py b/exo/orchestration/test_node.py deleted file mode 100644 index 6f992bbe..00000000 --- a/exo/orchestration/test_node.py +++ /dev/null @@ -1,66 +0,0 @@ -import unittest -from unittest.mock import Mock, AsyncMock -import numpy as np -import pytest - -from .node import Node -from exo.networking.peer_handle import PeerHandle -from exo.download.shard_download import NoopShardDownloader - -class TestNode(unittest.IsolatedAsyncioTestCase): - def setUp(self): - self.mock_inference_engine = AsyncMock() - self.mock_server = AsyncMock() - self.mock_server.start = AsyncMock() - self.mock_server.stop = AsyncMock() - self.mock_discovery = AsyncMock() - self.mock_discovery.start = AsyncMock() - self.mock_discovery.stop = AsyncMock() - mock_peer1 = Mock(spec=PeerHandle) - mock_peer1.id.return_value = "peer1" - mock_peer2 = Mock(spec=PeerHandle) - mock_peer2.id.return_value = "peer2" - self.mock_discovery.discover_peers = AsyncMock(return_value=[mock_peer1, mock_peer2]) - - self.node = Node("test_node", self.mock_server, self.mock_inference_engine, "localhost", 50051, self.mock_discovery, NoopShardDownloader()) - - async def asyncSetUp(self): - await self.node.start() - - async def asyncTearDown(self): - await self.node.stop() - - async def test_node_initialization(self): - self.assertEqual(self.node.node_id, "test_node") - self.assertEqual(self.node.host, "localhost") - self.assertEqual(self.node.port, 50051) - - async def test_node_start(self): - self.mock_server.start.assert_called_once_with("localhost", 50051) - - async def test_node_stop(self): - await self.node.stop() - self.mock_server.stop.assert_called_once() - - async def test_discover_and_connect_to_peers(self): - await self.node.discover_and_connect_to_peers() - self.assertEqual(len(self.node.peers), 2) - self.assertIn("peer1", map(lambda p: p.id(), self.node.peers)) - self.assertIn("peer2", map(lambda p: p.id(), self.node.peers)) - - async def test_process_tensor_calls_inference_engine(self): - mock_peer = Mock() - self.node.peers = [mock_peer] - - input_tensor = np.array([69, 1, 2]) - await self.node.process_tensor(input_tensor, None) - - self.node.inference_engine.process_shard.assert_called_once_with(input_tensor) - - @pytest.mark.asyncio - async def test_node_capabilities(): - node = Node() - await node.initialize() - caps = await node.get_device_capabilities() - assert caps is not None - assert caps.model != "" diff --git a/exo/orchestration/tracing.py b/exo/orchestration/tracing.py deleted file mode 100644 index 4466fc7d..00000000 --- a/exo/orchestration/tracing.py +++ /dev/null @@ -1,166 +0,0 @@ -from dataclasses import dataclass -from typing import Dict, Optional, Any -from opentelemetry import trace, context -from opentelemetry.trace import Status, StatusCode, SpanContext -from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator -from contextlib import contextmanager -import time -from threading import Lock - -@dataclass -class TraceContext: - request_id: str - sequence_number: int - current_span: Optional[trace.Span] = None - trace_parent: Optional[str] = None - token_group_span: Optional[trace.Span] = None - token_count: int = 0 - token_group_size: int = 10 # Default group size - request_span: Optional[trace.Span] = None # Track the main request span - -class Tracer: - def __init__(self): - self.tracer = trace.get_tracer("exo") - self.contexts: Dict[str, TraceContext] = {} - self._lock = Lock() - self.propagator = TraceContextTextMapPropagator() - - def get_context(self, request_id: str) -> Optional[TraceContext]: - with self._lock: - return self.contexts.get(request_id) - - def set_context(self, request_id: str, context: TraceContext): - with self._lock: - self.contexts[request_id] = context - - def inject_context(self, span: trace.Span) -> str: - """Inject current span context into carrier for propagation""" - carrier = {} - ctx = trace.set_span_in_context(span) - self.propagator.inject(carrier, context=ctx) - return carrier.get("traceparent", "") - - def extract_context(self, trace_parent: str) -> Optional[context.Context]: - """Extract span context from carrier""" - if not trace_parent: - return None - carrier = {"traceparent": trace_parent} - return self.propagator.extract(carrier) - - def create_context_from_parent(self, request_id: str, trace_parent: str, sequence_number: int = 0) -> TraceContext: - """Create a new context with the given trace parent""" - parent_ctx = self.extract_context(trace_parent) - if parent_ctx: - # Create a new request span that links to the parent context - request_span = self.tracer.start_span( - "request", - context=parent_ctx, - attributes={ - "request_id": request_id, - "sequence_number": sequence_number - } - ) - return TraceContext( - request_id=request_id, - sequence_number=sequence_number, - request_span=request_span, - current_span=request_span, - trace_parent=trace_parent - ) - return TraceContext(request_id=request_id, sequence_number=sequence_number) - - def handle_token(self, context: TraceContext, token: int, is_finished: bool = False): - """Handle token generation and manage token group spans""" - context.token_count += 1 - - # Start a new token group span if needed - if not context.token_group_span and context.request_span: - group_number = (context.token_count - 1) // context.token_group_size + 1 - - # Create token group span as child of request span - parent_ctx = trace.set_span_in_context(context.request_span) - context.token_group_span = self.tracer.start_span( - f"token_group_{group_number}", - context=parent_ctx, - attributes={ - "request_id": context.request_id, - "group.number": group_number, - "group.start_token": context.token_count, - "group.max_tokens": context.token_group_size - } - ) - - # Add token to current group span - if context.token_group_span: - relative_pos = ((context.token_count - 1) % context.token_group_size) + 1 - context.token_group_span.set_attribute(f"token.{relative_pos}", token) - context.token_group_span.set_attribute("token.count", relative_pos) - - # End current group span if we've reached the group size or if generation is finished - if context.token_count % context.token_group_size == 0 or is_finished: - context.token_group_span.set_attribute("token.final_count", relative_pos) - context.token_group_span.end() - context.token_group_span = None - - @contextmanager - def start_span(self, name: str, context: TraceContext, extra_attributes: Optional[Dict[str, Any]] = None): - """Start a new span with proper parent context""" - attributes = { - "request_id": context.request_id, - "sequence_number": context.sequence_number - } - if extra_attributes: - attributes.update(extra_attributes) - - # Use request span as parent if available - parent_ctx = None - if context.request_span: - parent_ctx = trace.set_span_in_context(context.request_span) - elif context.trace_parent: - parent_ctx = self.extract_context(context.trace_parent) - if parent_ctx and not context.request_span: - # Create a new request span that links to the parent context - context.request_span = self.tracer.start_span( - "request", - context=parent_ctx, - attributes={ - "request_id": context.request_id, - "sequence_number": context.sequence_number - } - ) - parent_ctx = trace.set_span_in_context(context.request_span) - elif context.current_span: - parent_ctx = trace.set_span_in_context(context.current_span) - - # Create span with parent context if it exists - if parent_ctx: - span = self.tracer.start_span( - name, - context=parent_ctx, - attributes=attributes - ) - else: - span = self.tracer.start_span( - name, - attributes=attributes - ) - - # Update context with current span - prev_span = context.current_span - context.current_span = span - - try: - start_time = time.perf_counter() - yield span - duration = time.perf_counter() - start_time - span.set_attribute("duration_s", duration) - span.set_status(Status(StatusCode.OK)) - except Exception as e: - span.set_status(Status(StatusCode.ERROR, str(e))) - raise - finally: - span.end() - context.current_span = prev_span - -# Global tracer instance -tracer = Tracer() \ No newline at end of file diff --git a/exo/test_callbacks.py b/exo/test_callbacks.py deleted file mode 100644 index c10083d6..00000000 --- a/exo/test_callbacks.py +++ /dev/null @@ -1,50 +0,0 @@ -import asyncio -from typing import Any, Callable -from exo.helpers import AsyncCallbackSystem, AsyncCallback - - -# Usage example -async def main() -> None: - callback_system = AsyncCallbackSystem[str, Any]() - - # Register callbacks - callback1 = callback_system.register("callback1") - callback2 = callback_system.register("callback2") - - def on_next_callback(name: str) -> Callable[..., None]: - def callback(*args: Any) -> None: - print(f"{name} received values: {args}") - - return callback - - callback1.on_next(on_next_callback("Callback1")) - callback2.on_next(on_next_callback("Callback2")) - - async def wait_for_callback(name: str, callback: AsyncCallback[Any], condition: Callable[..., bool]) -> None: - try: - result = await callback.wait(condition, timeout=2) - print(f"{name} wait completed with result: {result}") - except asyncio.TimeoutError: - print(f"{name} wait timed out") - - # Trigger all callbacks at once - callback_system.trigger_all("Hello", 42, True) - - # Wait for all callbacks with different conditions - await asyncio.gather( - wait_for_callback("Callback1", callback1, lambda msg, num, flag: isinstance(msg, str) and num > 0), - wait_for_callback("Callback2", callback2, lambda msg, num, flag: flag is True), - ) - - # Trigger individual callback - callback_system.trigger("callback2", "World", -10, False) - - # Demonstrate timeout - new_callback = callback_system.register("new_callback") - new_callback.on_next(on_next_callback("NewCallback")) - await wait_for_callback("NewCallback", new_callback, lambda msg, num, flag: num > 100) - - callback_system.trigger("callback2", "World", 200, False) - - -asyncio.run(main()) diff --git a/exo/tinychat/common.css b/exo/tinychat/common.css deleted file mode 100644 index e654d6a1..00000000 --- a/exo/tinychat/common.css +++ /dev/null @@ -1,130 +0,0 @@ -/* make it responsive */ -@media(min-width: 852px) { - body { - font-size: 14px; - } -} -@media(max-width: 852px) { - body { - font-size: 12px; - } -} - -/* resets */ -html, body { - width: 100%; - height: 100%; -} - -*::-webkit-scrollbar { - display: none; -} - -* { - -ms-overflow-style: none; - scrollbar-width: none; -} - -* { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; -} - -/* default */ -body { - margin: 0; - background-color: var(--primary-bg-color); - color: var(--foreground-color); -} - -h1, h2, h3, h4, h5, h6 { - margin: 0em; -} - -hr { - width: 92%; -} - -button { - cursor: pointer; - border: none; - background-color: transparent; -} -button:hover { -} -button:active { -} - -/* components */ -.container { - margin: 0 auto; - padding: 1rem; -} - -.centered { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.centered-w-only { - position: absolute; - left: 50%; - transform: translateX(-50%); -} - -.centered-h-only { - position: absolute; - top: 50%; - transform: translateY(-50%); -} - -.card { - padding: 0; -} - -.card-header { - padding: 0.5rem 1rem; -} - -.card-container { - width: 96vw; - height: 100%; - gap: 1rem; - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: center; - align-items: center; -} - -.clean-a { - text-decoration: underline; - text-decoration-color: #006fc1; - text-decoration-thickness: 2px; - color: inherit; -} - -.hover-underline { - text-decoration: underline; - text-decoration-color: #228039; - text-decoration-thickness: 2px; - color: inherit; -} - -.flex-horizontal { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; -} - -.vertical-separator { - padding: 0 0.5rem; -} - -[x-cloak] { - display: none !important; -} diff --git a/exo/tinychat/favicon.svg b/exo/tinychat/favicon.svg deleted file mode 100644 index 420a33e0..00000000 --- a/exo/tinychat/favicon.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - diff --git a/exo/tinychat/index.css b/exo/tinychat/index.css deleted file mode 100644 index 1971795a..00000000 --- a/exo/tinychat/index.css +++ /dev/null @@ -1,846 +0,0 @@ -/* define colors */ -:root { - --primary-color: #fff; - --secondary-color: #2a2a2a; - --secondary-color-transparent: #ffffff66; - --primary-bg-color: #1a1a1a; - --foreground-color: #f0f0f0; - --red-color: #a52e4d; -} - -main { - width: 100%; - height: 100%; - - display: flex; - flex-direction: column; - - place-items: center; -} - -.home { - width: 100%; - height: 90%; - margin-bottom: 10rem; - padding-top: 2rem; -} - -.title { - font-size: 3rem; - margin: 1rem 0; - margin-top: 3rem; -} - -.histories-container-container { - width: 100%; - max-height: 75%; - - position: relative; -} - -.histories-container { - overflow-y: auto; - overflow-x: hidden; - width: 100%; - height: 100%; - - display: flex; - flex-direction: column; - gap: 1rem; - align-items: center; - - margin: 0; - padding: 3rem 1rem; -} - -.histories-start { - height: 3rem; - width: 100%; - - z-index: 999; - top: 0; - position: absolute; - - background: linear-gradient( - 180deg, - var(--primary-bg-color) 0%, - transparent 100% - ); -} -.histories-end { - height: 3rem; - width: 100%; - - z-index: 999; - bottom: 0; - position: absolute; - - background: linear-gradient( - 0deg, - var(--primary-bg-color) 0%, - transparent 100% - ); -} - -.history { - padding: 1rem; - width: 100%; - max-width: 40rem; - - background-color: var(--secondary-color); - border-radius: 10px; - border-left: 2px solid var(--primary-color); - - cursor: pointer; - - transform: translateX(calc(1px * var(--tx, 0))); - opacity: var(--opacity, 1); -} -.history:hover { - background-color: var(--secondary-color); -} - -.history-delete-button { - position: absolute; - top: 0; - right: 0; - padding: 0.5rem; - margin: 0; - outline: none; - border: none; - background-color: var(--secondary-color); - color: var(--foreground-color); - border-radius: 0 0 0 10px; - cursor: pointer; - transition: 0.2s; -} -.history-delete-button:hover { - background-color: var(--secondary-color); - padding: 0.75rem; -} - -.messages { - overflow-y: auto; - height: 100%; - width: 100%; - max-width: 1200px; - - display: flex; - flex-direction: column; - gap: 1rem; - align-items: center; - padding-top: 4rem; - padding-bottom: 11rem; - margin: 0 auto; -} - -.message { - max-width: 75%; - padding: 0.5rem 1rem; - border-radius: 20px; -} - -@media(max-width: 1482px) { - .messages { - padding-left: 16px; - padding-right: 16px; - } -} - -.message-role-assistant { - background-color: var(--secondary-color); - margin-right: auto; - color: #fff; -} -.message-role-user { - margin-left: auto; - background-color: var(--primary-color); - color: #000; -} - -.message-role-user p { - white-space: pre-wrap; - word-wrap: break-word; -} - -.download-progress { - position: fixed; - bottom: 11rem; - left: 50%; - transform: translateX(-50%); - margin-left: 125px; - width: 100%; - max-width: 1200px; - overflow-y: auto; - min-height: 350px; - padding: 2rem; - z-index: 998; -} -.message > pre { - white-space: pre-wrap; -} - -.progress-bar-container { - width: 100%; - background-color: #e0e0e0; - border-radius: 4px; - margin: 10px 0; -} -.progress-bar { - height: 20px; - border-radius: 4px; - transition: width 0.5s ease-in-out; -} -.progress-bar.complete { - background-color: #4CAF50; -} -.progress-bar.in-progress { - background-color: #2196F3; -} - -.toast { - width: 100%; - background-color: #fc2a2a; - color: #fff; - text-align: left; - border-radius: 2px; - padding: 16px; - position: fixed; - z-index: 9999; - top: 0; - left: 0; - right: 0; - display: flex; - flex-direction: column; - white-space: pre-wrap; - font-family: monospace; -} - -.toast-header { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; -} - -.toast-error-message { - flex-grow: 1; -} - -.toast-header-buttons { - display: flex; - align-items: center; - gap: 16px; - margin-left: 24px; -} - -.toast-expand-button { - background: none; - border: none; - color: white; - padding: 4px; - cursor: pointer; - font-size: 1em; -} - -.toast-close-button { - background: none; - border: none; - color: white; - padding: 4px; - cursor: pointer; - font-size: 1.2em; - line-height: 1; -} - -.toast-expand-button:hover, -.toast-close-button:hover { - opacity: 0.8; -} - -.toast-content { - margin-top: 10px; - padding: 10px; - background-color: rgba(0, 0, 0, 0.2); - border-radius: 4px; -} - -.hljs { - width: 100%; - position: relative; - border-radius: 10px; - /* wrap code blocks */ - white-space: pre-wrap; -} -/* put clipboard button in the top right corner of the code block */ -.clipboard-button { - position: absolute; - top: 0; - right: 0; - padding: 0.5rem; - margin: 0; - outline: none; - border: none; - background-color: var(--secondary-color); - color: var(--foreground-color); - border-radius: 0 0 0 10px; - cursor: pointer; - transition: 0.2s; -} -.clipboard-button:hover { - background-color: var(--secondary-color); - padding: 0.75rem; -} - -.input-container { - position: fixed; - bottom: 0; - left: 250px; - width: calc(100% - 250px); - max-width: 1200px; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - z-index: 999; - background: linear-gradient( - 0deg, - var(--primary-bg-color) 55%, - transparent 100% - ); - left: 50%; - transform: translateX(-50%); - margin-left: 125px; -} - -.input-performance { - margin-top: 4rem; - - display: flex; - flex-direction: row; - gap: 1rem; -} - -.input-performance-point { - display: flex; - flex-direction: row; - place-items: center; - gap: 0.5rem; -} -.input-performance-point > p { - height: 1rem; - line-height: normal; -} - -.input { - width: 90%; - min-height: 3rem; - flex-shrink: 0; - - display: flex; - flex-direction: row; - justify-content: center; - gap: 0.5rem; - - align-items: flex-end; - margin-bottom: 2rem; -} - -.input-form { - width: 100%; - padding: 1rem; - min-height: 3rem; - max-height: 8rem; - - background-color: var(--secondary-color); - color: var(--foreground-color); - border-radius: 10px; - border: none; - resize: none; - outline: none; -} - -.input-button { - height: 3rem; - width: 4rem; - - background-color: var(--primary-color); - color: var(--secondary-color); - border-radius: 10px; - padding: 0.5rem; - cursor: pointer; -} -.input-button:hover { - background-color: var(--secondary-color-transparent); -} -.input-button:disabled { - background-color: var(--secondary-color); - cursor: not-allowed; -} - -/* wrap text */ -p { - white-space: pre-wrap; -} - -/* fonts */ -.megrim-regular { - font-family: "Megrim", system-ui; - font-weight: 400; - font-style: normal; -} - -.monospace { - font-family: monospace; -} - -.model-selector { - display: none; -} - -/* Image upload button styles */ -.image-input-button { - background-color: var(--secondary-color); - color: var(--foreground-color); - border: none; - border-radius: 50%; - width: 40px; - height: 40px; - font-size: 18px; - cursor: pointer; - transition: all 0.3s ease; - display: flex; - align-items: center; - justify-content: center; - margin-right: 10px; -} - -.image-input-button:hover { - background-color: var(--secondary-color-transparent); - transform: scale(1.1); -} - -.image-input-button:focus { - outline: none; - box-shadow: 0 0 0 3px rgba(var(--secondary-color-rgb), 0.5); -} - -.image-input-button i { - transition: all 0.3s ease; -} - -.image-input-button:hover i { - transform: scale(1.2); -} - -/* Hidden file input styles */ -#image-upload { - display: none; -} - -.image-preview-container { - position: relative; - display: inline-block; - margin-right: 10px; -} - -.image-preview { - max-width: 100px; - max-height: 100px; - object-fit: cover; - border-radius: 5px; -} - -.remove-image-button { - position: absolute; - top: -5px; - right: -5px; - background-color: rgba(255, 255, 255, 0.8); - border: none; - border-radius: 50%; - padding: 2px 5px; - cursor: pointer; -} - -.message > p > img { - max-width: 100%; - max-height: 100%; - object-fit: contain; -} - -.clear-history-button { - background-color: var(--red-color); - color: white; - padding: 10px 20px; - border-radius: 5px; - display: flex; - align-items: center; - gap: 8px; - transition: all 0.3s ease; - margin: 1rem auto; - border: none; - cursor: pointer; -} - -.clear-history-button:hover { - opacity: 0.8; - transform: scale(1.05); -} - -.clear-history-button i { - font-size: 14px; -} - -/* Add new sidebar styles */ -.sidebar { - position: fixed; - left: 0; - top: 0; - bottom: 0; - width: 250px; - background-color: var(--secondary-color); - padding: 20px; - overflow-y: auto; - z-index: 1000; -} - -.model-option { - padding: 12px; - margin: 8px 0; - border-radius: 8px; - background-color: var(--primary-bg-color); - cursor: pointer; - transition: all 0.2s ease; -} - -.model-option:hover { - transform: translateX(5px); -} - -.model-option.selected { - border-left: 3px solid var(--primary-color); - background-color: var(--secondary-color-transparent); -} - -.model-name { - font-weight: bold; - margin-bottom: 4px; -} - -.model-progress { - font-size: 0.9em; - color: var(--secondary-color-transparent); - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.model-progress-info { - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.model-progress i { - font-size: 0.9em; - color: var(--primary-color); -} - -/* Adjust main content to accommodate sidebar */ -main { - margin-left: 250px; - width: calc(100% - 250px); -} - -/* Add styles for the back button */ -.back-button { - position: fixed; - top: 1rem; - left: calc(250px + 1rem); /* Sidebar width + padding */ - background-color: var(--secondary-color); - color: var(--foreground-color); - padding: 0.5rem 1rem; - border-radius: 8px; - border: none; - cursor: pointer; - display: flex; - align-items: center; - gap: 0.5rem; - z-index: 1000; - transition: all 0.2s ease; -} - -.back-button:hover { - transform: translateX(-5px); - background-color: var(--secondary-color-transparent); -} - -.model-info { - display: flex; - flex-direction: column; - gap: 4px; -} - -.model-size { - font-size: 0.8em; - color: var(--secondary-color-transparent); - opacity: 0.8; -} - -.model-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 4px; -} - -.model-delete-button { - background: none; - border: none; - color: var(--red-color); - padding: 4px 8px; - cursor: pointer; - transition: all 0.2s ease; - opacity: 0.7; -} - -.model-delete-button:hover { - opacity: 1; - transform: scale(1.1); -} - -.model-option:hover .model-delete-button { - opacity: 1; -} - -.loading-container { - display: flex; - flex-direction: column; - align-items: center; - gap: 10px; - padding: 20px; - color: var(--secondary-color-transparent); -} - -.loading-container i { - font-size: 24px; -} - -.loading-container span { - font-size: 14px; -} - -/* Add this to your CSS */ -.fa-spin { - animation: fa-spin 2s infinite linear; -} - -@keyframes fa-spin { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -} - -.model-download-button { - background: none; - border: none; - color: var(--primary-color); - padding: 4px 8px; - border-radius: 4px; - cursor: pointer; - transition: all 0.2s ease; - display: inline-flex; - align-items: center; - gap: 6px; - background-color: var(--primary-bg-color); - font-size: 0.9em; - width: fit-content; - align-self: flex-start; -} - -.model-download-button:hover { - transform: scale(1.05); - background-color: var(--secondary-color-transparent); -} - -.model-download-button i { - font-size: 0.9em; -} - -.topology-section { - margin-bottom: 30px; - padding: 15px; - background: rgba(255, 255, 255, 0.05); - border-radius: 8px; -} - -.topology-visualization { - min-height: 150px; - position: relative; - margin-top: 10px; -} - -.topology-loading { - display: flex; - align-items: center; - gap: 10px; - color: #666; - font-size: 0.9em; -} - -.topology-node { - padding: 8px; - background: rgba(255, 255, 255, 0.05); - border-radius: 4px; - margin: 4px 0; - display: flex; - flex-direction: column; - gap: 4px; -} - -.node-info { - display: flex; - align-items: center; - gap: 6px; - font-size: 0.9em; -} - -.topology-node .status { - width: 6px; - height: 6px; - border-radius: 50%; - flex-shrink: 0; -} - -.topology-node .status.active { - background: #4CAF50; -} - -.topology-node .status.inactive { - background: #666; -} - -.node-details { - padding-left: 12px; - display: flex; - flex-direction: column; - gap: 2px; - font-size: 0.8em; - opacity: 0.6; -} - -.node-details span { - display: flex; - align-items: center; -} - -.peer-connections { - margin-top: 8px; - padding-left: 12px; - display: flex; - flex-direction: column; - gap: 4px; -} - -.peer-connection { - display: flex; - align-items: center; - gap: 8px; - font-size: 0.85em; - color: #a0a0a0; -} - -.peer-connection i { - font-size: 0.8em; - color: #666; -} - -.thinking-block { - background-color: rgba(255, 255, 255, 0.05); - border-radius: 8px; - margin: 8px 0; - overflow: hidden; -} - -.thinking-header { - background-color: rgba(255, 255, 255, 0.1); - padding: 8px 12px; - font-size: 0.9em; - color: #a0a0a0; - display: flex; - align-items: center; - gap: 8px; -} - -.thinking-content { - padding: 12px; - white-space: pre-wrap; -} - -@keyframes thinking-spin { - to { transform: rotate(360deg); } -} - -.thinking-header.thinking::before { - content: ''; - width: 12px; - height: 12px; - border: 2px solid #a0a0a0; - border-top-color: transparent; - border-radius: 50%; - animation: thinking-spin 1s linear infinite; -} - -.model-group { - margin-bottom: 12px; -} - -.model-group-header, -.model-subgroup-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 12px; - background-color: var(--primary-bg-color); - border-radius: 6px; - cursor: pointer; - transition: all 0.2s ease; - margin-bottom: 8px; -} - -.model-group-header:hover, -.model-subgroup-header:hover { - background-color: var(--secondary-color-transparent); -} - -.model-group-content { - padding-left: 12px; -} - -.model-subgroup { - margin-bottom: 8px; -} - -.model-subgroup-header { - font-size: 0.9em; - background-color: rgba(255, 255, 255, 0.05); -} - -.model-subgroup-content { - padding-left: 12px; -} - -.group-header-content { - display: flex; - align-items: center; - gap: 8px; -} - -.model-count { - font-size: 0.8em; - color: var(--secondary-color-transparent); - font-family: monospace; -} diff --git a/exo/tinychat/index.html b/exo/tinychat/index.html deleted file mode 100644 index 63e37c8a..00000000 --- a/exo/tinychat/index.html +++ /dev/null @@ -1,454 +0,0 @@ - - - -tinychat - - - - - - - - - - - - - - - - - - - - - - - -

- - -
-
- -
- - -
-
-
- -
-
- -
-

tinychat

- -
- -
- -
- -
-
- - -
-
- - - - - -
-
- -

-

-

-
- -

-

SEC TO FIRST TOKEN

-
- -

-

TOKENS/SEC

-
- -

-

TOKENS

-
-
-
- - -
-Uploaded Image - -
- - -
-
-
- - - diff --git a/exo/tinychat/index.js b/exo/tinychat/index.js deleted file mode 100644 index 5b39e64a..00000000 --- a/exo/tinychat/index.js +++ /dev/null @@ -1,889 +0,0 @@ -document.addEventListener("alpine:init", () => { - Alpine.data("state", () => ({ - // current state - cstate: { - time: null, - messages: [], - selectedModel: 'llama-3.2-1b', - }, - - // historical state - histories: JSON.parse(localStorage.getItem("histories")) || [], - - home: 0, - generating: false, - endpoint: `${window.location.origin}/v1`, - - // Initialize error message structure - errorMessage: null, - errorExpanded: false, - errorTimeout: null, - - // performance tracking - time_till_first: 0, - tokens_per_second: 0, - total_tokens: 0, - - // image handling - imagePreview: null, - - // download progress - downloadProgress: null, - downloadProgressInterval: null, // To keep track of the polling interval - - // Pending message storage - pendingMessage: null, - - modelPoolInterval: null, - - // Add models state alongside existing state - models: {}, - - // Show only models available locally - showDownloadedOnly: false, - - topology: null, - topologyInterval: null, - - // Add these new properties - expandedGroups: {}, - - init() { - // Clean up any pending messages - localStorage.removeItem("pendingMessage"); - - // Get initial model list - this.fetchInitialModels(); - - // Start polling for download progress - this.startDownloadProgressPolling(); - - // Start model polling with the new pattern - this.startModelPolling(); - }, - - async fetchInitialModels() { - try { - const response = await fetch(`${window.location.origin}/initial_models`); - if (response.ok) { - const initialModels = await response.json(); - this.models = initialModels; - } - } catch (error) { - console.error('Error fetching initial models:', error); - } - }, - - async startModelPolling() { - while (true) { - try { - await this.populateSelector(); - // Wait 15 seconds before next poll - await new Promise(resolve => setTimeout(resolve, 15000)); - } catch (error) { - console.error('Model polling error:', error); - // If there's an error, wait before retrying - await new Promise(resolve => setTimeout(resolve, 15000)); - } - } - }, - - async populateSelector() { - return new Promise((resolve, reject) => { - const evtSource = new EventSource(`${window.location.origin}/modelpool`); - - evtSource.onmessage = (event) => { - if (event.data === "[DONE]") { - evtSource.close(); - resolve(); - return; - } - - const modelData = JSON.parse(event.data); - // Update existing model data while preserving other properties - Object.entries(modelData).forEach(([modelName, data]) => { - if (this.models[modelName]) { - this.models[modelName] = { - ...this.models[modelName], - ...data, - loading: false - }; - } - }); - }; - - evtSource.onerror = (error) => { - console.error('EventSource failed:', error); - evtSource.close(); - reject(error); - }; - }); - }, - - removeHistory(cstate) { - const index = this.histories.findIndex((state) => { - return state.time === cstate.time; - }); - if (index !== -1) { - this.histories.splice(index, 1); - localStorage.setItem("histories", JSON.stringify(this.histories)); - } - }, - - clearAllHistory() { - this.histories = []; - localStorage.setItem("histories", JSON.stringify([])); - }, - - // Utility functions - formatBytes(bytes) { - if (bytes === 0) return '0 B'; - const k = 1024; - const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; - }, - - formatDuration(seconds) { - if (seconds === null || seconds === undefined || isNaN(seconds)) return ''; - const h = Math.floor(seconds / 3600); - const m = Math.floor((seconds % 3600) / 60); - const s = Math.floor(seconds % 60); - if (h > 0) return `${h}h ${m}m ${s}s`; - if (m > 0) return `${m}m ${s}s`; - return `${s}s`; - }, - - async handleImageUpload(event) { - const file = event.target.files[0]; - if (file) { - const reader = new FileReader(); - reader.onload = (e) => { - this.imagePreview = e.target.result; - this.imageUrl = e.target.result; // Store the image URL - // Add image preview to the chat - this.cstate.messages.push({ - role: "user", - content: `![Uploaded Image](${this.imagePreview})`, - }); - }; - reader.readAsDataURL(file); - } - }, - - - async handleSend() { - try { - const el = document.getElementById("input-form"); - const value = el.value.trim(); - if (!value && !this.imagePreview) return; - - if (this.generating) return; - this.generating = true; - if (this.home === 0) this.home = 1; - - // ensure that going back in history will go back to home - window.history.pushState({}, "", "/"); - - // add message to list - if (value) { - this.cstate.messages.push({ role: "user", content: value }); - } - - // clear textarea - el.value = ""; - el.style.height = "auto"; - el.style.height = el.scrollHeight + "px"; - - localStorage.setItem("pendingMessage", value); - this.processMessage(value); - } catch (error) { - console.error('error', error); - this.setError(error); - this.generating = false; - } - }, - - async processMessage(value) { - try { - // reset performance tracking - const prefill_start = Date.now(); - let start_time = 0; - let tokens = 0; - this.tokens_per_second = 0; - - // prepare messages for API request - let apiMessages = this.cstate.messages.map(msg => { - if (msg.content.startsWith('![Uploaded Image]')) { - return { - role: "user", - content: [ - { - type: "image_url", - image_url: { - url: this.imageUrl - } - }, - { - type: "text", - text: value // Use the actual text the user typed - } - ] - }; - } else { - return { - role: msg.role, - content: msg.content - }; - } - }); - - if (this.cstate.selectedModel === "stable-diffusion-2-1-base") { - // Send a request to the image generation endpoint - console.log(apiMessages[apiMessages.length - 1].content) - console.log(this.cstate.selectedModel) - console.log(this.endpoint) - const response = await fetch(`${this.endpoint}/image/generations`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - "model": 'stable-diffusion-2-1-base', - "prompt": apiMessages[apiMessages.length - 1].content, - "image_url": this.imageUrl - }), - }); - - if (!response.ok) { - throw new Error("Failed to fetch"); - } - const reader = response.body.getReader(); - let done = false; - let gottenFirstChunk = false; - - while (!done) { - const { value, done: readerDone } = await reader.read(); - done = readerDone; - const decoder = new TextDecoder(); - - if (value) { - // Assume non-binary data (text) comes first - const chunk = decoder.decode(value, { stream: true }); - const parsed = JSON.parse(chunk); - console.log(parsed) - - if (parsed.progress) { - if (!gottenFirstChunk) { - this.cstate.messages.push({ role: "assistant", content: "" }); - gottenFirstChunk = true; - } - this.cstate.messages[this.cstate.messages.length - 1].content = parsed.progress; - } - else if (parsed.images) { - if (!gottenFirstChunk) { - this.cstate.messages.push({ role: "assistant", content: "" }); - gottenFirstChunk = true; - } - const imageUrl = parsed.images[0].url; - console.log(imageUrl) - this.cstate.messages[this.cstate.messages.length - 1].content = `![Generated Image](${imageUrl}?t=${Date.now()})`; - } - } - } - } - - else{ - const containsImage = apiMessages.some(msg => Array.isArray(msg.content) && msg.content.some(item => item.type === 'image_url')); - if (containsImage) { - // Map all messages with string content to object with type text - apiMessages = apiMessages.map(msg => { - if (typeof msg.content === 'string') { - return { - ...msg, - content: [ - { - type: "text", - text: msg.content - } - ] - }; - } - return msg; - }); - } - - console.log(apiMessages) - //start receiving server sent events - let gottenFirstChunk = false; - for await ( - const chunk of this.openaiChatCompletion(this.cstate.selectedModel, apiMessages) - ) { - if (!gottenFirstChunk) { - this.cstate.messages.push({ role: "assistant", content: "" }); - gottenFirstChunk = true; - } - - // add chunk to the last message - this.cstate.messages[this.cstate.messages.length - 1].content += chunk; - - // calculate performance tracking - tokens += 1; - this.total_tokens += 1; - if (start_time === 0) { - start_time = Date.now(); - this.time_till_first = start_time - prefill_start; - } else { - const diff = Date.now() - start_time; - if (diff > 0) { - this.tokens_per_second = tokens / (diff / 1000); - } - } - } - } - // Clean the cstate before adding it to histories - const cleanedCstate = JSON.parse(JSON.stringify(this.cstate)); - cleanedCstate.messages = cleanedCstate.messages.map(msg => { - if (Array.isArray(msg.content)) { - return { - ...msg, - content: msg.content.map(item => - item.type === 'image_url' ? { type: 'image_url', image_url: { url: '[IMAGE_PLACEHOLDER]' } } : item - ) - }; - } - return msg; - }); - - // Update the state in histories or add it if it doesn't exist - const index = this.histories.findIndex((cstate) => cstate.time === cleanedCstate.time); - cleanedCstate.time = Date.now(); - if (index !== -1) { - // Update the existing entry - this.histories[index] = cleanedCstate; - } else { - // Add a new entry - this.histories.push(cleanedCstate); - } - console.log(this.histories) - // update in local storage - try { - localStorage.setItem("histories", JSON.stringify(this.histories)); - } catch (error) { - console.error("Failed to save histories to localStorage:", error); - } - } catch (error) { - console.error('error', error); - this.setError(error); - } finally { - this.generating = false; - } - }, - - async handleEnter(event) { - // if shift is not pressed - if (!event.shiftKey) { - event.preventDefault(); - await this.handleSend(); - } - }, - - updateTotalTokens(messages) { - fetch(`${this.endpoint}/chat/token/encode`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ messages }), - }).then((response) => response.json()).then((data) => { - this.total_tokens = data.length; - }).catch(console.error); - }, - - async *openaiChatCompletion(model, messages) { - const response = await fetch(`${this.endpoint}/chat/completions`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - "model": model, - "messages": messages, - "stream": true, - }), - }); - if (!response.ok) { - const errorResBody = await response.json() - if (errorResBody?.detail) { - throw new Error(`Failed to fetch completions: ${errorResBody.detail}`); - } else { - throw new Error("Failed to fetch completions: Unknown error"); - } - } - - const reader = response.body.pipeThrough(new TextDecoderStream()) - .pipeThrough(new EventSourceParserStream()).getReader(); - - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - if (value.type === "event") { - const json = JSON.parse(value.data); - if (json.choices) { - const choice = json.choices[0]; - if (choice.finish_reason === "stop") break; - if (choice.delta.content) yield choice.delta.content; - } - } - } - }, - - async fetchDownloadProgress() { - try { - const response = await fetch(`${this.endpoint}/download/progress`); - if (response.ok) { - const data = await response.json(); - const progressArray = Object.values(data); - if (progressArray.length > 0) { - this.downloadProgress = progressArray.map(progress => { - // Check if download is complete - if (progress.status === "complete") { - return { - ...progress, - isComplete: true, - percentage: 100 - }; - } else if (progress.status === "failed") { - return { - ...progress, - isComplete: false, - errorMessage: "Download failed" - }; - } else { - return { - ...progress, - isComplete: false, - downloaded_bytes_display: this.formatBytes(progress.downloaded_bytes), - total_bytes_display: this.formatBytes(progress.total_bytes), - overall_speed_display: progress.overall_speed ? this.formatBytes(progress.overall_speed) + '/s' : '', - overall_eta_display: progress.overall_eta ? this.formatDuration(progress.overall_eta) : '', - percentage: ((progress.downloaded_bytes / progress.total_bytes) * 100).toFixed(2) - }; - } - }); - const allComplete = this.downloadProgress.every(progress => progress.isComplete); - if (allComplete) { - // Check for pendingMessage - const savedMessage = localStorage.getItem("pendingMessage"); - if (savedMessage) { - // Clear pendingMessage - localStorage.removeItem("pendingMessage"); - // Call processMessage() with savedMessage - if (this.lastErrorMessage) { - await this.processMessage(savedMessage); - } - } - this.lastErrorMessage = null; - this.downloadProgress = null; - } - } else { - // No ongoing download - this.downloadProgress = null; - } - } - } catch (error) { - console.error("Error fetching download progress:", error); - this.downloadProgress = null; - } - }, - - startDownloadProgressPolling() { - if (this.downloadProgressInterval) { - // Already polling - return; - } - this.fetchDownloadProgress(); // Fetch immediately - this.downloadProgressInterval = setInterval(() => { - this.fetchDownloadProgress(); - }, 1000); // Poll every second - }, - - // Add a helper method to set errors consistently - setError(error) { - this.errorMessage = { - basic: error.message || "An unknown error occurred", - stack: error.stack || "" - }; - this.errorExpanded = false; - - if (this.errorTimeout) { - clearTimeout(this.errorTimeout); - } - - if (!this.errorExpanded) { - this.errorTimeout = setTimeout(() => { - this.errorMessage = null; - this.errorExpanded = false; - }, 30 * 1000); - } - }, - - async deleteModel(modelName, model) { - const downloadedSize = model.total_downloaded || 0; - const sizeMessage = downloadedSize > 0 ? - `This will free up ${this.formatBytes(downloadedSize)} of space.` : - 'This will remove any partially downloaded files.'; - - if (!confirm(`Are you sure you want to delete ${model.name}? ${sizeMessage}`)) { - return; - } - - try { - const response = await fetch(`${window.location.origin}/models/${modelName}`, { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json' - } - }); - - const data = await response.json(); - - if (!response.ok) { - throw new Error(data.detail || 'Failed to delete model'); - } - - // Update the model status in the UI - if (this.models[modelName]) { - this.models[modelName].downloaded = false; - this.models[modelName].download_percentage = 0; - this.models[modelName].total_downloaded = 0; - } - - // If this was the selected model, switch to a different one - if (this.cstate.selectedModel === modelName) { - const availableModel = Object.keys(this.models).find(key => this.models[key].downloaded); - this.cstate.selectedModel = availableModel || 'llama-3.2-1b'; - } - - // Show success message - console.log(`Model deleted successfully from: ${data.path}`); - - // Refresh the model list - await this.populateSelector(); - } catch (error) { - console.error('Error deleting model:', error); - this.setError(error.message || 'Failed to delete model'); - } - }, - - async handleDownload(modelName) { - try { - const response = await fetch(`${window.location.origin}/download`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - model: modelName - }) - }); - - const data = await response.json(); - - if (!response.ok) { - throw new Error(data.error || 'Failed to start download'); - } - - // Update the model's status immediately when download starts - if (this.models[modelName]) { - this.models[modelName] = { - ...this.models[modelName], - loading: true - }; - } - - } catch (error) { - console.error('Error starting download:', error); - this.setError(error); - } - }, - - async fetchTopology() { - try { - const response = await fetch(`${this.endpoint}/topology`); - if (!response.ok) throw new Error('Failed to fetch topology'); - return await response.json(); - } catch (error) { - console.error('Topology fetch error:', error); - return null; - } - }, - - initTopology() { - // Initial fetch - this.updateTopology(); - - // Set up periodic updates - this.topologyInterval = setInterval(() => this.updateTopology(), 5000); - - // Cleanup on page unload - window.addEventListener('beforeunload', () => { - if (this.topologyInterval) { - clearInterval(this.topologyInterval); - } - }); - }, - - async updateTopology() { - const topologyData = await this.fetchTopology(); - if (!topologyData) return; - - const vizElement = this.$refs.topologyViz; - vizElement.innerHTML = ''; // Clear existing visualization - - // Helper function to truncate node ID - const truncateNodeId = (id) => id.substring(0, 8); - - // Create nodes from object - Object.entries(topologyData.nodes).forEach(([nodeId, node]) => { - const nodeElement = document.createElement('div'); - nodeElement.className = 'topology-node'; - - // Get peer connections for this node - const peerConnections = topologyData.peer_graph[nodeId] || []; - const peerConnectionsHtml = peerConnections.map(peer => ` -
- - To ${truncateNodeId(peer.to_id)}: ${peer.description} -
- `).join(''); - - nodeElement.innerHTML = ` -
- - ${node.model} [${truncateNodeId(nodeId)}] -
-
- ${node.chip} - ${(node.memory / 1024).toFixed(1)}GB RAM - ${node.flops.fp32.toFixed(1)} TF -
-
- ${peerConnectionsHtml} -
- `; - vizElement.appendChild(nodeElement); - }); - }, - - // Add these helper methods - countDownloadedModels(models) { - return Object.values(models).filter(model => model.downloaded).length; - }, - - getGroupCounts(groupModels) { - const total = Object.keys(groupModels).length; - const downloaded = this.countDownloadedModels(groupModels); - return `[${downloaded}/${total}]`; - }, - - // Update the existing groupModelsByPrefix method to include counts - groupModelsByPrefix(models) { - const groups = {}; - const filteredModels = this.showDownloadedOnly ? - Object.fromEntries(Object.entries(models).filter(([, model]) => model.downloaded)) : - models; - - Object.entries(filteredModels).forEach(([key, model]) => { - const parts = key.split('-'); - const mainPrefix = parts[0].toUpperCase(); - - let subPrefix; - if (parts.length === 2) { - subPrefix = parts[1].toUpperCase(); - } else if (parts.length > 2) { - subPrefix = parts[1].toUpperCase(); - } else { - subPrefix = 'OTHER'; - } - - if (!groups[mainPrefix]) { - groups[mainPrefix] = {}; - } - if (!groups[mainPrefix][subPrefix]) { - groups[mainPrefix][subPrefix] = {}; - } - groups[mainPrefix][subPrefix][key] = model; - }); - return groups; - }, - - toggleGroup(prefix, subPrefix = null) { - const key = subPrefix ? `${prefix}-${subPrefix}` : prefix; - this.expandedGroups[key] = !this.expandedGroups[key]; - }, - - isGroupExpanded(prefix, subPrefix = null) { - const key = subPrefix ? `${prefix}-${subPrefix}` : prefix; - return this.expandedGroups[key] || false; - }, - })); -}); - -const { markedHighlight } = globalThis.markedHighlight; -marked.use(markedHighlight({ - langPrefix: "hljs language-", - highlight(code, lang, _info) { - const language = hljs.getLanguage(lang) ? lang : "plaintext"; - return hljs.highlight(code, { language }).value; - }, -})); - -// **** eventsource-parser **** -class EventSourceParserStream extends TransformStream { - constructor() { - let parser; - - super({ - start(controller) { - parser = createParser((event) => { - if (event.type === "event") { - controller.enqueue(event); - } - }); - }, - - transform(chunk) { - parser.feed(chunk); - }, - }); - } -} - -function createParser(onParse) { - let isFirstChunk; - let buffer; - let startingPosition; - let startingFieldLength; - let eventId; - let eventName; - let data; - reset(); - return { - feed, - reset, - }; - function reset() { - isFirstChunk = true; - buffer = ""; - startingPosition = 0; - startingFieldLength = -1; - eventId = void 0; - eventName = void 0; - data = ""; - } - function feed(chunk) { - buffer = buffer ? buffer + chunk : chunk; - if (isFirstChunk && hasBom(buffer)) { - buffer = buffer.slice(BOM.length); - } - isFirstChunk = false; - const length = buffer.length; - let position = 0; - let discardTrailingNewline = false; - while (position < length) { - if (discardTrailingNewline) { - if (buffer[position] === "\n") { - ++position; - } - discardTrailingNewline = false; - } - let lineLength = -1; - let fieldLength = startingFieldLength; - let character; - for ( - let index = startingPosition; - lineLength < 0 && index < length; - ++index - ) { - character = buffer[index]; - if (character === ":" && fieldLength < 0) { - fieldLength = index - position; - } else if (character === "\r") { - discardTrailingNewline = true; - lineLength = index - position; - } else if (character === "\n") { - lineLength = index - position; - } - } - if (lineLength < 0) { - startingPosition = length - position; - startingFieldLength = fieldLength; - break; - } else { - startingPosition = 0; - startingFieldLength = -1; - } - parseEventStreamLine(buffer, position, fieldLength, lineLength); - position += lineLength + 1; - } - if (position === length) { - buffer = ""; - } else if (position > 0) { - buffer = buffer.slice(position); - } - } - function parseEventStreamLine(lineBuffer, index, fieldLength, lineLength) { - if (lineLength === 0) { - if (data.length > 0) { - onParse({ - type: "event", - id: eventId, - event: eventName || void 0, - data: data.slice(0, -1), - // remove trailing newline - }); - - data = ""; - eventId = void 0; - } - eventName = void 0; - return; - } - const noValue = fieldLength < 0; - const field = lineBuffer.slice( - index, - index + (noValue ? lineLength : fieldLength), - ); - let step = 0; - if (noValue) { - step = lineLength; - } else if (lineBuffer[index + fieldLength + 1] === " ") { - step = fieldLength + 2; - } else { - step = fieldLength + 1; - } - const position = index + step; - const valueLength = lineLength - step; - const value = lineBuffer.slice(position, position + valueLength).toString(); - if (field === "data") { - data += value ? "".concat(value, "\n") : "\n"; - } else if (field === "event") { - eventName = value; - } else if (field === "id" && !value.includes("\0")) { - eventId = value; - } else if (field === "retry") { - const retry = parseInt(value, 10); - if (!Number.isNaN(retry)) { - onParse({ - type: "reconnect-interval", - value: retry, - }); - } - } - } -} - -const BOM = [239, 187, 191]; -function hasBom(buffer) { - return BOM.every((charCode, index) => buffer.charCodeAt(index) === charCode); -} diff --git a/exo/tinychat/static/cdn.jsdelivr.net/npm/@alpine-collective/toolkit@1.0.2/dist/cdn.min.js b/exo/tinychat/static/cdn.jsdelivr.net/npm/@alpine-collective/toolkit@1.0.2/dist/cdn.min.js deleted file mode 100644 index a363545c..00000000 --- a/exo/tinychat/static/cdn.jsdelivr.net/npm/@alpine-collective/toolkit@1.0.2/dist/cdn.min.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{var H=Object.create,v=Object.defineProperty,N=Object.getPrototypeOf,V=Object.prototype.hasOwnProperty,z=Object.getOwnPropertyNames,q=Object.getOwnPropertyDescriptor;var W=n=>v(n,"__esModule",{value:!0});var D=(n,e)=>()=>(e||(e={exports:{}},n(e.exports,e)),e.exports);var F=(n,e,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of z(e))!V.call(n,r)&&r!=="default"&&v(n,r,{get:()=>e[r],enumerable:!(o=q(e,r))||o.enumerable});return n},U=n=>F(W(v(n!=null?H(N(n)):{},"default",n&&n.__esModule&&"default"in n?{get:()=>n.default,enumerable:!0}:{value:n,enumerable:!0})),n);var I=D((E,w)=>{(function(){"use strict";function n(){var e=window,o=document;if("scrollBehavior"in o.documentElement.style&&e.__forceSmoothScrollPolyfill__!==!0)return;var r=e.HTMLElement||e.Element,i=468,f={scroll:e.scroll||e.scrollTo,scrollBy:e.scrollBy,elementScroll:r.prototype.scroll||b,scrollIntoView:r.prototype.scrollIntoView},u=e.performance&&e.performance.now?e.performance.now.bind(e.performance):Date.now;function c(t){var l=["MSIE ","Trident/","Edge/"];return new RegExp(l.join("|")).test(t)}var g=c(e.navigator.userAgent)?1:0;function b(t,l){this.scrollLeft=t,this.scrollTop=l}function M(t){return .5*(1-Math.cos(Math.PI*t))}function m(t){if(t===null||typeof t!="object"||t.behavior===void 0||t.behavior==="auto"||t.behavior==="instant")return!0;if(typeof t=="object"&&t.behavior==="smooth")return!1;throw new TypeError("behavior member of ScrollOptions "+t.behavior+" is not a valid value for enumeration ScrollBehavior.")}function O(t,l){if(l==="Y")return t.clientHeight+g1?1:a,s=M(a),d=t.startX+(t.x-t.startX)*s,p=t.startY+(t.y-t.startY)*s,t.method.call(t.scrollable,d,p),(d!==t.x||p!==t.y)&&e.requestAnimationFrame(S.bind(e,t))}function h(t,l,s){var d,p,a,y,_=u();t===o.body?(d=e,p=e.scrollX||e.pageXOffset,a=e.scrollY||e.pageYOffset,y=f.scroll):(d=t,p=t.scrollLeft,a=t.scrollTop,y=b),S({scrollable:d,method:y,startTime:_,startX:p,startY:a,x:l,y:s})}e.scroll=e.scrollTo=function(){if(arguments[0]!==void 0){if(m(arguments[0])===!0){f.scroll.call(e,arguments[0].left!==void 0?arguments[0].left:typeof arguments[0]!="object"?arguments[0]:e.scrollX||e.pageXOffset,arguments[0].top!==void 0?arguments[0].top:arguments[1]!==void 0?arguments[1]:e.scrollY||e.pageYOffset);return}h.call(e,o.body,arguments[0].left!==void 0?~~arguments[0].left:e.scrollX||e.pageXOffset,arguments[0].top!==void 0?~~arguments[0].top:e.scrollY||e.pageYOffset)}},e.scrollBy=function(){if(arguments[0]!==void 0){if(m(arguments[0])){f.scrollBy.call(e,arguments[0].left!==void 0?arguments[0].left:typeof arguments[0]!="object"?arguments[0]:0,arguments[0].top!==void 0?arguments[0].top:arguments[1]!==void 0?arguments[1]:0);return}h.call(e,o.body,~~arguments[0].left+(e.scrollX||e.pageXOffset),~~arguments[0].top+(e.scrollY||e.pageYOffset))}},r.prototype.scroll=r.prototype.scrollTo=function(){if(arguments[0]!==void 0){if(m(arguments[0])===!0){if(typeof arguments[0]=="number"&&arguments[1]===void 0)throw new SyntaxError("Value could not be converted");f.elementScroll.call(this,arguments[0].left!==void 0?~~arguments[0].left:typeof arguments[0]!="object"?~~arguments[0]:this.scrollLeft,arguments[0].top!==void 0?~~arguments[0].top:arguments[1]!==void 0?~~arguments[1]:this.scrollTop);return}var t=arguments[0].left,l=arguments[0].top;h.call(this,this,typeof t=="undefined"?this.scrollLeft:~~t,typeof l=="undefined"?this.scrollTop:~~l)}},r.prototype.scrollBy=function(){if(arguments[0]!==void 0){if(m(arguments[0])===!0){f.elementScroll.call(this,arguments[0].left!==void 0?~~arguments[0].left+this.scrollLeft:~~arguments[0]+this.scrollLeft,arguments[0].top!==void 0?~~arguments[0].top+this.scrollTop:~~arguments[1]+this.scrollTop);return}this.scroll({left:~~arguments[0].left+this.scrollLeft,top:~~arguments[0].top+this.scrollTop,behavior:arguments[0].behavior})}},r.prototype.scrollIntoView=function(){if(m(arguments[0])===!0){f.scrollIntoView.call(this,arguments[0]===void 0?!0:arguments[0]);return}var t=$(this),l=t.getBoundingClientRect(),s=this.getBoundingClientRect();t!==o.body?(h.call(this,t,t.scrollLeft+s.left-l.left,t.scrollTop+s.top-l.top),e.getComputedStyle(t).position!=="fixed"&&e.scrollBy({left:l.left,top:l.top,behavior:"smooth"})):e.scrollBy({left:s.left,top:s.top,behavior:"smooth"})}}typeof E=="object"&&typeof w!="undefined"?w.exports={polyfill:n}:n()})()});function j(n){n.magic("range",()=>function(e,o,r=1){typeof o=="undefined"&&(o=e,e=e?1:0);let i=e>o;i&&([e,o]=[o,e]);let f=Array.from({length:(o-e)/r+1},(u,c)=>e+c*r);return i?f.reverse():f})}var Y=U(I());function X(n){Y.default.polyfill(),n.magic("scroll",()=>function(e,o={}){let r=e,i=o.offset?parseInt(o.offset,10):0;if(delete o.offset,typeof e=="string"&&/^[0-9]+?/g.test(e)&&(e=parseInt(e,10)),typeof e=="string"&&(e=document.querySelector(e)),e instanceof Element&&(e=Math.floor(e.getBoundingClientRect().top+window.pageYOffset)),Number.isInteger(e)&&(e={top:e-i,behavior:"smooth"}),typeof e!="object")throw Error("Unsupported $scroll target: ",r);Object.assign(e,o),window.scroll(e)})}function B(n){let e=(o,r)=>{if(r[0].length<=o.length)return o;let i="\u2026";return typeof r[2]!="undefined"&&(i=r[2]),Object.prototype.hasOwnProperty.call(r[1],"ellipsis")&&(i=r[1].ellipsis),o+i};n.magic("truncate",()=>function(...o){return typeof o[0]!="string"||!o[1]?o[0]:typeof o[1]!="object"?e(o[0].slice(0,o[1]),o):Object.prototype.hasOwnProperty.call(o[1],"words")&&o[1].words?e(o[0].split(" ").splice(0,o[1].words).join(" "),o):Object.prototype.hasOwnProperty.call(o[1],"characters")&&o[1].characters?e(o[0].slice(0,o[1].characters),o):o[0]})}function L(n){n.magic("dbg",e=>function(...o){let r=o.map(i=>n.raw(i));console.log(...r)})}function x(n){let e=n.reactive({screensize:window.innerWidth}),o={xs:0,sm:640,md:768,lg:1024,xl:1280,"2xl":1536},r=window.AlpineMagicHelpersConfig&&window.AlpineMagicHelpersConfig.breakpoints?window.AlpineMagicHelpersConfig.breakpoints:o,i;window.addEventListener("resize",()=>{clearTimeout(i),i=setTimeout(()=>{e.screensize=window.innerWidth},150)}),n.magic("screen",f=>u=>{let c=e.screensize;if(Number.isInteger(u))return u<=c;if(r[u]===void 0)throw Error("Undefined $screen property: "+u+". Supported properties: "+Object.keys(r).join(", "));return r[u]<=c})}function P(n){n.magic("interval",()=>function(...e){if(typeof e[0]!="function")return e[0];let o=e[1],r=0,i=!1;typeof e[1]=="object"&&(Object.prototype.hasOwnProperty.call(e[1],"timer")&&(o=e[1].timer),Object.prototype.hasOwnProperty.call(e[1],"delay")&&(r=e[1].delay),Object.prototype.hasOwnProperty.call(e[1],"forceInterval")&&(i=e[1].forceInterval));let f=null,u=!0,c=()=>{let g=u?o+r:o;u=!1,f=setTimeout(()=>{e[0].call(this),i?c():requestAnimationFrame(c)},g)};n.effect(()=>{this.autoIntervalTest==null||this.autoIntervalTest?i?c():requestAnimationFrame(c):clearTimeout(f)})})}function C(n){j(n),X(n),B(n),L(n),x(n),P(n)}document.addEventListener("alpine:initializing",()=>{C(window.Alpine)});})(); diff --git a/exo/tinychat/static/cdn.jsdelivr.net/npm/@alpinejs/focus@3.x.x/dist/cdn.min.js b/exo/tinychat/static/cdn.jsdelivr.net/npm/@alpinejs/focus@3.x.x/dist/cdn.min.js deleted file mode 100644 index 5a2d18be..00000000 --- a/exo/tinychat/static/cdn.jsdelivr.net/npm/@alpinejs/focus@3.x.x/dist/cdn.min.js +++ /dev/null @@ -1,15 +0,0 @@ -(()=>{var _=["input","select","textarea","a[href]","button","[tabindex]:not(slot)","audio[controls]","video[controls]",'[contenteditable]:not([contenteditable="false"])',"details>summary:first-of-type","details"],k=_.join(","),K=typeof Element>"u",N=K?function(){}:Element.prototype.matches||Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector,B=!K&&Element.prototype.getRootNode?function(i){return i.getRootNode()}:function(i){return i.ownerDocument},V=function(e,t,a){var n=Array.prototype.slice.apply(e.querySelectorAll(k));return t&&N.call(e,k)&&n.unshift(e),n=n.filter(a),n},$=function i(e,t,a){for(var n=[],r=Array.from(e);r.length;){var s=r.shift();if(s.tagName==="SLOT"){var l=s.assignedElements(),m=l.length?l:s.children,p=i(m,!0,a);a.flatten?n.push.apply(n,p):n.push({scope:s,candidates:p})}else{var v=N.call(s,k);v&&a.filter(s)&&(t||!e.includes(s))&&n.push(s);var h=s.shadowRoot||typeof a.getShadowRoot=="function"&&a.getShadowRoot(s),y=!a.shadowRootFilter||a.shadowRootFilter(s);if(h&&y){var w=i(h===!0?s.children:h.children,!0,a);a.flatten?n.push.apply(n,w):n.push({scope:s,candidates:w})}else r.unshift.apply(r,s.children)}}return n},Y=function(e,t){return e.tabIndex<0&&(t||/^(AUDIO|VIDEO|DETAILS)$/.test(e.tagName)||e.isContentEditable)&&isNaN(parseInt(e.getAttribute("tabindex"),10))?0:e.tabIndex},se=function(e,t){return e.tabIndex===t.tabIndex?e.documentOrder-t.documentOrder:e.tabIndex-t.tabIndex},Z=function(e){return e.tagName==="INPUT"},ce=function(e){return Z(e)&&e.type==="hidden"},le=function(e){var t=e.tagName==="DETAILS"&&Array.prototype.slice.apply(e.children).some(function(a){return a.tagName==="SUMMARY"});return t},fe=function(e,t){for(var a=0;asummary:first-of-type"),s=r?e.parentElement:e;if(N.call(s,"details:not([open]) *"))return!0;var l=B(e).host,m=l?.ownerDocument.contains(l)||e.ownerDocument.contains(e);if(!a||a==="full"){if(typeof n=="function"){for(var p=e;e;){var v=e.parentElement,h=B(e);if(v&&!v.shadowRoot&&n(v)===!0)return W(e);e.assignedSlot?e=e.assignedSlot:!v&&h!==e.ownerDocument?e=h.host:e=v}e=p}if(m)return!e.getClientRects().length}else if(a==="non-zero-area")return W(e);return!1},pe=function(e){if(/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(e.tagName))for(var t=e.parentElement;t;){if(t.tagName==="FIELDSET"&&t.disabled){for(var a=0;a=0)},me=function i(e){var t=[],a=[];return e.forEach(function(n,r){var s=!!n.scope,l=s?n.scope:n,m=Y(l,s),p=s?i(n.candidates):l;m===0?s?t.push.apply(t,p):t.push(l):a.push({documentOrder:r,tabIndex:m,item:n,isScope:s,content:p})}),a.sort(se).reduce(function(n,r){return r.isScope?n.push.apply(n,r.content):n.push(r.content),n},[]).concat(t)},z=function(e,t){t=t||{};var a;return t.getShadowRoot?a=$([e],t.includeContainer,{filter:G.bind(null,t),flatten:!1,getShadowRoot:t.getShadowRoot,shadowRootFilter:ge}):a=V(e,t.includeContainer,G.bind(null,t)),me(a)},x=function(e,t){t=t||{};var a;return t.getShadowRoot?a=$([e],t.includeContainer,{filter:I.bind(null,t),flatten:!0,getShadowRoot:t.getShadowRoot}):a=V(e,t.includeContainer,I.bind(null,t)),a},A=function(e,t){if(t=t||{},!e)throw new Error("No node provided");return N.call(e,k)===!1?!1:G(t,e)},ye=_.concat("iframe").join(","),R=function(e,t){if(t=t||{},!e)throw new Error("No node provided");return N.call(e,ye)===!1?!1:I(t,e)};function Q(i,e){var t=Object.keys(i);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(i);e&&(a=a.filter(function(n){return Object.getOwnPropertyDescriptor(i,n).enumerable})),t.push.apply(t,a)}return t}function X(i){for(var e=1;e0){var a=i[i.length-1];a!==t&&a.pause()}var n=i.indexOf(t);n===-1||i.splice(n,1),i.push(t)},deactivateTrap:function(t){var a=i.indexOf(t);a!==-1&&i.splice(a,1),i.length>0&&i[i.length-1].unpause()}}}(),Te=function(e){return e.tagName&&e.tagName.toLowerCase()==="input"&&typeof e.select=="function"},Fe=function(e){return e.key==="Escape"||e.key==="Esc"||e.keyCode===27},Se=function(e){return e.key==="Tab"||e.keyCode===9},ee=function(e){return setTimeout(e,0)},te=function(e,t){var a=-1;return e.every(function(n,r){return t(n)?(a=r,!1):!0}),a},O=function(e){for(var t=arguments.length,a=new Array(t>1?t-1:0),n=1;n1?c-1:0),f=1;f=0)o=a.activeElement;else{var u=r.tabbableGroups[0],c=u&&u.firstTabbableNode;o=c||p("fallbackFocus")}if(!o)throw new Error("Your focus-trap needs to have at least one focusable element");return o},h=function(){if(r.containerGroups=r.containers.map(function(o){var u=z(o,n.tabbableOptions),c=x(o,n.tabbableOptions);return{container:o,tabbableNodes:u,focusableNodes:c,firstTabbableNode:u.length>0?u[0]:null,lastTabbableNode:u.length>0?u[u.length-1]:null,nextTabbableNode:function(f){var g=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0,F=c.findIndex(function(E){return E===f});if(!(F<0))return g?c.slice(F+1).find(function(E){return A(E,n.tabbableOptions)}):c.slice(0,F).reverse().find(function(E){return A(E,n.tabbableOptions)})}}}),r.tabbableGroups=r.containerGroups.filter(function(o){return o.tabbableNodes.length>0}),r.tabbableGroups.length<=0&&!p("fallbackFocus"))throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times")},y=function d(o){if(o!==!1&&o!==a.activeElement){if(!o||!o.focus){d(v());return}o.focus({preventScroll:!!n.preventScroll}),r.mostRecentlyFocusedNode=o,Te(o)&&o.select()}},w=function(o){var u=p("setReturnFocus",o);return u||(u===!1?!1:o)},S=function(o){var u=L(o);if(!(m(u)>=0)){if(O(n.clickOutsideDeactivates,o)){s.deactivate({returnFocus:n.returnFocusOnDeactivate&&!R(u,n.tabbableOptions)});return}O(n.allowOutsideClick,o)||o.preventDefault()}},D=function(o){var u=L(o),c=m(u)>=0;c||u instanceof Document?c&&(r.mostRecentlyFocusedNode=u):(o.stopImmediatePropagation(),y(r.mostRecentlyFocusedNode||v()))},T=function(o){var u=L(o);h();var c=null;if(r.tabbableGroups.length>0){var b=m(u),f=b>=0?r.containerGroups[b]:void 0;if(b<0)o.shiftKey?c=r.tabbableGroups[r.tabbableGroups.length-1].lastTabbableNode:c=r.tabbableGroups[0].firstTabbableNode;else if(o.shiftKey){var g=te(r.tabbableGroups,function(P){var j=P.firstTabbableNode;return u===j});if(g<0&&(f.container===u||R(u,n.tabbableOptions)&&!A(u,n.tabbableOptions)&&!f.nextTabbableNode(u,!1))&&(g=b),g>=0){var F=g===0?r.tabbableGroups.length-1:g-1,E=r.tabbableGroups[F];c=E.lastTabbableNode}}else{var C=te(r.tabbableGroups,function(P){var j=P.lastTabbableNode;return u===j});if(C<0&&(f.container===u||R(u,n.tabbableOptions)&&!A(u,n.tabbableOptions)&&!f.nextTabbableNode(u))&&(C=b),C>=0){var oe=C===r.tabbableGroups.length-1?0:C+1,ue=r.tabbableGroups[oe];c=ue.firstTabbableNode}}}else c=p("fallbackFocus");c&&(o.preventDefault(),y(c))},M=function(o){if(Fe(o)&&O(n.escapeDeactivates,o)!==!1){o.preventDefault(),s.deactivate();return}if(Se(o)){T(o);return}},q=function(o){var u=L(o);m(u)>=0||O(n.clickOutsideDeactivates,o)||O(n.allowOutsideClick,o)||(o.preventDefault(),o.stopImmediatePropagation())},H=function(){if(r.active)return J.activateTrap(s),r.delayInitialFocusTimer=n.delayInitialFocus?ee(function(){y(v())}):y(v()),a.addEventListener("focusin",D,!0),a.addEventListener("mousedown",S,{capture:!0,passive:!1}),a.addEventListener("touchstart",S,{capture:!0,passive:!1}),a.addEventListener("click",q,{capture:!0,passive:!1}),a.addEventListener("keydown",M,{capture:!0,passive:!1}),s},U=function(){if(r.active)return a.removeEventListener("focusin",D,!0),a.removeEventListener("mousedown",S,!0),a.removeEventListener("touchstart",S,!0),a.removeEventListener("click",q,!0),a.removeEventListener("keydown",M,!0),s};return s={get active(){return r.active},get paused(){return r.paused},activate:function(o){if(r.active)return this;var u=l(o,"onActivate"),c=l(o,"onPostActivate"),b=l(o,"checkCanFocusTrap");b||h(),r.active=!0,r.paused=!1,r.nodeFocusedBeforeActivation=a.activeElement,u&&u();var f=function(){b&&h(),H(),c&&c()};return b?(b(r.containers.concat()).then(f,f),this):(f(),this)},deactivate:function(o){if(!r.active)return this;var u=X({onDeactivate:n.onDeactivate,onPostDeactivate:n.onPostDeactivate,checkCanReturnFocus:n.checkCanReturnFocus},o);clearTimeout(r.delayInitialFocusTimer),r.delayInitialFocusTimer=void 0,U(),r.active=!1,r.paused=!1,J.deactivateTrap(s);var c=l(u,"onDeactivate"),b=l(u,"onPostDeactivate"),f=l(u,"checkCanReturnFocus"),g=l(u,"returnFocus","returnFocusOnDeactivate");c&&c();var F=function(){ee(function(){g&&y(w(r.nodeFocusedBeforeActivation)),b&&b()})};return g&&f?(f(w(r.nodeFocusedBeforeActivation)).then(F,F),this):(F(),this)},pause:function(){return r.paused||!r.active?this:(r.paused=!0,U(),this)},unpause:function(){return!r.paused||!r.active?this:(r.paused=!1,h(),H(),this)},updateContainerElements:function(o){var u=[].concat(o).filter(Boolean);return r.containers=u.map(function(c){return typeof c=="string"?a.querySelector(c):c}),r.active&&h(),this}},s.updateContainerElements(e),s};function ne(i){let e,t;window.addEventListener("focusin",()=>{e=t,t=document.activeElement}),i.magic("focus",a=>{let n=a;return{__noscroll:!1,__wrapAround:!1,within(r){return n=r,this},withoutScrolling(){return this.__noscroll=!0,this},noscroll(){return this.__noscroll=!0,this},withWrapAround(){return this.__wrapAround=!0,this},wrap(){return this.withWrapAround()},focusable(r){return R(r)},previouslyFocused(){return e},lastFocused(){return e},focused(){return t},focusables(){return Array.isArray(n)?n:x(n,{displayCheck:"none"})},all(){return this.focusables()},isFirst(r){let s=this.all();return s[0]&&s[0].isSameNode(r)},isLast(r){let s=this.all();return s.length&&s.slice(-1)[0].isSameNode(r)},getFirst(){return this.all()[0]},getLast(){return this.all().slice(-1)[0]},getNext(){let r=this.all(),s=document.activeElement;if(r.indexOf(s)!==-1)return this.__wrapAround&&r.indexOf(s)===r.length-1?r[0]:r[r.indexOf(s)+1]},getPrevious(){let r=this.all(),s=document.activeElement;if(r.indexOf(s)!==-1)return this.__wrapAround&&r.indexOf(s)===0?r.slice(-1)[0]:r[r.indexOf(s)-1]},first(){this.focus(this.getFirst())},last(){this.focus(this.getLast())},next(){this.focus(this.getNext())},previous(){this.focus(this.getPrevious())},prev(){return this.previous()},focus(r){r&&setTimeout(()=>{r.hasAttribute("tabindex")||r.setAttribute("tabindex","0"),r.focus({preventScroll:this.__noscroll})})}}}),i.directive("trap",i.skipDuringClone((a,{expression:n,modifiers:r},{effect:s,evaluateLater:l,cleanup:m})=>{let p=l(n),v=!1,h={escapeDeactivates:!1,allowOutsideClick:!0,fallbackFocus:()=>a};if(r.includes("noautofocus"))h.initialFocus=!1;else{let T=a.querySelector("[autofocus]");T&&(h.initialFocus=T)}let y=re(a,h),w=()=>{},S=()=>{},D=()=>{w(),w=()=>{},S(),S=()=>{},y.deactivate({returnFocus:!r.includes("noreturn")})};s(()=>p(T=>{v!==T&&(T&&!v&&(r.includes("noscroll")&&(S=Ee()),r.includes("inert")&&(w=ae(a)),setTimeout(()=>{y.activate()},15)),!T&&v&&D(),v=!!T)})),m(D)},(a,{expression:n,modifiers:r},{evaluate:s})=>{r.includes("inert")&&s(n)&&ae(a)}))}function ae(i){let e=[];return ie(i,t=>{let a=t.hasAttribute("aria-hidden");t.setAttribute("aria-hidden","true"),e.push(()=>a||t.removeAttribute("aria-hidden"))}),()=>{for(;e.length;)e.pop()()}}function ie(i,e){i.isSameNode(document.body)||!i.parentNode||Array.from(i.parentNode.children).forEach(t=>{t.isSameNode(i)?ie(i.parentNode,e):e(t)})}function Ee(){let i=document.documentElement.style.overflow,e=document.documentElement.style.paddingRight,t=window.innerWidth-document.documentElement.clientWidth;return document.documentElement.style.overflow="hidden",document.documentElement.style.paddingRight=`${t}px`,()=>{document.documentElement.style.overflow=i,document.documentElement.style.paddingRight=e}}document.addEventListener("alpine:init",()=>{window.Alpine.plugin(ne)});})(); -/*! Bundled license information: - -tabbable/dist/index.esm.js: - (*! - * tabbable 5.3.3 - * @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE - *) - -focus-trap/dist/focus-trap.esm.js: - (*! - * focus-trap 6.9.4 - * @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE - *) -*/ diff --git a/exo/tinychat/static/cdn.jsdelivr.net/npm/@alpinejs/intersect@3.x.x/dist/cdn.min.js b/exo/tinychat/static/cdn.jsdelivr.net/npm/@alpinejs/intersect@3.x.x/dist/cdn.min.js deleted file mode 100644 index 2342257f..00000000 --- a/exo/tinychat/static/cdn.jsdelivr.net/npm/@alpinejs/intersect@3.x.x/dist/cdn.min.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{function o(e){e.directive("intersect",e.skipDuringClone((t,{value:i,expression:l,modifiers:n},{evaluateLater:r,cleanup:c})=>{let s=r(l),a={rootMargin:x(n),threshold:f(n)},u=new IntersectionObserver(d=>{d.forEach(h=>{h.isIntersecting!==(i==="leave")&&(s(),n.includes("once")&&u.disconnect())})},a);u.observe(t),c(()=>{u.disconnect()})}))}function f(e){if(e.includes("full"))return .99;if(e.includes("half"))return .5;if(!e.includes("threshold"))return 0;let t=e[e.indexOf("threshold")+1];return t==="100"?1:t==="0"?0:Number(`.${t}`)}function p(e){let t=e.match(/^(-?[0-9]+)(px|%)?$/);return t?t[1]+(t[2]||"px"):void 0}function x(e){let t="margin",i="0px 0px 0px 0px",l=e.indexOf(t);if(l===-1)return i;let n=[];for(let r=1;r<5;r++)n.push(p(e[l+r]||""));return n=n.filter(r=>r!==void 0),n.length?n.join(" ").trim():i}document.addEventListener("alpine:init",()=>{window.Alpine.plugin(o)});})(); diff --git a/exo/tinychat/static/cdn.jsdelivr.net/npm/purecss@3.0.0/build/base-min.css b/exo/tinychat/static/cdn.jsdelivr.net/npm/purecss@3.0.0/build/base-min.css deleted file mode 100644 index df4fbc05..00000000 --- a/exo/tinychat/static/cdn.jsdelivr.net/npm/purecss@3.0.0/build/base-min.css +++ /dev/null @@ -1,11 +0,0 @@ -/*! -Pure v3.0.0 -Copyright 2013 Yahoo! -Licensed under the BSD License. -https://github.com/pure-css/pure/blob/master/LICENSE -*/ -/*! -normalize.css v | MIT License | https://necolas.github.io/normalize.css/ -Copyright (c) Nicolas Gallagher and Jonathan Neal -*/ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{font-family:sans-serif}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block} \ No newline at end of file diff --git a/exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css b/exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css deleted file mode 100644 index 45072b30..00000000 --- a/exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */ -.fa{font-family:var(--fa-style-family,"Font Awesome 6 Free");font-weight:var(--fa-style,900)}.fa,.fa-brands,.fa-classic,.fa-regular,.fa-sharp,.fa-solid,.fab,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display,inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fa-classic,.fa-regular,.fa-solid,.far,.fas{font-family:"Font Awesome 6 Free"}.fa-brands,.fab{font-family:"Font Awesome 6 Brands"}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07143em;vertical-align:.05357em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04167em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em)*-1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-radius:var(--fa-border-radius,.1em);border:var(--fa-border-width,.08em) var(--fa-border-style,solid) var(--fa-border-color,#eee);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{-webkit-animation-name:fa-beat;animation-name:fa-beat;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{-webkit-animation-name:fa-bounce;animation-name:fa-bounce;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{-webkit-animation-name:fa-fade;animation-name:fa-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{-webkit-animation-name:fa-beat-fade;animation-name:fa-beat-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{-webkit-animation-name:fa-flip;animation-name:fa-flip;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{-webkit-animation-name:fa-shake;animation-name:fa-shake;-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-duration:var(--fa-animation-duration,2s);animation-duration:var(--fa-animation-duration,2s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,steps(8));animation-timing-function:var(--fa-animation-timing,steps(8))}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-beat-fade,.fa-bounce,.fa-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{-webkit-animation-delay:-1ms;animation-delay:-1ms;-webkit-animation-duration:1ms;animation-duration:1ms;-webkit-animation-iteration-count:1;animation-iteration-count:1;-webkit-transition-delay:0s;transition-delay:0s;-webkit-transition-duration:0s;transition-duration:0s}}@-webkit-keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@-webkit-keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@-webkit-keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@-webkit-keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@-webkit-keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@-webkit-keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}.fa-rotate-by{-webkit-transform:rotate(var(--fa-rotate-angle,0));transform:rotate(var(--fa-rotate-angle,0))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)} - -.fa-0:before{content:"\30"}.fa-1:before{content:"\31"}.fa-2:before{content:"\32"}.fa-3:before{content:"\33"}.fa-4:before{content:"\34"}.fa-5:before{content:"\35"}.fa-6:before{content:"\36"}.fa-7:before{content:"\37"}.fa-8:before{content:"\38"}.fa-9:before{content:"\39"}.fa-fill-drip:before{content:"\f576"}.fa-arrows-to-circle:before{content:"\e4bd"}.fa-chevron-circle-right:before,.fa-circle-chevron-right:before{content:"\f138"}.fa-at:before{content:"\40"}.fa-trash-alt:before,.fa-trash-can:before{content:"\f2ed"}.fa-text-height:before{content:"\f034"}.fa-user-times:before,.fa-user-xmark:before{content:"\f235"}.fa-stethoscope:before{content:"\f0f1"}.fa-comment-alt:before,.fa-message:before{content:"\f27a"}.fa-info:before{content:"\f129"}.fa-compress-alt:before,.fa-down-left-and-up-right-to-center:before{content:"\f422"}.fa-explosion:before{content:"\e4e9"}.fa-file-alt:before,.fa-file-lines:before,.fa-file-text:before{content:"\f15c"}.fa-wave-square:before{content:"\f83e"}.fa-ring:before{content:"\f70b"}.fa-building-un:before{content:"\e4d9"}.fa-dice-three:before{content:"\f527"}.fa-calendar-alt:before,.fa-calendar-days:before{content:"\f073"}.fa-anchor-circle-check:before{content:"\e4aa"}.fa-building-circle-arrow-right:before{content:"\e4d1"}.fa-volleyball-ball:before,.fa-volleyball:before{content:"\f45f"}.fa-arrows-up-to-line:before{content:"\e4c2"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-circle-minus:before,.fa-minus-circle:before{content:"\f056"}.fa-door-open:before{content:"\f52b"}.fa-right-from-bracket:before,.fa-sign-out-alt:before{content:"\f2f5"}.fa-atom:before{content:"\f5d2"}.fa-soap:before{content:"\e06e"}.fa-heart-music-camera-bolt:before,.fa-icons:before{content:"\f86d"}.fa-microphone-alt-slash:before,.fa-microphone-lines-slash:before{content:"\f539"}.fa-bridge-circle-check:before{content:"\e4c9"}.fa-pump-medical:before{content:"\e06a"}.fa-fingerprint:before{content:"\f577"}.fa-hand-point-right:before{content:"\f0a4"}.fa-magnifying-glass-location:before,.fa-search-location:before{content:"\f689"}.fa-forward-step:before,.fa-step-forward:before{content:"\f051"}.fa-face-smile-beam:before,.fa-smile-beam:before{content:"\f5b8"}.fa-flag-checkered:before{content:"\f11e"}.fa-football-ball:before,.fa-football:before{content:"\f44e"}.fa-school-circle-exclamation:before{content:"\e56c"}.fa-crop:before{content:"\f125"}.fa-angle-double-down:before,.fa-angles-down:before{content:"\f103"}.fa-users-rectangle:before{content:"\e594"}.fa-people-roof:before{content:"\e537"}.fa-people-line:before{content:"\e534"}.fa-beer-mug-empty:before,.fa-beer:before{content:"\f0fc"}.fa-diagram-predecessor:before{content:"\e477"}.fa-arrow-up-long:before,.fa-long-arrow-up:before{content:"\f176"}.fa-burn:before,.fa-fire-flame-simple:before{content:"\f46a"}.fa-male:before,.fa-person:before{content:"\f183"}.fa-laptop:before{content:"\f109"}.fa-file-csv:before{content:"\f6dd"}.fa-menorah:before{content:"\f676"}.fa-truck-plane:before{content:"\e58f"}.fa-record-vinyl:before{content:"\f8d9"}.fa-face-grin-stars:before,.fa-grin-stars:before{content:"\f587"}.fa-bong:before{content:"\f55c"}.fa-pastafarianism:before,.fa-spaghetti-monster-flying:before{content:"\f67b"}.fa-arrow-down-up-across-line:before{content:"\e4af"}.fa-spoon:before,.fa-utensil-spoon:before{content:"\f2e5"}.fa-jar-wheat:before{content:"\e517"}.fa-envelopes-bulk:before,.fa-mail-bulk:before{content:"\f674"}.fa-file-circle-exclamation:before{content:"\e4eb"}.fa-circle-h:before,.fa-hospital-symbol:before{content:"\f47e"}.fa-pager:before{content:"\f815"}.fa-address-book:before,.fa-contact-book:before{content:"\f2b9"}.fa-strikethrough:before{content:"\f0cc"}.fa-k:before{content:"\4b"}.fa-landmark-flag:before{content:"\e51c"}.fa-pencil-alt:before,.fa-pencil:before{content:"\f303"}.fa-backward:before{content:"\f04a"}.fa-caret-right:before{content:"\f0da"}.fa-comments:before{content:"\f086"}.fa-file-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-code-pull-request:before{content:"\e13c"}.fa-clipboard-list:before{content:"\f46d"}.fa-truck-loading:before,.fa-truck-ramp-box:before{content:"\f4de"}.fa-user-check:before{content:"\f4fc"}.fa-vial-virus:before{content:"\e597"}.fa-sheet-plastic:before{content:"\e571"}.fa-blog:before{content:"\f781"}.fa-user-ninja:before{content:"\f504"}.fa-person-arrow-up-from-line:before{content:"\e539"}.fa-scroll-torah:before,.fa-torah:before{content:"\f6a0"}.fa-broom-ball:before,.fa-quidditch-broom-ball:before,.fa-quidditch:before{content:"\f458"}.fa-toggle-off:before{content:"\f204"}.fa-archive:before,.fa-box-archive:before{content:"\f187"}.fa-person-drowning:before{content:"\e545"}.fa-arrow-down-9-1:before,.fa-sort-numeric-desc:before,.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-face-grin-tongue-squint:before,.fa-grin-tongue-squint:before{content:"\f58a"}.fa-spray-can:before{content:"\f5bd"}.fa-truck-monster:before{content:"\f63b"}.fa-w:before{content:"\57"}.fa-earth-africa:before,.fa-globe-africa:before{content:"\f57c"}.fa-rainbow:before{content:"\f75b"}.fa-circle-notch:before{content:"\f1ce"}.fa-tablet-alt:before,.fa-tablet-screen-button:before{content:"\f3fa"}.fa-paw:before{content:"\f1b0"}.fa-cloud:before{content:"\f0c2"}.fa-trowel-bricks:before{content:"\e58a"}.fa-face-flushed:before,.fa-flushed:before{content:"\f579"}.fa-hospital-user:before{content:"\f80d"}.fa-tent-arrow-left-right:before{content:"\e57f"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-binoculars:before{content:"\f1e5"}.fa-microphone-slash:before{content:"\f131"}.fa-box-tissue:before{content:"\e05b"}.fa-motorcycle:before{content:"\f21c"}.fa-bell-concierge:before,.fa-concierge-bell:before{content:"\f562"}.fa-pen-ruler:before,.fa-pencil-ruler:before{content:"\f5ae"}.fa-people-arrows-left-right:before,.fa-people-arrows:before{content:"\e068"}.fa-mars-and-venus-burst:before{content:"\e523"}.fa-caret-square-right:before,.fa-square-caret-right:before{content:"\f152"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-sun-plant-wilt:before{content:"\e57a"}.fa-toilets-portable:before{content:"\e584"}.fa-hockey-puck:before{content:"\f453"}.fa-table:before{content:"\f0ce"}.fa-magnifying-glass-arrow-right:before{content:"\e521"}.fa-digital-tachograph:before,.fa-tachograph-digital:before{content:"\f566"}.fa-users-slash:before{content:"\e073"}.fa-clover:before{content:"\e139"}.fa-mail-reply:before,.fa-reply:before{content:"\f3e5"}.fa-star-and-crescent:before{content:"\f699"}.fa-house-fire:before{content:"\e50c"}.fa-minus-square:before,.fa-square-minus:before{content:"\f146"}.fa-helicopter:before{content:"\f533"}.fa-compass:before{content:"\f14e"}.fa-caret-square-down:before,.fa-square-caret-down:before{content:"\f150"}.fa-file-circle-question:before{content:"\e4ef"}.fa-laptop-code:before{content:"\f5fc"}.fa-swatchbook:before{content:"\f5c3"}.fa-prescription-bottle:before{content:"\f485"}.fa-bars:before,.fa-navicon:before{content:"\f0c9"}.fa-people-group:before{content:"\e533"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-heart-broken:before,.fa-heart-crack:before{content:"\f7a9"}.fa-external-link-square-alt:before,.fa-square-up-right:before{content:"\f360"}.fa-face-kiss-beam:before,.fa-kiss-beam:before{content:"\f597"}.fa-film:before{content:"\f008"}.fa-ruler-horizontal:before{content:"\f547"}.fa-people-robbery:before{content:"\e536"}.fa-lightbulb:before{content:"\f0eb"}.fa-caret-left:before{content:"\f0d9"}.fa-circle-exclamation:before,.fa-exclamation-circle:before{content:"\f06a"}.fa-school-circle-xmark:before{content:"\e56d"}.fa-arrow-right-from-bracket:before,.fa-sign-out:before{content:"\f08b"}.fa-chevron-circle-down:before,.fa-circle-chevron-down:before{content:"\f13a"}.fa-unlock-alt:before,.fa-unlock-keyhole:before{content:"\f13e"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-headphones-alt:before,.fa-headphones-simple:before{content:"\f58f"}.fa-sitemap:before{content:"\f0e8"}.fa-circle-dollar-to-slot:before,.fa-donate:before{content:"\f4b9"}.fa-memory:before{content:"\f538"}.fa-road-spikes:before{content:"\e568"}.fa-fire-burner:before{content:"\e4f1"}.fa-flag:before{content:"\f024"}.fa-hanukiah:before{content:"\f6e6"}.fa-feather:before{content:"\f52d"}.fa-volume-down:before,.fa-volume-low:before{content:"\f027"}.fa-comment-slash:before{content:"\f4b3"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-compress:before{content:"\f066"}.fa-wheat-alt:before,.fa-wheat-awn:before{content:"\e2cd"}.fa-ankh:before{content:"\f644"}.fa-hands-holding-child:before{content:"\e4fa"}.fa-asterisk:before{content:"\2a"}.fa-check-square:before,.fa-square-check:before{content:"\f14a"}.fa-peseta-sign:before{content:"\e221"}.fa-header:before,.fa-heading:before{content:"\f1dc"}.fa-ghost:before{content:"\f6e2"}.fa-list-squares:before,.fa-list:before{content:"\f03a"}.fa-phone-square-alt:before,.fa-square-phone-flip:before{content:"\f87b"}.fa-cart-plus:before{content:"\f217"}.fa-gamepad:before{content:"\f11b"}.fa-circle-dot:before,.fa-dot-circle:before{content:"\f192"}.fa-dizzy:before,.fa-face-dizzy:before{content:"\f567"}.fa-egg:before{content:"\f7fb"}.fa-house-medical-circle-xmark:before{content:"\e513"}.fa-campground:before{content:"\f6bb"}.fa-folder-plus:before{content:"\f65e"}.fa-futbol-ball:before,.fa-futbol:before,.fa-soccer-ball:before{content:"\f1e3"}.fa-paint-brush:before,.fa-paintbrush:before{content:"\f1fc"}.fa-lock:before{content:"\f023"}.fa-gas-pump:before{content:"\f52f"}.fa-hot-tub-person:before,.fa-hot-tub:before{content:"\f593"}.fa-map-location:before,.fa-map-marked:before{content:"\f59f"}.fa-house-flood-water:before{content:"\e50e"}.fa-tree:before{content:"\f1bb"}.fa-bridge-lock:before{content:"\e4cc"}.fa-sack-dollar:before{content:"\f81d"}.fa-edit:before,.fa-pen-to-square:before{content:"\f044"}.fa-car-side:before{content:"\f5e4"}.fa-share-alt:before,.fa-share-nodes:before{content:"\f1e0"}.fa-heart-circle-minus:before{content:"\e4ff"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-microscope:before{content:"\f610"}.fa-sink:before{content:"\e06d"}.fa-bag-shopping:before,.fa-shopping-bag:before{content:"\f290"}.fa-arrow-down-z-a:before,.fa-sort-alpha-desc:before,.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-mitten:before{content:"\f7b5"}.fa-person-rays:before{content:"\e54d"}.fa-users:before{content:"\f0c0"}.fa-eye-slash:before{content:"\f070"}.fa-flask-vial:before{content:"\e4f3"}.fa-hand-paper:before,.fa-hand:before{content:"\f256"}.fa-om:before{content:"\f679"}.fa-worm:before{content:"\e599"}.fa-house-circle-xmark:before{content:"\e50b"}.fa-plug:before{content:"\f1e6"}.fa-chevron-up:before{content:"\f077"}.fa-hand-spock:before{content:"\f259"}.fa-stopwatch:before{content:"\f2f2"}.fa-face-kiss:before,.fa-kiss:before{content:"\f596"}.fa-bridge-circle-xmark:before{content:"\e4cb"}.fa-face-grin-tongue:before,.fa-grin-tongue:before{content:"\f589"}.fa-chess-bishop:before{content:"\f43a"}.fa-face-grin-wink:before,.fa-grin-wink:before{content:"\f58c"}.fa-deaf:before,.fa-deafness:before,.fa-ear-deaf:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-road-circle-check:before{content:"\e564"}.fa-dice-five:before{content:"\f523"}.fa-rss-square:before,.fa-square-rss:before{content:"\f143"}.fa-land-mine-on:before{content:"\e51b"}.fa-i-cursor:before{content:"\f246"}.fa-stamp:before{content:"\f5bf"}.fa-stairs:before{content:"\e289"}.fa-i:before{content:"\49"}.fa-hryvnia-sign:before,.fa-hryvnia:before{content:"\f6f2"}.fa-pills:before{content:"\f484"}.fa-face-grin-wide:before,.fa-grin-alt:before{content:"\f581"}.fa-tooth:before{content:"\f5c9"}.fa-v:before{content:"\56"}.fa-bangladeshi-taka-sign:before{content:"\e2e6"}.fa-bicycle:before{content:"\f206"}.fa-rod-asclepius:before,.fa-rod-snake:before,.fa-staff-aesculapius:before,.fa-staff-snake:before{content:"\e579"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-ambulance:before,.fa-truck-medical:before{content:"\f0f9"}.fa-wheat-awn-circle-exclamation:before{content:"\e598"}.fa-snowman:before{content:"\f7d0"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-road-barrier:before{content:"\e562"}.fa-school:before{content:"\f549"}.fa-igloo:before{content:"\f7ae"}.fa-joint:before{content:"\f595"}.fa-angle-right:before{content:"\f105"}.fa-horse:before{content:"\f6f0"}.fa-q:before{content:"\51"}.fa-g:before{content:"\47"}.fa-notes-medical:before{content:"\f481"}.fa-temperature-2:before,.fa-temperature-half:before,.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-dong-sign:before{content:"\e169"}.fa-capsules:before{content:"\f46b"}.fa-poo-bolt:before,.fa-poo-storm:before{content:"\f75a"}.fa-face-frown-open:before,.fa-frown-open:before{content:"\f57a"}.fa-hand-point-up:before{content:"\f0a6"}.fa-money-bill:before{content:"\f0d6"}.fa-bookmark:before{content:"\f02e"}.fa-align-justify:before{content:"\f039"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-helmet-un:before{content:"\e503"}.fa-bullseye:before{content:"\f140"}.fa-bacon:before{content:"\f7e5"}.fa-hand-point-down:before{content:"\f0a7"}.fa-arrow-up-from-bracket:before{content:"\e09a"}.fa-folder-blank:before,.fa-folder:before{content:"\f07b"}.fa-file-medical-alt:before,.fa-file-waveform:before{content:"\f478"}.fa-radiation:before{content:"\f7b9"}.fa-chart-simple:before{content:"\e473"}.fa-mars-stroke:before{content:"\f229"}.fa-vial:before{content:"\f492"}.fa-dashboard:before,.fa-gauge-med:before,.fa-gauge:before,.fa-tachometer-alt-average:before{content:"\f624"}.fa-magic-wand-sparkles:before,.fa-wand-magic-sparkles:before{content:"\e2ca"}.fa-e:before{content:"\45"}.fa-pen-alt:before,.fa-pen-clip:before{content:"\f305"}.fa-bridge-circle-exclamation:before{content:"\e4ca"}.fa-user:before{content:"\f007"}.fa-school-circle-check:before{content:"\e56b"}.fa-dumpster:before{content:"\f793"}.fa-shuttle-van:before,.fa-van-shuttle:before{content:"\f5b6"}.fa-building-user:before{content:"\e4da"}.fa-caret-square-left:before,.fa-square-caret-left:before{content:"\f191"}.fa-highlighter:before{content:"\f591"}.fa-key:before{content:"\f084"}.fa-bullhorn:before{content:"\f0a1"}.fa-globe:before{content:"\f0ac"}.fa-synagogue:before{content:"\f69b"}.fa-person-half-dress:before{content:"\e548"}.fa-road-bridge:before{content:"\e563"}.fa-location-arrow:before{content:"\f124"}.fa-c:before{content:"\43"}.fa-tablet-button:before{content:"\f10a"}.fa-building-lock:before{content:"\e4d6"}.fa-pizza-slice:before{content:"\f818"}.fa-money-bill-wave:before{content:"\f53a"}.fa-area-chart:before,.fa-chart-area:before{content:"\f1fe"}.fa-house-flag:before{content:"\e50d"}.fa-person-circle-minus:before{content:"\e540"}.fa-ban:before,.fa-cancel:before{content:"\f05e"}.fa-camera-rotate:before{content:"\e0d8"}.fa-air-freshener:before,.fa-spray-can-sparkles:before{content:"\f5d0"}.fa-star:before{content:"\f005"}.fa-repeat:before{content:"\f363"}.fa-cross:before{content:"\f654"}.fa-box:before{content:"\f466"}.fa-venus-mars:before{content:"\f228"}.fa-arrow-pointer:before,.fa-mouse-pointer:before{content:"\f245"}.fa-expand-arrows-alt:before,.fa-maximize:before{content:"\f31e"}.fa-charging-station:before{content:"\f5e7"}.fa-shapes:before,.fa-triangle-circle-square:before{content:"\f61f"}.fa-random:before,.fa-shuffle:before{content:"\f074"}.fa-person-running:before,.fa-running:before{content:"\f70c"}.fa-mobile-retro:before{content:"\e527"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-spider:before{content:"\f717"}.fa-hands-bound:before{content:"\e4f9"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-plane-circle-exclamation:before{content:"\e556"}.fa-x-ray:before{content:"\f497"}.fa-spell-check:before{content:"\f891"}.fa-slash:before{content:"\f715"}.fa-computer-mouse:before,.fa-mouse:before{content:"\f8cc"}.fa-arrow-right-to-bracket:before,.fa-sign-in:before{content:"\f090"}.fa-shop-slash:before,.fa-store-alt-slash:before{content:"\e070"}.fa-server:before{content:"\f233"}.fa-virus-covid-slash:before{content:"\e4a9"}.fa-shop-lock:before{content:"\e4a5"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-blender-phone:before{content:"\f6b6"}.fa-building-wheat:before{content:"\e4db"}.fa-person-breastfeeding:before{content:"\e53a"}.fa-right-to-bracket:before,.fa-sign-in-alt:before{content:"\f2f6"}.fa-venus:before{content:"\f221"}.fa-passport:before{content:"\f5ab"}.fa-heart-pulse:before,.fa-heartbeat:before{content:"\f21e"}.fa-people-carry-box:before,.fa-people-carry:before{content:"\f4ce"}.fa-temperature-high:before{content:"\f769"}.fa-microchip:before{content:"\f2db"}.fa-crown:before{content:"\f521"}.fa-weight-hanging:before{content:"\f5cd"}.fa-xmarks-lines:before{content:"\e59a"}.fa-file-prescription:before{content:"\f572"}.fa-weight-scale:before,.fa-weight:before{content:"\f496"}.fa-user-friends:before,.fa-user-group:before{content:"\f500"}.fa-arrow-up-a-z:before,.fa-sort-alpha-up:before{content:"\f15e"}.fa-chess-knight:before{content:"\f441"}.fa-face-laugh-squint:before,.fa-laugh-squint:before{content:"\f59b"}.fa-wheelchair:before{content:"\f193"}.fa-arrow-circle-up:before,.fa-circle-arrow-up:before{content:"\f0aa"}.fa-toggle-on:before{content:"\f205"}.fa-person-walking:before,.fa-walking:before{content:"\f554"}.fa-l:before{content:"\4c"}.fa-fire:before{content:"\f06d"}.fa-bed-pulse:before,.fa-procedures:before{content:"\f487"}.fa-shuttle-space:before,.fa-space-shuttle:before{content:"\f197"}.fa-face-laugh:before,.fa-laugh:before{content:"\f599"}.fa-folder-open:before{content:"\f07c"}.fa-heart-circle-plus:before{content:"\e500"}.fa-code-fork:before{content:"\e13b"}.fa-city:before{content:"\f64f"}.fa-microphone-alt:before,.fa-microphone-lines:before{content:"\f3c9"}.fa-pepper-hot:before{content:"\f816"}.fa-unlock:before{content:"\f09c"}.fa-colon-sign:before{content:"\e140"}.fa-headset:before{content:"\f590"}.fa-store-slash:before{content:"\e071"}.fa-road-circle-xmark:before{content:"\e566"}.fa-user-minus:before{content:"\f503"}.fa-mars-stroke-up:before,.fa-mars-stroke-v:before{content:"\f22a"}.fa-champagne-glasses:before,.fa-glass-cheers:before{content:"\f79f"}.fa-clipboard:before{content:"\f328"}.fa-house-circle-exclamation:before{content:"\e50a"}.fa-file-arrow-up:before,.fa-file-upload:before{content:"\f574"}.fa-wifi-3:before,.fa-wifi-strong:before,.fa-wifi:before{content:"\f1eb"}.fa-bath:before,.fa-bathtub:before{content:"\f2cd"}.fa-underline:before{content:"\f0cd"}.fa-user-edit:before,.fa-user-pen:before{content:"\f4ff"}.fa-signature:before{content:"\f5b7"}.fa-stroopwafel:before{content:"\f551"}.fa-bold:before{content:"\f032"}.fa-anchor-lock:before{content:"\e4ad"}.fa-building-ngo:before{content:"\e4d7"}.fa-manat-sign:before{content:"\e1d5"}.fa-not-equal:before{content:"\f53e"}.fa-border-style:before,.fa-border-top-left:before{content:"\f853"}.fa-map-location-dot:before,.fa-map-marked-alt:before{content:"\f5a0"}.fa-jedi:before{content:"\f669"}.fa-poll:before,.fa-square-poll-vertical:before{content:"\f681"}.fa-mug-hot:before{content:"\f7b6"}.fa-battery-car:before,.fa-car-battery:before{content:"\f5df"}.fa-gift:before{content:"\f06b"}.fa-dice-two:before{content:"\f528"}.fa-chess-queen:before{content:"\f445"}.fa-glasses:before{content:"\f530"}.fa-chess-board:before{content:"\f43c"}.fa-building-circle-check:before{content:"\e4d2"}.fa-person-chalkboard:before{content:"\e53d"}.fa-mars-stroke-h:before,.fa-mars-stroke-right:before{content:"\f22b"}.fa-hand-back-fist:before,.fa-hand-rock:before{content:"\f255"}.fa-caret-square-up:before,.fa-square-caret-up:before{content:"\f151"}.fa-cloud-showers-water:before{content:"\e4e4"}.fa-bar-chart:before,.fa-chart-bar:before{content:"\f080"}.fa-hands-bubbles:before,.fa-hands-wash:before{content:"\e05e"}.fa-less-than-equal:before{content:"\f537"}.fa-train:before{content:"\f238"}.fa-eye-low-vision:before,.fa-low-vision:before{content:"\f2a8"}.fa-crow:before{content:"\f520"}.fa-sailboat:before{content:"\e445"}.fa-window-restore:before{content:"\f2d2"}.fa-plus-square:before,.fa-square-plus:before{content:"\f0fe"}.fa-torii-gate:before{content:"\f6a1"}.fa-frog:before{content:"\f52e"}.fa-bucket:before{content:"\e4cf"}.fa-image:before{content:"\f03e"}.fa-microphone:before{content:"\f130"}.fa-cow:before{content:"\f6c8"}.fa-caret-up:before{content:"\f0d8"}.fa-screwdriver:before{content:"\f54a"}.fa-folder-closed:before{content:"\e185"}.fa-house-tsunami:before{content:"\e515"}.fa-square-nfi:before{content:"\e576"}.fa-arrow-up-from-ground-water:before{content:"\e4b5"}.fa-glass-martini-alt:before,.fa-martini-glass:before{content:"\f57b"}.fa-rotate-back:before,.fa-rotate-backward:before,.fa-rotate-left:before,.fa-undo-alt:before{content:"\f2ea"}.fa-columns:before,.fa-table-columns:before{content:"\f0db"}.fa-lemon:before{content:"\f094"}.fa-head-side-mask:before{content:"\e063"}.fa-handshake:before{content:"\f2b5"}.fa-gem:before{content:"\f3a5"}.fa-dolly-box:before,.fa-dolly:before{content:"\f472"}.fa-smoking:before{content:"\f48d"}.fa-compress-arrows-alt:before,.fa-minimize:before{content:"\f78c"}.fa-monument:before{content:"\f5a6"}.fa-snowplow:before{content:"\f7d2"}.fa-angle-double-right:before,.fa-angles-right:before{content:"\f101"}.fa-cannabis:before{content:"\f55f"}.fa-circle-play:before,.fa-play-circle:before{content:"\f144"}.fa-tablets:before{content:"\f490"}.fa-ethernet:before{content:"\f796"}.fa-eur:before,.fa-euro-sign:before,.fa-euro:before{content:"\f153"}.fa-chair:before{content:"\f6c0"}.fa-check-circle:before,.fa-circle-check:before{content:"\f058"}.fa-circle-stop:before,.fa-stop-circle:before{content:"\f28d"}.fa-compass-drafting:before,.fa-drafting-compass:before{content:"\f568"}.fa-plate-wheat:before{content:"\e55a"}.fa-icicles:before{content:"\f7ad"}.fa-person-shelter:before{content:"\e54f"}.fa-neuter:before{content:"\f22c"}.fa-id-badge:before{content:"\f2c1"}.fa-marker:before{content:"\f5a1"}.fa-face-laugh-beam:before,.fa-laugh-beam:before{content:"\f59a"}.fa-helicopter-symbol:before{content:"\e502"}.fa-universal-access:before{content:"\f29a"}.fa-chevron-circle-up:before,.fa-circle-chevron-up:before{content:"\f139"}.fa-lari-sign:before{content:"\e1c8"}.fa-volcano:before{content:"\f770"}.fa-person-walking-dashed-line-arrow-right:before{content:"\e553"}.fa-gbp:before,.fa-pound-sign:before,.fa-sterling-sign:before{content:"\f154"}.fa-viruses:before{content:"\e076"}.fa-square-person-confined:before{content:"\e577"}.fa-user-tie:before{content:"\f508"}.fa-arrow-down-long:before,.fa-long-arrow-down:before{content:"\f175"}.fa-tent-arrow-down-to-line:before{content:"\e57e"}.fa-certificate:before{content:"\f0a3"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-suitcase:before{content:"\f0f2"}.fa-person-skating:before,.fa-skating:before{content:"\f7c5"}.fa-filter-circle-dollar:before,.fa-funnel-dollar:before{content:"\f662"}.fa-camera-retro:before{content:"\f083"}.fa-arrow-circle-down:before,.fa-circle-arrow-down:before{content:"\f0ab"}.fa-arrow-right-to-file:before,.fa-file-import:before{content:"\f56f"}.fa-external-link-square:before,.fa-square-arrow-up-right:before{content:"\f14c"}.fa-box-open:before{content:"\f49e"}.fa-scroll:before{content:"\f70e"}.fa-spa:before{content:"\f5bb"}.fa-location-pin-lock:before{content:"\e51f"}.fa-pause:before{content:"\f04c"}.fa-hill-avalanche:before{content:"\e507"}.fa-temperature-0:before,.fa-temperature-empty:before,.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-bomb:before{content:"\f1e2"}.fa-registered:before{content:"\f25d"}.fa-address-card:before,.fa-contact-card:before,.fa-vcard:before{content:"\f2bb"}.fa-balance-scale-right:before,.fa-scale-unbalanced-flip:before{content:"\f516"}.fa-subscript:before{content:"\f12c"}.fa-diamond-turn-right:before,.fa-directions:before{content:"\f5eb"}.fa-burst:before{content:"\e4dc"}.fa-house-laptop:before,.fa-laptop-house:before{content:"\e066"}.fa-face-tired:before,.fa-tired:before{content:"\f5c8"}.fa-money-bills:before{content:"\e1f3"}.fa-smog:before{content:"\f75f"}.fa-crutch:before{content:"\f7f7"}.fa-cloud-arrow-up:before,.fa-cloud-upload-alt:before,.fa-cloud-upload:before{content:"\f0ee"}.fa-palette:before{content:"\f53f"}.fa-arrows-turn-right:before{content:"\e4c0"}.fa-vest:before{content:"\e085"}.fa-ferry:before{content:"\e4ea"}.fa-arrows-down-to-people:before{content:"\e4b9"}.fa-seedling:before,.fa-sprout:before{content:"\f4d8"}.fa-arrows-alt-h:before,.fa-left-right:before{content:"\f337"}.fa-boxes-packing:before{content:"\e4c7"}.fa-arrow-circle-left:before,.fa-circle-arrow-left:before{content:"\f0a8"}.fa-group-arrows-rotate:before{content:"\e4f6"}.fa-bowl-food:before{content:"\e4c6"}.fa-candy-cane:before{content:"\f786"}.fa-arrow-down-wide-short:before,.fa-sort-amount-asc:before,.fa-sort-amount-down:before{content:"\f160"}.fa-cloud-bolt:before,.fa-thunderstorm:before{content:"\f76c"}.fa-remove-format:before,.fa-text-slash:before{content:"\f87d"}.fa-face-smile-wink:before,.fa-smile-wink:before{content:"\f4da"}.fa-file-word:before{content:"\f1c2"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-arrows-h:before,.fa-arrows-left-right:before{content:"\f07e"}.fa-house-lock:before{content:"\e510"}.fa-cloud-arrow-down:before,.fa-cloud-download-alt:before,.fa-cloud-download:before{content:"\f0ed"}.fa-children:before{content:"\e4e1"}.fa-blackboard:before,.fa-chalkboard:before{content:"\f51b"}.fa-user-alt-slash:before,.fa-user-large-slash:before{content:"\f4fa"}.fa-envelope-open:before{content:"\f2b6"}.fa-handshake-alt-slash:before,.fa-handshake-simple-slash:before{content:"\e05f"}.fa-mattress-pillow:before{content:"\e525"}.fa-guarani-sign:before{content:"\e19a"}.fa-arrows-rotate:before,.fa-refresh:before,.fa-sync:before{content:"\f021"}.fa-fire-extinguisher:before{content:"\f134"}.fa-cruzeiro-sign:before{content:"\e152"}.fa-greater-than-equal:before{content:"\f532"}.fa-shield-alt:before,.fa-shield-halved:before{content:"\f3ed"}.fa-atlas:before,.fa-book-atlas:before{content:"\f558"}.fa-virus:before{content:"\e074"}.fa-envelope-circle-check:before{content:"\e4e8"}.fa-layer-group:before{content:"\f5fd"}.fa-arrows-to-dot:before{content:"\e4be"}.fa-archway:before{content:"\f557"}.fa-heart-circle-check:before{content:"\e4fd"}.fa-house-chimney-crack:before,.fa-house-damage:before{content:"\f6f1"}.fa-file-archive:before,.fa-file-zipper:before{content:"\f1c6"}.fa-square:before{content:"\f0c8"}.fa-glass-martini:before,.fa-martini-glass-empty:before{content:"\f000"}.fa-couch:before{content:"\f4b8"}.fa-cedi-sign:before{content:"\e0df"}.fa-italic:before{content:"\f033"}.fa-table-cells-column-lock:before{content:"\e678"}.fa-church:before{content:"\f51d"}.fa-comments-dollar:before{content:"\f653"}.fa-democrat:before{content:"\f747"}.fa-z:before{content:"\5a"}.fa-person-skiing:before,.fa-skiing:before{content:"\f7c9"}.fa-road-lock:before{content:"\e567"}.fa-a:before{content:"\41"}.fa-temperature-arrow-down:before,.fa-temperature-down:before{content:"\e03f"}.fa-feather-alt:before,.fa-feather-pointed:before{content:"\f56b"}.fa-p:before{content:"\50"}.fa-snowflake:before{content:"\f2dc"}.fa-newspaper:before{content:"\f1ea"}.fa-ad:before,.fa-rectangle-ad:before{content:"\f641"}.fa-arrow-circle-right:before,.fa-circle-arrow-right:before{content:"\f0a9"}.fa-filter-circle-xmark:before{content:"\e17b"}.fa-locust:before{content:"\e520"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-list-1-2:before,.fa-list-numeric:before,.fa-list-ol:before{content:"\f0cb"}.fa-person-dress-burst:before{content:"\e544"}.fa-money-check-alt:before,.fa-money-check-dollar:before{content:"\f53d"}.fa-vector-square:before{content:"\f5cb"}.fa-bread-slice:before{content:"\f7ec"}.fa-language:before{content:"\f1ab"}.fa-face-kiss-wink-heart:before,.fa-kiss-wink-heart:before{content:"\f598"}.fa-filter:before{content:"\f0b0"}.fa-question:before{content:"\3f"}.fa-file-signature:before{content:"\f573"}.fa-arrows-alt:before,.fa-up-down-left-right:before{content:"\f0b2"}.fa-house-chimney-user:before{content:"\e065"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-puzzle-piece:before{content:"\f12e"}.fa-money-check:before{content:"\f53c"}.fa-star-half-alt:before,.fa-star-half-stroke:before{content:"\f5c0"}.fa-code:before{content:"\f121"}.fa-glass-whiskey:before,.fa-whiskey-glass:before{content:"\f7a0"}.fa-building-circle-exclamation:before{content:"\e4d3"}.fa-magnifying-glass-chart:before{content:"\e522"}.fa-arrow-up-right-from-square:before,.fa-external-link:before{content:"\f08e"}.fa-cubes-stacked:before{content:"\e4e6"}.fa-krw:before,.fa-won-sign:before,.fa-won:before{content:"\f159"}.fa-virus-covid:before{content:"\e4a8"}.fa-austral-sign:before{content:"\e0a9"}.fa-f:before{content:"\46"}.fa-leaf:before{content:"\f06c"}.fa-road:before{content:"\f018"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-person-circle-plus:before{content:"\e541"}.fa-chart-pie:before,.fa-pie-chart:before{content:"\f200"}.fa-bolt-lightning:before{content:"\e0b7"}.fa-sack-xmark:before{content:"\e56a"}.fa-file-excel:before{content:"\f1c3"}.fa-file-contract:before{content:"\f56c"}.fa-fish-fins:before{content:"\e4f2"}.fa-building-flag:before{content:"\e4d5"}.fa-face-grin-beam:before,.fa-grin-beam:before{content:"\f582"}.fa-object-ungroup:before{content:"\f248"}.fa-poop:before{content:"\f619"}.fa-location-pin:before,.fa-map-marker:before{content:"\f041"}.fa-kaaba:before{content:"\f66b"}.fa-toilet-paper:before{content:"\f71e"}.fa-hard-hat:before,.fa-hat-hard:before,.fa-helmet-safety:before{content:"\f807"}.fa-eject:before{content:"\f052"}.fa-arrow-alt-circle-right:before,.fa-circle-right:before{content:"\f35a"}.fa-plane-circle-check:before{content:"\e555"}.fa-face-rolling-eyes:before,.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-object-group:before{content:"\f247"}.fa-chart-line:before,.fa-line-chart:before{content:"\f201"}.fa-mask-ventilator:before{content:"\e524"}.fa-arrow-right:before{content:"\f061"}.fa-map-signs:before,.fa-signs-post:before{content:"\f277"}.fa-cash-register:before{content:"\f788"}.fa-person-circle-question:before{content:"\e542"}.fa-h:before{content:"\48"}.fa-tarp:before{content:"\e57b"}.fa-screwdriver-wrench:before,.fa-tools:before{content:"\f7d9"}.fa-arrows-to-eye:before{content:"\e4bf"}.fa-plug-circle-bolt:before{content:"\e55b"}.fa-heart:before{content:"\f004"}.fa-mars-and-venus:before{content:"\f224"}.fa-home-user:before,.fa-house-user:before{content:"\e1b0"}.fa-dumpster-fire:before{content:"\f794"}.fa-house-crack:before{content:"\e3b1"}.fa-cocktail:before,.fa-martini-glass-citrus:before{content:"\f561"}.fa-face-surprise:before,.fa-surprise:before{content:"\f5c2"}.fa-bottle-water:before{content:"\e4c5"}.fa-circle-pause:before,.fa-pause-circle:before{content:"\f28b"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-apple-alt:before,.fa-apple-whole:before{content:"\f5d1"}.fa-kitchen-set:before{content:"\e51a"}.fa-r:before{content:"\52"}.fa-temperature-1:before,.fa-temperature-quarter:before,.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-cube:before{content:"\f1b2"}.fa-bitcoin-sign:before{content:"\e0b4"}.fa-shield-dog:before{content:"\e573"}.fa-solar-panel:before{content:"\f5ba"}.fa-lock-open:before{content:"\f3c1"}.fa-elevator:before{content:"\e16d"}.fa-money-bill-transfer:before{content:"\e528"}.fa-money-bill-trend-up:before{content:"\e529"}.fa-house-flood-water-circle-arrow-right:before{content:"\e50f"}.fa-poll-h:before,.fa-square-poll-horizontal:before{content:"\f682"}.fa-circle:before{content:"\f111"}.fa-backward-fast:before,.fa-fast-backward:before{content:"\f049"}.fa-recycle:before{content:"\f1b8"}.fa-user-astronaut:before{content:"\f4fb"}.fa-plane-slash:before{content:"\e069"}.fa-trademark:before{content:"\f25c"}.fa-basketball-ball:before,.fa-basketball:before{content:"\f434"}.fa-satellite-dish:before{content:"\f7c0"}.fa-arrow-alt-circle-up:before,.fa-circle-up:before{content:"\f35b"}.fa-mobile-alt:before,.fa-mobile-screen-button:before{content:"\f3cd"}.fa-volume-high:before,.fa-volume-up:before{content:"\f028"}.fa-users-rays:before{content:"\e593"}.fa-wallet:before{content:"\f555"}.fa-clipboard-check:before{content:"\f46c"}.fa-file-audio:before{content:"\f1c7"}.fa-burger:before,.fa-hamburger:before{content:"\f805"}.fa-wrench:before{content:"\f0ad"}.fa-bugs:before{content:"\e4d0"}.fa-rupee-sign:before,.fa-rupee:before{content:"\f156"}.fa-file-image:before{content:"\f1c5"}.fa-circle-question:before,.fa-question-circle:before{content:"\f059"}.fa-plane-departure:before{content:"\f5b0"}.fa-handshake-slash:before{content:"\e060"}.fa-book-bookmark:before{content:"\e0bb"}.fa-code-branch:before{content:"\f126"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-bridge:before{content:"\e4c8"}.fa-phone-alt:before,.fa-phone-flip:before{content:"\f879"}.fa-truck-front:before{content:"\e2b7"}.fa-cat:before{content:"\f6be"}.fa-anchor-circle-exclamation:before{content:"\e4ab"}.fa-truck-field:before{content:"\e58d"}.fa-route:before{content:"\f4d7"}.fa-clipboard-question:before{content:"\e4e3"}.fa-panorama:before{content:"\e209"}.fa-comment-medical:before{content:"\f7f5"}.fa-teeth-open:before{content:"\f62f"}.fa-file-circle-minus:before{content:"\e4ed"}.fa-tags:before{content:"\f02c"}.fa-wine-glass:before{content:"\f4e3"}.fa-fast-forward:before,.fa-forward-fast:before{content:"\f050"}.fa-face-meh-blank:before,.fa-meh-blank:before{content:"\f5a4"}.fa-parking:before,.fa-square-parking:before{content:"\f540"}.fa-house-signal:before{content:"\e012"}.fa-bars-progress:before,.fa-tasks-alt:before{content:"\f828"}.fa-faucet-drip:before{content:"\e006"}.fa-cart-flatbed:before,.fa-dolly-flatbed:before{content:"\f474"}.fa-ban-smoking:before,.fa-smoking-ban:before{content:"\f54d"}.fa-terminal:before{content:"\f120"}.fa-mobile-button:before{content:"\f10b"}.fa-house-medical-flag:before{content:"\e514"}.fa-basket-shopping:before,.fa-shopping-basket:before{content:"\f291"}.fa-tape:before{content:"\f4db"}.fa-bus-alt:before,.fa-bus-simple:before{content:"\f55e"}.fa-eye:before{content:"\f06e"}.fa-face-sad-cry:before,.fa-sad-cry:before{content:"\f5b3"}.fa-audio-description:before{content:"\f29e"}.fa-person-military-to-person:before{content:"\e54c"}.fa-file-shield:before{content:"\e4f0"}.fa-user-slash:before{content:"\f506"}.fa-pen:before{content:"\f304"}.fa-tower-observation:before{content:"\e586"}.fa-file-code:before{content:"\f1c9"}.fa-signal-5:before,.fa-signal-perfect:before,.fa-signal:before{content:"\f012"}.fa-bus:before{content:"\f207"}.fa-heart-circle-xmark:before{content:"\e501"}.fa-home-lg:before,.fa-house-chimney:before{content:"\e3af"}.fa-window-maximize:before{content:"\f2d0"}.fa-face-frown:before,.fa-frown:before{content:"\f119"}.fa-prescription:before{content:"\f5b1"}.fa-shop:before,.fa-store-alt:before{content:"\f54f"}.fa-floppy-disk:before,.fa-save:before{content:"\f0c7"}.fa-vihara:before{content:"\f6a7"}.fa-balance-scale-left:before,.fa-scale-unbalanced:before{content:"\f515"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-comment-dots:before,.fa-commenting:before{content:"\f4ad"}.fa-plant-wilt:before{content:"\e5aa"}.fa-diamond:before{content:"\f219"}.fa-face-grin-squint:before,.fa-grin-squint:before{content:"\f585"}.fa-hand-holding-dollar:before,.fa-hand-holding-usd:before{content:"\f4c0"}.fa-bacterium:before{content:"\e05a"}.fa-hand-pointer:before{content:"\f25a"}.fa-drum-steelpan:before{content:"\f56a"}.fa-hand-scissors:before{content:"\f257"}.fa-hands-praying:before,.fa-praying-hands:before{content:"\f684"}.fa-arrow-right-rotate:before,.fa-arrow-rotate-forward:before,.fa-arrow-rotate-right:before,.fa-redo:before{content:"\f01e"}.fa-biohazard:before{content:"\f780"}.fa-location-crosshairs:before,.fa-location:before{content:"\f601"}.fa-mars-double:before{content:"\f227"}.fa-child-dress:before{content:"\e59c"}.fa-users-between-lines:before{content:"\e591"}.fa-lungs-virus:before{content:"\e067"}.fa-face-grin-tears:before,.fa-grin-tears:before{content:"\f588"}.fa-phone:before{content:"\f095"}.fa-calendar-times:before,.fa-calendar-xmark:before{content:"\f273"}.fa-child-reaching:before{content:"\e59d"}.fa-head-side-virus:before{content:"\e064"}.fa-user-cog:before,.fa-user-gear:before{content:"\f4fe"}.fa-arrow-up-1-9:before,.fa-sort-numeric-up:before{content:"\f163"}.fa-door-closed:before{content:"\f52a"}.fa-shield-virus:before{content:"\e06c"}.fa-dice-six:before{content:"\f526"}.fa-mosquito-net:before{content:"\e52c"}.fa-bridge-water:before{content:"\e4ce"}.fa-person-booth:before{content:"\f756"}.fa-text-width:before{content:"\f035"}.fa-hat-wizard:before{content:"\f6e8"}.fa-pen-fancy:before{content:"\f5ac"}.fa-digging:before,.fa-person-digging:before{content:"\f85e"}.fa-trash:before{content:"\f1f8"}.fa-gauge-simple-med:before,.fa-gauge-simple:before,.fa-tachometer-average:before{content:"\f629"}.fa-book-medical:before{content:"\f7e6"}.fa-poo:before{content:"\f2fe"}.fa-quote-right-alt:before,.fa-quote-right:before{content:"\f10e"}.fa-shirt:before,.fa-t-shirt:before,.fa-tshirt:before{content:"\f553"}.fa-cubes:before{content:"\f1b3"}.fa-divide:before{content:"\f529"}.fa-tenge-sign:before,.fa-tenge:before{content:"\f7d7"}.fa-headphones:before{content:"\f025"}.fa-hands-holding:before{content:"\f4c2"}.fa-hands-clapping:before{content:"\e1a8"}.fa-republican:before{content:"\f75e"}.fa-arrow-left:before{content:"\f060"}.fa-person-circle-xmark:before{content:"\e543"}.fa-ruler:before{content:"\f545"}.fa-align-left:before{content:"\f036"}.fa-dice-d6:before{content:"\f6d1"}.fa-restroom:before{content:"\f7bd"}.fa-j:before{content:"\4a"}.fa-users-viewfinder:before{content:"\e595"}.fa-file-video:before{content:"\f1c8"}.fa-external-link-alt:before,.fa-up-right-from-square:before{content:"\f35d"}.fa-table-cells:before,.fa-th:before{content:"\f00a"}.fa-file-pdf:before{content:"\f1c1"}.fa-bible:before,.fa-book-bible:before{content:"\f647"}.fa-o:before{content:"\4f"}.fa-medkit:before,.fa-suitcase-medical:before{content:"\f0fa"}.fa-user-secret:before{content:"\f21b"}.fa-otter:before{content:"\f700"}.fa-female:before,.fa-person-dress:before{content:"\f182"}.fa-comment-dollar:before{content:"\f651"}.fa-briefcase-clock:before,.fa-business-time:before{content:"\f64a"}.fa-table-cells-large:before,.fa-th-large:before{content:"\f009"}.fa-book-tanakh:before,.fa-tanakh:before{content:"\f827"}.fa-phone-volume:before,.fa-volume-control-phone:before{content:"\f2a0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-clipboard-user:before{content:"\f7f3"}.fa-child:before{content:"\f1ae"}.fa-lira-sign:before{content:"\f195"}.fa-satellite:before{content:"\f7bf"}.fa-plane-lock:before{content:"\e558"}.fa-tag:before{content:"\f02b"}.fa-comment:before{content:"\f075"}.fa-birthday-cake:before,.fa-cake-candles:before,.fa-cake:before{content:"\f1fd"}.fa-envelope:before{content:"\f0e0"}.fa-angle-double-up:before,.fa-angles-up:before{content:"\f102"}.fa-paperclip:before{content:"\f0c6"}.fa-arrow-right-to-city:before{content:"\e4b3"}.fa-ribbon:before{content:"\f4d6"}.fa-lungs:before{content:"\f604"}.fa-arrow-up-9-1:before,.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-litecoin-sign:before{content:"\e1d3"}.fa-border-none:before{content:"\f850"}.fa-circle-nodes:before{content:"\e4e2"}.fa-parachute-box:before{content:"\f4cd"}.fa-indent:before{content:"\f03c"}.fa-truck-field-un:before{content:"\e58e"}.fa-hourglass-empty:before,.fa-hourglass:before{content:"\f254"}.fa-mountain:before{content:"\f6fc"}.fa-user-doctor:before,.fa-user-md:before{content:"\f0f0"}.fa-circle-info:before,.fa-info-circle:before{content:"\f05a"}.fa-cloud-meatball:before{content:"\f73b"}.fa-camera-alt:before,.fa-camera:before{content:"\f030"}.fa-square-virus:before{content:"\e578"}.fa-meteor:before{content:"\f753"}.fa-car-on:before{content:"\e4dd"}.fa-sleigh:before{content:"\f7cc"}.fa-arrow-down-1-9:before,.fa-sort-numeric-asc:before,.fa-sort-numeric-down:before{content:"\f162"}.fa-hand-holding-droplet:before,.fa-hand-holding-water:before{content:"\f4c1"}.fa-water:before{content:"\f773"}.fa-calendar-check:before{content:"\f274"}.fa-braille:before{content:"\f2a1"}.fa-prescription-bottle-alt:before,.fa-prescription-bottle-medical:before{content:"\f486"}.fa-landmark:before{content:"\f66f"}.fa-truck:before{content:"\f0d1"}.fa-crosshairs:before{content:"\f05b"}.fa-person-cane:before{content:"\e53c"}.fa-tent:before{content:"\e57d"}.fa-vest-patches:before{content:"\e086"}.fa-check-double:before{content:"\f560"}.fa-arrow-down-a-z:before,.fa-sort-alpha-asc:before,.fa-sort-alpha-down:before{content:"\f15d"}.fa-money-bill-wheat:before{content:"\e52a"}.fa-cookie:before{content:"\f563"}.fa-arrow-left-rotate:before,.fa-arrow-rotate-back:before,.fa-arrow-rotate-backward:before,.fa-arrow-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-hard-drive:before,.fa-hdd:before{content:"\f0a0"}.fa-face-grin-squint-tears:before,.fa-grin-squint-tears:before{content:"\f586"}.fa-dumbbell:before{content:"\f44b"}.fa-list-alt:before,.fa-rectangle-list:before{content:"\f022"}.fa-tarp-droplet:before{content:"\e57c"}.fa-house-medical-circle-check:before{content:"\e511"}.fa-person-skiing-nordic:before,.fa-skiing-nordic:before{content:"\f7ca"}.fa-calendar-plus:before{content:"\f271"}.fa-plane-arrival:before{content:"\f5af"}.fa-arrow-alt-circle-left:before,.fa-circle-left:before{content:"\f359"}.fa-subway:before,.fa-train-subway:before{content:"\f239"}.fa-chart-gantt:before{content:"\e0e4"}.fa-indian-rupee-sign:before,.fa-indian-rupee:before,.fa-inr:before{content:"\e1bc"}.fa-crop-alt:before,.fa-crop-simple:before{content:"\f565"}.fa-money-bill-1:before,.fa-money-bill-alt:before{content:"\f3d1"}.fa-left-long:before,.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-dna:before{content:"\f471"}.fa-virus-slash:before{content:"\e075"}.fa-minus:before,.fa-subtract:before{content:"\f068"}.fa-chess:before{content:"\f439"}.fa-arrow-left-long:before,.fa-long-arrow-left:before{content:"\f177"}.fa-plug-circle-check:before{content:"\e55c"}.fa-street-view:before{content:"\f21d"}.fa-franc-sign:before{content:"\e18f"}.fa-volume-off:before{content:"\f026"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before,.fa-hands-american-sign-language-interpreting:before,.fa-hands-asl-interpreting:before{content:"\f2a3"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-droplet-slash:before,.fa-tint-slash:before{content:"\f5c7"}.fa-mosque:before{content:"\f678"}.fa-mosquito:before{content:"\e52b"}.fa-star-of-david:before{content:"\f69a"}.fa-person-military-rifle:before{content:"\e54b"}.fa-cart-shopping:before,.fa-shopping-cart:before{content:"\f07a"}.fa-vials:before{content:"\f493"}.fa-plug-circle-plus:before{content:"\e55f"}.fa-place-of-worship:before{content:"\f67f"}.fa-grip-vertical:before{content:"\f58e"}.fa-arrow-turn-up:before,.fa-level-up:before{content:"\f148"}.fa-u:before{content:"\55"}.fa-square-root-alt:before,.fa-square-root-variable:before{content:"\f698"}.fa-clock-four:before,.fa-clock:before{content:"\f017"}.fa-backward-step:before,.fa-step-backward:before{content:"\f048"}.fa-pallet:before{content:"\f482"}.fa-faucet:before{content:"\e005"}.fa-baseball-bat-ball:before{content:"\f432"}.fa-s:before{content:"\53"}.fa-timeline:before{content:"\e29c"}.fa-keyboard:before{content:"\f11c"}.fa-caret-down:before{content:"\f0d7"}.fa-clinic-medical:before,.fa-house-chimney-medical:before{content:"\f7f2"}.fa-temperature-3:before,.fa-temperature-three-quarters:before,.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-mobile-android-alt:before,.fa-mobile-screen:before{content:"\f3cf"}.fa-plane-up:before{content:"\e22d"}.fa-piggy-bank:before{content:"\f4d3"}.fa-battery-3:before,.fa-battery-half:before{content:"\f242"}.fa-mountain-city:before{content:"\e52e"}.fa-coins:before{content:"\f51e"}.fa-khanda:before{content:"\f66d"}.fa-sliders-h:before,.fa-sliders:before{content:"\f1de"}.fa-folder-tree:before{content:"\f802"}.fa-network-wired:before{content:"\f6ff"}.fa-map-pin:before{content:"\f276"}.fa-hamsa:before{content:"\f665"}.fa-cent-sign:before{content:"\e3f5"}.fa-flask:before{content:"\f0c3"}.fa-person-pregnant:before{content:"\e31e"}.fa-wand-sparkles:before{content:"\f72b"}.fa-ellipsis-v:before,.fa-ellipsis-vertical:before{content:"\f142"}.fa-ticket:before{content:"\f145"}.fa-power-off:before{content:"\f011"}.fa-long-arrow-alt-right:before,.fa-right-long:before{content:"\f30b"}.fa-flag-usa:before{content:"\f74d"}.fa-laptop-file:before{content:"\e51d"}.fa-teletype:before,.fa-tty:before{content:"\f1e4"}.fa-diagram-next:before{content:"\e476"}.fa-person-rifle:before{content:"\e54e"}.fa-house-medical-circle-exclamation:before{content:"\e512"}.fa-closed-captioning:before{content:"\f20a"}.fa-hiking:before,.fa-person-hiking:before{content:"\f6ec"}.fa-venus-double:before{content:"\f226"}.fa-images:before{content:"\f302"}.fa-calculator:before{content:"\f1ec"}.fa-people-pulling:before{content:"\e535"}.fa-n:before{content:"\4e"}.fa-cable-car:before,.fa-tram:before{content:"\f7da"}.fa-cloud-rain:before{content:"\f73d"}.fa-building-circle-xmark:before{content:"\e4d4"}.fa-ship:before{content:"\f21a"}.fa-arrows-down-to-line:before{content:"\e4b8"}.fa-download:before{content:"\f019"}.fa-face-grin:before,.fa-grin:before{content:"\f580"}.fa-backspace:before,.fa-delete-left:before{content:"\f55a"}.fa-eye-dropper-empty:before,.fa-eye-dropper:before,.fa-eyedropper:before{content:"\f1fb"}.fa-file-circle-check:before{content:"\e5a0"}.fa-forward:before{content:"\f04e"}.fa-mobile-android:before,.fa-mobile-phone:before,.fa-mobile:before{content:"\f3ce"}.fa-face-meh:before,.fa-meh:before{content:"\f11a"}.fa-align-center:before{content:"\f037"}.fa-book-dead:before,.fa-book-skull:before{content:"\f6b7"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-heart-circle-exclamation:before{content:"\e4fe"}.fa-home-alt:before,.fa-home-lg-alt:before,.fa-home:before,.fa-house:before{content:"\f015"}.fa-calendar-week:before{content:"\f784"}.fa-laptop-medical:before{content:"\f812"}.fa-b:before{content:"\42"}.fa-file-medical:before{content:"\f477"}.fa-dice-one:before{content:"\f525"}.fa-kiwi-bird:before{content:"\f535"}.fa-arrow-right-arrow-left:before,.fa-exchange:before{content:"\f0ec"}.fa-redo-alt:before,.fa-rotate-forward:before,.fa-rotate-right:before{content:"\f2f9"}.fa-cutlery:before,.fa-utensils:before{content:"\f2e7"}.fa-arrow-up-wide-short:before,.fa-sort-amount-up:before{content:"\f161"}.fa-mill-sign:before{content:"\e1ed"}.fa-bowl-rice:before{content:"\e2eb"}.fa-skull:before{content:"\f54c"}.fa-broadcast-tower:before,.fa-tower-broadcast:before{content:"\f519"}.fa-truck-pickup:before{content:"\f63c"}.fa-long-arrow-alt-up:before,.fa-up-long:before{content:"\f30c"}.fa-stop:before{content:"\f04d"}.fa-code-merge:before{content:"\f387"}.fa-upload:before{content:"\f093"}.fa-hurricane:before{content:"\f751"}.fa-mound:before{content:"\e52d"}.fa-toilet-portable:before{content:"\e583"}.fa-compact-disc:before{content:"\f51f"}.fa-file-arrow-down:before,.fa-file-download:before{content:"\f56d"}.fa-caravan:before{content:"\f8ff"}.fa-shield-cat:before{content:"\e572"}.fa-bolt:before,.fa-zap:before{content:"\f0e7"}.fa-glass-water:before{content:"\e4f4"}.fa-oil-well:before{content:"\e532"}.fa-vault:before{content:"\e2c5"}.fa-mars:before{content:"\f222"}.fa-toilet:before{content:"\f7d8"}.fa-plane-circle-xmark:before{content:"\e557"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen-sign:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble-sign:before,.fa-ruble:before{content:"\f158"}.fa-sun:before{content:"\f185"}.fa-guitar:before{content:"\f7a6"}.fa-face-laugh-wink:before,.fa-laugh-wink:before{content:"\f59c"}.fa-horse-head:before{content:"\f7ab"}.fa-bore-hole:before{content:"\e4c3"}.fa-industry:before{content:"\f275"}.fa-arrow-alt-circle-down:before,.fa-circle-down:before{content:"\f358"}.fa-arrows-turn-to-dots:before{content:"\e4c1"}.fa-florin-sign:before{content:"\e184"}.fa-arrow-down-short-wide:before,.fa-sort-amount-desc:before,.fa-sort-amount-down-alt:before{content:"\f884"}.fa-less-than:before{content:"\3c"}.fa-angle-down:before{content:"\f107"}.fa-car-tunnel:before{content:"\e4de"}.fa-head-side-cough:before{content:"\e061"}.fa-grip-lines:before{content:"\f7a4"}.fa-thumbs-down:before{content:"\f165"}.fa-user-lock:before{content:"\f502"}.fa-arrow-right-long:before,.fa-long-arrow-right:before{content:"\f178"}.fa-anchor-circle-xmark:before{content:"\e4ac"}.fa-ellipsis-h:before,.fa-ellipsis:before{content:"\f141"}.fa-chess-pawn:before{content:"\f443"}.fa-first-aid:before,.fa-kit-medical:before{content:"\f479"}.fa-person-through-window:before{content:"\e5a9"}.fa-toolbox:before{content:"\f552"}.fa-hands-holding-circle:before{content:"\e4fb"}.fa-bug:before{content:"\f188"}.fa-credit-card-alt:before,.fa-credit-card:before{content:"\f09d"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-hand-holding-hand:before{content:"\e4f7"}.fa-book-open-reader:before,.fa-book-reader:before{content:"\f5da"}.fa-mountain-sun:before{content:"\e52f"}.fa-arrows-left-right-to-line:before{content:"\e4ba"}.fa-dice-d20:before{content:"\f6cf"}.fa-truck-droplet:before{content:"\e58c"}.fa-file-circle-xmark:before{content:"\e5a1"}.fa-temperature-arrow-up:before,.fa-temperature-up:before{content:"\e040"}.fa-medal:before{content:"\f5a2"}.fa-bed:before{content:"\f236"}.fa-h-square:before,.fa-square-h:before{content:"\f0fd"}.fa-podcast:before{content:"\f2ce"}.fa-temperature-4:before,.fa-temperature-full:before,.fa-thermometer-4:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-bell:before{content:"\f0f3"}.fa-superscript:before{content:"\f12b"}.fa-plug-circle-xmark:before{content:"\e560"}.fa-star-of-life:before{content:"\f621"}.fa-phone-slash:before{content:"\f3dd"}.fa-paint-roller:before{content:"\f5aa"}.fa-hands-helping:before,.fa-handshake-angle:before{content:"\f4c4"}.fa-location-dot:before,.fa-map-marker-alt:before{content:"\f3c5"}.fa-file:before{content:"\f15b"}.fa-greater-than:before{content:"\3e"}.fa-person-swimming:before,.fa-swimmer:before{content:"\f5c4"}.fa-arrow-down:before{content:"\f063"}.fa-droplet:before,.fa-tint:before{content:"\f043"}.fa-eraser:before{content:"\f12d"}.fa-earth-america:before,.fa-earth-americas:before,.fa-earth:before,.fa-globe-americas:before{content:"\f57d"}.fa-person-burst:before{content:"\e53b"}.fa-dove:before{content:"\f4ba"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-socks:before{content:"\f696"}.fa-inbox:before{content:"\f01c"}.fa-section:before{content:"\e447"}.fa-gauge-high:before,.fa-tachometer-alt-fast:before,.fa-tachometer-alt:before{content:"\f625"}.fa-envelope-open-text:before{content:"\f658"}.fa-hospital-alt:before,.fa-hospital-wide:before,.fa-hospital:before{content:"\f0f8"}.fa-wine-bottle:before{content:"\f72f"}.fa-chess-rook:before{content:"\f447"}.fa-bars-staggered:before,.fa-reorder:before,.fa-stream:before{content:"\f550"}.fa-dharmachakra:before{content:"\f655"}.fa-hotdog:before{content:"\f80f"}.fa-blind:before,.fa-person-walking-with-cane:before{content:"\f29d"}.fa-drum:before{content:"\f569"}.fa-ice-cream:before{content:"\f810"}.fa-heart-circle-bolt:before{content:"\e4fc"}.fa-fax:before{content:"\f1ac"}.fa-paragraph:before{content:"\f1dd"}.fa-check-to-slot:before,.fa-vote-yea:before{content:"\f772"}.fa-star-half:before{content:"\f089"}.fa-boxes-alt:before,.fa-boxes-stacked:before,.fa-boxes:before{content:"\f468"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-assistive-listening-systems:before,.fa-ear-listen:before{content:"\f2a2"}.fa-tree-city:before{content:"\e587"}.fa-play:before{content:"\f04b"}.fa-font:before{content:"\f031"}.fa-table-cells-row-lock:before{content:"\e67a"}.fa-rupiah-sign:before{content:"\e23d"}.fa-magnifying-glass:before,.fa-search:before{content:"\f002"}.fa-ping-pong-paddle-ball:before,.fa-table-tennis-paddle-ball:before,.fa-table-tennis:before{content:"\f45d"}.fa-diagnoses:before,.fa-person-dots-from-line:before{content:"\f470"}.fa-trash-can-arrow-up:before,.fa-trash-restore-alt:before{content:"\f82a"}.fa-naira-sign:before{content:"\e1f6"}.fa-cart-arrow-down:before{content:"\f218"}.fa-walkie-talkie:before{content:"\f8ef"}.fa-file-edit:before,.fa-file-pen:before{content:"\f31c"}.fa-receipt:before{content:"\f543"}.fa-pen-square:before,.fa-pencil-square:before,.fa-square-pen:before{content:"\f14b"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-person-circle-exclamation:before{content:"\e53f"}.fa-chevron-down:before{content:"\f078"}.fa-battery-5:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-skull-crossbones:before{content:"\f714"}.fa-code-compare:before{content:"\e13a"}.fa-list-dots:before,.fa-list-ul:before{content:"\f0ca"}.fa-school-lock:before{content:"\e56f"}.fa-tower-cell:before{content:"\e585"}.fa-down-long:before,.fa-long-arrow-alt-down:before{content:"\f309"}.fa-ranking-star:before{content:"\e561"}.fa-chess-king:before{content:"\f43f"}.fa-person-harassing:before{content:"\e549"}.fa-brazilian-real-sign:before{content:"\e46c"}.fa-landmark-alt:before,.fa-landmark-dome:before{content:"\f752"}.fa-arrow-up:before{content:"\f062"}.fa-television:before,.fa-tv-alt:before,.fa-tv:before{content:"\f26c"}.fa-shrimp:before{content:"\e448"}.fa-list-check:before,.fa-tasks:before{content:"\f0ae"}.fa-jug-detergent:before{content:"\e519"}.fa-circle-user:before,.fa-user-circle:before{content:"\f2bd"}.fa-user-shield:before{content:"\f505"}.fa-wind:before{content:"\f72e"}.fa-car-burst:before,.fa-car-crash:before{content:"\f5e1"}.fa-y:before{content:"\59"}.fa-person-snowboarding:before,.fa-snowboarding:before{content:"\f7ce"}.fa-shipping-fast:before,.fa-truck-fast:before{content:"\f48b"}.fa-fish:before{content:"\f578"}.fa-user-graduate:before{content:"\f501"}.fa-adjust:before,.fa-circle-half-stroke:before{content:"\f042"}.fa-clapperboard:before{content:"\e131"}.fa-circle-radiation:before,.fa-radiation-alt:before{content:"\f7ba"}.fa-baseball-ball:before,.fa-baseball:before{content:"\f433"}.fa-jet-fighter-up:before{content:"\e518"}.fa-diagram-project:before,.fa-project-diagram:before{content:"\f542"}.fa-copy:before{content:"\f0c5"}.fa-volume-mute:before,.fa-volume-times:before,.fa-volume-xmark:before{content:"\f6a9"}.fa-hand-sparkles:before{content:"\e05d"}.fa-grip-horizontal:before,.fa-grip:before{content:"\f58d"}.fa-share-from-square:before,.fa-share-square:before{content:"\f14d"}.fa-child-combatant:before,.fa-child-rifle:before{content:"\e4e0"}.fa-gun:before{content:"\e19b"}.fa-phone-square:before,.fa-square-phone:before{content:"\f098"}.fa-add:before,.fa-plus:before{content:"\2b"}.fa-expand:before{content:"\f065"}.fa-computer:before{content:"\e4e5"}.fa-close:before,.fa-multiply:before,.fa-remove:before,.fa-times:before,.fa-xmark:before{content:"\f00d"}.fa-arrows-up-down-left-right:before,.fa-arrows:before{content:"\f047"}.fa-chalkboard-teacher:before,.fa-chalkboard-user:before{content:"\f51c"}.fa-peso-sign:before{content:"\e222"}.fa-building-shield:before{content:"\e4d8"}.fa-baby:before{content:"\f77c"}.fa-users-line:before{content:"\e592"}.fa-quote-left-alt:before,.fa-quote-left:before{content:"\f10d"}.fa-tractor:before{content:"\f722"}.fa-trash-arrow-up:before,.fa-trash-restore:before{content:"\f829"}.fa-arrow-down-up-lock:before{content:"\e4b0"}.fa-lines-leaning:before{content:"\e51e"}.fa-ruler-combined:before{content:"\f546"}.fa-copyright:before{content:"\f1f9"}.fa-equals:before{content:"\3d"}.fa-blender:before{content:"\f517"}.fa-teeth:before{content:"\f62e"}.fa-ils:before,.fa-shekel-sign:before,.fa-shekel:before,.fa-sheqel-sign:before,.fa-sheqel:before{content:"\f20b"}.fa-map:before{content:"\f279"}.fa-rocket:before{content:"\f135"}.fa-photo-film:before,.fa-photo-video:before{content:"\f87c"}.fa-folder-minus:before{content:"\f65d"}.fa-store:before{content:"\f54e"}.fa-arrow-trend-up:before{content:"\e098"}.fa-plug-circle-minus:before{content:"\e55e"}.fa-sign-hanging:before,.fa-sign:before{content:"\f4d9"}.fa-bezier-curve:before{content:"\f55b"}.fa-bell-slash:before{content:"\f1f6"}.fa-tablet-android:before,.fa-tablet:before{content:"\f3fb"}.fa-school-flag:before{content:"\e56e"}.fa-fill:before{content:"\f575"}.fa-angle-up:before{content:"\f106"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-holly-berry:before{content:"\f7aa"}.fa-chevron-left:before{content:"\f053"}.fa-bacteria:before{content:"\e059"}.fa-hand-lizard:before{content:"\f258"}.fa-notdef:before{content:"\e1fe"}.fa-disease:before{content:"\f7fa"}.fa-briefcase-medical:before{content:"\f469"}.fa-genderless:before{content:"\f22d"}.fa-chevron-right:before{content:"\f054"}.fa-retweet:before{content:"\f079"}.fa-car-alt:before,.fa-car-rear:before{content:"\f5de"}.fa-pump-soap:before{content:"\e06b"}.fa-video-slash:before{content:"\f4e2"}.fa-battery-2:before,.fa-battery-quarter:before{content:"\f243"}.fa-radio:before{content:"\f8d7"}.fa-baby-carriage:before,.fa-carriage-baby:before{content:"\f77d"}.fa-traffic-light:before{content:"\f637"}.fa-thermometer:before{content:"\f491"}.fa-vr-cardboard:before{content:"\f729"}.fa-hand-middle-finger:before{content:"\f806"}.fa-percent:before,.fa-percentage:before{content:"\25"}.fa-truck-moving:before{content:"\f4df"}.fa-glass-water-droplet:before{content:"\e4f5"}.fa-display:before{content:"\e163"}.fa-face-smile:before,.fa-smile:before{content:"\f118"}.fa-thumb-tack:before,.fa-thumbtack:before{content:"\f08d"}.fa-trophy:before{content:"\f091"}.fa-person-praying:before,.fa-pray:before{content:"\f683"}.fa-hammer:before{content:"\f6e3"}.fa-hand-peace:before{content:"\f25b"}.fa-rotate:before,.fa-sync-alt:before{content:"\f2f1"}.fa-spinner:before{content:"\f110"}.fa-robot:before{content:"\f544"}.fa-peace:before{content:"\f67c"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-warehouse:before{content:"\f494"}.fa-arrow-up-right-dots:before{content:"\e4b7"}.fa-splotch:before{content:"\f5bc"}.fa-face-grin-hearts:before,.fa-grin-hearts:before{content:"\f584"}.fa-dice-four:before{content:"\f524"}.fa-sim-card:before{content:"\f7c4"}.fa-transgender-alt:before,.fa-transgender:before{content:"\f225"}.fa-mercury:before{content:"\f223"}.fa-arrow-turn-down:before,.fa-level-down:before{content:"\f149"}.fa-person-falling-burst:before{content:"\e547"}.fa-award:before{content:"\f559"}.fa-ticket-alt:before,.fa-ticket-simple:before{content:"\f3ff"}.fa-building:before{content:"\f1ad"}.fa-angle-double-left:before,.fa-angles-left:before{content:"\f100"}.fa-qrcode:before{content:"\f029"}.fa-clock-rotate-left:before,.fa-history:before{content:"\f1da"}.fa-face-grin-beam-sweat:before,.fa-grin-beam-sweat:before{content:"\f583"}.fa-arrow-right-from-file:before,.fa-file-export:before{content:"\f56e"}.fa-shield-blank:before,.fa-shield:before{content:"\f132"}.fa-arrow-up-short-wide:before,.fa-sort-amount-up-alt:before{content:"\f885"}.fa-house-medical:before{content:"\e3b2"}.fa-golf-ball-tee:before,.fa-golf-ball:before{content:"\f450"}.fa-chevron-circle-left:before,.fa-circle-chevron-left:before{content:"\f137"}.fa-house-chimney-window:before{content:"\e00d"}.fa-pen-nib:before{content:"\f5ad"}.fa-tent-arrow-turn-left:before{content:"\e580"}.fa-tents:before{content:"\e582"}.fa-magic:before,.fa-wand-magic:before{content:"\f0d0"}.fa-dog:before{content:"\f6d3"}.fa-carrot:before{content:"\f787"}.fa-moon:before{content:"\f186"}.fa-wine-glass-alt:before,.fa-wine-glass-empty:before{content:"\f5ce"}.fa-cheese:before{content:"\f7ef"}.fa-yin-yang:before{content:"\f6ad"}.fa-music:before{content:"\f001"}.fa-code-commit:before{content:"\f386"}.fa-temperature-low:before{content:"\f76b"}.fa-biking:before,.fa-person-biking:before{content:"\f84a"}.fa-broom:before{content:"\f51a"}.fa-shield-heart:before{content:"\e574"}.fa-gopuram:before{content:"\f664"}.fa-earth-oceania:before,.fa-globe-oceania:before{content:"\e47b"}.fa-square-xmark:before,.fa-times-square:before,.fa-xmark-square:before{content:"\f2d3"}.fa-hashtag:before{content:"\23"}.fa-expand-alt:before,.fa-up-right-and-down-left-from-center:before{content:"\f424"}.fa-oil-can:before{content:"\f613"}.fa-t:before{content:"\54"}.fa-hippo:before{content:"\f6ed"}.fa-chart-column:before{content:"\e0e3"}.fa-infinity:before{content:"\f534"}.fa-vial-circle-check:before{content:"\e596"}.fa-person-arrow-down-to-line:before{content:"\e538"}.fa-voicemail:before{content:"\f897"}.fa-fan:before{content:"\f863"}.fa-person-walking-luggage:before{content:"\e554"}.fa-arrows-alt-v:before,.fa-up-down:before{content:"\f338"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-calendar:before{content:"\f133"}.fa-trailer:before{content:"\e041"}.fa-bahai:before,.fa-haykal:before{content:"\f666"}.fa-sd-card:before{content:"\f7c2"}.fa-dragon:before{content:"\f6d5"}.fa-shoe-prints:before{content:"\f54b"}.fa-circle-plus:before,.fa-plus-circle:before{content:"\f055"}.fa-face-grin-tongue-wink:before,.fa-grin-tongue-wink:before{content:"\f58b"}.fa-hand-holding:before{content:"\f4bd"}.fa-plug-circle-exclamation:before{content:"\e55d"}.fa-chain-broken:before,.fa-chain-slash:before,.fa-link-slash:before,.fa-unlink:before{content:"\f127"}.fa-clone:before{content:"\f24d"}.fa-person-walking-arrow-loop-left:before{content:"\e551"}.fa-arrow-up-z-a:before,.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-fire-alt:before,.fa-fire-flame-curved:before{content:"\f7e4"}.fa-tornado:before{content:"\f76f"}.fa-file-circle-plus:before{content:"\e494"}.fa-book-quran:before,.fa-quran:before{content:"\f687"}.fa-anchor:before{content:"\f13d"}.fa-border-all:before{content:"\f84c"}.fa-angry:before,.fa-face-angry:before{content:"\f556"}.fa-cookie-bite:before{content:"\f564"}.fa-arrow-trend-down:before{content:"\e097"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-draw-polygon:before{content:"\f5ee"}.fa-balance-scale:before,.fa-scale-balanced:before{content:"\f24e"}.fa-gauge-simple-high:before,.fa-tachometer-fast:before,.fa-tachometer:before{content:"\f62a"}.fa-shower:before{content:"\f2cc"}.fa-desktop-alt:before,.fa-desktop:before{content:"\f390"}.fa-m:before{content:"\4d"}.fa-table-list:before,.fa-th-list:before{content:"\f00b"}.fa-comment-sms:before,.fa-sms:before{content:"\f7cd"}.fa-book:before{content:"\f02d"}.fa-user-plus:before{content:"\f234"}.fa-check:before{content:"\f00c"}.fa-battery-4:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-house-circle-check:before{content:"\e509"}.fa-angle-left:before{content:"\f104"}.fa-diagram-successor:before{content:"\e47a"}.fa-truck-arrow-right:before{content:"\e58b"}.fa-arrows-split-up-and-left:before{content:"\e4bc"}.fa-fist-raised:before,.fa-hand-fist:before{content:"\f6de"}.fa-cloud-moon:before{content:"\f6c3"}.fa-briefcase:before{content:"\f0b1"}.fa-person-falling:before{content:"\e546"}.fa-image-portrait:before,.fa-portrait:before{content:"\f3e0"}.fa-user-tag:before{content:"\f507"}.fa-rug:before{content:"\e569"}.fa-earth-europe:before,.fa-globe-europe:before{content:"\f7a2"}.fa-cart-flatbed-suitcase:before,.fa-luggage-cart:before{content:"\f59d"}.fa-rectangle-times:before,.fa-rectangle-xmark:before,.fa-times-rectangle:before,.fa-window-close:before{content:"\f410"}.fa-baht-sign:before{content:"\e0ac"}.fa-book-open:before{content:"\f518"}.fa-book-journal-whills:before,.fa-journal-whills:before{content:"\f66a"}.fa-handcuffs:before{content:"\e4f8"}.fa-exclamation-triangle:before,.fa-triangle-exclamation:before,.fa-warning:before{content:"\f071"}.fa-database:before{content:"\f1c0"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-bottle-droplet:before{content:"\e4c4"}.fa-mask-face:before{content:"\e1d7"}.fa-hill-rockslide:before{content:"\e508"}.fa-exchange-alt:before,.fa-right-left:before{content:"\f362"}.fa-paper-plane:before{content:"\f1d8"}.fa-road-circle-exclamation:before{content:"\e565"}.fa-dungeon:before{content:"\f6d9"}.fa-align-right:before{content:"\f038"}.fa-money-bill-1-wave:before,.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-life-ring:before{content:"\f1cd"}.fa-hands:before,.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-calendar-day:before{content:"\f783"}.fa-ladder-water:before,.fa-swimming-pool:before,.fa-water-ladder:before{content:"\f5c5"}.fa-arrows-up-down:before,.fa-arrows-v:before{content:"\f07d"}.fa-face-grimace:before,.fa-grimace:before{content:"\f57f"}.fa-wheelchair-alt:before,.fa-wheelchair-move:before{content:"\e2ce"}.fa-level-down-alt:before,.fa-turn-down:before{content:"\f3be"}.fa-person-walking-arrow-right:before{content:"\e552"}.fa-envelope-square:before,.fa-square-envelope:before{content:"\f199"}.fa-dice:before{content:"\f522"}.fa-bowling-ball:before{content:"\f436"}.fa-brain:before{content:"\f5dc"}.fa-band-aid:before,.fa-bandage:before{content:"\f462"}.fa-calendar-minus:before{content:"\f272"}.fa-circle-xmark:before,.fa-times-circle:before,.fa-xmark-circle:before{content:"\f057"}.fa-gifts:before{content:"\f79c"}.fa-hotel:before{content:"\f594"}.fa-earth-asia:before,.fa-globe-asia:before{content:"\f57e"}.fa-id-card-alt:before,.fa-id-card-clip:before{content:"\f47f"}.fa-magnifying-glass-plus:before,.fa-search-plus:before{content:"\f00e"}.fa-thumbs-up:before{content:"\f164"}.fa-user-clock:before{content:"\f4fd"}.fa-allergies:before,.fa-hand-dots:before{content:"\f461"}.fa-file-invoice:before{content:"\f570"}.fa-window-minimize:before{content:"\f2d1"}.fa-coffee:before,.fa-mug-saucer:before{content:"\f0f4"}.fa-brush:before{content:"\f55d"}.fa-mask:before{content:"\f6fa"}.fa-magnifying-glass-minus:before,.fa-search-minus:before{content:"\f010"}.fa-ruler-vertical:before{content:"\f548"}.fa-user-alt:before,.fa-user-large:before{content:"\f406"}.fa-train-tram:before{content:"\e5b4"}.fa-user-nurse:before{content:"\f82f"}.fa-syringe:before{content:"\f48e"}.fa-cloud-sun:before{content:"\f6c4"}.fa-stopwatch-20:before{content:"\e06f"}.fa-square-full:before{content:"\f45c"}.fa-magnet:before{content:"\f076"}.fa-jar:before{content:"\e516"}.fa-note-sticky:before,.fa-sticky-note:before{content:"\f249"}.fa-bug-slash:before{content:"\e490"}.fa-arrow-up-from-water-pump:before{content:"\e4b6"}.fa-bone:before{content:"\f5d7"}.fa-user-injured:before{content:"\f728"}.fa-face-sad-tear:before,.fa-sad-tear:before{content:"\f5b4"}.fa-plane:before{content:"\f072"}.fa-tent-arrows-down:before{content:"\e581"}.fa-exclamation:before{content:"\21"}.fa-arrows-spin:before{content:"\e4bb"}.fa-print:before{content:"\f02f"}.fa-try:before,.fa-turkish-lira-sign:before,.fa-turkish-lira:before{content:"\e2bb"}.fa-dollar-sign:before,.fa-dollar:before,.fa-usd:before{content:"\24"}.fa-x:before{content:"\58"}.fa-magnifying-glass-dollar:before,.fa-search-dollar:before{content:"\f688"}.fa-users-cog:before,.fa-users-gear:before{content:"\f509"}.fa-person-military-pointing:before{content:"\e54a"}.fa-bank:before,.fa-building-columns:before,.fa-institution:before,.fa-museum:before,.fa-university:before{content:"\f19c"}.fa-umbrella:before{content:"\f0e9"}.fa-trowel:before{content:"\e589"}.fa-d:before{content:"\44"}.fa-stapler:before{content:"\e5af"}.fa-masks-theater:before,.fa-theater-masks:before{content:"\f630"}.fa-kip-sign:before{content:"\e1c4"}.fa-hand-point-left:before{content:"\f0a5"}.fa-handshake-alt:before,.fa-handshake-simple:before{content:"\f4c6"}.fa-fighter-jet:before,.fa-jet-fighter:before{content:"\f0fb"}.fa-share-alt-square:before,.fa-square-share-nodes:before{content:"\f1e1"}.fa-barcode:before{content:"\f02a"}.fa-plus-minus:before{content:"\e43c"}.fa-video-camera:before,.fa-video:before{content:"\f03d"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-person-circle-check:before{content:"\e53e"}.fa-level-up-alt:before,.fa-turn-up:before{content:"\f3bf"} -.fa-sr-only,.fa-sr-only-focusable:not(:focus),.sr-only,.sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}:host,:root{--fa-style-family-brands:"Font Awesome 6 Brands";--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}.fa-brands,.fab{font-weight:400}.fa-monero:before{content:"\f3d0"}.fa-hooli:before{content:"\f427"}.fa-yelp:before{content:"\f1e9"}.fa-cc-visa:before{content:"\f1f0"}.fa-lastfm:before{content:"\f202"}.fa-shopware:before{content:"\f5b5"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-aws:before{content:"\f375"}.fa-redhat:before{content:"\f7bc"}.fa-yoast:before{content:"\f2b1"}.fa-cloudflare:before{content:"\e07d"}.fa-ups:before{content:"\f7e0"}.fa-pixiv:before{content:"\e640"}.fa-wpexplorer:before{content:"\f2de"}.fa-dyalog:before{content:"\f399"}.fa-bity:before{content:"\f37a"}.fa-stackpath:before{content:"\f842"}.fa-buysellads:before{content:"\f20d"}.fa-first-order:before{content:"\f2b0"}.fa-modx:before{content:"\f285"}.fa-guilded:before{content:"\e07e"}.fa-vnv:before{content:"\f40b"}.fa-js-square:before,.fa-square-js:before{content:"\f3b9"}.fa-microsoft:before{content:"\f3ca"}.fa-qq:before{content:"\f1d6"}.fa-orcid:before{content:"\f8d2"}.fa-java:before{content:"\f4e4"}.fa-invision:before{content:"\f7b0"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-centercode:before{content:"\f380"}.fa-glide-g:before{content:"\f2a6"}.fa-drupal:before{content:"\f1a9"}.fa-jxl:before{content:"\e67b"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-unity:before{content:"\e049"}.fa-whmcs:before{content:"\f40d"}.fa-rocketchat:before{content:"\f3e8"}.fa-vk:before{content:"\f189"}.fa-untappd:before{content:"\f405"}.fa-mailchimp:before{content:"\f59e"}.fa-css3-alt:before{content:"\f38b"}.fa-reddit-square:before,.fa-square-reddit:before{content:"\f1a2"}.fa-vimeo-v:before{content:"\f27d"}.fa-contao:before{content:"\f26d"}.fa-square-font-awesome:before{content:"\e5ad"}.fa-deskpro:before{content:"\f38f"}.fa-brave:before{content:"\e63c"}.fa-sistrix:before{content:"\f3ee"}.fa-instagram-square:before,.fa-square-instagram:before{content:"\e055"}.fa-battle-net:before{content:"\f835"}.fa-the-red-yeti:before{content:"\f69d"}.fa-hacker-news-square:before,.fa-square-hacker-news:before{content:"\f3af"}.fa-edge:before{content:"\f282"}.fa-threads:before{content:"\e618"}.fa-napster:before{content:"\f3d2"}.fa-snapchat-square:before,.fa-square-snapchat:before{content:"\f2ad"}.fa-google-plus-g:before{content:"\f0d5"}.fa-artstation:before{content:"\f77a"}.fa-markdown:before{content:"\f60f"}.fa-sourcetree:before{content:"\f7d3"}.fa-google-plus:before{content:"\f2b3"}.fa-diaspora:before{content:"\f791"}.fa-foursquare:before{content:"\f180"}.fa-stack-overflow:before{content:"\f16c"}.fa-github-alt:before{content:"\f113"}.fa-phoenix-squadron:before{content:"\f511"}.fa-pagelines:before{content:"\f18c"}.fa-algolia:before{content:"\f36c"}.fa-red-river:before{content:"\f3e3"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-safari:before{content:"\f267"}.fa-google:before{content:"\f1a0"}.fa-font-awesome-alt:before,.fa-square-font-awesome-stroke:before{content:"\f35c"}.fa-atlassian:before{content:"\f77b"}.fa-linkedin-in:before{content:"\f0e1"}.fa-digital-ocean:before{content:"\f391"}.fa-nimblr:before{content:"\f5a8"}.fa-chromecast:before{content:"\f838"}.fa-evernote:before{content:"\f839"}.fa-hacker-news:before{content:"\f1d4"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-adversal:before{content:"\f36a"}.fa-creative-commons:before{content:"\f25e"}.fa-watchman-monitoring:before{content:"\e087"}.fa-fonticons:before{content:"\f280"}.fa-weixin:before{content:"\f1d7"}.fa-shirtsinbulk:before{content:"\f214"}.fa-codepen:before{content:"\f1cb"}.fa-git-alt:before{content:"\f841"}.fa-lyft:before{content:"\f3c3"}.fa-rev:before{content:"\f5b2"}.fa-windows:before{content:"\f17a"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-square-viadeo:before,.fa-viadeo-square:before{content:"\f2aa"}.fa-meetup:before{content:"\f2e0"}.fa-centos:before{content:"\f789"}.fa-adn:before{content:"\f170"}.fa-cloudsmith:before{content:"\f384"}.fa-opensuse:before{content:"\e62b"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-dribbble-square:before,.fa-square-dribbble:before{content:"\f397"}.fa-codiepie:before{content:"\f284"}.fa-node:before{content:"\f419"}.fa-mix:before{content:"\f3cb"}.fa-steam:before{content:"\f1b6"}.fa-cc-apple-pay:before{content:"\f416"}.fa-scribd:before{content:"\f28a"}.fa-debian:before{content:"\e60b"}.fa-openid:before{content:"\f19b"}.fa-instalod:before{content:"\e081"}.fa-expeditedssl:before{content:"\f23e"}.fa-sellcast:before{content:"\f2da"}.fa-square-twitter:before,.fa-twitter-square:before{content:"\f081"}.fa-r-project:before{content:"\f4f7"}.fa-delicious:before{content:"\f1a5"}.fa-freebsd:before{content:"\f3a4"}.fa-vuejs:before{content:"\f41f"}.fa-accusoft:before{content:"\f369"}.fa-ioxhost:before{content:"\f208"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-app-store:before{content:"\f36f"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-itunes-note:before{content:"\f3b5"}.fa-golang:before{content:"\e40f"}.fa-kickstarter:before,.fa-square-kickstarter:before{content:"\f3bb"}.fa-grav:before{content:"\f2d6"}.fa-weibo:before{content:"\f18a"}.fa-uncharted:before{content:"\e084"}.fa-firstdraft:before{content:"\f3a1"}.fa-square-youtube:before,.fa-youtube-square:before{content:"\f431"}.fa-wikipedia-w:before{content:"\f266"}.fa-rendact:before,.fa-wpressr:before{content:"\f3e4"}.fa-angellist:before{content:"\f209"}.fa-galactic-republic:before{content:"\f50c"}.fa-nfc-directional:before{content:"\e530"}.fa-skype:before{content:"\f17e"}.fa-joget:before{content:"\f3b7"}.fa-fedora:before{content:"\f798"}.fa-stripe-s:before{content:"\f42a"}.fa-meta:before{content:"\e49b"}.fa-laravel:before{content:"\f3bd"}.fa-hotjar:before{content:"\f3b1"}.fa-bluetooth-b:before{content:"\f294"}.fa-square-letterboxd:before{content:"\e62e"}.fa-sticker-mule:before{content:"\f3f7"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-hips:before{content:"\f452"}.fa-behance:before{content:"\f1b4"}.fa-reddit:before{content:"\f1a1"}.fa-discord:before{content:"\f392"}.fa-chrome:before{content:"\f268"}.fa-app-store-ios:before{content:"\f370"}.fa-cc-discover:before{content:"\f1f2"}.fa-wpbeginner:before{content:"\f297"}.fa-confluence:before{content:"\f78d"}.fa-shoelace:before{content:"\e60c"}.fa-mdb:before{content:"\f8ca"}.fa-dochub:before{content:"\f394"}.fa-accessible-icon:before{content:"\f368"}.fa-ebay:before{content:"\f4f4"}.fa-amazon:before{content:"\f270"}.fa-unsplash:before{content:"\e07c"}.fa-yarn:before{content:"\f7e3"}.fa-square-steam:before,.fa-steam-square:before{content:"\f1b7"}.fa-500px:before{content:"\f26e"}.fa-square-vimeo:before,.fa-vimeo-square:before{content:"\f194"}.fa-asymmetrik:before{content:"\f372"}.fa-font-awesome-flag:before,.fa-font-awesome-logo-full:before,.fa-font-awesome:before{content:"\f2b4"}.fa-gratipay:before{content:"\f184"}.fa-apple:before{content:"\f179"}.fa-hive:before{content:"\e07f"}.fa-gitkraken:before{content:"\f3a6"}.fa-keybase:before{content:"\f4f5"}.fa-apple-pay:before{content:"\f415"}.fa-padlet:before{content:"\e4a0"}.fa-amazon-pay:before{content:"\f42c"}.fa-github-square:before,.fa-square-github:before{content:"\f092"}.fa-stumbleupon:before{content:"\f1a4"}.fa-fedex:before{content:"\f797"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-shopify:before{content:"\e057"}.fa-neos:before{content:"\f612"}.fa-square-threads:before{content:"\e619"}.fa-hackerrank:before{content:"\f5f7"}.fa-researchgate:before{content:"\f4f8"}.fa-swift:before{content:"\f8e1"}.fa-angular:before{content:"\f420"}.fa-speakap:before{content:"\f3f3"}.fa-angrycreative:before{content:"\f36e"}.fa-y-combinator:before{content:"\f23b"}.fa-empire:before{content:"\f1d1"}.fa-envira:before{content:"\f299"}.fa-google-scholar:before{content:"\e63b"}.fa-gitlab-square:before,.fa-square-gitlab:before{content:"\e5ae"}.fa-studiovinari:before{content:"\f3f8"}.fa-pied-piper:before{content:"\f2ae"}.fa-wordpress:before{content:"\f19a"}.fa-product-hunt:before{content:"\f288"}.fa-firefox:before{content:"\f269"}.fa-linode:before{content:"\f2b8"}.fa-goodreads:before{content:"\f3a8"}.fa-odnoklassniki-square:before,.fa-square-odnoklassniki:before{content:"\f264"}.fa-jsfiddle:before{content:"\f1cc"}.fa-sith:before{content:"\f512"}.fa-themeisle:before{content:"\f2b2"}.fa-page4:before{content:"\f3d7"}.fa-hashnode:before{content:"\e499"}.fa-react:before{content:"\f41b"}.fa-cc-paypal:before{content:"\f1f4"}.fa-squarespace:before{content:"\f5be"}.fa-cc-stripe:before{content:"\f1f5"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-bitcoin:before{content:"\f379"}.fa-keycdn:before{content:"\f3ba"}.fa-opera:before{content:"\f26a"}.fa-itch-io:before{content:"\f83a"}.fa-umbraco:before{content:"\f8e8"}.fa-galactic-senate:before{content:"\f50d"}.fa-ubuntu:before{content:"\f7df"}.fa-draft2digital:before{content:"\f396"}.fa-stripe:before{content:"\f429"}.fa-houzz:before{content:"\f27c"}.fa-gg:before{content:"\f260"}.fa-dhl:before{content:"\f790"}.fa-pinterest-square:before,.fa-square-pinterest:before{content:"\f0d3"}.fa-xing:before{content:"\f168"}.fa-blackberry:before{content:"\f37b"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-playstation:before{content:"\f3df"}.fa-quinscape:before{content:"\f459"}.fa-less:before{content:"\f41d"}.fa-blogger-b:before{content:"\f37d"}.fa-opencart:before{content:"\f23d"}.fa-vine:before{content:"\f1ca"}.fa-signal-messenger:before{content:"\e663"}.fa-paypal:before{content:"\f1ed"}.fa-gitlab:before{content:"\f296"}.fa-typo3:before{content:"\f42b"}.fa-reddit-alien:before{content:"\f281"}.fa-yahoo:before{content:"\f19e"}.fa-dailymotion:before{content:"\e052"}.fa-affiliatetheme:before{content:"\f36b"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-bootstrap:before{content:"\f836"}.fa-odnoklassniki:before{content:"\f263"}.fa-nfc-symbol:before{content:"\e531"}.fa-mintbit:before{content:"\e62f"}.fa-ethereum:before{content:"\f42e"}.fa-speaker-deck:before{content:"\f83c"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-patreon:before{content:"\f3d9"}.fa-avianex:before{content:"\f374"}.fa-ello:before{content:"\f5f1"}.fa-gofore:before{content:"\f3a7"}.fa-bimobject:before{content:"\f378"}.fa-brave-reverse:before{content:"\e63d"}.fa-facebook-f:before{content:"\f39e"}.fa-google-plus-square:before,.fa-square-google-plus:before{content:"\f0d4"}.fa-web-awesome:before{content:"\e682"}.fa-mandalorian:before{content:"\f50f"}.fa-first-order-alt:before{content:"\f50a"}.fa-osi:before{content:"\f41a"}.fa-google-wallet:before{content:"\f1ee"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-periscope:before{content:"\f3da"}.fa-fulcrum:before{content:"\f50b"}.fa-cloudscale:before{content:"\f383"}.fa-forumbee:before{content:"\f211"}.fa-mizuni:before{content:"\f3cc"}.fa-schlix:before{content:"\f3ea"}.fa-square-xing:before,.fa-xing-square:before{content:"\f169"}.fa-bandcamp:before{content:"\f2d5"}.fa-wpforms:before{content:"\f298"}.fa-cloudversify:before{content:"\f385"}.fa-usps:before{content:"\f7e1"}.fa-megaport:before{content:"\f5a3"}.fa-magento:before{content:"\f3c4"}.fa-spotify:before{content:"\f1bc"}.fa-optin-monster:before{content:"\f23c"}.fa-fly:before{content:"\f417"}.fa-aviato:before{content:"\f421"}.fa-itunes:before{content:"\f3b4"}.fa-cuttlefish:before{content:"\f38c"}.fa-blogger:before{content:"\f37c"}.fa-flickr:before{content:"\f16e"}.fa-viber:before{content:"\f409"}.fa-soundcloud:before{content:"\f1be"}.fa-digg:before{content:"\f1a6"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-letterboxd:before{content:"\e62d"}.fa-symfony:before{content:"\f83d"}.fa-maxcdn:before{content:"\f136"}.fa-etsy:before{content:"\f2d7"}.fa-facebook-messenger:before{content:"\f39f"}.fa-audible:before{content:"\f373"}.fa-think-peaks:before{content:"\f731"}.fa-bilibili:before{content:"\e3d9"}.fa-erlang:before{content:"\f39d"}.fa-x-twitter:before{content:"\e61b"}.fa-cotton-bureau:before{content:"\f89e"}.fa-dashcube:before{content:"\f210"}.fa-42-group:before,.fa-innosoft:before{content:"\e080"}.fa-stack-exchange:before{content:"\f18d"}.fa-elementor:before{content:"\f430"}.fa-pied-piper-square:before,.fa-square-pied-piper:before{content:"\e01e"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-palfed:before{content:"\f3d8"}.fa-superpowers:before{content:"\f2dd"}.fa-resolving:before{content:"\f3e7"}.fa-xbox:before{content:"\f412"}.fa-square-web-awesome-stroke:before{content:"\e684"}.fa-searchengin:before{content:"\f3eb"}.fa-tiktok:before{content:"\e07b"}.fa-facebook-square:before,.fa-square-facebook:before{content:"\f082"}.fa-renren:before{content:"\f18b"}.fa-linux:before{content:"\f17c"}.fa-glide:before{content:"\f2a5"}.fa-linkedin:before{content:"\f08c"}.fa-hubspot:before{content:"\f3b2"}.fa-deploydog:before{content:"\f38e"}.fa-twitch:before{content:"\f1e8"}.fa-ravelry:before{content:"\f2d9"}.fa-mixer:before{content:"\e056"}.fa-lastfm-square:before,.fa-square-lastfm:before{content:"\f203"}.fa-vimeo:before{content:"\f40a"}.fa-mendeley:before{content:"\f7b3"}.fa-uniregistry:before{content:"\f404"}.fa-figma:before{content:"\f799"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-dropbox:before{content:"\f16b"}.fa-instagram:before{content:"\f16d"}.fa-cmplid:before{content:"\e360"}.fa-upwork:before{content:"\e641"}.fa-facebook:before{content:"\f09a"}.fa-gripfire:before{content:"\f3ac"}.fa-jedi-order:before{content:"\f50e"}.fa-uikit:before{content:"\f403"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-phabricator:before{content:"\f3db"}.fa-ussunnah:before{content:"\f407"}.fa-earlybirds:before{content:"\f39a"}.fa-trade-federation:before{content:"\f513"}.fa-autoprefixer:before{content:"\f41c"}.fa-whatsapp:before{content:"\f232"}.fa-square-upwork:before{content:"\e67c"}.fa-slideshare:before{content:"\f1e7"}.fa-google-play:before{content:"\f3ab"}.fa-viadeo:before{content:"\f2a9"}.fa-line:before{content:"\f3c0"}.fa-google-drive:before{content:"\f3aa"}.fa-servicestack:before{content:"\f3ec"}.fa-simplybuilt:before{content:"\f215"}.fa-bitbucket:before{content:"\f171"}.fa-imdb:before{content:"\f2d8"}.fa-deezer:before{content:"\e077"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-jira:before{content:"\f7b1"}.fa-docker:before{content:"\f395"}.fa-screenpal:before{content:"\e570"}.fa-bluetooth:before{content:"\f293"}.fa-gitter:before{content:"\f426"}.fa-d-and-d:before{content:"\f38d"}.fa-microblog:before{content:"\e01a"}.fa-cc-diners-club:before{content:"\f24c"}.fa-gg-circle:before{content:"\f261"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-yandex:before{content:"\f413"}.fa-readme:before{content:"\f4d5"}.fa-html5:before{content:"\f13b"}.fa-sellsy:before{content:"\f213"}.fa-square-web-awesome:before{content:"\e683"}.fa-sass:before{content:"\f41e"}.fa-wirsindhandwerk:before,.fa-wsh:before{content:"\e2d0"}.fa-buromobelexperte:before{content:"\f37f"}.fa-salesforce:before{content:"\f83b"}.fa-octopus-deploy:before{content:"\e082"}.fa-medapps:before{content:"\f3c6"}.fa-ns8:before{content:"\f3d5"}.fa-pinterest-p:before{content:"\f231"}.fa-apper:before{content:"\f371"}.fa-fort-awesome:before{content:"\f286"}.fa-waze:before{content:"\f83f"}.fa-bluesky:before{content:"\e671"}.fa-cc-jcb:before{content:"\f24b"}.fa-snapchat-ghost:before,.fa-snapchat:before{content:"\f2ab"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-rust:before{content:"\e07a"}.fa-wix:before{content:"\f5cf"}.fa-behance-square:before,.fa-square-behance:before{content:"\f1b5"}.fa-supple:before{content:"\f3f9"}.fa-webflow:before{content:"\e65c"}.fa-rebel:before{content:"\f1d0"}.fa-css3:before{content:"\f13c"}.fa-staylinked:before{content:"\f3f5"}.fa-kaggle:before{content:"\f5fa"}.fa-space-awesome:before{content:"\e5ac"}.fa-deviantart:before{content:"\f1bd"}.fa-cpanel:before{content:"\f388"}.fa-goodreads-g:before{content:"\f3a9"}.fa-git-square:before,.fa-square-git:before{content:"\f1d2"}.fa-square-tumblr:before,.fa-tumblr-square:before{content:"\f174"}.fa-trello:before{content:"\f181"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-get-pocket:before{content:"\f265"}.fa-perbyte:before{content:"\e083"}.fa-grunt:before{content:"\f3ad"}.fa-weebly:before{content:"\f5cc"}.fa-connectdevelop:before{content:"\f20e"}.fa-leanpub:before{content:"\f212"}.fa-black-tie:before{content:"\f27e"}.fa-themeco:before{content:"\f5c6"}.fa-python:before{content:"\f3e2"}.fa-android:before{content:"\f17b"}.fa-bots:before{content:"\e340"}.fa-free-code-camp:before{content:"\f2c5"}.fa-hornbill:before{content:"\f592"}.fa-js:before{content:"\f3b8"}.fa-ideal:before{content:"\e013"}.fa-git:before{content:"\f1d3"}.fa-dev:before{content:"\f6cc"}.fa-sketch:before{content:"\f7c6"}.fa-yandex-international:before{content:"\f414"}.fa-cc-amex:before{content:"\f1f3"}.fa-uber:before{content:"\f402"}.fa-github:before{content:"\f09b"}.fa-php:before{content:"\f457"}.fa-alipay:before{content:"\f642"}.fa-youtube:before{content:"\f167"}.fa-skyatlas:before{content:"\f216"}.fa-firefox-browser:before{content:"\e007"}.fa-replyd:before{content:"\f3e6"}.fa-suse:before{content:"\f7d6"}.fa-jenkins:before{content:"\f3b6"}.fa-twitter:before{content:"\f099"}.fa-rockrms:before{content:"\f3e9"}.fa-pinterest:before{content:"\f0d2"}.fa-buffer:before{content:"\f837"}.fa-npm:before{content:"\f3d4"}.fa-yammer:before{content:"\f840"}.fa-btc:before{content:"\f15a"}.fa-dribbble:before{content:"\f17d"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-internet-explorer:before{content:"\f26b"}.fa-stubber:before{content:"\e5c7"}.fa-telegram-plane:before,.fa-telegram:before{content:"\f2c6"}.fa-old-republic:before{content:"\f510"}.fa-odysee:before{content:"\e5c6"}.fa-square-whatsapp:before,.fa-whatsapp-square:before{content:"\f40c"}.fa-node-js:before{content:"\f3d3"}.fa-edge-legacy:before{content:"\e078"}.fa-slack-hash:before,.fa-slack:before{content:"\f198"}.fa-medrt:before{content:"\f3c8"}.fa-usb:before{content:"\f287"}.fa-tumblr:before{content:"\f173"}.fa-vaadin:before{content:"\f408"}.fa-quora:before{content:"\f2c4"}.fa-square-x-twitter:before{content:"\e61a"}.fa-reacteurope:before{content:"\f75d"}.fa-medium-m:before,.fa-medium:before{content:"\f23a"}.fa-amilia:before{content:"\f36d"}.fa-mixcloud:before{content:"\f289"}.fa-flipboard:before{content:"\f44d"}.fa-viacoin:before{content:"\f237"}.fa-critical-role:before{content:"\f6c9"}.fa-sitrox:before{content:"\e44a"}.fa-discourse:before{content:"\f393"}.fa-joomla:before{content:"\f1aa"}.fa-mastodon:before{content:"\f4f6"}.fa-airbnb:before{content:"\f834"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-buy-n-large:before{content:"\f8a6"}.fa-gulp:before{content:"\f3ae"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-strava:before{content:"\f428"}.fa-ember:before{content:"\f423"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-teamspeak:before{content:"\f4f9"}.fa-pushed:before{content:"\f3e1"}.fa-wordpress-simple:before{content:"\f411"}.fa-nutritionix:before{content:"\f3d6"}.fa-wodu:before{content:"\e088"}.fa-google-pay:before{content:"\e079"}.fa-intercom:before{content:"\f7af"}.fa-zhihu:before{content:"\f63f"}.fa-korvue:before{content:"\f42f"}.fa-pix:before{content:"\e43a"}.fa-steam-symbol:before{content:"\f3f6"}:host,:root{--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-weight:400}:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2"),url(../webfonts/fa-v4compatibility.ttf) format("truetype");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a} \ No newline at end of file diff --git a/exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-brands-400.ttf b/exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-brands-400.ttf deleted file mode 100644 index 1fbb1f7c32d46f5dcb89a50e10d00878ed43f1a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 209128 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%0AUSgWHQiFZ4^`_co**CW&|kdjzuG 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

O#Re#lfxr>sz;}@f8bqwz1up1^t)%zofq!AG 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

xA$6E^?#w4)r)%Sw3|NzgKHve*eJ-FKmx(duR9N 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 JOba38zW{E)cm@Cf diff --git a/exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-brands-400.woff2 b/exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-brands-400.woff2 deleted file mode 100644 index 5d28021697ff1f32507b1bcbcbf9e6a41d0ac99f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117852 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%0DxnTep24BZG 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 G?NiJ&y^c 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 Nb?&+EAr}Jx001#^dC~v? diff --git a/exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-regular-400.ttf b/exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-regular-400.ttf deleted file mode 100644 index 549d68dc023ff6e31b8774d784c2cfcc231e7976..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67860 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%0A>X@lH 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 KISZn8x&9A2X%@x+ diff --git a/exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-regular-400.woff2 b/exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-regular-400.woff2 deleted file mode 100644 index 18400d7fad27fc52cfbabbda495b871bd912d045..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25392 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 z 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 `GTC$U2 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 JQkvHO^gqOStd#%& diff --git a/exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-solid-900.woff2 b/exo/tinychat/static/cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-solid-900.woff2 deleted file mode 100644 index 758dd4f6070c7cb399334ae997ae9ff6523d3b55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156400 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 z 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 S`O1A00000000000000000000 z00001HUcCBAO>IqfgAvW9Lm2G%TNV~eFq=~i4zf3wI2eYk+N|9e*gDQd+!}v8jUQE zEX&d)@q!?$NtP#COIg-qTqyYw=e4{kxRkyvy2R<}7XYY0oDw?^xRU?h>$LlXq|rzt z0WK)%V=#d@(1-Pv0ESL=Vh{O?)w|IhgzKZN7Q8OaGpWW1TrGEshnvVNTGXQY^dsG9|GjhVX32XKmtuFf8Ktyz@y_vrq zK!Ap(!86mnrHyPAqb!e zn% zb%`S^VHyIEuoDIV7=~d)bMsD5W`dln_FSSmijZ zN-V09GsYOlBAq`tetdNN`0hh zzLk{~w7&S2g(B8pq|;yn%maulSL$z`L?%+FI`zfvrKbyd!xui)X(E$6%YV_Aa`Pra zvh31Jvy6~Uo2BfVoSRd#Znx{se#z5tG2g#`2b7sCyL89?{rO^OS@0N!r_2k6=(?SPv7+O;I1D^Sc>ys3hOyf46D>6H z0pD@F{}~%$KLDn2>I+ZlRF_gwrAw)jJXcaBd6qZgG*09|WqBrq%2g(W@+nn9`BbNP zbUYsW1Ua|l5yuHQKO$SY-ud8oJoX84ZpkB#6L5Y+nws9hlKA6}uD9e7&I89eA}!9b z0^*N5x?Ur1*&G3g%&Y^7Uxz;I1;Fn0u_7&uajb=(aOmJjUWw|J^y~3clyKB-sCs~q z|9JrGN9z)sSi&{{H;GqH2%s;RJxYAe8bwNBJUD z3&Qt$RF6ZI$V577PoAX$D4(Z#RF5N>3U0f0iXX%tlh(#2|Eqf#}%tz?|W(%PCbw^6)RmS=fQVLY+C zyxeLno1B{_3(h_=P0r0<6~zz=GRBMfejl;lpD!zhS1vCvw>q8HvT2(95U|l9Zknda z7_XseLCzTV`|}9%{XU;!L7pf!!({-t>QPqW+9wjTlsz(uV@;<@>C{oa@I^QbtTl+J z?s-b5p&A}dl@qip1&1D^eCxXzGuv(RUGp8+on~1ziG+_+B@|N+3qo0yHln>W-p5|T z5?!}5|1PuLHW_>OR!*5W;*168i>NBmkS52r5p68&F{T7@c)1UtI>L_s>a;(uHQqZ4m8}XPR5l3B29O^L z;R~JWg5sY*CUPHRtUbN2PtdlJF>Bf&bjlFOo4mKpu_H<}TQ^&R8`R*o-Qi|K)6N$kPPOph`bH(As9j4Z5wHKVG!TFi{_RU7o?7n?x zp1E(|Y!uDz+jk}aG{NG&A&$U+2)4l)02rsSiNSB#nELj<(nKaQL~r1QYM?@}ip$Yx zG^&<*yp7oAJzULQlxm3M8W8GzgCH1UoA-)4WKN`73xXghD#2qZ7SwCFhO2N4z>U+G zC=d>#s8U>ZU#Lc2)u9??iJm~IItl(Th;qf#f=njz8|vI`?xKt4EUvsS!Q4d`&9!vB z1F`yWwTV`DoV{&|1Xw}<``|RV2A&V#(wrKN#1J4xp(vHP;L^}NrFv$9fc48KaYPYm zP8pKAhGbI|MVOlXD@hVfOE}Rq!gB>a$9c?qu8q&Sn6I9XpSbC!o2G7>s$#=f^csfI z@b*%=_qgFj9M-J&wVb1Wh(yzpYSngKJLVjpWxG8db6!0k7yC#UAv!_BSoCT*^WgX1 zgcLY}X^3G5I4<*L;gl@l>2AQmI6y%G34;k0HF? z_(ASVJrAAwB2Ba0c*C`KxuW0gB6hodEOSy-6h%=d7RB8;!fv-;TBGWNwmmbmY17P% zZJ$k@k9M<5NOx+gOGvj#x}7d*I=Y5V;3QThUZLy-I<^(%OA9f4-qG$(b2sUB3F&sp z>0Ls)0J=>6+Gbz}oC}YGC3rqSo(a$CTlPq`h=?kmlnol58F)HnDOHh*l#iCA(WW_F zunhG3j(WmRF|Q1ubj`uQJfi_$e4T>2UaLUllKx@_XT7+^K8$f3R}xwxJbQ zJ^I~OdM)a{D!)w^v4ht-?qM6wSWt@rj|%J05WufO2&VzKVHi3ntj*vi6FE?!-sjQM zMQfQjFXNNJ9`$#}zVK8SU{RG=pzC&qj14=ki%vM3&Z$ZWQ&`}z@EGMov)wj_+%UX? zZd(oXbEo!fX!D@2MJ(e4itR7}aOYYVDr1GU=9AzQ=5fO?bX2GuSo_@e(XfgGuJNNN z!fY&X1j^-R%^=HX8EJ6CwWfG}Vs;m=GV#NNnNJXWTN;VwFFQf1Sx;YV0K_+7o~=0#(O@d$=UJY~XbXzHWV&JTWU(5I z(HGH_>zcaNwydV6ccWMBjID)@~mc4(=DsL#rKiS%^%z?&j>7h z2)k+Yz6ML!34oazR7GIW@j)*E-66DRZZTC^j*r*OyQpc>yG)JRM;YU?;D#4xTW0a7 zO_AHJv0;oIXq-pz8;@glOIBjLfBP2SxW~`|nDm?pPt1HYVs|Z7zUaRm?&Q{((y_() zMd1F03`<;v=~&DxUnu1u#DlX4wYh0b*5df_^&&dsgLK`n54sP9_Pj6d%L=g#WcnX$@Y8D^OuHAhAYSDG=! zFl-^dP3p|uZ|>j_zmbelWnKK zY_=`4=ZRq!eRZfN_?`Jm=gV1a2Xq z!EhDT1puEB5qu8t)&s8=FhAqxJobX=IObuTh+D!om7h6j+QQ;Q{ICl+cV>n;bN28T zlB(e4HwMks2~Aan5Q4G87!yJWRn-!!&B393K0h>+GR=l1Y?Bl5Q7RxYIs54-C*r2C ztp;7^a884RiQTzGvM=ZjO3p|j1k+SiIF3+NjR_&-Op>n1=b?f1zg1SpHBPS`a3I<^ zlZ$#kvxLaP$nJ1W8aFU=;sa8uZZ@fJG-5aF<=lzxXhw6wWGMd}BQceMnSvXjW`Q%qw?-cn9i7& zj5Uc#FVJ9N5n7rJ?u$%UiZlq`5<_CN?}ws5A&9w}oF*H#>yn@rFcvh2fJR((-$lxI zEXks#Lm4MtvUN>Wg=Omor>#^<%M$j8Ed&5}{DU&a3`MalTX1if=FZJ#=guWcB!^1j^y4_^azjz9wF^hD9&_hr7o}Vdolu8w z(0Y0UX&~4G!iBwn+scfmGex&lrPI<`e787N{B9~zppDaT_FHm5{r~%K(7HafZ2~SN z3L!SI1Nz!g-ARD{7iJgXOp;{pNp&n#KojH8w7+3nz>qas=rK=WQ`5ULcDi?sJ`!g4 z2E~q8G<0Ntk&gCdh??BgoK9da{Ac^$(hS{!@i@F~*c%5;t$WQ9Vm(Qh2%h!B%eImHdQCQ1crO=O^02Qe(PqUzoeS)s@jvl}zr~+|X;aeHsquE^N%w<9N=U zID3sU>6otDwy@OP!jId%iPGt+VRM7H=@|R?zQVcl00Y-qi;lDw!esYx(l_GI@)CBy z9yrv-rM{7^l4mN%Yr5|R%xzJdS-flXVst4rzrgi;K6KI$XJU?cEXQiFNt&6Rao;bb z5hseriQ<`K(PK%Xq_YK<=X8v=jr%rE36lV{{(t>%6^$N%4jV(54?i+(BJeUU3z+Za z+>AfsdO_B}@YG_1F+G#c#bT)-<}0h~rPzaq9(w3ukAJn>Zo6$8D?jzr>@80{HGAE* zZQFKVMNj?u>#xkR*sqq^W#V7Kx~4@Z9ewoCN6+7T@4fdv{hfD8d+(j^gV2Xz5aRJ0 zO!J0#{QU^_{_H*?gBo_nQ3!!-^nmQwMG0xwS-94X4P{Eh-mjYIZpyC(wIJo^oMdha zgB0_wK}J;9Eu?cxs)XIf>ghC5I$a;zE&mIPTGC)Kg;-28S1id4mR9+kfevV3{J|DvtbC{|SG1F9%iTJ?3tpZYUvJCgmSN;QV9SLgCQ}nm8OnE0( zj@_ju)a})8wYA!F`Z!bBbH;!%Zal_Seh|Nke{7DLmsthtb8&*b!amBr%zn-O*XcUH z$LsNT0i?(Q0RVt`V4$9yge)OXm3NQek%?hB&Wu&z7!76Y&FDzuO~%9uj4&2lJj5ih z5I@l5yHg6qKq?1NA^*e>_K0OWC~%00;SePgtDYDQ8|ZaL$9m;{#>8PX$yg{TCo>7u zl|QOe5)Ne#XiXl!r8&JW?4CT`o}7wgeqyQICG13U&55mExV3fAR7;-N8qf9Q*02}J zZrDA3OLJPbC&G^GwIbQ-hHZrR+93Wv0&d+o3zgTOD8ys@h9m8&wpOW46^lPA2+ zU6-{&wNPHVOMTIbTo-QbHly}2Z^l+(ZfKySVm*hB#9ncP>q?w}NvcDl_V$;9FQAO2ZO2z2d2*wz z=%R@TZH%LhEflWrWJvDCuEbq@3K1f7(L)_Y6tS_V_~wlkxMAH&hsJGVPe6r&eyt3d S2VsmQO06}ypn7Xr@LvO5t4+KB diff --git a/exo/tinychat/static/fonts.googleapis.com/css2 b/exo/tinychat/static/fonts.googleapis.com/css2 deleted file mode 100644 index 82f55e83..00000000 --- a/exo/tinychat/static/fonts.googleapis.com/css2 +++ /dev/null @@ -1,7 +0,0 @@ -@font-face { - font-family: 'Megrim'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/megrim/v16/46kulbz5WjvLqJZlbQ.ttf) format('truetype'); -} diff --git a/exo/tinychat/static/unpkg.com/@highlightjs/cdn-assets@11.9.0/highlight.min.js b/exo/tinychat/static/unpkg.com/@highlightjs/cdn-assets@11.9.0/highlight.min.js deleted file mode 100644 index 5d699ae6..00000000 --- a/exo/tinychat/static/unpkg.com/@highlightjs/cdn-assets@11.9.0/highlight.min.js +++ /dev/null @@ -1,1213 +0,0 @@ -/*! - Highlight.js v11.9.0 (git: f47103d4f1) - (c) 2006-2023 undefined and other contributors - License: BSD-3-Clause - */ -var hljs=function(){"use strict";function e(n){ -return n instanceof Map?n.clear=n.delete=n.set=()=>{ -throw Error("map is read-only")}:n instanceof Set&&(n.add=n.clear=n.delete=()=>{ -throw Error("set is read-only") -}),Object.freeze(n),Object.getOwnPropertyNames(n).forEach((t=>{ -const a=n[t],i=typeof a;"object"!==i&&"function"!==i||Object.isFrozen(a)||e(a) -})),n}class n{constructor(e){ -void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} -ignoreMatch(){this.isMatchIgnored=!0}}function t(e){ -return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") -}function a(e,...n){const t=Object.create(null);for(const n in e)t[n]=e[n] -;return n.forEach((e=>{for(const n in e)t[n]=e[n]})),t}const i=e=>!!e.scope -;class r{constructor(e,n){ -this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){ -this.buffer+=t(e)}openNode(e){if(!i(e))return;const n=((e,{prefix:n})=>{ -if(e.startsWith("language:"))return e.replace("language:","language-") -;if(e.includes(".")){const t=e.split(".") -;return[`${n}${t.shift()}`,...t.map(((e,n)=>`${e}${"_".repeat(n+1)}`))].join(" ") -}return`${n}${e}`})(e.scope,{prefix:this.classPrefix});this.span(n)} -closeNode(e){i(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ -this.buffer+=``}}const s=(e={})=>{const n={children:[]} -;return Object.assign(n,e),n};class o{constructor(){ -this.rootNode=s(),this.stack=[this.rootNode]}get top(){ -return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ -this.top.children.push(e)}openNode(e){const n=s({scope:e}) -;this.add(n),this.stack.push(n)}closeNode(){ -if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ -for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} -walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){ -return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n), -n.children.forEach((n=>this._walk(e,n))),e.closeNode(n)),e}static _collapse(e){ -"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ -o._collapse(e)})))}}class l extends o{constructor(e){super(),this.options=e} -addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ -this.closeNode()}__addSublanguage(e,n){const t=e.root -;n&&(t.scope="language:"+n),this.add(t)}toHTML(){ -return new r(this,this.options).value()}finalize(){ -return this.closeAllNodes(),!0}}function c(e){ -return e?"string"==typeof e?e:e.source:null}function d(e){return b("(?=",e,")")} -function g(e){return b("(?:",e,")*")}function u(e){return b("(?:",e,")?")} -function b(...e){return e.map((e=>c(e))).join("")}function m(...e){const n=(e=>{ -const n=e[e.length-1] -;return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{} -})(e);return"("+(n.capture?"":"?:")+e.map((e=>c(e))).join("|")+")"} -function p(e){return RegExp(e.toString()+"|").exec("").length-1} -const _=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ -;function h(e,{joinWith:n}){let t=0;return e.map((e=>{t+=1;const n=t -;let a=c(e),i="";for(;a.length>0;){const e=_.exec(a);if(!e){i+=a;break} -i+=a.substring(0,e.index), -a=a.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?i+="\\"+(Number(e[1])+n):(i+=e[0], -"("===e[0]&&t++)}return i})).map((e=>`(${e})`)).join(n)} -const f="[a-zA-Z]\\w*",E="[a-zA-Z_]\\w*",y="\\b\\d+(\\.\\d+)?",N="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",w="\\b(0b[01]+)",v={ -begin:"\\\\[\\s\\S]",relevance:0},O={scope:"string",begin:"'",end:"'", -illegal:"\\n",contains:[v]},k={scope:"string",begin:'"',end:'"',illegal:"\\n", -contains:[v]},x=(e,n,t={})=>{const i=a({scope:"comment",begin:e,end:n, -contains:[]},t);i.contains.push({scope:"doctag", -begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", -end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) -;const r=m("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) -;return i.contains.push({begin:b(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),i -},M=x("//","$"),S=x("/\\*","\\*/"),A=x("#","$");var C=Object.freeze({ -__proto__:null,APOS_STRING_MODE:O,BACKSLASH_ESCAPE:v,BINARY_NUMBER_MODE:{ -scope:"number",begin:w,relevance:0},BINARY_NUMBER_RE:w,COMMENT:x, -C_BLOCK_COMMENT_MODE:S,C_LINE_COMMENT_MODE:M,C_NUMBER_MODE:{scope:"number", -begin:N,relevance:0},C_NUMBER_RE:N,END_SAME_AS_BEGIN:e=>Object.assign(e,{ -"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{ -n.data._beginMatch!==e[1]&&n.ignoreMatch()}}),HASH_COMMENT_MODE:A,IDENT_RE:f, -MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+E,relevance:0}, -NUMBER_MODE:{scope:"number",begin:y,relevance:0},NUMBER_RE:y, -PHRASAL_WORDS_MODE:{ -begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ -},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, -end:/\/[gimuy]*/,contains:[v,{begin:/\[/,end:/\]/,relevance:0,contains:[v]}]}, -RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", -SHEBANG:(e={})=>{const n=/^#![ ]*\// -;return e.binary&&(e.begin=b(n,/.*\b/,e.binary,/\b.*/)),a({scope:"meta",begin:n, -end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)}, -TITLE_MODE:{scope:"title",begin:f,relevance:0},UNDERSCORE_IDENT_RE:E, -UNDERSCORE_TITLE_MODE:{scope:"title",begin:E,relevance:0}});function T(e,n){ -"."===e.input[e.index-1]&&n.ignoreMatch()}function R(e,n){ -void 0!==e.className&&(e.scope=e.className,delete e.className)}function D(e,n){ -n&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", -e.__beforeBegin=T,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, -void 0===e.relevance&&(e.relevance=0))}function I(e,n){ -Array.isArray(e.illegal)&&(e.illegal=m(...e.illegal))}function L(e,n){ -if(e.match){ -if(e.begin||e.end)throw Error("begin & end are not supported with match") -;e.begin=e.match,delete e.match}}function B(e,n){ -void 0===e.relevance&&(e.relevance=1)}const $=(e,n)=>{if(!e.beforeMatch)return -;if(e.starts)throw Error("beforeMatch cannot be used with starts") -;const t=Object.assign({},e);Object.keys(e).forEach((n=>{delete e[n] -})),e.keywords=t.keywords,e.begin=b(t.beforeMatch,d(t.begin)),e.starts={ -relevance:0,contains:[Object.assign(t,{endsParent:!0})] -},e.relevance=0,delete t.beforeMatch -},z=["of","and","for","in","not","or","if","then","parent","list","value"],F="keyword" -;function U(e,n,t=F){const a=Object.create(null) -;return"string"==typeof e?i(t,e.split(" ")):Array.isArray(e)?i(t,e):Object.keys(e).forEach((t=>{ -Object.assign(a,U(e[t],n,t))})),a;function i(e,t){ -n&&(t=t.map((e=>e.toLowerCase()))),t.forEach((n=>{const t=n.split("|") -;a[t[0]]=[e,j(t[0],t[1])]}))}}function j(e,n){ -return n?Number(n):(e=>z.includes(e.toLowerCase()))(e)?0:1}const P={},K=e=>{ -console.error(e)},H=(e,...n)=>{console.log("WARN: "+e,...n)},q=(e,n)=>{ -P[`${e}/${n}`]||(console.log(`Deprecated as of ${e}. ${n}`),P[`${e}/${n}`]=!0) -},G=Error();function Z(e,n,{key:t}){let a=0;const i=e[t],r={},s={} -;for(let e=1;e<=n.length;e++)s[e+a]=i[e],r[e+a]=!0,a+=p(n[e-1]) -;e[t]=s,e[t]._emit=r,e[t]._multi=!0}function W(e){(e=>{ -e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, -delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ -_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope -}),(e=>{if(Array.isArray(e.begin)){ -if(e.skip||e.excludeBegin||e.returnBegin)throw K("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), -G -;if("object"!=typeof e.beginScope||null===e.beginScope)throw K("beginScope must be object"), -G;Z(e,e.begin,{key:"beginScope"}),e.begin=h(e.begin,{joinWith:""})}})(e),(e=>{ -if(Array.isArray(e.end)){ -if(e.skip||e.excludeEnd||e.returnEnd)throw K("skip, excludeEnd, returnEnd not compatible with endScope: {}"), -G -;if("object"!=typeof e.endScope||null===e.endScope)throw K("endScope must be object"), -G;Z(e,e.end,{key:"endScope"}),e.end=h(e.end,{joinWith:""})}})(e)}function Q(e){ -function n(n,t){ -return RegExp(c(n),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(t?"g":"")) -}class t{constructor(){ -this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} -addRule(e,n){ -n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]), -this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) -;const e=this.regexes.map((e=>e[1]));this.matcherRe=n(h(e,{joinWith:"|" -}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex -;const n=this.matcherRe.exec(e);if(!n)return null -;const t=n.findIndex(((e,n)=>n>0&&void 0!==e)),a=this.matchIndexes[t] -;return n.splice(0,t),Object.assign(n,a)}}class i{constructor(){ -this.rules=[],this.multiRegexes=[], -this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ -if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t -;return this.rules.slice(e).forEach((([e,t])=>n.addRule(e,t))), -n.compile(),this.multiRegexes[e]=n,n}resumingScanAtSamePosition(){ -return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,n){ -this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){ -const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex -;let t=n.exec(e) -;if(this.resumingScanAtSamePosition())if(t&&t.index===this.lastIndex);else{ -const n=this.getMatcher(0);n.lastIndex=this.lastIndex+1,t=n.exec(e)} -return t&&(this.regexIndex+=t.position+1, -this.regexIndex===this.count&&this.considerAll()),t}} -if(e.compilerExtensions||(e.compilerExtensions=[]), -e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") -;return e.classNameAliases=a(e.classNameAliases||{}),function t(r,s){const o=r -;if(r.isCompiled)return o -;[R,L,W,$].forEach((e=>e(r,s))),e.compilerExtensions.forEach((e=>e(r,s))), -r.__beforeBegin=null,[D,I,B].forEach((e=>e(r,s))),r.isCompiled=!0;let l=null -;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), -l=r.keywords.$pattern, -delete r.keywords.$pattern),l=l||/\w+/,r.keywords&&(r.keywords=U(r.keywords,e.case_insensitive)), -o.keywordPatternRe=n(l,!0), -s&&(r.begin||(r.begin=/\B|\b/),o.beginRe=n(o.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), -r.end&&(o.endRe=n(o.end)), -o.terminatorEnd=c(o.end)||"",r.endsWithParent&&s.terminatorEnd&&(o.terminatorEnd+=(r.end?"|":"")+s.terminatorEnd)), -r.illegal&&(o.illegalRe=n(r.illegal)), -r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((n=>a(e,{ -variants:null},n)))),e.cachedVariants?e.cachedVariants:X(e)?a(e,{ -starts:e.starts?a(e.starts):null -}):Object.isFrozen(e)?a(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{t(e,o) -})),r.starts&&t(r.starts,s),o.matcher=(e=>{const n=new i -;return e.contains.forEach((e=>n.addRule(e.begin,{rule:e,type:"begin" -}))),e.terminatorEnd&&n.addRule(e.terminatorEnd,{type:"end" -}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n})(o),o}(e)}function X(e){ -return!!e&&(e.endsWithParent||X(e.starts))}class V extends Error{ -constructor(e,n){super(e),this.name="HTMLInjectionError",this.html=n}} -const J=t,Y=a,ee=Symbol("nomatch"),ne=t=>{ -const a=Object.create(null),i=Object.create(null),r=[];let s=!0 -;const o="Could not find the language '{}', did you forget to load/include a language module?",c={ -disableAutodetect:!0,name:"Plain text",contains:[]};let p={ -ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, -languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", -cssSelector:"pre code",languages:null,__emitter:l};function _(e){ -return p.noHighlightRe.test(e)}function h(e,n,t){let a="",i="" -;"object"==typeof n?(a=e, -t=n.ignoreIllegals,i=n.language):(q("10.7.0","highlight(lang, code, ...args) has been deprecated."), -q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), -i=e,a=n),void 0===t&&(t=!0);const r={code:a,language:i};x("before:highlight",r) -;const s=r.result?r.result:f(r.language,r.code,t) -;return s.code=r.code,x("after:highlight",s),s}function f(e,t,i,r){ -const l=Object.create(null);function c(){if(!x.keywords)return void S.addText(A) -;let e=0;x.keywordPatternRe.lastIndex=0;let n=x.keywordPatternRe.exec(A),t="" -;for(;n;){t+=A.substring(e,n.index) -;const i=w.case_insensitive?n[0].toLowerCase():n[0],r=(a=i,x.keywords[a]);if(r){ -const[e,a]=r -;if(S.addText(t),t="",l[i]=(l[i]||0)+1,l[i]<=7&&(C+=a),e.startsWith("_"))t+=n[0];else{ -const t=w.classNameAliases[e]||e;g(n[0],t)}}else t+=n[0] -;e=x.keywordPatternRe.lastIndex,n=x.keywordPatternRe.exec(A)}var a -;t+=A.substring(e),S.addText(t)}function d(){null!=x.subLanguage?(()=>{ -if(""===A)return;let e=null;if("string"==typeof x.subLanguage){ -if(!a[x.subLanguage])return void S.addText(A) -;e=f(x.subLanguage,A,!0,M[x.subLanguage]),M[x.subLanguage]=e._top -}else e=E(A,x.subLanguage.length?x.subLanguage:null) -;x.relevance>0&&(C+=e.relevance),S.__addSublanguage(e._emitter,e.language) -})():c(),A=""}function g(e,n){ -""!==e&&(S.startScope(n),S.addText(e),S.endScope())}function u(e,n){let t=1 -;const a=n.length-1;for(;t<=a;){if(!e._emit[t]){t++;continue} -const a=w.classNameAliases[e[t]]||e[t],i=n[t];a?g(i,a):(A=i,c(),A=""),t++}} -function b(e,n){ -return e.scope&&"string"==typeof e.scope&&S.openNode(w.classNameAliases[e.scope]||e.scope), -e.beginScope&&(e.beginScope._wrap?(g(A,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), -A=""):e.beginScope._multi&&(u(e.beginScope,n),A="")),x=Object.create(e,{parent:{ -value:x}}),x}function m(e,t,a){let i=((e,n)=>{const t=e&&e.exec(n) -;return t&&0===t.index})(e.endRe,a);if(i){if(e["on:end"]){const a=new n(e) -;e["on:end"](t,a),a.isMatchIgnored&&(i=!1)}if(i){ -for(;e.endsParent&&e.parent;)e=e.parent;return e}} -if(e.endsWithParent)return m(e.parent,t,a)}function _(e){ -return 0===x.matcher.regexIndex?(A+=e[0],1):(D=!0,0)}function h(e){ -const n=e[0],a=t.substring(e.index),i=m(x,e,a);if(!i)return ee;const r=x -;x.endScope&&x.endScope._wrap?(d(), -g(n,x.endScope._wrap)):x.endScope&&x.endScope._multi?(d(), -u(x.endScope,e)):r.skip?A+=n:(r.returnEnd||r.excludeEnd||(A+=n), -d(),r.excludeEnd&&(A=n));do{ -x.scope&&S.closeNode(),x.skip||x.subLanguage||(C+=x.relevance),x=x.parent -}while(x!==i.parent);return i.starts&&b(i.starts,e),r.returnEnd?0:n.length} -let y={};function N(a,r){const o=r&&r[0];if(A+=a,null==o)return d(),0 -;if("begin"===y.type&&"end"===r.type&&y.index===r.index&&""===o){ -if(A+=t.slice(r.index,r.index+1),!s){const n=Error(`0 width match regex (${e})`) -;throw n.languageName=e,n.badRule=y.rule,n}return 1} -if(y=r,"begin"===r.type)return(e=>{ -const t=e[0],a=e.rule,i=new n(a),r=[a.__beforeBegin,a["on:begin"]] -;for(const n of r)if(n&&(n(e,i),i.isMatchIgnored))return _(t) -;return a.skip?A+=t:(a.excludeBegin&&(A+=t), -d(),a.returnBegin||a.excludeBegin||(A=t)),b(a,e),a.returnBegin?0:t.length})(r) -;if("illegal"===r.type&&!i){ -const e=Error('Illegal lexeme "'+o+'" for mode "'+(x.scope||"")+'"') -;throw e.mode=x,e}if("end"===r.type){const e=h(r);if(e!==ee)return e} -if("illegal"===r.type&&""===o)return 1 -;if(R>1e5&&R>3*r.index)throw Error("potential infinite loop, way more iterations than matches") -;return A+=o,o.length}const w=v(e) -;if(!w)throw K(o.replace("{}",e)),Error('Unknown language: "'+e+'"') -;const O=Q(w);let k="",x=r||O;const M={},S=new p.__emitter(p);(()=>{const e=[] -;for(let n=x;n!==w;n=n.parent)n.scope&&e.unshift(n.scope) -;e.forEach((e=>S.openNode(e)))})();let A="",C=0,T=0,R=0,D=!1;try{ -if(w.__emitTokens)w.__emitTokens(t,S);else{for(x.matcher.considerAll();;){ -R++,D?D=!1:x.matcher.considerAll(),x.matcher.lastIndex=T -;const e=x.matcher.exec(t);if(!e)break;const n=N(t.substring(T,e.index),e) -;T=e.index+n}N(t.substring(T))}return S.finalize(),k=S.toHTML(),{language:e, -value:k,relevance:C,illegal:!1,_emitter:S,_top:x}}catch(n){ -if(n.message&&n.message.includes("Illegal"))return{language:e,value:J(t), -illegal:!0,relevance:0,_illegalBy:{message:n.message,index:T, -context:t.slice(T-100,T+100),mode:n.mode,resultSoFar:k},_emitter:S};if(s)return{ -language:e,value:J(t),illegal:!1,relevance:0,errorRaised:n,_emitter:S,_top:x} -;throw n}}function E(e,n){n=n||p.languages||Object.keys(a);const t=(e=>{ -const n={value:J(e),illegal:!1,relevance:0,_top:c,_emitter:new p.__emitter(p)} -;return n._emitter.addText(e),n})(e),i=n.filter(v).filter(k).map((n=>f(n,e,!1))) -;i.unshift(t);const r=i.sort(((e,n)=>{ -if(e.relevance!==n.relevance)return n.relevance-e.relevance -;if(e.language&&n.language){if(v(e.language).supersetOf===n.language)return 1 -;if(v(n.language).supersetOf===e.language)return-1}return 0})),[s,o]=r,l=s -;return l.secondBest=o,l}function y(e){let n=null;const t=(e=>{ -let n=e.className+" ";n+=e.parentNode?e.parentNode.className:"" -;const t=p.languageDetectRe.exec(n);if(t){const n=v(t[1]) -;return n||(H(o.replace("{}",t[1])), -H("Falling back to no-highlight mode for this block.",e)),n?t[1]:"no-highlight"} -return n.split(/\s+/).find((e=>_(e)||v(e)))})(e);if(_(t))return -;if(x("before:highlightElement",{el:e,language:t -}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) -;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), -console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), -console.warn("The element with unescaped HTML:"), -console.warn(e)),p.throwUnescapedHTML))throw new V("One of your code blocks includes unescaped HTML.",e.innerHTML) -;n=e;const a=n.textContent,r=t?h(a,{language:t,ignoreIllegals:!0}):E(a) -;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,n,t)=>{const a=n&&i[n]||t -;e.classList.add("hljs"),e.classList.add("language-"+a) -})(e,t,r.language),e.result={language:r.language,re:r.relevance, -relevance:r.relevance},r.secondBest&&(e.secondBest={ -language:r.secondBest.language,relevance:r.secondBest.relevance -}),x("after:highlightElement",{el:e,result:r,text:a})}let N=!1;function w(){ -"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(y):N=!0 -}function v(e){return e=(e||"").toLowerCase(),a[e]||a[i[e]]} -function O(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ -i[e.toLowerCase()]=n}))}function k(e){const n=v(e) -;return n&&!n.disableAutodetect}function x(e,n){const t=e;r.forEach((e=>{ -e[t]&&e[t](n)}))} -"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ -N&&w()}),!1),Object.assign(t,{highlight:h,highlightAuto:E,highlightAll:w, -highlightElement:y, -highlightBlock:e=>(q("10.7.0","highlightBlock will be removed entirely in v12.0"), -q("10.7.0","Please use highlightElement now."),y(e)),configure:e=>{p=Y(p,e)}, -initHighlighting:()=>{ -w(),q("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, -initHighlightingOnLoad:()=>{ -w(),q("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") -},registerLanguage:(e,n)=>{let i=null;try{i=n(t)}catch(n){ -if(K("Language definition for '{}' could not be registered.".replace("{}",e)), -!s)throw n;K(n),i=c} -i.name||(i.name=e),a[e]=i,i.rawDefinition=n.bind(null,t),i.aliases&&O(i.aliases,{ -languageName:e})},unregisterLanguage:e=>{delete a[e] -;for(const n of Object.keys(i))i[n]===e&&delete i[n]}, -listLanguages:()=>Object.keys(a),getLanguage:v,registerAliases:O, -autoDetection:k,inherit:Y,addPlugin:e=>{(e=>{ -e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=n=>{ -e["before:highlightBlock"](Object.assign({block:n.el},n)) -}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=n=>{ -e["after:highlightBlock"](Object.assign({block:n.el},n))})})(e),r.push(e)}, -removePlugin:e=>{const n=r.indexOf(e);-1!==n&&r.splice(n,1)}}),t.debugMode=()=>{ -s=!1},t.safeMode=()=>{s=!0},t.versionString="11.9.0",t.regex={concat:b, -lookahead:d,either:m,optional:u,anyNumberOfTimes:g} -;for(const n in C)"object"==typeof C[n]&&e(C[n]);return Object.assign(t,C),t -},te=ne({});te.newInstance=()=>ne({});var ae=te;const ie=e=>({IMPORTANT:{ -scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{ -scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/}, -FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/}, -ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", -contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ -scope:"number", -begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", -relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/} -}),re=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],se=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],oe=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],le=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],ce=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","justify-content","left","letter-spacing","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse(),de=oe.concat(le) -;var ge="[0-9](_*[0-9])*",ue=`\\.(${ge})`,be="[0-9a-fA-F](_*[0-9a-fA-F])*",me={ -className:"number",variants:[{ -begin:`(\\b(${ge})((${ue})|\\.)?|(${ue}))[eE][+-]?(${ge})[fFdD]?\\b`},{ -begin:`\\b(${ge})((${ue})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ -begin:`(${ue})[fFdD]?\\b`},{begin:`\\b(${ge})[fFdD]\\b`},{ -begin:`\\b0[xX]((${be})\\.?|(${be})?\\.(${be}))[pP][+-]?(${ge})[fFdD]?\\b`},{ -begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${be})[lL]?\\b`},{ -begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], -relevance:0};function pe(e,n,t){return-1===t?"":e.replace(n,(a=>pe(e,n,t-1)))} -const _e="[A-Za-z$_][0-9A-Za-z$_]*",he=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],fe=["true","false","null","undefined","NaN","Infinity"],Ee=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],ye=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],Ne=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],we=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],ve=[].concat(Ne,Ee,ye) -;function Oe(e){const n=e.regex,t=_e,a={begin:/<[A-Za-z0-9\\._:-]+/, -end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ -const t=e[0].length+e.index,a=e.input[t] -;if("<"===a||","===a)return void n.ignoreMatch();let i -;">"===a&&(((e,{after:n})=>{const t="",M={ -match:[/const|var|let/,/\s+/,t,/\s*/,/=\s*/,/(async\s*)?/,n.lookahead(x)], -keywords:"async",className:{1:"keyword",3:"title.function"},contains:[f]} -;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:i,exports:{ -PARAMS_CONTAINS:h,CLASS_REFERENCE:y},illegal:/#(?![$_A-z])/, -contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ -label:"use_strict",className:"meta",relevance:10, -begin:/^\s*['"]use (strict|asm)['"]/ -},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,d,g,u,b,m,{match:/\$\d+/},l,y,{ -className:"attr",begin:t+n.lookahead(":"),relevance:0},M,{ -begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", -keywords:"return throw case",relevance:0,contains:[m,e.REGEXP_MODE,{ -className:"function",begin:x,returnBegin:!0,end:"\\s*=>",contains:[{ -className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{ -className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0, -excludeEnd:!0,keywords:i,contains:h}]}]},{begin:/,/,relevance:0},{match:/\s+/, -relevance:0},{variants:[{begin:"<>",end:""},{ -match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:a.begin, -"on:begin":a.isTrulyOpeningTag,end:a.end}],subLanguage:"xml",contains:[{ -begin:a.begin,end:a.end,skip:!0,contains:["self"]}]}]},N,{ -beginKeywords:"while if switch catch for"},{ -begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", -returnBegin:!0,label:"func.def",contains:[f,e.inherit(e.TITLE_MODE,{begin:t, -className:"title.function"})]},{match:/\.\.\./,relevance:0},O,{match:"\\$"+t, -relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, -contains:[f]},w,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, -className:"variable.constant"},E,k,{match:/\$[(.]/}]}} -const ke=e=>b(/\b/,e,/\w$/.test(e)?/\b/:/\B/),xe=["Protocol","Type"].map(ke),Me=["init","self"].map(ke),Se=["Any","Self"],Ae=["actor","any","associatedtype","async","await",/as\?/,/as!/,"as","borrowing","break","case","catch","class","consume","consuming","continue","convenience","copy","default","defer","deinit","didSet","distributed","do","dynamic","each","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","macro","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],Ce=["false","nil","true"],Te=["assignment","associativity","higherThan","left","lowerThan","none","right"],Re=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warning"],De=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],Ie=m(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),Le=m(Ie,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),Be=b(Ie,Le,"*"),$e=m(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),ze=m($e,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),Fe=b($e,ze,"*"),Ue=b(/[A-Z]/,ze,"*"),je=["attached","autoclosure",b(/convention\(/,m("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","freestanding","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",b(/objc\(/,Fe,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","Sendable","testable","UIApplicationMain","unchecked","unknown","usableFromInline","warn_unqualified_access"],Pe=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"] -;var Ke=Object.freeze({__proto__:null,grmr_bash:e=>{const n=e.regex,t={},a={ -begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]} -;Object.assign(t,{className:"variable",variants:[{ -begin:n.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},a]});const i={ -className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},r={ -begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/, -end:/(\w+)/,className:"string"})]}},s={className:"string",begin:/"/,end:/"/, -contains:[e.BACKSLASH_ESCAPE,t,i]};i.contains.push(s);const o={begin:/\$?\(\(/, -end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t] -},l=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 -}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, -contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ -name:"Bash",aliases:["sh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/, -keyword:["if","then","else","elif","fi","for","while","until","in","do","done","case","esac","function","select"], -literal:["true","false"], -built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"] -},contains:[l,e.SHEBANG(),c,o,e.HASH_COMMENT_MODE,r,{match:/(\/[a-z._-]+)+/},s,{ -match:/\\"/},{className:"string",begin:/'/,end:/'/},{match:/\\'/},t]}}, -grmr_c:e=>{const n=e.regex,t=e.COMMENT("//","$",{contains:[{begin:/\\\n/}] -}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ -className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{ -match:/\batomic_[a-z]{3,6}\b/}]},o={className:"string",variants:[{ -begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ -begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", -end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ -begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ -className:"number",variants:[{begin:"\\b(0b[01']+)"},{ -begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" -},{ -begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" -}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ -keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" -},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ -className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ -className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 -},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ -keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"], -type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","complex","bool","imaginary"], -literal:"true false NULL", -built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr" -},b=[c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],m={variants:[{begin:/=/,end:/;/},{ -begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], -keywords:u,contains:b.concat([{begin:/\(/,end:/\)/,keywords:u, -contains:b.concat(["self"]),relevance:0}]),relevance:0},p={ -begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, -keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ -begin:g,returnBegin:!0,contains:[e.inherit(d,{className:"title.function"})], -relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/, -keywords:u,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/, -end:/\)/,keywords:u,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s] -}]},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u, -disableAutodetect:!0,illegal:"=]/,contains:[{ -beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c, -strings:o,keywords:u}}},grmr_cpp:e=>{const n=e.regex,t=e.COMMENT("//","$",{ -contains:[{begin:/\\\n/}] -}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="(?!struct)("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ -className:"type",begin:"\\b[a-z\\d_]*_t\\b"},o={className:"string",variants:[{ -begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ -begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", -end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ -begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ -className:"number",variants:[{begin:"\\b(0b[01']+)"},{ -begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" -},{ -begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" -}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ -keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" -},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ -className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ -className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 -},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ -type:["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"], -keyword:["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"], -literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"], -_type_hints:["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"] -},b={className:"function.dispatch",relevance:0,keywords:{ -_hint:["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"] -}, -begin:n.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,n.lookahead(/(<[^<>]+>|)\s*\(/)) -},m=[b,c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],p={variants:[{begin:/=/,end:/;/},{ -begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], -keywords:u,contains:m.concat([{begin:/\(/,end:/\)/,keywords:u, -contains:m.concat(["self"]),relevance:0}]),relevance:0},_={className:"function", -begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, -keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ -begin:g,returnBegin:!0,contains:[d],relevance:0},{begin:/::/,relevance:0},{ -begin:/:/,endsWithParent:!0,contains:[o,l]},{relevance:0,match:/,/},{ -className:"params",begin:/\(/,end:/\)/,keywords:u,relevance:0, -contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/,end:/\)/,keywords:u, -relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s]}] -},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C++", -aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:u,illegal:"",keywords:u,contains:["self",s]},{begin:e.IDENT_RE+"::",keywords:u},{ -match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/], -className:{1:"keyword",3:"title.class"}}])}},grmr_csharp:e=>{const n={ -keyword:["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","scoped","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]), -built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"], -literal:["default","false","null","true"]},t=e.inherit(e.TITLE_MODE,{ -begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{ -begin:"\\b(0b[01']+)"},{ -begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{ -begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" -}],relevance:0},i={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}] -},r=e.inherit(i,{illegal:/\n/}),s={className:"subst",begin:/\{/,end:/\}/, -keywords:n},o=e.inherit(s,{illegal:/\n/}),l={className:"string",begin:/\$"/, -end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/ -},e.BACKSLASH_ESCAPE,o]},c={className:"string",begin:/\$@"/,end:'"',contains:[{ -begin:/\{\{/},{begin:/\}\}/},{begin:'""'},s]},d=e.inherit(c,{illegal:/\n/, -contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},o]}) -;s.contains=[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE], -o.contains=[d,l,r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{ -illegal:/\n/})];const g={variants:[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] -},u={begin:"<",end:">",contains:[{beginKeywords:"in out"},t] -},b=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",m={ -begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"], -keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0, -contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{ -begin:"\x3c!--|--\x3e"},{begin:""}]}] -}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#", -end:"$",keywords:{ -keyword:"if else elif endif define undef warning error line region endregion pragma checksum" -}},g,a,{beginKeywords:"class interface",relevance:0,end:/[{;=]/, -illegal:/[^\s:,]/,contains:[{beginKeywords:"where class" -},t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace", -relevance:0,end:/[{;=]/,illegal:/[^\s:]/, -contains:[t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ -beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/, -contains:[t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta", -begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{ -className:"string",begin:/"/,end:/"/}]},{ -beginKeywords:"new return throw await else",relevance:0},{className:"function", -begin:"("+b+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, -end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{ -beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial", -relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, -contains:[e.TITLE_MODE,u],relevance:0},{match:/\(\)/},{className:"params", -begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0, -contains:[g,a,e.C_BLOCK_COMMENT_MODE] -},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},m]}},grmr_css:e=>{ -const n=e.regex,t=ie(e),a=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{ -name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{ -keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"}, -contains:[t.BLOCK_COMMENT,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/ -},t.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0 -},{className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0 -},t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{ -begin:":("+oe.join("|")+")"},{begin:":(:)?("+le.join("|")+")"}] -},t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ce.join("|")+")\\b"},{ -begin:/:/,end:/[;}{]/, -contains:[t.BLOCK_COMMENT,t.HEXCOLOR,t.IMPORTANT,t.CSS_NUMBER_MODE,...a,{ -begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri" -},contains:[...a,{className:"string",begin:/[^)]/,endsWithParent:!0, -excludeEnd:!0}]},t.FUNCTION_DISPATCH]},{begin:n.lookahead(/@/),end:"[{;]", -relevance:0,illegal:/:/,contains:[{className:"keyword",begin:/@-?\w[\w]*(-\w+)*/ -},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{ -$pattern:/[a-z-]+/,keyword:"and or not only",attribute:se.join(" ")},contains:[{ -begin:/[a-z-]+(?=:)/,className:"attribute"},...a,t.CSS_NUMBER_MODE]}]},{ -className:"selector-tag",begin:"\\b("+re.join("|")+")\\b"}]}},grmr_diff:e=>{ -const n=e.regex;return{name:"Diff",aliases:["patch"],contains:[{ -className:"meta",relevance:10, -match:n.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/) -},{className:"comment",variants:[{ -begin:n.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/), -end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{ -className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, -end:/$/}]}},grmr_go:e=>{const n={ -keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"], -type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"], -literal:["true","false","iota","nil"], -built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"] -};return{name:"Go",aliases:["golang"],keywords:n,illegal:"{const n=e.regex;return{name:"GraphQL",aliases:["gql"], -case_insensitive:!0,disableAutodetect:!1,keywords:{ -keyword:["query","mutation","subscription","type","input","schema","directive","interface","union","scalar","fragment","enum","on"], -literal:["true","false","null"]}, -contains:[e.HASH_COMMENT_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{ -scope:"punctuation",match:/[.]{3}/,relevance:0},{scope:"punctuation", -begin:/[\!\(\)\:\=\[\]\{\|\}]{1}/,relevance:0},{scope:"variable",begin:/\$/, -end:/\W/,excludeEnd:!0,relevance:0},{scope:"meta",match:/@\w+/,excludeEnd:!0},{ -scope:"symbol",begin:n.concat(/[_A-Za-z][_0-9A-Za-z]*/,n.lookahead(/\s*:/)), -relevance:0}],illegal:[/[;<']/,/BEGIN/]}},grmr_ini:e=>{const n=e.regex,t={ -className:"number",relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{ -begin:e.NUMBER_RE}]},a=e.COMMENT();a.variants=[{begin:/;/,end:/$/},{begin:/#/, -end:/$/}];const i={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{ -begin:/\$\{(.*?)\}/}]},r={className:"literal", -begin:/\bon|off|true|false|yes|no\b/},s={className:"string", -contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{ -begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}] -},o={begin:/\[/,end:/\]/,contains:[a,r,i,s,t,"self"],relevance:0 -},l=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{ -name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/, -contains:[a,{className:"section",begin:/\[+/,end:/\]+/},{ -begin:n.concat(l,"(\\s*\\.\\s*",l,")*",n.lookahead(/\s*=\s*[^#\s]/)), -className:"attr",starts:{end:/$/,contains:[a,o,r,i,s,t]}}]}},grmr_java:e=>{ -const n=e.regex,t="[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*",a=t+pe("(?:<"+t+"~~~(?:\\s*,\\s*"+t+"~~~)*>)?",/~~~/g,2),i={ -keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits"], -literal:["false","true","null"], -type:["char","boolean","long","float","int","byte","short","double"], -built_in:["super","this"]},r={className:"meta",begin:"@"+t,contains:[{ -begin:/\(/,end:/\)/,contains:["self"]}]},s={className:"params",begin:/\(/, -end:/\)/,keywords:i,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0} -;return{name:"Java",aliases:["jsp"],keywords:i,illegal:/<\/|#/, -contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, -relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ -begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 -},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/, -className:"string",contains:[e.BACKSLASH_ESCAPE] -},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ -match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,t],className:{ -1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{ -begin:[n.concat(/(?!else)/,t),/\s+/,t,/\s+/,/=(?!=)/],className:{1:"type", -3:"variable",5:"operator"}},{begin:[/record/,/\s+/,t],className:{1:"keyword", -3:"title.class"},contains:[s,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ -beginKeywords:"new throw return else",relevance:0},{ -begin:["(?:"+a+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{ -2:"title.function"},keywords:i,contains:[{className:"params",begin:/\(/, -end:/\)/,keywords:i,relevance:0, -contains:[r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,me,e.C_BLOCK_COMMENT_MODE] -},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},me,r]}},grmr_javascript:Oe, -grmr_json:e=>{const n=["true","false","null"],t={scope:"literal", -beginKeywords:n.join(" ")};return{name:"JSON",keywords:{literal:n},contains:[{ -className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{ -match:/[{}[\],:]/,className:"punctuation",relevance:0 -},e.QUOTE_STRING_MODE,t,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], -illegal:"\\S"}},grmr_kotlin:e=>{const n={ -keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", -built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", -literal:"true false null"},t={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" -},a={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},i={ -className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", -variants:[{begin:'"""',end:'"""(?=[^"])',contains:[i,a]},{begin:"'",end:"'", -illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, -contains:[e.BACKSLASH_ESCAPE,i,a]}]};a.contains.push(r);const s={ -className:"meta", -begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" -},o={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, -end:/\)/,contains:[e.inherit(r,{className:"string"}),"self"]}] -},l=me,c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),d={ -variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, -contains:[]}]},g=d;return g.variants[1].contains=[d],d.variants[1].contains=[g], -{name:"Kotlin",aliases:["kt","kts"],keywords:n, -contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag", -begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword", -begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", -begin:/@\w+/}]}},t,s,o,{className:"function",beginKeywords:"fun",end:"[(]|$", -returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ -begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, -contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://, -keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, -endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, -endsWithParent:!0,contains:[d,e.C_LINE_COMMENT_MODE,c],relevance:0 -},e.C_LINE_COMMENT_MODE,c,s,o,r,e.C_NUMBER_MODE]},c]},{ -begin:[/class|interface|trait/,/\s+/,e.UNDERSCORE_IDENT_RE],beginScope:{ -3:"title.class"},keywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, -illegal:"extends implements",contains:[{ -beginKeywords:"public protected internal private constructor" -},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0, -excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,){\s]|$/, -excludeBegin:!0,returnEnd:!0},s,o]},r,{className:"meta",begin:"^#!/usr/bin/env", -end:"$",illegal:"\n"},l]}},grmr_less:e=>{ -const n=ie(e),t=de,a="[\\w-]+",i="("+a+"|@\\{"+a+"\\})",r=[],s=[],o=e=>({ -className:"string",begin:"~?"+e+".*?"+e}),l=(e,n,t)=>({className:e,begin:n, -relevance:t}),c={$pattern:/[a-z-]+/,keyword:"and or not only", -attribute:se.join(" ")},d={begin:"\\(",end:"\\)",contains:s,keywords:c, -relevance:0} -;s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,o("'"),o('"'),n.CSS_NUMBER_MODE,{ -begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]", -excludeEnd:!0} -},n.HEXCOLOR,d,l("variable","@@?"+a,10),l("variable","@\\{"+a+"\\}"),l("built_in","~?`[^`]*?`"),{ -className:"attribute",begin:a+"\\s*:",end:":",returnBegin:!0,excludeEnd:!0 -},n.IMPORTANT,{beginKeywords:"and not"},n.FUNCTION_DISPATCH);const g=s.concat({ -begin:/\{/,end:/\}/,contains:r}),u={beginKeywords:"when",endsWithParent:!0, -contains:[{beginKeywords:"and not"}].concat(s)},b={begin:i+"\\s*:", -returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/ -},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ce.join("|")+")\\b", -end:/(?=:)/,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}] -},m={className:"keyword", -begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b", -starts:{end:"[;{}]",keywords:c,returnEnd:!0,contains:s,relevance:0}},p={ -className:"variable",variants:[{begin:"@"+a+"\\s*:",relevance:15},{begin:"@"+a -}],starts:{end:"[;}]",returnEnd:!0,contains:g}},_={variants:[{ -begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:i,end:/\{/}],returnBegin:!0, -returnEnd:!0,illegal:"[<='$\"]",relevance:0, -contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,u,l("keyword","all\\b"),l("variable","@\\{"+a+"\\}"),{ -begin:"\\b("+re.join("|")+")\\b",className:"selector-tag" -},n.CSS_NUMBER_MODE,l("selector-tag",i,0),l("selector-id","#"+i),l("selector-class","\\."+i,0),l("selector-tag","&",0),n.ATTRIBUTE_SELECTOR_MODE,{ -className:"selector-pseudo",begin:":("+oe.join("|")+")"},{ -className:"selector-pseudo",begin:":(:)?("+le.join("|")+")"},{begin:/\(/, -end:/\)/,relevance:0,contains:g},{begin:"!important"},n.FUNCTION_DISPATCH]},h={ -begin:a+":(:)?"+`(${t.join("|")})`,returnBegin:!0,contains:[_]} -;return r.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,m,p,h,b,_,u,n.FUNCTION_DISPATCH), -{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:r}}, -grmr_lua:e=>{const n="\\[=*\\[",t="\\]=*\\]",a={begin:n,end:t,contains:["self"] -},i=[e.COMMENT("--(?!"+n+")","$"),e.COMMENT("--"+n,t,{contains:[a],relevance:10 -})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, -literal:"true false nil", -keyword:"and break do else elseif end for goto if in local not or repeat return then until while", -built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" -},contains:i.concat([{className:"function",beginKeywords:"function",end:"\\)", -contains:[e.inherit(e.TITLE_MODE,{ -begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", -begin:"\\(",endsWithParent:!0,contains:i}].concat(i) -},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", -begin:n,end:t,contains:[a],relevance:5}])}},grmr_makefile:e=>{const n={ -className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)", -contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{ -const n={begin:/<\/?[A-Za-z_]/,end:">",subLanguage:"xml",relevance:0},t={ -variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{ -begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, -relevance:2},{ -begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), -relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ -begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ -},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, -returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", -excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", -end:"\\]",excludeBegin:!0,excludeEnd:!0}]},a={className:"strong",contains:[], -variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] -},i={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ -begin:/_(?![_\s])/,end:/_/,relevance:0}]},r=e.inherit(a,{contains:[] -}),s=e.inherit(i,{contains:[]});a.contains.push(s),i.contains.push(r) -;let o=[n,t];return[a,i,r,s].forEach((e=>{e.contains=e.contains.concat(o) -})),o=o.concat(a,i),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ -className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:o},{ -begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", -contains:o}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", -end:"\\s+",excludeEnd:!0},a,i,{className:"quote",begin:"^>\\s+",contains:o, -end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ -begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ -begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", -contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ -begin:"^[-\\*]{3,}",end:"$"},t,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ -className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ -className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}},grmr_objectivec:e=>{ -const n=/[a-zA-Z@][a-zA-Z0-9_]*/,t={$pattern:n, -keyword:["@interface","@class","@protocol","@implementation"]};return{ -name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"], -keywords:{"variable.language":["this","super"],$pattern:n, -keyword:["while","export","sizeof","typedef","const","struct","for","union","volatile","static","mutable","if","do","return","goto","enum","else","break","extern","asm","case","default","register","explicit","typename","switch","continue","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"], -literal:["false","true","FALSE","TRUE","nil","YES","NO","NULL"], -built_in:["dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"], -type:["int","float","char","unsigned","signed","short","long","double","wchar_t","unichar","void","bool","BOOL","id|0","_Bool"] -},illegal:"/,end:/$/,illegal:"\\n" -},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class", -begin:"("+t.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:t, -contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE, -relevance:0}]}},grmr_perl:e=>{const n=e.regex,t=/[dualxmsipngr]{0,12}/,a={ -$pattern:/[\w.]+/, -keyword:"abs accept alarm and atan2 bind binmode bless break caller chdir chmod chomp chop chown chr chroot close closedir connect continue cos crypt dbmclose dbmopen defined delete die do dump each else elsif endgrent endhostent endnetent endprotoent endpwent endservent eof eval exec exists exit exp fcntl fileno flock for foreach fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt given glob gmtime goto grep gt hex if index int ioctl join keys kill last lc lcfirst length link listen local localtime log lstat lt ma map mkdir msgctl msgget msgrcv msgsnd my ne next no not oct open opendir or ord our pack package pipe pop pos print printf prototype push q|0 qq quotemeta qw qx rand read readdir readline readlink readpipe recv redo ref rename require reset return reverse rewinddir rindex rmdir say scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat state study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unless unlink unpack unshift untie until use utime values vec wait waitpid wantarray warn when while write x|0 xor y|0" -},i={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:a},r={begin:/->\{/, -end:/\}/},s={variants:[{begin:/\$\d/},{ -begin:n.concat(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])") -},{begin:/[$%@][^\s\w{]/,relevance:0}] -},o=[e.BACKSLASH_ESCAPE,i,s],l=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],c=(e,a,i="\\1")=>{ -const r="\\1"===i?i:n.concat(i,a) -;return n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,r,/(?:\\.|[^\\\/])*?/,i,t) -},d=(e,a,i)=>n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,i,t),g=[s,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{ -endsWithParent:!0}),r,{className:"string",contains:o,variants:[{ -begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[", -end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{ -begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">", -relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'", -contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`", -contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{ -begin:"-?\\w+\\s*=>",relevance:0}]},{className:"number", -begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b", -relevance:0},{ -begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*", -keywords:"split return print reverse grep",relevance:0, -contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{ -begin:c("s|tr|y",n.either(...l,{capture:!0}))},{begin:c("s|tr|y","\\(","\\)")},{ -begin:c("s|tr|y","\\[","\\]")},{begin:c("s|tr|y","\\{","\\}")}],relevance:2},{ -className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{ -begin:d("(?:m|qr)?",/\//,/\//)},{begin:d("m|qr",n.either(...l,{capture:!0 -}),/\1/)},{begin:d("m|qr",/\(/,/\)/)},{begin:d("m|qr",/\[/,/\]/)},{ -begin:d("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub", -end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{ -begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$", -subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}] -}];return i.contains=g,r.contains=g,{name:"Perl",aliases:["pl","pm"],keywords:a, -contains:g}},grmr_php:e=>{ -const n=e.regex,t=/(?![A-Za-z0-9])(?![$])/,a=n.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,t),i=n.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,t),r={ -scope:"variable",match:"\\$+"+a},s={scope:"subst",variants:[{begin:/\$\w+/},{ -begin:/\{\$/,end:/\}/}]},o=e.inherit(e.APOS_STRING_MODE,{illegal:null -}),l="[ \t\n]",c={scope:"string",variants:[e.inherit(e.QUOTE_STRING_MODE,{ -illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(s)}),o,{ -begin:/<<<[ \t]*(?:(\w+)|"(\w+)")\n/,end:/[ \t]*(\w+)\b/, -contains:e.QUOTE_STRING_MODE.contains.concat(s),"on:begin":(e,n)=>{ -n.data._beginMatch=e[1]||e[2]},"on:end":(e,n)=>{ -n.data._beginMatch!==e[1]&&n.ignoreMatch()}},e.END_SAME_AS_BEGIN({ -begin:/<<<[ \t]*'(\w+)'\n/,end:/[ \t]*(\w+)\b/})]},d={scope:"number",variants:[{ -begin:"\\b0[bB][01]+(?:_[01]+)*\\b"},{begin:"\\b0[oO][0-7]+(?:_[0-7]+)*\\b"},{ -begin:"\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b"},{ -begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?" -}],relevance:0 -},g=["false","null","true"],u=["__CLASS__","__DIR__","__FILE__","__FUNCTION__","__COMPILER_HALT_OFFSET__","__LINE__","__METHOD__","__NAMESPACE__","__TRAIT__","die","echo","exit","include","include_once","print","require","require_once","array","abstract","and","as","binary","bool","boolean","break","callable","case","catch","class","clone","const","continue","declare","default","do","double","else","elseif","empty","enddeclare","endfor","endforeach","endif","endswitch","endwhile","enum","eval","extends","final","finally","float","for","foreach","from","global","goto","if","implements","instanceof","insteadof","int","integer","interface","isset","iterable","list","match|0","mixed","new","never","object","or","private","protected","public","readonly","real","return","string","switch","throw","trait","try","unset","use","var","void","while","xor","yield"],b=["Error|0","AppendIterator","ArgumentCountError","ArithmeticError","ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException","CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError","DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator","GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException","LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException","OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException","RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator","RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator","RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList","SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage","SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError","UnderflowException","UnexpectedValueException","UnhandledMatchError","ArrayAccess","BackedEnum","Closure","Fiber","Generator","Iterator","IteratorAggregate","Serializable","Stringable","Throwable","Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter","self","static","stdClass"],m={ -keyword:u,literal:(e=>{const n=[];return e.forEach((e=>{ -n.push(e),e.toLowerCase()===e?n.push(e.toUpperCase()):n.push(e.toLowerCase()) -})),n})(g),built_in:b},p=e=>e.map((e=>e.replace(/\|\d+$/,""))),_={variants:[{ -match:[/new/,n.concat(l,"+"),n.concat("(?!",p(b).join("\\b|"),"\\b)"),i],scope:{ -1:"keyword",4:"title.class"}}]},h=n.concat(a,"\\b(?!\\()"),f={variants:[{ -match:[n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{2:"variable.constant" -}},{match:[/::/,/class/],scope:{2:"variable.language"}},{ -match:[i,n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{1:"title.class", -3:"variable.constant"}},{match:[i,n.concat("::",n.lookahead(/(?!class\b)/))], -scope:{1:"title.class"}},{match:[i,/::/,/class/],scope:{1:"title.class", -3:"variable.language"}}]},E={scope:"attr", -match:n.concat(a,n.lookahead(":"),n.lookahead(/(?!::)/))},y={relevance:0, -begin:/\(/,end:/\)/,keywords:m,contains:[E,r,f,e.C_BLOCK_COMMENT_MODE,c,d,_] -},N={relevance:0, -match:[/\b/,n.concat("(?!fn\\b|function\\b|",p(u).join("\\b|"),"|",p(b).join("\\b|"),"\\b)"),a,n.concat(l,"*"),n.lookahead(/(?=\()/)], -scope:{3:"title.function.invoke"},contains:[y]};y.contains.push(N) -;const w=[E,f,e.C_BLOCK_COMMENT_MODE,c,d,_];return{case_insensitive:!1, -keywords:m,contains:[{begin:n.concat(/#\[\s*/,i),beginScope:"meta",end:/]/, -endScope:"meta",keywords:{literal:g,keyword:["new","array"]},contains:[{ -begin:/\[/,end:/]/,keywords:{literal:g,keyword:["new","array"]}, -contains:["self",...w]},...w,{scope:"meta",match:i}] -},e.HASH_COMMENT_MODE,e.COMMENT("//","$"),e.COMMENT("/\\*","\\*/",{contains:[{ -scope:"doctag",match:"@[A-Za-z]+"}]}),{match:/__halt_compiler\(\);/, -keywords:"__halt_compiler",starts:{scope:"comment",end:e.MATCH_NOTHING_RE, -contains:[{match:/\?>/,scope:"meta",endsParent:!0}]}},{scope:"meta",variants:[{ -begin:/<\?php/,relevance:10},{begin:/<\?=/},{begin:/<\?/,relevance:.1},{ -begin:/\?>/}]},{scope:"variable.language",match:/\$this\b/},r,N,f,{ -match:[/const/,/\s/,a],scope:{1:"keyword",3:"variable.constant"}},_,{ -scope:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/, -excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use" -},e.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{scope:"params", -begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:m, -contains:["self",r,f,e.C_BLOCK_COMMENT_MODE,c,d]}]},{scope:"class",variants:[{ -beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait", -illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{ -beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{ -beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/, -contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{scope:"title.class"})]},{ -beginKeywords:"use",relevance:0,end:";",contains:[{ -match:/\b(as|const|function)\b/,scope:"keyword"},e.UNDERSCORE_TITLE_MODE]},c,d]} -},grmr_php_template:e=>({name:"PHP template",subLanguage:"xml",contains:[{ -begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*", -end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0 -},e.inherit(e.APOS_STRING_MODE,{illegal:null,className:null,contains:null, -skip:!0}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null,className:null, -contains:null,skip:!0})]}]}),grmr_plaintext:e=>({name:"Plain text", -aliases:["text","txt"],disableAutodetect:!0}),grmr_python:e=>{ -const n=e.regex,t=/[\p{XID_Start}_]\p{XID_Continue}*/u,a=["and","as","assert","async","await","break","case","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","match","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],i={ -$pattern:/[A-Za-z]\w+|__\w+__/,keyword:a, -built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"], -literal:["__debug__","Ellipsis","False","None","NotImplemented","True"], -type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"] -},r={className:"meta",begin:/^(>>>|\.\.\.) /},s={className:"subst",begin:/\{/, -end:/\}/,keywords:i,illegal:/#/},o={begin:/\{\{/,relevance:0},l={ -className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{ -begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/, -contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ -begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/, -contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ -begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/, -contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/, -end:/"""/,contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([uU]|[rR])'/,end:/'/, -relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{ -begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/, -end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/, -contains:[e.BACKSLASH_ESCAPE,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/, -contains:[e.BACKSLASH_ESCAPE,o,s]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] -},c="[0-9](_?[0-9])*",d=`(\\b(${c}))?\\.(${c})|\\b(${c})\\.`,g="\\b|"+a.join("|"),u={ -className:"number",relevance:0,variants:[{ -begin:`(\\b(${c})|(${d}))[eE][+-]?(${c})[jJ]?(?=${g})`},{begin:`(${d})[jJ]?`},{ -begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${g})`},{ -begin:`\\b0[bB](_?[01])+[lL]?(?=${g})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${g})` -},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${g})`},{begin:`\\b(${c})[jJ](?=${g})` -}]},b={className:"comment",begin:n.lookahead(/# type:/),end:/$/,keywords:i, -contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},m={ -className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/, -end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i, -contains:["self",r,u,l,e.HASH_COMMENT_MODE]}]};return s.contains=[l,u,r],{ -name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:i, -illegal:/(<\/|\?)|=>/,contains:[r,u,{begin:/\bself\b/},{beginKeywords:"if", -relevance:0},l,b,e.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,t],scope:{ -1:"keyword",3:"title.function"},contains:[m]},{variants:[{ -match:[/\bclass/,/\s+/,t,/\s*/,/\(\s*/,t,/\s*\)/]},{match:[/\bclass/,/\s+/,t]}], -scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{ -className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[u,m,l]}]}}, -grmr_python_repl:e=>({aliases:["pycon"],contains:[{className:"meta.prompt", -starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{ -begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}),grmr_r:e=>{ -const n=e.regex,t=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,a=n.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),i=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,r=n.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/) -;return{name:"R",keywords:{$pattern:t, -keyword:"function if in break next repeat else for while", -literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10", -built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm" -},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/, -starts:{end:n.lookahead(n.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)), -endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{ -scope:"variable",variants:[{match:t},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0 -}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}] -}),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE], -variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/ -}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/ -}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/ -}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/ -}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/ -}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"', -relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{ -1:"operator",2:"number"},match:[i,a]},{scope:{1:"operator",2:"number"}, -match:[/%[^%]*%/,a]},{scope:{1:"punctuation",2:"number"},match:[r,a]},{scope:{ -2:"number"},match:[/[^a-zA-Z0-9._]|^/,a]}]},{scope:{3:"operator"}, -match:[t,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:i},{ -match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:r},{begin:"`",end:"`", -contains:[{begin:/\\./}]}]}},grmr_ruby:e=>{ -const n=e.regex,t="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",a=n.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),i=n.concat(a,/(::\w+)*/),r={ -"variable.constant":["__FILE__","__LINE__","__ENCODING__"], -"variable.language":["self","super"], -keyword:["alias","and","begin","BEGIN","break","case","class","defined","do","else","elsif","end","END","ensure","for","if","in","module","next","not","or","redo","require","rescue","retry","return","then","undef","unless","until","when","while","yield","include","extend","prepend","public","private","protected","raise","throw"], -built_in:["proc","lambda","attr_accessor","attr_reader","attr_writer","define_method","private_constant","module_function"], -literal:["true","false","nil"]},s={className:"doctag",begin:"@[A-Za-z]+"},o={ -begin:"#<",end:">"},l=[e.COMMENT("#","$",{contains:[s] -}),e.COMMENT("^=begin","^=end",{contains:[s],relevance:10 -}),e.COMMENT("^__END__",e.MATCH_NOTHING_RE)],c={className:"subst",begin:/#\{/, -end:/\}/,keywords:r},d={className:"string",contains:[e.BACKSLASH_ESCAPE,c], -variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{ -begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{ -begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//, -end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{ -begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{ -begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{ -begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{ -begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{ -begin:n.concat(/<<[-~]?'?/,n.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)), -contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/, -contains:[e.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",u={className:"number", -relevance:0,variants:[{ -begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{ -begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b" -},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{ -begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{ -begin:"\\b0(_?[0-7])+r?i?\\b"}]},b={variants:[{match:/\(\)/},{ -className:"params",begin:/\(/,end:/(?=\))/,excludeBegin:!0,endsParent:!0, -keywords:r}]},m=[d,{variants:[{match:[/class\s+/,i,/\s+<\s+/,i]},{ -match:[/\b(class|module)\s+/,i]}],scope:{2:"title.class", -4:"title.class.inherited"},keywords:r},{match:[/(include|extend)\s+/,i],scope:{ -2:"title.class"},keywords:r},{relevance:0,match:[i,/\.new[. (]/],scope:{ -1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, -className:"variable.constant"},{relevance:0,match:a,scope:"title.class"},{ -match:[/def/,/\s+/,t],scope:{1:"keyword",3:"title.function"},contains:[b]},{ -begin:e.IDENT_RE+"::"},{className:"symbol", -begin:e.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol", -begin:":(?!\\s)",contains:[d,{begin:t}],relevance:0},u,{className:"variable", -begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{ -className:"params",begin:/\|/,end:/\|/,excludeBegin:!0,excludeEnd:!0, -relevance:0,keywords:r},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*", -keywords:"unless",contains:[{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c], -illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{ -begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[", -end:"\\][a-z]*"}]}].concat(o,l),relevance:0}].concat(o,l) -;c.contains=m,b.contains=m;const p=[{begin:/^\s*=>/,starts:{end:"$",contains:m} -},{className:"meta.prompt", -begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", -starts:{end:"$",keywords:r,contains:m}}];return l.unshift(o),{name:"Ruby", -aliases:["rb","gemspec","podspec","thor","irb"],keywords:r,illegal:/\/\*/, -contains:[e.SHEBANG({binary:"ruby"})].concat(p).concat(l).concat(m)}}, -grmr_rust:e=>{const n=e.regex,t={className:"title.function.invoke",relevance:0, -begin:n.concat(/\b/,/(?!let|for|while|if|else|match\b)/,e.IDENT_RE,n.lookahead(/\s*\(/)) -},a="([ui](8|16|32|64|128|size)|f(32|64))?",i=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","eprintln!","panic!","file!","format!","format_args!","include_bytes!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"],r=["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"] -;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:r, -keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","unsafe","unsized","use","virtual","where","while","yield"], -literal:["true","false","Some","None","Ok","Err"],built_in:i},illegal:""},t]}}, -grmr_scss:e=>{const n=ie(e),t=le,a=oe,i="@[a-z-]+",r={className:"variable", -begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b",relevance:0};return{name:"SCSS", -case_insensitive:!0,illegal:"[=/|']", -contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,n.CSS_NUMBER_MODE,{ -className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{ -className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0 -},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag", -begin:"\\b("+re.join("|")+")\\b",relevance:0},{className:"selector-pseudo", -begin:":("+a.join("|")+")"},{className:"selector-pseudo", -begin:":(:)?("+t.join("|")+")"},r,{begin:/\(/,end:/\)/, -contains:[n.CSS_NUMBER_MODE]},n.CSS_VARIABLE,{className:"attribute", -begin:"\\b("+ce.join("|")+")\\b"},{ -begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" -},{begin:/:/,end:/[;}{]/,relevance:0, -contains:[n.BLOCK_COMMENT,r,n.HEXCOLOR,n.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.IMPORTANT,n.FUNCTION_DISPATCH] -},{begin:"@(page|font-face)",keywords:{$pattern:i,keyword:"@page @font-face"}},{ -begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/, -keyword:"and or not only",attribute:se.join(" ")},contains:[{begin:i, -className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute" -},r,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.HEXCOLOR,n.CSS_NUMBER_MODE] -},n.FUNCTION_DISPATCH]}},grmr_shell:e=>({name:"Shell Session", -aliases:["console","shellsession"],contains:[{className:"meta.prompt", -begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/, -subLanguage:"bash"}}]}),grmr_sql:e=>{ -const n=e.regex,t=e.COMMENT("--","$"),a=["true","false","unknown"],i=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],r=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],s=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],o=r,l=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!r.includes(e))),c={ -begin:n.concat(/\b/,n.either(...o),/\s*\(/),relevance:0,keywords:{built_in:o}} -;return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{ -$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:n,when:t}={})=>{const a=t -;return n=n||[],e.map((e=>e.match(/\|\d+$/)||n.includes(e)?e:a(e)?e+"|0":e)) -})(l,{when:e=>e.length<3}),literal:a,type:i, -built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"] -},contains:[{begin:n.either(...s),relevance:0,keywords:{$pattern:/[\w\.]+/, -keyword:l.concat(s),literal:a,type:i}},{className:"type", -begin:n.either("double precision","large object","with timezone","without timezone") -},c,{className:"variable",begin:/@[a-z0-9][a-z0-9_]*/},{className:"string", -variants:[{begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/, -contains:[{begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{ -className:"operator",begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/, -relevance:0}]}},grmr_swift:e=>{const n={match:/\s+/,relevance:0 -},t=e.COMMENT("/\\*","\\*/",{contains:["self"]}),a=[e.C_LINE_COMMENT_MODE,t],i={ -match:[/\./,m(...xe,...Me)],className:{2:"keyword"}},r={match:b(/\./,m(...Ae)), -relevance:0},s=Ae.filter((e=>"string"==typeof e)).concat(["_|0"]),o={variants:[{ -className:"keyword", -match:m(...Ae.filter((e=>"string"!=typeof e)).concat(Se).map(ke),...Me)}]},l={ -$pattern:m(/\b\w+/,/#\w+/),keyword:s.concat(Re),literal:Ce},c=[i,r,o],g=[{ -match:b(/\./,m(...De)),relevance:0},{className:"built_in", -match:b(/\b/,m(...De),/(?=\()/)}],u={match:/->/,relevance:0},p=[u,{ -className:"operator",relevance:0,variants:[{match:Be},{match:`\\.(\\.|${Le})+`}] -}],_="([0-9]_*)+",h="([0-9a-fA-F]_*)+",f={className:"number",relevance:0, -variants:[{match:`\\b(${_})(\\.(${_}))?([eE][+-]?(${_}))?\\b`},{ -match:`\\b0x(${h})(\\.(${h}))?([pP][+-]?(${_}))?\\b`},{match:/\b0o([0-7]_*)+\b/ -},{match:/\b0b([01]_*)+\b/}]},E=(e="")=>({className:"subst",variants:[{ -match:b(/\\/,e,/[0\\tnr"']/)},{match:b(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}] -}),y=(e="")=>({className:"subst",match:b(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/) -}),N=(e="")=>({className:"subst",label:"interpol",begin:b(/\\/,e,/\(/),end:/\)/ -}),w=(e="")=>({begin:b(e,/"""/),end:b(/"""/,e),contains:[E(e),y(e),N(e)] -}),v=(e="")=>({begin:b(e,/"/),end:b(/"/,e),contains:[E(e),N(e)]}),O={ -className:"string", -variants:[w(),w("#"),w("##"),w("###"),v(),v("#"),v("##"),v("###")] -},k=[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0, -contains:[e.BACKSLASH_ESCAPE]}],x={begin:/\/[^\s](?=[^/\n]*\/)/,end:/\//, -contains:k},M=e=>{const n=b(e,/\//),t=b(/\//,e);return{begin:n,end:t, -contains:[...k,{scope:"comment",begin:`#(?!.*${t})`,end:/$/}]}},S={ -scope:"regexp",variants:[M("###"),M("##"),M("#"),x]},A={match:b(/`/,Fe,/`/) -},C=[A,{className:"variable",match:/\$\d+/},{className:"variable", -match:`\\$${ze}+`}],T=[{match:/(@|#(un)?)available/,scope:"keyword",starts:{ -contains:[{begin:/\(/,end:/\)/,keywords:Pe,contains:[...p,f,O]}]}},{ -scope:"keyword",match:b(/@/,m(...je))},{scope:"meta",match:b(/@/,Fe)}],R={ -match:d(/\b[A-Z]/),relevance:0,contains:[{className:"type", -match:b(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,ze,"+") -},{className:"type",match:Ue,relevance:0},{match:/[?!]+/,relevance:0},{ -match:/\.\.\./,relevance:0},{match:b(/\s+&\s+/,d(Ue)),relevance:0}]},D={ -begin://,keywords:l,contains:[...a,...c,...T,u,R]};R.contains.push(D) -;const I={begin:/\(/,end:/\)/,relevance:0,keywords:l,contains:["self",{ -match:b(Fe,/\s*:/),keywords:"_|0",relevance:0 -},...a,S,...c,...g,...p,f,O,...C,...T,R]},L={begin://, -keywords:"repeat each",contains:[...a,R]},B={begin:/\(/,end:/\)/,keywords:l, -contains:[{begin:m(d(b(Fe,/\s*:/)),d(b(Fe,/\s+/,Fe,/\s*:/))),end:/:/, -relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params", -match:Fe}]},...a,...c,...p,f,O,...T,R,I],endsParent:!0,illegal:/["']/},$={ -match:[/(func|macro)/,/\s+/,m(A.match,Fe,Be)],className:{1:"keyword", -3:"title.function"},contains:[L,B,n],illegal:[/\[/,/%/]},z={ -match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"}, -contains:[L,B,n],illegal:/\[|%/},F={match:[/operator/,/\s+/,Be],className:{ -1:"keyword",3:"title"}},U={begin:[/precedencegroup/,/\s+/,Ue],className:{ -1:"keyword",3:"title"},contains:[R],keywords:[...Te,...Ce],end:/}/} -;for(const e of O.variants){const n=e.contains.find((e=>"interpol"===e.label)) -;n.keywords=l;const t=[...c,...g,...p,f,O,...C];n.contains=[...t,{begin:/\(/, -end:/\)/,contains:["self",...t]}]}return{name:"Swift",keywords:l, -contains:[...a,$,z,{beginKeywords:"struct protocol class extension enum actor", -end:"\\{",excludeEnd:!0,keywords:l,contains:[e.inherit(e.TITLE_MODE,{ -className:"title.class",begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...c] -},F,U,{beginKeywords:"import",end:/$/,contains:[...a],relevance:0 -},S,...c,...g,...p,f,O,...C,...T,R,I]}},grmr_typescript:e=>{ -const n=Oe(e),t=_e,a=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],i={ -beginKeywords:"namespace",end:/\{/,excludeEnd:!0, -contains:[n.exports.CLASS_REFERENCE]},r={beginKeywords:"interface",end:/\{/, -excludeEnd:!0,keywords:{keyword:"interface extends",built_in:a}, -contains:[n.exports.CLASS_REFERENCE]},s={$pattern:_e, -keyword:he.concat(["type","namespace","interface","public","private","protected","implements","declare","abstract","readonly","enum","override"]), -literal:fe,built_in:ve.concat(a),"variable.language":we},o={className:"meta", -begin:"@"+t},l=(e,n,t)=>{const a=e.contains.findIndex((e=>e.label===n)) -;if(-1===a)throw Error("can not find mode to replace");e.contains.splice(a,1,t)} -;return Object.assign(n.keywords,s), -n.exports.PARAMS_CONTAINS.push(o),n.contains=n.contains.concat([o,i,r]), -l(n,"shebang",e.SHEBANG()),l(n,"use_strict",{className:"meta",relevance:10, -begin:/^\s*['"]use strict['"]/ -}),n.contains.find((e=>"func.def"===e.label)).relevance=0,Object.assign(n,{ -name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),n},grmr_vbnet:e=>{ -const n=e.regex,t=/\d{1,2}\/\d{1,2}\/\d{4}/,a=/\d{4}-\d{1,2}-\d{1,2}/,i=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,r=/\d{1,2}(:\d{1,2}){1,2}/,s={ -className:"literal",variants:[{begin:n.concat(/# */,n.either(a,t),/ *#/)},{ -begin:n.concat(/# */,r,/ *#/)},{begin:n.concat(/# */,i,/ *#/)},{ -begin:n.concat(/# */,n.either(a,t),/ +/,n.either(i,r),/ *#/)}] -},o=e.COMMENT(/'''/,/$/,{contains:[{className:"doctag",begin:/<\/?/,end:/>/}] -}),l=e.COMMENT(null,/$/,{variants:[{begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]}) -;return{name:"Visual Basic .NET",aliases:["vb"],case_insensitive:!0, -classNameAliases:{label:"symbol"},keywords:{ -keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield", -built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort", -type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort", -literal:"true false nothing"}, -illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[{ -className:"string",begin:/"(""|[^/n])"C\b/},{className:"string",begin:/"/, -end:/"/,illegal:/\n/,contains:[{begin:/""/}]},s,{className:"number",relevance:0, -variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/ -},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{ -begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{ -className:"label",begin:/^\w+:/},o,l,{className:"meta", -begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/, -end:/$/,keywords:{ -keyword:"const disable else elseif enable end externalsource if region then"}, -contains:[l]}]}},grmr_wasm:e=>{e.regex;const n=e.COMMENT(/\(;/,/;\)/) -;return n.contains.push("self"),{name:"WebAssembly",keywords:{$pattern:/[\w.]+/, -keyword:["anyfunc","block","br","br_if","br_table","call","call_indirect","data","drop","elem","else","end","export","func","global.get","global.set","local.get","local.set","local.tee","get_global","get_local","global","if","import","local","loop","memory","memory.grow","memory.size","module","mut","nop","offset","param","result","return","select","set_global","set_local","start","table","tee_local","then","type","unreachable"] -},contains:[e.COMMENT(/;;/,/$/),n,{match:[/(?:offset|align)/,/\s*/,/=/], -className:{1:"keyword",3:"operator"}},{className:"variable",begin:/\$[\w_]+/},{ -match:/(\((?!;)|\))+/,className:"punctuation",relevance:0},{ -begin:[/(?:func|call|call_indirect)/,/\s+/,/\$[^\s)]+/],className:{1:"keyword", -3:"title.function"}},e.QUOTE_STRING_MODE,{match:/(i32|i64|f32|f64)(?!\.)/, -className:"type"},{className:"keyword", -match:/\b(f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))\b/ -},{className:"number",relevance:0, -match:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/ -}]}},grmr_xml:e=>{ -const n=e.regex,t=n.concat(/[\p{L}_]/u,n.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),a={ -className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},i={begin:/\s/, -contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] -},r=e.inherit(i,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{ -className:"string"}),o=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),l={ -endsWithParent:!0,illegal:/`]+/}]}]}]};return{ -name:"HTML, XML", -aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], -case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[i,o,s,r,{begin:/\[/,end:/\]/,contains:[{ -className:"meta",begin://,contains:[i,r,o,s]}]}] -},e.COMMENT(//,{relevance:10}),{begin://, -relevance:10},a,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/, -relevance:10,contains:[o]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag", -begin:/)/,end:/>/,keywords:{name:"style"},contains:[l],starts:{ -end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", -begin:/)/,end:/>/,keywords:{name:"script"},contains:[l],starts:{ -end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ -className:"tag",begin:/<>|<\/>/},{className:"tag", -begin:n.concat(//,/>/,/\s/)))), -end:/\/?>/,contains:[{className:"name",begin:t,relevance:0,starts:l}]},{ -className:"tag",begin:n.concat(/<\//,n.lookahead(n.concat(t,/>/))),contains:[{ -className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]} -},grmr_yaml:e=>{ -const n="true false yes no null",t="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={ -className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ -},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", -variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(a,{ -variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),r={ -end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},s={begin:/\{/, -end:/\}/,contains:[r],illegal:"\\n",relevance:0},o={begin:"\\[",end:"\\]", -contains:[r],illegal:"\\n",relevance:0},l=[{className:"attr",variants:[{ -begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{ -begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$", -relevance:10},{className:"string", -begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ -begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, -relevance:0},{className:"type",begin:"!\\w+!"+t},{className:"type", -begin:"!<"+t+">"},{className:"type",begin:"!"+t},{className:"type",begin:"!!"+t -},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", -begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", -relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ -className:"number", -begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" -},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},s,o,a],c=[...l] -;return c.pop(),c.push(i),r.contains=c,{name:"YAML",case_insensitive:!0, -aliases:["yml"],contains:l}}});const He=ae;for(const e of Object.keys(Ke)){ -const n=e.replace("grmr_","").replace("_","-");He.registerLanguage(n,Ke[e])} -return He}() -;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); \ No newline at end of file diff --git a/exo/tinychat/static/unpkg.com/@highlightjs/cdn-assets@11.9.0/styles/vs2015.min.css b/exo/tinychat/static/unpkg.com/@highlightjs/cdn-assets@11.9.0/styles/vs2015.min.css deleted file mode 100644 index 7f6fe11c..00000000 --- a/exo/tinychat/static/unpkg.com/@highlightjs/cdn-assets@11.9.0/styles/vs2015.min.css +++ /dev/null @@ -1 +0,0 @@ -pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#1e1e1e;color:#dcdcdc}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-symbol{color:#569cd6}.hljs-link{color:#569cd6;text-decoration:underline}.hljs-built_in,.hljs-type{color:#4ec9b0}.hljs-class,.hljs-number{color:#b8d7a3}.hljs-meta .hljs-string,.hljs-string{color:#d69d85}.hljs-regexp,.hljs-template-tag{color:#9a5334}.hljs-formula,.hljs-function,.hljs-params,.hljs-subst,.hljs-title{color:#dcdcdc}.hljs-comment,.hljs-quote{color:#57a64a;font-style:italic}.hljs-doctag{color:#608b4e}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-tag{color:#9b9b9b}.hljs-template-variable,.hljs-variable{color:#bd63c5}.hljs-attr,.hljs-attribute{color:#9cdcfe}.hljs-section{color:gold}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-bullet,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{color:#d7ba7d}.hljs-addition{background-color:#144212;display:inline-block;width:100%}.hljs-deletion{background-color:#600;display:inline-block;width:100%} \ No newline at end of file diff --git a/exo/tinychat/static/unpkg.com/@marcreichel/alpine-autosize@1.3.x/dist/alpine-autosize.min.js b/exo/tinychat/static/unpkg.com/@marcreichel/alpine-autosize@1.3.x/dist/alpine-autosize.min.js deleted file mode 100644 index dd119d59..00000000 --- a/exo/tinychat/static/unpkg.com/@marcreichel/alpine-autosize@1.3.x/dist/alpine-autosize.min.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";var e=new Map;function t(t){var o=e.get(t);o&&o.destroy()}function o(t){var o=e.get(t);o&&o.update()}var r=null;"undefined"==typeof window?((r=function(e){return e}).destroy=function(e){return e},r.update=function(e){return e}):((r=function(t,o){return t&&Array.prototype.forEach.call(t.length?t:[t],(function(t){return function(t){if(t&&t.nodeName&&"TEXTAREA"===t.nodeName&&!e.has(t)){var o,r=null,n=window.getComputedStyle(t),i=(o=t.value,function(){s({testForHeightReduction:""===o||!t.value.startsWith(o),restoreTextAlign:null}),o=t.value}),l=function(o){t.removeEventListener("autosize:destroy",l),t.removeEventListener("autosize:update",a),t.removeEventListener("input",i),window.removeEventListener("resize",a),Object.keys(o).forEach((function(e){return t.style[e]=o[e]})),e.delete(t)}.bind(t,{height:t.style.height,resize:t.style.resize,textAlign:t.style.textAlign,overflowY:t.style.overflowY,overflowX:t.style.overflowX,wordWrap:t.style.wordWrap});t.addEventListener("autosize:destroy",l),t.addEventListener("autosize:update",a),t.addEventListener("input",i),window.addEventListener("resize",a),t.style.overflowX="hidden",t.style.wordWrap="break-word",e.set(t,{destroy:l,update:a}),a()}function s(e){var o,i,l=e.restoreTextAlign,a=void 0===l?null:l,d=e.testForHeightReduction,u=void 0===d||d,c=n.overflowY;if(0!==t.scrollHeight&&("vertical"===n.resize?t.style.resize="none":"both"===n.resize&&(t.style.resize="horizontal"),u&&(o=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push([e.parentNode,e.parentNode.scrollTop]),e=e.parentNode;return function(){return t.forEach((function(e){var t=e[0],o=e[1];t.style.scrollBehavior="auto",t.scrollTop=o,t.style.scrollBehavior=null}))}}(t),t.style.height=""),i="content-box"===n.boxSizing?t.scrollHeight-(parseFloat(n.paddingTop)+parseFloat(n.paddingBottom)):t.scrollHeight+parseFloat(n.borderTopWidth)+parseFloat(n.borderBottomWidth),"none"!==n.maxHeight&&i>parseFloat(n.maxHeight)?("hidden"===n.overflowY&&(t.style.overflow="scroll"),i=parseFloat(n.maxHeight)):"hidden"!==n.overflowY&&(t.style.overflow="hidden"),t.style.height=i+"px",a&&(t.style.textAlign=a),o&&o(),r!==i&&(t.dispatchEvent(new Event("autosize:resized",{bubbles:!0})),r=i),c!==n.overflow&&!a)){var f=n.textAlign;"hidden"===n.overflow&&(t.style.textAlign="start"===f?"end":"start"),s({restoreTextAlign:f,testForHeightReduction:!0})}}function a(){s({testForHeightReduction:!0,restoreTextAlign:null})}}(t)})),t}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],t),e},r.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],o),e});var n=r;document.addEventListener("alpine:init",(()=>{var e;(e=window.Alpine).directive("autosize",((e,{modifiers:t},{cleanup:o})=>{n(e);const r=Array.from(e.attributes);let i=!1;for(let{nodeName:e}of r)if("wire:model"===e||e.startsWith("wire:model.")){i=!0;break}!e.hasAttribute("wire:ignore")&&i&&e.setAttribute("wire:ignore","");const l=()=>{n.update(e)};e.addEventListener("autosize",l),o((()=>{n.destroy(e),e.removeEventListener("autosize",l)}))})),e.magic("autosize",(e=>t=>{(t||e).dispatchEvent(new Event("autosize"))}))}))})); -//# sourceMappingURL=alpine-autosize.min.js.map diff --git a/exo/tinychat/static/unpkg.com/alpinejs@3.x.x/dist/cdn.min.js b/exo/tinychat/static/unpkg.com/alpinejs@3.x.x/dist/cdn.min.js deleted file mode 100644 index 2ca48278..00000000 --- a/exo/tinychat/static/unpkg.com/alpinejs@3.x.x/dist/cdn.min.js +++ /dev/null @@ -1,5 +0,0 @@ -(()=>{var rt=!1,nt=!1,U=[],it=-1;function qt(e){Cn(e)}function Cn(e){U.includes(e)||U.push(e),Tn()}function Ee(e){let t=U.indexOf(e);t!==-1&&t>it&&U.splice(t,1)}function Tn(){!nt&&!rt&&(rt=!0,queueMicrotask(Rn))}function Rn(){rt=!1,nt=!0;for(let e=0;ee.effect(t,{scheduler:r=>{ot?qt(r):r()}}),st=e.raw}function at(e){D=e}function Gt(e){let t=()=>{};return[n=>{let i=D(n);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(o=>o())}),e._x_effects.add(i),t=()=>{i!==void 0&&(e._x_effects.delete(i),L(i))},i},()=>{t()}]}function ve(e,t){let r=!0,n,i=D(()=>{let o=e();JSON.stringify(o),r?n=o:queueMicrotask(()=>{t(o,n),n=o}),r=!1});return()=>L(i)}var Jt=[],Yt=[],Xt=[];function Zt(e){Xt.push(e)}function ee(e,t){typeof t=="function"?(e._x_cleanups||(e._x_cleanups=[]),e._x_cleanups.push(t)):(t=e,Yt.push(t))}function Ae(e){Jt.push(e)}function Oe(e,t,r){e._x_attributeCleanups||(e._x_attributeCleanups={}),e._x_attributeCleanups[t]||(e._x_attributeCleanups[t]=[]),e._x_attributeCleanups[t].push(r)}function ct(e,t){e._x_attributeCleanups&&Object.entries(e._x_attributeCleanups).forEach(([r,n])=>{(t===void 0||t.includes(r))&&(n.forEach(i=>i()),delete e._x_attributeCleanups[r])})}function Qt(e){if(e._x_cleanups)for(;e._x_cleanups.length;)e._x_cleanups.pop()()}var lt=new MutationObserver(pt),ut=!1;function le(){lt.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),ut=!0}function ft(){Mn(),lt.disconnect(),ut=!1}var ce=[];function Mn(){let e=lt.takeRecords();ce.push(()=>e.length>0&&pt(e));let t=ce.length;queueMicrotask(()=>{if(ce.length===t)for(;ce.length>0;)ce.shift()()})}function _(e){if(!ut)return e();ft();let t=e();return le(),t}var dt=!1,Se=[];function er(){dt=!0}function tr(){dt=!1,pt(Se),Se=[]}function pt(e){if(dt){Se=Se.concat(e);return}let t=new Set,r=new Set,n=new Map,i=new Map;for(let o=0;os.nodeType===1&&t.add(s)),e[o].removedNodes.forEach(s=>s.nodeType===1&&r.add(s))),e[o].type==="attributes")){let s=e[o].target,a=e[o].attributeName,c=e[o].oldValue,l=()=>{n.has(s)||n.set(s,[]),n.get(s).push({name:a,value:s.getAttribute(a)})},u=()=>{i.has(s)||i.set(s,[]),i.get(s).push(a)};s.hasAttribute(a)&&c===null?l():s.hasAttribute(a)?(u(),l()):u()}i.forEach((o,s)=>{ct(s,o)}),n.forEach((o,s)=>{Jt.forEach(a=>a(s,o))});for(let o of r)t.has(o)||Yt.forEach(s=>s(o));t.forEach(o=>{o._x_ignoreSelf=!0,o._x_ignore=!0});for(let o of t)r.has(o)||o.isConnected&&(delete o._x_ignoreSelf,delete o._x_ignore,Xt.forEach(s=>s(o)),o._x_ignore=!0,o._x_ignoreSelf=!0);t.forEach(o=>{delete o._x_ignoreSelf,delete o._x_ignore}),t=null,r=null,n=null,i=null}function Ce(e){return F(j(e))}function P(e,t,r){return e._x_dataStack=[t,...j(r||e)],()=>{e._x_dataStack=e._x_dataStack.filter(n=>n!==t)}}function j(e){return e._x_dataStack?e._x_dataStack:typeof ShadowRoot=="function"&&e instanceof ShadowRoot?j(e.host):e.parentNode?j(e.parentNode):[]}function F(e){return new Proxy({objects:e},Nn)}var Nn={ownKeys({objects:e}){return Array.from(new Set(e.flatMap(t=>Object.keys(t))))},has({objects:e},t){return t==Symbol.unscopables?!1:e.some(r=>Object.prototype.hasOwnProperty.call(r,t)||Reflect.has(r,t))},get({objects:e},t,r){return t=="toJSON"?Dn:Reflect.get(e.find(n=>Reflect.has(n,t))||{},t,r)},set({objects:e},t,r,n){let i=e.find(s=>Object.prototype.hasOwnProperty.call(s,t))||e[e.length-1],o=Object.getOwnPropertyDescriptor(i,t);return o?.set&&o?.get?o.set.call(n,r)||!0:Reflect.set(i,t,r)}};function Dn(){return Reflect.ownKeys(this).reduce((t,r)=>(t[r]=Reflect.get(this,r),t),{})}function Te(e){let t=n=>typeof n=="object"&&!Array.isArray(n)&&n!==null,r=(n,i="")=>{Object.entries(Object.getOwnPropertyDescriptors(n)).forEach(([o,{value:s,enumerable:a}])=>{if(a===!1||s===void 0||typeof s=="object"&&s!==null&&s.__v_skip)return;let c=i===""?o:`${i}.${o}`;typeof s=="object"&&s!==null&&s._x_interceptor?n[o]=s.initialize(e,c,o):t(s)&&s!==n&&!(s instanceof Element)&&r(s,c)})};return r(e)}function Re(e,t=()=>{}){let r={initialValue:void 0,_x_interceptor:!0,initialize(n,i,o){return e(this.initialValue,()=>Pn(n,i),s=>mt(n,i,s),i,o)}};return t(r),n=>{if(typeof n=="object"&&n!==null&&n._x_interceptor){let i=r.initialize.bind(r);r.initialize=(o,s,a)=>{let c=n.initialize(o,s,a);return r.initialValue=c,i(o,s,a)}}else r.initialValue=n;return r}}function Pn(e,t){return t.split(".").reduce((r,n)=>r[n],e)}function mt(e,t,r){if(typeof t=="string"&&(t=t.split(".")),t.length===1)e[t[0]]=r;else{if(t.length===0)throw error;return e[t[0]]||(e[t[0]]={}),mt(e[t[0]],t.slice(1),r)}}var rr={};function y(e,t){rr[e]=t}function ue(e,t){return Object.entries(rr).forEach(([r,n])=>{let i=null;function o(){if(i)return i;{let[s,a]=_t(t);return i={interceptor:Re,...s},ee(t,a),i}}Object.defineProperty(e,`$${r}`,{get(){return n(t,o())},enumerable:!1})}),e}function nr(e,t,r,...n){try{return r(...n)}catch(i){te(i,e,t)}}function te(e,t,r=void 0){e=Object.assign(e??{message:"No error message given."},{el:t,expression:r}),console.warn(`Alpine Expression Error: ${e.message} - -${r?'Expression: "'+r+`" - -`:""}`,t),setTimeout(()=>{throw e},0)}var Me=!0;function De(e){let t=Me;Me=!1;let r=e();return Me=t,r}function M(e,t,r={}){let n;return x(e,t)(i=>n=i,r),n}function x(...e){return ir(...e)}var ir=gt;function or(e){ir=e}function gt(e,t){let r={};ue(r,e);let n=[r,...j(e)],i=typeof t=="function"?In(n,t):Ln(n,t,e);return nr.bind(null,e,t,i)}function In(e,t){return(r=()=>{},{scope:n={},params:i=[]}={})=>{let o=t.apply(F([n,...e]),i);Ne(r,o)}}var ht={};function kn(e,t){if(ht[e])return ht[e];let r=Object.getPrototypeOf(async function(){}).constructor,n=/^[\n\s]*if.*\(.*\)/.test(e.trim())||/^(let|const)\s/.test(e.trim())?`(async()=>{ ${e} })()`:e,o=(()=>{try{let s=new r(["__self","scope"],`with (scope) { __self.result = ${n} }; __self.finished = true; return __self.result;`);return Object.defineProperty(s,"name",{value:`[Alpine] ${e}`}),s}catch(s){return te(s,t,e),Promise.resolve()}})();return ht[e]=o,o}function Ln(e,t,r){let n=kn(t,r);return(i=()=>{},{scope:o={},params:s=[]}={})=>{n.result=void 0,n.finished=!1;let a=F([o,...e]);if(typeof n=="function"){let c=n(n,a).catch(l=>te(l,r,t));n.finished?(Ne(i,n.result,a,s,r),n.result=void 0):c.then(l=>{Ne(i,l,a,s,r)}).catch(l=>te(l,r,t)).finally(()=>n.result=void 0)}}}function Ne(e,t,r,n,i){if(Me&&typeof t=="function"){let o=t.apply(r,n);o instanceof Promise?o.then(s=>Ne(e,s,r,n)).catch(s=>te(s,i,t)):e(o)}else typeof t=="object"&&t instanceof Promise?t.then(o=>e(o)):e(t)}var bt="x-";function C(e=""){return bt+e}function sr(e){bt=e}var Pe={};function d(e,t){return Pe[e]=t,{before(r){if(!Pe[r]){console.warn(String.raw`Cannot find directive \`${r}\`. \`${e}\` will use the default order of execution`);return}let n=W.indexOf(r);W.splice(n>=0?n:W.indexOf("DEFAULT"),0,e)}}}function ar(e){return Object.keys(Pe).includes(e)}function de(e,t,r){if(t=Array.from(t),e._x_virtualDirectives){let o=Object.entries(e._x_virtualDirectives).map(([a,c])=>({name:a,value:c})),s=wt(o);o=o.map(a=>s.find(c=>c.name===a.name)?{name:`x-bind:${a.name}`,value:`"${a.value}"`}:a),t=t.concat(o)}let n={};return t.map(ur((o,s)=>n[o]=s)).filter(dr).map(jn(n,r)).sort(Fn).map(o=>$n(e,o))}function wt(e){return Array.from(e).map(ur()).filter(t=>!dr(t))}var xt=!1,fe=new Map,cr=Symbol();function lr(e){xt=!0;let t=Symbol();cr=t,fe.set(t,[]);let r=()=>{for(;fe.get(t).length;)fe.get(t).shift()();fe.delete(t)},n=()=>{xt=!1,r()};e(r),n()}function _t(e){let t=[],r=a=>t.push(a),[n,i]=Gt(e);return t.push(i),[{Alpine:B,effect:n,cleanup:r,evaluateLater:x.bind(x,e),evaluate:M.bind(M,e)},()=>t.forEach(a=>a())]}function $n(e,t){let r=()=>{},n=Pe[t.type]||r,[i,o]=_t(e);Oe(e,t.original,o);let s=()=>{e._x_ignore||e._x_ignoreSelf||(n.inline&&n.inline(e,t,i),n=n.bind(n,e,t,i),xt?fe.get(cr).push(n):n())};return s.runCleanups=o,s}var Ie=(e,t)=>({name:r,value:n})=>(r.startsWith(e)&&(r=r.replace(e,t)),{name:r,value:n}),ke=e=>e;function ur(e=()=>{}){return({name:t,value:r})=>{let{name:n,value:i}=fr.reduce((o,s)=>s(o),{name:t,value:r});return n!==t&&e(n,t),{name:n,value:i}}}var fr=[];function re(e){fr.push(e)}function dr({name:e}){return pr().test(e)}var pr=()=>new RegExp(`^${bt}([^:^.]+)\\b`);function jn(e,t){return({name:r,value:n})=>{let i=r.match(pr()),o=r.match(/:([a-zA-Z0-9\-_:]+)/),s=r.match(/\.[^.\]]+(?=[^\]]*$)/g)||[],a=t||e[r]||r;return{type:i?i[1]:null,value:o?o[1]:null,modifiers:s.map(c=>c.replace(".","")),expression:n,original:a}}}var yt="DEFAULT",W=["ignore","ref","data","id","anchor","bind","init","for","model","modelable","transition","show","if",yt,"teleport"];function Fn(e,t){let r=W.indexOf(e.type)===-1?yt:e.type,n=W.indexOf(t.type)===-1?yt:t.type;return W.indexOf(r)-W.indexOf(n)}function G(e,t,r={}){e.dispatchEvent(new CustomEvent(t,{detail:r,bubbles:!0,composed:!0,cancelable:!0}))}function T(e,t){if(typeof ShadowRoot=="function"&&e instanceof ShadowRoot){Array.from(e.children).forEach(i=>T(i,t));return}let r=!1;if(t(e,()=>r=!0),r)return;let n=e.firstElementChild;for(;n;)T(n,t,!1),n=n.nextElementSibling}function E(e,...t){console.warn(`Alpine Warning: ${e}`,...t)}var mr=!1;function _r(){mr&&E("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),mr=!0,document.body||E("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's ` - - - - - - - """ - - # Write the dashboard - dashboard_path = output_dir / "dashboard.html" - with open(dashboard_path, "w") as f: - f.write(dashboard_html) - - # Generate summary with available metrics - latest_data = {} - - if not df_size.empty: - latest = df_size.iloc[-1] - previous = df_size.iloc[-2] if len(df_size) > 1 else latest - size_change = float(latest['total_size_mb'] - previous['total_size_mb']) - latest_data.update({ - 'timestamp': latest['timestamp'].isoformat(), - 'commit_hash': latest['commit_hash'], - 'commit_url': latest['commit_url'], - 'total_size_mb': float(latest['total_size_mb']), - 'size_change_mb': size_change, - 'packages': latest.get('packages', []) - }) - - if not df_lines.empty: - latest = df_lines.iloc[-1] - previous = df_lines.iloc[-2] if len(df_lines) > 1 else latest - linecount_change = int(latest['total_lines'] - previous['total_lines']) - if not latest_data: # Only add timestamp and commit info if not already added - latest_data.update({ - 'timestamp': latest['timestamp'].isoformat(), - 'commit_hash': latest['commit_hash'], - 'commit_url': latest['commit_url'], - }) - latest_data.update({ - 'total_lines': int(latest['total_lines']), - 'linecount_change': linecount_change - }) - - if not df_benchmark.empty: - latest = df_benchmark.iloc[-1] - previous = df_benchmark.iloc[-2] if len(df_benchmark) > 1 else latest - tokens_change = float(latest['tokens_per_second'] - previous['tokens_per_second']) - if not latest_data: # Only add timestamp and commit info if not already added - latest_data.update({ - 'timestamp': latest['timestamp'].isoformat(), - 'commit_hash': latest['commit_hash'], - 'commit_url': latest['commit_url'], - }) - latest_data.update({ - 'tokens_per_second': float(latest['tokens_per_second']), - 'tokens_change': tokens_change - }) - - if latest_data: - with open(output_dir / 'latest_data.json', 'w') as f: - json.dump(latest_data, f, indent=2) - - self._print_summary(latest_data) - self.logger.info(f"Report generated in {output_dir}") - return str(output_dir) - - return None - - def _print_summary(self, latest_data: Dict): - print("\n=== Package Size Summary ===") - print(f"Timestamp: {latest_data['timestamp']}") - print(f"Commit: {latest_data['commit_hash'][:7]}") - - if 'total_size_mb' in latest_data: - print(f"Total Size: {latest_data['total_size_mb']:.2f}MB") - change = latest_data['size_change_mb'] - change_symbol = "↓" if change <= 0 else "↑" - print(f"Change: {change_symbol} {abs(change):.2f}MB") - - if latest_data.get('packages'): - print("\nTop 5 Largest Packages:") - sorted_packages = sorted(latest_data['packages'], key=lambda x: x['size_mb'], reverse=True) - for pkg in sorted_packages[:5]: - print(f"- {pkg['name']}: {pkg['size_mb']:.2f}MB") - - if 'total_lines' in latest_data: - print("\nLine Count Stats:") - print(f"Total Lines: {latest_data['total_lines']:,}") - change = latest_data['linecount_change'] - change_symbol = "↓" if change <= 0 else "↑" - print(f"Change: {change_symbol} {abs(change):,}") - - if 'tokens_per_second' in latest_data: - print("\nBenchmark Stats:") - print(f"Tokens per Second: {latest_data['tokens_per_second']:.2f}") - if 'time_to_first_token' in latest_data: - print(f"Time to First Token: {latest_data['time_to_first_token']:.3f}s") - - print("\n") - - def _calculate_data_hash(self, data: List[Dict]) -> str: - """Calculate a hash of the data to detect changes""" - return hash(str(sorted([ - (d.get('commit_hash'), d.get('timestamp')) - for d in data - ]))) - - def _play_sound(self, sound_key: str): - """Play a specific notification sound using pygame""" - try: - sound_path = self.sounds.get(sound_key) - if sound_path and sound_path.exists(): - sound = pygame.mixer.Sound(str(sound_path)) - sound.play() - # Wait for the sound to finish playing - pygame.time.wait(int(sound.get_length() * 1000)) - else: - self.logger.warning(f"Sound file not found: {sound_key} at {sound_path}") - except Exception as e: - self.logger.error(f"Failed to play sound {sound_key}: {e}") - - def _check_metrics_changes(self, current_data: List[Dict], previous_data: List[Dict]): - # Sort data by timestamp in descending order (most recent first) - def sort_by_timestamp(data): - return sorted( - data, - key=lambda x: x.get('timestamp', ''), - reverse=True # Most recent first - ) - - current_data = sort_by_timestamp(current_data) - previous_data = sort_by_timestamp(previous_data) - - # Helper to find latest entry with a specific metric - def find_latest_with_metric(data: List[Dict], metric: str) -> Optional[Dict]: - return next((d for d in data if metric in d), None) - - # Check line count changes - current_lines = find_latest_with_metric(current_data, 'total_lines') - previous_lines = find_latest_with_metric(previous_data, 'total_lines') - - if current_lines and previous_lines: - diff = current_lines['total_lines'] - previous_lines['total_lines'] - self.logger.debug(f"Lines of code diff: {diff}") - if diff > 0: - self.logger.info(f"Lines of code increased by {diff:,}") - self._play_sound('lines_up') - elif diff < 0: - self.logger.info(f"Lines of code decreased by {abs(diff):,}") - self._play_sound('lines_down') - else: - self.logger.debug("No lines of code data found") - - # Check tokens per second changes - current_tokens = find_latest_with_metric(current_data, 'tokens_per_second') - previous_tokens = find_latest_with_metric(previous_data, 'tokens_per_second') - - if current_tokens and previous_tokens: - diff = current_tokens['tokens_per_second'] - previous_tokens['tokens_per_second'] - self.logger.debug(f"Tokens per second diff: {diff}") - if diff > 0: - self.logger.info(f"Tokens per second increased by {diff:.2f}") - self._play_sound('tokens_up') - elif diff < 0: - self.logger.info(f"Tokens per second decreased by {abs(diff):.2f}") - self._play_sound('tokens_down') - else: - self.logger.debug("No tokens per second data found") - - # Check package size changes - current_size = find_latest_with_metric(current_data, 'total_size_mb') - previous_size = find_latest_with_metric(previous_data, 'total_size_mb') - - if current_size and previous_size: - diff = current_size['total_size_mb'] - previous_size['total_size_mb'] - self.logger.debug(f"Package size diff: {diff:.2f}MB") - if diff > 0: - self.logger.info(f"Package size increased by {diff:.2f}MB") - self._play_sound('size_up') - elif diff < 0: - self.logger.info(f"Package size decreased by {abs(diff):.2f}MB") - self._play_sound('size_down') - else: - self.logger.debug("No package size data found") - - async def run_dashboard(self, update_interval: int = 10): - """Run the dashboard with periodic updates""" - try: - update_interval = float(update_interval) - self.logger.debug(f"Update interval type: {type(update_interval)}, value: {update_interval}") - except ValueError as e: - self.logger.error(f"Failed to convert update_interval to float: {update_interval}") - raise - - self.logger.info(f"Starting real-time dashboard with {update_interval}s updates") - previous_data = None - - while True: - try: - start_time = time.time() - - # Collect new data - current_data = await self.collect_data() - if not current_data: - self.logger.warning("No data collected") - await asyncio.sleep(update_interval) - continue - - # Generate report - report_path = self.generate_report(current_data) - if report_path: - self.logger.info( - f"Dashboard updated at {datetime.now().strftime('%H:%M:%S')}" - ) - - print("Curr:", len(current_data)) - print("Prev:", len(previous_data) if previous_data else "None") - if previous_data: - # Check for metric changes and play appropriate sounds - self.logger.debug(f"Checking metrics changes between {len(current_data)} current and {len(previous_data)} previous data points") - self._check_metrics_changes(current_data, previous_data) - - # Update previous data - previous_data = current_data.copy() # Make a copy to prevent reference issues - - # Calculate sleep time - elapsed = float(time.time() - start_time) - sleep_time = max(0.0, update_interval - elapsed) - await asyncio.sleep(sleep_time) - - except Exception as e: - self.logger.error(f"Error in dashboard update loop: {e}", exc_info=True) - if self.debug: - raise - await asyncio.sleep(update_interval) - -async def main(): - token = os.getenv("CIRCLECI_TOKEN") - project_slug = os.getenv("CIRCLECI_PROJECT_SLUG") - debug = os.getenv("DEBUG", "").lower() in ("true", "1", "yes") - - try: - # Get update interval from environment or use default - update_interval = float(os.getenv("UPDATE_INTERVAL", "10")) - print(f"Update interval type: {type(update_interval)}, value: {update_interval}") # Debug print - except ValueError as e: - print(f"Error converting UPDATE_INTERVAL to float: {os.getenv('UPDATE_INTERVAL')}") - update_interval = 10.0 - - if not token or not project_slug: - print("Error: Please set CIRCLECI_TOKEN and CIRCLECI_PROJECT_SLUG environment variables") - return - - tracker = PackageSizeTracker(token, project_slug, debug) - - try: - await tracker.run_dashboard(update_interval) - except KeyboardInterrupt: - print("\nDashboard stopped by user") - except Exception as e: - logging.error(f"Error: {str(e)}", exc_info=True) - if debug: - raise - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/extra/dashboard/requirements.txt b/extra/dashboard/requirements.txt deleted file mode 100644 index 7b978bc0..00000000 --- a/extra/dashboard/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -plotly -pandas -requests -aiohttp -pygame \ No newline at end of file diff --git a/extra/dashboard/sounds/gta5_wasted.mp3 b/extra/dashboard/sounds/gta5_wasted.mp3 deleted file mode 100644 index 2723ff8a..00000000 --- a/extra/dashboard/sounds/gta5_wasted.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fb3fb66dd02827fbff86ef1ce3bc6438371c823aed7d4c3803ed522f008e4947 -size 206399 diff --git a/extra/dashboard/sounds/pokemon_evolve.mp3 b/extra/dashboard/sounds/pokemon_evolve.mp3 deleted file mode 100644 index 35b01432..00000000 --- a/extra/dashboard/sounds/pokemon_evolve.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d99cc9bdab4a4639d50f439b424547000e7c79f195b5b121734ad4ead435911c -size 633345 diff --git a/extra/line_counter.py b/extra/line_counter.py deleted file mode 100644 index 01e7ee20..00000000 --- a/extra/line_counter.py +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/env python3 -import os -import sys -import json -import token -import tokenize -from datetime import datetime, timezone - -TOKEN_WHITELIST = [token.OP, token.NAME, token.NUMBER, token.STRING] - -def is_docstring(t): - return t.type == token.STRING and t.string.startswith('"""') and t.line.strip().startswith('"""') - -def gen_stats(base_path="."): - table = [] - exo_path = os.path.join(base_path, "exo") - if not os.path.exists(exo_path): - print(f"Warning: {exo_path} directory not found") - return table - - for path, _, files in os.walk(exo_path): - for name in files: - if not name.endswith(".py"): - continue - - filepath = os.path.join(path, name) - relfilepath = os.path.relpath(filepath, base_path).replace('\\', '/') - - try: - with tokenize.open(filepath) as file_: - tokens = [t for t in tokenize.generate_tokens(file_.readline) - if t.type in TOKEN_WHITELIST and not is_docstring(t)] - token_count = len(tokens) - line_count = len(set([x for t in tokens - for x in range(t.start[0], t.end[0]+1)])) - if line_count > 0: - table.append([relfilepath, line_count, token_count/line_count]) - except Exception as e: - print(f"Error processing {filepath}: {e}") - continue - - return table - -def gen_diff(table_old, table_new): - table = [] - files_new = set([x[0] for x in table_new]) - files_old = set([x[0] for x in table_old]) - - added = files_new - files_old - deleted = files_old - files_new - unchanged = files_new & files_old - - for file in added: - file_stat = [stats for stats in table_new if file in stats][0] - table.append([file_stat[0], file_stat[1], file_stat[1], file_stat[2], file_stat[2]]) - - for file in deleted: - file_stat = [stats for stats in table_old if file in stats][0] - table.append([file_stat[0], 0, -file_stat[1], 0, -file_stat[2]]) - - for file in unchanged: - file_stat_old = [stats for stats in table_old if file in stats][0] - file_stat_new = [stats for stats in table_new if file in stats][0] - if file_stat_new[1] != file_stat_old[1] or file_stat_new[2] != file_stat_old[2]: - table.append([ - file_stat_new[0], - file_stat_new[1], - file_stat_new[1] - file_stat_old[1], - file_stat_new[2], - file_stat_new[2] - file_stat_old[2] - ]) - - return table - -def create_json_report(table, is_diff=False): - timestamp = datetime.now(timezone.utc).isoformat() - commit_sha = os.environ.get('GITHUB_SHA', 'unknown') - branch = os.environ.get('GITHUB_REF_NAME', 'unknown') - pr_number = os.environ.get('GITHUB_EVENT_NUMBER', '') - - if is_diff: - files = [{ - 'name': row[0], - 'current_lines': row[1], - 'line_diff': row[2], - 'current_tokens_per_line': row[3], - 'tokens_per_line_diff': row[4] - } for row in table] - - report = { - 'type': 'diff', - 'timestamp': timestamp, - 'commit_sha': commit_sha, - 'branch': branch, - 'pr_number': pr_number, - 'files': files, - 'total_line_changes': sum(row[2] for row in table), - 'total_files_changed': len(files) - } - else: - files = [{ - 'name': row[0], - 'lines': row[1], - 'tokens_per_line': row[2] - } for row in table] - - report = { - 'type': 'snapshot', - 'timestamp': timestamp, - 'commit_sha': commit_sha, - 'branch': branch, - 'files': files, - 'total_lines': sum(row[1] for row in table), - 'total_files': len(files) - } - - return report - -def display_diff(diff): - return "+" + str(diff) if diff > 0 else str(diff) - -def format_table(rows, headers, floatfmt): - if not rows: - return "" - - # Add headers as first row - all_rows = [headers] + rows - - # Calculate column widths - col_widths = [] - for col in range(len(headers)): - col_width = max(len(str(row[col])) for row in all_rows) - col_widths.append(col_width) - - # Format rows - output = [] - for row_idx, row in enumerate(all_rows): - formatted_cols = [] - for col_idx, (value, width) in enumerate(zip(row, col_widths)): - if isinstance(value, float): - # Handle float formatting based on floatfmt - fmt = floatfmt[col_idx] - if fmt.startswith('+'): - value = f"{value:+.1f}" - else: - value = f"{value:.1f}" - elif isinstance(value, int) and col_idx > 0: # Skip filename column - # Handle integer formatting based on floatfmt - fmt = floatfmt[col_idx] - if fmt.startswith('+'): - value = f"{value:+d}" - else: - value = f"{value:d}" - formatted_cols.append(str(value).ljust(width)) - output.append(" ".join(formatted_cols)) - - # Add separator line after headers - if row_idx == 0: - separator = [] - for width in col_widths: - separator.append("-" * width) - output.append(" ".join(separator)) - - return "\n".join(output) - -if __name__ == "__main__": - if len(sys.argv) == 3: - # Comparing two directories - headers = ["File", "Lines", "Diff", "Tokens/Line", "Diff"] - table = gen_diff(gen_stats(sys.argv[1]), gen_stats(sys.argv[2])) - - if table: - # Print table output - print("### Code Changes in 'exo' Directory") - print("```") - print(format_table( - sorted(table, key=lambda x: abs(x[2]) if len(x) > 2 else 0, reverse=True), - headers, - (".1f", "d", "+d", ".1f", "+.1f") - )) - total_changes = sum(row[2] for row in table) - print(f"\nTotal line changes: {display_diff(total_changes)}") - print("```") - - # Generate JSON report - report = create_json_report(table, is_diff=True) - with open('line-count-diff.json', 'w') as f: - json.dump(report, f, indent=2) - else: - # Single directory analysis - headers = ["File", "Lines", "Tokens/Line"] - table = gen_stats(sys.argv[1] if len(sys.argv) > 1 else ".") - - if table: - # Print table output - print("### Code Statistics for 'exo' Directory") - print("```") - print(format_table( - sorted(table, key=lambda x: x[1], reverse=True), - headers, - (".1f", "d", ".1f") - )) - total_lines = sum(row[1] for row in table) - print(f"\nTotal lines: {total_lines}") - print("```") - - # Generate JSON report - report = create_json_report(table, is_diff=False) - with open('line-count-snapshot.json', 'w') as f: - json.dump(report, f, indent=2) diff --git a/extra/pipsize.py b/extra/pipsize.py deleted file mode 100644 index 406d177c..00000000 --- a/extra/pipsize.py +++ /dev/null @@ -1,113 +0,0 @@ -import os -import importlib.metadata -import importlib.util -import json -import sys - - -def calc_container(path): - """Calculate total size of a directory or file.""" - if os.path.isfile(path): - try: - return os.path.getsize(path) - except (OSError, FileNotFoundError): - return 0 - - total_size = 0 - for dirpath, dirnames, filenames in os.walk(path): - for f in filenames: - fp = os.path.join(dirpath, f) - try: - total_size += os.path.getsize(fp) - except (OSError, FileNotFoundError): - continue - return total_size - - -def get_package_location(package_name): - """Get the actual location of a package's files.""" - try: - spec = importlib.util.find_spec(package_name) - if spec is None: - return None - - if spec.submodule_search_locations: - # Return the first location for namespace packages - return spec.submodule_search_locations[0] - elif spec.origin: - # For single-file modules, return the file path itself - return spec.origin - except ImportError: - return None - - -def get_package_sizes(min_size_mb=0.1): - """Get sizes of installed packages above minimum size threshold.""" - package_sizes = [] - - # Get all installed distributions - for dist in importlib.metadata.distributions(): - try: - package_name = dist.metadata["Name"] - location = get_package_location(package_name.replace("-", "_")) - - if location and os.path.exists(location): - size = calc_container(location) - size_mb = size / (1024 * 1024) - - if size_mb > min_size_mb: - package_sizes.append((package_name, size)) - except Exception as e: - print( - f"Error processing {dist.metadata.get('Name', 'Unknown package')}: {e}" - ) - - return package_sizes - - -def main(): - # Get and sort package sizes - package_sizes = get_package_sizes() - package_sizes.sort(key=lambda x: x[1], reverse=True) - - # Convert sizes to MB and prepare data - table_data = [(name, size/(1024*1024)) for name, size in package_sizes] - total_size = sum(size for _, size in package_sizes)/(1024*1024) - - # Check if --json flag is present - if "--json" in sys.argv: - try: - output_file = sys.argv[sys.argv.index("--json") + 1] - json_data = { - "packages": [{ - "name": name, - "size_mb": round(size, 2) - } for name, size in table_data], - "total_size_mb": round(total_size, 2) - } - - with open(output_file, 'w') as f: - json.dump(json_data, f, indent=2) - print(f"JSON data written to {output_file}") - return - except IndexError: - print("Error: Please provide a filename after --json") - sys.exit(1) - except Exception as e: - print(f"Error writing JSON file: {e}") - sys.exit(1) - - # Original table output code - max_name_width = max(len(name) for name, _ in table_data) - max_name_width = max(max_name_width, len("Package")) - - print(f"\n{'Package':<{max_name_width}} | Size (MB)") - print("-" * max_name_width + "-+-" + "-" * 10) - - for name, size in table_data: - print(f"{name:<{max_name_width}} | {size:>8.2f}") - - print(f"\nTotal size: {total_size:.2f} MB\n") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/extra/start_openwebui.sh b/extra/start_openwebui.sh deleted file mode 100755 index 2cf3d66a..00000000 --- a/extra/start_openwebui.sh +++ /dev/null @@ -1,3 +0,0 @@ -API_ENDPOINT="http://${API_ENDPOINT:-$(ifconfig | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | head -n 1):52415}" -echo "Using API_ENDPOINT=${API_ENDPOINT}" -docker run -d -p 3000:8080 -e OPENAI_API_BASE_URL="${API_ENDPOINT}" -e OPENAI_API_KEY=your_secret_key -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main diff --git a/format.py b/format.py deleted file mode 100644 index 68842493..00000000 --- a/format.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -import subprocess -import sys -import os - - -def run_yapf(target): - if os.path.isfile(target): - files = [target] - else: - files = [os.path.join(root, file) for root, _, files in os.walk(target) for file in files if file.endswith('.py')] - - for file in files: - try: - command = ["yapf", "-i", file] - subprocess.run(command, check=True, capture_output=True, text=True) - print(f"Formatted: {file}") - except subprocess.CalledProcessError as e: - print(f"Error formatting {file}: {e.stderr}") - - -def main(): - if len(sys.argv) < 2: - print("Usage: python3 format.py e.g. python3 format.py ./exo") - sys.exit(1) - - target = sys.argv[1] - run_yapf(target) - print("Formatting completed.") - - -if __name__ == "__main__": - main() diff --git a/install.sh b/install.sh deleted file mode 100755 index a5fffec6..00000000 --- a/install.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -if command -v python3.12 &>/dev/null; then - echo "Python 3.12 is installed, proceeding with python3.12..." - python3.12 -m venv .venv -else - echo "The recommended version of Python to run exo with is Python 3.12, but $(python3 --version) is installed. Proceeding with $(python3 --version)" - python3 -m venv .venv -fi -source .venv/bin/activate -pip install -e . diff --git a/scripts/build_exo.py b/scripts/build_exo.py deleted file mode 100644 index be3704e1..00000000 --- a/scripts/build_exo.py +++ /dev/null @@ -1,64 +0,0 @@ -import site -import subprocess -import sys -import os -import pkgutil - -def run(): - site_packages = site.getsitepackages()[0] - base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - baseimages_dir = os.path.join(base_dir, "exo", "apputil", "baseimages") - - command = [ - f"{sys.executable}", "-m", "nuitka", "exo/main.py", - "--company-name=exolabs", - "--product-name=exo", - "--output-dir=dist", - "--follow-imports", - "--standalone", - "--output-filename=exo", - "--python-flag=no_site", - "--onefile", - f"--include-data-dir={baseimages_dir}=exo/apputil/baseimages" - ] - - if sys.platform == "darwin": - command.extend([ - "--macos-app-name=exo", - "--macos-app-mode=gui", - "--macos-app-version=0.0.1", - "--macos-signed-app-name=net.exolabs.exo", - "--include-distribution-meta=mlx", - "--include-module=mlx._reprlib_fix", - "--include-module=mlx._os_warning", - "--include-distribution-meta=huggingface_hub", - "--include-module=huggingface_hub.repocard", - f"--include-data-files={site_packages}/mlx/lib/mlx.metallib=mlx/lib/mlx.metallib", - f"--include-data-files={site_packages}/mlx/lib/mlx.metallib=./mlx.metallib", - "--include-distribution-meta=pygments", - "--nofollow-import-to=tinygrad" - ]) - inference_modules = [ - name for _, name, _ in pkgutil.iter_modules(['exo/inference/mlx/models']) - ] - for module in inference_modules: - command.append(f"--include-module=exo.inference.mlx.models.{module}") - elif sys.platform == "win32": - command.extend([ - "--windows-icon-from-ico=docs/exo-logo-win.ico", - "--file-version=0.0.1", - "--product-version=0.0.1" - ]) - elif sys.platform.startswith("linux"): - command.extend([ - "--include-distribution-metadata=pygments", - "--linux-icon=docs/exo-rounded.png" - ]) - try: - subprocess.run(command, check=True) - print("Build completed!") - except subprocess.CalledProcessError as e: - print(f"An error occurred: {e}") - -if __name__ == "__main__": - run() diff --git a/scripts/compile_grpc.sh b/scripts/compile_grpc.sh deleted file mode 100755 index b0333bb5..00000000 --- a/scripts/compile_grpc.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -source ./install.sh -pushd exo/networking/grpc -python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. node_service.proto -sed -i '' "s/import\ node_service_pb2/from . &/" node_service_pb2_grpc.py -popd - diff --git a/setup.py b/setup.py deleted file mode 100644 index 6f922c43..00000000 --- a/setup.py +++ /dev/null @@ -1,89 +0,0 @@ -import sys -import platform -import subprocess - -from setuptools import find_packages, setup - -# Base requirements for all platforms -install_requires = [ - "aiohttp==3.10.11", - "aiohttp_cors==0.7.0", - "aiofiles==24.1.0", - "grpcio==1.70.0", - "grpcio-tools==1.70.0", - "Jinja2==3.1.4", - "numpy==2.0.0", - "nuitka==2.5.1", - "nvidia-ml-py==12.560.30", - "opencv-python==4.10.0.84", - "pillow==10.4.0", - "prometheus-client==0.20.0", - "protobuf==5.28.1", - "psutil==6.0.0", - "pyamdgpuinfo==2.1.6;platform_system=='Linux'", - "pydantic==2.9.2", - "requests==2.32.3", - "rich==13.7.1", - "scapy==2.6.1", - "tqdm==4.66.4", - "transformers==4.46.3", - "uuid==1.30", - "uvloop==0.21.0", - "tinygrad @ git+https://github.com/tinygrad/tinygrad.git@ec120ce6b9ce8e4ff4b5692566a683ef240e8bc8", -] - -extras_require = { - "formatting": ["yapf==0.40.2",], - "apple_silicon": [ - "mlx==0.22.0", - "mlx-lm==0.21.1", - ], - "windows": ["pywin32==308",], - "nvidia-gpu": ["nvidia-ml-py==12.560.30",], - "amd-gpu": ["pyrsmi==0.2.0"], -} - -# Check if running on macOS with Apple Silicon -if sys.platform.startswith("darwin") and platform.machine() == "arm64": - install_requires.extend(extras_require["apple_silicon"]) - -# Check if running Windows -if sys.platform.startswith("win32"): - install_requires.extend(extras_require["windows"]) - - -def _add_gpu_requires(): - global install_requires - # Add Nvidia-GPU - try: - out = subprocess.run(['nvidia-smi', '--query-gpu=name', '--format=csv,noheader'], shell=True, text=True, capture_output=True, check=False) - if out.returncode == 0: - install_requires.extend(extras_require["nvidia-gpu"]) - except subprocess.CalledProcessError: - pass - - # Add AMD-GPU - # This will mostly work only on Linux, amd/rocm-smi is not yet supported on Windows - try: - out = subprocess.run(['amd-smi', 'list', '--csv'], shell=True, text=True, capture_output=True, check=False) - if out.returncode == 0: - install_requires.extend(extras_require["amd-gpu"]) - except: - out = subprocess.run(['rocm-smi', 'list', '--csv'], shell=True, text=True, capture_output=True, check=False) - if out.returncode == 0: - install_requires.extend(extras_require["amd-gpu"]) - finally: - pass - - -_add_gpu_requires() - -setup( - name="exo", - version="0.0.1", - packages=find_packages(), - install_requires=install_requires, - extras_require=extras_require, - package_data={"exo": ["tinychat/**/*"]}, - entry_points={"console_scripts": ["exo = exo.main:run"]}, -) diff --git a/test/reconnect.sh b/test/reconnect.sh deleted file mode 100755 index 1e9a2add..00000000 --- a/test/reconnect.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -echo "Starting node 1" -DEBUG_DISCOVERY=7 DEBUG=7 python3 main.py --node-id "node1" --listen-port 5678 --broadcast-port 5679 --chatgpt-api-port 52415 --chatgpt-api-response-timeout 900 > output1.log 2>&1 & -PID1=$! -echo "Started node 1 PID: $PID1" -echo "Starting node 2" -DEBUG_DISCOVERY=7 DEBUG=7 python3 main.py --node-id "node2" --listen-port 5679 --broadcast-port 5678 --chatgpt-api-port 8001 --chatgpt-api-response-timeout 900 > output2.log 2>&1 & -PID2=$! -echo "Started node 2 PID: $PID2" -sleep 5 -kill $PID2 -sleep 5 -echo "Starting node 2 again..." -DEBUG_DISCOVERY=7 DEBUG=7 python3 main.py --node-id "node2" --listen-port 5679 --broadcast-port 5678 --chatgpt-api-port 8001 --chatgpt-api-response-timeout 900 > output3.log 2>&1 & -PID2=$! -sleep 5 -echo "Killing nodes and ending test..." -kill $PID1 -kill $PID2 -echo "Test complete." \ No newline at end of file diff --git a/test/test_model_helpers.py b/test/test_model_helpers.py deleted file mode 100644 index a8215017..00000000 --- a/test/test_model_helpers.py +++ /dev/null @@ -1,121 +0,0 @@ -import unittest -from exo.models import get_supported_models, model_cards -from exo.inference.inference_engine import inference_engine_classes -from typing import NamedTuple - -class TestCase(NamedTuple): - name: str - engine_lists: list # Will contain short names, will be mapped to class names - expected_models_contains: list - min_count: int | None - exact_count: int | None - max_count: int | None - -# Helper function to map short names to class names -def expand_engine_lists(engine_lists): - def map_engine(engine): - return inference_engine_classes.get(engine, engine) # Return original name if not found - - return [[map_engine(engine) for engine in sublist] - for sublist in engine_lists] - -test_cases = [ - TestCase( - name="single_mlx_engine", - engine_lists=[["mlx"]], - expected_models_contains=["llama-3.2-1b", "llama-3.1-70b", "mistral-nemo"], - min_count=10, - exact_count=None, - max_count=None - ), - TestCase( - name="single_tinygrad_engine", - engine_lists=[["tinygrad"]], - expected_models_contains=["llama-3.2-1b", "llama-3.2-3b"], - min_count=5, - exact_count=None, - max_count=15 - ), - TestCase( - name="multiple_engines_or", - engine_lists=[["mlx", "tinygrad"], ["mlx"]], - expected_models_contains=["llama-3.2-1b", "llama-3.2-3b", "mistral-nemo"], - min_count=10, - exact_count=None, - max_count=None - ), - TestCase( - name="multiple_engines_all", - engine_lists=[["mlx", "tinygrad"], ["mlx", "tinygrad"]], - expected_models_contains=["llama-3.2-1b", "llama-3.2-3b", "mistral-nemo"], - min_count=10, - exact_count=None, - max_count=None - ), - TestCase( - name="distinct_engine_lists", - engine_lists=[["mlx"], ["tinygrad"]], - expected_models_contains=["llama-3.2-1b"], - min_count=5, - exact_count=None, - max_count=15 - ), - TestCase( - name="no_engines", - engine_lists=[], - expected_models_contains=None, - min_count=None, - exact_count=len(model_cards), - max_count=None - ), - TestCase( - name="nonexistent_engine", - engine_lists=[["NonexistentEngine"]], - expected_models_contains=[], - min_count=None, - exact_count=0, - max_count=None - ), - TestCase( - name="dummy_engine", - engine_lists=[["dummy"]], - expected_models_contains=["dummy"], - min_count=None, - exact_count=1, - max_count=None - ), -] - -class TestModelHelpers(unittest.TestCase): - def test_get_supported_models(self): - for case in test_cases: - with self.subTest(f"{case.name}_short_names"): - result = get_supported_models(case.engine_lists) - self._verify_results(case, result) - - with self.subTest(f"{case.name}_class_names"): - class_name_lists = expand_engine_lists(case.engine_lists) - result = get_supported_models(class_name_lists) - self._verify_results(case, result) - - def _verify_results(self, case, result): - if case.expected_models_contains: - for model in case.expected_models_contains: - self.assertIn(model, result) - - if case.min_count: - self.assertGreater(len(result), case.min_count) - - if case.exact_count is not None: - self.assertEqual(len(result), case.exact_count) - - # Special case for distinct lists test - if case.name == "distinct_engine_lists": - self.assertLess(len(result), 15) - self.assertNotIn("mistral-nemo", result) - - if case.max_count: - self.assertLess(len(result), case.max_count) - -if __name__ == '__main__': - unittest.main() diff --git a/test/test_tokenizers.py b/test/test_tokenizers.py deleted file mode 100644 index 6e1b383e..00000000 --- a/test/test_tokenizers.py +++ /dev/null @@ -1,42 +0,0 @@ -import os -import re -from transformers import AutoTokenizer, AutoProcessor -from exo.models import model_cards - - -def test_tokenizer(name, tokenizer, verbose=False): - print(f"--- {name} ({tokenizer.__class__.__name__}) ---") - text = "Hello! How can I assist you today? Let me know if you need help with something or just want to chat." - encoded = tokenizer.encode(text) - decoded = tokenizer.decode(encoded) - - print(f"{encoded=}") - print(f"{decoded=}") - - reconstructed = "" - for token in encoded: - if verbose: - print(f"{token=}") - print(f"{tokenizer.decode([token])=}") - reconstructed += tokenizer.decode([token]) - print(f"{reconstructed=}") - - strip_tokens = lambda s: s.lstrip(tokenizer.decode([tokenizer.bos_token_id])).rstrip(tokenizer.decode([tokenizer.eos_token_id])) - assert text == strip_tokens(decoded) == strip_tokens(reconstructed) - -ignore = ["TriAiExperiments/SFR-Iterative-DPO-LLaMA-3-70B-R", "mlx-community/DeepSeek-Coder-V2-Lite-Instruct-4bit-mlx", "mlx-community/DeepSeek-V2.5-MLX-AQ4_1_64", "llava-hf/llava-1.5-7b-hf", "mlx-community/Qwen*", "dummy", "mlx-community/Meta-Llama-3.1-405B-Instruct-8bit", "mlx-community/Phi-3.5-mini-instruct-4bit", "mlx-community/phi-4-4bit", "stabilityai/stable-diffusion-2-1-base"] -ignore_pattern = re.compile(r"^(" + "|".join(model.replace("*", ".*") for model in ignore) + r")") -models = [] -for model_id in model_cards: - for engine_type, repo_id in model_cards[model_id].get("repo", {}).items(): - if not ignore_pattern.match(repo_id): - models.append(repo_id) -models = list(set(models)) - -verbose = os.environ.get("VERBOSE", "0").lower() == "1" -for m in models: - # TODO: figure out why use_fast=False is giving inconsistent behaviour (no spaces decoding invididual tokens) for Mistral-Large-Instruct-2407-4bit - # test_tokenizer(m, AutoProcessor.from_pretrained(m, use_fast=False), verbose) - if m not in ["mlx-community/DeepSeek-R1-4bit", "mlx-community/DeepSeek-R1-3bit", "mlx-community/DeepSeek-V3-4bit", "mlx-community/DeepSeek-V3-3bit"]: - test_tokenizer(m, AutoProcessor.from_pretrained(m, use_fast=True, trust_remote_code=True), verbose) - test_tokenizer(m, AutoTokenizer.from_pretrained(m, trust_remote_code=True), verbose)