main
bicijinlian 1 week ago
parent c415864fab
commit 4dde98d864

@ -16,8 +16,9 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.AI" Version="9.7.1" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.7.1-preview.1.25365.4" />
<PackageReference Include="Microsoft.Extensions.AI" Version="9.8.0" />
<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="Moq" Version="4.20.72" />
<PackageReference Include="OllamaSharp" Version="5.3.4" />

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

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

@ -21,7 +21,7 @@ namespace OllamaStudy.UseExtensionsAI
#endregion
#region Startup 风格
/// <summary>
/// (可选) 创建IHostBuilder
/// </summary>
@ -31,9 +31,9 @@ namespace OllamaStudy.UseExtensionsAI
return Host.CreateDefaultBuilder();
}
public void ConfigureHost(IHostBuilder hostBuilder)
public void ConfigureHost(IHostBuilder hostBuilder)
{
hostBuilder.ConfigureAppConfiguration((hostBuilder,configBuilder) =>
hostBuilder.ConfigureAppConfiguration((hostBuilder, configBuilder) =>
{
configBuilder
.SetBasePath(Directory.GetCurrentDirectory())
@ -63,12 +63,12 @@ namespace OllamaStudy.UseExtensionsAI
.Configure<OllamaServerOption>(context.Configuration.GetRequiredSection("OllamaServer"));
services
.AddScoped<IOllamaApiClient>(provider =>
.AddScoped<IOllamaApiClient>(provider =>
{
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 =>
{
@ -76,10 +76,10 @@ namespace OllamaStudy.UseExtensionsAI
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 =>
{
@ -87,14 +87,14 @@ namespace OllamaStudy.UseExtensionsAI
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 客户端是线程安全的,可安全的注册为单例
.AddKeyedSingleton<OpenAI.Chat.ChatClient>("OpenAIChatClient",(provider,obj) =>
.AddKeyedSingleton<OpenAI.Chat.ChatClient>("OpenAIChatClient", (provider, obj) =>
{
var options = provider.GetRequiredService<IOptionsMonitor<OllamaServerOption>>().CurrentValue;
@ -123,7 +123,7 @@ namespace OllamaStudy.UseExtensionsAI
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
.AddKeyedSingleton<OpenAIClient>("ZipuAPIClient", (provider, obj) =>
@ -135,6 +135,72 @@ namespace OllamaStudy.UseExtensionsAI
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
}

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

Loading…
Cancel
Save