mirror of
https://github.com/GoodStartLabs/AI_Diplomacy.git
synced 2026-05-01 17:45:26 +00:00
fixed logging and elevan labs!
This commit is contained in:
parent
476402a23e
commit
f00a037aed
7 changed files with 148 additions and 35 deletions
|
|
@ -1,10 +1,62 @@
|
|||
import { gameState } from "./gameState";
|
||||
|
||||
// --- ElevenLabs Text-to-Speech configuration ---
|
||||
const ELEVENLABS_API_KEY = import.meta.env.VITE_ELEVENLABS_API_KEY || "";
|
||||
let ELEVENLABS_API_KEY = '';
|
||||
|
||||
// Try to load from import.meta.env
|
||||
try {
|
||||
// First check if we have the Vite-specific variable
|
||||
if (import.meta.env.VITE_ELEVENLABS_API_KEY) {
|
||||
ELEVENLABS_API_KEY = String(import.meta.env.VITE_ELEVENLABS_API_KEY).trim();
|
||||
// Simplified logging
|
||||
}
|
||||
// Fallback to the direct env variable (for dev environments)
|
||||
else if (import.meta.env.ELEVENLABS_API_KEY) {
|
||||
ELEVENLABS_API_KEY = String(import.meta.env.ELEVENLABS_API_KEY).trim();
|
||||
}
|
||||
|
||||
|
||||
// Clean and validate the key
|
||||
if (ELEVENLABS_API_KEY) {
|
||||
// Remove any unexpected characters that might have been added
|
||||
ELEVENLABS_API_KEY = ELEVENLABS_API_KEY.replace(/[^a-zA-Z0-9_\-]/g, '');
|
||||
console.log(`ElevenLabs API key: ${ELEVENLABS_API_KEY ? 'Valid' : 'Invalid'} (${ELEVENLABS_API_KEY.length} chars)`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error loading API key:', err);
|
||||
}
|
||||
|
||||
const VOICE_ID = "onwK4e9ZLuTAKqWW03F9";
|
||||
const MODEL_ID = "eleven_multilingual_v2";
|
||||
|
||||
// Test the API key validity directly but don't log unless there's an issue
|
||||
testElevenLabsKey().catch(err => console.error("Key test failed:", err));
|
||||
|
||||
async function testElevenLabsKey() {
|
||||
if (!ELEVENLABS_API_KEY) {
|
||||
console.warn("Cannot test API key - none provided");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('https://api.elevenlabs.io/v1/voices', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'xi-api-key': ELEVENLABS_API_KEY
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
console.log("✅ ElevenLabs API key is valid and ready for TTS");
|
||||
} else {
|
||||
const errorText = await response.text().catch(() => 'No error details available');
|
||||
console.error(`❌ ElevenLabs API key invalid: ${response.status}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ ElevenLabs API connection error");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call ElevenLabs TTS to speak the summary out loud.
|
||||
* Returns a promise that resolves only after the audio finishes playing (or fails).
|
||||
|
|
@ -13,6 +65,27 @@ const MODEL_ID = "eleven_multilingual_v2";
|
|||
* @returns Promise that resolves when audio completes or rejects on error
|
||||
*/
|
||||
export async function speakSummary(summaryText: string): Promise<void> {
|
||||
if (!summaryText || summaryText.trim() === '') {
|
||||
console.warn("No summary text provided to speakSummary function");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the summary is in JSON format and extract the actual summary text
|
||||
let textToSpeak = summaryText;
|
||||
try {
|
||||
// Check if it starts with a JSON format indicator
|
||||
if (summaryText.trim().startsWith('{') && summaryText.includes('"summary"')) {
|
||||
const parsedSummary = JSON.parse(summaryText);
|
||||
if (parsedSummary.summary) {
|
||||
textToSpeak = parsedSummary.summary;
|
||||
// clean text, drop /n
|
||||
textToSpeak = textToSpeak.replace(/\n/g, ' ');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Failed to parse summary as JSON");
|
||||
}
|
||||
|
||||
if (!ELEVENLABS_API_KEY) {
|
||||
console.warn("No ElevenLabs API key found. Skipping TTS.");
|
||||
return;
|
||||
|
|
@ -23,29 +96,26 @@ export async function speakSummary(summaryText: string): Promise<void> {
|
|||
|
||||
try {
|
||||
// Truncate text to first 100 characters for ElevenLabs
|
||||
const truncatedText = summaryText.substring(0, 100);
|
||||
if (truncatedText.length < summaryText.length) {
|
||||
console.log(`TTS text truncated from ${summaryText.length} to 100 characters`);
|
||||
}
|
||||
|
||||
const truncatedText = textToSpeak.substring(0, 100);
|
||||
|
||||
// Hit ElevenLabs TTS endpoint with the truncated text
|
||||
const headers = {
|
||||
"xi-api-key": ELEVENLABS_API_KEY,
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "audio/mpeg"
|
||||
};
|
||||
|
||||
const response = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${VOICE_ID}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"xi-api-key": ELEVENLABS_API_KEY,
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "audio/mpeg"
|
||||
},
|
||||
headers: headers,
|
||||
body: JSON.stringify({
|
||||
text: truncatedText,
|
||||
model_id: MODEL_ID,
|
||||
// Optional fine-tuning parameters
|
||||
// voice_settings: { stability: 0.3, similarity_boost: 0.8 },
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`ElevenLabs TTS error: ${response.statusText}`);
|
||||
throw new Error(`ElevenLabs TTS error: ${response.status}`);
|
||||
}
|
||||
|
||||
// Convert response into a playable blob
|
||||
|
|
@ -62,7 +132,7 @@ export async function speakSummary(summaryText: string): Promise<void> {
|
|||
resolve();
|
||||
};
|
||||
}).catch(err => {
|
||||
console.error("Audio playback error", err);
|
||||
console.error("Audio playback error");
|
||||
// Make sure to clear the flag even if there's an error
|
||||
gameState.isSpeaking = false;
|
||||
reject(err);
|
||||
|
|
@ -70,7 +140,7 @@ export async function speakSummary(summaryText: string): Promise<void> {
|
|||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error("Failed to generate TTS from ElevenLabs:", err);
|
||||
console.error("Failed to generate TTS from ElevenLabs");
|
||||
// Make sure to clear the flag if there's any exception
|
||||
gameState.isSpeaking = false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue