mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-10-03 00:55:14 -07:00
Enhance release workflow and update provider settings
- Added macOS support for PyTorch CPU providers in the release workflow. - Updated the ProviderSettings component to handle macOS-specific conditions and improve UI interactions. - Refactored the radio group component styles for better accessibility and visual consistency. - Improved provider management logic to ensure proper handling of available providers across different platforms.
This commit is contained in:
@@ -28,13 +28,21 @@ jobs:
|
|||||||
provider: "pytorch-cuda"
|
provider: "pytorch-cuda"
|
||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
# PyTorch CPU provider (Linux)
|
# PyTorch CPU provider (Linux)
|
||||||
# - platform: "ubuntu-22.04"
|
- platform: "ubuntu-22.04"
|
||||||
# provider: "pytorch-cpu"
|
provider: "pytorch-cpu"
|
||||||
# python-version: "3.12"
|
python-version: "3.12"
|
||||||
# PyTorch CUDA provider (Linux) - large binary, uploaded to R2
|
# PyTorch CUDA provider (Linux) - large binary, uploaded to R2
|
||||||
- platform: "ubuntu-22.04"
|
- platform: "ubuntu-22.04"
|
||||||
provider: "pytorch-cuda"
|
provider: "pytorch-cuda"
|
||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
|
# PyTorch CPU provider (macOS Apple Silicon)
|
||||||
|
- platform: "macos-latest"
|
||||||
|
provider: "pytorch-cpu"
|
||||||
|
python-version: "3.12"
|
||||||
|
# PyTorch CPU provider (macOS Intel)
|
||||||
|
- platform: "macos-15-intel"
|
||||||
|
provider: "pytorch-cpu"
|
||||||
|
python-version: "3.12"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -101,6 +109,10 @@ jobs:
|
|||||||
# Add platform suffix for clarity
|
# Add platform suffix for clarity
|
||||||
if [ "${{ matrix.platform }}" == "windows-latest" ]; then
|
if [ "${{ matrix.platform }}" == "windows-latest" ]; then
|
||||||
UPLOAD_NAME="tts-provider-${{ matrix.provider }}-windows.exe"
|
UPLOAD_NAME="tts-provider-${{ matrix.provider }}-windows.exe"
|
||||||
|
elif [ "${{ matrix.platform }}" == "macos-latest" ]; then
|
||||||
|
UPLOAD_NAME="tts-provider-${{ matrix.provider }}-macos-arm64"
|
||||||
|
elif [ "${{ matrix.platform }}" == "macos-15-intel" ]; then
|
||||||
|
UPLOAD_NAME="tts-provider-${{ matrix.provider }}-macos-x64"
|
||||||
else
|
else
|
||||||
UPLOAD_NAME="tts-provider-${{ matrix.provider }}-linux"
|
UPLOAD_NAME="tts-provider-${{ matrix.provider }}-linux"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { Delete01Icon, Download01Icon, Loading01Icon } from '@hugeicons/core-free-icons';
|
||||||
import { HugeiconsIcon } from '@hugeicons/react';
|
import { HugeiconsIcon } from '@hugeicons/react';
|
||||||
import { Download01Icon, Loading01Icon, Delete01Icon } from '@hugeicons/core-free-icons';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
@@ -23,12 +23,18 @@ import { useModelDownloadToast } from '@/lib/hooks/useModelDownloadToast';
|
|||||||
|
|
||||||
const isMacOS = () => navigator.platform.toLowerCase().includes('mac');
|
const isMacOS = () => navigator.platform.toLowerCase().includes('mac');
|
||||||
|
|
||||||
type ProviderType = 'auto' | 'bundled-mlx' | 'bundled-pytorch' | 'pytorch-cpu' | 'pytorch-cuda' | 'remote' | 'openai';
|
type ProviderType =
|
||||||
|
| 'auto'
|
||||||
|
| 'bundled-mlx'
|
||||||
|
| 'bundled-pytorch'
|
||||||
|
| 'pytorch-cpu'
|
||||||
|
| 'pytorch-cuda'
|
||||||
|
| 'remote'
|
||||||
|
| 'openai';
|
||||||
|
|
||||||
export function ProviderSettings() {
|
export function ProviderSettings() {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [selectedProvider, setSelectedProvider] = useState<ProviderType>('auto');
|
|
||||||
const [downloadingProvider, setDownloadingProvider] = useState<string | null>(null);
|
const [downloadingProvider, setDownloadingProvider] = useState<string | null>(null);
|
||||||
|
|
||||||
const { data: providersData, isLoading } = useQuery({
|
const { data: providersData, isLoading } = useQuery({
|
||||||
@@ -167,6 +173,7 @@ export function ProviderSettings() {
|
|||||||
|
|
||||||
// Determine current active provider
|
// Determine current active provider
|
||||||
const currentProvider = activeProvider?.provider || 'auto';
|
const currentProvider = activeProvider?.provider || 'auto';
|
||||||
|
const selectedProvider = currentProvider as ProviderType;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -176,41 +183,25 @@ export function ProviderSettings() {
|
|||||||
<CardDescription>Choose how Voicebox generates speech</CardDescription>
|
<CardDescription>Choose how Voicebox generates speech</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<RadioGroup
|
<RadioGroup value={selectedProvider} onValueChange={(value) => handleStart(value)}>
|
||||||
value={selectedProvider}
|
|
||||||
onValueChange={(value) => setSelectedProvider(value as ProviderType)}
|
|
||||||
>
|
|
||||||
{/* Auto-detect */}
|
|
||||||
<div className="flex items-center space-x-2 py-2">
|
|
||||||
<RadioGroupItem value="auto" id="auto" />
|
|
||||||
<Label htmlFor="auto" className="flex-1 cursor-pointer">
|
|
||||||
<div className="font-medium">Auto-detect (Recommended)</div>
|
|
||||||
<div className="text-sm text-muted-foreground">
|
|
||||||
Automatically choose the best available provider
|
|
||||||
</div>
|
|
||||||
</Label>
|
|
||||||
{currentProvider === 'auto' && (
|
|
||||||
<Badge variant="outline" className="ml-2">
|
|
||||||
Active
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* PyTorch CUDA */}
|
{/* PyTorch CUDA */}
|
||||||
<div className="flex items-center justify-between py-2">
|
<div
|
||||||
<div className="flex items-center space-x-2 flex-1">
|
className={`flex items-center justify-between py-2 ${isMacOS() ? 'opacity-50' : ''}`}
|
||||||
<RadioGroupItem value="pytorch-cuda" id="cuda" />
|
>
|
||||||
<Label htmlFor="cuda" className="flex-1 cursor-pointer">
|
<div className="flex items-center space-x-3 flex-1">
|
||||||
|
<RadioGroupItem value="pytorch-cuda" id="cuda" disabled={isMacOS()} />
|
||||||
|
<Label
|
||||||
|
htmlFor="cuda"
|
||||||
|
className={`flex-1 ${isMacOS() ? 'cursor-not-allowed' : 'cursor-pointer'}`}
|
||||||
|
>
|
||||||
<div className="font-medium">PyTorch CUDA (NVIDIA GPU)</div>
|
<div className="font-medium">PyTorch CUDA (NVIDIA GPU)</div>
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-sm text-muted-foreground">
|
||||||
4-5x faster inference on NVIDIA GPUs
|
{isMacOS() ? 'Not available on macOS' : '4-5x faster inference on NVIDIA GPUs'}
|
||||||
</div>
|
</div>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{currentProvider === 'pytorch-cuda' && (
|
{currentProvider === 'pytorch-cuda' && <Badge variant="outline">Active</Badge>}
|
||||||
<Badge variant="outline">Active</Badge>
|
|
||||||
)}
|
|
||||||
{!installedProviders.includes('pytorch-cuda') && (
|
{!installedProviders.includes('pytorch-cuda') && (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleDownload('pytorch-cuda')}
|
onClick={() => handleDownload('pytorch-cuda')}
|
||||||
@@ -218,7 +209,11 @@ export function ProviderSettings() {
|
|||||||
disabled={downloadingProvider === 'pytorch-cuda'}
|
disabled={downloadingProvider === 'pytorch-cuda'}
|
||||||
>
|
>
|
||||||
{downloadingProvider === 'pytorch-cuda' ? (
|
{downloadingProvider === 'pytorch-cuda' ? (
|
||||||
<HugeiconsIcon icon={Loading01Icon} size={16} className="h-4 w-4 animate-spin" />
|
<HugeiconsIcon
|
||||||
|
icon={Loading01Icon}
|
||||||
|
size={16}
|
||||||
|
className="h-4 w-4 animate-spin"
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<HugeiconsIcon icon={Download01Icon} size={16} className="h-4 w-4 mr-1" />
|
<HugeiconsIcon icon={Download01Icon} size={16} className="h-4 w-4 mr-1" />
|
||||||
@@ -227,148 +222,109 @@ export function ProviderSettings() {
|
|||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{installedProviders.includes('pytorch-cuda') && selectedProvider !== 'pytorch-cuda' && (
|
{installedProviders.includes('pytorch-cuda') &&
|
||||||
<Button
|
selectedProvider !== 'pytorch-cuda' && (
|
||||||
onClick={() => handleStart('pytorch-cuda')}
|
<Button onClick={() => handleStart('pytorch-cuda')} size="sm" variant="outline">
|
||||||
size="sm"
|
Start
|
||||||
variant="outline"
|
</Button>
|
||||||
>
|
)}
|
||||||
Start
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{installedProviders.includes('pytorch-cuda') && (
|
{installedProviders.includes('pytorch-cuda') && (
|
||||||
<Button
|
<Button onClick={() => handleDelete('pytorch-cuda')} size="sm" variant="ghost">
|
||||||
onClick={() => handleDelete('pytorch-cuda')}
|
|
||||||
size="sm"
|
|
||||||
variant="ghost"
|
|
||||||
>
|
|
||||||
<HugeiconsIcon icon={Delete01Icon} size={16} className="h-4 w-4" />
|
<HugeiconsIcon icon={Delete01Icon} size={16} className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* PyTorch CPU (Windows/Linux only) */}
|
{/* PyTorch CPU */}
|
||||||
{!isMacOS() && (
|
<div className="flex items-center justify-between py-2">
|
||||||
<div className="flex items-center justify-between py-2">
|
<div className="flex items-center space-x-3 flex-1">
|
||||||
<div className="flex items-center space-x-2 flex-1">
|
<RadioGroupItem value="pytorch-cpu" id="cpu" />
|
||||||
<RadioGroupItem value="pytorch-cpu" id="cpu" />
|
<Label htmlFor="cpu" className="flex-1 cursor-pointer">
|
||||||
<Label htmlFor="cpu" className="flex-1 cursor-pointer">
|
<div className="font-medium">PyTorch CPU</div>
|
||||||
<div className="font-medium">PyTorch CPU</div>
|
<div className="text-sm text-muted-foreground">
|
||||||
<div className="text-sm text-muted-foreground">
|
Works on any system, slower inference
|
||||||
Works on any system, slower inference
|
</div>
|
||||||
</div>
|
</Label>
|
||||||
</Label>
|
</div>
|
||||||
</div>
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex items-center gap-2">
|
{currentProvider === 'pytorch-cpu' && <Badge variant="outline">Active</Badge>}
|
||||||
{currentProvider === 'pytorch-cpu' && (
|
{!installedProviders.includes('pytorch-cpu') && (
|
||||||
<Badge variant="outline">Active</Badge>
|
<Button
|
||||||
)}
|
onClick={() => handleDownload('pytorch-cpu')}
|
||||||
{!installedProviders.includes('pytorch-cpu') && (
|
size="sm"
|
||||||
<Button
|
disabled={downloadingProvider === 'pytorch-cpu'}
|
||||||
onClick={() => handleDownload('pytorch-cpu')}
|
>
|
||||||
size="sm"
|
{downloadingProvider === 'pytorch-cpu' ? (
|
||||||
disabled={downloadingProvider === 'pytorch-cpu'}
|
<HugeiconsIcon
|
||||||
>
|
icon={Loading01Icon}
|
||||||
{downloadingProvider === 'pytorch-cpu' ? (
|
size={16}
|
||||||
<HugeiconsIcon icon={Loading01Icon} size={16} className="h-4 w-4 animate-spin" />
|
className="h-4 w-4 animate-spin"
|
||||||
) : (
|
/>
|
||||||
<>
|
) : (
|
||||||
<HugeiconsIcon icon={Download01Icon} size={16} className="h-4 w-4 mr-1" />
|
<>
|
||||||
Download (300MB)
|
<HugeiconsIcon icon={Download01Icon} size={16} className="h-4 w-4 mr-1" />
|
||||||
</>
|
Download (300MB)
|
||||||
)}
|
</>
|
||||||
</Button>
|
)}
|
||||||
)}
|
</Button>
|
||||||
{installedProviders.includes('pytorch-cpu') && selectedProvider !== 'pytorch-cpu' && (
|
)}
|
||||||
<Button
|
{installedProviders.includes('pytorch-cpu') &&
|
||||||
onClick={() => handleStart('pytorch-cpu')}
|
selectedProvider !== 'pytorch-cpu' && (
|
||||||
size="sm"
|
<Button onClick={() => handleStart('pytorch-cpu')} size="sm" variant="outline">
|
||||||
variant="outline"
|
|
||||||
>
|
|
||||||
Start
|
Start
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{installedProviders.includes('pytorch-cpu') && (
|
{installedProviders.includes('pytorch-cpu') && (
|
||||||
<Button
|
<Button onClick={() => handleDelete('pytorch-cpu')} size="sm" variant="ghost">
|
||||||
onClick={() => handleDelete('pytorch-cpu')}
|
<HugeiconsIcon icon={Delete01Icon} size={16} className="h-4 w-4" />
|
||||||
size="sm"
|
</Button>
|
||||||
variant="ghost"
|
)}
|
||||||
>
|
|
||||||
<HugeiconsIcon icon={Delete01Icon} size={16} className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
|
||||||
{/* MLX bundled (macOS only) */}
|
{/* MLX bundled (macOS Apple Silicon only) */}
|
||||||
{isMacOS() && (
|
{isMacOS() && (
|
||||||
<div className="p-3 bg-muted rounded-md">
|
<div className="flex items-center space-x-3 py-2">
|
||||||
<div className="text-sm">
|
<RadioGroupItem value="bundled-mlx" id="mlx" />
|
||||||
<div className="font-medium flex items-center gap-2">
|
<Label htmlFor="mlx" className="flex-1 cursor-pointer">
|
||||||
MLX (Apple Silicon)
|
<div className="font-medium">MLX (Apple Silicon)</div>
|
||||||
{currentProvider === 'bundled-mlx' && (
|
<div className="text-sm text-muted-foreground">
|
||||||
<Badge variant="outline">Active</Badge>
|
Bundled with the app - optimized for M-series chips
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-muted-foreground mt-1">
|
</Label>
|
||||||
Bundled with the app - optimized for M1/M2/M3 chips
|
{currentProvider === 'bundled-mlx' && (
|
||||||
</div>
|
<Badge variant="outline" className="ml-2">
|
||||||
</div>
|
Active
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Remote */}
|
{/* Remote */}
|
||||||
<div className="space-y-2 py-2">
|
<div className="space-y-2 py-2 opacity-50">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-3">
|
||||||
<RadioGroupItem value="remote" id="remote" />
|
<RadioGroupItem value="remote" id="remote" disabled />
|
||||||
<Label htmlFor="remote" className="flex-1 cursor-pointer">
|
<Label htmlFor="remote" className="flex-1 cursor-not-allowed">
|
||||||
<div className="font-medium">Remote Server</div>
|
<div className="font-medium">Remote Server</div>
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-sm text-muted-foreground">
|
||||||
Connect to your own TTS server
|
Connect to your own TTS server (coming soon)
|
||||||
</div>
|
</div>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
{selectedProvider === 'remote' && (
|
|
||||||
<div className="ml-6">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="http://your-server:8000"
|
|
||||||
className="w-full px-3 py-2 border rounded-md"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
<div className="text-xs text-muted-foreground mt-1">
|
|
||||||
Remote provider support coming soon
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* OpenAI */}
|
{/* OpenAI */}
|
||||||
<div className="space-y-2 py-2">
|
<div className="space-y-2 py-2 opacity-50">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-3">
|
||||||
<RadioGroupItem value="openai" id="openai" />
|
<RadioGroupItem value="openai" id="openai" disabled />
|
||||||
<Label htmlFor="openai" className="flex-1 cursor-pointer">
|
<Label htmlFor="openai" className="flex-1 cursor-not-allowed">
|
||||||
<div className="font-medium">OpenAI API</div>
|
<div className="font-medium">OpenAI API</div>
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-sm text-muted-foreground">
|
||||||
Use OpenAI's TTS API (requires API key)
|
Use OpenAI's TTS API (coming soon)
|
||||||
</div>
|
</div>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
{selectedProvider === 'openai' && (
|
|
||||||
<div className="ml-6">
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
placeholder="sk-..."
|
|
||||||
className="w-full px-3 py-2 border rounded-md"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
<div className="text-xs text-muted-foreground mt-1">
|
|
||||||
OpenAI provider support coming soon
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@@ -385,7 +341,10 @@ export function ProviderSettings() {
|
|||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||||
<AlertDialogAction onClick={confirmDelete} className="bg-destructive text-destructive-foreground">
|
<AlertDialogAction
|
||||||
|
onClick={confirmDelete}
|
||||||
|
className="bg-destructive text-destructive-foreground"
|
||||||
|
>
|
||||||
Delete
|
Delete
|
||||||
</AlertDialogAction>
|
</AlertDialogAction>
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const RadioGroupItem = React.forwardRef<
|
|||||||
<RadioGroupPrimitive.Item
|
<RadioGroupPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
"aspect-square h-4 w-4 rounded-full border border-accent text-accent ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from .base import TTSProvider
|
|||||||
from .types import ProviderType
|
from .types import ProviderType
|
||||||
from .bundled import BundledProvider
|
from .bundled import BundledProvider
|
||||||
from .local import LocalProvider
|
from .local import LocalProvider
|
||||||
from .installer import get_provider_binary_path
|
from .installer import get_provider_binary_path, _get_providers_dir
|
||||||
from ..config import get_data_dir
|
from ..config import get_data_dir
|
||||||
import subprocess
|
import subprocess
|
||||||
import socket
|
import socket
|
||||||
@@ -50,38 +50,44 @@ class ProviderManager:
|
|||||||
Args:
|
Args:
|
||||||
provider_type: Type of provider to start
|
provider_type: Type of provider to start
|
||||||
"""
|
"""
|
||||||
if provider_type in ["bundled-mlx", "bundled-pytorch"]:
|
if provider_type == "bundled-mlx":
|
||||||
# Use bundled provider
|
# Use bundled MLX provider
|
||||||
self.active_provider = self._get_default_provider()
|
self.active_provider = self._get_default_provider()
|
||||||
elif provider_type in ["pytorch-cpu", "pytorch-cuda"]:
|
elif provider_type in ["pytorch-cpu", "pytorch-cuda"]:
|
||||||
# Start local provider subprocess
|
# Try to start external provider subprocess if binary exists
|
||||||
provider_path = get_provider_binary_path(provider_type)
|
provider_path = get_provider_binary_path(provider_type)
|
||||||
if not provider_path or not provider_path.exists():
|
if provider_path and provider_path.exists():
|
||||||
raise ValueError(f"Provider {provider_type} is not installed. Please download it first.")
|
# External downloaded provider exists, start it
|
||||||
|
# Find a free port
|
||||||
|
port = self._get_free_port()
|
||||||
|
|
||||||
# Find a free port
|
# Start provider subprocess
|
||||||
port = self._get_free_port()
|
from ..config import get_data_dir
|
||||||
|
process = subprocess.Popen(
|
||||||
|
[
|
||||||
|
str(provider_path),
|
||||||
|
"--port", str(port),
|
||||||
|
"--data-dir", str(get_data_dir()),
|
||||||
|
],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
|
||||||
# Start provider subprocess
|
# Wait for provider to be ready
|
||||||
from ..config import get_data_dir
|
base_url = f"http://127.0.0.1:{port}"
|
||||||
process = subprocess.Popen(
|
await self._wait_for_provider_health(base_url, timeout=30)
|
||||||
[
|
|
||||||
str(provider_path),
|
|
||||||
"--port", str(port),
|
|
||||||
"--data-dir", str(get_data_dir()),
|
|
||||||
],
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Wait for provider to be ready
|
# Create LocalProvider instance
|
||||||
base_url = f"http://127.0.0.1:{port}"
|
self.active_provider = LocalProvider(base_url)
|
||||||
await self._wait_for_provider_health(base_url, timeout=30)
|
self._provider_process = process
|
||||||
|
self._provider_port = port
|
||||||
# Create LocalProvider instance
|
else:
|
||||||
self.active_provider = LocalProvider(base_url)
|
# No external binary, use bundled provider (if available)
|
||||||
self._provider_process = process
|
if provider_type == "pytorch-cpu":
|
||||||
self._provider_port = port
|
# PyTorch CPU can use bundled backend
|
||||||
|
self.active_provider = self._get_default_provider()
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Provider {provider_type} is not installed. Please download it first.")
|
||||||
elif provider_type == "remote":
|
elif provider_type == "remote":
|
||||||
# Remote provider - will be implemented in Phase 5
|
# Remote provider - will be implemented in Phase 5
|
||||||
raise NotImplementedError("Remote provider not yet implemented")
|
raise NotImplementedError("Remote provider not yet implemented")
|
||||||
@@ -124,9 +130,15 @@ class ProviderManager:
|
|||||||
machine = platform.machine()
|
machine = platform.machine()
|
||||||
|
|
||||||
if system == "Darwin" and machine == "arm64":
|
if system == "Darwin" and machine == "arm64":
|
||||||
|
# Apple Silicon gets MLX
|
||||||
installed.append("bundled-mlx")
|
installed.append("bundled-mlx")
|
||||||
else:
|
|
||||||
installed.append("bundled-pytorch")
|
# PyTorch CPU is available on all platforms (check if bundled or downloaded)
|
||||||
|
# For now, assume it's bundled on macOS Intel, Windows, Linux
|
||||||
|
# Downloaded binaries will be detected below
|
||||||
|
if not (system == "Darwin" and machine == "arm64"):
|
||||||
|
# Non-Apple Silicon systems have PyTorch CPU bundled
|
||||||
|
installed.append("pytorch-cpu")
|
||||||
|
|
||||||
# Check for downloaded providers (Phase 2)
|
# Check for downloaded providers (Phase 2)
|
||||||
providers_dir = _get_providers_dir()
|
providers_dir = _get_providers_dir()
|
||||||
|
|||||||
@@ -62,6 +62,14 @@ def _get_provider_download_name(provider_type: str) -> str:
|
|||||||
elif system == "Linux":
|
elif system == "Linux":
|
||||||
platform_suffix = "linux"
|
platform_suffix = "linux"
|
||||||
ext = ""
|
ext = ""
|
||||||
|
elif system == "Darwin":
|
||||||
|
# Detect macOS architecture
|
||||||
|
machine = platform.machine()
|
||||||
|
if machine == "arm64":
|
||||||
|
platform_suffix = "macos-arm64"
|
||||||
|
else:
|
||||||
|
platform_suffix = "macos-x64"
|
||||||
|
ext = ""
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Provider downloads not supported on {system}")
|
raise ValueError(f"Provider downloads not supported on {system}")
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user