Skip to content
This repository was archived by the owner on Aug 26, 2025. It is now read-only.

Commit 6c815ff

Browse files
Merge pull request #68 from wieslawsoltes/FunctionCalling
Function calling
2 parents f9c7f28 + d9e7d35 commit 6c815ff

22 files changed

+622
-9
lines changed

ChatGPT.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatGPT.UI.Game", "samples\
5656
EndProject
5757
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{5EDF3913-3E89-44F6-A11F-52DA003AD315}"
5858
EndProject
59+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatGPT.CLI.FunctionCalling", "samples\ChatGPT.CLI.FunctionCalling\ChatGPT.CLI.FunctionCalling.csproj", "{0590592D-CDF7-4705-9F34-D8E779B66644}"
60+
EndProject
5961
Global
6062
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6163
Debug|Any CPU = Debug|Any CPU
@@ -122,6 +124,10 @@ Global
122124
{B0A8A296-575A-443A-BD55-12713EAFE506}.Debug|Any CPU.Build.0 = Debug|Any CPU
123125
{B0A8A296-575A-443A-BD55-12713EAFE506}.Release|Any CPU.ActiveCfg = Release|Any CPU
124126
{B0A8A296-575A-443A-BD55-12713EAFE506}.Release|Any CPU.Build.0 = Release|Any CPU
127+
{0590592D-CDF7-4705-9F34-D8E779B66644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
128+
{0590592D-CDF7-4705-9F34-D8E779B66644}.Debug|Any CPU.Build.0 = Debug|Any CPU
129+
{0590592D-CDF7-4705-9F34-D8E779B66644}.Release|Any CPU.ActiveCfg = Release|Any CPU
130+
{0590592D-CDF7-4705-9F34-D8E779B66644}.Release|Any CPU.Build.0 = Release|Any CPU
125131
EndGlobalSection
126132
GlobalSection(SolutionProperties) = preSolution
127133
HideSolutionNode = FALSE
@@ -142,5 +148,6 @@ Global
142148
{A9793A98-235E-4877-A3FC-C7C3DB4852FA} = {62EB4E5F-59EB-4D59-83A0-2A8B5B2397ED}
143149
{F9D8D17B-228B-4D5E-8126-39E8F41B87A2} = {D3B27217-AB31-4AB0-8FF9-C7528DA03FDE}
144150
{B0A8A296-575A-443A-BD55-12713EAFE506} = {5EDF3913-3E89-44F6-A11F-52DA003AD315}
151+
{0590592D-CDF7-4705-9F34-D8E779B66644} = {5EDF3913-3E89-44F6-A11F-52DA003AD315}
145152
EndGlobalSection
146153
EndGlobal
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net7.0</TargetFramework>
5+
<RuntimeIdentifiers>win-x64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<TrimMode>full</TrimMode>
9+
<IsPackable>False</IsPackable>
10+
<RootNamespace>ChatGPT.CLI</RootNamespace>
11+
</PropertyGroup>
12+
<PropertyGroup Condition="'$(Configuration)'=='Release'">
13+
<PublishAot>True</PublishAot>
14+
<TrimmerSingleWarn>false</TrimmerSingleWarn>
15+
</PropertyGroup>
16+
<ItemGroup>
17+
<ProjectReference Include="..\..\src\ChatGPT.Core\ChatGPT.Core.csproj" />
18+
</ItemGroup>
19+
</Project>
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
using ChatGPT;
2+
using ChatGPT.ViewModels.Chat;
3+
4+
Defaults.ConfigureDefaultServices();
5+
6+
var directions =
7+
"""
8+
You are a helpful assistant.
9+
Write answers in plain text.
10+
Do not use markdown.
11+
Only use the functions you have been provided with.
12+
""";
13+
14+
if (args.Length == 1)
15+
{
16+
directions = args[0];
17+
}
18+
19+
using var cts = new CancellationTokenSource();
20+
21+
var functions = GetFunctions();
22+
23+
var chat = new ChatViewModel(new ChatSettingsViewModel
24+
{
25+
MaxTokens = 2000,
26+
Model = "gpt-3.5-turbo-0613",
27+
Functions = functions,
28+
FunctionCall = "auto"
29+
// Force function call by setting FunctionCall property.
30+
// FunctionCall = new { name = "GetCurrentWeather" }
31+
});
32+
33+
// Enable to debug json requests and responses.
34+
// chat.Debug = true;
35+
36+
chat.AddSystemMessage(directions);
37+
38+
while (true)
39+
{
40+
Console.Write("> ");
41+
42+
var input = Console.ReadLine();
43+
if (string.IsNullOrWhiteSpace(input) || input == Environment.NewLine)
44+
{
45+
continue;
46+
}
47+
48+
try
49+
{
50+
chat.AddUserMessage(input);
51+
var result = await chat.SendAsync(chat.CreateChatMessages(), cts.Token);
52+
53+
chat.AddAssistantMessage(result?.Message);
54+
55+
if (result?.Message is { })
56+
{
57+
Console.WriteLine(result.Message);
58+
}
59+
60+
if (result?.FunctionCall is { } functionCall)
61+
{
62+
if (functionCall.Name == "GetCurrentWeather" && functionCall.Arguments is { })
63+
{
64+
functionCall.Arguments.TryGetValue("location", out var location);
65+
functionCall.Arguments.TryGetValue("unit", out var unit);
66+
var functionCallResult = GetCurrentWeather(location, unit ?? "celsius");
67+
chat.AddFunctionMessage(functionCallResult, functionCall.Name);
68+
69+
Console.WriteLine(functionCallResult);
70+
}
71+
}
72+
}
73+
catch (Exception ex)
74+
{
75+
Console.WriteLine("Error: " + ex.Message);
76+
}
77+
}
78+
79+
string GetCurrentWeather(string? location, string? unit)
80+
{
81+
Console.WriteLine($"Weather for {location} [{unit}].");
82+
return "Cloudy.";
83+
}
84+
85+
object GetFunctions()
86+
{
87+
return new[]
88+
{
89+
new
90+
{
91+
name = "GetCurrentWeather",
92+
description = "Get the current weather in a given location",
93+
parameters = new
94+
{
95+
type = "object",
96+
properties = new
97+
{
98+
location = new
99+
{
100+
type = "string",
101+
description = "The city and state, e.g. San Francisco, CA"
102+
},
103+
unit = new
104+
{
105+
type = "string",
106+
@enum = new[] {"celsius", "fahrenheit"}
107+
},
108+
},
109+
required = new[] {"location"}
110+
},
111+
}
112+
};
113+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System.Text.Json.Serialization;
2+
using CommunityToolkit.Mvvm.ComponentModel;
3+
4+
namespace ChatGPT.ViewModels.Chat;
5+
6+
public class ChatFunctionCallViewModel : ObservableObject
7+
{
8+
private string? _name;
9+
10+
[JsonConstructor]
11+
public ChatFunctionCallViewModel()
12+
{
13+
}
14+
15+
public ChatFunctionCallViewModel(string name)
16+
: this()
17+
{
18+
_name = name;
19+
}
20+
21+
[JsonPropertyName("name")]
22+
public string? Name
23+
{
24+
get => _name;
25+
set => SetProperty(ref _name, value);
26+
}
27+
28+
public ChatFunctionCallViewModel Copy()
29+
{
30+
return new ChatFunctionCallViewModel
31+
{
32+
Name = _name,
33+
};
34+
}
35+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System.Text.Json.Serialization;
2+
using CommunityToolkit.Mvvm.ComponentModel;
3+
4+
namespace ChatGPT.ViewModels.Chat;
5+
6+
public class ChatFunctionViewModel : ObservableObject
7+
{
8+
private string? _name;
9+
private string? _description;
10+
private object? _parameters;
11+
12+
[JsonConstructor]
13+
public ChatFunctionViewModel()
14+
{
15+
}
16+
17+
public ChatFunctionViewModel(string name, string description)
18+
: this()
19+
{
20+
_name = name;
21+
_description = description;
22+
}
23+
24+
public ChatFunctionViewModel(string name, string description, object parameters)
25+
: this()
26+
{
27+
_name = name;
28+
_description = description;
29+
_parameters = parameters;
30+
}
31+
32+
[JsonPropertyName("name")]
33+
public string? Name
34+
{
35+
get => _name;
36+
set => SetProperty(ref _name, value);
37+
}
38+
39+
[JsonPropertyName("description")]
40+
public string? Description
41+
{
42+
get => _description;
43+
set => SetProperty(ref _description, value);
44+
}
45+
46+
[JsonPropertyName("parameters")]
47+
public object? Parameters
48+
{
49+
get => _parameters;
50+
set => SetProperty(ref _parameters, value);
51+
}
52+
53+
public ChatFunctionViewModel Copy()
54+
{
55+
return new ChatFunctionViewModel
56+
{
57+
Name = _name,
58+
Description = _description,
59+
// TODO: Copy Parameters if type is reference.
60+
Parameters = _parameters
61+
};
62+
}
63+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Text.Json.Serialization;
4+
using CommunityToolkit.Mvvm.ComponentModel;
5+
6+
namespace ChatGPT.ViewModels.Chat;
7+
8+
public class ChatMessageFunctionCallViewModel : ObservableObject
9+
{
10+
private string? _name;
11+
private Dictionary<string, string>? _arguments;
12+
13+
[JsonConstructor]
14+
public ChatMessageFunctionCallViewModel()
15+
{
16+
}
17+
18+
public ChatMessageFunctionCallViewModel(string role, Dictionary<string, string> arguments)
19+
: this()
20+
{
21+
_name = role;
22+
_arguments = arguments;
23+
}
24+
25+
[JsonPropertyName("name")]
26+
public string? Name
27+
{
28+
get => _name;
29+
set => SetProperty(ref _name, value);
30+
}
31+
32+
[JsonPropertyName("arguments")]
33+
public Dictionary<string, string>? Arguments
34+
{
35+
get => _arguments;
36+
set => SetProperty(ref _arguments, value);
37+
}
38+
39+
public ChatMessageFunctionCallViewModel Copy()
40+
{
41+
var functionCall = new ChatMessageFunctionCallViewModel
42+
{
43+
Name = _name,
44+
// TODO: Copy entry Value if it's reference value.
45+
Arguments = _arguments?.ToDictionary(
46+
e => e.Key,
47+
e => e.Value)
48+
};
49+
50+
return functionCall;
51+
}
52+
}

src/ChatGPT.Core/ViewModels/Chat/ChatMessageViewModel.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public class ChatMessageViewModel : ObservableObject
1313
{
1414
private string? _role;
1515
private string? _message;
16+
private string? _name;
17+
private ChatMessageFunctionCallViewModel? _functionCall;
1618
private string? _format;
1719
private bool _isSent;
1820
private bool _isAwaiting;
@@ -52,20 +54,63 @@ public ChatMessageViewModel(string role, string message)
5254
_message = message;
5355
}
5456

57+
public ChatMessageViewModel(string role, string message, string name)
58+
: this()
59+
{
60+
_role = role;
61+
_message = message;
62+
_name = name;
63+
}
64+
65+
public ChatMessageViewModel(string role, string? message, string name, ChatMessageFunctionCallViewModel functionCall)
66+
: this()
67+
{
68+
_role = role;
69+
_message = message;
70+
_name = name;
71+
_functionCall = functionCall;
72+
}
73+
74+
/// <summary>
75+
/// The role of the messages author. One of system, user, assistant, or function.
76+
/// </summary>
5577
[JsonPropertyName("role")]
5678
public string? Role
5779
{
5880
get => _role;
5981
set => SetProperty(ref _role, value);
6082
}
6183

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

94+
/// <summary>
95+
/// 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.
96+
/// </summary>
97+
[JsonPropertyName("name")]
98+
public string? Name
99+
{
100+
get => _name;
101+
set => SetProperty(ref _name, value);
102+
}
103+
104+
/// <summary>
105+
/// The name and arguments of a function that should be called, as generated by the model.
106+
/// </summary>
107+
[JsonPropertyName("function_call")]
108+
public ChatMessageFunctionCallViewModel? FunctionCall
109+
{
110+
get => _functionCall;
111+
set => SetProperty(ref _functionCall, value);
112+
}
113+
69114
[JsonPropertyName("format")]
70115
public string? Format
71116
{
@@ -299,6 +344,8 @@ public ChatMessageViewModel Copy()
299344
{
300345
Role = _role,
301346
Message = _message,
347+
Name = _name,
348+
FunctionCall = _functionCall?.Copy(),
302349
Format = _format,
303350
IsSent = _isSent,
304351
IsAwaiting = _isAwaiting,

0 commit comments

Comments
 (0)