Some checks failed
Build & Push / build-and-push (push) Has been cancelled
- 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>
169 lines
8.2 KiB
TypeScript
169 lines
8.2 KiB
TypeScript
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: 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 function generateEphemeralToken(locale: string) {
|
||
const config = buildLiveConfig(locale);
|
||
return { config, model: GEMINI_LIVE_MODEL };
|
||
}
|