Skip to content

Commit

Permalink
Merge pull request #39 from AndreaPic/develop
Browse files Browse the repository at this point in the history
Function Feature Release
  • Loading branch information
AndreaPic committed Jul 30, 2023
2 parents 180a52c + cc2731f commit 6271480
Show file tree
Hide file tree
Showing 10 changed files with 561 additions and 17 deletions.
9 changes: 5 additions & 4 deletions DevExtremeAI/DevExtremeAI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,19 @@
<RepositoryUrl>https://github.com/AndreaPic/DevExtremeAI</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>$(VersionPrefix)$(VersionSuffix)</Version>
<VersionPrefix>1.0.4</VersionPrefix>
<VersionPrefix>1.1.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
<PackageTags>openai;dotnet;aspnet;csharp;gpt-4;gpt-3.5-turbo;davinci;DALL·E;Whisper;fine-tunes</PackageTags>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<AssemblyVersion>1.1.0.0</AssemblyVersion>
<FileVersion>1.1.0.0</FileVersion>
<RepositoryType>git</RepositoryType>
<Description>This library is full compliant to openAI specs and also implement openAI error response. It's very easy to use with asp.net core, has full support to dependency injection and it's very easy to use in libraries without dependency injection.</Description>
<PackageIcon></PackageIcon>
<Copyright></Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReleaseNotes>
- (new) Added support for multiple stop sequences in completions
- (1.1.0 new) *** Full functions implementation ***
- (1.0.4) Added support for multiple stop sequences in completions
</PackageReleaseNotes>
</PropertyGroup>

Expand Down
113 changes: 100 additions & 13 deletions DevExtremeAI/OpenAIDTO/ChatDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using DevExtremeAI.Utils;
using System.Text.Json.Nodes;

namespace DevExtremeAI.OpenAIDTO
{
Expand All @@ -28,11 +29,66 @@ public class CreateChatCompletionRequest
[JsonPropertyName("messages")]
public List<ChatCompletionRequestMessage> Messages { get; private set; } = new List<ChatCompletionRequestMessage>();

/// <summary>
/// Add a message to the completion
/// </summary>
/// <param name="message">The message to add</param>
public void AddMessage(ChatCompletionRequestMessage message)
{
Messages.Add(message);
}

/// <summary>
/// Function definitions for ai
/// </summary>
private IList<FunctionDefinition>? functions;

/// <summary>
/// A list of functions the model may generate JSON inputs for.
/// </summary>
[JsonPropertyName("functions")]
public IList<FunctionDefinition>? Functions
{
get
{
if ( (functions == null) || (functions.Count == 0))
{
return null;
}
return functions;
}
set
{
functions = value;
}
}

/// <summary>
/// Add a function definition to the completion
/// </summary>
/// <param name="functionDefinition">The funciton definition</param>
public void AddFunction(FunctionDefinition functionDefinition)
{
if (functions == null)
{
functions = new List<FunctionDefinition>();
}
functions.Add(functionDefinition);
}


/// <summary>
/// Controls how 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.
/// "none" is the default when no functions are present.
/// "auto" is the default if functions are present
/// </summary>
[JsonPropertyName("function_call")]
public string? FunctionCall { get; set; }

/// <summary>
/// What sampling temperature to use, between 0 and 2.
/// Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. We generally recommend altering this or `top_p` but not both.
Expand Down Expand Up @@ -67,6 +123,9 @@ public void AddMessage(ChatCompletionRequestMessage message)
[JsonPropertyName("stop")]
public object? Stop => stops.Count switch { 0 => null, 1 => stops[0], > 1 => stops, _ => null };

/// <summary>
/// stop list
/// </summary>
private List<string> stops { get; set; } = new List<string>();

/// <summary>
Expand Down Expand Up @@ -146,6 +205,38 @@ public class ChatCompletionRequestMessage
[JsonPropertyName("name")]
public string? Name { get; set; }

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

}


public class ChatCompletionfunction
{
/// <summary>
/// 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.
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; }

/// <summary>
/// A description of what the function does, used by the model to choose when and how to call the function.
/// </summary>
[JsonPropertyName("description")]
public string? Description { get; set; }

/// <summary>
/// The parameters the functions accepts, described as a JSON Schema object.
/// See the guide (https://platform.openai.com/docs/guides/gpt/function-calling) for examples,
/// and the JSON Schema reference (https://json-schema.org/understanding-json-schema/) for documentation about the format.
/// To describe a function that accepts no parameters, provide the value {"type": "object", "properties": { } }.
/// </summary>
[JsonPropertyName("parameters")]
public string JSONSchemaParameters { get; set; }

}

[JsonConverter(typeof(JsonStringEnumConverterEx<ChatCompletionMessageRoleEnum>))]
Expand All @@ -156,7 +247,9 @@ public enum ChatCompletionMessageRoleEnum
[EnumMember(Value = "user")]
User = 1,
[EnumMember(Value = "assistant")]
Assistant = 2
Assistant = 2,
[EnumMember(Value = "function")]
Function = 3
}


Expand Down Expand Up @@ -211,19 +304,13 @@ public class ChatCompletionResponseMessage
/// </summary>
[JsonPropertyName("content")]
public string Content { get; set; }
}

//[JsonConverter(typeof(JsonStringEnumConverterEx<ChatCompletionResponseMessageRoleEnum>))]

//public enum ChatCompletionResponseMessageRoleEnum
//{
// [EnumMember(Value = "system")]
// System = 0,
// [EnumMember(Value = "user")]
// User = 1,
// [EnumMember(Value = "assistant")]
// Assistant = 2
//}
/// <summary>
/// The function to call
/// </summary>
[JsonPropertyName("function_call")]
public FunctionCallDefinition? FunctionCall { get; set; }
}

}

12 changes: 12 additions & 0 deletions DevExtremeAI/OpenAIDTO/CompletionsDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,16 @@ public class CreateCompletionRequest
/// </summary>
[JsonPropertyName("prompt")]
public object? Prompt => Prompts.Count switch { 0 => null, 1 => Prompts[0], > 1 => Prompts, _ => null };

/// <summary>
/// Prompt container
/// </summary>
private List<string> Prompts { get; set; } = new List<string>();

/// <summary>
/// Add a propt to the container
/// </summary>
/// <param name="prompt">The value of the prompt to add</param>
public void AddCompletionPrompt(string prompt)
{
Prompts.Add(prompt);
Expand Down Expand Up @@ -94,6 +103,9 @@ public void AddCompletionPrompt(string prompt)
[JsonPropertyName("stop")]
public object? Stop => stops.Count switch { 0 => null, 1 => stops[0], > 1 => stops, _ => null };

/// <summary>
/// The list of stops
/// </summary>
private List<string> stops { get; set; } = new List<string>();

/// <summary>
Expand Down
135 changes: 135 additions & 0 deletions DevExtremeAI/OpenAIDTO/FunctionDefinition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using DevExtremeAI.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace DevExtremeAI.OpenAIDTO
{


/// <summary>
/// This object define a schema for the function's description used by OpenAI
/// </summary>
public class FunctionDefinition
{
/// <summary>
/// The name of the funciont
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; }

/// <summary>
/// The description of the function behavior
/// </summary>
[JsonPropertyName("description")]
public string Description { get; set; }

/// <summary>
/// The function parameters
/// </summary>
[JsonPropertyName("parameters")]
public ParametersDefinition Parameters {get;set;} = new ();

}

/// <summary>
/// Definition of the function parameters
/// </summary>
public class ParametersDefinition
{
/// <summary>
/// parameters are in dto object so in openai example the value is "object"
/// </summary>
[JsonPropertyName("type")]
public string TypeName { get; set; } = "object";

/// <summary>
/// List of the properties used as argument for the function (key is the property name, value is the property definition)
/// </summary>
[JsonPropertyName("properties")]
public IDictionary<string, PropertyDefinition> Properties { get; set; } = new Dictionary<string, PropertyDefinition>();

/// <summary>
/// List of the required properties
/// </summary>
[JsonPropertyName("required")]
public IList<string>? Required { get; set; }

/// <summary>
/// Add a property to required property list
/// </summary>
/// <param name="propertyName"></param>
public void AddRequiredProperty(string propertyName)
{
if (Required == null)
{
Required = new List<string>();
}
Required.Add(propertyName);
}
}

/// <summary>
/// Property definition
/// </summary>
public class PropertyDefinition
{
/// <summary>
/// property type (like string,integer)
/// </summary>
/// <remarks>
/// In case of enum you can use string as property type
/// </remarks>
[JsonPropertyName("type")]
public string TypeName { get; set; }

/// <summary>
/// property discription
/// </summary>
[JsonPropertyName("description")]
public string Description { get; set; }

/// <summary>
/// If the property is a enum value, here there are valid values
/// </summary>
[JsonPropertyName("enum")]
public IList<string>? EnumValues { get; set; }

/// <summary>
/// Add a valid value to EnumValues
/// </summary>
/// <param name="enumValue">
/// A value for the enum list EnumValues
/// </param>
public void AddEnumValue(string enumValue)
{
if (EnumValues == null)
{
EnumValues = new List<string>();
}
EnumValues.Add(enumValue);
}
}

/// <summary>
/// The definition of a function call
/// </summary>
public class FunctionCallDefinition
{
/// <summary>
/// Function name
/// </summary>
[JsonPropertyName("name")]
public string FunctionName { get; set; }

/// <summary>
/// The arguments of the function, the key is the property name of the object to pass to the function and the value is the value of the property
/// </summary>
[JsonPropertyName("arguments")]
[JsonConverter(typeof(JsonStringArgumentsDictionaryConverter))]
public IDictionary<string, object> Arguments { get; set; } = new Dictionary<string, object>();
}
}
48 changes: 48 additions & 0 deletions DevExtremeAI/Utils/JsonStringArgumentsDictionaryConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace DevExtremeAI.Utils
{
internal sealed class JsonStringArgumentsDictionaryConverter
: JsonConverter<IDictionary<string, object>>
{
public override IDictionary<string, object>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Dictionary<string, object> ret = null;

Check warning on line 17 in DevExtremeAI/Utils/JsonStringArgumentsDictionaryConverter.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.

Check warning on line 17 in DevExtremeAI/Utils/JsonStringArgumentsDictionaryConverter.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.

if (reader.TokenType != JsonTokenType.String)
{
throw new JsonException();
}

var dictionary = new Dictionary<string, object>();

var arguments = reader.GetString();

if (!string.IsNullOrEmpty(arguments))
{
ret = JsonSerializer.Deserialize<Dictionary<string, object>>(arguments);

Check warning on line 30 in DevExtremeAI/Utils/JsonStringArgumentsDictionaryConverter.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.

Check warning on line 30 in DevExtremeAI/Utils/JsonStringArgumentsDictionaryConverter.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.
}
else
{
ret = new Dictionary<string, object>();
}

return ret;
}

public override void Write(Utf8JsonWriter writer, IDictionary<string, object> dictionary, JsonSerializerOptions options)
{
var json = JsonSerializer.Serialize<IDictionary<string,object>>(dictionary, options);

writer.WriteStringValue(json);

}
}
}

0 comments on commit 6271480

Please sign in to comment.