Skip to content

Commit

Permalink
Realtime Api Integration
Browse files Browse the repository at this point in the history
  • Loading branch information
kayhantolga committed Nov 10, 2024
1 parent 36ba527 commit 23a3f67
Show file tree
Hide file tree
Showing 22 changed files with 3,322 additions and 6 deletions.
10 changes: 10 additions & 0 deletions OpenAI.Playground/OpenAI.Playground.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,27 @@

<ItemGroup>
<PackageReference Include="LaserCatEyes.HttpClientListener" Version="8.0.1" />
<PackageReference Include="NAudio" Version="2.2.1" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' != 'net8.0' And '$(TargetFramework)' != 'net7.0'">
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
Expand Down Expand Up @@ -77,6 +81,9 @@
<None Update="SampleData\FineTuningJobSample2.jsonl">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="SampleData\Hello.wav">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="SampleData\How-Assistants-work.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand All @@ -98,6 +105,9 @@
<None Update="SampleData\SentimentAnalysisSample.jsonl">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="SampleData\Tell_Me_Story.wav">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
22 changes: 18 additions & 4 deletions OpenAI.Playground/Program.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
using Betalgo.Ranul.OpenAI.Extensions;
using Betalgo.Ranul.OpenAI;
using Betalgo.Ranul.OpenAI.Extensions;
using Betalgo.Ranul.OpenAI.Interfaces;
using Betalgo.Ranul.OpenAI.Managers;
using LaserCatEyes.HttpClientListener;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OpenAI.Playground.TestHelpers;
using OpenAI.Playground.TestHelpers.RealtimeHelpers;

using Microsoft.Extensions.Logging.Console;

var builder = new ConfigurationBuilder().AddJsonFile("ApiSettings.json").AddUserSecrets<Program>();

Expand All @@ -18,6 +25,7 @@

//if you want to use beta services you have to set UseBeta to true. Otherwise, it will use the stable version of OpenAI apis.
serviceCollection.AddOpenAIService(r => r.UseBeta = true);
serviceCollection.AddOpenAIRealtimeService();

//serviceCollection.AddOpenAIService();
//// DeploymentId and ResourceName are only for Azure OpenAI. If you want to use Azure OpenAI services you have to set Provider type To Azure.
Expand All @@ -29,8 +37,12 @@
// options.ResourceName = "MyResourceName";
//});

var serviceProvider = serviceCollection.BuildServiceProvider();
var serviceProvider = serviceCollection.AddLogging((loggingBuilder) => loggingBuilder
.SetMinimumLevel(LogLevel.Debug)
.AddConsole()
).BuildServiceProvider();
var sdk = serviceProvider.GetRequiredService<IOpenAIService>();
var realtimeSdk = serviceProvider.GetRequiredService<IOpenAIRealtimeService>();

// CHAT GPT
// |-----------------------------------------------------------------------|
Expand All @@ -39,8 +51,10 @@
// | / \ / \ | \ /) | ( \ /o\ / ) | (\ / | / \ / \ |
// |-----------------------------------------------------------------------|

await ChatCompletionTestHelper.RunSimpleChatCompletionTest(sdk);
await ChatCompletionTestHelper.RunSimpleCompletionStreamTest(sdk);
//await ChatCompletionTestHelper.RunSimpleChatCompletionTest(sdk);
//await ChatCompletionTestHelper.RunSimpleCompletionStreamTest(sdk);

await (new RealtimeAudioExample(realtimeSdk)).Run();

//Assistants - BETA
//await AssistantTestHelper.BasicsTestHelper.RunTests(sdk);
Expand Down
Binary file added OpenAI.Playground/SampleData/Hello.wav
Binary file not shown.
Binary file added OpenAI.Playground/SampleData/Tell_Me_Story.wav
Binary file not shown.
199 changes: 199 additions & 0 deletions OpenAI.Playground/TestHelpers/RealtimeHelpers/RealtimeAudioExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
using System.Text.Json;
using Betalgo.Ranul.OpenAI.Managers;
using Betalgo.Ranul.OpenAI.ObjectModels.RealtimeModels;
using Betalgo.Ranul.OpenAI.ObjectModels.SharedModels;

namespace OpenAI.Playground.TestHelpers.RealtimeHelpers;

public class RealtimeAudioExample : IDisposable
{
private readonly IOpenAIRealtimeService _ai;
private readonly VoiceInput _voiceInput;
private readonly VoiceOutput _voiceOutput;

public RealtimeAudioExample(IOpenAIRealtimeService ai)
{
_ai = ai;
_voiceInput = new(_ai);
_voiceOutput = new();
}

public void Dispose()
{
_voiceInput.Dispose();
_voiceOutput.Dispose();
_ai.Dispose();
}


public async Task Run()
{
SetupEventHandlers();
await _ai.ConnectAsync();
await _ai.ClientEvents.Session.Update(new()
{
Session = new()
{
Instructions = "You are a great, upbeat friend. You made jokes all the time and your voices is full of joy.",
Voice = "verse",
Modalities = ["text", "audio"],
Tools =
[
new()
{
Type = "function",
Name = "get_current_weather",
Description = "Get the current weather",
Parameters = PropertyDefinition.DefineObject(new Dictionary<string, PropertyDefinition>
{
{ "location", PropertyDefinition.DefineString("The city and state, e.g. San Francisco, CA") },
{ "unit", PropertyDefinition.DefineEnum(["celsius", "fahrenheit"], string.Empty) }
}, ["location"], null, null, null)
}
]
}
});

Console.WriteLine("Press 'R' to start recording, 'S' to stop, 'Q' to quit");

while (true)
{
var key = Console.ReadKey(true).Key;
if (key == ConsoleKey.R)
{
_voiceInput.StartRecording();
Console.WriteLine("Recording started...");
}
else if (key == ConsoleKey.S)
{
await StopAndSendAudio();
}
else if (key == ConsoleKey.Q)
{
break;
}
}
}

private async Task StopAndSendAudio()
{
_voiceInput.StopRecording();
Console.WriteLine("Recording stopped.");
await _ai.ClientEvents.InputAudioBuffer.Commit();
await _ai.ClientEvents.Response.Create();
}

private async Task SendPreRecordedAudio(string filePath)
{
Console.WriteLine($"Sending pre-recorded audio: {filePath}");
await _voiceInput.SendAudioFile(filePath);
await _ai.ClientEvents.InputAudioBuffer.Commit();
}

private void SetupEventHandlers()
{
// Handle server events related to audio input
_ai.ServerEvents.Conversation.Item.InputAudioTranscription.OnCompleted += (sender, args) => { Console.WriteLine($"Transcription completed: {args.Transcript}"); };
_ai.ServerEvents.Conversation.Item.InputAudioTranscription.OnFailed += (sender, args) => { Console.WriteLine($"Transcription failed: {args.Error}"); };
_ai.ServerEvents.InputAudioBuffer.OnCommitted += (sender, args) => { Console.WriteLine("Audio buffer committed."); };
_ai.ServerEvents.InputAudioBuffer.OnCleared += (sender, args) => { Console.WriteLine("Audio buffer cleared."); };
_ai.ServerEvents.InputAudioBuffer.OnSpeechStopped += (sender, args) => { Console.WriteLine("Speech stopped detected."); };
_ai.ServerEvents.InputAudioBuffer.OnSpeechStarted += async (sender, args) =>
{
Console.WriteLine("Speech started detected.");
_voiceOutput.StopAndClear();

// Optionally, notify the server to cancel any ongoing responses
await _ai.ClientEvents.Response.Cancel();
};
_ai.ServerEvents.Response.AudioTranscript.OnDelta += (sender, args) =>
{
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.Write($"{args.Delta}");
Console.ResetColor();
};
_ai.ServerEvents.Response.Audio.OnDelta += (sender, args) =>
{
try
{
if (!string.IsNullOrEmpty(args.Delta))
{
var audioData = Convert.FromBase64String(args.Delta);
_voiceOutput.EnqueueAudioData(audioData);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error processing audio delta: {ex.Message}");
}
};
_ai.ServerEvents.Response.Audio.OnDone += (sender, args) =>
{
Console.WriteLine();
Console.WriteLine("Audio response completed.");
};


_ai.ServerEvents.Response.FunctionCallArguments.OnDelta += (sender, args) =>
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"Function call arguments delta: {args.Delta}");
Console.ResetColor();
};

_ai.ServerEvents.Response.FunctionCallArguments.OnDone += async (sender, args) =>
{
if (args.Arguments != null)
{
Console.WriteLine($"Function call completed: {args.Arguments}");
if (args.Name == "get_current_weather")
{
await HandleWeatherFunction(args.Arguments, args.CallId);
}
}
};
_ai.ServerEvents.OnError += (sender, args) => { Console.WriteLine($"Error: {args.Error.Message}"); };
//for debug
//_ai.ServerEvents.OnAll += (sender, args) => { Console.WriteLine($"Received response: {args}"); };
}

private async Task HandleWeatherFunction(string arguments, string callId)
{
try
{
var args = JsonSerializer.Deserialize<WeatherArgs>(arguments);
// Simulate weather API call
var weatherResult = new
{
temperature = args.unit == "celsius" ? 22 : 72,
unit = args.unit,
description = "Sunny with light clouds",
location = args.location
};

// Create function output
await _ai.ClientEvents.Conversation.Item.Create(new()
{
Item = new()
{
Type = ItemType.FunctionCallOutput,
CallId = callId,
Output = JsonSerializer.Serialize(weatherResult)
}
});

// Generate new response
await _ai.ClientEvents.Response.Create();
}
catch (Exception ex)
{
Console.WriteLine($"Error handling weather function: {ex.Message}");
}
}

private class WeatherArgs
{
public string location { get; set; }
public string unit { get; set; }
}
}
Loading

0 comments on commit 23a3f67

Please sign in to comment.