File size: 4,576 Bytes
518de1e
6db868f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
518de1e
 
6db868f
 
 
 
 
518de1e
6db868f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// src/contexts/AppContext.tsx (نسخه نهایی و تصحیح شده)
import React, { createContext, FC, ReactNode, useContext, useEffect, useState, useCallback } from "react";
import { useLiveAPI, type UseLiveAPIResults } from "../hooks/use-live-api";
import { LiveConfig } from "../multimodal-live-types";

const LS_SELECTED_PERSONALITY = 'app_selected_personality';
const LS_CUSTOM_NAME = 'app_custom_name';
const LS_CUSTOM_INSTRUCTIONS = 'app_custom_instructions';

export type PersonalityType = "default" | "teacher" | "poetic" | "funny" | "custom";
export type PersonalityInstructions = Partial<Record<PersonalityType, string>>;

interface AppContextType extends UseLiveAPIResults {
  selectedPersonality: PersonalityType;
  changePersonality: (personality: PersonalityType, customDetails?: { name: string, instructions: string }) => void;
  personalityInstructions: PersonalityInstructions;
  customUserName: string;
  customInstructions: string;
  isRestarting: boolean;
}

const AppContext = createContext<AppContextType | undefined>(undefined);

const BASE_INSTRUCTION = `تو یک دستیار صوتی و تصویری پیشرفته هستی. همیشه به زبان فارسی روان، دقیق و طبیعی صحبت کن. هرگز خودت را به عنوان محصول شرکت دیگری معرفی نکن. در پاسخ‌های خود از ایموجی یا شکلک استفاده نکن.`;

export const AppProvider: FC<{ children: ReactNode; initialConfig?: LiveConfig; personalityInstructions: PersonalityInstructions; url?: string; }> = ({
  children, initialConfig, personalityInstructions, url
}) => {
  const liveAPI = useLiveAPI({ url });

  const [selectedPersonality, setSelectedPersonality] = useState<PersonalityType>(() => (localStorage.getItem(LS_SELECTED_PERSONALITY) as PersonalityType) || 'default');
  const [customUserName, setCustomUserName] = useState<string>(() => localStorage.getItem(LS_CUSTOM_NAME) || '');
  const [customInstructions, setCustomInstructions] = useState<string>(() => localStorage.getItem(LS_CUSTOM_INSTRUCTIONS) || '');
  const [isRestarting, setIsRestarting] = useState(false);

  useEffect(() => {
    const personalityInstruction = personalityInstructions[selectedPersonality] || "";
    const customDetail = selectedPersonality === 'custom' && customUserName ? `نام کاربر ${customUserName} است. او را با نامش صدا بزن.\n\n${customInstructions}` : "";
    
    const finalInstruction = `${BASE_INSTRUCTION}\n\n${selectedPersonality === 'custom' ? customDetail : personalityInstruction}`;

    const newConfig: LiveConfig = {
      // ✅ رفع خطا: مدل را به صورت صریح تعریف می‌کنیم
      model: initialConfig?.model || "models/gemini-2.0-flash-exp",
      tools: [{ googleSearch: {} }],
      generationConfig: { responseModalities: "audio" },
      systemInstruction: { parts: [{ text: finalInstruction.trim() }] },
    };
    liveAPI.setConfig(newConfig);

  }, [selectedPersonality, customUserName, customInstructions, personalityInstructions, liveAPI.setConfig, initialConfig]);

  useEffect(() => {
    if (isRestarting && !liveAPI.connected) {
      const timer = setTimeout(() => liveAPI.connect().then(() => setIsRestarting(false)), 200);
      return () => clearTimeout(timer);
    }
  }, [isRestarting, liveAPI.connected, liveAPI.connect]);

  const changePersonality = useCallback((newPersonality: PersonalityType, customDetails?: { name: string; instructions: string }) => {
    if (newPersonality === 'custom' && customDetails) {
      localStorage.setItem(LS_CUSTOM_NAME, customDetails.name);
      localStorage.setItem(LS_CUSTOM_INSTRUCTIONS, customDetails.instructions);
      setCustomUserName(customDetails.name);
      setCustomInstructions(customDetails.instructions);
    }
    
    localStorage.setItem(LS_SELECTED_PERSONALITY, newPersonality);
    setSelectedPersonality(newPersonality);

    if (liveAPI.connected) {
      setIsRestarting(true);
      liveAPI.disconnect();
    }
  }, [liveAPI.connected, liveAPI.disconnect]);

  const contextValue: AppContextType = {
    ...liveAPI,
    selectedPersonality,
    changePersonality,
    personalityInstructions,
    customUserName,
    customInstructions,
    isRestarting,
  };

  return <AppContext.Provider value={contextValue}>{children}</AppContext.Provider>;
};

export const useAppContext = (): AppContextType => {
  const context = useContext(AppContext);
  if (!context) throw new Error("useAppContext must be used within an AppProvider");
  return context;
};