main
bicijinlian 1 week ago
parent c415864fab
commit 4dde98d864

@ -16,8 +16,9 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.AI" Version="9.7.1" /> <PackageReference Include="Microsoft.Extensions.AI" Version="9.8.0" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.7.1-preview.1.25365.4" /> <PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.8.0-preview.1.25412.6" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.8" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Moq" Version="4.20.72" /> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="OllamaSharp" Version="5.3.4" /> <PackageReference Include="OllamaSharp" Version="5.3.4" />

@ -12,17 +12,32 @@ namespace OllamaStudy.UseExtensionsAI
/// </summary> /// </summary>
public class OpenAIAPITest public class OpenAIAPITest
{ {
private ITestOutputHelper _output; private readonly ITestOutputHelper _output;
private IOptionsMonitor<OllamaServerOption> _ollamaOptionsMonitor; private readonly IOptionsMonitor<OllamaServerOption> _ollamaOptionsMonitor;
private OpenAIClient _defaultOpenAIClient; private readonly IHttpClientFactory _httpClientFactory;
private ChatClient _chatClient; private readonly HttpClient _defaultHttpClient;
private readonly HttpClient _ollamaHttpClient;
public OpenAIAPITest(ITestOutputHelper outputHelper, OpenAIClient defaultOpenAIClient, IOptionsMonitor<OllamaServerOption> ollamaOptionsMonitor) private readonly HttpClient _uiUiAPIHttpClient;
private readonly HttpClient _bailianHttpClient;
private readonly HttpClient _zipuHttpClient;
public OpenAIAPITest
(
ITestOutputHelper outputHelper,
OpenAIClient defaultOpenAIClient,
IOptionsMonitor<OllamaServerOption> ollamaOptionsMonitor,
IHttpClientFactory httpClientFactory
)
{ {
_output = outputHelper; _output = outputHelper;
_defaultOpenAIClient = defaultOpenAIClient;
_ollamaOptionsMonitor = ollamaOptionsMonitor; _ollamaOptionsMonitor = ollamaOptionsMonitor;
_chatClient = _defaultOpenAIClient.GetChatClient(_ollamaOptionsMonitor.CurrentValue.Model); _httpClientFactory = httpClientFactory;
_defaultHttpClient = _httpClientFactory.CreateClient("OpenAIHttpClient");
_ollamaHttpClient = _httpClientFactory.CreateClient("OllamaHttpClient");
_uiUiAPIHttpClient = _httpClientFactory.CreateClient("UiUiAPIHttpClient");
_bailianHttpClient = _httpClientFactory.CreateClient("BailianHttpClient");
_zipuHttpClient = _httpClientFactory.CreateClient("ZiPuHttpClient");
} }
#region 各种业务Client #region 各种业务Client
@ -32,74 +47,52 @@ namespace OllamaStudy.UseExtensionsAI
[Fact] [Fact]
public void GetClients_Test() public void GetClients_Test()
{ {
#pragma warning disable OPENAI001 Assert.NotNull(_defaultHttpClient);
Assert.NotNull(_defaultOpenAIClient); Assert.NotNull(_ollamaHttpClient);
Assert.NotNull(_uiUiAPIHttpClient);
//音频客户端 Assert.NotNull(_bailianHttpClient);
var audioClient = _defaultOpenAIClient.GetAudioClient(_ollamaOptionsMonitor.CurrentValue.Model); Assert.NotNull(_zipuHttpClient);
Assert.NotNull(audioClient); }
#endregion
//聊天客户端
var chatClient = _defaultOpenAIClient.GetChatClient(_ollamaOptionsMonitor.CurrentValue.Model);
Assert.NotNull(chatClient);
//嵌入客户端
var embeddingClient = _defaultOpenAIClient.GetEmbeddingClient(_ollamaOptionsMonitor.CurrentValue.Model);
Assert.NotNull(embeddingClient);
//图像客户端
var imageClient = _defaultOpenAIClient.GetImageClient(_ollamaOptionsMonitor.CurrentValue.Model);
Assert.NotNull(imageClient);
//微调客户端
var moderationClient = _defaultOpenAIClient.GetModerationClient(_ollamaOptionsMonitor.CurrentValue.Model);
Assert.NotNull(moderationClient);
//文件客户端
var openAIFileClient = _defaultOpenAIClient.GetOpenAIFileClient();
Assert.NotNull(openAIFileClient);
//模型客户端 #region 音频
var modelClient = _defaultOpenAIClient.GetOpenAIModelClient(); [Fact]
Assert.NotNull(modelClient); public async Task Audio_Test()
{
var requetData = new
{
//语音模型
model = "gpt-4o-mini-tts",
//助手客户端(仅评估) //要生成音频的文本。最大长度为4096个字符。
var assistantClient = _defaultOpenAIClient.GetAssistantClient(); input = "你好,上海今天的天气非常好,很适合户外游玩!",
Assert.NotNull(assistantClient);
//批量客户端(仅评估) //生成音频时使用的语音。支持的语音有:alloy、echo、fable、onyx、nova 和 shimmer。
var batchClient = _defaultOpenAIClient.GetBatchClient(); voice = "alloy",
Assert.NotNull(batchClient);
//评估客户端(仅评估) //默认为 mp3 音频的格式。支持的格式有:mp3、opus、aac 和 flac。
var evaluationClient = _defaultOpenAIClient.GetEvaluationClient(); response_format = "mp3",
Assert.NotNull(evaluationClient);
//微调客户端(仅评估) //默认为 1 生成的音频速度。选择0.25到4.0之间的值。1.0是默认值。
var FineTuningClient = _defaultOpenAIClient.GetFineTuningClient(); speed = 1.0f,
Assert.NotNull(FineTuningClient); };
//响应客户端(仅评估) using var requestMessage = new HttpRequestMessage(HttpMethod.Post, "https://sg.uiuiapi.com/v1/audio/speech")
var openAIResponseClient = _defaultOpenAIClient.GetOpenAIResponseClient(_ollamaOptionsMonitor.CurrentValue.Model); {
Assert.NotNull(openAIResponseClient); Content = JsonContent.Create(requetData)
};
//实时客户端(仅评估) var responseMessage = await _uiUiAPIHttpClient.SendAsync(requestMessage);
#pragma warning disable OPENAI002 responseMessage.EnsureSuccessStatusCode();
var realtimeClient = _defaultOpenAIClient.GetRealtimeClient();
Assert.NotNull(realtimeClient);
#pragma warning restore OPENAI002
//向量存储客户端(仅评估) //处理响应
var vectorStoreClient = _defaultOpenAIClient.GetVectorStoreClient(); var responseObject = await responseMessage.Content.ReadAsByteArrayAsync();
Assert.NotNull(vectorStoreClient);
#pragma warning restore OPENAI001 using FileStream stream = File.OpenWrite($"{Guid.NewGuid()}.mp3");
stream.Write(responseObject);
} }
#endregion #endregion
#region 音频
#endregion
#region 聊天 #region 聊天
#endregion #endregion
@ -126,11 +119,7 @@ namespace OllamaStudy.UseExtensionsAI
[Fact] [Fact]
public void List_Models_Test() public void List_Models_Test()
{ {
var modelClient = _defaultOpenAIClient.GetOpenAIModelClient();
OpenAI.Models.OpenAIModelCollection openAIModelCollection = modelClient.GetModels().Value;
_output.WriteLine($"Ollama服务中共有{openAIModelCollection.Count()}个模型,包括[{string.Join(",", openAIModelCollection)}]");
} }
#endregion #endregion

@ -573,7 +573,7 @@ public class OpenAISdkTest
They can serve as accents, adding contrast and texture. They can serve as accents, adding contrast and texture.
"""; """;
ImageGenerationOptions options = new() OpenAI.Images.ImageGenerationOptions options = new()
{ {
Quality = GeneratedImageQuality.High, Quality = GeneratedImageQuality.High,
Size = GeneratedImageSize.W1792xH1024, Size = GeneratedImageSize.W1792xH1024,

@ -21,7 +21,7 @@ namespace OllamaStudy.UseExtensionsAI
#endregion #endregion
#region Startup 风格 #region Startup 风格
/// <summary> /// <summary>
/// (可选) 创建IHostBuilder /// (可选) 创建IHostBuilder
/// </summary> /// </summary>
@ -31,9 +31,9 @@ namespace OllamaStudy.UseExtensionsAI
return Host.CreateDefaultBuilder(); return Host.CreateDefaultBuilder();
} }
public void ConfigureHost(IHostBuilder hostBuilder) public void ConfigureHost(IHostBuilder hostBuilder)
{ {
hostBuilder.ConfigureAppConfiguration((hostBuilder,configBuilder) => hostBuilder.ConfigureAppConfiguration((hostBuilder, configBuilder) =>
{ {
configBuilder configBuilder
.SetBasePath(Directory.GetCurrentDirectory()) .SetBasePath(Directory.GetCurrentDirectory())
@ -63,12 +63,12 @@ namespace OllamaStudy.UseExtensionsAI
.Configure<OllamaServerOption>(context.Configuration.GetRequiredSection("OllamaServer")); .Configure<OllamaServerOption>(context.Configuration.GetRequiredSection("OllamaServer"));
services services
.AddScoped<IOllamaApiClient>(provider => .AddScoped<IOllamaApiClient>(provider =>
{ {
var options = provider.GetRequiredService<IOptionsMonitor<OllamaServerOption>>().CurrentValue; var options = provider.GetRequiredService<IOptionsMonitor<OllamaServerOption>>().CurrentValue;
return new OllamaApiClient(options.OllamaServerUrl,options.Model); return new OllamaApiClient(options.OllamaServerUrl, options.Model);
}) })
.AddScoped<OpenAI.OpenAIClient>(provider => .AddScoped<OpenAI.OpenAIClient>(provider =>
{ {
@ -76,10 +76,10 @@ namespace OllamaStudy.UseExtensionsAI
var openAIClientOptions = new OpenAIClientOptions() var openAIClientOptions = new OpenAIClientOptions()
{ {
Endpoint = new Uri(new Uri(options.OllamaServerUrl),"v1") Endpoint = new Uri(new Uri(options.OllamaServerUrl), "v1")
}; };
return new OpenAIClient(new ApiKeyCredential("nokey"),openAIClientOptions); return new OpenAIClient(new ApiKeyCredential("nokey"), openAIClientOptions);
}) })
.AddScoped<OpenAI.Chat.ChatClient>(provider => .AddScoped<OpenAI.Chat.ChatClient>(provider =>
{ {
@ -87,14 +87,14 @@ namespace OllamaStudy.UseExtensionsAI
var openAIClientOptions = new OpenAIClientOptions() var openAIClientOptions = new OpenAIClientOptions()
{ {
Endpoint = new Uri(new Uri(options.OllamaServerUrl),"v1") Endpoint = new Uri(new Uri(options.OllamaServerUrl), "v1")
}; };
return new ChatClient(options.Model,new ApiKeyCredential("nokey"),openAIClientOptions); return new ChatClient(options.Model, new ApiKeyCredential("nokey"), openAIClientOptions);
}) })
//OpenAI 客户端是线程安全的,可安全的注册为单例 //OpenAI 客户端是线程安全的,可安全的注册为单例
.AddKeyedSingleton<OpenAI.Chat.ChatClient>("OpenAIChatClient",(provider,obj) => .AddKeyedSingleton<OpenAI.Chat.ChatClient>("OpenAIChatClient", (provider, obj) =>
{ {
var options = provider.GetRequiredService<IOptionsMonitor<OllamaServerOption>>().CurrentValue; var options = provider.GetRequiredService<IOptionsMonitor<OllamaServerOption>>().CurrentValue;
@ -123,7 +123,7 @@ namespace OllamaStudy.UseExtensionsAI
Endpoint = new Uri("https://dashscope.aliyuncs.com/compatible-mode/v1") Endpoint = new Uri("https://dashscope.aliyuncs.com/compatible-mode/v1")
}; };
return new OpenAIClient(new ApiKeyCredential(""), openAIClientOptions); return new OpenAIClient(new ApiKeyCredential("sk-0122f39e383546b9a0999f70b9ef99e3"), openAIClientOptions);
}) })
//智谱 OpenAI兼容API //智谱 OpenAI兼容API
.AddKeyedSingleton<OpenAIClient>("ZipuAPIClient", (provider, obj) => .AddKeyedSingleton<OpenAIClient>("ZipuAPIClient", (provider, obj) =>
@ -135,6 +135,72 @@ namespace OllamaStudy.UseExtensionsAI
return new OpenAIClient(new ApiKeyCredential("397a799102a6453282da8abb2a1b2581.8fTMHZGRkPHJya4R"), openAIClientOptions); return new OpenAIClient(new ApiKeyCredential("397a799102a6453282da8abb2a1b2581.8fTMHZGRkPHJya4R"), openAIClientOptions);
}); });
//HttpClient 注册
//Ollama HttpClient
services
.AddHttpClient("OllamaHttpClient", (provider, client) =>
{
var options = provider.GetRequiredService<IOptionsMonitor<OllamaServerOption>>().CurrentValue;
client.BaseAddress = new Uri(options.OllamaServerUrl);
client.DefaultRequestHeaders.Add("Authorization", $"Bearer nokey");
client.Timeout = TimeSpan.FromSeconds(60);
});
//OpenAI HttpClient
services
.AddHttpClient("OpenAIHttpClient", (provider, client) =>
{
var options = provider.GetRequiredService<IOptionsMonitor<OllamaServerOption>>().CurrentValue;
client.BaseAddress = new Uri(options.OllamaServerUrl);
client.DefaultRequestHeaders.Add("Authorization", $"Bearer nokey");
client.Timeout = TimeSpan.FromSeconds(60);
});
//UiUiAPI HttpClient
services
.AddHttpClient("UiUiAPIHttpClient", (provider, client) =>
{
client.BaseAddress = new Uri("https://sg.uiuiapi.com/v1");
client.DefaultRequestHeaders.Add("Authorization", $"Bearer sk-4azuOUkbzNGP22pQkND8ad1vZl7ladwBQyqGKlWWZyxYgX1L");
//client.DefaultRequestHeaders.Add("Content-Type", "application/json");
client.Timeout = TimeSpan.FromSeconds(60);
})
.UseSocketsHttpHandler((socketHttpHandeler, serviceProvider) =>
{
//配置请求代理:请求走 Fiddler 代理,便于调试
socketHttpHandeler.Proxy = new System.Net.WebProxy("http://127.0.0.1:8888");
});
//阿里百炼 HttpClient
services
.AddHttpClient("BailianHttpClient", (provider, client) =>
{
//var options = provider.GetRequiredService<IOptionsMonitor<OllamaServerOption>>().CurrentValue;
client.BaseAddress = new Uri("https://dashscope.aliyuncs.com/compatible-mode/v1");
client.DefaultRequestHeaders.Add("Authorization", $"Bearer sk-0122f39e383546b9a0999f70b9ef99e3");
client.Timeout = TimeSpan.FromSeconds(60);
});
//智谱 HttpClient
services
.AddHttpClient("ZiPuHttpClient", (provider, client) =>
{
//var options = provider.GetRequiredService<IOptionsMonitor<OllamaServerOption>>().CurrentValue;
client.BaseAddress = new Uri("https://open.bigmodel.cn/api/paas/v4/");
client.DefaultRequestHeaders.Add("Authorization", $"Bearer 397a799102a6453282da8abb2a1b2581.8fTMHZGRkPHJya4R");
client.Timeout = TimeSpan.FromSeconds(60);
})
.UseSocketsHttpHandler((socketHttpHandeler, serviceProvider) =>
{
//配置请求代理:请求走 Fiddler 代理,便于调试
socketHttpHandeler.Proxy = new System.Net.WebProxy("http://127.0.0.1:8888");
});
} }
#endregion #endregion
} }

@ -889,45 +889,45 @@ namespace OllamaStudy.UseOllamaSharp
} }
/// <summary> /// <summary>
/// 结构化输出对话请求(没有直接实现) /// 结构化输出对话请求
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Fact] [Fact]
public void ChatRequest_StructuredOutputs_Test() public async Task ChatRequest_StructuredOutputs_Test()
{ {
//var chatRequest = new ChatRequest() var chatRequest = new ChatRequest()
//{ {
// Messages = new[] Messages = new Message[]
// { {
// new Message() new Message()
// { {
// Role = ChatRole.User, Role = ChatRole.User,
// Content = "Ollama is 22 years old and busy saving the world. Return a JSON object with the age and availability.", Content = "Ollama is 22 years old and busy saving the world. Return a JSON object with the age and availability.",
// } },
// }, },
// Think = false, Think = false,
// //(可选)禁用流式处理响应 //(可选)禁用流式处理响应
// Stream = false, Stream = false,
// Format = new Format = new
// { {
// type = "object", type = "object",
// properties = new { age = new { type = "integer" }, available = new { type = "boolean" } }, properties = new { age = new { type = "integer" }, available = new { type = "boolean" } },
// required = new string[] { "age", "available" } required = new string[] { "age", "available" }
// }, },
//}; };
//var chatDoneResponse = await _ollamaApiClient.ChatAsync(chatRequest).StreamToEndAsync(); var chatDoneResponse = await _ollamaApiClient.ChatAsync(chatRequest).StreamToEndAsync();
//_output.WriteLine(chatDoneResponse?.Message.Content); _output.WriteLine(chatDoneResponse?.Message.Content);
//var jsonObject = new { age = 0, available = false }; var jsonObject = new { age = 0, available = false };
//var responseObject = Newtonsoft.Json.JsonConvert.DeserializeAnonymousType(chatDoneResponse?.Message.Content ?? "{}", jsonObject); var responseObject = Newtonsoft.Json.JsonConvert.DeserializeAnonymousType(chatDoneResponse?.Message.Content ?? "{}", jsonObject);
//Assert.NotNull(responseObject); Assert.NotNull(responseObject);
//Assert.Equal(22, responseObject.age); Assert.Equal(22, responseObject.age);
//Assert.True(responseObject.available); Assert.True(responseObject.available);
} }
/// <summary> /// <summary>

Loading…
Cancel
Save