Views
No views yet
1from mlx_lm import load, generate
2
3model, tokenizer = load("pherber3/Qwen3-Omni-30B-A3B-Instruct-4bit-mlx")
4
5messages = [{"role": "user", "content": "What is machine learning?"}]
6prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
7
8response = generate(model, tokenizer, prompt=prompt, max_tokens=100)
9print(response)1from openai import OpenAI
2client = OpenAI(
3 base_url="http://localhost:10240/v1",
4 api_key="not-needed"
5)
6
7response = client.chat.completions.create(
8 model="pherber3/Qwen3-Omni-30B-A3B-Instruct-4bit-mlx",
9 messages=[{"role": "user", "content": "Hello!"}]
10)
11print(response.choices[0].message.content)1mlx_lm.generate --model pherber3/Qwen3-Omni-30B-A3B-Instruct-4bit-mlx \
2 --prompt "Explain quantum computing"1import torch
2import mlx.core as mx
3from mlx_lm import load
4from mlx_lm.generate import generate_step
5from mlx_lm.sample_utils import make_sampler
6from transformers import Qwen3OmniMoeProcessor, AutoConfig
7from qwen_omni_utils import process_mm_info
8import numpy as np
9
10from transformers.models.qwen3_omni_moe.modeling_qwen3_omni_moe import Qwen3OmniMoeAudioEncoder
11from huggingface_hub import snapshot_download
12from openai import OpenAI
13import glob
14import os
15from safetensors import safe_open
16
17MLX_MODEL_PATH = "pherber3/Qwen3-Omni-30B-A3B-Instruct-4bit-mlx"
18HF_MODEL_PATH = "Qwen/Qwen3-Omni-30B-A3B-Instruct"
19
20# Make a dummy audio file for testing
21input_str = """
22The sky above the port was the color of television, tuned to a dead channel.
23"It's not like I'm using," Case heard someone say, as he shouldered his way
24through the crowd around the door of the Chat. "It's like my body's developed
25this massive drug deficiency." It was a Sprawl voice and a Sprawl joke.
26"""
27
28client = OpenAI(
29 base_url="http://localhost:10240/v1",
30 api_key="not-needed"
31)
32response = client.audio.speech.create(
33 model="mlx-community/Kokoro-82M-4bit",
34 voice="af_sky",
35 input=input_str,
36)
37response.stream_to_file("neuro_output.wav")
38
39### Process audio → embeddings → MLX generation (so begins the jank) ###
40
41print("Loading processor...")
42processor = Qwen3OmniMoeProcessor.from_pretrained(HF_MODEL_PATH)
43
44print("Loading audio_tower...")
45
46config = AutoConfig.from_pretrained(HF_MODEL_PATH, trust_remote_code=True)
47audio_config = config.thinker_config.audio_config
48audio_tower = Qwen3OmniMoeAudioEncoder(audio_config)
49audio_tower.eval()
50
51# Load audio_tower weights
52model_path = snapshot_download(HF_MODEL_PATH, allow_patterns=["*.safetensors", "*.json"])
53safetensor_files = sorted(glob.glob(os.path.join(model_path, "*.safetensors")))
54audio_tower_weights = {}
55for st_file in safetensor_files:
56 with safe_open(st_file, framework="pt") as f:
57 for key in f.keys():
58 if key.startswith("thinker.audio_tower."):
59 new_key = key.replace("thinker.audio_tower.", "")
60 audio_tower_weights[new_key] = f.get_tensor(key)
61audio_tower.load_state_dict(audio_tower_weights, strict=False)
62
63print("Loading MLX language model...")
64model, tokenizer = load(MLX_MODEL_PATH)
65
66# Function to process audio and generate response
67def understand_audio(audio_path, question):
68 """Process audio file and answer questions about it"""
69
70 # Prepare conversation
71 conversation = [{"role": "user", "content": [
72 {"type": "audio", "audio": audio_path},
73 {"type": "text", "text": question}
74 ]}]
75
76 # Process inputs
77 text_prompt = processor.apply_chat_template(conversation, add_generation_prompt=True, tokenize=False)
78 audios, images, videos = process_mm_info(conversation, use_audio_in_video=False)
79 inputs = processor(text=text_prompt, audio=audios, images=images, videos=videos,
80 return_tensors="pt", padding=True, use_audio_in_video=False)
81
82 # Process audio through audio_tower
83 with torch.no_grad():
84 audio_features = inputs['input_features'].squeeze(0)
85 feature_lens = inputs['feature_attention_mask'].sum(dim=1)
86 audio_outputs = audio_tower(audio_features, feature_lens=feature_lens)
87 audio_embeddings = audio_outputs.last_hidden_state
88
89 # Merge text and audio embeddings
90 audio_token_id = config.thinker_config.audio_token_id
91 input_ids_np = inputs["input_ids"][0].numpy()
92 audio_positions = np.where(input_ids_np == audio_token_id)[0]
93
94 embed_layer = model.language_model.model.embed_tokens
95 all_embeddings = embed_layer(mx.array(input_ids_np))
96 audio_embeddings_mlx = mx.array(audio_embeddings.cpu().numpy())
97
98 # Replace audio tokens with audio embeddings
99 segments = []
100 last_pos = 0
101 for i, pos in enumerate(audio_positions):
102 pos = int(pos)
103 if pos > last_pos:
104 segments.append(all_embeddings[last_pos:pos])
105 segments.append(audio_embeddings_mlx[i:i+1])
106 last_pos = pos + 1
107 if last_pos < all_embeddings.shape[0]:
108 segments.append(all_embeddings[last_pos:])
109 merged_embeddings = mx.concatenate(segments, axis=0)
110
111 # Generate response
112 sampler = make_sampler(temp=0.7, top_p=0.9)
113 dummy_prompt = mx.zeros((merged_embeddings.shape[0],), dtype=mx.int32)
114
115 eos_token_id = tokenizer.eos_token_id
116 im_end_id = tokenizer.convert_tokens_to_ids("<|im_end|>")
117 stop_tokens = {eos_token_id, im_end_id}
118
119 tokens = []
120 for (token, _), n in zip(generate_step(prompt=dummy_prompt, model=model,
121 max_tokens=100, sampler=sampler,
122 input_embeddings=merged_embeddings),
123 range(100)):
124 token_id = token if isinstance(token, int) else token.item()
125 tokens.append(token_id)
126 if token_id in stop_tokens:
127 break
128
129 response = tokenizer.decode(tokens).replace("<|im_end|>", "").replace("<|endoftext|>", "").strip()
130 return response
131
132# Models will stay loaded and you can input new audio files and prompts here
133response = understand_audio("neuro_output.wav", "Summarize this audio clip.")
134print(response)| Property | Value |
|---|---|
| Base Model | Qwen3-Omni-30B-A3B-Instruct |
| Quantization | 4-bit (group_size=64, bits=4) |
| Framework | MLX / Apple Silicon |
| Components | Text-only (thinker component) |
qwen3_moe architecture that:input_embeddings parameter for hybrid multimodal use cases