تم تصميم وحدة تحكم OpenAI Realtime لتكون مفتشًا ومرجعًا تفاعليًا لواجهة برمجة التطبيقات (API) لواجهة برمجة تطبيقات OpenAI Realtime. يأتي مزودًا بمكتبتين مساعدتين، openai/openai-realtime-api-beta التي تعمل كعميل مرجعي (للمتصفح وNode.js) و /src/lib/wavtools
الذي يسمح بإدارة الصوت البسيطة في المتصفح.
هذا هو مشروع React تم إنشاؤه باستخدام create-react-app
الذي تم تجميعه عبر Webpack. قم بتثبيته عن طريق استخراج محتويات هذه الحزمة واستخدامه؛
$ npm i
ابدأ الخادم الخاص بك بـ:
$ npm start
يجب أن يكون متاحًا عبر localhost:3000
.
تتطلب وحدة التحكم مفتاح OpenAI API ( مفتاح المستخدم أو مفتاح المشروع ) الذي يتمتع بإمكانية الوصول إلى Realtime API. سيُطلب منك عند بدء التشغيل إدخاله. سيتم حفظه عبر localStorage
ويمكن تغييره في أي وقت من واجهة المستخدم.
لبدء جلسة ستحتاج إلى الاتصال . سيتطلب هذا الوصول إلى الميكروفون. يمكنك بعد ذلك الاختيار بين وضعي المحادثة اليدوي (الضغط والتحدث) و vad (اكتشاف النشاط الصوتي)، والتبديل بينهما في أي وقت.
هناك وظيفتان ممكّنتان؛
get_weather
: اسأل عن الطقس في أي مكان وسيبذل النموذج قصارى جهده لتحديد الموقع وإظهاره على الخريطة والحصول على الطقس لذلك الموقع. لاحظ أنه لا يمكنه الوصول إلى الموقع، ويتم "تخمين" الإحداثيات من بيانات تدريب النموذج، لذا قد لا تكون الدقة مثالية.set_memory
: يمكنك أن تطلب من النموذج أن يتذكر المعلومات لك، وسوف يقوم بتخزينها في JSON blob على اليسار.يمكنك مقاطعة النموذج بحرية في أي وقت في وضع الضغط والتحدث أو VAD.
إذا كنت ترغب في إنشاء تطبيق أكثر قوة والتعامل مع العميل المرجعي باستخدام خادمك الخاص، فقد قمنا بتضمين خادم Node.js Relay Server.
$ npm run relay
سيبدأ تلقائيًا على localhost:8081
.
ستحتاج إلى إنشاء ملف .env
بالتهيئة التالية:
OPENAI_API_KEY=YOUR_API_KEY
REACT_APP_LOCAL_RELAY_SERVER_URL=http://localhost:8081
ستحتاج إلى إعادة تشغيل كل من تطبيق React وخادم الترحيل لـ .env.
التغييرات لتصبح نافذة المفعول. يتم تحميل عنوان URL للخادم المحلي عبر ConsolePage.tsx
. للتوقف عن استخدام خادم الترحيل في أي وقت، ما عليك سوى حذف متغير البيئة أو تعيينه على سلسلة فارغة.
/**
* Running a local relay server will allow you to hide your API key
* and run custom logic on the server
*
* Set the local relay server address to:
* REACT_APP_LOCAL_RELAY_SERVER_URL=http://localhost:8081
*
* This will also require you to set OPENAI_API_KEY= in a `.env` file
* You can run it with `npm run relay`, in parallel with `npm start`
*/
const LOCAL_RELAY_SERVER_URL : string =
process . env . REACT_APP_LOCAL_RELAY_SERVER_URL || '' ;
هذا الخادم هو مجرد ترحيل رسائل بسيط ، ولكن يمكن توسيعه إلى:
instructions
) على الخادم مباشرةسيكون عليك تنفيذ هذه الميزات بنفسك.
يتوفر أحدث عميل مرجعي ووثائق على GitHub على openai/openai-realtime-api-beta.
يمكنك استخدام هذا العميل بنفسك في أي مشروع React (الواجهة الأمامية) أو Node.js. للحصول على التوثيق الكامل، ارجع إلى مستودع GitHub، ولكن يمكنك استخدام الدليل هنا كمقدمة للبدء.
import { RealtimeClient } from '/src/lib/realtime-api-beta/index.js' ;
const client = new RealtimeClient ( { apiKey : process . env . OPENAI_API_KEY } ) ;
// Can set parameters ahead of connecting
client . updateSession ( { instructions : 'You are a great, upbeat friend.' } ) ;
client . updateSession ( { voice : 'alloy' } ) ;
client . updateSession ( { turn_detection : 'server_vad' } ) ;
client . updateSession ( { input_audio_transcription : { model : 'whisper-1' } } ) ;
// Set up event handling
client . on ( 'conversation.updated' , ( { item , delta } ) => {
const items = client . conversation . getItems ( ) ; // can use this to render all items
/* includes all changes to conversations, delta may be populated */
} ) ;
// Connect to Realtime API
await client . connect ( ) ;
// Send an item and triggers a generation
client . sendUserMessageContent ( [ { type : 'text' , text : `How are you?` } ] ) ;
لإرسال دفق الصوت، استخدم الأسلوب .appendInputAudio()
. إذا كنت في وضع turn_detection: 'disabled'
، فستحتاج إلى استخدام .generate()
لإخبار النموذج بالاستجابة.
// Send user audio, must be Int16Array or ArrayBuffer
// Default audio format is pcm16 with sample rate of 24,000 Hz
// This populates 1s of noise in 0.1s chunks
for ( let i = 0 ; i < 10 ; i ++ ) {
const data = new Int16Array ( 2400 ) ;
for ( let n = 0 ; n < 2400 ; n ++ ) {
const value = Math . floor ( ( Math . random ( ) * 2 - 1 ) * 0x8000 ) ;
data [ n ] = value ;
}
client . appendInputAudio ( data ) ;
}
// Pending audio is committed and model is asked to generate
client . createResponse ( ) ;
العمل مع الأدوات سهل. ما عليك سوى الاتصال .addTool()
وتعيين رد اتصال كمعلمة ثانية. سيتم تنفيذ رد الاتصال باستخدام معلمات الأداة، وسيتم إرسال النتيجة تلقائيًا مرة أخرى إلى النموذج.
// We can add tools as well, with callbacks specified
client . addTool (
{
name : 'get_weather' ,
description :
'Retrieves the weather for a given lat, lng coordinate pair. Specify a label for the location.' ,
parameters : {
type : 'object' ,
properties : {
lat : {
type : 'number' ,
description : 'Latitude' ,
} ,
lng : {
type : 'number' ,
description : 'Longitude' ,
} ,
location : {
type : 'string' ,
description : 'Name of the location' ,
} ,
} ,
required : [ 'lat' , 'lng' , 'location' ] ,
} ,
} ,
async ( { lat , lng , location } ) => {
const result = await fetch (
`https://api.open-meteo.com/v1/forecast?latitude= ${ lat } &longitude= ${ lng } ¤t=temperature_2m,wind_speed_10m`
) ;
const json = await result . json ( ) ;
return json ;
}
) ;
قد ترغب في مقاطعة النموذج يدويًا، خاصة في وضع turn_detection: 'disabled'
. للقيام بذلك يمكننا استخدام:
// id is the id of the item currently being generated
// sampleCount is the number of audio samples that have been heard by the listener
client . cancelResponse ( id , sampleCount ) ;
ستؤدي هذه الطريقة إلى إيقاف إنشاء النموذج على الفور، ولكنها ستؤدي أيضًا إلى اقتطاع العنصر الذي يتم تشغيله عن طريق إزالة كل الصوت بعد sampleCount
ومسح الاستجابة النصية. باستخدام هذه الطريقة، يمكنك مقاطعة النموذج ومنعه من "تذكر" أي شيء قام بإنشائه يسبق حالة المستخدم.
هناك خمسة أحداث رئيسية للعميل لتدفق التحكم في التطبيق في RealtimeClient
. لاحظ أن هذه مجرد نظرة عامة على استخدام العميل، وأن مواصفات حدث Realtime API الكاملة أكبر بكثير، إذا كنت بحاجة إلى مزيد من التحكم، فراجع مستودع GitHub: openai/openai-realtime-api-beta.
// errors like connection failures
client . on ( 'error' , ( event ) => {
// do thing
} ) ;
// in VAD mode, the user starts speaking
// we can use this to stop audio playback of a previous response if necessary
client . on ( 'conversation.interrupted' , ( ) => {
/* do something */
} ) ;
// includes all changes to conversations
// delta may be populated
client . on ( 'conversation.updated' , ( { item , delta } ) => {
// get all items, e.g. if you need to update a chat window
const items = client . conversation . getItems ( ) ;
switch ( item . type ) {
case 'message' :
// system, user, or assistant message (item.role)
break ;
case 'function_call' :
// always a function call from the model
break ;
case 'function_call_output' :
// always a response from the user / application
break ;
}
if ( delta ) {
// Only one of the following will be populated for any given event
// delta.audio = Int16Array, audio added
// delta.transcript = string, transcript added
// delta.arguments = string, function arguments added
}
} ) ;
// only triggered after item added to conversation
client . on ( 'conversation.item.appended' , ( { item } ) => {
/* item status can be 'in_progress' or 'completed' */
} ) ;
// only triggered after item completed in conversation
// will always be triggered after conversation.item.appended
client . on ( 'conversation.item.completed' , ( { item } ) => {
/* item status will always be 'completed' */
} ) ;
يحتوي Wavtools على إدارة سهلة لتدفقات الصوت PCM16 في المتصفح، سواء في التسجيل أو التشغيل.
import { WavRecorder } from '/src/lib/wavtools/index.js' ;
const wavRecorder = new WavRecorder ( { sampleRate : 24000 } ) ;
wavRecorder . getStatus ( ) ; // "ended"
// request permissions, connect microphone
await wavRecorder . begin ( ) ;
wavRecorder . getStatus ( ) ; // "paused"
// Start recording
// This callback will be triggered in chunks of 8192 samples by default
// { mono, raw } are Int16Array (PCM16) mono & full channel data
await wavRecorder . record ( ( data ) => {
const { mono , raw } = data ;
} ) ;
wavRecorder . getStatus ( ) ; // "recording"
// Stop recording
await wavRecorder . pause ( ) ;
wavRecorder . getStatus ( ) ; // "paused"
// outputs "audio/wav" audio file
const audio = await wavRecorder . save ( ) ;
// clears current audio buffer and starts recording
await wavRecorder . clear ( ) ;
await wavRecorder . record ( ) ;
// get data for visualization
const frequencyData = wavRecorder . getFrequencies ( ) ;
// Stop recording, disconnects microphone, output file
await wavRecorder . pause ( ) ;
const finalAudio = await wavRecorder . end ( ) ;
// Listen for device change; e.g. if somebody disconnects a microphone
// deviceList is array of MediaDeviceInfo[] + `default` property
wavRecorder . listenForDeviceChange ( ( deviceList ) => { } ) ;
import { WavStreamPlayer } from '/src/lib/wavtools/index.js' ;
const wavStreamPlayer = new WavStreamPlayer ( { sampleRate : 24000 } ) ;
// Connect to audio output
await wavStreamPlayer . connect ( ) ;
// Create 1s of empty PCM16 audio
const audio = new Int16Array ( 24000 ) ;
// Queue 3s of audio, will start playing immediately
wavStreamPlayer . add16BitPCM ( audio , 'my-track' ) ;
wavStreamPlayer . add16BitPCM ( audio , 'my-track' ) ;
wavStreamPlayer . add16BitPCM ( audio , 'my-track' ) ;
// get data for visualization
const frequencyData = wavStreamPlayer . getFrequencies ( ) ;
// Interrupt the audio (halt playback) at any time
// To restart, need to call .add16BitPCM() again
const trackOffset = await wavStreamPlayer . interrupt ( ) ;
trackOffset . trackId ; // "my-track"
trackOffset . offset ; // sample number
trackOffset . currentTime ; // time in track
شكرًا لك على التحقق من وحدة التحكم في الوقت الفعلي. نأمل أن تستمتع مع Realtime API. شكر خاص لفريق Realtime API بأكمله لجعل هذا ممكنًا. لا تتردد في التواصل معنا أو طرح الأسئلة أو تقديم التعليقات عن طريق إنشاء مشكلة في المستودع. يمكنك أيضًا التواصل معنا وإخبارنا برأيك مباشرةً!