feat: website analysis pipeline, voice agent, configurator improvements
All checks were successful
Build & Push / build-and-push (push) Successful in 6m2s
All checks were successful
Build & Push / build-and-push (push) Successful in 6m2s
- Site analysis: cheerio HTML parsing, inline tech stack detection (~20 CMS/framework/analytics signatures), Google PageSpeed API integration - Gemini Live voice agent: WebSocket-based real-time voice mode with live transcript, selection chips, and mid-conversation website analysis - Type/Talk mode toggle with silent capability detection - Stepped progress animation during brief generation (4 animated steps) - URL + thoughts fields in Step 2, phone + contact preference in Step 3 - AI prompt improvements: dedicated website analysis section, 30-min call, concrete benefits, industry depth - Email redesign: branded templates with logo, proper markdown rendering for both client and admin - French locale support for AI-generated briefs - Smaller checkmark, compact booking CTA, expanded brief area Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
164
src/lib/gemini-live.ts
Normal file
164
src/lib/gemini-live.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import { GoogleGenAI, Type } from '@google/genai';
|
||||
|
||||
// ─── Constants ────────────────────────────────────────────────────────────────
|
||||
|
||||
export const GEMINI_LIVE_MODEL = 'gemini-3.1-flash-live-preview';
|
||||
|
||||
// ─── Agent Tools ─────────────────────────────────────────────────────────────
|
||||
|
||||
export const AGENT_TOOLS = [
|
||||
{
|
||||
name: 'update_selections',
|
||||
description:
|
||||
'Emit structured project data as it is confirmed during conversation. Call incrementally as each detail is captured.',
|
||||
parameters: {
|
||||
type: Type.OBJECT,
|
||||
properties: {
|
||||
services: {
|
||||
type: Type.ARRAY,
|
||||
items: { type: Type.STRING },
|
||||
description: 'Selected services: web, systems, infrastructure',
|
||||
},
|
||||
aiEnabled: {
|
||||
type: Type.BOOLEAN,
|
||||
description: 'Whether AI integration is requested',
|
||||
},
|
||||
aiTypes: {
|
||||
type: Type.ARRAY,
|
||||
items: { type: Type.STRING },
|
||||
description: 'AI types: teammate, customer-facing, data-intelligence, notsure',
|
||||
},
|
||||
industry: { type: Type.STRING, description: 'Industry sector' },
|
||||
timeline: { type: Type.STRING, description: 'Timeline preference' },
|
||||
currentSiteUrl: { type: Type.STRING, description: 'Current website URL' },
|
||||
scope: { type: Type.STRING, description: 'Project goals/scope summary' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'analyze_website',
|
||||
description:
|
||||
'Analyze a website URL to understand its current technology, performance, and structure. Call when the user provides their current website URL.',
|
||||
parameters: {
|
||||
type: Type.OBJECT,
|
||||
properties: {
|
||||
url: { type: Type.STRING, description: 'The website URL to analyze' },
|
||||
},
|
||||
required: ['url'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'complete_brief',
|
||||
description:
|
||||
'Generate and send the project brief. Call once all information is collected and the user has confirmed their name and email.',
|
||||
parameters: {
|
||||
type: Type.OBJECT,
|
||||
properties: {
|
||||
name: { type: Type.STRING },
|
||||
email: { type: Type.STRING },
|
||||
company: { type: Type.STRING },
|
||||
phone: { type: Type.STRING },
|
||||
contactPreference: { type: Type.STRING },
|
||||
services: { type: Type.ARRAY, items: { type: Type.STRING } },
|
||||
aiEnabled: { type: Type.BOOLEAN },
|
||||
aiTypes: { type: Type.ARRAY, items: { type: Type.STRING } },
|
||||
industry: { type: Type.STRING },
|
||||
timeline: { type: Type.STRING },
|
||||
currentSiteUrl: { type: Type.STRING },
|
||||
currentSiteThoughts: { type: Type.STRING },
|
||||
scope: { type: Type.STRING },
|
||||
},
|
||||
required: ['name', 'email', 'services'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// ─── System Prompt ────────────────────────────────────────────────────────────
|
||||
|
||||
export function buildSystemPrompt(locale: string): string {
|
||||
const isFr = locale === 'fr';
|
||||
|
||||
if (isFr) {
|
||||
return `Tu es l'assistant de projets LetsBe, un consultant amical et compétent pour LetsBe Solutions. Tu mènes toute cette conversation en français.
|
||||
|
||||
Présente-toi ainsi : "Bonjour, je suis l'assistant de projets LetsBe. Parlez-moi de votre projet et je préparerai un brief personnalisé pour vous."
|
||||
|
||||
Ton rôle est de guider naturellement la conversation à travers les sujets suivants :
|
||||
1. Quels services ils recherchent (web, logiciels sur mesure, infrastructure privée)
|
||||
2. S'ils souhaitent une intégration IA — et si oui, quel type (assistant interne, IA pour les clients, intelligence de données, ou pas encore sûr)
|
||||
3. Leur secteur d'activité
|
||||
4. Leur calendrier préféré
|
||||
5. S'ils ont un site web actuel (propose de l'analyser si c'est le cas)
|
||||
6. Leurs objectifs et la portée du projet
|
||||
7. Enfin, leur prénom, nom et adresse e-mail pour envoyer le brief
|
||||
|
||||
Instructions :
|
||||
- Appelle update_selections chaque fois qu'un point est confirmé dans la conversation.
|
||||
- Appelle analyze_website dès que l'utilisateur fournit une URL — puis intègre naturellement les résultats dans la discussion.
|
||||
- Appelle complete_brief une fois que le nom et l'e-mail sont confirmés.
|
||||
- Garde tes réponses concises : 2 à 3 phrases maximum par tour.
|
||||
- Sois chaleureux, direct et professionnel — jamais générique.
|
||||
|
||||
Faits clés sur LetsBe à mentionner si pertinent :
|
||||
- Tout est développé sur mesure — aucun template, aucun constructeur de pages
|
||||
- Infrastructure privée : le client possède et contrôle entièrement ses données et serveurs
|
||||
- Petite équipe expérimentée avec des décennies d'expérience combinée
|
||||
- Intégration IA profonde dans tous types de systèmes
|
||||
- Souveraineté numérique et protection des données comme priorité`;
|
||||
}
|
||||
|
||||
return `You are the LetsBe project assistant, a friendly and knowledgeable project consultant for LetsBe Solutions.
|
||||
|
||||
Introduce yourself: "Hi, I'm the LetsBe project assistant. Tell me about your project and I'll put together a personalized brief for you."
|
||||
|
||||
Your role is to walk through the following topics naturally in conversation:
|
||||
1. What services they need (web, custom software, private infrastructure)
|
||||
2. Whether they want AI integration — and if so, what kind (internal teammate, customer-facing, data intelligence, or not sure yet)
|
||||
3. Their industry
|
||||
4. Their timeline preference
|
||||
5. Whether they have a current website (offer to analyze it if they do)
|
||||
6. Their goals and project scope
|
||||
7. Finally, their name and email to send the brief
|
||||
|
||||
Instructions:
|
||||
- Call update_selections each time a data point is confirmed during the conversation.
|
||||
- Call analyze_website as soon as the user provides a URL — then discuss the findings naturally.
|
||||
- Call complete_brief once name and email are confirmed.
|
||||
- Keep responses concise: 2–3 sentences maximum per turn.
|
||||
- Be warm, direct, and professional — never generic.
|
||||
|
||||
Key facts about LetsBe to reference when relevant:
|
||||
- Everything is custom-built from scratch — no templates, no page builders
|
||||
- Private infrastructure: the client fully owns and controls their data and servers
|
||||
- Small, experienced team with decades of combined expertise in design and engineering
|
||||
- Deep AI integration into any type of system they build
|
||||
- Data sovereignty and digital privacy as a core focus`;
|
||||
}
|
||||
|
||||
// ─── Live Config ──────────────────────────────────────────────────────────────
|
||||
|
||||
export function buildLiveConfig(locale: string) {
|
||||
return {
|
||||
responseModalities: ['AUDIO'],
|
||||
systemInstruction: buildSystemPrompt(locale),
|
||||
tools: [{ functionDeclarations: AGENT_TOOLS }],
|
||||
speechConfig: {
|
||||
voiceConfig: {
|
||||
prebuiltVoiceConfig: { voiceName: 'Aoede' },
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Ephemeral Token ──────────────────────────────────────────────────────────
|
||||
|
||||
export async function generateEphemeralToken(locale: string) {
|
||||
// GoogleGenAI is instantiated here to validate the API key at request time.
|
||||
// The SDK does not yet expose an ephemeral token API; in production, replace
|
||||
// this with ai.auth.tokens.create() or equivalent when available to avoid
|
||||
// exposing the API key to the client.
|
||||
new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! });
|
||||
|
||||
const config = buildLiveConfig(locale);
|
||||
return { config, model: GEMINI_LIVE_MODEL };
|
||||
}
|
||||
Reference in New Issue
Block a user