Skip to content

Commit

Permalink
docs: Added FunctionCalling example.
Browse files Browse the repository at this point in the history
  • Loading branch information
HavenDV committed Sep 1, 2024
1 parent 07b8eae commit 7015d9c
Show file tree
Hide file tree
Showing 11 changed files with 337 additions and 362 deletions.
12 changes: 11 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,14 @@ double? priceInUsd = CreateSpeechRequestModel.Tts1Hd.TryGetPriceInUsd(

Priority place for bugs: https://github.com/tryAGI/OpenAI/issues
Priority place for ideas and general questions: https://github.com/tryAGI/OpenAI/discussions
Discord: https://discord.gg/Ca2xhfBf3v
Discord: https://discord.gg/Ca2xhfBf3v
## Acknowledgments

![JetBrains logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.png)
This project is supported by JetBrains through the [Open Source Support Program](https://jb.gg/OpenSourceSupport).
![CodeRabbit logo](https://opengraph.githubassets.com/1c51002d7d0bbe0c4fd72ff8f2e58192702f73a7037102f77e4dbb98ac00ea8f/marketplace/coderabbitai)
This project is supported by CodeRabbit through the [Open Source Support Program](https://github.com/marketplace/coderabbitai).
56 changes: 34 additions & 22 deletions docs/samples/Assistants.AssistantsWithVision.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,32 +46,44 @@ ThreadObject thread = await api.Assistants.CreateThreadAsync(new CreateThreadReq
]
});

// AsyncResultCollection<StreamingUpdate> streamingUpdates = api.Assistants.CreateRunStreamingAsync(
// thread,
// assistant,
// new RunCreationOptions()
// {
// AdditionalInstructions = "When possible, try to sneak in puns if you're asked to compare things.",
// });
//
// await foreach (StreamingUpdate streamingUpdate in streamingUpdates)
// {
// if (streamingUpdate.UpdateKind == StreamingUpdateReason.RunCreated)
// {
// Console.WriteLine($"--- Run started! ---");
// }
// if (streamingUpdate is MessageContentUpdate contentUpdate)
// {
// Console.Write(contentUpdate.Text);
// }
// }
RunObject response = await api.Assistants.CreateRunAsync(
var streamingUpdates = api.Assistants.CreateRunAsStreamAsync(
threadId: thread.Id,
assistantId: assistant.Id,
instructions: "When possible, try to sneak in puns if you're asked to compare things.");

Console.WriteLine(response[0].Content);
await foreach (AssistantStreamEvent streamingUpdate in streamingUpdates)
{
if (streamingUpdate.IsRun && streamingUpdate.Run.Value.IsValue1) // RunCreated
{
Console.WriteLine("--- Run started! ---");
}
if (streamingUpdate is { IsMessage: true, Message: var messageStreamEvent } &&
messageStreamEvent.Value is { IsValue3: true, Value3: var delta })
{
foreach (var deltaVariation in delta.Data.Delta.Content ?? [])
{
if (deltaVariation.IsValue1)
{
Console.WriteLine();
Console.WriteLine(deltaVariation.Value1.ImageFile?.FileId);
}
if (deltaVariation.IsValue2)
{
Console.Write(deltaVariation.Value2.Text?.Value);
}
if (deltaVariation.IsValue3)
{
Console.WriteLine();
Console.WriteLine(deltaVariation.Value3.Refusal);
}
if (deltaVariation.IsValue4)
{
Console.WriteLine();
Console.WriteLine(deltaVariation.Value4.ImageUrl?.Url);
}
}
}
}

_ = await api.Files.DeleteFileAsync(pictureOfAppleFile.Id);
_ = await api.Assistants.DeleteThreadAsync(thread.Id);
Expand Down
90 changes: 90 additions & 0 deletions docs/samples/Chat.FunctionCalling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
```csharp
using var api = GetAuthenticatedClient();

List<ChatCompletionRequestMessage> messages = [
"What's the weather like today?",
];

var service = new FunctionCallingService();
IList<ChatCompletionTool> tools = service.AsTools();

bool requiresAction;

do
{
requiresAction = false;
CreateChatCompletionResponse chatCompletion = await api.Chat.CreateChatCompletionAsync(
messages,
model: CreateChatCompletionRequestModel.Gpt4o,
tools: tools);

switch (chatCompletion.Choices[0].FinishReason)
{
case CreateChatCompletionResponseChoiceFinishReason.Stop:
{
// Add the assistant message to the conversation history.
messages.Add(chatCompletion.Choices[0].Message.AsRequestMessage());
break;
}

case CreateChatCompletionResponseChoiceFinishReason.ToolCalls:
{
// First, add the assistant message with tool calls to the conversation history.
messages.Add(chatCompletion.Choices[0].Message.AsRequestMessage());

// Then, add a new tool message for each tool call that is resolved.
foreach (ChatCompletionMessageToolCall toolCall in chatCompletion.Choices[0].Message.ToolCalls ?? [])
{
var json = await service.CallAsync(
functionName: toolCall.Function.Name,
argumentsAsJson: toolCall.Function.Arguments);
messages.Add(json.AsToolMessage(toolCall.Id));
}

requiresAction = true;
break;
}

case CreateChatCompletionResponseChoiceFinishReason.Length:
throw new NotImplementedException("Incomplete model output due to MaxTokens parameter or token limit exceeded.");

case CreateChatCompletionResponseChoiceFinishReason.ContentFilter:
throw new NotImplementedException("Omitted content due to a content filter flag.");

case CreateChatCompletionResponseChoiceFinishReason.FunctionCall:
throw new NotImplementedException("Deprecated in favor of tool calls.");

default:
throw new NotImplementedException(chatCompletion.Choices[0].FinishReason.ToString());
}
} while (requiresAction);

foreach (ChatCompletionRequestMessage requestMessage in messages)
{
if (requestMessage.System is { } systemMessage)
{
Console.WriteLine($"[SYSTEM]:");
Console.WriteLine($"{systemMessage.Content.Value1}");
Console.WriteLine();
break;
}
else if (requestMessage.User is { } userMessage)
{
Console.WriteLine($"[USER]:");
Console.WriteLine($"{userMessage.Content.Value1}");
Console.WriteLine();
}
else if (requestMessage.Assistant is { Content: not null } assistantMessage)
{
Console.WriteLine($"[ASSISTANT]:");
Console.WriteLine($"{assistantMessage.Content?.Value1}");
Console.WriteLine();
}
else if (requestMessage.Tool is { } toolMessage)
{
// Do not print any tool messages; let the assistant summarize the tool results instead.
break;

}
}
```
42 changes: 42 additions & 0 deletions docs/samples/Chat.FunctionCallingService.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
```csharp
public enum WeatherUnit
{
Celsius,
Fahrenheit,
}

[OpenAiTools(Strict = true)]
public interface IFunctionCallingService
{
[Description("Get the user's current location")]
public Task<string> GetCurrentLocation(
CancellationToken cancellationToken = default);

[Description("Get the current weather in a given location")]
public Task<string> GetCurrentWeatherAsync(
[Description("The city and state, e.g. Boston, MA")]
string location,
[Description("The temperature unit to use. Infer this from the specified location.")]
WeatherUnit unit = WeatherUnit.Celsius,
CancellationToken cancellationToken = default);
}

public class FunctionCallingService : IFunctionCallingService
{
public Task<string> GetCurrentLocation(
CancellationToken cancellationToken = default)
{
// Call the location API here.
return Task.FromResult("San Francisco");
}

public Task<string> GetCurrentWeatherAsync(
string location,
WeatherUnit unit = WeatherUnit.Celsius,
CancellationToken cancellationToken = default)
{
// Call the weather API here.
return Task.FromResult($"31 {unit:G}");
}
}
```
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ nav:
- SimpleChat: samples/Chat.SimpleChat.md
- SimpleChatStreaming: samples/Chat.SimpleChatStreaming.md
- ChatWithVision: samples/Chat.ChatWithVision.md
- FunctionCalling: samples/Chat.FunctionCalling.md
- FunctionCallingService: samples/Chat.FunctionCallingService.md
- Assistants:
- AssistantsWithVision: samples/Assistants.AssistantsWithVision.md
- ListFiles: samples/Assistants.ListFiles.md
Expand Down
20 changes: 14 additions & 6 deletions src/helpers/GenerateDocs/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,20 @@
{
var code = await File.ReadAllTextAsync(path);

var start = code.IndexOf("\n {", StringComparison.Ordinal);
var end = code.IndexOf("\n }", StringComparison.Ordinal);
code = code.Substring(start + 4, end - start + 4);

var lines = code.Split('\n')[1..^2];
code = string.Join('\n', lines.Select(x => x.Length > 8 ? x[8..] : string.Empty));
var startExample = code.IndexOf("// # START EXAMPLE #", StringComparison.Ordinal);
if (startExample == -1)
{
var start = code.IndexOf("\n {", StringComparison.Ordinal);
var end = code.IndexOf("\n }", StringComparison.Ordinal);
code = code.Substring(start + 4, end - start + 4);

var lines = code.Split('\n')[1..^2];
code = string.Join('\n', lines.Select(x => x.Length > 8 ? x[8..] : string.Empty));
}
else
{
code = code[(startExample + "// # START EXAMPLE #".Length)..].Trim();
}

var newPath = Path.Combine(newDir, $"{Path.GetFileNameWithoutExtension(path).Replace("Examples.", string.Empty)}.md");
await File.WriteAllTextAsync(newPath, $@"```csharp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
namespace OpenAI.IntegrationTests.Examples;

public partial class Examples
{
[Test]
[Explicit]
public async Task FunctionCalling()
{
using var api = GetAuthenticatedClient();

List<ChatCompletionRequestMessage> messages = [
"What's the weather like today?",
];

var service = new FunctionCallingService();
IList<ChatCompletionTool> tools = service.AsTools();

bool requiresAction;

do
{
requiresAction = false;
CreateChatCompletionResponse chatCompletion = await api.Chat.CreateChatCompletionAsync(
messages,
model: CreateChatCompletionRequestModel.Gpt4o,
tools: tools);

switch (chatCompletion.Choices[0].FinishReason)
{
case CreateChatCompletionResponseChoiceFinishReason.Stop:
{
// Add the assistant message to the conversation history.
messages.Add(chatCompletion.Choices[0].Message.AsRequestMessage());
break;
}

case CreateChatCompletionResponseChoiceFinishReason.ToolCalls:
{
// First, add the assistant message with tool calls to the conversation history.
messages.Add(chatCompletion.Choices[0].Message.AsRequestMessage());

// Then, add a new tool message for each tool call that is resolved.
foreach (ChatCompletionMessageToolCall toolCall in chatCompletion.Choices[0].Message.ToolCalls ?? [])
{
var json = await service.CallAsync(
functionName: toolCall.Function.Name,
argumentsAsJson: toolCall.Function.Arguments);
messages.Add(json.AsToolMessage(toolCall.Id));
}

requiresAction = true;
break;
}

case CreateChatCompletionResponseChoiceFinishReason.Length:
throw new NotImplementedException("Incomplete model output due to MaxTokens parameter or token limit exceeded.");

case CreateChatCompletionResponseChoiceFinishReason.ContentFilter:
throw new NotImplementedException("Omitted content due to a content filter flag.");

case CreateChatCompletionResponseChoiceFinishReason.FunctionCall:
throw new NotImplementedException("Deprecated in favor of tool calls.");

default:
throw new NotImplementedException(chatCompletion.Choices[0].FinishReason.ToString());
}
} while (requiresAction);

foreach (ChatCompletionRequestMessage requestMessage in messages)
{
if (requestMessage.System is { } systemMessage)
{
Console.WriteLine($"[SYSTEM]:");
Console.WriteLine($"{systemMessage.Content.Value1}");
Console.WriteLine();
break;
}
else if (requestMessage.User is { } userMessage)
{
Console.WriteLine($"[USER]:");
Console.WriteLine($"{userMessage.Content.Value1}");
Console.WriteLine();
}
else if (requestMessage.Assistant is { Content: not null } assistantMessage)
{
Console.WriteLine($"[ASSISTANT]:");
Console.WriteLine($"{assistantMessage.Content?.Value1}");
Console.WriteLine();
}
else if (requestMessage.Tool is { } toolMessage)
{
// Do not print any tool messages; let the assistant summarize the tool results instead.
break;

}
}
}
}
Loading

0 comments on commit 7015d9c

Please sign in to comment.