Add Qwen vision loading and Metal inference
This commit is contained in:
1 parent
48c2f751b4
commit
e32954ac1b
30 files changed
+1687
-100
No files matched your search
+48
-24
@@ -14,8 +14,8 @@ pub(crate) fn download_managed_artifact(
|
||||
cancel: &AtomicBool,
|
||||
verified_bytes: &AtomicU64,
|
||||
) -> Result<DownloadOutcome, String> {
|
||||
if id == ManagedArtifactId::Qwen38FlashNext {
|
||||
return download_qwen(models_path, cancel, verified_bytes);
|
||||
if id.is_qwen() {
|
||||
return download_qwen(id, models_path, cancel, verified_bytes);
|
||||
}
|
||||
download_artifact_with_cancel(
|
||||
id.model(),
|
||||
@@ -32,8 +32,8 @@ pub(crate) fn validate_managed_artifact(
|
||||
cancel: &AtomicBool,
|
||||
verified_bytes: &AtomicU64,
|
||||
) -> Result<DownloadOutcome, String> {
|
||||
if id == ManagedArtifactId::Qwen38FlashNext {
|
||||
return validate_qwen(models_path, cancel, verified_bytes);
|
||||
if id.is_qwen() {
|
||||
return validate_qwen(id, models_path, cancel, verified_bytes);
|
||||
}
|
||||
let model = id.model();
|
||||
let artifact = id.artifact();
|
||||
@@ -73,8 +73,8 @@ pub(crate) fn delete_managed_artifact(
|
||||
id: ManagedArtifactId,
|
||||
models_path: &Path,
|
||||
) -> Result<(), String> {
|
||||
if id == ManagedArtifactId::Qwen38FlashNext {
|
||||
return delete_qwen(models_path);
|
||||
if id.is_qwen() {
|
||||
return delete_qwen(id, models_path);
|
||||
}
|
||||
let model = id.model();
|
||||
let artifact = id.artifact();
|
||||
@@ -93,6 +93,7 @@ pub(crate) fn delete_managed_artifact(
|
||||
}
|
||||
|
||||
fn download_qwen(
|
||||
id: ManagedArtifactId,
|
||||
models_path: &Path,
|
||||
cancel: &AtomicBool,
|
||||
verified_bytes: &AtomicU64,
|
||||
@@ -100,7 +101,7 @@ fn download_qwen(
|
||||
let root = qwen::root(models_path);
|
||||
fs::create_dir_all(&root).map_err(|error| format!("{}: {error}", root.display()))?;
|
||||
verified_bytes.store(0, Ordering::Relaxed);
|
||||
for artifact in qwen::artifacts() {
|
||||
for artifact in qwen::selected(id) {
|
||||
if cancel.load(Ordering::Relaxed) {
|
||||
return Ok(DownloadOutcome::Stopped);
|
||||
}
|
||||
@@ -145,26 +146,29 @@ fn download_qwen(
|
||||
fs::write(qwen::verification_path(&root, artifact), &artifact.sha256)
|
||||
.map_err(|error| error.to_string())?;
|
||||
}
|
||||
if let Err(error) = crate::engine::validate_qwen_artifacts(&root) {
|
||||
clear_qwen_markers(&root)?;
|
||||
if id == ManagedArtifactId::Qwen38FlashNext
|
||||
&& let Err(error) = crate::engine::validate_qwen_artifacts(&root)
|
||||
{
|
||||
clear_qwen_markers(id, &root)?;
|
||||
return Err(error);
|
||||
}
|
||||
Ok(DownloadOutcome::Complete)
|
||||
}
|
||||
|
||||
fn validate_qwen(
|
||||
id: ManagedArtifactId,
|
||||
models_path: &Path,
|
||||
cancel: &AtomicBool,
|
||||
verified_bytes: &AtomicU64,
|
||||
) -> Result<DownloadOutcome, String> {
|
||||
let root = qwen::root(models_path);
|
||||
verified_bytes.store(0, Ordering::Relaxed);
|
||||
for artifact in qwen::artifacts() {
|
||||
for artifact in qwen::selected(id) {
|
||||
if !qwen::path(&root, artifact).exists() && !qwen::partial_path(&root, artifact).exists() {
|
||||
return Err(format!("{} is not downloaded", artifact.path));
|
||||
}
|
||||
}
|
||||
for artifact in qwen::artifacts() {
|
||||
for artifact in qwen::selected(id) {
|
||||
let destination = qwen::path(&root, artifact);
|
||||
let partial = qwen::partial_path(&root, artifact);
|
||||
let (source, promote) = if destination.exists() {
|
||||
@@ -189,15 +193,17 @@ fn validate_qwen(
|
||||
fs::write(qwen::verification_path(&root, artifact), &artifact.sha256)
|
||||
.map_err(|error| error.to_string())?;
|
||||
}
|
||||
if let Err(error) = crate::engine::validate_qwen_artifacts(&root) {
|
||||
clear_qwen_markers(&root)?;
|
||||
if id == ManagedArtifactId::Qwen38FlashNext
|
||||
&& let Err(error) = crate::engine::validate_qwen_artifacts(&root)
|
||||
{
|
||||
clear_qwen_markers(id, &root)?;
|
||||
return Err(error);
|
||||
}
|
||||
Ok(DownloadOutcome::Complete)
|
||||
}
|
||||
|
||||
fn clear_qwen_markers(root: &Path) -> Result<(), String> {
|
||||
for artifact in qwen::artifacts() {
|
||||
fn clear_qwen_markers(id: ManagedArtifactId, root: &Path) -> Result<(), String> {
|
||||
for artifact in qwen::selected(id) {
|
||||
let path = qwen::verification_path(root, artifact);
|
||||
match fs::remove_file(&path) {
|
||||
Ok(()) => {}
|
||||
@@ -208,9 +214,9 @@ fn clear_qwen_markers(root: &Path) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_qwen(models_path: &Path) -> Result<(), String> {
|
||||
fn delete_qwen(id: ManagedArtifactId, models_path: &Path) -> Result<(), String> {
|
||||
let root = qwen::root(models_path);
|
||||
for artifact in qwen::artifacts() {
|
||||
for artifact in qwen::selected(id) {
|
||||
for path in [
|
||||
qwen::path(&root, artifact),
|
||||
qwen::partial_path(&root, artifact),
|
||||
@@ -225,7 +231,14 @@ fn delete_qwen(models_path: &Path) -> Result<(), String> {
|
||||
}
|
||||
match fs::remove_dir(&root) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),
|
||||
Err(error)
|
||||
if matches!(
|
||||
error.kind(),
|
||||
std::io::ErrorKind::NotFound | std::io::ErrorKind::DirectoryNotEmpty
|
||||
) =>
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => Err(format!("{}: {error}", root.display())),
|
||||
}
|
||||
}
|
||||
@@ -480,7 +493,7 @@ mod tests {
|
||||
);
|
||||
assert!(ModelChoice::from_id("unknown").is_none());
|
||||
assert_eq!(MODEL_CHOICES.len(), 5);
|
||||
assert_eq!(MANAGED_ARTIFACTS.len(), 7);
|
||||
assert_eq!(MANAGED_ARTIFACTS.len(), 8);
|
||||
assert_eq!(
|
||||
ModelChoice::from_id("qwen3.8-flash-next"),
|
||||
Some(ModelChoice::Qwen38FlashNext)
|
||||
@@ -600,20 +613,31 @@ mod tests {
|
||||
))
|
||||
.unwrap();
|
||||
assert!(
|
||||
validate_qwen(&models, &AtomicBool::new(false), &AtomicU64::new(0))
|
||||
.unwrap_err()
|
||||
.contains("mtp.safetensors is not downloaded")
|
||||
validate_qwen(
|
||||
ManagedArtifactId::Qwen38FlashNext,
|
||||
&models,
|
||||
&AtomicBool::new(false),
|
||||
&AtomicU64::new(0)
|
||||
)
|
||||
.unwrap_err()
|
||||
.contains("mtp.safetensors is not downloaded")
|
||||
);
|
||||
fs::hard_link(source.join("mtp.safetensors"), root.join("mtp.safetensors")).unwrap();
|
||||
let verified = AtomicU64::new(0);
|
||||
assert_eq!(
|
||||
validate_qwen(&models, &AtomicBool::new(false), &verified).unwrap(),
|
||||
validate_qwen(
|
||||
ManagedArtifactId::Qwen38FlashNext,
|
||||
&models,
|
||||
&AtomicBool::new(false),
|
||||
&verified
|
||||
)
|
||||
.unwrap(),
|
||||
DownloadOutcome::Complete
|
||||
);
|
||||
assert_eq!(verified.load(Ordering::Relaxed), qwen::total_bytes());
|
||||
assert!(qwen::is_installed(&root));
|
||||
assert!(crate::engine::validate_qwen_artifacts(&root).is_ok());
|
||||
delete_qwen(&models).unwrap();
|
||||
delete_qwen(ManagedArtifactId::Qwen38FlashNext, &models).unwrap();
|
||||
assert!(!root.exists());
|
||||
fs::remove_dir_all(models).unwrap();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user