Files
LetsBeBiz-Site/src/lib/gemini-live.ts
Matt 1e41c1c07c
Some checks failed
Build & Push / build-and-push (push) Has been cancelled
fix: use predefined values for tool calls, improve completion flow
- System prompt now specifies exact predefined values for services/industries/timelines
- Agent instructed to call complete_brief IMMEDIATELY when name+email confirmed
- Chip labels fall back to raw value for unknown keys instead of showing i18n path
- Added completion transition logging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 14:49:56 +01:00

169 lines
8.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { 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é. Utilise UNIQUEMENT ces valeurs prédéfinies :
- services : "web", "systems", "infrastructure"
- aiTypes : "teammate", "customer-facing", "data-intelligence", "notsure"
- industry : "maritime", "hospitality", "technology", "realestate", "finance", "ngo", "other"
- timeline : "asap", "1-3months", "3-6months", "exploring"
Associe ce que l'utilisateur dit à la valeur prédéfinie la plus proche.
- Appelle analyze_website dès que l'utilisateur fournit une URL — puis intègre naturellement les résultats dans la discussion.
- Appelle complete_brief IMMÉDIATEMENT dès que le nom et l'e-mail sont confirmés. N'attends pas — appelle l'outil tout de suite. Dis quelque chose comme "Parfait, je génère votre brief maintenant".
- 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. Use ONLY these predefined values:
- services: "web", "systems", "infrastructure"
- aiTypes: "teammate", "customer-facing", "data-intelligence", "notsure"
- industry: "maritime", "hospitality", "technology", "realestate", "finance", "ngo", "other"
- timeline: "asap", "1-3months", "3-6months", "exploring"
Map what the user says to the closest predefined value.
- Call analyze_website as soon as the user provides a URL — then discuss the findings naturally.
- Call complete_brief IMMEDIATELY once you have confirmed the user's name and email. Do not wait — call the tool right away. Say something like "Great, I'm generating your brief now" while calling the tool.
- Keep responses concise: 23 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 function generateEphemeralToken(locale: string) {
const config = buildLiveConfig(locale);
return { config, model: GEMINI_LIVE_MODEL };
}