Skip to content

Commit

Permalink
Merge pull request #68 from wieslawsoltes/FunctionCalling
Browse files Browse the repository at this point in the history
Function calling
  • Loading branch information
wieslawsoltes authored Jul 27, 2023
2 parents f9c7f28 + d9e7d35 commit 6c815ff
Show file tree
Hide file tree
Showing 22 changed files with 622 additions and 9 deletions.
7 changes: 7 additions & 0 deletions ChatGPT.sln
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatGPT.UI.Game", "samples\
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{5EDF3913-3E89-44F6-A11F-52DA003AD315}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatGPT.CLI.FunctionCalling", "samples\ChatGPT.CLI.FunctionCalling\ChatGPT.CLI.FunctionCalling.csproj", "{0590592D-CDF7-4705-9F34-D8E779B66644}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -122,6 +124,10 @@ Global
{B0A8A296-575A-443A-BD55-12713EAFE506}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0A8A296-575A-443A-BD55-12713EAFE506}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0A8A296-575A-443A-BD55-12713EAFE506}.Release|Any CPU.Build.0 = Release|Any CPU
{0590592D-CDF7-4705-9F34-D8E779B66644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0590592D-CDF7-4705-9F34-D8E779B66644}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0590592D-CDF7-4705-9F34-D8E779B66644}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0590592D-CDF7-4705-9F34-D8E779B66644}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -142,5 +148,6 @@ Global
{A9793A98-235E-4877-A3FC-C7C3DB4852FA} = {62EB4E5F-59EB-4D59-83A0-2A8B5B2397ED}
{F9D8D17B-228B-4D5E-8126-39E8F41B87A2} = {D3B27217-AB31-4AB0-8FF9-C7528DA03FDE}
{B0A8A296-575A-443A-BD55-12713EAFE506} = {5EDF3913-3E89-44F6-A11F-52DA003AD315}
{0590592D-CDF7-4705-9F34-D8E779B66644} = {5EDF3913-3E89-44F6-A11F-52DA003AD315}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifiers>win-x64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TrimMode>full</TrimMode>
<IsPackable>False</IsPackable>
<RootNamespace>ChatGPT.CLI</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<PublishAot>True</PublishAot>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\ChatGPT.Core\ChatGPT.Core.csproj" />
</ItemGroup>
</Project>
113 changes: 113 additions & 0 deletions samples/ChatGPT.CLI.FunctionCalling/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using ChatGPT;
using ChatGPT.ViewModels.Chat;

Defaults.ConfigureDefaultServices();

var directions =
"""
You are a helpful assistant.
Write answers in plain text.
Do not use markdown.
Only use the functions you have been provided with.
""";

if (args.Length == 1)
{
directions = args[0];
}

using var cts = new CancellationTokenSource();

var functions = GetFunctions();

var chat = new ChatViewModel(new ChatSettingsViewModel
{
MaxTokens = 2000,
Model = "gpt-3.5-turbo-0613",
Functions = functions,
FunctionCall = "auto"
// Force function call by setting FunctionCall property.
// FunctionCall = new { name = "GetCurrentWeather" }
});

// Enable to debug json requests and responses.
// chat.Debug = true;

chat.AddSystemMessage(directions);

while (true)
{
Console.Write("> ");

var input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input) || input == Environment.NewLine)
{
continue;
}

try
{
chat.AddUserMessage(input);
var result = await chat.SendAsync(chat.CreateChatMessages(), cts.Token);

chat.AddAssistantMessage(result?.Message);

if (result?.Message is { })
{
Console.WriteLine(result.Message);
}

if (result?.FunctionCall is { } functionCall)
{
if (functionCall.Name == "GetCurrentWeather" && functionCall.Arguments is { })
{
functionCall.Arguments.TryGetValue("location", out var location);
functionCall.Arguments.TryGetValue("unit", out var unit);
var functionCallResult = GetCurrentWeather(location, unit ?? "celsius");
chat.AddFunctionMessage(functionCallResult, functionCall.Name);

Console.WriteLine(functionCallResult);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
}

string GetCurrentWeather(string? location, string? unit)
{
Console.WriteLine($"Weather for {location} [{unit}].");
return "Cloudy.";
}

object GetFunctions()
{
return new[]
{
new
{
name = "GetCurrentWeather",
description = "Get the current weather in a given location",
parameters = new
{
type = "object",
properties = new
{
location = new
{
type = "string",
description = "The city and state, e.g. San Francisco, CA"
},
unit = new
{
type = "string",
@enum = new[] {"celsius", "fahrenheit"}
},
},
required = new[] {"location"}
},
}
};
}
35 changes: 35 additions & 0 deletions src/ChatGPT.Core/ViewModels/Chat/ChatFunctionCallViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Text.Json.Serialization;
using CommunityToolkit.Mvvm.ComponentModel;

namespace ChatGPT.ViewModels.Chat;

public class ChatFunctionCallViewModel : ObservableObject
{
private string? _name;

[JsonConstructor]
public ChatFunctionCallViewModel()
{
}

public ChatFunctionCallViewModel(string name)
: this()
{
_name = name;
}

[JsonPropertyName("name")]
public string? Name
{
get => _name;
set => SetProperty(ref _name, value);
}

public ChatFunctionCallViewModel Copy()
{
return new ChatFunctionCallViewModel
{
Name = _name,
};
}
}
63 changes: 63 additions & 0 deletions src/ChatGPT.Core/ViewModels/Chat/ChatFunctionViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.Text.Json.Serialization;
using CommunityToolkit.Mvvm.ComponentModel;

namespace ChatGPT.ViewModels.Chat;

public class ChatFunctionViewModel : ObservableObject
{
private string? _name;
private string? _description;
private object? _parameters;

[JsonConstructor]
public ChatFunctionViewModel()
{
}

public ChatFunctionViewModel(string name, string description)
: this()
{
_name = name;
_description = description;
}

public ChatFunctionViewModel(string name, string description, object parameters)
: this()
{
_name = name;
_description = description;
_parameters = parameters;
}

[JsonPropertyName("name")]
public string? Name
{
get => _name;
set => SetProperty(ref _name, value);
}

[JsonPropertyName("description")]
public string? Description
{
get => _description;
set => SetProperty(ref _description, value);
}

[JsonPropertyName("parameters")]
public object? Parameters
{
get => _parameters;
set => SetProperty(ref _parameters, value);
}

public ChatFunctionViewModel Copy()
{
return new ChatFunctionViewModel
{
Name = _name,
Description = _description,
// TODO: Copy Parameters if type is reference.
Parameters = _parameters
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using CommunityToolkit.Mvvm.ComponentModel;

namespace ChatGPT.ViewModels.Chat;

public class ChatMessageFunctionCallViewModel : ObservableObject
{
private string? _name;
private Dictionary<string, string>? _arguments;

[JsonConstructor]
public ChatMessageFunctionCallViewModel()
{
}

public ChatMessageFunctionCallViewModel(string role, Dictionary<string, string> arguments)
: this()
{
_name = role;
_arguments = arguments;
}

[JsonPropertyName("name")]
public string? Name
{
get => _name;
set => SetProperty(ref _name, value);
}

[JsonPropertyName("arguments")]
public Dictionary<string, string>? Arguments
{
get => _arguments;
set => SetProperty(ref _arguments, value);
}

public ChatMessageFunctionCallViewModel Copy()
{
var functionCall = new ChatMessageFunctionCallViewModel
{
Name = _name,
// TODO: Copy entry Value if it's reference value.
Arguments = _arguments?.ToDictionary(
e => e.Key,
e => e.Value)
};

return functionCall;
}
}
47 changes: 47 additions & 0 deletions src/ChatGPT.Core/ViewModels/Chat/ChatMessageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class ChatMessageViewModel : ObservableObject
{
private string? _role;
private string? _message;
private string? _name;
private ChatMessageFunctionCallViewModel? _functionCall;
private string? _format;
private bool _isSent;
private bool _isAwaiting;
Expand Down Expand Up @@ -52,20 +54,63 @@ public ChatMessageViewModel(string role, string message)
_message = message;
}

public ChatMessageViewModel(string role, string message, string name)
: this()
{
_role = role;
_message = message;
_name = name;
}

public ChatMessageViewModel(string role, string? message, string name, ChatMessageFunctionCallViewModel functionCall)
: this()
{
_role = role;
_message = message;
_name = name;
_functionCall = functionCall;
}

/// <summary>
/// The role of the messages author. One of system, user, assistant, or function.
/// </summary>
[JsonPropertyName("role")]
public string? Role
{
get => _role;
set => SetProperty(ref _role, value);
}

/// <summary>
/// The contents of the message. content is required for all messages, and may be null for assistant messages with function calls.
/// </summary>
[JsonPropertyName("message")]
public string? Message
{
get => _message;
set => SetProperty(ref _message, value);
}

/// <summary>
/// The name of the author of this message. name is required if role is function, and it should be the name of the function whose response is in the content. May contain a-z, A-Z, 0-9, and underscores, with a maximum length of 64 characters.
/// </summary>
[JsonPropertyName("name")]
public string? Name
{
get => _name;
set => SetProperty(ref _name, value);
}

/// <summary>
/// The name and arguments of a function that should be called, as generated by the model.
/// </summary>
[JsonPropertyName("function_call")]
public ChatMessageFunctionCallViewModel? FunctionCall
{
get => _functionCall;
set => SetProperty(ref _functionCall, value);
}

[JsonPropertyName("format")]
public string? Format
{
Expand Down Expand Up @@ -299,6 +344,8 @@ public ChatMessageViewModel Copy()
{
Role = _role,
Message = _message,
Name = _name,
FunctionCall = _functionCall?.Copy(),
Format = _format,
IsSent = _isSent,
IsAwaiting = _isAwaiting,
Expand Down
Loading

0 comments on commit 6c815ff

Please sign in to comment.