Skip to content

Commit

Permalink
Update ChatGptClient to support tools
Browse files Browse the repository at this point in the history
  • Loading branch information
selfdocumentingcode committed Aug 3, 2024
1 parent 0b0d427 commit 7bd1b2b
Show file tree
Hide file tree
Showing 20 changed files with 358 additions and 142 deletions.
34 changes: 18 additions & 16 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
root = true
# Remove the line below if you want to inherit .editorconfig settings from higher directories

# C# files
[*.cs]
Expand Down Expand Up @@ -47,11 +46,11 @@ dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = stat

dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
dotnet_naming_symbols.private_or_internal_field.required_modifiers =

dotnet_naming_symbols.local.applicable_kinds = local
dotnet_naming_symbols.local.applicable_accessibilities = local
dotnet_naming_symbols.local.required_modifiers =
dotnet_naming_symbols.local.required_modifiers =

dotnet_naming_symbols.constant_field.applicable_kinds = field
dotnet_naming_symbols.constant_field.applicable_accessibilities = *
Expand All @@ -60,18 +59,18 @@ dotnet_naming_symbols.constant_field.required_modifiers = const
# Naming styles

dotnet_naming_style._fieldname.required_prefix = _
dotnet_naming_style._fieldname.required_suffix =
dotnet_naming_style._fieldname.word_separator =
dotnet_naming_style._fieldname.required_suffix =
dotnet_naming_style._fieldname.word_separator =
dotnet_naming_style._fieldname.capitalization = camel_case

dotnet_naming_style.camelcase.required_prefix =
dotnet_naming_style.camelcase.required_suffix =
dotnet_naming_style.camelcase.word_separator =
dotnet_naming_style.camelcase.required_prefix =
dotnet_naming_style.camelcase.required_suffix =
dotnet_naming_style.camelcase.word_separator =
dotnet_naming_style.camelcase.capitalization = camel_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

### Stylecop rules ###
Expand All @@ -92,6 +91,7 @@ dotnet_diagnostic.sa1202.severity = suggestion # Elements should be ordered by a
# Naming

dotnet_diagnostic.sa1309.severity = none # Field names should not begin with underscore
dotnet_diagnostic.sa1312.severity = none # Variable names must being with lower-case letter

# Maintainability

Expand All @@ -102,7 +102,6 @@ dotnet_diagnostic.sa1402.severity = suggestion # File may only contain a single
dotnet_diagnostic.sa1502.severity = none # Element should not be on a single line
dotnet_diagnostic.sa1503.severity = none # Braces should not be omitted
dotnet_diagnostic.sa1515.severity = suggestion # Single-line comment should be preceded by blank line

# Documentation

dotnet_diagnostic.sa1600.severity = none # Elements should be documented
Expand Down Expand Up @@ -152,15 +151,14 @@ dotnet_diagnostic.cs1591.severity = none # Missing XML comment for publicly visi

# Source-generated code

dotnet_diagnostic.syslib1045.severity = none # Convert to 'GeneratedRegexAttribute'
dotnet_diagnostic.syslib1045.severity = none # Convert to 'GeneratedRegexAttribute'.

### Rider auto-generated rules ###

# Microsoft .NET properties
csharp_preferred_modifier_order = public, protected, private, internal, file, new, static, abstract, virtual, sealed, override, async, extern, unsafe, volatile, readonly, required:suggestion
csharp_preserve_single_line_blocks = true
csharp_style_var_elsewhere = false:none
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_for_built_in_types = false:none
csharp_style_var_when_type_is_apparent = true:none
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:none
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none
Expand All @@ -176,19 +174,23 @@ resharper_braces_for_while = required
resharper_braces_redundant = false
resharper_csharp_empty_block_style = together
resharper_csharp_insert_final_newline = true
resharper_csharp_keep_blank_lines_in_declarations = 1
resharper_csharp_wrap_after_declaration_lpar = true
resharper_csharp_wrap_after_invocation_lpar = true
resharper_csharp_wrap_arguments_style = chop_if_long
resharper_csharp_wrap_before_binary_opsign = true
resharper_csharp_wrap_extends_list_style = chop_always
resharper_csharp_wrap_parameters_style = chop_if_long
resharper_for_built_in_types = use_var_when_evident
resharper_instance_members_qualify_declared_in =
resharper_for_other_types = use_var_when_evident
resharper_instance_members_qualify_declared_in =
resharper_keep_existing_embedded_block_arrangement = false
resharper_keep_existing_enum_arrangement = false
resharper_nested_ternary_style = expanded
resharper_parentheses_same_type_operations = true
resharper_place_accessorholder_attribute_on_same_line = false
resharper_place_expr_property_on_single_line = true
resharper_place_field_attribute_on_same_line = false
resharper_place_simple_embedded_statement_on_same_line = true
resharper_place_simple_initializer_on_single_line = false
resharper_trailing_comma_in_multiline_lists = true
28 changes: 28 additions & 0 deletions Kattbot.Common/Models/KattGpt/ChatCompletionChoice.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Text.Json.Serialization;

namespace Kattbot.Common.Models.KattGpt;

public record ChatCompletionChoice
{
/// <summary>
/// The index of the choice in the list of choices.
/// </summary>
[JsonPropertyName("index")]
public int Index { get; set; }

Check warning on line 11 in Kattbot.Common/Models/KattGpt/ChatCompletionChoice.cs

View workflow job for this annotation

GitHub Actions / Build

The property's documentation summary text should begin with: 'Gets or sets' (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1623.md)

/// <summary>
/// A chat completion message generated by the model.
/// </summary>
[JsonPropertyName("message")]
public ChatCompletionMessage Message { get; set; } = null!;

Check warning on line 17 in Kattbot.Common/Models/KattGpt/ChatCompletionChoice.cs

View workflow job for this annotation

GitHub Actions / Build

The property's documentation summary text should begin with: 'Gets or sets' (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1623.md)

/// <summary>
/// The reason the model stopped generating tokens.
/// This will be 'stop' if the model hit a natural stop point or a provided stop sequence,
/// 'length' if the maximum number of tokens specified in the request was reached,
/// 'content_filter' if content was omitted due to a flag from our content filters
/// or 'tool_calls' if the model called a tool.
/// </summary>
[JsonPropertyName("finish_reason")]
public ChoiceFinishReason FinishReason { get; set; }

Check warning on line 27 in Kattbot.Common/Models/KattGpt/ChatCompletionChoice.cs

View workflow job for this annotation

GitHub Actions / Build

The property's documentation summary text should begin with: 'Gets or sets' (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1623.md)
}
40 changes: 28 additions & 12 deletions Kattbot.Common/Models/KattGpt/ChatCompletionCreateRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

namespace Kattbot.Common.Models.KattGpt;

/// <summary>
/// Creates a model response for the given chat conversation.
/// https://platform.openai.com/docs/api-reference/chat/create

Check warning on line 7 in Kattbot.Common/Models/KattGpt/ChatCompletionCreateRequest.cs

View workflow job for this annotation

GitHub Actions / Build

/// </summary>
public record ChatCompletionCreateRequest
{
/// <summary>
Expand All @@ -19,22 +23,34 @@ public record ChatCompletionCreateRequest
public ChatCompletionMessage[] Messages { get; set; } = null!;

/// <summary>
/// Gets or sets a list of functions the model may generate JSON inputs for.
/// https://platform.openai.com/docs/api-reference/chat/create#functions.
/// A list of tools the model may call. Currently, only functions are supported as a tool.
/// Use this to provide a list of functions the model may generate JSON inputs for.
/// A max of 128 functions are supported.
/// https://platform.openai.com/docs/api-reference/chat/create#chat-create-tools
/// </summary>
[JsonPropertyName("functions")]
public ChatCompletionFunction[]? Functions { get; set; }
[JsonPropertyName("tools")]
public ChatCompletionTool[]? Tools { get; set; }

/// <summary>
/// Gets or sets the mode for controlling the model responds to function calls. none means the model does not call a
/// function,
/// and responds to the end-user. auto means the model can pick between an end-user or calling a function.
/// Specifying a particular function via {"name": "my_function"} forces the model to call that function.
/// Defaults to "none" when no functions are present and "auto" if functions are present.
/// https://platform.openai.com/docs/api-reference/chat/create#function_call.
/// Controls which (if any) tool is called by the model.
/// none means the model will not call any tool and instead generates a message.
/// auto means the model can pick between generating a message or calling one or more tools.
/// required means the model must call one or more tools.
/// Specifying a particular tool via {"type": "function", "function": {"name": "my_function"}}
/// forces the model to call that tool.
/// none is the default when no tools are present. auto is the default if tools are present.
/// https://platform.openai.com/docs/api-reference/chat/create#chat-create-tool_choice
/// </summary>
[JsonPropertyName("function_call")]
public string? FunctionCall { get; set; }
[JsonPropertyName("tool_choice")]
public StringOrObject<ChatCompletionToolChoice> ToolChoice { get; set; }

Check warning on line 45 in Kattbot.Common/Models/KattGpt/ChatCompletionCreateRequest.cs

View workflow job for this annotation

GitHub Actions / Build

Non-nullable property 'ToolChoice' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

/// <summary>
/// Whether to enable parallel function calling during tool use.
/// Defaults to true
/// https://platform.openai.com/docs/api-reference/chat/create#chat-create-parallel_tool_calls
/// </summary>
[JsonPropertyName("parallel_tool_calls")]
public bool ParallelToolCalls { get; set; } = false;

/// <summary>
/// Gets or sets what sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more
Expand Down
10 changes: 5 additions & 5 deletions Kattbot.Common/Models/KattGpt/ChatCompletionCreateResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ namespace Kattbot.Common.Models.KattGpt;
public record ChatCompletionCreateResponse
{
[JsonPropertyName("id")]
public string Id { get; set; } = null!;
public string Id { get; init; } = null!;

[JsonPropertyName("object")]
public string Object { get; set; } = null!;
public string Object { get; init; } = null!;

[JsonPropertyName("created")]
public int Created { get; set; }
public int Created { get; init; }

[JsonPropertyName("choices")]
public List<Choice> Choices { get; set; } = new();
public List<ChatCompletionChoice> Choices { get; init; } = [];

[JsonPropertyName("usage")]
public Usage Usage { get; set; } = null!;
public ChatCompletionUsage Usage { get; init; } = null!;
}
10 changes: 5 additions & 5 deletions Kattbot.Common/Models/KattGpt/ChatCompletionFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,25 @@ namespace Kattbot.Common.Models.KattGpt;
public record ChatCompletionFunction
{
/// <summary>
/// Gets or sets the name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with
/// Gets the name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with
/// a maximum length of 64.
/// https://platform.openai.com/docs/api-reference/chat/create#functions-name.
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; } = null!;
public string Name { get; init; } = null!;

/// <summary>
/// Gets or sets a description of what the function does, used by the model to choose when and how to call the
/// function.
/// https://platform.openai.com/docs/api-reference/chat/create#functions-description.
/// </summary>
[JsonPropertyName("description")]
public string? Description { get; set; }
public string? Description { get; init; }

/// <summary>
/// Gets or sets the parameters the functions accepts, described as a JSON Schema object.
/// Gets the parameters the functions accepts, described as a JSON Schema object.
/// https://platform.openai.com/docs/api-reference/chat/create#functions-parameters.
/// </summary>
[JsonPropertyName("parameters")]
public JsonObject Parameters { get; set; } = null!;
public JsonObject Parameters { get; init; } = null!;
}
27 changes: 27 additions & 0 deletions Kattbot.Common/Models/KattGpt/ChatCompletionFunctionCall.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Text.Json.Serialization;

namespace Kattbot.Common.Models.KattGpt;

public record ChatCompletionFunctionCall
{
public ChatCompletionFunctionCall(string name, string arguments)
{
Name = name;
Arguments = arguments;
}

/// <summary>
/// The name of the function to call.
/// </summary>
[JsonPropertyName("name")]
public string Name { get; }

Check warning on line 17 in Kattbot.Common/Models/KattGpt/ChatCompletionFunctionCall.cs

View workflow job for this annotation

GitHub Actions / Build

The property's documentation summary text should begin with: 'Gets' (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1623.md)

/// <summary>
/// The arguments to call the function with, as generated by the model in JSON format.
/// Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your
/// function schema.
/// Validate the arguments in your code before calling your function.
/// </summary>
[JsonPropertyName("arguments")]
public string Arguments { get; }

Check warning on line 26 in Kattbot.Common/Models/KattGpt/ChatCompletionFunctionCall.cs

View workflow job for this annotation

GitHub Actions / Build

The property's documentation summary text should begin with: 'Gets' (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1623.md)
}
12 changes: 12 additions & 0 deletions Kattbot.Common/Models/KattGpt/ChatCompletionFunctionChoice.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;

namespace Kattbot.Common.Models.KattGpt;

public record ChatCompletionFunctionChoice
{
/// <summary>
/// The name of the function to call.
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; } = null!;

Check warning on line 11 in Kattbot.Common/Models/KattGpt/ChatCompletionFunctionChoice.cs

View workflow job for this annotation

GitHub Actions / Build

The property's documentation summary text should begin with: 'Gets or sets' (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1623.md)
}
56 changes: 31 additions & 25 deletions Kattbot.Common/Models/KattGpt/ChatCompletionMessage.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace Kattbot.Common.Models.KattGpt;

Expand All @@ -10,19 +11,25 @@ public ChatCompletionMessage(string role, string? content)
Content = content;
}

public ChatCompletionMessage(string role, string name, string content)
public ChatCompletionMessage(string role, string content, string toolCallId)
{
Role = role;
Name = name;
ToolCallId = toolCallId;
Content = content;
}

[JsonConstructor]
public ChatCompletionMessage(string role, string name, string? content, FunctionCall? functionCall)
public ChatCompletionMessage(
string role,
string? content,
string? name,
string? toolCallId,
List<ChatCompletionToolCall>? toolCalls)
: this(role, content)
{
FunctionCall = functionCall;
ToolCalls = toolCalls;
Name = name;
ToolCallId = toolCallId;
}

/// <summary>
Expand All @@ -33,33 +40,32 @@ public ChatCompletionMessage(string role, string name, string? content, Function
public string Role { get; } = null!;

/// <summary>
/// Gets or sets the contents of the message. content is required for all messages, and may be null for assistant
/// messages with function calls.
/// https://platform.openai.com/docs/api-reference/chat/create#messages-content.
/// The contents of the assistant message.
/// Required unless tool_calls or function_call is specified.
/// Dev note: This should be private but the api is being weird and doesn't allow nulls like it says it does
/// </summary>
[JsonPropertyName("content")]
public string?
Content
{
get;
set;
} // This should be private but the api is being weird and doesn't allow nulls like it says it does
public string? Content { get; set; }

/// <summary>
/// Gets 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.
/// https://platform.openai.com/docs/api-reference/chat/create#messages-name.
/// An optional name for the participant.
/// Provides the model information to differentiate between participants of the same role.
/// TODO: Use this instead of tokens in messages.
/// </summary>
[JsonPropertyName("name")]
public string? Name { get; }

/// <summary>
/// Gets the name and arguments of a function that should be called, as generated by the model.
/// https://platform.openai.com/docs/api-reference/chat/create#messages-function_call.
/// Tool call that this message is responding to.
/// </summary>
[JsonPropertyName("tool_call_id")]
public string? ToolCallId { get; set; }

/// <summary>
/// The tool calls generated by the model, such as function calls.
/// </summary>
[JsonPropertyName("function_call")]
public FunctionCall? FunctionCall { get; }
[JsonPropertyName("tool_calls")]
public List<ChatCompletionToolCall>? ToolCalls { get; }

public static ChatCompletionMessage AsSystem(string content)
{
Expand All @@ -79,11 +85,11 @@ public static ChatCompletionMessage AsAssistant(string content)
/// <summary>
/// Builds a message as a function call which contains the function result to be added to the context.
/// </summary>
/// <param name="name">The name of the function.</param>
/// <param name="content">The result of the function.</param>
/// <param name="toolCallId">The name of the function.</param>
/// <returns>A <see cref="ChatCompletionMessage" />.</returns>
public static ChatCompletionMessage AsFunctionCallResult(string name, string content)
public static ChatCompletionMessage AsToolCallResult(string content, string toolCallId)
{
return new ChatCompletionMessage("function", name, content);
return new ChatCompletionMessage("tool", content, toolCallId);
}
}
Loading

0 comments on commit 7bd1b2b

Please sign in to comment.