Skip to content

Commit

Permalink
feat: Use official OpenAPI spec. (#78)
Browse files Browse the repository at this point in the history
* feat: Use official OpenAPI spec.

* feat: Implemented.
  • Loading branch information
HavenDV authored Dec 9, 2024
1 parent 3258088 commit 0357382
Show file tree
Hide file tree
Showing 828 changed files with 59,643 additions and 4,412 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![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 [OpenAPI specification](https://raw.githubusercontent.com/davidmigloz/langchain_dart/main/packages/anthropic_sdk_dart/oas/anthropic_openapi_curated.yaml) using [OpenApiGenerator](https://github.com/HavenDV/OpenApiGenerator)
- Fully generated C# SDK based on [official OpenAPI specification](https://raw.githubusercontent.com/anthropics/anthropic-sdk-typescript/refs/heads/main/.stats.yml) using [AutoSDK](https://github.com/HavenDV/OpenApiGenerator)
- Automatic releases of new preview versions if there was an update to the OpenAPI specification
- Source generator to define tools natively through C# interfaces
- All modern .NET features - nullability, trimming, NativeAOT, etc.
Expand Down
1 change: 1 addition & 0 deletions src/helpers/FixOpenApiSpec/FixOpenApiSpec.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoSDK" Version="0.27.1-dev.3" />
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.6.22" />
</ItemGroup>

Expand Down
72 changes: 55 additions & 17 deletions src/helpers/FixOpenApiSpec/Program.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,72 @@
using AutoSDK.Helpers;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers;

var path = args[0];
var jsonOrYaml = await File.ReadAllTextAsync(path);

if (OpenApi31Support.IsOpenApi31(jsonOrYaml))
{
jsonOrYaml = OpenApi31Support.ConvertToOpenApi30(jsonOrYaml);
}

var openApiDocument = new OpenApiStringReader().Read(jsonOrYaml, out var diagnostics);

openApiDocument.Components.Schemas["TextBlock"].Properties["type"].Enum = new List<IOpenApiAny>
openApiDocument.Components.Schemas.Add("Ping", new OpenApiSchema
{
new OpenApiString("text"),
};
openApiDocument.Components.Schemas["ImageBlock"].Properties["type"].Enum = new List<IOpenApiAny>
{
new OpenApiString("image"),
};
openApiDocument.Components.Schemas["ToolUseBlock"]!.Properties["type"].Enum = new List<IOpenApiAny>
Type = "object",
Properties = new Dictionary<string, OpenApiSchema>
{
["type"] = new()
{
Enum = new List<IOpenApiAny>
{
new OpenApiString("ping"),
},
Type = "string",
Default = new OpenApiString("ping"),
},
},
Required = new HashSet<string>
{
"type",
},
});
openApiDocument.Components.Schemas["MessageStreamEvent"].OneOf.Add(new OpenApiSchema
{
new OpenApiString("tool_use"),
};
openApiDocument.Components.Schemas["ToolResultBlock"]!.Properties["type"].Enum = new List<IOpenApiAny>
Reference = new OpenApiReference
{
Type = ReferenceType.Schema,
Id = "Ping",
},
});

openApiDocument.Components.SecuritySchemes.Clear();
openApiDocument.Components.SecuritySchemes.Add("ApiKeyAuth", new OpenApiSecurityScheme
{
new OpenApiString("tool_result"),
};
Type = SecuritySchemeType.ApiKey,
In = ParameterLocation.Header,
Name = "x-api-key",
});

openApiDocument.Components.Schemas["TextBlock"].Required.Add("type");
openApiDocument.Components.Schemas["ImageBlock"].Required.Add("type");
openApiDocument.Components.Schemas["ToolUseBlock"].Required.Add("type");
openApiDocument.Components.Schemas["ToolResultBlock"].Required.Add("type");
openApiDocument.SecurityRequirements.Clear();
openApiDocument.SecurityRequirements.Add(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "ApiKeyAuth",
},
},
new List<string>()
}
});

jsonOrYaml = openApiDocument.SerializeAsYaml(OpenApiSpecVersion.OpenApi3_0);
_ = new OpenApiStringReader().Read(jsonOrYaml, out diagnostics);
Expand Down
89 changes: 68 additions & 21 deletions src/libs/Anthropic/AnthropicClient.Streaming.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Headers;
using System.Runtime.CompilerServices;

// ReSharper disable RedundantNameQualifier
Expand All @@ -11,35 +10,39 @@ public partial class AnthropicClient
{
/// <summary>
/// Create a Message<br/>
/// Send a structured list of input messages with text and/or image content, and the<br/>
/// model will generate the next message in the conversation.<br/>
/// The Messages API can be used for either single queries or stateless multi-turn<br/>
/// conversations.
/// Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation.<br/>
/// The Messages API can be used for either single queries or stateless multi-turn conversations.
/// </summary>
/// <param name="anthropicVersion">
/// The version of the Anthropic API you want to use.<br/>
/// Read more about versioning and our version history [here](https://docs.anthropic.com/en/api/versioning).
/// </param>
/// <param name="request"></param>
/// <param name="cancellationToken">The token to cancel the operation with</param>
/// <exception cref="global::System.InvalidOperationException"></exception>
/// <exception cref="global::Anthropic.ApiException"></exception>
public async IAsyncEnumerable<global::Anthropic.MessageStreamEvent> CreateMessageAsStreamAsync(
global::Anthropic.CreateMessageRequest request,
global::Anthropic.CreateMessageParams request,
string? anthropicVersion = default,
[EnumeratorCancellation] global::System.Threading.CancellationToken cancellationToken = default)
{
request = request ?? throw new global::System.ArgumentNullException(nameof(request));
request.Stream = true;

PrepareArguments(
client: HttpClient);
PrepareCreateMessageArguments(
httpClient: HttpClient,
request: request);

var __pathBuilder = new PathBuilder(
path: "/messages",
path: "/v1/messages",
baseUri: HttpClient.BaseAddress);
var __path = __pathBuilder.ToString();
using var __httpRequest = new global::System.Net.Http.HttpRequestMessage(
method: global::System.Net.Http.HttpMethod.Post,
requestUri: new global::System.Uri(__path, global::System.UriKind.RelativeOrAbsolute));
__httpRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream"));
#if NET6_0_OR_GREATER
__httpRequest.Version = global::System.Net.HttpVersion.Version11;
__httpRequest.VersionPolicy = global::System.Net.Http.HttpVersionPolicy.RequestVersionOrHigher;
#endif

foreach (var __authorization in Authorizations)
{
Expand All @@ -56,6 +59,12 @@ public partial class AnthropicClient
__httpRequest.Headers.Add(__authorization.Name, __authorization.Value);
}
}

if (anthropicVersion != default)
{
__httpRequest.Headers.TryAddWithoutValidation("anthropic-version", anthropicVersion.ToString());
}

var __httpRequestContentBody = request.ToJson(JsonSerializerContext);
var __httpRequestContent = new global::System.Net.Http.StringContent(
content: __httpRequestContentBody,
Expand All @@ -66,10 +75,6 @@ public partial class AnthropicClient
PrepareRequest(
client: HttpClient,
request: __httpRequest);
PrepareCreateMessageRequest(
httpClient: HttpClient,
httpRequestMessage: __httpRequest,
request: request);

using var __response = await HttpClient.SendAsync(
request: __httpRequest,
Expand All @@ -79,10 +84,52 @@ public partial class AnthropicClient
ProcessResponse(
client: HttpClient,
response: __response);
ProcessCreateMessageResponse(
httpClient: HttpClient,
httpResponseMessage: __response);

// Error response. See our [errors documentation](https://docs.anthropic.com/en/api/errors) for more details.
if ((int)__response.StatusCode >= 400 && (int)__response.StatusCode <= 499)
{
string? __content_4XX = null;
global::Anthropic.ErrorResponse? __value_4XX = null;
if (ReadResponseAsString)
{
__content_4XX = await __response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
__value_4XX = global::Anthropic.ErrorResponse.FromJson(__content_4XX, JsonSerializerContext);
}
else
{
var __contentStream_4XX = await __response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
__value_4XX = await global::Anthropic.ErrorResponse.FromJsonStreamAsync(__contentStream_4XX, JsonSerializerContext).ConfigureAwait(false);
}

throw new global::Anthropic.ApiException<global::Anthropic.ErrorResponse>(
message: __response.ReasonPhrase ?? string.Empty,
statusCode: __response.StatusCode)
{
ResponseBody = __content_4XX,
ResponseObject = __value_4XX,
ResponseHeaders = global::System.Linq.Enumerable.ToDictionary(
__response.Headers,
h => h.Key,
h => h.Value),
};
}

try
{
__response.EnsureSuccessStatusCode();
}
catch (global::System.Net.Http.HttpRequestException __ex)
{
throw new global::Anthropic.ApiException(
message: __response.ReasonPhrase ?? string.Empty,
innerException: __ex,
statusCode: __response.StatusCode)
{
ResponseHeaders = global::System.Linq.Enumerable.ToDictionary(
__response.Headers,
h => h.Key,
h => h.Value),
};
}

#if NET6_0_OR_GREATER
using var __content = await __response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
Expand Down
Loading

0 comments on commit 0357382

Please sign in to comment.