Skip to content

Commit 9517aae

Browse files
committed
Add support for Audio and Speech support for OpenRouter
1 parent 4ce201e commit 9517aae

9 files changed

Lines changed: 742 additions & 227 deletions

File tree

llms/llms.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,6 @@
385385
},
386386
"codestral": {
387387
"enabled": true,
388-
"id": "codestral",
389388
"npm": "codestral",
390389
"api": "https://codestral.mistral.ai/v1",
391390
"env": [
@@ -478,7 +477,6 @@
478477
},
479478
"ollama": {
480479
"enabled": false,
481-
"id": "ollama",
482480
"npm": "ollama",
483481
"api": "http://localhost:11434"
484482
},
@@ -630,7 +628,6 @@
630628
},
631629
"openrouter": {
632630
"enabled": true,
633-
"id": "openrouter",
634631
"server_tools": [
635632
"tool://openrouter/web_search",
636633
"tool://openrouter/datetime",

llms/main.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,24 +1336,7 @@ async def process_chat(self, chat, provider_id=None):
13361336
ret.pop("modalities", None)
13371337
return ret
13381338

1339-
async def chat(self, chat, context=None):
1340-
chat["model"] = self.provider_model(chat["model"]) or chat["model"]
1341-
1342-
modalities = chat.get("modalities") or []
1343-
if len(modalities) > 0:
1344-
for modality in modalities:
1345-
# use default implementation for text modalities
1346-
if modality == "text":
1347-
continue
1348-
modality_provider = self.modalities.get(modality)
1349-
if modality_provider:
1350-
return await modality_provider.chat(chat, self, context=context)
1351-
else:
1352-
raise Exception(f"Provider {self.name} does not support '{modality}' modality")
1353-
1354-
# with open(os.path.join(os.path.dirname(__file__), 'chat.wip.json'), "w") as f:
1355-
# f.write(json.dumps(chat, indent=2))
1356-
1339+
def init_chat(self, chat):
13571340
if self.frequency_penalty is not None:
13581341
chat["frequency_penalty"] = self.frequency_penalty
13591342
if self.max_completion_tokens is not None:
@@ -1389,7 +1372,28 @@ async def chat(self, chat, context=None):
13891372
if self.enable_thinking is not None:
13901373
chat["enable_thinking"] = self.enable_thinking
13911374

1375+
async def chat(self, chat, context=None):
1376+
chat["model"] = self.provider_model(chat["model"]) or chat["model"]
1377+
1378+
modalities = chat.get("modalities") or []
1379+
if len(modalities) > 0:
1380+
for modality in modalities:
1381+
# use default implementation for text modalities
1382+
if modality == "text":
1383+
continue
1384+
modality_provider = self.modalities.get(modality)
1385+
if modality_provider:
1386+
return await modality_provider.chat(chat, self, context=context)
1387+
else:
1388+
raise Exception(f"Provider {self.name} does not support '{modality}' modality")
1389+
1390+
# with open(os.path.join(os.path.dirname(__file__), 'chat.wip.json'), "w") as f:
1391+
# f.write(json.dumps(chat, indent=2))
1392+
1393+
self.init_chat(chat)
1394+
13921395
chat = await self.process_chat(chat, provider_id=self.id)
1396+
13931397
_log(f"POST {self.chat_url}")
13941398
_log(chat_summary(chat))
13951399
# remove metadata if any (conflicts with some providers, e.g. Z.ai)

llms/providers-extra.json

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,160 @@
8989
},
9090
"openrouter": {
9191
"models": {
92+
"microsoft/mai-voice-2": {
93+
"name": "MAI-Voice-2",
94+
"release_date": "2026-06-03",
95+
"modalities": {
96+
"input": [
97+
"text"
98+
],
99+
"output": [
100+
"speech"
101+
]
102+
},
103+
"cost": {
104+
"input": 0,
105+
"output": 22
106+
},
107+
"voices": [
108+
"en-US-Harper:MAI-Voice-2",
109+
"es-MX-Valeria:MAI-Voice-2",
110+
"fr-FR-Soleil:MAI-Voice-2",
111+
"de-DE-Klaus:MAI-Voice-2"
112+
],
113+
"defaults": {
114+
"voice": "en-US-Harper:MAI-Voice-2"
115+
}
116+
},
117+
"x-ai/grok-voice-tts-1.0": {
118+
"name": "Grok Voice TTS 1.0",
119+
"release_date": "2026-05-15",
120+
"modalities": {
121+
"input": [
122+
"text"
123+
],
124+
"output": [
125+
"speech"
126+
]
127+
},
128+
"cost": {
129+
"input": 0,
130+
"output": 15
131+
},
132+
"voices": [
133+
"eve",
134+
"ara",
135+
"rex",
136+
"sal",
137+
"leo"
138+
],
139+
"defaults": {
140+
"voice": "eve"
141+
}
142+
},
143+
"google/gemini-3.1-flash-tts-preview": {
144+
"name": "Gemini 3.1 Flash TTS",
145+
"release_date": "2026-04-24",
146+
"modalities": {
147+
"input": [
148+
"text"
149+
],
150+
"output": [
151+
"speech"
152+
]
153+
},
154+
"cost": {
155+
"input": 1,
156+
"output": 22
157+
},
158+
"voices": [
159+
"Zephyr",
160+
"Puck",
161+
"Charon",
162+
"Kore",
163+
"Fenrir",
164+
"Leda",
165+
"Orus",
166+
"Aoede",
167+
"Callirrhoe",
168+
"Autonoe",
169+
"Enceladus",
170+
"Iapetus",
171+
"Umbriel",
172+
"Algieba",
173+
"Despina",
174+
"Erinome",
175+
"Algenib",
176+
"Rasalgethi",
177+
"Laomedeia",
178+
"Achernar",
179+
"Alnilam",
180+
"Schedar",
181+
"Gacrux",
182+
"Pulcherrima",
183+
"Achird",
184+
"Zubenelgenubi",
185+
"Vindemiatrix",
186+
"Sadachbia",
187+
"Sadaltager",
188+
"Sulafat"
189+
],
190+
"defaults": {
191+
"voice": "Kore",
192+
"response_format": "pcm"
193+
}
194+
},
195+
"mistralai/voxtral-mini-tts-2603": {
196+
"name": "Voxtral Mini TTS",
197+
"release_date": "2026-03-26",
198+
"modalities": {
199+
"input": [
200+
"text"
201+
],
202+
"output": [
203+
"speech"
204+
]
205+
},
206+
"cost": {
207+
"input": 0.05,
208+
"output": 0.20
209+
},
210+
"voices": [
211+
"en_paul_sad",
212+
"en_paul_neutral",
213+
"en_paul_happy",
214+
"en_paul_frustrated",
215+
"en_paul_excited",
216+
"en_paul_confident",
217+
"en_paul_cheerful",
218+
"en_paul_angry",
219+
"gb_oliver_neutral",
220+
"gb_oliver_sad",
221+
"gb_oliver_excited",
222+
"gb_oliver_curious",
223+
"gb_oliver_confident",
224+
"gb_oliver_cheerful",
225+
"gb_oliver_angry",
226+
"gb_jane_sarcasm",
227+
"gb_jane_confused",
228+
"gb_jane_shameful",
229+
"gb_jane_sad",
230+
"gb_jane_neutral",
231+
"gb_jane_jealousy",
232+
"gb_jane_frustrated",
233+
"gb_jane_curious",
234+
"gb_jane_confident",
235+
"fr_marie_sad",
236+
"fr_marie_neutral",
237+
"fr_marie_happy",
238+
"fr_marie_excited",
239+
"fr_marie_curious",
240+
"fr_marie_angry"
241+
],
242+
"defaults": {
243+
"voice": "en_paul_cheerful"
244+
}
245+
},
92246
"recraft/recraft-v4.1-pro-vector": {
93247
"name": "Recraft V4.1 Pro Vector",
94248
"release_date": "2026-05-14",
@@ -383,6 +537,40 @@
383537
"input": 14.64,
384538
"output": 14.64
385539
}
540+
},
541+
"google/lyria-3-clip-preview": {
542+
"name": "Lyria 3 Clip Preview",
543+
"release_date": "2026-05-14",
544+
"modalities": {
545+
"input": [
546+
"text"
547+
],
548+
"output": [
549+
"audio"
550+
]
551+
},
552+
"cost": {
553+
"input": 0.04,
554+
"output": 0.0,
555+
"type": "request"
556+
}
557+
},
558+
"google/lyria-3-pro-preview": {
559+
"name": "Lyria 3 Pro Preview",
560+
"release_date": "2026-05-14",
561+
"modalities": {
562+
"input": [
563+
"text"
564+
],
565+
"output": [
566+
"audio"
567+
]
568+
},
569+
"cost": {
570+
"input": 0.08,
571+
"output": 0.0,
572+
"type": "request"
573+
}
386574
}
387575
}
388576
},

0 commit comments

Comments
 (0)