diff --git a/README.md b/README.md index 22e7ca2..37062a9 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ # 👾 ElatoAI: Realtime Speech AI Agents for ESP32 -Realtime AI Speech powered by SoTA AI voice models like **OpenAI Realtime API**, **Eleven Labs AI Agents**, **Gemini Live API**, **Hume AI EVI-4**, on ESP32, with Secure WebSockets, and Deno Edge Functions for >15-minute uninterrupted conversations globally. +Realtime AI Speech powered by SoTA AI voice models like **OpenAI Realtime API**, **Eleven Labs AI Agents**, **Gemini Live API**, **Hume AI EVI-4**, **xAI's Grok Voice Agent API** on ESP32, with Secure WebSockets, and Deno Edge Functions for >15-minute uninterrupted conversations globally.
|WebSocket| Edge[Deno Edge Function] Edge -->|OpenAI API| OpenAI[OpenAI Realtime API] Edge -->|Gemini API| Gemini[Gemini Live API] + Edge -->|xAI API| xAI[xAI Grok Voice Agent API] Edge -->|ElevenLabs API| ElevenLabs[ElevenLabs AI Agents] Edge -->|Hume API| Hume[Hume AI EVI4] OpenAI --> Edge Gemini --> Edge + xAI --> Edge ElevenLabs --> Edge Hume --> Edge Edge -->|WebSocket| ESP32 @@ -308,6 +312,8 @@ lib_deps = 5. ~~Plug in Eleven Labs API for voice generation~~ 6. Add Azure OpenAI Support (easy pickens) 7. Add Cartesia Support (easy pickens) +8. Add Amazon Nova Support +9. Add Deepgram We welcome contributions - Fork this repository. diff --git a/assets/grok.svg b/assets/grok.svg new file mode 100644 index 0000000..f917942 --- /dev/null +++ b/assets/grok.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/firmware-arduino/src/Config.cpp b/firmware-arduino/src/Config.cpp index 75b69d0..fc6ca44 100644 --- a/firmware-arduino/src/Config.cpp +++ b/firmware-arduino/src/Config.cpp @@ -33,11 +33,11 @@ volatile bool sleepRequested = false; */ #ifdef DEV_MODE -const char *ws_server = "192.168.1.37"; +const char *ws_server = "172.20.10.2"; const uint16_t ws_port = 8000; const char *ws_path = "/"; // Backend server details -const char *backend_server = "192.168.1.37"; +const char *backend_server = "172.20.10.2"; const uint16_t backend_port = 3000; #elif defined(PROD_MODE) diff --git a/frontend-nextjs/app/components/CreateCharacter/BuildDashboard.tsx b/frontend-nextjs/app/components/CreateCharacter/BuildDashboard.tsx index 01082a0..9494dea 100644 --- a/frontend-nextjs/app/components/CreateCharacter/BuildDashboard.tsx +++ b/frontend-nextjs/app/components/CreateCharacter/BuildDashboard.tsx @@ -13,7 +13,7 @@ import { v4 as uuidv4 } from 'uuid'; import { toast } from "@/components/ui/use-toast"; import { useRouter } from "next/navigation"; import { z } from "zod"; -import { emotionOptions, geminiVoices, openaiVoices, r2UrlAudio } from "@/lib/data"; +import { emotionOptions, geminiVoices, grokVoices, openaiVoices, r2UrlAudio } from "@/lib/data"; import EmojiComponent from "./EmojiComponent"; import { PitchFactors } from "@/lib/utils"; import { Slider } from "@/components/ui/slider"; @@ -26,7 +26,7 @@ interface SettingsDashboardProps { } const formSchema = z.object({ - provider: z.enum(["openai", "gemini"]), + provider: z.enum(["openai", "gemini", "grok"]), title: z.string().min(2, "Minimum 2 characters").max(50, "Maximum 50 characters"), description: z.string().min(50, "Minimum 50 characters").max(200, "Maximum 200 characters"), prompt: z.string().min(100, "Minimum 100 characters").max(1000, "Maximum 1000 characters"), @@ -66,6 +66,7 @@ const SettingsDashboard: React.FC = ({ const [touchedFields, setTouchedFields] = useState>({}); const [formErrors, setFormErrors] = useState>>({}); const [previewingVoice, setPreviewingVoice] = useState(null); + const [expandedProvider, setExpandedProvider] = useState("openai"); const handleBlur = (field: keyof FormData | 'features') => { // Mark the field as touched @@ -249,6 +250,19 @@ const SettingsDashboard: React.FC = ({ } } + const getProviderBadge = (provider: ModelProvider) => { + if (provider === "openai") { + return { label: "OpenAI", className: "bg-emerald-500 text-white" }; + } + if (provider === "gemini") { + return { label: "Gemini", className: "bg-purple-500 text-white" }; + } + if (provider === "grok") { + return { label: "Grok", className: "bg-slate-900 text-white" }; + } + return { label: provider, className: "bg-gray-600 text-white" }; + }; + const Heading = () => { return (
@@ -273,60 +287,98 @@ const SettingsDashboard: React.FC = ({

- Click a voice to preview how it sounds. + Choose from a list of voices and model providers to create your AI character.

-
-
- {[...openaiVoices, ...geminiVoices].map((voice: VoiceType) => ( -
{ - setFormData(prev => ({ +
+ {([ + { provider: "openai" as ModelProvider, label: "OpenAI" }, + { provider: "gemini" as ModelProvider, label: "Gemini" }, + { provider: "grok" as ModelProvider, label: "Grok" }, + ]).map((p) => ( + + ))}
+ + {expandedProvider && ( +
+
+ {(expandedProvider === "openai" ? openaiVoices : expandedProvider === "gemini" ? geminiVoices : grokVoices).map((voice: VoiceType) => ( +
{ + setFormData(prev => ({ + ...prev, + provider: voice.provider as ModelProvider, + voice: voice.id + })); + previewVoice(voice); + }} + > +
+
+
+ +
+
+ {voice.name} + {voice.description} +
+ {getProviderBadge(voice.provider as ModelProvider).label} +
+
+
+ {previewingVoice === voice.id && ( +
+
+ +
+
+ )} + {formData.voice === voice.id && ( +
+
+ +
+
+ )} +
+
+ ))} +
+
+ )}
{/* ElevenLabs Alternative */} diff --git a/frontend-nextjs/app/components/LandingPage/ProductsSection.tsx b/frontend-nextjs/app/components/LandingPage/ProductsSection.tsx index 1598346..b9dcd8e 100644 --- a/frontend-nextjs/app/components/LandingPage/ProductsSection.tsx +++ b/frontend-nextjs/app/components/LandingPage/ProductsSection.tsx @@ -54,7 +54,7 @@ export default function ProductsSection() {

- Our Product + Our Products

Everything you need to bring conversational AI to your world diff --git a/frontend-nextjs/app/components/LandingPage/YoutubeDemo.tsx b/frontend-nextjs/app/components/LandingPage/YoutubeDemo.tsx index a562643..bb06e16 100644 --- a/frontend-nextjs/app/components/LandingPage/YoutubeDemo.tsx +++ b/frontend-nextjs/app/components/LandingPage/YoutubeDemo.tsx @@ -4,15 +4,16 @@ import React from "react"; interface YoutubeDemoProps { caption: string; + youtubeId: string; } -export default function YoutubeDemo({ caption }: YoutubeDemoProps) { +export default function YoutubeDemo({ caption, youtubeId }: YoutubeDemoProps) { return