Implemented the transcription API (#623)
Build and Release / Read metadata (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-01-11 16:02:28 +01:00
1 parent b94614b0ab
commit 529986837e
42 files changed
+700 -138

No files matched your search

@@ -1,4 +1,5 @@
using System.Net;
using System.Net.Http.Headers;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Serialization;
@@ -6,10 +7,15 @@ using System.Text.Json.Serialization;
using AIStudio.Chat;
using AIStudio.Provider.Anthropic;
using AIStudio.Provider.OpenAI;
using AIStudio.Provider.SelfHosted;
using AIStudio.Settings;
using AIStudio.Tools.MIME;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.Rust;
using AIStudio.Tools.Services;
using Host = AIStudio.Provider.SelfHosted.Host;
namespace AIStudio.Provider;
/// <summary>
@@ -89,6 +95,9 @@ public abstract class BaseProvider : IProvider, ISecretId
/// <inheritdoc />
public abstract IAsyncEnumerable<ImageURL> StreamImageCompletion(Model imageModel, string promptPositive, string promptNegative = FilterOperator.String.Empty, ImageURL referenceImageURL = default, CancellationToken token = default);
/// <inheritdoc />
public abstract Task<string> TranscribeAudioAsync(Model transcriptionModel, string audioFilePath, SettingsManager settingsManager, CancellationToken token = default);
/// <inheritdoc />
public abstract Task<IEnumerable<Model>> GetTextModels(string? apiKeyProvisional = null, CancellationToken token = default);
@@ -536,6 +545,78 @@ public abstract class BaseProvider : IProvider, ISecretId
streamReader.Dispose();
}
protected async Task<string> PerformStandardTranscriptionRequest(RequestedSecret requestedSecret, Model transcriptionModel, string audioFilePath, Host host = Host.NONE, CancellationToken token = default)
{
try
{
using var form = new MultipartFormDataContent();
var mimeType = Builder.FromFilename(audioFilePath);
await using var fileStream = File.OpenRead(audioFilePath);
using var fileContent = new StreamContent(fileStream);
fileContent.Headers.ContentType = new MediaTypeHeaderValue(mimeType);
form.Add(fileContent, "file", Path.GetFileName(audioFilePath));
form.Add(new StringContent(transcriptionModel.Id), "model");
using var request = new HttpRequestMessage(HttpMethod.Post, host.TranscriptionURL());
request.Content = form;
// Handle the authorization header based on the provider:
switch (this.Provider)
{
case LLMProviders.SELF_HOSTED:
if(requestedSecret.Success)
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
break;
case LLMProviders.FIREWORKS:
if(!requestedSecret.Success)
{
this.logger.LogError("No valid API key available for transcription request.");
return string.Empty;
}
request.Headers.Add("Authorization", await requestedSecret.Secret.Decrypt(ENCRYPTION));
break;
default:
if(!requestedSecret.Success)
{
this.logger.LogError("No valid API key available for transcription request.");
return string.Empty;
}
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await requestedSecret.Secret.Decrypt(ENCRYPTION));
break;
}
using var response = await this.httpClient.SendAsync(request, token);
var responseBody = response.Content.ReadAsStringAsync(token).Result;
if (!response.IsSuccessStatusCode)
{
this.logger.LogError("Transcription request failed with status code {ResponseStatusCode} and body: '{ResponseBody}'.", response.StatusCode, responseBody);
return string.Empty;
}
var transcriptionResponse = JsonSerializer.Deserialize<TranscriptionResponse>(responseBody, JSON_SERIALIZER_OPTIONS);
if(transcriptionResponse is null)
{
this.logger.LogError("Was not able to deserialize the transcription response.");
return string.Empty;
}
return transcriptionResponse.Text;
}
catch (Exception e)
{
this.logger.LogError("Failed to perform transcription request: '{Message}'.", e.Message);
return string.Empty;
}
}
/// <summary>
/// Parse and convert API parameters from a provided JSON string into a dictionary,
/// optionally merging additional parameters and removing specific keys.