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

@alvesfabi OBO Support #803

Merged
merged 76 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from 68 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
30b55c0
initial version
alvesfabi Jan 29, 2024
bbb1446
in progress with OBO token
alvesfabi Jan 31, 2024
d096fe3
working version native plugin hardcoded
alvesfabi Jan 31, 2024
05d6888
working version odata query
alvesfabi Jan 31, 2024
529ed41
working version persona
alvesfabi Jan 31, 2024
72fb304
working version with app settings
alvesfabi Feb 5, 2024
e17ab92
Merge pull request #1 from alvesfabi/clean-for-pr
alvesfabi Feb 5, 2024
5960d97
cleaned up version
alvesfabi Feb 13, 2024
7e9bc5a
Merge pull request #2 from alvesfabi/clean-for-pr
alvesfabi Feb 13, 2024
bde84da
cleaned up version 3
alvesfabi Feb 13, 2024
f02cb0f
Merge pull request #3 from alvesfabi/clean-for-pr
alvesfabi Feb 13, 2024
0b348f3
improved readme
alvesfabi Feb 13, 2024
2616c56
Merge pull request #4 from alvesfabi/clean-for-pr
alvesfabi Feb 13, 2024
39f2611
fixed obo readme
alvesfabi Feb 14, 2024
683d7a9
Merge pull request #5 from alvesfabi/fixed-readme-obo
alvesfabi Feb 14, 2024
f5a2f2d
Merge remote-tracking branch 'upstream/main'
alvesfabi Feb 14, 2024
da81269
fixed plugin description
alvesfabi Feb 14, 2024
4f402e6
Including OBO Auth Config in PlannerOptions + leveraging existing DI
teresaqhoang Feb 16, 2024
1022cc5
Merge branch 'main' of https://github.com/microsoft/chat-copilot
teresaqhoang Feb 21, 2024
62a9505
update config settins
alvesfabi Feb 22, 2024
00647c4
Merge pull request #7 from alvesfabi/clean-for-pr-3
alvesfabi Feb 22, 2024
2e98d6f
fix for pr
alvesfabi Feb 22, 2024
64d4b2a
Merge pull request #8 from alvesfabi/clean-for-pr-4
alvesfabi Feb 22, 2024
c8566d8
fix text
alvesfabi Feb 22, 2024
003d16c
fix formatting
alvesfabi Feb 22, 2024
4b5bc58
fix formatting with dotnet format
alvesfabi Feb 22, 2024
78fcfef
fix using order
alvesfabi Feb 22, 2024
844c451
fix using order
alvesfabi Feb 22, 2024
a869401
fix using order 2
alvesfabi Feb 22, 2024
0752212
fix typo
alvesfabi Feb 22, 2024
5cfbcb8
fix warnings
alvesfabi Feb 23, 2024
e1ea576
fix more warnings
alvesfabi Feb 23, 2024
f7ea66c
remove local settings
alvesfabi Feb 23, 2024
fc330b2
fixed comments
alvesfabi Feb 27, 2024
96f3942
revert .vscode
alvesfabi Feb 27, 2024
f882c49
revert .vscode 2
alvesfabi Feb 27, 2024
f80ebbf
Merge pull request #9 from alvesfabi/fixes-review
alvesfabi Feb 27, 2024
3867f09
initial merge
alvesfabi Apr 8, 2024
75f3170
initial merge
alvesfabi Apr 8, 2024
f0cbebe
webapp msal fixes
alvesfabi Apr 8, 2024
6b3fbf0
webapp msal fixes
alvesfabi Apr 9, 2024
0242376
test-for-teresa
alvesfabi May 1, 2024
ba9d934
Working MS Graph Obo Plugin
teresaqhoang May 2, 2024
86fae32
test-gtp-4
alvesfabi May 2, 2024
a1d2aaf
Merge branch 'main' of https://github.com/microsoft/chat-copilot into…
alvesfabi May 5, 2024
edec36b
Merge branch 'microsoft-main'
alvesfabi May 5, 2024
013d1e1
workin with merged
alvesfabi May 5, 2024
e631266
formatting fixes
alvesfabi May 6, 2024
090d4fe
space formatting issue
alvesfabi May 6, 2024
01c85a4
space formatting issue
alvesfabi May 6, 2024
9cf6b77
space formatting issue 2
alvesfabi May 6, 2024
0ba89eb
space formatting issue 3
alvesfabi May 6, 2024
73e9f17
Merge pull request #12 from alvesfabi/fixes-over-merged
alvesfabi May 6, 2024
ea6e2b9
fix readme.md
alvesfabi May 6, 2024
662b6af
fix readme.md
alvesfabi May 6, 2024
11334cb
fix readme.md
alvesfabi May 6, 2024
0d760a4
readme fixes 2
alvesfabi May 6, 2024
5717794
readme fixes 3
alvesfabi May 6, 2024
690d5a8
readme fixes 4
alvesfabi May 6, 2024
90ed2d4
fix json response
alvesfabi May 6, 2024
2a5d2e3
fix json response 2
alvesfabi May 6, 2024
a0719a8
fix json response 3
alvesfabi May 8, 2024
a18b0b3
fix json response 4
alvesfabi May 8, 2024
4dedf43
fix in doc and remove token control
alvesfabi May 9, 2024
75e657b
fix remove token limit
alvesfabi May 9, 2024
bde294d
fix remove token limit
alvesfabi May 9, 2024
69bf834
fix remove token limit
alvesfabi May 9, 2024
f4ec445
Merge pull request #13 from alvesfabi/fix-doc-obo
alvesfabi May 9, 2024
1eb3b1e
fix obo readme doc 2
alvesfabi May 10, 2024
da6a3d5
Merge pull request #14 from alvesfabi/fix-doc-obo
alvesfabi May 10, 2024
02f2708
Update plugins/OBO/README.md
alvesfabi May 10, 2024
849f88e
Update webapi/Controllers/ChatController.cs
alvesfabi May 10, 2024
2ae555d
Addressing comments
teresaqhoang May 10, 2024
a5a5b1f
get changes from index.ts
alvesfabi May 11, 2024
015d564
Merge branch 'main' of https://github.com/microsoft/chat-copilot
alvesfabi May 11, 2024
9d84e41
get changes msal fix for version changes
alvesfabi May 11, 2024
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
2 changes: 1 addition & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,4 @@
"password": true
}
]
}
}
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,28 @@ By default, Chat Copilot runs locally without authentication, using a guest user
./start.sh
```

## Optional Configuration: [Ms Graph API Plugin with On-Behalf-Of Flow](./plugins/OBO/README.md)

This native plugin enables the execution of Microsoft Graph APIs using the On-Behalf-Of (OBO) flow with delegated permissions.

The OBO flows is used to ensure that the backend APIs are consumed with the identity of the user, not the managed identity or service principal of the middle-tier application (in this case the WebApi).

Also, this ensures that consent is given, so that the client app (WebApp) can call the middle-tier app (WebApi), and the middle-tier app has permission to call the back-end resource (MSGraph).

This sample does not implement incremental consent in the UI so all the Graph scopes to be used need to have "Administrator Consent" given in the middle-tier app registration.

More information in the [OBO readme.md](./plugins/OBO/README.md).

### Requirements

Backend authentication via Azure AD must be enabled. Detailed instructions for enabling backend authentication are provided below.

### Limitations

- Currently, the plugin only supports GET operations. Future updates may add support for other types of operations.
- Graph queries that return large results, may reach the token limit for the AI model, producing an error.
- Incremental consent is not implemented in this sample.

# Troubleshooting

1. **_Issue:_** Unable to load chats.
Expand Down
99 changes: 99 additions & 0 deletions plugins/OBO/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Ms Graph plugin using On-Behalf-Of Flow for Ms Graph APIs

This repository contains a sample Plugin that uses the On-Behalf-Of (OBO) flow to call Microsoft Graph APIs.

In this document we will refer to the client app as the WebApp (src/webapp), the middle-tier app as the WebApi (src/webapi) and the backend resource as the Ms Graph Api.

> **IMPORTANT:** This sample is for educational purposes only and is not recommended for production deployments.

> **NOTE:** This plugin was implemented as a native Kernel function, in the WebAPI code. This is not an implementation of the OpenAI plugin spec.

> **NOTE:** This plugin works better GTP-4 or GTP-4-Turbo as these models works better with the function model.

## Prerequisites

- Enable backend authentication via Azure AD as described in the main `README.md` file.
alvesfabi marked this conversation as resolved.
Show resolved Hide resolved

## Setup Instructions

1. **Add the WebApp to the "known client application list" in the WebApi app registration.**

- Go to the WebApp app registration in your tenant and copy the Application Id (Client ID).
- Go to the WebAPI app registration in your tenant.
- Click on "Manifest" option and add an entry for the `knownClientApplications` attribute using the Application Id (Client ID) of the WebApp registration as described in this [document](https://learn.microsoft.com/en-us/entra/identity-platform/reference-app-manifest#knownclientapplications-attribute)

- Save the manifest.

2. **Give the WebApi the delegated permissions.**

- Go to the WebApi API app registration.
- Select the "API permissions" option.
- Click on "+ Add Permission" option and choose the "Microsoft Graph" option.
- Select "Delegated permission" and choose all the delegated permissions needed.
- Click on "Add Permissions".
- As the UI does not implement incremental consent, you need to grant "Admin Consent" to the new permissions added.

3. **Create a Client Secret for the WebAPI app registration OBO Configuration.**

- In the WebAPI app registration click on "Certificates & Secrets".
- Create a new secret by clicking in the "+ New client secret", enter a description and the expiration days.
- Copy the Client Secret and the Application Id (Client ID) to use in the WebAPI appsetting configuration.

4. **Change the WebAPI `appsettings.json` file.**
- Add your OBO configuration values in the OnBehalfOfAuth section as shown below. The ClientId must be the WebAPI Application Id (Client ID).

```json
// OBO Configuration for Plugins
"OnBehalfOfAuth": {
"Authority": "https://login.microsoftonline.com",
"TenantId": "[ENTER YOUR TENANT ID]",
"ClientId": "[ENTER YOUR CLIENT ID]",
"ClientSecret": "[ENTER YOUR CLIENT SECRET]"
}
```

5. Change the scope for the Ms Graph Obo plugin in the WebApp code

- As the UI does not implement incremental consent, you need to configure the WebApp to use the [.default scope](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-on-behalf-of-flow#default-and-combined-consent). The scope name is formed by the Application ID of the WebAPI app registration so you need to update it with the WebApi Application ID (Client ID).

- Change the Constants.ts file located in the webapp/src folder, add the msGraphOboScopes entry with the WebApi Application Id, as shown below:

```typescript
plugins: {
msGraphOboScopes: ['api://[ENTER THE WE API APPLICATION ID]/.default'],
}
```

## Test Instructions

1. Login to the app

![Login Step](./test-step-1.png)

2. Enable Ms Graph OBO Plugin

![Plugin Step](./test-step-2.png)

![Plugin Step 2](./test-step-3.png)

![Plugin Step 3](./test-step-4.png)

3. Update the Persona Meta Prompt with the following text:

```text
This is a chat between an intelligent AI bot named Copilot and one or more participants. SK stands for Semantic Kernel, the AI platform used to build the bot. The AI was trained on data through 2021 and is not aware of events that have occurred since then. The bot has the ability to call Graph APIs using the MS Graph OBO tool to fetch real-time data. The user must first enable the plugin. To call a Graph API, the bot would call the \\"CallGraphApiTasksAsync\\" function, and provide the Graph API URL with the ODATA query and its required scopes as a list as arguments. The plugin will automatically handle authentication. Otherwise, the bot has no ability to access data on the Internet, so it should not claim that it can or say that it will go and look things up. Try to be concise with your answers, though it is not required. Knowledge cutoff: {{$knowledgeCutoff}} / Current date: {{TimePlugin.Now}}.
```

![Persona Step 1](./test-step-5.png)

4. Run a prompt to check if the bot understands that can can a graph API and then ask to run a query by providing a sample

- Please get the list of applications in my tenant.
You can call the Graph API: `https://graph.microsoft.com/v1.0/applications$select=appId,identifierUris,displayName,publisherDomain,signInAudience`
Required scope: Application.Read.All

![Check Step 1](./test-step-6.png)

6. After the sample prompt the bot will execute any graph api query without the need of indicating the graph api, odata query or scopes

![Check Step 2](./test-step-7.png)
Binary file added plugins/OBO/test-step-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added plugins/OBO/test-step-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added plugins/OBO/test-step-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added plugins/OBO/test-step-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added plugins/OBO/test-step-5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added plugins/OBO/test-step-6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added plugins/OBO/test-step-7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 5 additions & 4 deletions scripts/Start.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ $cmd = get-command 'pwsh'
$ErrorActionPreference = 'Continue'

if (!$cmd) {
Write-Warning "Please update your powershell installation: https://aka.ms/powershell"
return;
Write-Warning "Please update your powershell installation: https://aka.ms/powershell"
return;
}

$BackendScript = Join-Path "$PSScriptRoot" 'Start-Backend.ps1'
Expand Down Expand Up @@ -41,12 +41,13 @@ while ($backendRunning -eq $false -and $retryCount -lt $maxRetries) {
if ($backendRunning -eq $true) {
# Start frontend (in current PS process)
& $FrontendScript
} else {
}
else {
# otherwise, write to the console that the backend is not running and we have exceeded the number of retries and we are exiting
Write-Host "*************************************************"
Write-Host "Backend is not running and we have exceeded "
Write-Host "the maximum number of retries."
Write-Host ""
Write-Host "Therefore, we are exiting."
Write-Host "*************************************************"
}
}
23 changes: 22 additions & 1 deletion webapi/Controllers/ChatController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@ namespace CopilotChat.WebApi.Controllers;
public class ChatController : ControllerBase, IDisposable
{
private readonly ILogger<ChatController> _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly System.Net.Http.IHttpClientFactory _httpClientFactory;
alvesfabi marked this conversation as resolved.
Show resolved Hide resolved
private readonly List<IDisposable> _disposables;
private readonly ITelemetryService _telemetryService;
private readonly ServiceOptions _serviceOptions;
private readonly MsGraphOboPluginOptions _msGraphOboPluginOptions;
private readonly PromptsOptions _promptsOptions;
private readonly IDictionary<string, Plugin> _plugins;

private const string ChatPluginName = nameof(ChatPlugin);
Expand All @@ -58,13 +60,17 @@ public ChatController(
IHttpClientFactory httpClientFactory,
ITelemetryService telemetryService,
IOptions<ServiceOptions> serviceOptions,
IOptions<MsGraphOboPluginOptions> msGraphOboPluginOptions,
IOptions<PromptsOptions> promptsOptions,
IDictionary<string, Plugin> plugins)
{
this._logger = logger;
this._httpClientFactory = httpClientFactory;
this._telemetryService = telemetryService;
this._disposables = new List<IDisposable>();
this._serviceOptions = serviceOptions.Value;
this._msGraphOboPluginOptions = msGraphOboPluginOptions.Value;
this._promptsOptions = promptsOptions.Value;
this._plugins = plugins;
}

Expand Down Expand Up @@ -214,6 +220,12 @@ private async Task RegisterFunctionsAsync(Kernel kernel, Dictionary<string, stri
tasks.Add(this.RegisterMicrosoftGraphPlugins(kernel, GraphAuthHeader));
}

// Microsoft Graph OBO
if (authHeaders.TryGetValue("MSGRAPHOBO", out string? GraphOboAuthHeader))
{
tasks.Add(this.RegisterMicrosoftGraphOBOPlugins(kernel, GraphOboAuthHeader));
}

if (variables.TryGetValue("customPlugins", out object? customPluginsString))
{
tasks.AddRange(this.RegisterCustomPlugins(kernel, customPluginsString, authHeaders));
Expand Down Expand Up @@ -263,6 +275,15 @@ private Task RegisterMicrosoftGraphPlugins(Kernel kernel, string GraphAuthHeader
return Task.CompletedTask;
}

private Task RegisterMicrosoftGraphOBOPlugins(Kernel kernel, string GraphOboAuthHeader)
{
this._logger.LogInformation("Enabling Microsoft Graph OBO plugin(s).");
kernel.ImportPluginFromObject(
new MsGraphOboPlugin(GraphOboAuthHeader, this._httpClientFactory, this._msGraphOboPluginOptions, this._promptsOptions.FunctionCallingTokenLimit, this._logger),
"msGraphObo");
return Task.CompletedTask;
}

private IEnumerable<Task> RegisterCustomPlugins(Kernel kernel, object? customPluginsString, Dictionary<string, string> authHeaders)
{
CustomPlugin[]? customPlugins = JsonSerializer.Deserialize<CustomPlugin[]>(customPluginsString!.ToString()!);
Expand Down
2 changes: 2 additions & 0 deletions webapi/Extensions/ServiceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ public static IServiceCollection AddOptions(this IServiceCollection services, Co

AddOptions<FrontendOptions>(FrontendOptions.PropertyName);

AddOptions<MsGraphOboPluginOptions>(MsGraphOboPluginOptions.PropertyName);

return services;

void AddOptions<TOptions>(string propertyName)
Expand Down
24 changes: 24 additions & 0 deletions webapi/Options/MsGraphOboPluginOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft. All rights reserved.

namespace CopilotChat.WebApi.Options;

public class MsGraphOboPluginOptions
{
public const string PropertyName = "OnBehalfOf";
/// <summary>
/// The authority to use for OBO Auth.
/// </summary>
public string? Authority { get; set; }
/// <summary>
/// The Tenant Id to use for OBO Auth.
/// </summary>
public string? TenantId { get; set; }
/// <summary>
/// The Client Id to use for OBO Auth.
/// </summary>
public string? ClientId { get; set; }
/// <summary>
/// The Client Secret to use for OBO Auth.
/// </summary>
public string? ClientSecret { get; set; }
}
5 changes: 5 additions & 0 deletions webapi/Options/PromptsOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ public class PromptsOptions
/// </summary>
[Required, Range(0, int.MaxValue)] public int ResponseTokenLimit { get; set; }

/// <summary>
/// The token count allowed for function calling responses.
/// </summary>
[Required, Range(0, int.MaxValue)] public int FunctionCallingTokenLimit { get; set; }

/// <summary>
/// Weight of memories in the contextual part of the final prompt.
/// Contextual prompt excludes all the system commands and user intent.
Expand Down
6 changes: 4 additions & 2 deletions webapi/Plugins/Chat/ChatPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -582,10 +582,12 @@ private int GetMaxRequestTokenBudget()
// "content": "Assistant is a large language model.","role": "system"
// This burns just under 20 tokens which need to be accounted for.
const int ExtraOpenAiMessageTokens = 20;

return this._promptOptions.CompletionTokenLimit // Total token limit
- ExtraOpenAiMessageTokens
- this._promptOptions.ResponseTokenLimit; // Token count reserved for model to generate a response
// Token count reserved for model to generate a response
- this._promptOptions.ResponseTokenLimit
// Buffer for Tool Calls
- 1000;
teresaqhoang marked this conversation as resolved.
Show resolved Hide resolved
}

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions webapi/Plugins/Chat/CopilotChatPlanner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading
Loading