Merge branch 'main' into fix/transformers-v5-tiers
This commit is contained in:
commit
b03e07ee2d
7 changed files with 93 additions and 51 deletions
10
install.ps1
10
install.ps1
|
|
@ -819,7 +819,7 @@ shell.Run cmd, 0, False
|
|||
if ($SkipTorch) {
|
||||
# No-torch: install unsloth + unsloth-zoo with --no-deps, then
|
||||
# runtime deps (typer, safetensors, transformers, etc.) with --no-deps.
|
||||
$baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --no-deps --reinstall-package unsloth --reinstall-package unsloth-zoo "unsloth>=2026.4.2" unsloth-zoo }
|
||||
$baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --no-deps --reinstall-package unsloth --reinstall-package unsloth-zoo "unsloth>=2026.4.4" unsloth-zoo }
|
||||
if ($baseInstallExit -eq 0) {
|
||||
$NoTorchReq = Find-NoTorchRuntimeFile
|
||||
if ($NoTorchReq) {
|
||||
|
|
@ -827,7 +827,7 @@ shell.Run cmd, 0, False
|
|||
}
|
||||
}
|
||||
} else {
|
||||
$baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --reinstall-package unsloth --reinstall-package unsloth-zoo "unsloth>=2026.4.2" unsloth-zoo }
|
||||
$baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --reinstall-package unsloth --reinstall-package unsloth-zoo "unsloth>=2026.4.4" unsloth-zoo }
|
||||
}
|
||||
if ($baseInstallExit -ne 0) {
|
||||
Write-Host "[ERROR] Failed to install unsloth (exit code $baseInstallExit)" -ForegroundColor Red
|
||||
|
|
@ -857,7 +857,7 @@ shell.Run cmd, 0, False
|
|||
if ($SkipTorch) {
|
||||
# No-torch: install unsloth + unsloth-zoo with --no-deps, then
|
||||
# runtime deps (typer, safetensors, transformers, etc.) with --no-deps.
|
||||
$baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --no-deps --upgrade-package unsloth --upgrade-package unsloth-zoo "unsloth>=2026.4.2" unsloth-zoo }
|
||||
$baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --no-deps --upgrade-package unsloth --upgrade-package unsloth-zoo "unsloth>=2026.4.4" unsloth-zoo }
|
||||
if ($baseInstallExit -eq 0) {
|
||||
$NoTorchReq = Find-NoTorchRuntimeFile
|
||||
if ($NoTorchReq) {
|
||||
|
|
@ -865,7 +865,7 @@ shell.Run cmd, 0, False
|
|||
}
|
||||
}
|
||||
} elseif ($StudioLocalInstall) {
|
||||
$baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --upgrade-package unsloth "unsloth>=2026.4.2" unsloth-zoo }
|
||||
$baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --upgrade-package unsloth "unsloth>=2026.4.4" unsloth-zoo }
|
||||
} else {
|
||||
$baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython --upgrade-package unsloth "$PackageName" }
|
||||
}
|
||||
|
|
@ -886,7 +886,7 @@ shell.Run cmd, 0, False
|
|||
# Fallback: GPU detection failed to produce a URL -- let uv resolve torch
|
||||
substep "installing unsloth (this may take a few minutes)..."
|
||||
if ($StudioLocalInstall) {
|
||||
$baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython unsloth-zoo "unsloth>=2026.4.2" --torch-backend=auto }
|
||||
$baseInstallExit = Invoke-InstallCommand { uv pip install --python $VenvPython unsloth-zoo "unsloth>=2026.4.4" --torch-backend=auto }
|
||||
if ($baseInstallExit -ne 0) {
|
||||
Write-Host "[ERROR] Failed to install unsloth (exit code $baseInstallExit)" -ForegroundColor Red
|
||||
return
|
||||
|
|
|
|||
10
install.sh
10
install.sh
|
|
@ -1040,7 +1040,7 @@ if [ "$_MIGRATED" = true ]; then
|
|||
# to prevent transitive torch resolution.
|
||||
run_install_cmd "install unsloth (migrated no-torch)" uv pip install --python "$_VENV_PY" --no-deps \
|
||||
--reinstall-package unsloth --reinstall-package unsloth-zoo \
|
||||
"unsloth>=2026.4.2" unsloth-zoo
|
||||
"unsloth>=2026.4.4" unsloth-zoo
|
||||
_NO_TORCH_RT="$(_find_no_torch_runtime)"
|
||||
if [ -n "$_NO_TORCH_RT" ]; then
|
||||
run_install_cmd "install no-torch runtime deps" uv pip install --python "$_VENV_PY" --no-deps -r "$_NO_TORCH_RT"
|
||||
|
|
@ -1048,7 +1048,7 @@ if [ "$_MIGRATED" = true ]; then
|
|||
else
|
||||
run_install_cmd "install unsloth (migrated)" uv pip install --python "$_VENV_PY" \
|
||||
--reinstall-package unsloth --reinstall-package unsloth-zoo \
|
||||
"unsloth>=2026.4.2" unsloth-zoo
|
||||
"unsloth>=2026.4.4" unsloth-zoo
|
||||
fi
|
||||
if [ "$STUDIO_LOCAL_INSTALL" = true ]; then
|
||||
substep "overlaying local repo (editable)..."
|
||||
|
|
@ -1070,7 +1070,7 @@ elif [ -n "$TORCH_INDEX_URL" ]; then
|
|||
# runtime deps (typer, safetensors, transformers, etc.) with --no-deps.
|
||||
run_install_cmd "install unsloth (no-torch)" uv pip install --python "$_VENV_PY" --no-deps \
|
||||
--upgrade-package unsloth --upgrade-package unsloth-zoo \
|
||||
"unsloth>=2026.4.2" unsloth-zoo
|
||||
"unsloth>=2026.4.4" unsloth-zoo
|
||||
_NO_TORCH_RT="$(_find_no_torch_runtime)"
|
||||
if [ -n "$_NO_TORCH_RT" ]; then
|
||||
run_install_cmd "install no-torch runtime deps" uv pip install --python "$_VENV_PY" --no-deps -r "$_NO_TORCH_RT"
|
||||
|
|
@ -1081,7 +1081,7 @@ elif [ -n "$TORCH_INDEX_URL" ]; then
|
|||
fi
|
||||
elif [ "$STUDIO_LOCAL_INSTALL" = true ]; then
|
||||
run_install_cmd "install unsloth (local)" uv pip install --python "$_VENV_PY" \
|
||||
--upgrade-package unsloth "unsloth>=2026.4.2" unsloth-zoo
|
||||
--upgrade-package unsloth "unsloth>=2026.4.4" unsloth-zoo
|
||||
substep "overlaying local repo (editable)..."
|
||||
run_install_cmd "overlay local repo" uv pip install --python "$_VENV_PY" -e "$_REPO_ROOT" --no-deps
|
||||
else
|
||||
|
|
@ -1092,7 +1092,7 @@ else
|
|||
# Fallback: GPU detection failed to produce a URL -- let uv resolve torch
|
||||
substep "installing unsloth (this may take a few minutes)..."
|
||||
if [ "$STUDIO_LOCAL_INSTALL" = true ]; then
|
||||
run_install_cmd "install unsloth (auto torch backend)" uv pip install --python "$_VENV_PY" unsloth-zoo "unsloth>=2026.4.2" --torch-backend=auto
|
||||
run_install_cmd "install unsloth (auto torch backend)" uv pip install --python "$_VENV_PY" unsloth-zoo "unsloth>=2026.4.4" --torch-backend=auto
|
||||
substep "overlaying local repo (editable)..."
|
||||
run_install_cmd "overlay local repo" uv pip install --python "$_VENV_PY" -e "$_REPO_ROOT" --no-deps
|
||||
else
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ huggingfacenotorch = [
|
|||
]
|
||||
huggingface = [
|
||||
"unsloth[huggingfacenotorch]",
|
||||
"unsloth_zoo>=2026.4.2",
|
||||
"unsloth_zoo>=2026.4.3",
|
||||
"torchvision",
|
||||
"unsloth[triton]",
|
||||
]
|
||||
|
|
@ -578,7 +578,7 @@ colab-ampere-torch220 = [
|
|||
"flash-attn>=2.6.3 ; ('linux' in sys_platform)",
|
||||
]
|
||||
colab-new = [
|
||||
"unsloth_zoo>=2026.4.2",
|
||||
"unsloth_zoo>=2026.4.3",
|
||||
"packaging",
|
||||
"tyro",
|
||||
"transformers>=4.51.3,!=4.52.0,!=4.52.1,!=4.52.2,!=4.52.3,!=4.53.0,!=4.54.0,!=4.55.0,!=4.55.1,!=4.57.0,!=4.57.4,!=4.57.5,!=5.0.0,!=5.1.0,<=5.5.0",
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ const LoraCompareContent = memo(function LoraCompareContent({
|
|||
modelType="base"
|
||||
pairId={pairId}
|
||||
initialThreadId={baseThreadId}
|
||||
syncActiveThreadId={false}
|
||||
>
|
||||
<RegisterCompareHandle name="base" />
|
||||
<Thread hideComposer={true} hideWelcome={true} />
|
||||
|
|
@ -242,6 +243,7 @@ const LoraCompareContent = memo(function LoraCompareContent({
|
|||
modelType="lora"
|
||||
pairId={pairId}
|
||||
initialThreadId={loraThreadId}
|
||||
syncActiveThreadId={false}
|
||||
>
|
||||
<RegisterCompareHandle name="lora" />
|
||||
<Thread hideComposer={true} hideWelcome={true} />
|
||||
|
|
@ -343,6 +345,7 @@ const GeneralCompareContent = memo(function GeneralCompareContent({
|
|||
modelType="model1"
|
||||
pairId={pairId}
|
||||
initialThreadId={model1ThreadId}
|
||||
syncActiveThreadId={false}
|
||||
>
|
||||
<RegisterCompareHandle name="model1" />
|
||||
<Thread hideComposer={true} hideWelcome={true} />
|
||||
|
|
@ -376,6 +379,7 @@ const GeneralCompareContent = memo(function GeneralCompareContent({
|
|||
modelType="model2"
|
||||
pairId={pairId}
|
||||
initialThreadId={model2ThreadId}
|
||||
syncActiveThreadId={false}
|
||||
>
|
||||
<RegisterCompareHandle name="model2" />
|
||||
<Thread hideComposer={true} hideWelcome={true} />
|
||||
|
|
@ -479,11 +483,19 @@ function TopBarActions({
|
|||
);
|
||||
}
|
||||
|
||||
function getInitialSingleChatView(): ChatView {
|
||||
const id = useChatRuntimeStore.getState().activeThreadId;
|
||||
if (typeof id === "string" && id.length > 0 && !id.startsWith("__LOCALID_")) {
|
||||
return { mode: "single", threadId: id };
|
||||
}
|
||||
return { mode: "single" };
|
||||
}
|
||||
|
||||
export function ChatPage(): ReactElement {
|
||||
const [view, setView] = useState<ChatView>({
|
||||
mode: "single",
|
||||
newThreadNonce: crypto.randomUUID(),
|
||||
});
|
||||
// Do not set newThreadNonce here: each /chat mount would run ThreadNewChatSwitch
|
||||
// and create spurious threads when navigating (e.g. Recipes / Export). New Chat
|
||||
// explicitly sets a nonce in handleNewThread.
|
||||
const [view, setView] = useState<ChatView>(getInitialSingleChatView);
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const [modelSelectorOpen, setModelSelectorOpen] = useState(false);
|
||||
const [modelSelectorLocked, setModelSelectorLocked] = useState(false);
|
||||
|
|
@ -587,9 +599,20 @@ export function ChatPage(): ReactElement {
|
|||
void ejectModel();
|
||||
}, [ejectModel]);
|
||||
const handleNewThread = useCallback(() => {
|
||||
// Skip if we are already on a fresh unsaved draft with no messages sent.
|
||||
// Once the user sends a message, append() sets activeThreadId in the store,
|
||||
// so we check the store to know whether the current draft has been sent.
|
||||
if (
|
||||
view.mode === "single" &&
|
||||
!view.threadId &&
|
||||
!useChatRuntimeStore.getState().activeThreadId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
useChatRuntimeStore.getState().setActiveThreadId(null);
|
||||
setView({ mode: "single", newThreadNonce: crypto.randomUUID() });
|
||||
}, []);
|
||||
}, [view]);
|
||||
const handleNewCompare = useCallback(() => {
|
||||
setView({ mode: "compare", pairId: crypto.randomUUID() });
|
||||
// Clear activeThreadId so compare panes do not inherit the single-chat
|
||||
|
|
@ -922,7 +945,7 @@ export function ChatPage(): ReactElement {
|
|||
|
||||
{view.mode === "single" ? (
|
||||
<SingleContent
|
||||
key={view.threadId ?? view.newThreadNonce ?? "new"}
|
||||
key={view.threadId ?? "single"}
|
||||
threadId={view.threadId}
|
||||
newThreadNonce={view.newThreadNonce}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -596,6 +596,15 @@ function ThreadHistoryProvider({
|
|||
|
||||
async append({ parentId, message }: ExportedMessageRepositoryItem) {
|
||||
const { remoteId } = await aui.threadListItem().initialize();
|
||||
// Keep single-chat runtime state in sync once a new chat is first
|
||||
// persisted. Compare panes intentionally do not write global activeThreadId.
|
||||
const thread = await db.threads.get(remoteId);
|
||||
if (thread?.modelType === "base" && !thread.pairId) {
|
||||
const store = useChatRuntimeStore.getState();
|
||||
if (store.activeThreadId !== remoteId) {
|
||||
store.setActiveThreadId(remoteId);
|
||||
}
|
||||
}
|
||||
const content = cloneContent(message.content);
|
||||
const attachments =
|
||||
message.role === "user" ? cloneAttachments(message.attachments) : [];
|
||||
|
|
@ -658,7 +667,11 @@ function useRuntimeHook(): ReturnType<typeof useLocalRuntime> {
|
|||
|
||||
function ThreadAutoSwitch({
|
||||
threadId,
|
||||
}: { threadId: string }): ReactElement | null {
|
||||
syncActiveThreadId = true,
|
||||
}: {
|
||||
threadId: string;
|
||||
syncActiveThreadId?: boolean;
|
||||
}): ReactElement | null {
|
||||
const aui = useAui();
|
||||
const isLoading = useAuiState(({ threads }) => threads.isLoading);
|
||||
const mainThreadId = useAuiState(({ threads }) => threads.mainThreadId);
|
||||
|
|
@ -669,6 +682,13 @@ function ThreadAutoSwitch({
|
|||
}
|
||||
}, [aui, isLoading, mainThreadId, threadId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!syncActiveThreadId || isLoading || mainThreadId !== threadId) {
|
||||
return;
|
||||
}
|
||||
useChatRuntimeStore.getState().setActiveThreadId(threadId);
|
||||
}, [isLoading, mainThreadId, syncActiveThreadId, threadId]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -682,30 +702,10 @@ function ThreadNewChatSwitch({
|
|||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
// Clear immediately so the adapter never picks up a stale thread ID
|
||||
// from a previous chat while we initialize the new one.
|
||||
// Switch to a fresh local thread without persisting it yet.
|
||||
// Persistence still happens on first message append.
|
||||
void aui.threads().switchToNewThread();
|
||||
useChatRuntimeStore.getState().setActiveThreadId(null);
|
||||
|
||||
void (async () => {
|
||||
try {
|
||||
aui.threads().switchToNewThread();
|
||||
const { remoteId } = await aui.threadListItem().initialize();
|
||||
if (!cancelled) {
|
||||
useChatRuntimeStore.getState().setActiveThreadId(remoteId);
|
||||
}
|
||||
} catch (error) {
|
||||
if (!cancelled) {
|
||||
useChatRuntimeStore.getState().setActiveThreadId(null);
|
||||
}
|
||||
console.error("Failed to initialize new chat thread", error);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [aui, isLoading, nonce]);
|
||||
|
||||
return null;
|
||||
|
|
@ -733,12 +733,14 @@ export function ChatRuntimeProvider({
|
|||
pairId,
|
||||
initialThreadId,
|
||||
newThreadNonce,
|
||||
syncActiveThreadId = true,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
modelType?: ModelType;
|
||||
pairId?: string;
|
||||
initialThreadId?: string;
|
||||
newThreadNonce?: string;
|
||||
syncActiveThreadId?: boolean;
|
||||
}): ReactElement {
|
||||
const runtime = useRemoteThreadListRuntime({
|
||||
runtimeHook: useRuntimeHook,
|
||||
|
|
@ -754,8 +756,15 @@ export function ChatRuntimeProvider({
|
|||
|
||||
return (
|
||||
<AssistantRuntimeProvider runtime={runtime} aui={aui}>
|
||||
<ActiveThreadSync enabled={modelType === "base" && !pairId && !newThreadNonce} />
|
||||
{initialThreadId && <ThreadAutoSwitch threadId={initialThreadId} />}
|
||||
<ActiveThreadSync
|
||||
enabled={modelType === "base" && !pairId && !newThreadNonce && !initialThreadId}
|
||||
/>
|
||||
{initialThreadId && (
|
||||
<ThreadAutoSwitch
|
||||
threadId={initialThreadId}
|
||||
syncActiveThreadId={syncActiveThreadId}
|
||||
/>
|
||||
)}
|
||||
{!initialThreadId && newThreadNonce && (
|
||||
<ThreadNewChatSwitch nonce={newThreadNonce} />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {
|
|||
} from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { db, useLiveQuery } from "./db";
|
||||
import { useChatRuntimeStore } from "./stores/chat-runtime-store";
|
||||
import type { ChatView, ThreadRecord } from "./types";
|
||||
|
||||
interface SidebarItem {
|
||||
|
|
@ -76,12 +77,17 @@ export function ThreadSidebar({
|
|||
onNewCompare: () => void;
|
||||
showCompare: boolean;
|
||||
}) {
|
||||
const allThreads = useLiveQuery(
|
||||
() => db.threads.orderBy("createdAt").reverse().toArray(),
|
||||
[],
|
||||
);
|
||||
const allThreads = useLiveQuery(async () => {
|
||||
const threadIdsWithMessage = new Set(
|
||||
(await db.messages.orderBy("threadId").uniqueKeys()) as string[],
|
||||
);
|
||||
const rows = await db.threads.orderBy("createdAt").reverse().toArray();
|
||||
return rows.filter((t) => !t.archived && threadIdsWithMessage.has(t.id));
|
||||
}, []);
|
||||
const items = groupThreads(allThreads ?? []);
|
||||
const activeId = view.mode === "single" ? view.threadId : view.pairId;
|
||||
const storeThreadId = useChatRuntimeStore((s) => s.activeThreadId);
|
||||
const activeId =
|
||||
view.mode === "single" ? (view.threadId ?? storeThreadId) : view.pairId;
|
||||
|
||||
function viewForItem(item: SidebarItem): ChatView {
|
||||
return item.type === "single"
|
||||
|
|
@ -101,7 +107,11 @@ export function ThreadSidebar({
|
|||
}
|
||||
}
|
||||
if (activeId === item.id) {
|
||||
onSelect({ mode: "single" });
|
||||
// Directly set a new view with a nonce rather than going through
|
||||
// onNewThread(), which may return early if the guard sees no
|
||||
// threadId and no activeThreadId (after we just cleared it).
|
||||
useChatRuntimeStore.getState().setActiveThreadId(null);
|
||||
onSelect({ mode: "single", newThreadNonce: crypto.randomUUID() });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
__version__ = "2026.4.2"
|
||||
__version__ = "2026.4.4"
|
||||
|
||||
__all__ = [
|
||||
"SUPPORTS_BFLOAT16",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue