diff --git a/openai/.gitignore b/openai/.gitignore new file mode 100644 index 00000000..2cb90536 --- /dev/null +++ b/openai/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +*.sln.iml +.idea/**/* +**/.idea/* \ No newline at end of file diff --git a/openai/Apps/OpenAIExample.cs b/openai/Apps/OpenAIExample.cs new file mode 100644 index 00000000..63f58bbb --- /dev/null +++ b/openai/Apps/OpenAIExample.cs @@ -0,0 +1,59 @@ +using Ivy; +using OpenAI.Chat; + +namespace OpenAIExample.Apps +{ + [App(icon: Icons.MessageCircle, title: "OpenAI Chat Demo")] + public class OpenAIExample : ViewBase + { + private readonly ChatClient _aiClient; + + public OpenAIExample() + { + // Retrieve OpenAI API key from environment variable + var openAiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY"); + if (string.IsNullOrWhiteSpace(openAiKey)) + throw new InvalidOperationException("OPENAI_API_KEY environment variable is not set."); + + const string Model = "gpt-4o-mini"; // recommended lightweight model + + // Initialize OpenAI ChatClient + _aiClient = new ChatClient(Model, openAiKey); + } + + public override object? Build() + { + // Initialize chat messages with a greeting from the assistant + var messages = UseState(ImmutableArray.Create( + new Ivy.ChatMessage(ChatSender.Assistant, "Hello! I'm an OpenAI bot. How can I assist you!") + )); + + async void HandleMessageAsync(Event @event) + { + // Add user message + messages.Set(messages.Value.Add(new Ivy.ChatMessage(ChatSender.User, @event.Value))); + + // Add assistant "Thinking..." status + var currentMessages = messages.Value; + messages.Set(currentMessages.Add(new Ivy.ChatMessage(ChatSender.Assistant, new ChatStatus("Thinking...")))); + + try + { + // Call OpenAI API + ChatCompletion completion = await _aiClient.CompleteChatAsync(@event.Value); + string aiResponse = completion.Content[0].Text; + + // Replace "Thinking..." with actual response + messages.Set(currentMessages.Add(new Ivy.ChatMessage(ChatSender.Assistant, aiResponse))); + } + catch (Exception ex) + { + messages.Set(currentMessages.Add(new Ivy.ChatMessage(ChatSender.Assistant, $"Error: {ex.Message}"))); + } + } + + return Layout.Center().Padding(0, 10, 0, 10) + | new Chat(messages.Value.ToArray(), HandleMessageAsync).Width(Size.Full().Max(200)); + } + } +} \ No newline at end of file diff --git a/openai/GlobalUsings.cs b/openai/GlobalUsings.cs new file mode 100644 index 00000000..2e3a5a35 --- /dev/null +++ b/openai/GlobalUsings.cs @@ -0,0 +1,29 @@ +global using Ivy; +global using Ivy.Apps; +global using Ivy.Auth; +global using Ivy.Chrome; +global using Ivy.Client; +global using Ivy.Core; +global using Ivy.Core.Hooks; +global using Ivy.Helpers; +global using Ivy.Hooks; +global using Ivy.Shared; +global using Ivy.Services; +global using Ivy.Views; +global using Ivy.Views.Alerts; +global using Ivy.Views.Blades; +global using Ivy.Views.Builders; +global using Ivy.Views.Charts; +global using Ivy.Views.Dashboards; +global using Ivy.Views.Forms; +global using Ivy.Views.Tables; +global using Ivy.Widgets.Inputs; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; +global using System.Collections.Immutable; +global using System.ComponentModel.DataAnnotations; +global using System.Globalization; +global using System.Reactive.Linq; + +namespace OpenAIExample; diff --git a/openai/OpenAIExample.csproj b/openai/OpenAIExample.csproj new file mode 100644 index 00000000..99baec02 --- /dev/null +++ b/openai/OpenAIExample.csproj @@ -0,0 +1,25 @@ + + + + Exe + net9.0 + enable + enable + CS8618;CS8603;CS8602;CS8604;CS9113 + OpenAIExample + + + + + + + + + + + + + + + + diff --git a/openai/OpenAIExample.sln b/openai/OpenAIExample.sln new file mode 100644 index 00000000..2f53c242 --- /dev/null +++ b/openai/OpenAIExample.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36414.22 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenAIExample", "OpenAIExample.csproj", "{EF960359-8B28-8AFB-5F46-5541F544614E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EF960359-8B28-8AFB-5F46-5541F544614E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF960359-8B28-8AFB-5F46-5541F544614E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF960359-8B28-8AFB-5F46-5541F544614E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF960359-8B28-8AFB-5F46-5541F544614E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {15C2FC16-C3CE-4060-8FF2-C381126407C6} + EndGlobalSection +EndGlobal diff --git a/openai/Program.cs b/openai/Program.cs new file mode 100644 index 00000000..db294f64 --- /dev/null +++ b/openai/Program.cs @@ -0,0 +1,12 @@ + +CultureInfo.DefaultThreadCurrentCulture = CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en-US"); +var server = new Server(); +#if DEBUG +server.UseHotReload(); +#endif +server.AddAppsFromAssembly(); +server.AddConnectionsFromAssembly(); +var chromeSettings = new ChromeSettings() + .UseTabs(preventDuplicates: true); +server.UseChrome(chromeSettings); +await server.RunAsync(); \ No newline at end of file diff --git a/openai/README.md b/openai/README.md new file mode 100644 index 00000000..067af85c --- /dev/null +++ b/openai/README.md @@ -0,0 +1,17 @@ +# OpenAIExample + +Web application created using [Ivy](https://github.com/Ivy-Interactive/Ivy). + +Ivy is a web framework for building interactive web applications using C# and .NET. + +## Run + +``` +dotnet watch +``` + +## Deploy + +``` +ivy deploy +``` \ No newline at end of file diff --git a/openai/Screenshot-OpenAI.png b/openai/Screenshot-OpenAI.png new file mode 100644 index 00000000..0c28c9e1 Binary files /dev/null and b/openai/Screenshot-OpenAI.png differ