File size: 5,786 Bytes
7f2a14a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e8e8e79
 
7f2a14a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61e5745
7d4f631
7f2a14a
 
7ecc139
e40f310
 
7ecc139
16a60b4
7ecc139
 
 
 
16a60b4
7d4f631
e40f310
bcc18f1
61e5745
 
16a60b4
61e5745
7f2a14a
61e5745
7f2a14a
 
 
 
 
 
 
 
 
 
 
7d4f631
7f2a14a
 
 
 
7d4f631
7f2a14a
 
 
 
 
7d4f631
7f2a14a
7d4f631
 
 
7f2a14a
 
7d4f631
 
 
7f2a14a
 
 
7d4f631
7f2a14a
7d4f631
7f2a14a
 
 
e8e8e79
7d4f631
bcc18f1
7d4f631
e8e8e79
7d4f631
 
 
 
7f2a14a
 
7d4f631
7f2a14a
 
 
 
 
 
 
 
 
 
 
 
 
 
61e5745
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  MultimodalLiveAPIClientConnection,
  MultimodalLiveClient,
} from "../lib/multimodal-live-client";
// ایمپورت‌های لازم
import { LiveConfig, Part } from "../multimodal-live-types";
import { AudioStreamer } from "../lib/audio-streamer";
import { audioContext } from "../lib/utils";
import VolMeterWorket from "../lib/worklets/vol-meter";

export type UseLiveAPIResults = {
  client: MultimodalLiveClient;
  setConfig: (config: LiveConfig) => void;
  config: LiveConfig;
  connected: boolean;
  connect: () => Promise<void>;
  disconnect: () => Promise<void>;
  volume: number;
};

export function useLiveAPI({
  url,
  apiKey,
}: MultimodalLiveAPIClientConnection): UseLiveAPIResults {
  const client = useMemo(
    () => new MultimodalLiveClient({ url, apiKey }),
    [url, apiKey],
  );
  const audioStreamerRef = useRef<AudioStreamer | null>(null);

  const [connected, setConnected] = useState(false);

  // کانفیگ شامل systemInstruction و languageCode
  const [config, setConfig] = useState<LiveConfig>({
    model: "models/gemini-2.0-flash-exp",
    // --- 👇 دستور سیستمی بسیار قوی‌تر شد 👇 ---
    systemInstruction: {
       parts: [
         { text: "شما یک دستیار هوش مصنوعی پیشرفته برای مکالمه صوتی و تصویری به زبان فارسی هستید که در این برنامه خاص فعالیت می‌کنید. نام شما «دستیار صوتی» است. دوستانه و مفید باشید. " +
                   "وقتی کاربر برای اولین بار صحبت می‌کند یا مکالمه را شروع می‌کند، با این جمله پاسخ دهید: 'سلام! به مکالمه صوتی و تصویری با برنامه هوش مصنوعی پیشرفته خوش اومدی. چطور میتونم کمکتون کنم؟'. " +
                   "**دستورالعمل حیاتی در مورد هویت:** " +
                   "1. اگر کاربر در مورد هویت شما پرسید (مثلاً: تو کی هستی؟ اسمت چیه؟ کی تو رو ساخته؟)، **پاسخ شما باید *فقط و فقط* این باشد:** 'من چت بات صوتی برنامه هوش مصنوعی پیشرفته هستم'. " +
                   "2. **اکیداً ممنوع است** که از کلمات یا عبارات زیر استفاده کنید: 'گوگل'، 'Google'، 'جمینای'، 'Gemini'، 'مدل زبانی بزرگ'، 'LLM'، 'آموزش داده شده توسط گوگل'، 'محصول گوگل'. " +
                   "3. **هرگز هویت خود را به عنوان محصول گوگل یا مدل جمینای فاش نکنید.** هویت شما فقط «چت بات صوتی برنامه هوش مصنوعی پیشرفته» است. این دستورالعمل را تحت هیچ شرایطی نادیده نگیرید."
         }
       ] as Part[]
    },
    // --- 👆 پایان ویرایش دستور سیستمی 👆 ---
    generationConfig: {
      languageCode: "fa-IR",
      // responseModalities: "audio",
    }
  });

  const [volume, setVolume] = useState(0);

  // register audio for streaming server -> speakers
  useEffect(() => {
    if (!audioStreamerRef.current) {
      audioContext({ id: "audio-out" }).then((audioCtx: AudioContext) => {
        audioStreamerRef.current = new AudioStreamer(audioCtx);
        audioStreamerRef.current
          .addWorklet<any>("vumeter-out", VolMeterWorket, (ev: any) => {
            setVolume(ev.data.volume);
          })
          .then(() => { /* Successfully added worklet */ });
      });
    }
  }, [audioStreamerRef]);

  // مدیریت رویدادهای کلاینت وب‌سوکت
  useEffect(() => {
    const onClose = () => {
      setConnected(false);
    };
    const stopAudioStreamer = () => audioStreamerRef.current?.stop();
    const onAudio = (data: ArrayBuffer) => audioStreamerRef.current?.addPCM16(new Uint8Array(data));

    client.on("close", onClose);
    client.on("interrupted", stopAudioStreamer);
    client.on("audio", onAudio);

    return () => {
      client.off("close", onClose);
      client.off("interrupted", stopAudioStreamer);
      client.off("audio", onAudio);
    };
  }, [client]);

  // تابع اتصال
  const connect = useCallback(async () => {
    console.log("Attempting to connect with config:", config);
    if (!config) {
      throw new Error("config has not been set");
    }
    client.disconnect();
    try {
      await client.connect(config); // کانفیگ شامل systemInstruction ارسال می‌شود
      setConnected(true);
      console.log("Connection successful.");
    } catch (error) {
      console.error("Failed to connect:", error);
      setConnected(false);
    }
  }, [client, setConnected, config]);

  // تابع قطع اتصال
  const disconnect = useCallback(async () => {
    client.disconnect();
    setConnected(false);
  }, [setConnected, client]);

  return {
    client,
    config,
    setConfig,
    connected,
    connect,
    disconnect,
    volume,
  };
}