DEV Community

Cover image for Lucy AI: Intelligence Meets Elegance
Rajguru Yadav
Rajguru Yadav

Posted on

Lucy AI: Intelligence Meets Elegance

Hey dev,

Imagine having a personal AI assistant like Iron Man’s Jarvis — one that can answer questions, control your computer, manage reminders, play music, and even tell jokes. In this post, I’ll show you how to build Lucy AI, a modular, real-world Jarvis prototype, with working code examples.

Project Structure

lucy-ai/
├─ server.js             # Node.js backend
├─ public/
│  ├─ index.html         # UI frontend
│  ├─ main.js            # Frontend JS for chat & voice
│  └─ style.css          # Dark-glass UI styling
├─ modules/
│  ├─ multiLLM.js        # Multi-LLM reasoning (ChatGPT, Gemini, Grok, DeepSeek, OpenRouter)
│  ├─ speech.js          # Voice recognition & TTS
│  └─ fun.js             # Fun modules (jokes, trivia)
├─ package.json
└─ .env                  # API keys

Enter fullscreen mode Exit fullscreen mode

server.js (Backend)

import express from "express";
import dotenv from "dotenv";
import { askAI } from "./modules/multiLLM.js";
import { tellJoke } from "./modules/fun.js";

dotenv.config();
const app = express();
app.use(express.json());
app.use(express.static("public"));

// AI reasoning endpoint
app.post("/ask", async (req, res) => {
    const { question, provider } = req.body;
    const answer = await askAI(question, provider || "auto");
    res.json({ answer });
});

// Fun endpoint
app.get("/joke", async (req, res) => {
    const joke = await tellJoke();
    res.json({ joke });
});

app.listen(3000, () => console.log("Lucy AI running at http://localhost:3000"));

Enter fullscreen mode Exit fullscreen mode

modules/multiLLM.js

import OpenAI from "openai";
import axios from "axios";
import dotenv from "dotenv";
dotenv.config();

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

// Main Multi-LLM function
export async function askAI(question, provider = "auto") {
    let answer;
    try {
        switch(provider) {
            case "chatgpt": answer = await chatGPT(question); break;
            case "gemini": answer = await gemini(question); break;
            case "grok": answer = await grok(question); break;
            case "deepseek": answer = await deepSeek(question); break;
            case "openrouter": answer = await openRouter(question); break;
            case "auto":
            default:
                answer = await chatGPT(question)
                    .catch(() => gemini(question))
                    .catch(() => grok(question))
                    .catch(() => deepSeek(question))
                    .catch(() => openRouter(question));
        }
    } catch (err) {
        console.error("All LLM providers failed:", err);
        answer = "Sorry, I couldn't fetch an answer right now.";
    }
    return answer;
}

// Providers

async function chatGPT(question) {
    const res = await openai.chat.completions.create({
        model: "gpt-4o-mini",
        messages: [{ role: "user", content: question }],
    });
    return res.choices[0].message.content;
}

async function gemini(question) {
    const res = await axios.post("https://api.gemini.com/v1/query", {
        prompt: question,
        key: process.env.GEMINI_API_KEY,
    });
    return res.data.answer;
}

async function grok(question) {
    const res = await axios.post("https://api.grok.ai/query", {
        prompt: question,
        api_key: process.env.GROK_API_KEY,
    });
    return res.data.response;
}

async function deepSeek(question) {
    const res = await axios.post("https://api.deepseek.ai/ask", {
        query: question,
        token: process.env.DEEPSEEK_API_KEY
    });
    return res.data.answer;
}

async function openRouter(question) {
    const res = await axios.post("https://openrouter.ai/api/v1/chat/completions", {
        model: "gpt-4o-mini",
        messages: [{ role: "user", content: question }]
    }, {
        headers: { "Authorization": `Bearer ${process.env.OPENROUTER_API_KEY}` }
    });
    return res.data.choices[0].message.content;
}

Enter fullscreen mode Exit fullscreen mode

modules/speech.js

// Voice recognition and TTS
export function speak(text) {
    const utterance = new SpeechSynthesisUtterance(text);
    utterance.lang = "en-US";
    utterance.rate = 0.95;
    speechSynthesis.speak(utterance);
}

export function listen(callback) {
    const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
    recognition.lang = "en-US";
    recognition.onresult = (e) => callback(e.results[0][0].transcript);
    recognition.start();
}

Enter fullscreen mode Exit fullscreen mode

modules/fun.js

export async function tellJoke() {
    const res = await fetch("https://official-joke-api.appspot.com/random_joke");
    const joke = await res.json();
    return `${joke.setup} ... ${joke.punchline}`;
}

Enter fullscreen mode Exit fullscreen mode

public/index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lucy AI - Your Personal Assistant</title>
<link rel="stylesheet" href="style.css">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="app-container">
    <header class="app-header">
        <h1>Lucy AI 🤖</h1>
        <p>Your intelligent personal assistant</p>
    </header>

    <main class="chat-container">
        <div id="messages" class="messages"></div>
        <div class="input-container">
            <input type="text" id="query" placeholder="Ask Lucy something..." />
            <button id="askBtn">Send</button>
            <button id="voiceBtn" title="Talk to Lucy">🎙️</button>
            <button id="jokeBtn" title="Ask Lucy for a joke">😂</button>
        </div>
    </main>

    <footer class="app-footer">
        <small>Lucy AI &copy; 2025 | Developed by YourName</small>
    </footer>
</div>

<script src="main.js"></script>
</body>
</html>

Enter fullscreen mode Exit fullscreen mode

public/style.css (Dark-Glass UI)

/* Global Styles */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Roboto', sans-serif;
    background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
    color: #fff;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}

/* App Container */
.app-container {
    width: 100%;
    max-width: 500px;
    height: 90vh;
    background: rgba(255, 255, 255, 0.05);
    backdrop-filter: blur(15px);
    border-radius: 20px;
    box-shadow: 0 0 40px rgba(0, 255, 255, 0.3);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border: 1px solid rgba(255,255,255,0.1);
}

/* Header */
.app-header {
    text-align: center;
    padding: 15px 20px;
    border-bottom: 1px solid rgba(255,255,255,0.1);
}

.app-header h1 {
    font-size: 1.8rem;
    color: #00f0ff;
}

.app-header p {
    font-size: 0.9rem;
    color: #aaa;
    margin-top: 5px;
}

/* Chat Container */
.chat-container {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding: 10px 15px;
}

.messages {
    flex: 1;
    overflow-y: auto;
    padding-right: 5px;
}

.messages div {
    margin-bottom: 12px;
    max-width: 80%;
    padding: 10px 15px;
    border-radius: 15px;
    animation: fadeIn 0.3s ease-in;
}

.messages .user {
    background: rgba(0, 255, 255, 0.2);
    align-self: flex-end;
    text-align: right;
}

.messages .lucy {
    background: rgba(0, 255, 255, 0.1);
    align-self: flex-start;
    text-align: left;
}

/* Input Container */
.input-container {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
}

#query {
    flex: 1;
    padding: 10px 15px;
    border-radius: 25px;
    border: none;
    background: rgba(255,255,255,0.1);
    color: #fff;
    outline: none;
    font-size: 0.95rem;
}

.input-container button {
    padding: 10px 15px;
    border-radius: 50%;
    border: none;
    cursor: pointer;
    background: #00f0ff;
    color: #000;
    font-weight: bold;
    transition: all 0.2s ease;
}

.input-container button:hover {
    background: #00c0cc;
    transform: scale(1.1);
}

/* Footer */
.app-footer {
    text-align: center;
    padding: 8px;
    font-size: 0.75rem;
    color: #888;
    border-top: 1px solid rgba(255,255,255,0.1);
}

/* Animations */
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(10px);}
    to { opacity: 1; transform: translateY(0);}
}

/* Scrollbar Styling */
.messages::-webkit-scrollbar {
    width: 6px;
}

.messages::-webkit-scrollbar-thumb {
    background: rgba(0,255,255,0.3);
    border-radius: 3px;
}

Enter fullscreen mode Exit fullscreen mode

public/main.js

import { speak, listen } from "../modules/speech.js";

const messagesDiv = document.getElementById("messages");
const queryInput = document.getElementById("query");

document.getElementById("askBtn").addEventListener("click", askLucy);
document.getElementById("voiceBtn").addEventListener("click", () => {
    listen((text) => {
        queryInput.value = text;
        askLucy();
    });
});

document.getElementById("jokeBtn").addEventListener("click", async () => {
    const res = await fetch("/joke");
    const data = await res.json();
    addMessage("Lucy", data.joke);
    speak(data.joke);
});

async function askLucy() {
    const question = queryInput.value;
    if(!question) return;
    addMessage("You", question);
    queryInput.value = "";

    const res = await fetch("/ask", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ question })
    });
    const data = await res.json();
    addMessage("Lucy", data.answer);
    speak(data.answer);
}

function addMessage(sender, text) {
    const msg = document.createElement("div");
    msg.innerHTML = `<b>${sender}:</b> ${text}`;
    messagesDiv.appendChild(msg);
    messagesDiv.scrollTop = messagesDiv.scrollHeight;
}

Enter fullscreen mode Exit fullscreen mode

✅ Features of this Full Starter Kit#

  • Multi-LLM Reasoning: ChatGPT, Gemini, Grok, DeepSeek, OpenRouter

  • Interactive UI: Dark-glass theme with chatbox and buttons

  • Voice Interaction: Speech-to-text and TTS

  • Fun Module: Random jokes

  • Modular Structure: Easily expandable for IoT, reminders, calendar, or multimedia

This is ready to run:
1) Add your API keys to .env
2) Run:
node server.js
3) Open **

- Raj Guru Yadav

Top comments (0)