6import "jsr:@std/dotenv/load"; // needed for deno run; not req for smallweb or valtown
7
8// Function to handle audio transcription using Groq's Whisper API
9export const audioTranscriptionHandler = async (c) => {
10 console.log("🎤 Audio transcription request received");
20 }
21
22 // Get API key from environment variable
23 const apiKey = Deno.env.get("GROQ_API_KEY");
24 if (!apiKey) {
25 console.error("❌ Transcription error: Missing API key");
26 return c.json({ error: "API key not configured" }, 500);
27 }
28
38
39 // If the file doesn't have a proper name or type, add one
40 // This ensures the file has the right extension for the API
41 if (!audioFile.name || !audioFile.type.startsWith('audio/')) {
42 const newFile = new File(
50 }
51
52 // Prepare the form data for Groq API
53 const groqFormData = new FormData();
54
65 groqFormData.append("timestamp_granularities[]", "word");
66
67 // Call Groq API
68 console.log("🎤 Sending request to Groq Whisper API");
69 const start = Date.now();
70 const response = await fetch("https://api.groq.com/openai/v1/audio/transcriptions", {
71 method: "POST",
72 headers: {
73 "Authorization": `Bearer ${apiKey}`
74 },
75 body: groqFormData
76 });
77 const elapsed = Date.now() - start;
78 console.log(`🎤 Groq Whisper API response received in ${elapsed}ms, status: ${response.status}`);
79
80 // Get response content type
99 errorMessage = `Server error: ${response.status} ${response.statusText}`;
100 // Log the full response for debugging
101 console.error("❌ Transcription API error response:", {
102 status: response.status,
103 statusText: response.statusText,
108 }
109 } catch (parseError) {
110 console.error("❌ Error parsing Groq API response:", parseError);
111 errorMessage = "Failed to parse error response from server";
112 }
113
114 return c.json({
115 error: `Groq API error: ${errorMessage}`,
116 status: response.status
117 }, response.status);
150 console.log(`🔵 Last user message: "${messages.find(m => m.role === 'user')?.content?.substring(0, 50)}..."`);
151
152 const GROQ_API_KEY = Deno.env.get("GROQ_API_KEY");
153 if (!GROQ_API_KEY) {
154 console.error("❌ Missing GROQ_API_KEY environment variable");
155 return c.json({ error: "GROQ_API_KEY environment variable is not set" }, 500);
156 }
157
158 console.log("🔵 Sending request to Groq API");
159 const start = Date.now();
160 const response = await fetch("https://api.groq.com/openai/v1/chat/completions", {
161 method: "POST",
162 headers: {
163 "Content-Type": "application/json",
164 "Authorization": `Bearer ${GROQ_API_KEY}`
165 },
166 body: JSON.stringify({
171 });
172 const elapsed = Date.now() - start;
173 console.log(`🔵 Groq API response received in ${elapsed}ms, status: ${response.status}`);
174
175 if (!response.ok) {
176 const errorData = await response.json();
177 console.error("❌ Chat API error:", errorData);
178 return c.json({ error: "Failed to get chat completion", details: errorData }, response.status);
179 }
204 }
205
206 // Get API key from environment variable
207 const apiKey = Deno.env.get("GROQ_API_KEY");
208 if (!apiKey) {
209 console.error("❌ TTS error: Missing API key");
210 return c.json({ error: "API key not configured" }, 500);
211 }
212
213 // Call Groq Speech API
214 console.log("🔊 Sending request to Groq Speech API");
215 const start = Date.now();
216 const response = await fetch("https://api.groq.com/openai/v1/audio/speech", {
217 method: "POST",
218 headers: {
219 "Content-Type": "application/json",
220 "Authorization": `Bearer ${apiKey}`
221 },
222 body: JSON.stringify({
228 });
229 const elapsed = Date.now() - start;
230 console.log(`🔊 Groq Speech API response received in ${elapsed}ms, status: ${response.status}`);
231
232 if (!response.ok) {
235 const errorData = await response.json();
236 errorMessage = errorData.error?.message || JSON.stringify(errorData);
237 console.error("❌ TTS API error:", errorData);
238 } catch (e) {
239 // If response is not JSON
240 errorMessage = `Server error: ${response.status} ${response.statusText}`;
241 console.error("❌ TTS API non-JSON error:", errorMessage);
242 }
243
601 // Now immediately send this message to get AI response
602 try {
603 // Prepare messages for the API
604 const apiMessages = this.messages.map(({ role, content }) => ({ role, content }));
605
606 // Ensure first message is always the correct system message for current mode
607 if (apiMessages.length > 0 && apiMessages[0].role === 'system') {
608 const systemMessage = this.chatMode === 'concise'
609 ? 'You are a helpful assistant powered by the Llama-3.3-70b-versatile model. Keep your responses short, concise and conversational. Aim for 1-3 sentences when possible.'
610 : 'You are a helpful assistant powered by the Llama-3.3-70b-versatile model. Respond conversationally and accurately to the user.';
611
612 apiMessages[0].content = systemMessage;
613 }
614
616 method: 'POST',
617 headers: { 'Content-Type': 'application/json' },
618 body: JSON.stringify({ messages: apiMessages })
619 });
620
679 this.statusMessage = 'Thinking...';
680
681 // Prepare messages for the API (excluding UI-only properties)
682 const apiMessages = this.messages.map(({ role, content }) => ({ role, content }));
683
684 // Ensure first message is always the correct system message for current mode
685 if (apiMessages.length > 0 && apiMessages[0].role === 'system') {
686 const systemMessage = this.chatMode === 'concise'
687 ? 'You are a helpful assistant powered by the Llama-3.3-70b-versatile model. Keep your responses short, concise and conversational. Aim for 1-3 sentences when possible.'
688 : 'You are a helpful assistant powered by the Llama-3.3-70b-versatile model. Respond conversationally and accurately to the user.';
689
690 apiMessages[0].content = systemMessage;
691 }
692
695 method: 'POST',
696 headers: { 'Content-Type': 'application/json' },
697 body: JSON.stringify({ messages: apiMessages })
698 });
699
967
968 <p class="text-center text-sm text-gray-600 mt-4">
969 Powered by Llama-3.3-70b-versatile through Groq API. Audio transcription and speech synthesis provided by Groq. Text-to-speech provided through PlayHT. <a class="underline" href="https://console.groq.com/docs/speech-to-text" target="_blank" rel="noopener noreferrer">Documentation here</a>. <a class="underline" href="https://www.val.town/v/yawnxyz/groqAudioChat" target="_blank" rel="noopener noreferrer">Code here</a>
970 </p>
971 <div class="text-center text-sm text-gray-600 mt-4 w-full mx-auto">