Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ElevenLabs-DotNet 3.0.0 #49

Merged
merged 25 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -339,3 +339,4 @@ ASALocalRun/
# BeatPulse healthcheck temp database
healthchecksdb
.vscode
*.dubbed.*
38 changes: 20 additions & 18 deletions ElevenLabs-DotNet-Proxy/ElevenLabs-DotNet-Proxy.csproj
Original file line number Diff line number Diff line change
@@ -1,52 +1,54 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<Authors>Stephen Hodgson</Authors>
<Title>ElevenLabs API Proxy</Title>
<Product>ElevenLabs-DotNet-Proxy</Product>
<PackageId>ElevenLabs-DotNet-Proxy</PackageId>
<RootNamespace>ElevenLabs.Proxy</RootNamespace>
<Description>A simple Proxy API gateway for ElevenLabs-DotNet to make authenticated requests from a front end application without exposing your API keys.</Description>
<Copyright>2024</Copyright>
<PackageProjectUrl>https://github.com/RageAgainstThePixel/ElevenLabs-DotNet</PackageProjectUrl>
<RepositoryUrl>https://github.com/RageAgainstThePixel/ElevenLabs-DotNet</RepositoryUrl>
<PackageTags>ElevenLabs, AI, ML, API, api-proxy, proxy, gateway</PackageTags>
<Title>ElevenLabs API Proxy</Title>
<PackageId>ElevenLabs-DotNet-Proxy</PackageId>
<Version>2.2.2</Version>
<RootNamespace>ElevenLabs.Proxy</RootNamespace>
<PackageReleaseNotes>Version 2.2.2
<PackageIcon>ElevenLabsIcon.png</PackageIcon>
<PackageReadmeFile>Readme.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<IncludeSymbols>true</IncludeSymbols>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<Version>3.0.0</Version>
<PackageReleaseNotes>
Version 3.0.0
- Renamed ElevenLabsProxyStartup to ElevenLabsProxy
- Deprecated ValidateAuthentication
Version 2.2.2
- Updated EndpointRouteBuilder with optional route prefix parameter
Version 2.2.1
- Refactor with modern WebApplication builder
- Added ElevenLabs.Proxy.EndpointRouteBuilder
</PackageReleaseNotes>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageReadmeFile>Readme.md</PackageReadmeFile>
<IncludeSymbols>True</IncludeSymbols>
<PackageIcon>ElevenLabsIcon.png</PackageIcon>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ElevenLabs-DotNet\ElevenLabs-DotNet.csproj" />
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<None Include="..\ElevenLabs-DotNet\Assets\ElevenLabsIcon.png">
<Pack>True</Pack>
<Pack>true</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\LICENSE">
<Pack>True</Pack>
<Pack>true</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<None Update="Readme.md">
<Pack>True</Pack>
<Pack>true</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace ElevenLabs.Proxy
{
/// <inheritdoc />
public abstract class AbstractAuthenticationFilter : IAuthenticationFilter
{
/// <inheritdoc />
public abstract void ValidateAuthentication(IHeaderDictionary request);
public virtual void ValidateAuthentication(IHeaderDictionary request) { }

/// <inheritdoc />
public abstract Task ValidateAuthenticationAsync(IHeaderDictionary request);
Expand Down
114 changes: 114 additions & 0 deletions ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Threading.Tasks;

namespace ElevenLabs.Proxy
{
/// <summary>
/// Used in ASP.NET Core WebApps to start your own ElevenLabs web api proxy.
/// </summary>
public class ElevenLabsProxy
{
private ElevenLabsClient elevenLabsClient;
private IAuthenticationFilter authenticationFilter;

/// <summary>
/// Configures the <see cref="ElevenLabsClient"/> and <see cref="IAuthenticationFilter"/> services.
/// </summary>
/// <param name="services"></param>
public void ConfigureServices(IServiceCollection services)
=> SetupServices(services.BuildServiceProvider());

/// <summary>
/// Configures the <see cref="IApplicationBuilder"/> to handle requests and forward them to OpenAI API.
/// </summary>
/// <param name="app"><see cref="IApplicationBuilder"/>.</param>
/// <param name="env"><see cref="IWebHostEnvironment"/>.</param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

SetupServices(app.ApplicationServices);

app.UseHttpsRedirection();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/health", HealthEndpoint);
endpoints.MapElevenLabsEndpoints(elevenLabsClient, authenticationFilter);
});
}

/// <summary>
/// Creates a new <see cref="IHost"/> that acts as a proxy web api for ElevenLabs.
/// </summary>
/// <typeparam name="T"><see cref="IAuthenticationFilter"/> type to use to validate your custom issued tokens.</typeparam>
/// <param name="args">Startup args.</param>
/// <param name="elevenLabsClient"><see cref="ElevenLabsClient"/> with configured <see cref="ElevenLabsAuthentication"/> and <see cref="ElevenLabsClientSettings"/>.</param>
public static IHost CreateDefaultHost<T>(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
=> Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<ElevenLabsProxy>();
webBuilder.ConfigureKestrel(ConfigureKestrel);
})
.ConfigureServices(services =>
{
services.AddSingleton(elevenLabsClient);
services.AddSingleton<IAuthenticationFilter, T>();
}).Build();

/// <summary>
/// Creates a new <see cref="WebApplication"/> that acts as a proxy web api for OpenAI.
/// </summary>
/// <typeparam name="T"><see cref="IAuthenticationFilter"/> type to use to validate your custom issued tokens.</typeparam>
/// <param name="args">Startup args.</param>
/// <param name="elevenLabsClient"><see cref="ElevenLabsClient"/> with configured <see cref="ElevenLabsAuthentication"/> and <see cref="ElevenLabsClientSettings"/>.</param>
public static WebApplication CreateWebApplication<T>(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
{
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(ConfigureKestrel);
builder.Services.AddSingleton(elevenLabsClient);
builder.Services.AddSingleton<IAuthenticationFilter, T>();
var app = builder.Build();
var startup = new ElevenLabsProxy();
startup.Configure(app, app.Environment);
return app;
}

private static void ConfigureKestrel(KestrelServerOptions options)
{
options.AllowSynchronousIO = false;
options.Limits.MinRequestBodyDataRate = null;
options.Limits.MinResponseDataRate = null;
options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(10);
options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(2);
}

private void SetupServices(IServiceProvider serviceProvider)
{
elevenLabsClient = serviceProvider.GetRequiredService<ElevenLabsClient>();
authenticationFilter = serviceProvider.GetRequiredService<IAuthenticationFilter>();
}

private static async Task HealthEndpoint(HttpContext context)
{
// Respond with a 200 OK status code and a plain text message
context.Response.StatusCode = StatusCodes.Status200OK;
const string contentType = "text/plain";
context.Response.ContentType = contentType;
const string content = "OK";
await context.Response.WriteAsync(content);
}
}
}
107 changes: 8 additions & 99 deletions ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxyStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,113 +2,22 @@

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Threading.Tasks;

namespace ElevenLabs.Proxy
{
/// <summary>
/// Used in ASP.NET Core WebApps to start your own ElevenLabs web api proxy.
/// </summary>
[Obsolete("Use ElevenLabsProxy")]
public class ElevenLabsProxyStartup
{
private ElevenLabsClient elevenLabsClient;
private IAuthenticationFilter authenticationFilter;
public void ConfigureServices(IServiceCollection services) { }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { }

/// <summary>
/// Configures the <see cref="ElevenLabsClient"/> and <see cref="IAuthenticationFilter"/> services.
/// </summary>
/// <param name="services"></param>
public void ConfigureServices(IServiceCollection services)
=> SetupServices(services.BuildServiceProvider());
public static IHost CreateDefaultHost<T>(string[] args, ElevenLabsClient elevenLabsClient)
where T : class, IAuthenticationFilter => null;

/// <summary>
/// Configures the <see cref="IApplicationBuilder"/> to handle requests and forward them to OpenAI API.
/// </summary>
/// <param name="app"><see cref="IApplicationBuilder"/>.</param>
/// <param name="env"><see cref="IWebHostEnvironment"/>.</param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

SetupServices(app.ApplicationServices);

app.UseHttpsRedirection();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/health", HealthEndpoint);
endpoints.MapElevenLabsEndpoints(elevenLabsClient, authenticationFilter);
});
}

/// <summary>
/// Creates a new <see cref="IHost"/> that acts as a proxy web api for ElevenLabs.
/// </summary>
/// <typeparam name="T"><see cref="IAuthenticationFilter"/> type to use to validate your custom issued tokens.</typeparam>
/// <param name="args">Startup args.</param>
/// <param name="elevenLabsClient"><see cref="ElevenLabsClient"/> with configured <see cref="ElevenLabsAuthentication"/> and <see cref="ElevenLabsClientSettings"/>.</param>
public static IHost CreateDefaultHost<T>(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
=> Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<ElevenLabsProxyStartup>();
webBuilder.ConfigureKestrel(ConfigureKestrel);
})
.ConfigureServices(services =>
{
services.AddSingleton(elevenLabsClient);
services.AddSingleton<IAuthenticationFilter, T>();
}).Build();

/// <summary>
/// Creates a new <see cref="WebApplication"/> that acts as a proxy web api for OpenAI.
/// </summary>
/// <typeparam name="T"><see cref="IAuthenticationFilter"/> type to use to validate your custom issued tokens.</typeparam>
/// <param name="args">Startup args.</param>
/// <param name="elevenLabsClient"><see cref="ElevenLabsClient"/> with configured <see cref="ElevenLabsAuthentication"/> and <see cref="ElevenLabsClientSettings"/>.</param>
public static WebApplication CreateWebApplication<T>(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
{
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(ConfigureKestrel);
builder.Services.AddSingleton(elevenLabsClient);
builder.Services.AddSingleton<IAuthenticationFilter, T>();
var app = builder.Build();
var startup = new ElevenLabsProxyStartup();
startup.Configure(app, app.Environment);
return app;
}

private static void ConfigureKestrel(KestrelServerOptions options)
{
options.AllowSynchronousIO = false;
options.Limits.MinRequestBodyDataRate = null;
options.Limits.MinResponseDataRate = null;
options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(10);
options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(2);
}

private void SetupServices(IServiceProvider serviceProvider)
{
elevenLabsClient = serviceProvider.GetRequiredService<ElevenLabsClient>();
authenticationFilter = serviceProvider.GetRequiredService<IAuthenticationFilter>();
}

private static async Task HealthEndpoint(HttpContext context)
{
// Respond with a 200 OK status code and a plain text message
context.Response.StatusCode = StatusCodes.Status200OK;
const string contentType = "text/plain";
context.Response.ContentType = contentType;
const string content = "OK";
await context.Response.WriteAsync(content);
}
public static WebApplication CreateWebApplication<T>(string[] args, ElevenLabsClient elevenLabsClient)
where T : class, IAuthenticationFilter => null;
}
}
}
23 changes: 11 additions & 12 deletions ElevenLabs-DotNet-Proxy/Proxy/EndpointRouteBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,10 @@ async Task HandleRequest(HttpContext httpContext, string endpoint)
{
try
{
#pragma warning disable CS0618 // Type or member is obsolete
// ReSharper disable once MethodHasAsyncOverload
// just in case either method is implemented we call it twice.
authenticationFilter.ValidateAuthentication(httpContext.Request.Headers);
#pragma warning restore CS0618 // Type or member is obsolete
await authenticationFilter.ValidateAuthenticationAsync(httpContext.Request.Headers);

var method = new HttpMethod(httpContext.Request.Method);
Expand All @@ -70,6 +71,11 @@ async Task HandleRequest(HttpContext httpContext, string endpoint)
using var request = new HttpRequestMessage(method, uri);
request.Content = new StreamContent(httpContext.Request.Body);

if (httpContext.Request.Body.CanSeek)
{
httpContext.Request.Body.Position = 0;
}

if (httpContext.Request.ContentType != null)
{
request.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse(httpContext.Request.ContentType);
Expand All @@ -80,21 +86,13 @@ async Task HandleRequest(HttpContext httpContext, string endpoint)

foreach (var (key, value) in proxyResponse.Headers)
{
if (excludedHeaders.Contains(key))
{
continue;
}

if (excludedHeaders.Contains(key)) { continue; }
httpContext.Response.Headers[key] = value.ToArray();
}

foreach (var (key, value) in proxyResponse.Content.Headers)
{
if (excludedHeaders.Contains(key))
{
continue;
}

if (excludedHeaders.Contains(key)) { continue; }
httpContext.Response.Headers[key] = value.ToArray();
}

Expand All @@ -103,6 +101,7 @@ async Task HandleRequest(HttpContext httpContext, string endpoint)

if (httpContext.Response.ContentType.Equals(streamingContent))
{
using var reader = new StreamReader(await request.Content.ReadAsStreamAsync());
var stream = await proxyResponse.Content.ReadAsStreamAsync();
await WriteServerStreamEventsAsync(httpContext, stream);
}
Expand All @@ -119,7 +118,7 @@ async Task HandleRequest(HttpContext httpContext, string endpoint)
catch (Exception e)
{
httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
var response = JsonSerializer.Serialize(new { error = new { e.Message, e.StackTrace } });
var response = JsonSerializer.Serialize(new { error = new { message = e.Message, stackTrace = e.StackTrace } });
await httpContext.Response.WriteAsync(response);
}

Expand Down
Loading
Loading