Skip to content

Commit

Permalink
docs: Improved docs.
Browse files Browse the repository at this point in the history
  • Loading branch information
HavenDV committed Aug 31, 2024
1 parent 758b27e commit 7aa699b
Show file tree
Hide file tree
Showing 26 changed files with 1,081 additions and 78 deletions.
230 changes: 230 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
# OpenAI

[![Nuget package](https://img.shields.io/nuget/vpre/tryAGI.OpenAI)](https://www.nuget.org/packages/tryAGI.OpenAI/)
[![dotnet](https://github.com/tryAGI/OpenAI/actions/workflows/dotnet.yml/badge.svg?branch=main)](https://github.com/tryAGI/OpenAI/actions/workflows/dotnet.yml)
[![License: MIT](https://img.shields.io/github/license/tryAGI/OpenAI)](https://github.com/tryAGI/OpenAI/blob/main/LICENSE.txt)
[![Discord](https://img.shields.io/discord/1115206893015662663?label=Discord&logo=discord&logoColor=white&color=d82679)](https://discord.gg/Ca2xhfBf3v)

## Features 🔥
- Fully generated C# SDK based on [official OpenAI OpenAPI specification](https://raw.githubusercontent.com/openai/openai-openapi/master/openapi.yaml) using [OpenApiGenerator](https://github.com/HavenDV/OpenApiGenerator)
- Same day update to support new features
- Updated and supported automatically if there are no breaking changes
- Contains a supported list of constants such as current prices, models, and other
- Source generator to define functions natively through C# interfaces
- All modern .NET features - nullability, trimming, NativeAOT, etc.
- Support .Net Framework/.Net Standard 2.0
- Support all OpenAI API endpoints including completions, chat, embeddings, images, assistants and more.
- Regularly tested for compatibility with popular custom providers like OpenRouter/DeepSeek

## Documentation
Examples and documentation can be found here: https://tryagi.github.io/OpenAI/

## Usage
```csharp
using var api = new OpenAiApi("API_KEY");
string response = await api.Chat.CreateChatCompletionAsync(
messages: ["Generate five random words."],
model: CreateChatCompletionRequestModel.Gpt4oMini);
Console.WriteLine(response); // "apple, banana, cherry, date, elderberry"
var enumerable = api.Chat.CreateChatCompletionAsStreamAsync(
messages: ["Generate five random words."],
model: CreateChatCompletionRequestModel.Gpt4oMini);

await foreach (string response in enumerable)
{
Console.WriteLine(response);
}
```
It uses three implicit conversions:
- from `string` to `ChatCompletionRequestUserMessage`. It will always be converted to the user message.
- from `ChatCompletionResponseMessage` to `string` . It will always contain the first choice message content.
- from `CreateChatCompletionStreamResponse` to `string` . It will always contain the first delta content.

You still can use the full response objects if you need more information, just replace `string response` to `var response`.

### Tools
```csharp
using OpenAI;

public enum Unit
{
Celsius,
Fahrenheit,
}

public class Weather
{
public string Location { get; set; } = string.Empty;
public double Temperature { get; set; }
public Unit Unit { get; set; }
public string Description { get; set; } = string.Empty;
}

[OpenAiTools(Strict = true)] // false by default. You can't use parameters with default values in Strict mode.
public interface IWeatherFunctions
{
[Description("Get the current weather in a given location")]
public Task<Weather> GetCurrentWeatherAsync(
[Description("The city and state, e.g. San Francisco, CA")] string location,
Unit unit,
CancellationToken cancellationToken = default);
}

public class WeatherService : IWeatherFunctions
{
public Task<Weather> GetCurrentWeatherAsync(string location, Unit unit = Unit.Celsius, CancellationToken cancellationToken = default)
{
return Task.FromResult(new Weather
{
Location = location,
Temperature = 22.0,
Unit = unit,
Description = "Sunny",
});
}
}

using var api = new OpenAiApi("API_KEY");

var service = new WeatherService();
var tools = service.AsTools();

var messages = new List<ChatCompletionRequestMessage>
{
"You are a helpful weather assistant.".AsSystemMessage(),
"What is the current temperature in Dubai, UAE in Celsius?".AsUserMessage(),
};
var model = CreateChatCompletionRequestModel.Gpt4oMini;
var result = await api.Chat.CreateChatCompletionAsync(
messages,
model: model,
tools: tools);
var resultMessage = result.Choices.First().Message;
messages.Add(resultMessage.AsRequestMessage());

foreach (var call in resultMessage.ToolCalls)
{
var json = await service.CallAsync(
functionName: call.Function.Name,
argumentsAsJson: call.Function.Arguments);
messages.Add(json.AsToolMessage(call.Id));
}

var result = await api.Chat.CreateChatCompletionAsync(
messages,
model: model,
tools: tools);
var resultMessage = result.Choices.First().Message;
messages.Add(resultMessage.AsRequestMessage());
```
```
> System:
You are a helpful weather assistant.
> User:
What is the current temperature in Dubai, UAE in Celsius?
> Assistant:
call_3sptsiHzKnaxF8bs8BWxPo0B:
GetCurrentWeather({"location":"Dubai, UAE","unit":"celsius"})
> Tool(call_3sptsiHzKnaxF8bs8BWxPo0B):
{"location":"Dubai, UAE","temperature":22,"unit":"celsius","description":"Sunny"}
> Assistant:
The current temperature in Dubai, UAE is 22°C with sunny weather.
```

### Structured Outputs
```csharp
using OpenAI;

using var api = new OpenAiApi("API_KEY");

var response = await api.Chat.CreateChatCompletionAsAsync<Weather>(
messages: ["Generate random weather."],
model: CreateChatCompletionRequestModel.Gpt4oMini,
jsonSerializerOptions: new JsonSerializerOptions
{
Converters = {new JsonStringEnumConverter()},
});
// or (if you need trimmable/NativeAOT version)
var response = await api.Chat.CreateChatCompletionAsAsync(
jsonTypeInfo: SourceGeneratedContext.Default.Weather,
messages: ["Generate random weather."],
model: CreateChatCompletionRequestModel.Gpt4oMini);

// response.Value1 contains the structured output
// response.Value2 contains the CreateChatCompletionResponse object
```
```
Weather:
Location: San Francisco, CA
Temperature: 65
Unit: Fahrenheit
Description: Partly cloudy with a light breeze and occasional sunshine.
Raw Response:
{"Location":"San Francisco, CA","Temperature":65,"Unit":"Fahrenheit","Description":"Partly cloudy with a light breeze and occasional sunshine."}
```
Additional code for trimmable/NativeAOT version:
```csharp
[JsonSourceGenerationOptions(Converters = [typeof(JsonStringEnumConverter<Unit>)])]
[JsonSerializable(typeof(Weather))]
public partial class SourceGeneratedContext : JsonSerializerContext;
```

### Custom providers
```csharp
using OpenAI;

using var api = CustomProviders.GitHubModels("GITHUB_TOKEN");
using var api = CustomProviders.Azure("API_KEY", "ENDPOINT");
using var api = CustomProviders.DeepInfra("API_KEY");
using var api = CustomProviders.DeepSeek("API_KEY");
using var api = CustomProviders.Fireworks("API_KEY");
using var api = CustomProviders.OpenRouter("API_KEY");
using var api = CustomProviders.Together("API_KEY");
```

### Constants
All `tryGetXXX` methods return `null` if the value is not found.
There also non-try methods that throw an exception if the value is not found.
```cs
using OpenAI;

// You can try to get the enum from string using:
var model = CreateChatCompletionRequestModelExtensions.ToEnum("gpt-4o") ?? throw new Exception("Invalid model");

// Chat
var model = CreateChatCompletionRequestModel.Gpt4oMini;
double? priceInUsd = model.TryGetPriceInUsd(
inputTokens: 500,
outputTokens: 500)
double? priceInUsd = model.TryGetFineTunePriceInUsd(
trainingTokens: 500,
inputTokens: 500,
outputTokens: 500)
int contextLength = model.TryGetContextLength() // 128_000
int outputLength = model.TryGetOutputLength() // 16_000
// Embeddings
var model = CreateEmbeddingRequestModel.TextEmbedding3Small;
int? maxInputTokens = model.TryGetMaxInputTokens() // 8191
double? priceInUsd = model.TryGetPriceInUsd(tokens: 500)

// Images
double? priceInUsd = CreateImageRequestModel.DallE3.TryGetPriceInUsd(
size: CreateImageRequestSize.x1024x1024,
quality: CreateImageRequestQuality.Hd)

// Speech to Text
double? priceInUsd = CreateTranscriptionRequestModel.Whisper1.TryGetPriceInUsd(
seconds: 60)

// Text to Speech
double? priceInUsd = CreateSpeechRequestModel.Tts1Hd.TryGetPriceInUsd(
characters: 1000)
```

## Support

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
79 changes: 79 additions & 0 deletions docs/samples/Assistants.AssistantsWithVision.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
```csharp
using var api = GetAuthenticatedClient();

ImagesResponse appleImage = await api.Images.CreateImageAsync(
prompt: "picture of apple",
responseFormat: CreateImageRequestResponseFormat.B64Json);
byte[] appleBytes = appleImage.Data[0].Bytes;

FileInfo appleFileInfo = new($"{Guid.NewGuid()}.png");

await File.WriteAllBytesAsync(appleFileInfo.FullName, appleBytes);

Console.WriteLine($"Apple image available at:\n{new Uri(appleFileInfo.FullName).AbsoluteUri}");

ImagesResponse orangeImage = await api.Images.CreateImageAsync(
prompt: "picture of orange",
responseFormat: CreateImageRequestResponseFormat.B64Json);
byte[] orangeBytes = orangeImage.Data[0].Bytes;

FileInfo orangeFileInfo = new($"{Guid.NewGuid()}.png");

await File.WriteAllBytesAsync(orangeFileInfo.FullName, orangeBytes);

Console.WriteLine($"Orange image available at:\n{new Uri(orangeFileInfo.FullName).AbsoluteUri}");

OpenAIFile pictureOfAppleFile = await api.Files.CreateFileAsync(
file: appleBytes,
filename: appleFileInfo.Name,
purpose: CreateFileRequestPurpose.Vision);
OpenAIFile pictureOfOrangeFile = await api.Files.CreateFileAsync(
file: orangeBytes,
filename: orangeFileInfo.Name,
purpose: CreateFileRequestPurpose.Vision);

AssistantObject assistant = await api.Assistants.CreateAssistantAsync(
model: CreateAssistantRequestModel.Gpt4o,
instructions: "When asked a question, attempt to answer very concisely. " +
"Prefer one-sentence answers whenever feasible.");

ThreadObject thread = await api.Assistants.CreateThreadAsync(new CreateThreadRequest
{
Messages = [
"Hello, assistant! Please compare these two images for me:",
pictureOfAppleFile,
pictureOfOrangeFile,
]
});

// 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(
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 api.Files.DeleteFileAsync(pictureOfAppleFile.Id);
_ = await api.Assistants.DeleteThreadAsync(thread.Id);
_ = await api.Assistants.DeleteAssistantAsync(assistant.Id);
```
15 changes: 15 additions & 0 deletions docs/samples/Assistants.ListAssistantsWithPagination.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
```csharp
using var api = GetAuthenticatedClient();

int count = 0;

ListAssistantsResponse response = await api.Assistants.ListAssistantsAsync();
foreach (AssistantObject assistant in response.Data)
{
Console.WriteLine($"[{count,3}] {assistant.Id} {assistant.CreatedAt:s} {assistant.Name}");

count++;

//_ = await api.Assistants.DeleteAssistantAsync(assistant.Id);
}
```
15 changes: 15 additions & 0 deletions docs/samples/Assistants.ListFiles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
```csharp
using var api = GetAuthenticatedClient();

int count = 0;

ListFilesResponse files = await api.Files.ListFilesAsync(purpose: CreateFileRequestPurpose.Assistants.ToValueString());
foreach (OpenAIFile file in files.Data)
{
Console.WriteLine($"[{count,3}] {file.Id} {file.CreatedAt:s} {file.Filename}");

count++;

//_ = await api.Files.DeleteFileAsync(file.Id);
}
```
Loading

0 comments on commit 7aa699b

Please sign in to comment.