Skip to content

Latest commit

 

History

History
 
 

cext-trey-research-auth

Trey Research Declarative Agent (OAuth version)

Trey Research is a fictitious consulting company that supplies talent in the software and pharmaceuticals industries. The vision for this demo is to show the full potential of Copilot extensions in a relatable business environment.

NOTE: Declarative agents were called "declarative copilots" during private preview; you may find the old terminology in some of our documentation and tools as they are being updated.

NOTE: This version of the Trey Research sample uses OAuth authentication and has been tested using Entra ID as the identity service. See the Setup section below for details on configuring the necessary apps in Entra ID.

Prompts that work

  • what projects am i assigned to? (NOTE: In this "anonymous" version of the sample, the user is assumed to be consultant "Avery Howard". If Copilot decides to request information using your real name, the request will fail. Unless your name happens to be "Avery Howard".)
  • what projects is domi working on?
  • do we have any consultants with azure certifications?
  • what projects are we doing for relecloud?
  • which consultants are working with woodgrove bank?
  • how many hours has avery delivered this month?
  • please find a consultant with python skills who is available immediately
  • are any consultants available who are AWS certified? (multi-parameter!)
  • does trey research have any architects with javascript skills? (multi-parameter!)
  • what designers are working at woodgrove bank? (multi-parameter!)
  • please charge 10 hours to woodgrove bank (POST request)
  • please add sanjay to the contoso project (POST request with easy to forget entities, hoping to prompt the user; for now they are defaulted)

If the sample files are uploaded, these will work as well:

  • find my hours spreadsheet and get the hours for woodgrove, then bill the client
  • make a list of my projects, then write a summary of each based on the statement of work.

Plugin Features

The sample showcases the following features:

  1. Declarative agent with branding and instructions, access to relevant SharePoint documents and the API plugin
  2. API based plugin works with any platform that supports REST requests
  3. Copilot will construct queries for specific data using GET requests
  4. Copilot updates and adds data using POST requests
  5. Multi-parameter queries to filter results
  6. Show a confirmation card before POSTing data; capture missing parameters
  7. Display rich adaptive cards
  8. Authenticate the user using Entra ID and OAuth 2.0

Note on validating OAuth tokens

Microsoft does not provide a supported library for validating Entra ID tokens in NodeJS, but instead provides this detailed documentation{target=_blank} on how to write your own. Another useful article{target=_blank} is also available from Microsoft MVP Andrew Connell{target=_blank}. This lab uses a community provided library{target=_blank} written by Waldek Mastykarz{target=_blank}, which is intended to follow this guidance. Note that this library is not supported by Microsoft and is under an MIT License, so use it at your own risk.

Setup

Prerequisites

Setup instructions

Register your Entra ID applications

You will need to set up 2 applications in Entra ID:

  1. API Service app - is used to secure HTTP requests going to your API
  2. API Plugin app - is used to represent the plugin running in Microsoft 365 that will access your API

Detailed setup instructions are here. After following those steps you will have saved a number of values for the app ID's and other details needed below.

Setup instructions (one-time setup)

  1. Log into Teams Toolkit using your target tenant.

  2. Add these lines to your env/.env.local file. Create a new file if there isn't already one present, and fill in the values you stored during app registration.

API_APPLICATION_ID=<your-api-service-client-id>
API_TENANT_ID=<your-tenant-id>
  1. If your project doesn't yet have a file env/.env.local.user, then create one by copying env/.env.local.user.sample. If you do have such a file, ensure it includes these lines.
SECRET_STORAGE_ACCOUNT_CONNECTION_STRING=UseDevelopmentStorage=true
  1. Update the Plugin file: Open the appPackage/trey-plugin.json file and find the line:
"reference_id":  "<your oauth plugin vault reference id>"

Fill in your OAuth plugin vault reference ID from when you registered the app in Teams Developer portal.

  1. Update the Open API Definition file

Since you will be using a persistent developer tunnel for your API, find this line in the appPackage/trey-definition.json and put your tunnel URL in place of the token ${{OPENAPI_SERVER_URL}}:

    "url": "${{OPENAPI_SERVER_URL}}/api/",
  1. OPTIONAL: Copy the files from the /sampleDocs folder to OneDrive or SharePoint. Add the location of these files in the OneDriveAndSharePoint capability in the declarative copilot (/appPackage/trey-declarative-copilot.json).

Running the solution (after each build)

  1. Ensure your persistent developer tunnel is running

  2. Press F5 to start the application. It will take a while on first run to download the dependencies. Eventually a browser window will open up and your package is installed.

  3. Navigate to Copilot as shown below 1️⃣ Running in Copilot

  4. Access the declarative agent by opening the flyout 4️⃣, then select the Trey Genie Local solution 5️⃣.

API Summary

postman

We have a Postman collection for you to try out the APIs. It's a great way to get to know the data that Copilot is accessing.

All API operations are included in the collection, with parameters and body provided to make it easier for you to test our GET and POST calls.

Make sure you have Postman desktop to be able to test urls with localhost domain. Or simply replace part of the URL http://localhost:7071 with your tunnel/host URL.

GET Requests

 GET /api/me/ - get my consulting profile and projects

GET /api/consultants/ - get all consultants
// Query string params can be used in any combination to filter results
GET /api/consultants/?consultantName=Avery - get consultants with names containing "Avery"
GET /api/consultants/?projectName=Foo - get consultants on projects with "Foo" in the name
GET /api/consultants/?skill=Foo - get consultants with "Foo" in their skills list
GET /api/consultants/?certification=Foo - get consultants with "Foo" in their certifications list
GET /api/consultants/?role=Foo - get consultants who can serve the "Foo" role on a project
GET /api/consultants/?availability=x - get consultants with x hours availability this month or next month

The above requests all return an array of consultant objects, which are defined in the ApiConsultant interface in /model/apiModel.ts.

GET /api/projects/ - get all projects
// Query string params can be used in any combination to filter results
GET /api/projects/?projectName=Foo - get projects with "Foo" in the name
GET /api/projects/?consultantName=Avery - get projects where a consultant containing "Avery" is assigned

The above requests all return an array of project objects, which are defined in the ApiProject interface in /model/apiModel.ts.

POST Requests

POST /api/me/chargeTime - Add hours to project with "Foo" in the name

Request body:
{
  projectName: "foo",
  hours: 5
}
Response body:
{
    status: 200,
    message: "Charged 3 hours to Woodgrove Bank on project \"Financial data plugin for Microsoft Copilot\". You have 17 hours remaining this month";
}

POST /api/projects/assignConsultant - Add consultant to project with "Foo" in the name
Request body:
{
    projectName: "foo",
    consultantName: "avery",
    role: "architect",
    forecast: number
}
Response body:
{
    status: 200
    message: "Added Alice to the \"Financial data plugin for Microsoft Copilot\" project at Woodgrove Bank. She has 100 hours remaining this month.";
}

API Design considerations

The process began with a bunch of sample prompts that serve as simple use cases for the service. The API is designed specifically to serve those use cases and likely prompts. In order to make it easier for use in the RAG orchestration, the service:

  1. Completes each prompt / use case in a single HTTP request

    • accept names or partial names that might be stated in a user prompt rather than requiring IDs which must be looked up
    • return enough information to allow for richer responses; err on the side of providing more detail including related entities
  2. For best Copilot performance, limit the number of parameter options to 10-15

  3. Ensure that parameters, properties, messages, etc. are human readable, as they will be interpreted by a large language model

  4. Return all the data Copilot might need to fulfull a user prompt. For example, when retrieving a consultant, the API has no way to know if the user was seeking the consultant's skills, location, project list, or something else. Thus, the API returns all this information.

  5. In GET requests, use the resource that corresponds to the entity the user is asking for. Don't expect Copilot to figure out that some data is buried in another entity.

  6. In POST requests, use a command style such as /me/chargeTime, as opposed to asking the API to update a data structure

  7. Don't expect Copilot to filter data; instead provide parameters and filter it server side. (I have seen some filtering by Copilot however - this is for further study)