Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Init package with a support of API Keys #3

Merged
merged 9 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**

Steps to reproduce the behavior.
Ideally, describes :
* Json input:
```json
{
"resultField1": 132.46,
"resultField2": true
}
```
* Json transformation:
```json
{
"resultField1": "$.field1->ToInteger()",
"resultField2": "$.field2"
}
```
* Json result:
```json
{
"resultField1": "hello",
"resultField2": ""
}
```
* Json expected result:
```json
{
"resultField1": 132,
"resultField2": true
}
```

**Expected behavior**
A clear and concise description of what you expected to happen.

**Additional context**
Add any other context about the problem here.
10 changes: 10 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: Feature request
about: Create a feature request
title: ''
labels:
assignees: ''

---

Describe the feature here.
19 changes: 19 additions & 0 deletions .github/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- enhancement
- bug
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: .NET

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
inputs:
publishToNuget:
description: 'Publish to nuget'
required: true
default: true
type: boolean

jobs:
dotnet:
uses: mathieumack/MyGithubActions/.github/workflows/dotnetlib.yml@main
with:
publishToNuget: ${{ github.event.inputs.publishToNuget == true }}
secrets:
NUGETPACKAGEIDENTIFIER: ${{ secrets.NUGETPACKAGEIDENTIFIER }}
NUGETAPIKEY: ${{ secrets.NUGETAPIKEY }}
SONAR_ORGANIZATION_CODE: ${{ secrets.SONAR_ORGANIZATION_CODE }}
SONAR_PROJECT_CODE: ${{ secrets.SONAR_PROJECT_CODE }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel;
using MDev.Dotnet.SemanticKernel.Connectors.AzureAIStudio.Phi3.Diagnostics;
using System.Net.Http.Headers;
using Microsoft.Extensions.Logging;
using Azure.Identity;
using Microsoft.Extensions.Http.Logging;

namespace MDev.Dotnet.SemanticKernel.Connectors.AzureAIStudio.Phi3;

public static class AIStudioMaaSPhi3ServiceCollectionExtensions
{
/// <summary>
/// Adds the Azure OpenAI chat completion service to the list.
/// </summary>
/// <param name="builder">The <see cref="IKernelBuilder"/> instance to augment.</param>
/// <param name="endpoint">Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="apiKey">Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="serviceId">A local identifier for the given AI service</param>
/// <returns>The same instance as <paramref name="builder"/>.</returns>
public static IKernelBuilder AddAzureAIStudioPhi3ChatCompletion(
this IKernelBuilder builder,
string endpoint,
string apiKey,
string? serviceId = null)
{
Verify.NotNull(builder);
Verify.NotNullOrWhiteSpace(endpoint);
Verify.NotNullOrWhiteSpace(apiKey);

var deploymentIdenfifier = Guid.NewGuid().ToString();

builder.Services.AddHttpClient($"http--{deploymentIdenfifier}");

Func<IServiceProvider, object?, AzureAIStudioMaaSPhi3ChatCompletionService> factory = (serviceProvider, _) =>
{
var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
var httpClient = httpClientFactory.CreateClient($"http-{deploymentIdenfifier}");

var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
var logger = loggerFactory?.CreateLogger(typeof(AzureAIStudioMaaSPhi3ChatCompletionService));

var client = new AzureAIStudioMaaSPhi3ChatCompletionService(new Uri(endpoint),
new Azure.AzureKeyCredential(apiKey),
new(),
httpClient,
logger);
return client;
};

builder.Services.AddKeyedSingleton<IChatCompletionService>(serviceId, factory);

return builder;
}

/// <summary>
/// Adds the Azure OpenAI chat completion service to the list.
/// </summary>
/// <param name="builder">The <see cref="IKernelBuilder"/> instance to augment.</param>
/// <param name="endpoint">Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="apiKey">Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="serviceId">A local identifier for the given AI service</param>
/// <param name="httpClient">The HttpClient to use with this service.</param>
/// <returns>The same instance as <paramref name="builder"/>.</returns>
public static IKernelBuilder AddAzureAIStudioPhi3ChatCompletion(
this IKernelBuilder builder,
string endpoint,
string apiKey,
HttpClient httpClient,
string? serviceId = null)
{
Verify.NotNull(builder);
Verify.NotNullOrWhiteSpace(endpoint);
Verify.NotNullOrWhiteSpace(apiKey);

Func<IServiceProvider, object?, AzureAIStudioMaaSPhi3ChatCompletionService> factory = (serviceProvider, _) =>
{
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
var logger = loggerFactory?.CreateLogger(typeof(AzureAIStudioMaaSPhi3ChatCompletionService));

var client = new AzureAIStudioMaaSPhi3ChatCompletionService(new Uri(endpoint),
new Azure.AzureKeyCredential(apiKey),
new(),
httpClient,
logger);
return client;
};

builder.Services.AddKeyedSingleton<IChatCompletionService>(serviceId, factory);

return builder;
}

/// <summary>
/// Adds the Azure OpenAI chat completion service to the list.
/// </summary>
/// <param name="builder">The <see cref="IKernelBuilder"/> instance to augment.</param>
/// <param name="endpoint">Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
/// <param name="credentials">Azure credentials that can be used for a managed identity or for current user</param>
/// <param name="serviceId">A local identifier for the given AI service</param>
/// <param name="httpClient">The HttpClient to use with this service.</param>
/// <returns>The same instance as <paramref name="builder"/>.</returns>
public static IKernelBuilder AddAzureAIStudioPhi3ChatCompletion(
this IKernelBuilder builder,
string endpoint,
DefaultAzureCredential credentials,
HttpClient httpClient,
string? serviceId = null)
{
Verify.NotNull(builder);
Verify.NotNullOrWhiteSpace(endpoint);
Verify.NotNull(credentials);

httpClient.BaseAddress = new Uri(endpoint);

Func<IServiceProvider, object?, AzureAIStudioMaaSPhi3ChatCompletionService> factory = (serviceProvider, _) =>
{
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
var logger = loggerFactory?.CreateLogger(typeof(AzureAIStudioMaaSPhi3ChatCompletionService));

var client = new AzureAIStudioMaaSPhi3ChatCompletionService(new Uri(endpoint),
credentials,
new(),
httpClient,
logger);
return client;
};

builder.Services.AddKeyedSingleton<IChatCompletionService>(serviceId, factory);

return builder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Azure.Core.Pipeline;
using Azure.Core;
using Azure;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Net;

namespace MDev.Dotnet.SemanticKernel.Connectors.AzureAIStudio.Phi3.AzureCore
{
internal class MDEVAzureKeyCredentialPolicy : HttpPipelineSynchronousPolicy
{
private readonly string _name;
private readonly AzureKeyCredential _credential;
private readonly string? _prefix;

/// <summary>
/// Initializes a new instance of the <see cref="MDEVAzureKeyCredentialPolicy"/> class.
/// </summary>
/// <param name="credential">The <see cref="AzureKeyCredential"/> used to authenticate requests.</param>
/// <param name="name">The name of the key header used for the credential.</param>
/// <param name="prefix">The prefix to apply before the credential key. For example, a prefix of "SharedAccessKey" would result in
/// a value of "SharedAccessKey {credential.Key}" being stamped on the request header with header key of <paramref name="name"/>.</param>
public MDEVAzureKeyCredentialPolicy(AzureKeyCredential credential, string name, string? prefix = null)
{
if (credential is null)
{
throw new ArgumentNullException(nameof(credential));
}
if (name is null)
{
throw new ArgumentNullException(nameof(name));
}
if (name.Length == 0)
{
throw new ArgumentException("Value cannot be an empty string.", nameof(name));
}
_credential = credential;
_name = name;
_prefix = prefix;
}

/// <inheritdoc/>
public override void OnSendingRequest(HttpMessage message)
{
base.OnSendingRequest(message);
message.Request.Headers.SetValue(_name, _prefix != null ? $"{_prefix} {_credential.Key}" : _credential.Key);
}
}
}
Loading
Loading