Skip to content

Commit

Permalink
ElevenLabs-DotNet 3.0.0 (#49)
Browse files Browse the repository at this point in the history
- Updated plugin to .net 8
- Added ability to specify fully customizable domain proxies
- Added environment variable parsing for ELEVENLABS_API_KEY
- Added SoundEffects API endpoints
- Added Dubbing API endpoints
- Updated default models
- Fixed adding and editing Voices

---------

Co-authored-by: Austin Hale <[email protected]>
  • Loading branch information
StephenHodgson and austinbhale authored Sep 2, 2024
1 parent c7a2a95 commit 1f08fd3
Show file tree
Hide file tree
Showing 56 changed files with 2,133 additions and 436 deletions.
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
4 changes: 2 additions & 2 deletions ElevenLabs-DotNet-Proxy/Proxy/AbstractAuthenticationFilter.cs
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

0 comments on commit 1f08fd3

Please sign in to comment.