-
-
Notifications
You must be signed in to change notification settings - Fork 87
/
WhisperGgmlDownloader.cs
140 lines (125 loc) · 5.96 KB
/
WhisperGgmlDownloader.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Licensed under the MIT license: https://opensource.org/licenses/MIT
using System.IO.Compression;
namespace Whisper.net.Ggml;
public static class WhisperGgmlDownloader
{
private static readonly Lazy<HttpClient> httpClient = new(() => new HttpClient() { Timeout = Timeout.InfiniteTimeSpan });
/// <summary>
/// Gets the download stream for the model
/// </summary>
/// <param name="type">The type of the model which needs to be downloaded.</param>
/// <param name="quantization">The quantization of the model.</param>
/// <param name="cancellationToken">A cancellation token used to cancell the request to huggingface.</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static async Task<Stream> GetGgmlModelAsync(GgmlType type, QuantizationType quantization = QuantizationType.NoQuantization, CancellationToken cancellationToken = default)
{
var subdirectory = GetQuantizationSubdirectory(quantization);
var modelName = GetModelName(type);
var url = $"https://huggingface.co/sandrohanea/whisper.net/resolve/v3/{subdirectory}/{modelName}.bin";
var request = new HttpRequestMessage(HttpMethod.Get, url);
var response = await httpClient.Value.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
response.EnsureSuccessStatusCode();
#if NETSTANDARD
return await response.Content.ReadAsStreamAsync();
#else
return await response.Content.ReadAsStreamAsync(cancellationToken);
#endif
}
/// <summary>
/// Gets the download stream for the OpenVino model, which is a zip file.
/// </summary>
/// <param name="type">The type of the model which needs to be downloaded.</param>
/// <param name="cancellationToken">A cancellation token used to stop the request to huggingface.</param>
/// <returns></returns>
public static async Task<Stream> GetEncoderOpenVinoModelAsync(GgmlType type, CancellationToken cancellationToken = default)
{
var modelName = GetModelName(type);
var url = $"https://huggingface.co/sandrohanea/whisper.net/resolve/v3/openvino/{modelName}-encoder.zip";
var request = new HttpRequestMessage(HttpMethod.Get, url);
var response = await httpClient.Value.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
response.EnsureSuccessStatusCode();
#if NETSTANDARD
return await response.Content.ReadAsStreamAsync();
#else
return await response.Content.ReadAsStreamAsync(cancellationToken);
#endif
}
/// <summary>
/// Gets the manifest file for the OpenVino model.
/// </summary>
/// <param name="type"> The type of the model which needs to be loaded</param>
/// <returns></returns>
public static string GetOpenVinoManifestFileName(GgmlType type)
{
var modelName = GetModelName(type);
return $"{modelName}-encoder.xml";
}
/// <summary>
/// Gets the download stream for the CoreML model, which is a zip file.
/// </summary>
/// <param name="type">The type of the model which needs to be downloaded.</param>
/// <param name="cancellationToken">A cancellation token used to stop the request to huggingface.</param>
/// <remarks>
/// Needs to be extracted on in the same directory as the ggml model, also ggml model needs to be loaded using file path, not stream.
/// </remarks>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static async Task<Stream> GetEncoderCoreMLModelAsync(GgmlType type, CancellationToken cancellationToken = default)
{
var modelName = GetModelName(type);
var url = $"https://huggingface.co/sandrohanea/whisper.net/resolve/v3/coreml/{modelName}-encoder.zip";
var request = new HttpRequestMessage(HttpMethod.Get, url);
var response = await httpClient.Value.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
response.EnsureSuccessStatusCode();
#if NETSTANDARD
return await response.Content.ReadAsStreamAsync();
#else
return await response.Content.ReadAsStreamAsync(cancellationToken);
#endif
}
/// <summary>
/// Extracts the given zip stream to the given path.
/// </summary>
/// <param name="zipStream">The zip stream to be extracted.</param>
/// <param name="path">The path.</param>
/// <remarks>
/// In order to work, you'll need to provide the same path as the ggml model.
/// </remarks>
/// <returns></returns>
public static async Task ExtractToPath(this Task<Stream> zipStream, string path)
{
using var zipArchive = new ZipArchive(await zipStream, ZipArchiveMode.Read);
zipArchive.ExtractToDirectory(path);
}
private static string GetModelName(GgmlType type)
{
return type switch
{
GgmlType.Tiny => "ggml-tiny",
GgmlType.TinyEn => "ggml-tiny.en",
GgmlType.Base => "ggml-base",
GgmlType.BaseEn => "ggml-base.en",
GgmlType.Small => "ggml-small",
GgmlType.SmallEn => "ggml-small.en",
GgmlType.Medium => "ggml-medium",
GgmlType.MediumEn => "ggml-medium.en",
GgmlType.LargeV1 => "ggml-large-v1",
GgmlType.LargeV2 => "ggml-large-v2",
GgmlType.LargeV3 => "ggml-large-v3",
GgmlType.LargeV3Turbo => "ggml-large-v3-turbo",
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
}
private static string GetQuantizationSubdirectory(QuantizationType quantization)
{
return quantization switch
{
QuantizationType.NoQuantization => "classic",
QuantizationType.Q4_0 => "q4_0",
QuantizationType.Q4_1 => "q4_1",
QuantizationType.Q5_0 => "q5_0",
QuantizationType.Q5_1 => "q5_1",
QuantizationType.Q8_0 => "q8_0",
_ => throw new ArgumentOutOfRangeException(nameof(quantization), quantization, null)
};
}
}