mirror of
https://github.com/exo-explore/exo.git
synced 2026-02-19 07:17:30 -05:00
Compare commits
1 Commits
feat/dashb
...
aiohttp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
523eff541e |
@@ -16,10 +16,9 @@
|
||||
/* Gotham-inspired accent colors */
|
||||
--exo-grid: oklch(0.25 0 0);
|
||||
--exo-scanline: oklch(0.15 0 0);
|
||||
--exo-glow-yellow: oklch(0.85 0.18 85 / 0.3);
|
||||
--exo-glow-yellow-strong: oklch(0.85 0.18 85 / 0.5);
|
||||
--exo-bg-hover: oklch(0.18 0 0);
|
||||
|
||||
--exo-glow-yellow: 0 0 20px oklch(0.85 0.18 85 / 0.3);
|
||||
--exo-glow-yellow-strong: 0 0 40px oklch(0.85 0.18 85 / 0.5);
|
||||
|
||||
/* Theme Variables */
|
||||
--radius: 0.375rem;
|
||||
--background: var(--exo-black);
|
||||
@@ -42,237 +41,6 @@
|
||||
--ring: var(--exo-yellow);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
LIGHT THEME — "Mission Control, Dawn Shift"
|
||||
Warm parchment + deep amber. Applied when <html> has .light class.
|
||||
============================================================ */
|
||||
html.light {
|
||||
/* EXO brand palette — warm amber shift */
|
||||
--exo-black: oklch(0.97 0.015 80);
|
||||
--exo-dark-gray: oklch(0.92 0.012 80);
|
||||
--exo-medium-gray: oklch(0.83 0.009 78);
|
||||
--exo-light-gray: oklch(0.50 0.018 75);
|
||||
--exo-yellow: oklch(0.50 0.14 65);
|
||||
--exo-yellow-darker: oklch(0.40 0.13 65);
|
||||
--exo-yellow-glow: oklch(0.60 0.14 65);
|
||||
|
||||
--exo-grid: oklch(0.88 0.009 80);
|
||||
--exo-scanline: oklch(0.93 0.010 80);
|
||||
--exo-glow-yellow: oklch(0.50 0.14 65 / 0.12);
|
||||
--exo-glow-yellow-strong: oklch(0.50 0.14 65 / 0.22);
|
||||
--exo-bg-hover: oklch(0.89 0.010 80);
|
||||
|
||||
/* Semantic tokens */
|
||||
--background: oklch(0.97 0.015 80);
|
||||
--foreground: oklch(0.13 0.015 75);
|
||||
--card: oklch(0.92 0.012 80);
|
||||
--card-foreground: oklch(0.13 0.015 75);
|
||||
--popover: oklch(0.95 0.012 80);
|
||||
--popover-foreground: oklch(0.13 0.015 75);
|
||||
--primary: oklch(0.50 0.14 65);
|
||||
--primary-foreground: oklch(0.97 0.015 80);
|
||||
--secondary: oklch(0.88 0.008 80);
|
||||
--secondary-foreground: oklch(0.15 0.012 75);
|
||||
--muted: oklch(0.90 0.009 80);
|
||||
--muted-foreground: oklch(0.50 0.018 75);
|
||||
--accent: oklch(0.88 0.008 80);
|
||||
--accent-foreground: oklch(0.15 0.012 75);
|
||||
--destructive: oklch(0.52 0.22 25);
|
||||
--border: oklch(0.84 0.007 78);
|
||||
--input: oklch(0.87 0.008 80);
|
||||
--ring: oklch(0.50 0.14 65);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
LIGHT MODE UTILITY OVERRIDES
|
||||
============================================================ */
|
||||
html.light {
|
||||
& .text-white,
|
||||
& .text-white\/90,
|
||||
& .text-white\/80,
|
||||
& .text-white\/70 {
|
||||
color: var(--foreground) !important;
|
||||
}
|
||||
& .text-white\/60,
|
||||
& .text-white\/50 {
|
||||
color: color-mix(in oklch, var(--foreground) 60%, transparent) !important;
|
||||
}
|
||||
& .text-white\/40,
|
||||
& .text-white\/30 {
|
||||
color: color-mix(in oklch, var(--foreground) 38%, transparent) !important;
|
||||
}
|
||||
|
||||
& .bg-black\/80,
|
||||
& .bg-black\/60,
|
||||
& .bg-black\/50,
|
||||
& .bg-black\/40 {
|
||||
background-color: oklch(0.90 0.010 80 / 0.7) !important;
|
||||
}
|
||||
& [class*="bg-exo-black/"] {
|
||||
background-color: oklch(0.90 0.010 80 / 0.6) !important;
|
||||
}
|
||||
& [class*="shadow-black"] {
|
||||
--tw-shadow-color: oklch(0.30 0.010 75 / 0.10) !important;
|
||||
}
|
||||
|
||||
& ::-webkit-scrollbar-track {
|
||||
background: oklch(0.93 0.010 80) !important;
|
||||
}
|
||||
& ::-webkit-scrollbar-thumb {
|
||||
background: oklch(0.76 0.010 78) !important;
|
||||
}
|
||||
& ::-webkit-scrollbar-thumb:hover {
|
||||
background: oklch(0.50 0.14 65 / 0.6) !important;
|
||||
}
|
||||
|
||||
& .command-panel {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
oklch(0.94 0.012 80 / 0.96) 0%,
|
||||
oklch(0.91 0.010 80 / 0.98) 100%
|
||||
) !important;
|
||||
border-color: oklch(0.82 0.008 78) !important;
|
||||
box-shadow:
|
||||
inset 0 1px 0 oklch(1 0 0 / 0.6),
|
||||
0 4px 20px oklch(0.30 0.010 75 / 0.08) !important;
|
||||
}
|
||||
|
||||
& .glow-text {
|
||||
text-shadow:
|
||||
0 0 12px oklch(0.50 0.14 65 / 0.20),
|
||||
0 1px 3px oklch(0.30 0.010 75 / 0.12) !important;
|
||||
}
|
||||
|
||||
& .grid-bg {
|
||||
background-image:
|
||||
linear-gradient(oklch(0.75 0.008 78 / 0.25) 1px, transparent 1px),
|
||||
linear-gradient(90deg, oklch(0.75 0.008 78 / 0.25) 1px, transparent 1px) !important;
|
||||
}
|
||||
|
||||
& .scanlines::before {
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent,
|
||||
transparent 2px,
|
||||
oklch(0.50 0.010 78 / 0.018) 2px,
|
||||
oklch(0.50 0.010 78 / 0.018) 4px
|
||||
) !important;
|
||||
}
|
||||
|
||||
& .crt-screen {
|
||||
background: radial-gradient(
|
||||
ellipse at center,
|
||||
oklch(0.95 0.012 80) 0%,
|
||||
oklch(0.92 0.010 80) 50%,
|
||||
oklch(0.89 0.009 80) 100%
|
||||
) !important;
|
||||
box-shadow:
|
||||
inset 0 0 60px oklch(0.30 0.010 75 / 0.04),
|
||||
0 0 30px oklch(0.50 0.14 65 / 0.04) !important;
|
||||
}
|
||||
|
||||
& .graph-link {
|
||||
stroke: oklch(0.50 0.018 75 / 0.45) !important;
|
||||
filter: none !important;
|
||||
}
|
||||
& .graph-link-active {
|
||||
stroke: oklch(0.50 0.14 65 / 0.75) !important;
|
||||
filter: none !important;
|
||||
}
|
||||
|
||||
& .shooting-stars {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
& img[alt="EXO"] {
|
||||
filter: brightness(0) drop-shadow(0 0 6px oklch(0.30 0.010 75 / 0.10)) !important;
|
||||
}
|
||||
|
||||
& .text-red-400 { color: oklch(0.52 0.22 25) !important; }
|
||||
& .text-green-400 { color: oklch(0.48 0.17 155) !important; }
|
||||
& .text-blue-200,
|
||||
& .text-blue-300,
|
||||
& .text-blue-400 { color: oklch(0.48 0.17 250) !important; }
|
||||
|
||||
& .bg-red-500\/10 { background-color: oklch(0.52 0.22 25 / 0.07) !important; }
|
||||
& .bg-red-500\/20 { background-color: oklch(0.52 0.22 25 / 0.11) !important; }
|
||||
& .bg-red-500\/30 { background-color: oklch(0.52 0.22 25 / 0.14) !important; }
|
||||
|
||||
& textarea,
|
||||
& input[type="text"] { color: var(--foreground) !important; }
|
||||
& textarea::placeholder,
|
||||
& input::placeholder { color: oklch(0.50 0.012 78 / 0.55) !important; }
|
||||
|
||||
& .code-block-wrapper,
|
||||
& .math-display-wrapper {
|
||||
background: oklch(0.95 0.010 80) !important;
|
||||
border-color: oklch(0.83 0.007 78) !important;
|
||||
}
|
||||
& .code-block-header,
|
||||
& .math-display-header {
|
||||
background: oklch(0.91 0.009 80) !important;
|
||||
border-color: oklch(0.85 0.007 78) !important;
|
||||
}
|
||||
& .inline-code {
|
||||
background: oklch(0.89 0.009 80) !important;
|
||||
color: oklch(0.20 0.012 75) !important;
|
||||
}
|
||||
|
||||
& blockquote { background: oklch(0.93 0.010 80) !important; }
|
||||
& th {
|
||||
background: oklch(0.90 0.009 80) !important;
|
||||
border-color: oklch(0.80 0.007 78) !important;
|
||||
}
|
||||
& td { border-color: oklch(0.84 0.007 78) !important; }
|
||||
& hr { border-color: oklch(0.84 0.007 78) !important; }
|
||||
|
||||
& .hljs { color: oklch(0.22 0.012 75) !important; }
|
||||
& .hljs-keyword, & .hljs-selector-tag, & .hljs-literal, & .hljs-section, & .hljs-link {
|
||||
color: oklch(0.45 0.18 300) !important;
|
||||
}
|
||||
& .hljs-string, & .hljs-title, & .hljs-name, & .hljs-type,
|
||||
& .hljs-attribute, & .hljs-symbol, & .hljs-bullet, & .hljs-addition,
|
||||
& .hljs-variable, & .hljs-template-tag, & .hljs-template-variable {
|
||||
color: oklch(0.45 0.14 65) !important;
|
||||
}
|
||||
& .hljs-comment, & .hljs-quote, & .hljs-deletion, & .hljs-meta {
|
||||
color: oklch(0.55 0.010 78) !important;
|
||||
}
|
||||
& .hljs-number, & .hljs-regexp, & .hljs-built_in {
|
||||
color: oklch(0.45 0.15 160) !important;
|
||||
}
|
||||
& .hljs-function, & .hljs-class .hljs-title {
|
||||
color: oklch(0.42 0.17 240) !important;
|
||||
}
|
||||
|
||||
& .katex, & .katex .mord, & .katex .minner, & .katex .mop,
|
||||
& .katex .mbin, & .katex .mrel, & .katex .mpunct {
|
||||
color: oklch(0.15 0.012 75) !important;
|
||||
}
|
||||
& .katex .frac-line, & .katex .overline-line, & .katex .underline-line,
|
||||
& .katex .hline, & .katex .rule {
|
||||
border-color: oklch(0.25 0.012 75) !important;
|
||||
background: oklch(0.25 0.012 75) !important;
|
||||
}
|
||||
& .katex svg { fill: oklch(0.25 0.012 75) !important; stroke: oklch(0.25 0.012 75) !important; }
|
||||
& .katex svg path { stroke: oklch(0.25 0.012 75) !important; }
|
||||
& .katex .mopen, & .katex .mclose,
|
||||
& .katex .delimsizing, & [class^="katex .delim-size"] {
|
||||
color: oklch(0.35 0.012 75) !important;
|
||||
}
|
||||
|
||||
& .latex-proof { background: oklch(0.96 0.010 80) !important; border-left-color: oklch(0.72 0.010 78) !important; }
|
||||
& .latex-proof-header { color: oklch(0.22 0.012 75) !important; }
|
||||
& .latex-proof-content { color: oklch(0.15 0.012 75) !important; }
|
||||
& .latex-proof-content::after { color: oklch(0.48 0.012 75) !important; }
|
||||
& .latex-theorem { background: oklch(0.94 0.010 80) !important; border-color: oklch(0.80 0.008 78) !important; }
|
||||
& .latex-diagram-placeholder {
|
||||
background: oklch(0.96 0.010 80) !important;
|
||||
border-color: oklch(0.80 0.008 78) !important;
|
||||
color: oklch(0.38 0.012 75) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--radius-sm: calc(var(--radius) - 2px);
|
||||
--radius-md: var(--radius);
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="dark">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script>
|
||||
try {
|
||||
if (localStorage.getItem('exo-theme') === 'light') {
|
||||
document.documentElement.classList.remove('dark');
|
||||
document.documentElement.classList.add('light');
|
||||
}
|
||||
} catch (_) {}
|
||||
</script>
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>EXO</title>
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
const modelSupportsThinking = $derived(() => {
|
||||
if (!currentModel) return false;
|
||||
const caps = modelCapabilities[currentModel] || [];
|
||||
return caps.includes("thinking_toggle") && caps.includes("text");
|
||||
return caps.includes("thinking") && caps.includes("text");
|
||||
});
|
||||
|
||||
const isEditOnlyWithoutImage = $derived(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { browser } from "$app/environment";
|
||||
import { theme } from "$lib/stores/theme.svelte";
|
||||
|
||||
export let showHome = true;
|
||||
export let onHome: (() => void) | null = null;
|
||||
@@ -80,48 +79,10 @@
|
||||
/>
|
||||
</button>
|
||||
|
||||
<!-- Right: Theme toggle + Home + Downloads -->
|
||||
<!-- Right: Home + Downloads -->
|
||||
<div
|
||||
class="absolute right-6 top-1/2 -translate-y-1/2 flex items-center gap-4"
|
||||
>
|
||||
<button
|
||||
onclick={() => theme.toggle()}
|
||||
class="p-2 rounded border border-exo-medium-gray/40 hover:border-exo-yellow/50 transition-colors cursor-pointer"
|
||||
title={theme.isLight ? "Switch to dark mode" : "Switch to light mode"}
|
||||
aria-label={theme.isLight
|
||||
? "Switch to dark mode"
|
||||
: "Switch to light mode"}
|
||||
>
|
||||
{#if theme.isLight}
|
||||
<svg
|
||||
class="w-4 h-4 text-exo-light-gray"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M21 12.79A9 9 0 1111.21 3a7 7 0 009.79 9.79z"
|
||||
/>
|
||||
</svg>
|
||||
{:else}
|
||||
<svg
|
||||
class="w-4 h-4 text-exo-light-gray"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<circle cx="12" cy="12" r="5" />
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
d="M12 1v2m0 18v2M4.22 4.22l1.42 1.42m12.72 12.72l1.42 1.42M1 12h2m18 0h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
{#if showHome}
|
||||
<button
|
||||
onclick={handleHome}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { browser } from "$app/environment";
|
||||
|
||||
let _isLight = $state(false);
|
||||
|
||||
export const theme = {
|
||||
get isLight() {
|
||||
return _isLight;
|
||||
},
|
||||
|
||||
init() {
|
||||
if (!browser) return;
|
||||
_isLight = document.documentElement.classList.contains("light");
|
||||
},
|
||||
|
||||
toggle() {
|
||||
if (!browser) return;
|
||||
_isLight = !_isLight;
|
||||
if (_isLight) {
|
||||
document.documentElement.classList.remove("dark");
|
||||
document.documentElement.classList.add("light");
|
||||
localStorage.setItem("exo-theme", "light");
|
||||
} else {
|
||||
document.documentElement.classList.remove("light");
|
||||
document.documentElement.classList.add("dark");
|
||||
localStorage.setItem("exo-theme", "dark");
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,13 +1,7 @@
|
||||
<script lang="ts">
|
||||
import "../app.css";
|
||||
import { onMount } from "svelte";
|
||||
import { theme } from "$lib/stores/theme.svelte";
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
onMount(() => {
|
||||
theme.init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
||||
@@ -6,8 +6,6 @@ readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"aiofiles>=24.1.0",
|
||||
"aiohttp>=3.12.14",
|
||||
"types-aiofiles>=24.1.0.20250708",
|
||||
"pydantic>=2.11.7",
|
||||
"fastapi>=0.116.1",
|
||||
"filelock>=3.18.0",
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "deepseek"
|
||||
quantization = "4bit"
|
||||
base_model = "DeepSeek V3.1"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 405874409472
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "deepseek"
|
||||
quantization = "8bit"
|
||||
base_model = "DeepSeek V3.1"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 765577920512
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "glm"
|
||||
quantization = "8bit"
|
||||
base_model = "GLM 4.5 Air"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 122406567936
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "glm"
|
||||
quantization = "bf16"
|
||||
base_model = "GLM 4.5 Air"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 229780750336
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "glm"
|
||||
quantization = "4bit"
|
||||
base_model = "GLM 4.7"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 198556925568
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "glm"
|
||||
quantization = "6bit"
|
||||
base_model = "GLM 4.7"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 286737579648
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "glm"
|
||||
quantization = "8bit"
|
||||
base_model = "GLM 4.7"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 396963397248
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "glm"
|
||||
quantization = "4bit"
|
||||
base_model = "GLM 4.7 Flash"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 19327352832
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "glm"
|
||||
quantization = "5bit"
|
||||
base_model = "GLM 4.7 Flash"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 22548578304
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "glm"
|
||||
quantization = "6bit"
|
||||
base_model = "GLM 4.7 Flash"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 26843545600
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "glm"
|
||||
quantization = "8bit"
|
||||
base_model = "GLM 4.7 Flash"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 34359738368
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "kimi"
|
||||
quantization = ""
|
||||
base_model = "Kimi K2"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 706522120192
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "kimi"
|
||||
quantization = ""
|
||||
base_model = "Kimi K2.5"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 662498705408
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "minimax"
|
||||
quantization = "3bit"
|
||||
base_model = "MiniMax M2.1"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 100086644736
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "minimax"
|
||||
quantization = "8bit"
|
||||
base_model = "MiniMax M2.1"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 242986745856
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "qwen"
|
||||
quantization = "4bit"
|
||||
base_model = "Qwen3 0.6B"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 342884352
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "qwen"
|
||||
quantization = "8bit"
|
||||
base_model = "Qwen3 0.6B"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 698351616
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "qwen"
|
||||
quantization = "4bit"
|
||||
base_model = "Qwen3 235B"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 141733920768
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "qwen"
|
||||
quantization = "8bit"
|
||||
base_model = "Qwen3 235B"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 268435456000
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "qwen"
|
||||
quantization = "4bit"
|
||||
base_model = "Qwen3 30B"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 17612931072
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "qwen"
|
||||
quantization = "8bit"
|
||||
base_model = "Qwen3 30B"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 33279705088
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "qwen"
|
||||
quantization = "4bit"
|
||||
base_model = "Qwen3 Next 80B"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 47080074240
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "qwen"
|
||||
quantization = "8bit"
|
||||
base_model = "Qwen3 Next 80B"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 88814387200
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "step"
|
||||
quantization = "4bit"
|
||||
base_model = "Step 3.5 Flash"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 114572190076
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "step"
|
||||
quantization = "6bit"
|
||||
base_model = "Step 3.5 Flash"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 159039627774
|
||||
|
||||
@@ -6,7 +6,7 @@ tasks = ["TextGeneration"]
|
||||
family = "step"
|
||||
quantization = "8bit"
|
||||
base_model = "Step 3.5 Flash"
|
||||
capabilities = ["text", "thinking", "thinking_toggle"]
|
||||
capabilities = ["text", "thinking"]
|
||||
|
||||
[storage_size]
|
||||
in_bytes = 209082699847
|
||||
|
||||
@@ -8,13 +8,13 @@ import traceback
|
||||
from collections.abc import Awaitable
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
from typing import Callable, Literal
|
||||
from typing import Callable, Literal, cast
|
||||
from urllib.parse import urljoin
|
||||
|
||||
import aiofiles
|
||||
import aiofiles.os as aios
|
||||
import aiohttp
|
||||
import certifi
|
||||
import httpx
|
||||
from huggingface_hub import (
|
||||
snapshot_download, # pyright: ignore[reportUnknownVariableType]
|
||||
)
|
||||
@@ -330,17 +330,17 @@ async def _fetch_file_list(
|
||||
headers = await get_download_headers()
|
||||
async with (
|
||||
create_http_session(timeout_profile="short") as session,
|
||||
session.get(url, headers=headers) as response,
|
||||
):
|
||||
if response.status in [401, 403]:
|
||||
msg = await _build_auth_error_message(response.status, model_id)
|
||||
response = await session.get(url, headers=headers)
|
||||
if response.status_code in [401, 403]:
|
||||
msg = await _build_auth_error_message(response.status_code, model_id)
|
||||
raise HuggingFaceAuthenticationError(msg)
|
||||
elif response.status == 429:
|
||||
elif response.status_code == 429:
|
||||
raise HuggingFaceRateLimitError(
|
||||
f"Couldn't download {model_id} because of HuggingFace rate limit."
|
||||
)
|
||||
elif response.status == 200:
|
||||
data_json = await response.text()
|
||||
elif response.status_code == 200:
|
||||
data_json = response.text
|
||||
data = TypeAdapter(list[FileListEntry]).validate_json(data_json)
|
||||
files: list[FileListEntry] = []
|
||||
for item in data:
|
||||
@@ -353,7 +353,7 @@ async def _fetch_file_list(
|
||||
files.extend(subfiles)
|
||||
return files
|
||||
else:
|
||||
raise Exception(f"Failed to fetch file list: {response.status}")
|
||||
raise Exception(f"Failed to fetch file list: {response.status_code}")
|
||||
|
||||
|
||||
async def get_download_headers() -> dict[str, str]:
|
||||
@@ -361,34 +361,29 @@ async def get_download_headers() -> dict[str, str]:
|
||||
|
||||
|
||||
def create_http_session(
|
||||
auto_decompress: bool = False,
|
||||
timeout_profile: Literal["short", "long"] = "long",
|
||||
) -> aiohttp.ClientSession:
|
||||
) -> httpx.AsyncClient:
|
||||
if timeout_profile == "short":
|
||||
total_timeout = 30
|
||||
connect_timeout = 10
|
||||
sock_read_timeout = 30
|
||||
sock_connect_timeout = 10
|
||||
read_timeout = 30
|
||||
else:
|
||||
total_timeout = 1800
|
||||
connect_timeout = 60
|
||||
sock_read_timeout = 60
|
||||
sock_connect_timeout = 60
|
||||
read_timeout = 60
|
||||
|
||||
ssl_context = ssl.create_default_context(
|
||||
cafile=os.getenv("SSL_CERT_FILE") or certifi.where()
|
||||
)
|
||||
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
||||
|
||||
return aiohttp.ClientSession(
|
||||
auto_decompress=auto_decompress,
|
||||
connector=connector,
|
||||
proxy=os.getenv("HTTPS_PROXY") or os.getenv("HTTP_PROXY") or None,
|
||||
timeout=aiohttp.ClientTimeout(
|
||||
total=total_timeout,
|
||||
# default here is to load env vars
|
||||
return httpx.AsyncClient(
|
||||
verify=ssl_context,
|
||||
timeout=httpx.Timeout(
|
||||
connect=connect_timeout,
|
||||
sock_read=sock_read_timeout,
|
||||
sock_connect=sock_connect_timeout,
|
||||
read=read_timeout,
|
||||
write=total_timeout,
|
||||
pool=total_timeout,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -415,26 +410,28 @@ async def file_meta(
|
||||
headers = await get_download_headers()
|
||||
async with (
|
||||
create_http_session(timeout_profile="short") as session,
|
||||
session.head(url, headers=headers) as r,
|
||||
session.stream("HEAD", url, headers=headers) as r,
|
||||
):
|
||||
if r.status == 307:
|
||||
if r.status_code == 307:
|
||||
# On redirect, only trust Hugging Face's x-linked-* headers.
|
||||
x_linked_size = r.headers.get("x-linked-size")
|
||||
x_linked_etag = r.headers.get("x-linked-etag")
|
||||
x_linked_size = cast(str | None, r.headers.get("x-linked-size"))
|
||||
x_linked_etag = cast(str | None, r.headers.get("x-linked-etag"))
|
||||
if x_linked_size and x_linked_etag:
|
||||
content_length = int(x_linked_size)
|
||||
etag = trim_etag(x_linked_etag)
|
||||
return content_length, etag
|
||||
# Otherwise, follow the redirect to get authoritative size/hash
|
||||
redirected_location = r.headers.get("location")
|
||||
redirected_location = cast(str | None, r.headers.get("location"))
|
||||
return await file_meta(model_id, revision, path, redirected_location)
|
||||
if r.status in [401, 403]:
|
||||
msg = await _build_auth_error_message(r.status, model_id)
|
||||
if r.status_code in [401, 403]:
|
||||
msg = await _build_auth_error_message(r.status_code, model_id)
|
||||
raise HuggingFaceAuthenticationError(msg)
|
||||
content_length = int(
|
||||
r.headers.get("x-linked-size") or r.headers.get("content-length") or 0
|
||||
content_length = cast(
|
||||
str | None,
|
||||
r.headers.get("x-linked-size") or r.headers.get("content-length"),
|
||||
)
|
||||
etag = r.headers.get("x-linked-etag") or r.headers.get("etag")
|
||||
content_length = 0 if content_length is None else int(content_length)
|
||||
etag = cast(str | None, r.headers.get("x-linked-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}"
|
||||
etag = trim_etag(etag)
|
||||
@@ -537,20 +534,20 @@ async def _download_file(
|
||||
n_read = resume_byte_pos or 0
|
||||
async with (
|
||||
create_http_session(timeout_profile="long") as session,
|
||||
session.get(url, headers=headers) as r,
|
||||
session.stream("GET", url, headers=headers, follow_redirects=True) as r,
|
||||
):
|
||||
if r.status == 404:
|
||||
if r.status_code == 404:
|
||||
raise FileNotFoundError(f"File not found: {url}")
|
||||
if r.status in [401, 403]:
|
||||
msg = await _build_auth_error_message(r.status, model_id)
|
||||
if r.status_code in [401, 403]:
|
||||
msg = await _build_auth_error_message(r.status_code, model_id)
|
||||
raise HuggingFaceAuthenticationError(msg)
|
||||
assert r.status in [200, 206], (
|
||||
f"Failed to download {path} from {url}: {r.status}"
|
||||
assert r.status_code in [200, 206], (
|
||||
f"Failed to download {path} from {url}: {r.status_code}"
|
||||
)
|
||||
async with aiofiles.open(
|
||||
partial_path, "ab" if resume_byte_pos else "wb"
|
||||
) as f:
|
||||
while chunk := await r.content.read(8 * 1024 * 1024):
|
||||
async for chunk in r.aiter_bytes(8 * 1024 * 1024):
|
||||
n_read = n_read + (await f.write(chunk))
|
||||
on_progress(n_read, length, False)
|
||||
|
||||
|
||||
@@ -189,6 +189,7 @@ class ResumableShardDownloader(ShardDownloader):
|
||||
try:
|
||||
yield await task
|
||||
except Exception as e:
|
||||
task.cancel()
|
||||
logger.warning(f"Error downloading shard: {type(e).__name__}")
|
||||
|
||||
async def get_shard_download_status_for_shard(
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import time
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
K = TypeVar("K")
|
||||
from collections.abc import Hashable
|
||||
|
||||
|
||||
class KeyedBackoff(Generic[K]):
|
||||
class KeyedBackoff[K: Hashable]:
|
||||
"""Tracks exponential backoff state per key."""
|
||||
|
||||
def __init__(self, base: float = 0.5, cap: float = 10.0):
|
||||
|
||||
4
uv.lock
generated
4
uv.lock
generated
@@ -367,7 +367,6 @@ version = "0.3.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiofiles", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "aiohttp", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "exo-pyo3-bindings", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "fastapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
@@ -389,7 +388,6 @@ dependencies = [
|
||||
{ name = "rustworkx", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "tiktoken", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "tomlkit", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "types-aiofiles", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "zstandard", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
]
|
||||
|
||||
@@ -406,7 +404,6 @@ dev = [
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "aiofiles", specifier = ">=24.1.0" },
|
||||
{ name = "aiohttp", specifier = ">=3.12.14" },
|
||||
{ name = "anyio", specifier = "==4.11.0" },
|
||||
{ name = "exo-pyo3-bindings", editable = "rust/exo_pyo3_bindings" },
|
||||
{ name = "fastapi", specifier = ">=0.116.1" },
|
||||
@@ -428,7 +425,6 @@ requires-dist = [
|
||||
{ name = "rustworkx", specifier = ">=0.17.1" },
|
||||
{ name = "tiktoken", specifier = ">=0.12.0" },
|
||||
{ name = "tomlkit", specifier = ">=0.14.0" },
|
||||
{ name = "types-aiofiles", specifier = ">=24.1.0.20250708" },
|
||||
{ name = "zstandard", specifier = ">=0.23.0" },
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user