Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function calling #68

Merged
merged 28 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
72abc94
Add Function role to message
wieslawsoltes Jul 11, 2023
2c09bfa
Add function calling json api
wieslawsoltes Jul 11, 2023
5ecfba0
Add xml docs
wieslawsoltes Jul 14, 2023
f61ee19
Add Name property
wieslawsoltes Jul 14, 2023
9eb9182
Handle name property
wieslawsoltes Jul 14, 2023
8258d47
Add helper method AddFunctionMessage
wieslawsoltes Jul 14, 2023
13826e8
Update ChatSettingsViewModel.cs
wieslawsoltes Jul 14, 2023
e57e383
Add FunctionCall property to chat message
wieslawsoltes Jul 14, 2023
ec30929
Create ChatFunctionCallViewModel.cs
wieslawsoltes Jul 14, 2023
6d3c43b
Create ChatFunctionViewModel.cs
wieslawsoltes Jul 14, 2023
e1d1837
Update ChatSettingsViewModel.cs
wieslawsoltes Jul 14, 2023
362cb7f
Update ChatJsonContext.cs
wieslawsoltes Jul 14, 2023
503f814
Update MainViewModelJsonContext.cs
wieslawsoltes Jul 14, 2023
9c48972
Update ChatFunctionCallViewModel.cs
wieslawsoltes Jul 14, 2023
7bf3b97
Update ChatRequestBody.cs
wieslawsoltes Jul 14, 2023
113ef50
Update ChatServiceSettings.cs
wieslawsoltes Jul 14, 2023
bf2b760
Update ChatViewModel.cs
wieslawsoltes Jul 14, 2023
bf67560
Update ChatSettingsViewModel.cs
wieslawsoltes Jul 14, 2023
36f1273
Implement function calling
wieslawsoltes Jul 23, 2023
7ed4fce
Add ChatGPT.CLI.FunctionCalling sample
wieslawsoltes Jul 23, 2023
8217998
Update ChatFunctionsJsonConverter.cs
wieslawsoltes Jul 23, 2023
b09db19
Update ChatViewModel.cs
wieslawsoltes Jul 23, 2023
d49c67c
Update SystemTextJsonChatSerializer.cs
wieslawsoltes Jul 23, 2023
8c3ad07
Add json debug support
wieslawsoltes Jul 23, 2023
5dff2e6
Update Program.cs
wieslawsoltes Jul 23, 2023
12b91cd
Update Program.cs
wieslawsoltes Jul 23, 2023
6599453
Update Program.cs
wieslawsoltes Jul 23, 2023
d9e7d35
Update Program.cs
wieslawsoltes Jul 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading