├── docs
├── faq.md
├── troubleshooting.md
├── assets
│ ├── HLA.png
│ ├── ui.gif
│ ├── java.png
│ ├── HLA-MCP.png
│ ├── azd-success.png
│ ├── langchain4j.png
│ ├── multi-agents.png
│ ├── robot-agents.png
│ ├── robot-agents-small.png
│ └── transaction-tracing.png
├── kusto-queries.md
└── multi-agents
│ └── introduction.md
├── .gitattributes
├── app
├── frontend
│ ├── .npmrc
│ ├── .dockerignore
│ ├── .env.production
│ ├── .prettierignore
│ ├── .env.dev
│ ├── .env.local
│ ├── src
│ │ ├── vite-env.d.ts
│ │ ├── api
│ │ │ ├── index.ts
│ │ │ ├── models.ts
│ │ │ └── api.ts
│ │ ├── components
│ │ │ ├── LoginButton
│ │ │ │ ├── index.tsx
│ │ │ │ ├── LoginButton.module.css
│ │ │ │ └── LoginButton.tsx
│ │ │ ├── QuestionInput
│ │ │ │ ├── index.ts
│ │ │ │ ├── QuestionContext.ts
│ │ │ │ └── QuestionInput.module.css
│ │ │ ├── ClearChatButton
│ │ │ │ ├── index.tsx
│ │ │ │ ├── ClearChatButton.module.css
│ │ │ │ └── ClearChatButton.tsx
│ │ │ ├── SettingsButton
│ │ │ │ ├── index.tsx
│ │ │ │ ├── SettingsButton.module.css
│ │ │ │ └── SettingsButton.tsx
│ │ │ ├── UserChatMessage
│ │ │ │ ├── index.ts
│ │ │ │ ├── UserChatMessage.module.css
│ │ │ │ └── UserChatMessage.tsx
│ │ │ ├── SupportingContent
│ │ │ │ ├── index.ts
│ │ │ │ ├── SupportingContentParser.ts
│ │ │ │ ├── SupportingContent.module.css
│ │ │ │ └── SupportingContent.tsx
│ │ │ ├── TokenClaimsDisplay
│ │ │ │ ├── index.tsx
│ │ │ │ └── TokenClaimsDisplay.tsx
│ │ │ ├── Example
│ │ │ │ ├── index.tsx
│ │ │ │ ├── Example.tsx
│ │ │ │ ├── Example.module.css
│ │ │ │ └── ExampleList.tsx
│ │ │ ├── AnalysisPanel
│ │ │ │ ├── index.tsx
│ │ │ │ ├── AnalysisPanelTabs.tsx
│ │ │ │ ├── AnalysisPanel.module.css
│ │ │ │ └── AnalysisPanel.tsx
│ │ │ ├── Answer
│ │ │ │ ├── index.ts
│ │ │ │ ├── AnswerIcon.tsx
│ │ │ │ ├── AnswerError.tsx
│ │ │ │ ├── AnswerLoading.tsx
│ │ │ │ ├── AnswerParser.tsx
│ │ │ │ └── Answer.module.css
│ │ │ └── AttachmentType.ts
│ │ ├── pages
│ │ │ ├── NoPage.tsx
│ │ │ ├── layout
│ │ │ │ ├── Layout.module.css
│ │ │ │ └── Layout.tsx
│ │ │ └── chat
│ │ │ │ └── Chat.module.css
│ │ ├── index.css
│ │ ├── assets
│ │ │ ├── search.svg
│ │ │ └── github.svg
│ │ └── index.tsx
│ ├── public
│ │ └── favicon.ico
│ ├── .prettierrc.json
│ ├── nginx
│ │ └── nginx.conf.template
│ ├── manifests
│ │ ├── frontend-service.yml
│ │ └── frontend-deployment.tmpl.yml
│ ├── Dockerfile-aks
│ ├── index.html
│ ├── Dockerfile
│ ├── tsconfig.json
│ ├── package.json
│ └── vite.config.ts
├── business-api
│ ├── account
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── resources
│ │ │ │ │ └── application-dev.properties
│ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── microsoft
│ │ │ │ │ └── openai
│ │ │ │ │ └── samples
│ │ │ │ │ └── assistant
│ │ │ │ │ └── business
│ │ │ │ │ ├── models
│ │ │ │ │ ├── Beneficiary.java
│ │ │ │ │ ├── PaymentMethodSummary.java
│ │ │ │ │ ├── PaymentMethod.java
│ │ │ │ │ └── Account.java
│ │ │ │ │ ├── AccountApplication.java
│ │ │ │ │ ├── mcp
│ │ │ │ │ ├── config
│ │ │ │ │ │ └── MCPServerConfiguration.java
│ │ │ │ │ └── server
│ │ │ │ │ │ ├── UserMCPService.java
│ │ │ │ │ │ └── AccountMCPService.java
│ │ │ │ │ ├── controller
│ │ │ │ │ ├── UserController.java
│ │ │ │ │ └── AccountController.java
│ │ │ │ │ └── service
│ │ │ │ │ └── UserService.java
│ │ │ └── test
│ │ │ │ └── java
│ │ │ │ └── org
│ │ │ │ └── springframework
│ │ │ │ └── ai
│ │ │ │ └── mcp
│ │ │ │ └── sample
│ │ │ │ └── client
│ │ │ │ └── AccountMCPClient.java
│ │ ├── applicationinsights.json
│ │ ├── .mvn
│ │ │ └── wrapper
│ │ │ │ └── maven-wrapper.properties
│ │ ├── Dockerfile
│ │ └── pom.xml
│ ├── payment
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── resources
│ │ │ │ ├── application.properties
│ │ │ │ ├── application-dev.properties
│ │ │ │ └── payments.yaml
│ │ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── microsoft
│ │ │ │ └── openai
│ │ │ │ └── samples
│ │ │ │ └── assistant
│ │ │ │ └── business
│ │ │ │ ├── mcp
│ │ │ │ ├── config
│ │ │ │ │ └── MCPServerConfiguration.java
│ │ │ │ └── server
│ │ │ │ │ └── PaymentMCPService.java
│ │ │ │ ├── models
│ │ │ │ ├── Payment.java
│ │ │ │ └── Transaction.java
│ │ │ │ ├── PaymentApplication.java
│ │ │ │ └── controller
│ │ │ │ └── PaymentsController.java
│ │ ├── applicationinsights.json
│ │ ├── .mvn
│ │ │ └── wrapper
│ │ │ │ └── maven-wrapper.properties
│ │ └── Dockerfile
│ └── transactions-history
│ │ ├── applicationinsights.json
│ │ ├── src
│ │ └── main
│ │ │ ├── resources
│ │ │ └── application-dev.properties
│ │ │ └── java
│ │ │ └── com
│ │ │ └── microsoft
│ │ │ └── openai
│ │ │ └── samples
│ │ │ └── assistant
│ │ │ └── business
│ │ │ ├── mcp
│ │ │ ├── config
│ │ │ │ └── MCPServerConfiguration.java
│ │ │ └── server
│ │ │ │ └── TransactionMCPService.java
│ │ │ ├── TransactionsHistoryApplication.java
│ │ │ ├── Transaction.java
│ │ │ └── TransactionController.java
│ │ ├── .mvn
│ │ └── wrapper
│ │ │ └── maven-wrapper.properties
│ │ └── Dockerfile
├── copilot
│ ├── applicationinsights.json
│ ├── langchain4j-agents
│ │ ├── src
│ │ │ ├── main
│ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── microsoft
│ │ │ │ │ ├── langchain4j
│ │ │ │ │ └── agent
│ │ │ │ │ │ ├── mcp
│ │ │ │ │ │ ├── MCPProtocolType.java
│ │ │ │ │ │ └── MCPServerMetadata.java
│ │ │ │ │ │ ├── AgentMetadata.java
│ │ │ │ │ │ ├── Agent.java
│ │ │ │ │ │ └── AgentExecutionException.java
│ │ │ │ │ └── openai
│ │ │ │ │ └── samples
│ │ │ │ │ └── assistant
│ │ │ │ │ └── langchain4j
│ │ │ │ │ ├── tools
│ │ │ │ │ └── InvoiceScanTool.java
│ │ │ │ │ └── agent
│ │ │ │ │ └── mcp
│ │ │ │ │ ├── AccountMCPAgent.java
│ │ │ │ │ └── TransactionHistoryMCPAgent.java
│ │ │ └── test
│ │ │ │ ├── resources
│ │ │ │ ├── logback.xml
│ │ │ │ └── payments.yaml
│ │ │ │ └── java
│ │ │ │ └── dev
│ │ │ │ └── langchain4j
│ │ │ │ └── openapi
│ │ │ │ └── mcp
│ │ │ │ ├── TransactionHistoryMCPAgentIntegrationTest.java
│ │ │ │ └── AccountMCPAgentIntegrationTest.java
│ │ └── pom.xml
│ ├── copilot-backend
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── java
│ │ │ │ │ └── com
│ │ │ │ │ │ └── microsoft
│ │ │ │ │ │ └── openai
│ │ │ │ │ │ └── samples
│ │ │ │ │ │ └── assistant
│ │ │ │ │ │ ├── security
│ │ │ │ │ │ ├── LoggedUser.java
│ │ │ │ │ │ └── LoggedUserService.java
│ │ │ │ │ │ ├── controller
│ │ │ │ │ │ ├── ChatAppRequestContext.java
│ │ │ │ │ │ ├── ResponseContext.java
│ │ │ │ │ │ ├── ResponseMessage.java
│ │ │ │ │ │ ├── ResponseChoice.java
│ │ │ │ │ │ ├── ChatAppRequest.java
│ │ │ │ │ │ ├── auth
│ │ │ │ │ │ │ └── AuthSetup.java
│ │ │ │ │ │ ├── ChatAppRequestOverrides.java
│ │ │ │ │ │ └── ChatResponse.java
│ │ │ │ │ │ ├── plugin
│ │ │ │ │ │ ├── mock
│ │ │ │ │ │ │ └── PaymentTransaction.java
│ │ │ │ │ │ ├── LoggedUserPlugin.java.sample
│ │ │ │ │ │ ├── PaymentMockPlugin.java.sample
│ │ │ │ │ │ └── TransactionHistoryMockPlugin.java.sample
│ │ │ │ │ │ ├── common
│ │ │ │ │ │ └── ChatGPTMessage.java
│ │ │ │ │ │ ├── CopilotApplication.java
│ │ │ │ │ │ └── config
│ │ │ │ │ │ ├── Langchain4JConfiguration.java
│ │ │ │ │ │ ├── DocumentIntelligenceConfiguration.java
│ │ │ │ │ │ ├── BlobStorageProxyConfiguration.java
│ │ │ │ │ │ ├── AzureAuthenticationConfiguration.java
│ │ │ │ │ │ ├── DocumentIntelligenceInvoiceScanConfiguration.java
│ │ │ │ │ │ └── MCPAgentsConfiguration.java
│ │ │ │ └── resources
│ │ │ │ │ ├── application.properties
│ │ │ │ │ └── payments.yaml
│ │ │ └── test
│ │ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── microsoft
│ │ │ │ └── openai
│ │ │ │ └── samples
│ │ │ │ └── assistant
│ │ │ │ └── AccountAgentIntegrationTest.java
│ │ ├── manifests
│ │ │ ├── backend-service.yml
│ │ │ ├── backend-deployment.tmpl.yml
│ │ │ ├── ingress.yml
│ │ │ └── azd-env-configmap.yml
│ │ └── pom.xml
│ ├── .mvn
│ │ ├── jvm.config
│ │ └── wrapper
│ │ │ └── maven-wrapper.properties
│ ├── copilot-common
│ │ ├── pom.xml
│ │ └── src
│ │ │ └── main
│ │ │ └── java
│ │ │ └── com
│ │ │ └── microsoft
│ │ │ └── openai
│ │ │ └── samples
│ │ │ └── assistant
│ │ │ └── proxy
│ │ │ └── BlobStorageProxy.java
│ ├── Dockerfile
│ └── pom.xml
├── package-lock.json
├── compose.yaml
├── start-compose.sh
└── start-compose.ps1
├── data
├── gori.png
└── eventbrite.png
├── CHANGELOG.md
├── CODEOWNERS
├── .github
├── CODE_OF_CONDUCT.md
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── aca-deploy.yaml
│ └── acr-build-push.yaml
├── infra
├── shared
│ ├── monitor
│ │ ├── loganalytics.bicep
│ │ ├── applicationinsights.bicep
│ │ └── monitoring.bicep
│ ├── security
│ │ ├── role.bicep
│ │ ├── keyvault-access.bicep
│ │ ├── registry-access.bicep
│ │ ├── keyvault-secret.bicep
│ │ └── keyvault.bicep
│ ├── host
│ │ ├── container-apps-environment.bicep
│ │ └── container-apps.bicep
│ └── ai
│ │ └── cognitiveservices.bicep
├── app
│ ├── web.bicep
│ ├── account.bicep
│ ├── copilot.bicep
│ ├── payment.bicep
│ └── transaction.bicep
└── main.parameters.json
├── azure.yaml
├── LICENSE
├── LICENSE.md
├── .devcontainer
└── devcontainer.json
└── SECURITY.md
/docs/faq.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/troubleshooting.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.sh text eol=lf
--------------------------------------------------------------------------------
/app/frontend/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 |
--------------------------------------------------------------------------------
/app/frontend/.dockerignore:
--------------------------------------------------------------------------------
1 | manifests
2 | node_modules
--------------------------------------------------------------------------------
/app/frontend/.env.production:
--------------------------------------------------------------------------------
1 | VITE_BACKEND_URI=/api
2 |
3 |
--------------------------------------------------------------------------------
/app/frontend/.prettierignore:
--------------------------------------------------------------------------------
1 | # Ignore JSON
2 | **/*.json
3 |
--------------------------------------------------------------------------------
/app/frontend/.env.dev:
--------------------------------------------------------------------------------
1 | VITE_BACKEND_URI=http://localhost:8081/api
2 |
--------------------------------------------------------------------------------
/app/frontend/.env.local:
--------------------------------------------------------------------------------
1 | VITE_BACKEND_URI=http://localhost:8081/api
2 |
--------------------------------------------------------------------------------
/app/frontend/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/app/business-api/account/src/main/resources/application-dev.properties:
--------------------------------------------------------------------------------
1 | server.port=8070
--------------------------------------------------------------------------------
/app/frontend/src/api/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./api";
2 | export * from "./models";
3 |
--------------------------------------------------------------------------------
/app/frontend/src/components/LoginButton/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./LoginButton";
2 |
--------------------------------------------------------------------------------
/app/frontend/src/components/QuestionInput/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./QuestionInput";
2 |
--------------------------------------------------------------------------------
/app/frontend/src/components/ClearChatButton/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./ClearChatButton";
2 |
--------------------------------------------------------------------------------
/app/frontend/src/components/SettingsButton/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./SettingsButton";
2 |
--------------------------------------------------------------------------------
/app/frontend/src/components/UserChatMessage/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./UserChatMessage";
2 |
--------------------------------------------------------------------------------
/app/frontend/src/components/SupportingContent/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./SupportingContent";
2 |
--------------------------------------------------------------------------------
/app/frontend/src/components/TokenClaimsDisplay/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./TokenClaimsDisplay";
2 |
--------------------------------------------------------------------------------
/data/gori.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wellswang2/agent-openai-assistant/HEAD/data/gori.png
--------------------------------------------------------------------------------
/data/eventbrite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wellswang2/agent-openai-assistant/HEAD/data/eventbrite.png
--------------------------------------------------------------------------------
/docs/assets/HLA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wellswang2/agent-openai-assistant/HEAD/docs/assets/HLA.png
--------------------------------------------------------------------------------
/docs/assets/ui.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wellswang2/agent-openai-assistant/HEAD/docs/assets/ui.gif
--------------------------------------------------------------------------------
/app/copilot/applicationinsights.json:
--------------------------------------------------------------------------------
1 | {
2 | "role": {
3 | "name": "copilot-api"
4 | }
5 | }
--------------------------------------------------------------------------------
/app/frontend/src/components/Example/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./Example";
2 | export * from "./ExampleList";
3 |
--------------------------------------------------------------------------------
/docs/assets/java.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wellswang2/agent-openai-assistant/HEAD/docs/assets/java.png
--------------------------------------------------------------------------------
/app/business-api/payment/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | transactions.api.url=${TRANSACTIONS_API_SERVER_URL}
--------------------------------------------------------------------------------
/docs/assets/HLA-MCP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wellswang2/agent-openai-assistant/HEAD/docs/assets/HLA-MCP.png
--------------------------------------------------------------------------------
/app/business-api/account/applicationinsights.json:
--------------------------------------------------------------------------------
1 | {
2 | "role": {
3 | "name": "accounts-api"
4 | }
5 | }
--------------------------------------------------------------------------------
/app/business-api/payment/applicationinsights.json:
--------------------------------------------------------------------------------
1 | {
2 | "role": {
3 | "name": "payments-api"
4 | }
5 | }
--------------------------------------------------------------------------------
/docs/assets/azd-success.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wellswang2/agent-openai-assistant/HEAD/docs/assets/azd-success.png
--------------------------------------------------------------------------------
/docs/assets/langchain4j.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wellswang2/agent-openai-assistant/HEAD/docs/assets/langchain4j.png
--------------------------------------------------------------------------------
/app/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {}
6 | }
7 |
--------------------------------------------------------------------------------
/docs/assets/multi-agents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wellswang2/agent-openai-assistant/HEAD/docs/assets/multi-agents.png
--------------------------------------------------------------------------------
/docs/assets/robot-agents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wellswang2/agent-openai-assistant/HEAD/docs/assets/robot-agents.png
--------------------------------------------------------------------------------
/app/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wellswang2/agent-openai-assistant/HEAD/app/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/app/frontend/src/components/AnalysisPanel/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./AnalysisPanel";
2 | export * from "./AnalysisPanelTabs";
3 |
--------------------------------------------------------------------------------
/docs/assets/robot-agents-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wellswang2/agent-openai-assistant/HEAD/docs/assets/robot-agents-small.png
--------------------------------------------------------------------------------
/app/business-api/payment/src/main/resources/application-dev.properties:
--------------------------------------------------------------------------------
1 | server.port=8060
2 |
3 | logging.level.org.springframework.web=DEBUG
--------------------------------------------------------------------------------
/app/business-api/transactions-history/applicationinsights.json:
--------------------------------------------------------------------------------
1 | {
2 | "role": {
3 | "name": "transactions-api"
4 | }
5 | }
--------------------------------------------------------------------------------
/docs/assets/transaction-tracing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wellswang2/agent-openai-assistant/HEAD/docs/assets/transaction-tracing.png
--------------------------------------------------------------------------------
/app/frontend/src/components/Answer/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./Answer";
2 | export * from "./AnswerLoading";
3 | export * from "./AnswerError";
4 |
--------------------------------------------------------------------------------
/app/business-api/transactions-history/src/main/resources/application-dev.properties:
--------------------------------------------------------------------------------
1 | server.port=8090
2 |
3 | logging.level.org.springframework.web=DEBUG
--------------------------------------------------------------------------------
/app/frontend/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "printWidth": 160,
4 | "arrowParens": "avoid",
5 | "trailingComma": "none"
6 | }
7 |
--------------------------------------------------------------------------------
/app/frontend/src/pages/NoPage.tsx:
--------------------------------------------------------------------------------
1 | export function Component(): JSX.Element {
2 | return
404
;
3 | }
4 |
5 | Component.displayName = "NoPage";
6 |
--------------------------------------------------------------------------------
/app/frontend/src/components/AttachmentType.ts:
--------------------------------------------------------------------------------
1 | export type AttachmentType = {
2 | name: string;
3 | file: File; //Reference to the javascript File object.
4 | };
--------------------------------------------------------------------------------
/app/frontend/src/components/LoginButton/LoginButton.module.css:
--------------------------------------------------------------------------------
1 | .loginButton {
2 | border-radius: 5px;
3 | padding: 30px 30px;
4 | font-weight: 100;
5 | }
6 |
--------------------------------------------------------------------------------
/app/frontend/src/components/ClearChatButton/ClearChatButton.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | align-items: center;
4 | gap: 6px;
5 | cursor: pointer;
6 | }
7 |
--------------------------------------------------------------------------------
/app/frontend/src/components/SettingsButton/SettingsButton.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | align-items: center;
4 | gap: 6px;
5 | cursor: pointer;
6 | }
7 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [project-title] Changelog
2 |
3 |
4 | # x.y.z (yyyy-mm-dd)
5 |
6 | *Features*
7 | * ...
8 |
9 | *Bug Fixes*
10 | * ...
11 |
12 | *Breaking Changes*
13 | * ...
14 |
--------------------------------------------------------------------------------
/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/mcp/MCPProtocolType.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.langchain4j.agent.mcp;
2 |
3 | public enum MCPProtocolType {
4 | SSE,
5 | STDIO
6 | }
7 |
--------------------------------------------------------------------------------
/app/frontend/src/components/AnalysisPanel/AnalysisPanelTabs.tsx:
--------------------------------------------------------------------------------
1 | export enum AnalysisPanelTabs {
2 | ThoughtProcessTab = "thoughtProcess",
3 | SupportingContentTab = "supportingContent",
4 | CitationTab = "citation"
5 | }
6 |
--------------------------------------------------------------------------------
/app/frontend/src/components/QuestionInput/QuestionContext.ts:
--------------------------------------------------------------------------------
1 | import { AttachmentType } from "../AttachmentType";
2 |
3 | export type QuestionContextType = {
4 | question: string;
5 | attachments?: string[];
6 |
7 | };
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # These owners will be the default owners for everything in
2 | # this repo. Unless a later match takes precedence,
3 | # @global-owner1 and @global-owner2 will be requested for
4 | # review when someone opens a pull request.
5 | * @dantelmomsft
6 |
--------------------------------------------------------------------------------
/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/AgentMetadata.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.langchain4j.agent;
2 |
3 | import java.util.List;
4 |
5 | public record AgentMetadata(String description, List intents) {
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/mcp/MCPServerMetadata.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.langchain4j.agent.mcp;
2 |
3 | public record MCPServerMetadata(String serverName, String url, MCPProtocolType protocolType) {
4 | }
5 |
6 |
--------------------------------------------------------------------------------
/app/frontend/src/components/AnalysisPanel/AnalysisPanel.module.css:
--------------------------------------------------------------------------------
1 | .thoughtProcess {
2 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
3 | word-wrap: break-word;
4 | padding-top: 12px;
5 | padding-bottom: 12px;
6 | }
7 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/security/LoggedUser.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.security;
2 |
3 | public record LoggedUser(String username, String mail, String role, String displayName) {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/app/frontend/src/components/Answer/AnswerIcon.tsx:
--------------------------------------------------------------------------------
1 | import { Sparkle28Filled } from "@fluentui/react-icons";
2 |
3 | export const AnswerIcon = () => {
4 | return ;
5 | };
6 |
--------------------------------------------------------------------------------
/app/copilot/.mvn/jvm.config:
--------------------------------------------------------------------------------
1 | --add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
2 | --add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
3 | --add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
4 | --add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
5 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/AccountAgentIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant;
2 |
3 | public class AccountAgentIntegrationTest {
4 |
5 | public static void main(String[] args) {
6 |
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/controller/ChatAppRequestContext.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.controller;
3 |
4 | public record ChatAppRequestContext(ChatAppRequestOverrides overrides) {}
5 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/manifests/backend-service.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: backend-service
5 | namespace: azure-open-ai
6 | spec:
7 | type: ClusterIP
8 | ports:
9 | - protocol: TCP
10 | port: 80
11 | targetPort: 8080
12 | selector:
13 | app: backend
14 |
--------------------------------------------------------------------------------
/app/frontend/nginx/nginx.conf.template:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | location / {
4 | root /usr/share/nginx/html;
5 | index index.html index.htm;
6 | }
7 |
8 | location /api {
9 | proxy_ssl_server_name on;
10 | proxy_http_version 1.1;
11 | proxy_pass $REACT_APP_API_BASE_URL;
12 | }
13 | }
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/controller/ResponseContext.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.controller;
3 |
4 | import java.util.List;
5 |
6 | public record ResponseContext(String thoughts, List data_points) {}
7 |
--------------------------------------------------------------------------------
/app/frontend/manifests/frontend-service.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: frontend-service
5 | namespace: azure-open-ai
6 | spec:
7 | type: ClusterIP
8 | ports:
9 | - protocol: TCP
10 | port: 80
11 | targetPort: 80
12 | selector:
13 | app: frontend
14 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/controller/ResponseMessage.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.controller;
3 |
4 | import java.util.List;
5 |
6 | public record ResponseMessage(String content, String role, List attachments) {}
7 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/controller/ResponseChoice.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.controller;
3 |
4 | public record ResponseChoice(
5 | int index, ResponseMessage message, ResponseContext context, ResponseMessage delta) {}
6 |
--------------------------------------------------------------------------------
/app/frontend/Dockerfile-aks:
--------------------------------------------------------------------------------
1 | FROM node:18-alpine AS build
2 |
3 | # make the 'app' folder the current working directory
4 | WORKDIR /app
5 |
6 | COPY . .
7 |
8 |
9 | # install project dependencies
10 | RUN npm install
11 | RUN npm run build
12 |
13 | FROM nginx:alpine
14 |
15 | WORKDIR /usr/share/nginx/html
16 | COPY --from=build /app/build .
17 |
18 | EXPOSE 80
19 |
20 | CMD ["/bin/sh", "-c", "nginx -g \"daemon off;\""]
--------------------------------------------------------------------------------
/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/Agent.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.langchain4j.agent;
2 |
3 | import dev.langchain4j.data.message.ChatMessage;
4 |
5 | import java.util.List;
6 |
7 | public interface Agent {
8 |
9 | String getName();
10 | AgentMetadata getMetadata();
11 | List invoke(List chatHistory) throws AgentExecutionException;
12 | }
13 |
--------------------------------------------------------------------------------
/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/AgentExecutionException.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.langchain4j.agent;
2 |
3 | public class AgentExecutionException extends RuntimeException {
4 | public AgentExecutionException(String message) {
5 | super(message);
6 | }
7 |
8 | public AgentExecutionException(String message, Throwable cause) {
9 | super(message, cause);
10 | }
11 | }
--------------------------------------------------------------------------------
/app/frontend/src/components/Example/Example.tsx:
--------------------------------------------------------------------------------
1 | import styles from "./Example.module.css";
2 |
3 | interface Props {
4 | text: string;
5 | value: string;
6 | onClick: (value: string) => void;
7 | }
8 |
9 | export const Example = ({ text, value, onClick }: Props) => {
10 | return (
11 | onClick(value)}>
12 |
{text}
13 |
14 | );
15 | };
16 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/controller/ChatAppRequest.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.controller;
3 |
4 | import java.util.List;
5 |
6 | public record ChatAppRequest(
7 | List messages,
8 |
9 | List attachments,
10 | ChatAppRequestContext context,
11 | boolean stream,
12 | String approach) {}
13 |
--------------------------------------------------------------------------------
/app/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | GPT + Enterprise data | Java Sample
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/plugin/mock/PaymentTransaction.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.plugin.mock;
2 |
3 | public record PaymentTransaction(
4 | String id,
5 | String description,
6 | String recipientName,
7 | String recipientBankReference,
8 | String accountId,
9 | String paymentType,
10 | String amount,
11 | String timestamp
12 | ) {}
13 |
14 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Microsoft Open Source Code of Conduct
2 |
3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
4 |
5 | Resources:
6 |
7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
10 |
--------------------------------------------------------------------------------
/app/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18-alpine AS build
2 |
3 | # make the 'app' folder the current working directory
4 | WORKDIR /app
5 |
6 | COPY . .
7 |
8 | # install project dependencies
9 | RUN npm install
10 | RUN npm run build
11 |
12 | FROM nginx:alpine
13 |
14 | WORKDIR /usr/share/nginx/html
15 | COPY --from=build /app/build .
16 | COPY --from=build /app/nginx/nginx.conf.template /etc/nginx/conf.d
17 |
18 | EXPOSE 80
19 |
20 | CMD ["/bin/sh", "-c", "envsubst < /etc/nginx/conf.d/nginx.conf.template > /etc/nginx/conf.d/default.conf && nginx -g \"daemon off;\""]
21 |
--------------------------------------------------------------------------------
/app/frontend/src/components/SupportingContent/SupportingContentParser.ts:
--------------------------------------------------------------------------------
1 | type ParsedSupportingContentItem = {
2 | title: string;
3 | content: string;
4 | };
5 |
6 | export function parseSupportingContentItem(item: string): ParsedSupportingContentItem {
7 | // Assumes the item starts with the file name followed by : and the content.
8 | // Example: "sdp_corporate.pdf: this is the content that follows".
9 | const parts = item.split(": ");
10 | const title = parts[0];
11 | const content = parts.slice(1).join(": ");
12 |
13 | return {
14 | title,
15 | content
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/app/business-api/account/src/main/java/com/microsoft/openai/samples/assistant/business/models/Beneficiary.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.models;
2 |
3 |
4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
5 | import com.fasterxml.jackson.annotation.JsonProperty;
6 |
7 |
8 | @JsonIgnoreProperties(ignoreUnknown = true)
9 | public record Beneficiary(
10 | @JsonProperty("id") String id,
11 | @JsonProperty("fullName") String fullName,
12 | @JsonProperty("bankCode") String bankCode,
13 | @JsonProperty("bankName") String bankName
14 | ) {}
15 |
16 |
--------------------------------------------------------------------------------
/infra/shared/monitor/loganalytics.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates a Log Analytics workspace.'
2 | param name string
3 | param location string = resourceGroup().location
4 | param tags object = {}
5 |
6 | resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = {
7 | name: name
8 | location: location
9 | tags: tags
10 | properties: any({
11 | retentionInDays: 30
12 | features: {
13 | searchVersion: 1
14 | }
15 | sku: {
16 | name: 'PerGB2018'
17 | }
18 | })
19 | }
20 |
21 | output id string = logAnalytics.id
22 | output name string = logAnalytics.name
23 |
24 |
--------------------------------------------------------------------------------
/app/copilot/langchain4j-agents/src/test/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx",
18 | "types": ["vite/client"]
19 | },
20 | "include": ["src"]
21 | }
22 |
--------------------------------------------------------------------------------
/app/business-api/account/src/main/java/com/microsoft/openai/samples/assistant/business/models/PaymentMethodSummary.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.models;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 |
6 | @JsonIgnoreProperties(ignoreUnknown = true)
7 | public record PaymentMethodSummary(
8 | @JsonProperty("id") String id,
9 | @JsonProperty("type") String type,
10 | @JsonProperty("activationDate") String activationDate,
11 | @JsonProperty("expirationDate") String expirationDate
12 | ) {}
13 |
14 |
--------------------------------------------------------------------------------
/app/frontend/src/components/SettingsButton/SettingsButton.tsx:
--------------------------------------------------------------------------------
1 | import { Settings24Regular } from "@fluentui/react-icons";
2 | import { Button } from "@fluentui/react-components";
3 |
4 | import styles from "./SettingsButton.module.css";
5 |
6 | interface Props {
7 | className?: string;
8 | onClick: () => void;
9 | }
10 |
11 | export const SettingsButton = ({ className, onClick }: Props) => {
12 | return (
13 |
14 | } onClick={onClick}>
15 | {"Developer settings"}
16 |
17 |
18 | );
19 | };
20 |
--------------------------------------------------------------------------------
/app/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | html,
6 | body {
7 | height: 100%;
8 | margin: 0;
9 | padding: 0;
10 | }
11 |
12 | html {
13 | background: #f2f2f2;
14 |
15 | font-family:
16 | "Segoe UI",
17 | -apple-system,
18 | BlinkMacSystemFont,
19 | "Roboto",
20 | "Oxygen",
21 | "Ubuntu",
22 | "Cantarell",
23 | "Fira Sans",
24 | "Droid Sans",
25 | "Helvetica Neue",
26 | sans-serif;
27 | -webkit-font-smoothing: antialiased;
28 | -moz-osx-font-smoothing: grayscale;
29 | }
30 |
31 | #root {
32 | height: 100%;
33 | }
34 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/controller/auth/AuthSetup.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.controller.auth;
3 |
4 | import org.springframework.web.bind.annotation.GetMapping;
5 | import org.springframework.web.bind.annotation.RestController;
6 |
7 | @RestController
8 | public class AuthSetup {
9 |
10 | @GetMapping("/api/auth_setup")
11 | public String authSetup() {
12 | return """
13 | {
14 | "useLogin": false
15 | }
16 | """
17 | .stripIndent();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/infra/shared/security/role.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates a role assignment for a service principal.'
2 | param principalId string
3 |
4 | @allowed([
5 | 'Device'
6 | 'ForeignGroup'
7 | 'Group'
8 | 'ServicePrincipal'
9 | 'User'
10 | ])
11 | param principalType string = 'ServicePrincipal'
12 | param roleDefinitionId string
13 |
14 | resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
15 | name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId)
16 | properties: {
17 | principalId: principalId
18 | principalType: principalType
19 | roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/infra/shared/security/keyvault-access.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Assigns an Azure Key Vault access policy.'
2 | param name string = 'add'
3 |
4 | param keyVaultName string
5 | param permissions object = { secrets: [ 'get', 'list' ] }
6 | param principalId string
7 |
8 | resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = {
9 | parent: keyVault
10 | name: name
11 | properties: {
12 | accessPolicies: [ {
13 | objectId: principalId
14 | tenantId: subscription().tenantId
15 | permissions: permissions
16 | } ]
17 | }
18 | }
19 |
20 | resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
21 | name: keyVaultName
22 | }
23 |
--------------------------------------------------------------------------------
/app/frontend/src/components/ClearChatButton/ClearChatButton.tsx:
--------------------------------------------------------------------------------
1 | import { Delete24Regular } from "@fluentui/react-icons";
2 | import { Button } from "@fluentui/react-components";
3 |
4 | import styles from "./ClearChatButton.module.css";
5 |
6 | interface Props {
7 | className?: string;
8 | onClick: () => void;
9 | disabled?: boolean;
10 | }
11 |
12 | export const ClearChatButton = ({ className, disabled, onClick }: Props) => {
13 | return (
14 |
15 | } disabled={disabled} onClick={onClick}>
16 | {"Clear chat"}
17 |
18 |
19 | );
20 | };
21 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/controller/ChatAppRequestOverrides.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.controller;
3 |
4 | public record ChatAppRequestOverrides(
5 | boolean semantic_ranker,
6 | boolean semantic_captions,
7 | String exclude_category,
8 | int top,
9 | float temperature,
10 | String prompt_template,
11 | String prompt_template_prefix,
12 | String prompt_template_suffix,
13 | boolean suggest_followup_questions,
14 | boolean use_oid_security_filter,
15 | boolean use_groups_security_filter,
16 | String semantic_kernel_mode) {}
17 |
--------------------------------------------------------------------------------
/app/frontend/src/components/SupportingContent/SupportingContent.module.css:
--------------------------------------------------------------------------------
1 | .supportingContentNavList {
2 | list-style: none;
3 | padding-left: 5px;
4 | display: flex;
5 | flex-direction: column;
6 | gap: 10px;
7 | }
8 |
9 | .supportingContentItem {
10 | word-break: break-word;
11 | background: rgb(249, 249, 249);
12 | border-radius: 8px;
13 | box-shadow:
14 | rgb(0 0 0 / 5%) 0px 0px 0px 1px,
15 | rgb(0 0 0 / 10%) 0px 2px 3px 0px;
16 | outline: transparent solid 1px;
17 |
18 | display: flex;
19 | flex-direction: column;
20 | padding: 20px;
21 | }
22 |
23 | .supportingContentItemHeader {
24 | margin: 0;
25 | }
26 |
27 | .supportingContentItemText {
28 | margin-bottom: 0;
29 | font-weight: 300;
30 | }
31 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/manifests/backend-deployment.tmpl.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: backend-deployment
5 | namespace: azure-open-ai
6 | labels:
7 | app: backend
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: backend
13 | template:
14 | metadata:
15 | labels:
16 | app: backend
17 | spec:
18 | containers:
19 | - name: backend
20 | image: {{.Env.SERVICE_API_IMAGE_NAME}}
21 | imagePullPolicy: IfNotPresent
22 | ports:
23 | - containerPort: 8080
24 | envFrom:
25 | - configMapRef:
26 | name: azd-env-configmap
27 | resources:
28 | requests:
29 | memory: "2Gi"
30 |
--------------------------------------------------------------------------------
/app/frontend/src/components/UserChatMessage/UserChatMessage.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | justify-content: flex-end;
4 | margin-bottom: 20px;
5 | max-width: 80%;
6 | margin-left: auto;
7 | }
8 |
9 | .message {
10 | padding: 20px;
11 | background: #e8ebfa;
12 | border-radius: 8px;
13 | box-shadow:
14 | 0px 2px 4px rgba(0, 0, 0, 0.14),
15 | 0px 0px 2px rgba(0, 0, 0, 0.12);
16 | outline: transparent solid 1px;
17 | }
18 |
19 | .attachementPreview {
20 | display: flex;
21 | justify-content: flex-end;
22 | margin-left: auto;
23 | border: 1px solid #ddd; /* Gray border */
24 | border-radius: 4px; /* Rounded border */
25 | padding: 5px; /* Some padding */
26 | height: 500px; /* Set a small width */
27 | }
--------------------------------------------------------------------------------
/app/frontend/manifests/frontend-deployment.tmpl.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: frontend-deployment
5 | namespace: azure-open-ai
6 | labels:
7 | app: frontend
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: frontend
13 | template:
14 | metadata:
15 | labels:
16 | app: frontend
17 | spec:
18 | containers:
19 | - name: frontend
20 | image: {{.Env.SERVICE_FRONTEND_IMAGE_NAME}}
21 | imagePullPolicy: IfNotPresent
22 | ports:
23 | - containerPort: 80
24 | envFrom:
25 | - configMapRef:
26 | name: azd-env-configmap
27 |
--------------------------------------------------------------------------------
/app/business-api/payment/src/main/java/com/microsoft/openai/samples/assistant/business/mcp/config/MCPServerConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.mcp.config;
2 |
3 | import com.microsoft.openai.samples.assistant.business.mcp.server.PaymentMCPService;
4 | import org.springframework.ai.tool.ToolCallbackProvider;
5 | import org.springframework.ai.tool.method.MethodToolCallbackProvider;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | @Configuration
10 | public class MCPServerConfiguration {
11 |
12 | @Bean
13 | public ToolCallbackProvider paymentTools(PaymentMCPService paymentMCPService) {
14 | return MethodToolCallbackProvider.builder().toolObjects(paymentMCPService).build();
15 | }
16 | }
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/manifests/ingress.yml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: ingress-api
5 | namespace: azure-open-ai
6 | # annotations:
7 | # nginx.ingress.kubernetes.io/use-regex: "true"
8 | # nginx.ingress.kubernetes.io/rewrite-target: /$2
9 | spec:
10 | ingressClassName: webapprouting.kubernetes.azure.com
11 | rules:
12 | - http:
13 | paths:
14 | - path: /api
15 | pathType: Prefix
16 | backend:
17 | service:
18 | name: backend-service
19 | port:
20 | number: 80
21 | - path: /
22 | pathType: Prefix
23 | backend:
24 | service:
25 | name: frontend-service
26 | port:
27 | number: 80
--------------------------------------------------------------------------------
/app/frontend/src/components/Answer/AnswerError.tsx:
--------------------------------------------------------------------------------
1 | import { Stack, PrimaryButton } from "@fluentui/react";
2 | import { ErrorCircle24Regular } from "@fluentui/react-icons";
3 |
4 | import styles from "./Answer.module.css";
5 |
6 | interface Props {
7 | error: string;
8 | onRetry: () => void;
9 | }
10 |
11 | export const AnswerError = ({ error, onRetry }: Props) => {
12 | return (
13 |
14 |
15 |
16 |
17 | {error}
18 |
19 |
20 |
21 |
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/app/business-api/payment/src/main/java/com/microsoft/openai/samples/assistant/business/models/Payment.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.models;
2 |
3 |
4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
5 | import com.fasterxml.jackson.annotation.JsonProperty;
6 |
7 | @JsonIgnoreProperties(ignoreUnknown = true)
8 | public record Payment(
9 | @JsonProperty("description") String description,
10 | @JsonProperty("recipientName") String recipientName,
11 | @JsonProperty("recipientBankCode") String recipientBankCode,
12 | @JsonProperty("accountId") String accountId,
13 | @JsonProperty("paymentMethodId") String paymentMethodId,
14 | @JsonProperty("paymentType") String paymentType,
15 | @JsonProperty("amount") String amount,
16 | @JsonProperty("timestamp") String timestamp
17 | ) {}
--------------------------------------------------------------------------------
/app/business-api/transactions-history/src/main/java/com/microsoft/openai/samples/assistant/business/mcp/config/MCPServerConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.mcp.config;
2 |
3 | import com.microsoft.openai.samples.assistant.business.mcp.server.TransactionMCPService;
4 | import org.springframework.ai.tool.ToolCallbackProvider;
5 | import org.springframework.ai.tool.method.MethodToolCallbackProvider;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | @Configuration
10 | public class MCPServerConfiguration {
11 |
12 | @Bean
13 | public ToolCallbackProvider transactionTools(TransactionMCPService transactionMCPService) {
14 | return MethodToolCallbackProvider.builder().toolObjects(transactionMCPService).build();
15 | }
16 | }
--------------------------------------------------------------------------------
/app/business-api/payment/src/main/java/com/microsoft/openai/samples/assistant/business/PaymentApplication.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.business;
3 |
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.autoconfigure.SpringBootApplication;
8 |
9 | @SpringBootApplication
10 | public class PaymentApplication {
11 |
12 | private static final Logger LOG = LoggerFactory.getLogger(PaymentApplication.class);
13 |
14 | public static void main(String[] args) {
15 | LOG.info(
16 | "Application profile from system property is [{}]",
17 | System.getProperty("spring.profiles.active"));
18 | new SpringApplication(PaymentApplication.class).run(args);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/infra/shared/security/registry-access.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.'
2 | param containerRegistryName string
3 | param principalId string
4 |
5 | var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
6 |
7 | resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
8 | scope: containerRegistry // Use when specifying a scope that is different than the deployment scope
9 | name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole)
10 | properties: {
11 | roleDefinitionId: acrPullRole
12 | principalType: 'ServicePrincipal'
13 | principalId: principalId
14 | }
15 | }
16 |
17 | resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' existing = {
18 | name: containerRegistryName
19 | }
20 |
--------------------------------------------------------------------------------
/app/business-api/payment/src/main/java/com/microsoft/openai/samples/assistant/business/mcp/server/PaymentMCPService.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.mcp.server;
2 |
3 |
4 | import com.microsoft.openai.samples.assistant.business.models.Payment;
5 | import com.microsoft.openai.samples.assistant.business.service.PaymentService;
6 | import org.springframework.ai.tool.annotation.Tool;
7 |
8 | import org.springframework.stereotype.Service;
9 |
10 | @Service
11 | public class PaymentMCPService {
12 |
13 | private final PaymentService paymentService;
14 |
15 | public PaymentMCPService(PaymentService paymentService) {
16 | this.paymentService = paymentService;
17 | }
18 |
19 | @Tool(description = "Submit a payment request")
20 | public void processPayment(Payment payment){
21 | paymentService.processPayment(payment);
22 | }
23 | }
--------------------------------------------------------------------------------
/app/frontend/src/components/QuestionInput/QuestionInput.module.css:
--------------------------------------------------------------------------------
1 | .questionInputContainer {
2 | border-radius: 8px;
3 | box-shadow:
4 | 0px 8px 16px rgba(0, 0, 0, 0.14),
5 | 0px 0px 2px rgba(0, 0, 0, 0.12);
6 | height: 150px;
7 | width: 100%;
8 | padding: 15px;
9 | background: white;
10 | }
11 |
12 | .questionInputTextArea {
13 | width: 100%;
14 | line-height: 40px;
15 | }
16 |
17 | .questionInputButtonsContainer {
18 | display: flex;
19 | flex-direction: column;
20 | justify-content: flex-end;
21 | }
22 |
23 | .attachmentContainer {
24 | display: flex;
25 | justify-content: space-between;
26 | align-items: center;
27 | }
28 |
29 | .imagePreview {
30 | border: 1px solid #ddd; /* Gray border */
31 | border-radius: 4px; /* Rounded border */
32 | padding: 5px; /* Some padding */
33 | height: 130px; /* Set a small width */
34 | }
--------------------------------------------------------------------------------
/app/frontend/src/assets/search.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/business-api/account/src/main/java/com/microsoft/openai/samples/assistant/business/models/PaymentMethod.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.models;
2 |
3 |
4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
5 | import com.fasterxml.jackson.annotation.JsonInclude;
6 | import com.fasterxml.jackson.annotation.JsonProperty;
7 |
8 | @JsonInclude(JsonInclude.Include.NON_NULL)
9 | @JsonIgnoreProperties(ignoreUnknown = true)
10 | public record PaymentMethod(
11 | @JsonProperty("id") String id,
12 | @JsonProperty("type") String type,
13 | @JsonProperty("activationDate") String activationDate,
14 | @JsonProperty("expirationDate") String expirationDate,
15 | @JsonProperty("availableBalance") String availableBalance,
16 | // card number is valued only for credit card type
17 | @JsonProperty("cardNumber") String cardNumber
18 | ) {}
19 |
20 |
--------------------------------------------------------------------------------
/app/business-api/transactions-history/src/main/java/com/microsoft/openai/samples/assistant/business/TransactionsHistoryApplication.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.business;
3 |
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.autoconfigure.SpringBootApplication;
8 |
9 | @SpringBootApplication
10 | public class TransactionsHistoryApplication {
11 |
12 | private static final Logger LOG = LoggerFactory.getLogger(TransactionsHistoryApplication.class);
13 |
14 | public static void main(String[] args) {
15 | LOG.info(
16 | "Application profile from system property is [{}]",
17 | System.getProperty("spring.profiles.active"));
18 | new SpringApplication(TransactionsHistoryApplication.class).run(args);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/frontend/src/components/SupportingContent/SupportingContent.tsx:
--------------------------------------------------------------------------------
1 | import { parseSupportingContentItem } from "./SupportingContentParser";
2 |
3 | import styles from "./SupportingContent.module.css";
4 |
5 | interface Props {
6 | supportingContent: string[];
7 | }
8 |
9 | export const SupportingContent = ({ supportingContent }: Props) => {
10 | return (
11 |
12 | {supportingContent.map((x, i) => {
13 | const parsed = parseSupportingContentItem(x);
14 |
15 | return (
16 | -
17 |
{parsed.title}
18 | {parsed.content}
19 |
20 | );
21 | })}
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/app/business-api/account/src/main/java/com/microsoft/openai/samples/assistant/business/models/Account.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.models;
2 |
3 |
4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
5 | import com.fasterxml.jackson.annotation.JsonInclude;
6 | import com.fasterxml.jackson.annotation.JsonProperty;
7 |
8 | import java.util.List;
9 |
10 | @JsonInclude(JsonInclude.Include.NON_NULL)
11 | @JsonIgnoreProperties(ignoreUnknown = true)
12 | public record Account(
13 | @JsonProperty("id") String id,
14 | @JsonProperty("userName") String userName,
15 | @JsonProperty("accountHolderFullName") String accountHolderFullName,
16 | @JsonProperty("currency") String currency,
17 | @JsonProperty("activationDate") String activationDate,
18 | @JsonProperty("balance") String balance,
19 | List paymentMethods
20 | ) {}
21 |
22 |
--------------------------------------------------------------------------------
/app/business-api/transactions-history/src/main/java/com/microsoft/openai/samples/assistant/business/Transaction.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business;
2 |
3 |
4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
5 | import com.fasterxml.jackson.annotation.JsonProperty;
6 |
7 | @JsonIgnoreProperties(ignoreUnknown = true)
8 | public record Transaction(
9 | @JsonProperty("id") String id,
10 | @JsonProperty("description") String description,
11 | //income/outcome
12 | @JsonProperty("type") String type,
13 | @JsonProperty("recipientName") String recipientName,
14 | @JsonProperty("recipientBankReference") String recipientBankReference,
15 | @JsonProperty("accountId") String accountId,
16 | @JsonProperty("paymentType") String paymentType,
17 | @JsonProperty("amount") String amount,
18 | @JsonProperty("timestamp") String timestamp
19 | ) {}
--------------------------------------------------------------------------------
/infra/shared/security/keyvault-secret.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates or updates a secret in an Azure Key Vault.'
2 | param name string
3 | param tags object = {}
4 | param keyVaultName string
5 | param contentType string = 'string'
6 | @description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates')
7 | @secure()
8 | param secretValue string
9 |
10 | param enabled bool = true
11 | param exp int = 0
12 | param nbf int = 0
13 |
14 | resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = {
15 | name: name
16 | tags: tags
17 | parent: keyVault
18 | properties: {
19 | attributes: {
20 | enabled: enabled
21 | exp: exp
22 | nbf: nbf
23 | }
24 | contentType: contentType
25 | value: secretValue
26 | }
27 | }
28 |
29 | resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
30 | name: keyVaultName
31 | }
32 |
--------------------------------------------------------------------------------
/app/business-api/payment/src/main/java/com/microsoft/openai/samples/assistant/business/models/Transaction.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.models;
2 |
3 |
4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
5 | import com.fasterxml.jackson.annotation.JsonProperty;
6 |
7 | @JsonIgnoreProperties(ignoreUnknown = true)
8 | public record Transaction(
9 | @JsonProperty("id") String id,
10 | @JsonProperty("description") String description,
11 | //income/outcome
12 | @JsonProperty("type") String type,
13 | @JsonProperty("recipientName") String recipientName,
14 | @JsonProperty("recipientBankReference") String recipientBankReference,
15 | @JsonProperty("accountId") String accountId,
16 | @JsonProperty("paymentType") String paymentType,
17 | @JsonProperty("amount") String amount,
18 | @JsonProperty("timestamp") String timestamp
19 | ) {}
--------------------------------------------------------------------------------
/app/frontend/src/assets/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/frontend/src/components/Answer/AnswerLoading.tsx:
--------------------------------------------------------------------------------
1 | import { Stack } from "@fluentui/react";
2 | import { animated, useSpring } from "@react-spring/web";
3 |
4 | import styles from "./Answer.module.css";
5 | import { AnswerIcon } from "./AnswerIcon";
6 |
7 | export const AnswerLoading = () => {
8 | const animatedStyles = useSpring({
9 | from: { opacity: 0 },
10 | to: { opacity: 1 }
11 | });
12 |
13 | return (
14 |
15 |
16 |
17 |
18 |
19 | Generating answer
20 |
21 |
22 |
23 |
24 |
25 | );
26 | };
27 |
--------------------------------------------------------------------------------
/app/frontend/src/components/Example/Example.module.css:
--------------------------------------------------------------------------------
1 | .examplesNavList {
2 | list-style: none;
3 | padding-left: 0;
4 | display: flex;
5 | flex-wrap: wrap;
6 | gap: 10px;
7 | flex: 1;
8 | justify-content: center;
9 | }
10 |
11 | .example {
12 | word-break: break-word;
13 | background: #dbdbdb;
14 | border-radius: 8px;
15 | display: flex;
16 | flex-direction: column;
17 | padding: 20px;
18 | margin-bottom: 5px;
19 | cursor: pointer;
20 | }
21 |
22 | .example:hover {
23 | box-shadow:
24 | 0px 8px 16px rgba(0, 0, 0, 0.14),
25 | 0px 0px 2px rgba(0, 0, 0, 0.12);
26 | outline: 2px solid rgba(115, 118, 225, 1);
27 | }
28 |
29 | .exampleText {
30 | margin: 0;
31 | font-size: 22px;
32 | width: 280px;
33 | height: 100px;
34 | }
35 |
36 | @media only screen and (max-height: 780px) {
37 | .exampleText {
38 | font-size: 20px;
39 | height: 80px;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/business-api/account/src/main/java/com/microsoft/openai/samples/assistant/business/AccountApplication.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.business;
3 |
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.autoconfigure.SpringBootApplication;
8 | import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
9 |
10 | @SpringBootApplication
11 | public class AccountApplication {
12 |
13 | private static final Logger LOG = LoggerFactory.getLogger(AccountApplication.class);
14 |
15 | public static void main(String[] args) {
16 | LOG.info(
17 | "Application profile from system property is [{}]",
18 | System.getProperty("spring.profiles.active"));
19 | new SpringApplication(AccountApplication.class).run(args);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/common/ChatGPTMessage.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.common;
3 |
4 | import com.azure.core.util.ExpandableStringEnum;
5 |
6 | import java.util.Collection;
7 |
8 | public record ChatGPTMessage(ChatRole role, String content) {
9 |
10 | public static final class ChatRole extends ExpandableStringEnum {
11 | public static final ChatRole SYSTEM = fromString("system");
12 |
13 | public static final ChatRole ASSISTANT = fromString("assistant");
14 |
15 | public static final ChatRole USER = fromString("user");
16 |
17 | public static ChatRole fromString(String name) {
18 | return fromString(name, ChatRole.class);
19 | }
20 |
21 | public static Collection values() {
22 | return values(ChatRole.class);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/plugin/LoggedUserPlugin.java.sample:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.plugin;
2 |
3 | import com.microsoft.openai.samples.assistant.security.LoggedUser;
4 | import com.microsoft.openai.samples.assistant.security.LoggedUserService;
5 | import com.microsoft.semantickernel.semanticfunctions.annotations.DefineKernelFunction;
6 |
7 | public class LoggedUserPlugin {
8 |
9 | private final LoggedUserService loggedUserService;
10 | public LoggedUserPlugin(LoggedUserService loggedUserService)
11 | {
12 | this.loggedUserService = loggedUserService;
13 | }
14 | @DefineKernelFunction(name = "UserContext", description = "Gets the user details after login")
15 | public String getUserContext() {
16 | LoggedUser loggedUser = loggedUserService.getLoggedUser();
17 | return loggedUser.toString();
18 |
19 |
20 | }
21 |
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/CopilotApplication.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant;
3 |
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.autoconfigure.SpringBootApplication;
8 | import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
9 |
10 | @SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
11 |
12 | public class CopilotApplication {
13 |
14 | private static final Logger LOG = LoggerFactory.getLogger(CopilotApplication.class);
15 |
16 | public static void main(String[] args) {
17 | LOG.info(
18 | "Application profile from system property is [{}]",
19 | System.getProperty("spring.profiles.active"));
20 | new SpringApplication(CopilotApplication.class).run(args);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/frontend/src/components/Example/ExampleList.tsx:
--------------------------------------------------------------------------------
1 | import { Example } from "./Example";
2 |
3 | import styles from "./Example.module.css";
4 |
5 | export type ExampleModel = {
6 | text: string;
7 | value: string;
8 | };
9 |
10 | const EXAMPLES: ExampleModel[] = [
11 | { text: "I want to pay a bill", value: "I want to pay a bill"},
12 | { text: "what are this year payments ?", value: "what are this year payments ?" },
13 | { text: "What is the limit on my visa ?", value: "What is the limit on my visa ?" }
14 | ];
15 |
16 | interface Props {
17 | onExampleClicked: (value: string) => void;
18 | }
19 |
20 | export const ExampleList = ({ onExampleClicked }: Props) => {
21 | return (
22 |
23 | {EXAMPLES.map((x, i) => (
24 | -
25 |
26 |
27 | ))}
28 |
29 | );
30 | };
31 |
--------------------------------------------------------------------------------
/infra/shared/security/keyvault.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Azure Key Vault.'
2 | param name string
3 | param location string = resourceGroup().location
4 | param tags object = {}
5 |
6 | param principalId string = ''
7 |
8 | @description('Allow the key vault to be used for template deployment.')
9 | param enabledForDeployment bool = false
10 |
11 | resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
12 | name: name
13 | location: location
14 | tags: tags
15 | properties: {
16 | tenantId: subscription().tenantId
17 | sku: { family: 'A', name: 'standard' }
18 | accessPolicies: !empty(principalId) ? [
19 | {
20 | objectId: principalId
21 | permissions: { secrets: [ 'get', 'list' ] }
22 | tenantId: subscription().tenantId
23 | }
24 | ] : []
25 | enabledForDeployment: enabledForDeployment
26 | }
27 | }
28 |
29 | output endpoint string = keyVault.properties.vaultUri
30 | output id string = keyVault.id
31 | output name string = keyVault.name
32 |
--------------------------------------------------------------------------------
/azure.yaml:
--------------------------------------------------------------------------------
1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
2 |
3 | name: personal-finance-assistance-java
4 | metadata:
5 | template: agent-openai-java-banking-assistant@1.0.0-alpha
6 | services:
7 | copilot:
8 | project: app/copilot
9 | language: java
10 | host: containerapp
11 | docker:
12 | remoteBuild: true
13 | account:
14 | project: app/business-api/account
15 | language: java
16 | host: containerapp
17 | docker:
18 | remoteBuild: true
19 | payment:
20 | project: app/business-api/payment
21 | language: java
22 | host: containerapp
23 | docker:
24 | remoteBuild: true
25 | transaction:
26 | project: app/business-api/transactions-history
27 | language: java
28 | host: containerapp
29 | docker:
30 | remoteBuild: true
31 | web:
32 | project: app/frontend
33 | language: js
34 | host: containerapp
35 | docker:
36 | remoteBuild: true
37 |
--------------------------------------------------------------------------------
/app/copilot/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | wrapperVersion=3.3.2
18 | distributionType=only-script
19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip
20 |
--------------------------------------------------------------------------------
/app/business-api/account/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | wrapperVersion=3.3.2
18 | distributionType=only-script
19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip
20 |
--------------------------------------------------------------------------------
/app/business-api/payment/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | wrapperVersion=3.3.2
18 | distributionType=only-script
19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip
20 |
--------------------------------------------------------------------------------
/app/business-api/transactions-history/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | wrapperVersion=3.3.2
18 | distributionType=only-script
19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip
20 |
--------------------------------------------------------------------------------
/app/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "private": true,
4 | "version": "1.0.0-alpha",
5 | "type": "module",
6 | "engines": {
7 | "node": ">=14.0.0"
8 | },
9 | "scripts": {
10 | "dev": "vite --port=8081",
11 | "build": "tsc && vite build",
12 | "preview": "vite preview"
13 | },
14 | "dependencies": {
15 | "@azure/msal-browser": "^3.1.0",
16 | "@azure/msal-react": "^2.0.4",
17 | "@fluentui/react": "^8.112.5",
18 | "@fluentui/react-components": "^9.37.3",
19 | "@fluentui/react-icons": "^2.0.221",
20 | "@react-spring/web": "^9.7.3",
21 | "dompurify": "^3.2.4",
22 | "frontend": "file:",
23 | "ndjson-readablestream": "^1.0.7",
24 | "react": "^18.2.0",
25 | "react-dom": "^18.2.0",
26 | "react-router-dom": "^6.18.0",
27 | "scheduler": "^0.20.2"
28 | },
29 | "devDependencies": {
30 | "@types/dompurify": "^3.0.3",
31 | "@types/react": "^18.2.34",
32 | "@types/react-dom": "^18.2.14",
33 | "@vitejs/plugin-react": "^4.3.4",
34 | "prettier": "^3.0.3",
35 | "typescript": "^5.2.2",
36 | "vite": "^6.3.1"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/business-api/account/src/main/java/com/microsoft/openai/samples/assistant/business/mcp/config/MCPServerConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.mcp.config;
2 |
3 |
4 | import com.microsoft.openai.samples.assistant.business.mcp.server.AccountMCPService;
5 | import com.microsoft.openai.samples.assistant.business.mcp.server.UserMCPService;
6 | import org.springframework.ai.tool.ToolCallbackProvider;
7 | import org.springframework.ai.tool.method.MethodToolCallbackProvider;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.context.annotation.Configuration;
10 |
11 | @Configuration
12 | public class MCPServerConfiguration {
13 |
14 | @Bean
15 | public ToolCallbackProvider accountTools(AccountMCPService accountMCPService) {
16 | return MethodToolCallbackProvider.builder().toolObjects(accountMCPService).build();
17 | }
18 |
19 | @Bean
20 | public ToolCallbackProvider userTools(UserMCPService userMCPService) {
21 | return MethodToolCallbackProvider.builder().toolObjects(userMCPService).build();
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | #Used to enable mocked class to take precedence over real class in unit tests
2 | spring.main.lazy-initialization=true
3 |
4 | openai.service=${AZURE_OPENAI_SERVICE}
5 | openai.chatgpt.deployment=${AZURE_OPENAI_CHATGPT_DEPLOYMENT:gpt-4o}
6 | openai.tracing.enabled=${AZURE_OPENAI_TRACING_ENABLED:false}
7 |
8 | documentintelligence.service=${AZURE_DOCUMENT_INTELLIGENCE_SERVICE:example}
9 |
10 |
11 | storage-account.service=${AZURE_STORAGE_ACCOUNT}
12 | blob.container.name=${AZURE_STORAGE_CONTAINER:content}
13 |
14 | logging.level.com.microsoft.openai.samples.rag.ask.approaches.semantickernel=DEBUG
15 | logging.level.com.microsoft.semantickernel.samples.openapi.OpenAPIHttpRequestPlugin=DEBUG
16 |
17 | server.error.include-message=always
18 |
19 | # Support for User Assigned Managed identity
20 | azure.identity.client-id=${AZURE_CLIENT_ID:system-managed-identity}
21 |
22 | # MCP endpoints
23 | transactions.api.url=${TRANSACTIONS_API_SERVER_URL}/sse
24 | accounts.api.url=${ACCOUNTS_API_SERVER_URL}/sse
25 | payments.api.url=${PAYMENTS_API_SERVER_URL}/sse
--------------------------------------------------------------------------------
/app/frontend/src/components/UserChatMessage/UserChatMessage.tsx:
--------------------------------------------------------------------------------
1 | import styles from "./UserChatMessage.module.css";
2 | import { Stack} from "@fluentui/react";
3 | import { AttachmentType } from "../AttachmentType";
4 | import { getImage} from "../../api";
5 |
6 | interface Props {
7 | message: string;
8 | attachments?: string[];
9 | }
10 |
11 | //
12 |
13 | export const UserChatMessage = ({message, attachments}: Props) => {
14 | return (
15 | <>
16 | {attachments && (
17 | <>
18 | {attachments.map((attachment, index) => (
19 |
20 |
})
21 |
22 |
23 | ))}
24 | >
25 | )}
26 |
29 | >
30 | );
31 | };
32 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
4 | > Please provide us with the following information:
5 | > ---------------------------------------------------------------
6 |
7 | ### This issue is for a: (mark with an `x`)
8 | ```
9 | - [ ] bug report -> please search issues before submitting
10 | - [ ] feature request
11 | - [ ] documentation issue or request
12 | - [ ] regression (a behavior that used to work and stopped in a new release)
13 | ```
14 |
15 | ### Minimal steps to reproduce
16 | >
17 |
18 | ### Any log messages given by the failure
19 | >
20 |
21 | ### Expected/desired behavior
22 | >
23 |
24 | ### OS and Version?
25 | > Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?)
26 |
27 | ### azd version?
28 | > run `azd version` and copy paste here.
29 |
30 | ### Versions
31 | >
32 |
33 | ### Mention any other details that might be useful
34 |
35 | > ---------------------------------------------------------------
36 | > Thanks! We'll be in touch soon.
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Azure Samples
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/config/Langchain4JConfiguration.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.config;
3 |
4 |
5 | import com.azure.ai.openai.OpenAIClient;
6 |
7 | import dev.langchain4j.model.azure.AzureOpenAiChatModel;
8 | import dev.langchain4j.model.chat.ChatLanguageModel;
9 | import org.springframework.beans.factory.annotation.Value;
10 | import org.springframework.context.annotation.Bean;
11 | import org.springframework.context.annotation.Configuration;
12 |
13 | @Configuration
14 | public class Langchain4JConfiguration {
15 |
16 | @Value("${openai.chatgpt.deployment}")
17 | private String gptChatDeploymentModelId;
18 |
19 | @Bean
20 | public ChatLanguageModel chatLanguageModel(OpenAIClient azureOpenAICLient) {
21 |
22 | return AzureOpenAiChatModel.builder()
23 | .openAIClient(azureOpenAICLient)
24 | .deploymentName(gptChatDeploymentModelId)
25 | .temperature(0.3)
26 | .logRequestsAndResponses(true)
27 | .build();
28 | }
29 |
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/app/business-api/account/src/main/java/com/microsoft/openai/samples/assistant/business/mcp/server/UserMCPService.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.mcp.server;
2 |
3 | import com.microsoft.openai.samples.assistant.business.models.Account;
4 | import com.microsoft.openai.samples.assistant.business.service.UserService;
5 | import org.springframework.ai.tool.annotation.Tool;
6 | import org.springframework.ai.tool.annotation.ToolParam;
7 | import org.springframework.stereotype.Service;
8 |
9 | import java.util.Arrays;
10 | import java.util.HashMap;
11 | import java.util.List;
12 | import java.util.Map;
13 |
14 |
15 | @Service
16 | public class UserMCPService {
17 | private final UserService userService;
18 |
19 | public UserMCPService(UserService userService) {
20 | this.userService = userService;
21 | }
22 |
23 | @Tool(description = "Get the list of all accounts for a specific user")
24 | public List getAccountsByUserName(@ToolParam( description ="userName once the user has logged" ) String userName) {
25 | return userService.getAccountsByUserName(userName);
26 | }
27 |
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Purpose
2 |
3 | * ...
4 |
5 | ## Does this introduce a breaking change?
6 |
7 | ```
8 | [ ] Yes
9 | [ ] No
10 | ```
11 |
12 | ## Pull Request Type
13 | What kind of change does this Pull Request introduce?
14 |
15 |
16 | ```
17 | [ ] Bugfix
18 | [ ] Feature
19 | [ ] Code style update (formatting, local variables)
20 | [ ] Refactoring (no functional changes, no api changes)
21 | [ ] Documentation content changes
22 | [ ] Other... Please describe:
23 | ```
24 |
25 | ## How to Test
26 | * Get the code
27 |
28 | ```
29 | git clone [repo-address]
30 | cd [repo-name]
31 | git checkout [branch-name]
32 | npm install
33 | ```
34 |
35 | * Test the code
36 |
37 | ```
38 | ```
39 |
40 | ## What to Check
41 | Verify that the following are valid
42 | * ...
43 |
44 | ## Other Information
45 |
--------------------------------------------------------------------------------
/infra/shared/monitor/applicationinsights.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.'
2 | param name string
3 | param dashboardName string = ''
4 | param location string = resourceGroup().location
5 | param tags object = {}
6 | param logAnalyticsWorkspaceId string
7 |
8 | resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
9 | name: name
10 | location: location
11 | tags: tags
12 | kind: 'web'
13 | properties: {
14 | Application_Type: 'web'
15 | WorkspaceResourceId: logAnalyticsWorkspaceId
16 | }
17 | }
18 |
19 | module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) {
20 | name: 'application-insights-dashboard'
21 | params: {
22 | name: dashboardName
23 | location: location
24 | applicationInsightsName: applicationInsights.name
25 | }
26 | }
27 |
28 | output connectionString string = applicationInsights.properties.ConnectionString
29 | output id string = applicationInsights.id
30 | output instrumentationKey string = applicationInsights.properties.InstrumentationKey
31 | output name string = applicationInsights.name
32 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/plugin/PaymentMockPlugin.java.sample:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.plugin;
2 |
3 | import com.microsoft.semantickernel.semanticfunctions.annotations.DefineKernelFunction;
4 | import com.microsoft.semantickernel.semanticfunctions.annotations.KernelFunctionParameter;
5 |
6 | public class PaymentMockPlugin {
7 |
8 |
9 | @DefineKernelFunction(name = "payBill", description = "Gets the last payment transactions based on the payee, recipient name")
10 | public String submitBillPayment(
11 | @KernelFunctionParameter(name = "recipientName", description = "Name of the payee, recipient") String recipientName,
12 | @KernelFunctionParameter(name = "documentId", description = " the bill id or invoice number") String documentID,
13 | @KernelFunctionParameter(name = "amount", description = "the total amount to pay") String amount) {
14 |
15 | System.out.println("Bill payment executed for recipient: " + recipientName + " with documentId: " + documentID + " and amount: " + amount);
16 |
17 | return "Payment Successful";
18 |
19 | }
20 |
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
--------------------------------------------------------------------------------
/app/business-api/payment/src/main/java/com/microsoft/openai/samples/assistant/business/controller/PaymentsController.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.controller;
2 |
3 | import com.microsoft.openai.samples.assistant.business.models.Payment;
4 | import com.microsoft.openai.samples.assistant.business.service.PaymentService;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.http.ResponseEntity;
8 | import org.springframework.web.bind.annotation.PostMapping;
9 | import org.springframework.web.bind.annotation.RequestBody;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 | @RestController
13 | public class PaymentsController {
14 |
15 | private static final Logger logger = LoggerFactory.getLogger(PaymentsController.class);
16 |
17 | private final PaymentService paymentService;
18 |
19 | public PaymentsController(PaymentService paymentService) {
20 | this.paymentService = paymentService;
21 | }
22 |
23 | @PostMapping("/payments")
24 | public void submitPayment(@RequestBody Payment payment) {
25 | logger.info("Received payment request: {}", payment);
26 | paymentService.processPayment(payment);
27 | }
28 | }
--------------------------------------------------------------------------------
/app/copilot/copilot-common/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.microsoft.openai.samples.assistant
8 | copilot-parent
9 | 1.0.0-SNAPSHOT
10 |
11 |
12 | copilot-common
13 |
14 |
15 |
16 | com.azure
17 | azure-ai-documentintelligence
18 |
19 |
20 |
21 |
22 | org.json
23 | json
24 | 20240303
25 |
26 |
27 | com.azure
28 | azure-storage-blob
29 |
30 |
31 |
--------------------------------------------------------------------------------
/infra/shared/monitor/monitoring.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.'
2 | param logAnalyticsName string
3 | param applicationInsightsName string
4 | param applicationInsightsDashboardName string = ''
5 | param location string = resourceGroup().location
6 | param tags object = {}
7 |
8 | module logAnalytics 'loganalytics.bicep' = {
9 | name: 'loganalytics'
10 | params: {
11 | name: logAnalyticsName
12 | location: location
13 | tags: tags
14 | }
15 | }
16 |
17 | module applicationInsights 'applicationinsights.bicep' = {
18 | name: 'applicationinsights'
19 | params: {
20 | name: applicationInsightsName
21 | location: location
22 | tags: tags
23 | dashboardName: applicationInsightsDashboardName
24 | logAnalyticsWorkspaceId: logAnalytics.outputs.id
25 | }
26 | }
27 |
28 | output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString
29 | output applicationInsightsId string = applicationInsights.outputs.id
30 | output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey
31 | output applicationInsightsName string = applicationInsights.outputs.name
32 | output logAnalyticsWorkspaceId string = logAnalytics.outputs.id
33 | output logAnalyticsWorkspaceName string = logAnalytics.outputs.name
34 |
35 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/config/DocumentIntelligenceConfiguration.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.config;
3 |
4 | import com.azure.ai.documentintelligence.DocumentIntelligenceClient;
5 | import com.azure.ai.documentintelligence.DocumentIntelligenceClientBuilder;
6 | import com.azure.core.credential.TokenCredential;
7 | import org.springframework.beans.factory.annotation.Value;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.context.annotation.Configuration;
10 |
11 | @Configuration
12 | public class DocumentIntelligenceConfiguration {
13 |
14 | @Value("${documentintelligence.service}")
15 | String documentIntelligenceServiceName;
16 |
17 | final TokenCredential tokenCredential;
18 |
19 | public DocumentIntelligenceConfiguration(TokenCredential tokenCredential) {
20 | this.tokenCredential = tokenCredential;
21 | }
22 |
23 | @Bean
24 | public DocumentIntelligenceClient documentIntelligenceClient() {
25 | String endpoint = "https://%s.cognitiveservices.azure.com".formatted(documentIntelligenceServiceName);
26 |
27 | return new DocumentIntelligenceClientBuilder()
28 | .credential(tokenCredential)
29 | .endpoint(endpoint)
30 | .buildClient();
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Java 17 and maven 3.8.8 DevContainer to build Java RAG example with Azure AI",
3 | "image": "mcr.microsoft.com/devcontainers/java:1-17-bullseye",
4 | "features": {
5 | "azure-cli": "latest",
6 | "ghcr.io/azure/azure-dev/azd:latest": {
7 | "version": "1.9.1"
8 | },
9 | "ghcr.io/devcontainers/features/java:1": {
10 | "version": "none",
11 | "installMaven": true,
12 | "mavenVersion": "3.8.8"
13 | },
14 | "ghcr.io/devcontainers/features/node:1": {
15 | "version": "20.5.0"
16 | },
17 |
18 | "ghcr.io/devcontainers/features/git:1": {
19 | "version": "2.39.1"
20 | },
21 | "ghcr.io/devcontainers-contrib/features/typescript:2": {},
22 | "ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {},
23 | "docker-in-docker": {
24 | "version": "latest",
25 | "moby": true,
26 | "dockerDashComposeVersion": "v1"
27 | }
28 | },
29 | "customizations": {
30 | "vscode": {
31 | "extensions": [
32 | "GitHub.vscode-github-actions",
33 | "ms-azuretools.azure-dev",
34 | "ms-azuretools.vscode-bicep",
35 | "vscjava.vscode-java-pack",
36 | "amodio.tsl-problem-matcher"
37 | ]
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/app/business-api/account/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build
2 |
3 | WORKDIR /workspace/app
4 | EXPOSE 3100
5 |
6 | COPY mvnw .
7 | COPY .mvn .mvn
8 | COPY pom.xml .
9 | COPY src src
10 |
11 | RUN chmod +x ./mvnw
12 | # Convert CRLF to LF
13 | RUN sed -i 's/\r$//' ./mvnw
14 | RUN ./mvnw package -DskipTests
15 | RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
16 |
17 | RUN apt-get update && apt-get install -y curl
18 | RUN curl -LJ -o applicationinsights-agent-3.5.4.jar https://github.com/microsoft/ApplicationInsights-Java/releases/download/3.5.4/applicationinsights-agent-3.5.4.jar
19 | COPY applicationinsights.json .
20 |
21 | #for production deployment use mcr.microsoft.com/openjdk/jdk:17-distroless
22 | FROM mcr.microsoft.com/openjdk/jdk:17-distroless
23 |
24 | ARG DEPENDENCY=/workspace/app/target/dependency
25 | COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
26 | COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
27 | COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
28 | COPY --from=build /workspace/app/applicationinsights-agent-3.5.4.jar /app
29 | COPY --from=build /workspace/app/applicationinsights.json /app
30 |
31 | EXPOSE 8080
32 |
33 | ENTRYPOINT ["java","-javaagent:/app/applicationinsights-agent-3.5.4.jar","-noverify", "-XX:MaxRAMPercentage=70", "-XX:+UseParallelGC", "-XX:ActiveProcessorCount=2", "-cp","app:app/lib/*","com.microsoft.openai.samples.assistant.business.AccountApplication"]
--------------------------------------------------------------------------------
/app/business-api/payment/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build
2 |
3 | WORKDIR /workspace/app
4 | EXPOSE 3100
5 |
6 | COPY mvnw .
7 | COPY .mvn .mvn
8 | COPY pom.xml .
9 | COPY src src
10 |
11 | RUN chmod +x ./mvnw
12 | # Convert CRLF to LF
13 | RUN sed -i 's/\r$//' ./mvnw
14 | RUN ./mvnw package -DskipTests
15 | RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
16 |
17 | RUN apt-get update && apt-get install -y curl
18 | RUN curl -LJ -o applicationinsights-agent-3.5.4.jar https://github.com/microsoft/ApplicationInsights-Java/releases/download/3.5.4/applicationinsights-agent-3.5.4.jar
19 | COPY applicationinsights.json .
20 |
21 | #for production deployment use mcr.microsoft.com/openjdk/jdk:17-distroless
22 | FROM mcr.microsoft.com/openjdk/jdk:17-distroless
23 |
24 | ARG DEPENDENCY=/workspace/app/target/dependency
25 | COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
26 | COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
27 | COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
28 | COPY --from=build /workspace/app/applicationinsights-agent-3.5.4.jar /app
29 | COPY --from=build /workspace/app/applicationinsights.json /app
30 |
31 | EXPOSE 8080
32 |
33 | ENTRYPOINT ["java","-javaagent:/app/applicationinsights-agent-3.5.4.jar","-noverify", "-XX:MaxRAMPercentage=70", "-XX:+UseParallelGC", "-XX:ActiveProcessorCount=2", "-cp","app:app/lib/*","com.microsoft.openai.samples.assistant.business.PaymentApplication"]
--------------------------------------------------------------------------------
/app/business-api/transactions-history/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build
2 |
3 | WORKDIR /workspace/app
4 | EXPOSE 3100
5 |
6 | COPY mvnw .
7 | COPY .mvn .mvn
8 | COPY pom.xml .
9 | COPY src src
10 |
11 | RUN chmod +x ./mvnw
12 | # Convert CRLF to LF
13 | RUN sed -i 's/\r$//' ./mvnw
14 | RUN ./mvnw package -DskipTests
15 | RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
16 |
17 | #install curl
18 | RUN apt-get update && apt-get install -y curl
19 | RUN curl -LJ -o applicationinsights-agent-3.5.4.jar https://github.com/microsoft/ApplicationInsights-Java/releases/download/3.5.4/applicationinsights-agent-3.5.4.jar
20 | COPY applicationinsights.json .
21 |
22 | #for production deployment use mcr.microsoft.com/openjdk/jdk:17-distroless
23 | FROM mcr.microsoft.com/openjdk/jdk:17-distroless
24 |
25 | ARG DEPENDENCY=/workspace/app/target/dependency
26 | COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
27 | COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
28 | COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
29 | COPY --from=build /workspace/app/applicationinsights-agent-3.5.4.jar /app
30 | COPY --from=build /workspace/app/applicationinsights.json /app
31 |
32 | EXPOSE 8080
33 |
34 | ENTRYPOINT ["java","-javaagent:/app/applicationinsights-agent-3.5.4.jar","-noverify", "-XX:MaxRAMPercentage=70", "-XX:+UseParallelGC", "-XX:ActiveProcessorCount=2", "-cp","app:app/lib/*","com.microsoft.openai.samples.assistant.business.TransactionsHistoryApplication"]
--------------------------------------------------------------------------------
/app/business-api/account/src/main/java/com/microsoft/openai/samples/assistant/business/controller/UserController.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.controller;
2 |
3 | import com.microsoft.openai.samples.assistant.business.models.Account;
4 | import com.microsoft.openai.samples.assistant.business.service.UserService;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.web.bind.annotation.GetMapping;
9 | import org.springframework.web.bind.annotation.PathVariable;
10 | import org.springframework.web.bind.annotation.RequestMapping;
11 | import org.springframework.web.bind.annotation.RestController;
12 |
13 | import java.util.List;
14 |
15 | @RestController
16 | @RequestMapping("/users")
17 | public class UserController {
18 |
19 | private final UserService userService;
20 | private static final Logger logger = LoggerFactory.getLogger(UserController.class);
21 |
22 |
23 | @Autowired
24 | public UserController(UserService userService) {
25 | this.userService = userService;
26 | }
27 | @GetMapping("/{userName}/accounts")
28 | public List getAccountsByUserName(@PathVariable String userName) {
29 | // Implement the logic to get the list of all accounts for a specific user
30 | logger.info("Received request to get accounts for user: {}", userName);
31 | return userService.getAccountsByUserName(userName);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/business-api/transactions-history/src/main/java/com/microsoft/openai/samples/assistant/business/mcp/server/TransactionMCPService.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.mcp.server;
2 |
3 | import com.microsoft.openai.samples.assistant.business.Transaction;
4 | import com.microsoft.openai.samples.assistant.business.TransactionService;
5 | import org.springframework.stereotype.Service;
6 | import org.springframework.ai.tool.annotation.Tool;
7 | import org.springframework.ai.tool.annotation.ToolParam;
8 |
9 | import java.util.List;
10 |
11 | @Service
12 | public class TransactionMCPService {
13 |
14 | private final TransactionService transactionService;
15 |
16 | public TransactionMCPService(TransactionService transactionService) {
17 | this.transactionService = transactionService;
18 | }
19 |
20 | @Tool(description = "Get transactions by recipient name")
21 | public List getTransactionsByRecipientName(
22 | @ToolParam(description = "The account ID") String accountId,
23 | @ToolParam(description = "The recipient's name") String recipientName) {
24 | return transactionService.getTransactionsByRecipientName(accountId, recipientName);
25 | }
26 |
27 | @Tool(description = "Get the last transactions for an account")
28 | public List getLastTransactions(
29 | @ToolParam(description = "The account ID") String accountId) {
30 | return transactionService.getlastTransactions(accountId);
31 | }
32 | }
--------------------------------------------------------------------------------
/app/frontend/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import react from "@vitejs/plugin-react";
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | build: {
8 | outDir: "./build",
9 | emptyOutDir: true,
10 | sourcemap: true,
11 | rollupOptions: {
12 | output: {
13 | manualChunks: id => {
14 | if (id.includes("@fluentui/react-icons")) {
15 | return "fluentui-icons";
16 | } else if (id.includes("@fluentui/react")) {
17 | return "fluentui-react";
18 | } else if (id.includes("node_modules")) {
19 | return "vendor";
20 | }
21 | }
22 | }
23 | },
24 | target: "esnext"
25 | },
26 | server: {
27 | proxy: {
28 | "/api/ask": {
29 | target: 'http://localhost:8080',
30 | changeOrigin: true
31 | },
32 | "/api/chat": {
33 | target: 'http://localhost:8080',
34 | changeOrigin: true
35 | },
36 | "/api/content": {
37 | target: 'http://localhost:8080',
38 | changeOrigin: true
39 | },
40 | "/api/auth_setup": {
41 | target: 'http://localhost:8080',
42 | changeOrigin: true
43 | }
44 | }
45 | }
46 | });
47 |
--------------------------------------------------------------------------------
/app/frontend/src/pages/layout/Layout.module.css:
--------------------------------------------------------------------------------
1 | .layout {
2 | display: flex;
3 | flex-direction: column;
4 | height: 100%;
5 | }
6 |
7 | .header {
8 | background-color: #222222;
9 | color: #f2f2f2;
10 | }
11 |
12 | .headerContainer {
13 | display: flex;
14 | align-items: center;
15 | justify-content: space-around;
16 | margin-right: 12px;
17 | margin-left: 12px;
18 | }
19 |
20 | .headerTitleContainer {
21 | display: flex;
22 | align-items: center;
23 | margin-right: 40px;
24 | color: #f2f2f2;
25 | text-decoration: none;
26 | }
27 |
28 | .headerLogo {
29 | height: 40px;
30 | }
31 |
32 | .headerTitle {
33 | margin-left: 12px;
34 | font-weight: 600;
35 | }
36 |
37 | .headerNavList {
38 | display: flex;
39 | list-style: none;
40 | padding-left: 0;
41 | }
42 |
43 | .headerNavPageLink {
44 | color: #f2f2f2;
45 | text-decoration: none;
46 | opacity: 0.75;
47 |
48 | transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
49 | transition-duration: 500ms;
50 | transition-property: opacity;
51 | }
52 |
53 | .headerNavPageLink:hover {
54 | opacity: 1;
55 | }
56 |
57 | .headerNavPageLinkActive {
58 | color: #f2f2f2;
59 | text-decoration: none;
60 | }
61 |
62 | .headerNavLeftMargin {
63 | margin-left: 20px;
64 | }
65 |
66 | .headerRightText {
67 | font-weight: normal;
68 | margin-left: 40px;
69 | }
70 |
71 | .microsoftLogo {
72 | height: 23px;
73 | font-weight: 600;
74 | }
75 |
76 | .githubLogo {
77 | height: 20px;
78 | }
79 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/controller/ChatResponse.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.controller;
3 |
4 |
5 |
6 | import com.microsoft.openai.samples.assistant.common.ChatGPTMessage;
7 | import dev.langchain4j.data.message.AiMessage;
8 |
9 | import java.util.Collections;
10 | import java.util.List;
11 |
12 | public record ChatResponse(List choices) {
13 |
14 | public static ChatResponse buildChatResponse(AiMessage aiMessage) {
15 | List dataPoints = Collections.emptyList();
16 | String thoughts = "";
17 | List attachments = Collections.emptyList();
18 |
19 | return new ChatResponse(
20 | List.of(
21 | new ResponseChoice(
22 | 0,
23 | new ResponseMessage(
24 | aiMessage.text(),
25 | ChatGPTMessage.ChatRole.ASSISTANT.toString(),
26 | attachments
27 | ),
28 | new ResponseContext(thoughts, dataPoints),
29 | new ResponseMessage(
30 | aiMessage.text(),
31 | ChatGPTMessage.ChatRole.ASSISTANT.toString(),
32 | attachments))));
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/config/BlobStorageProxyConfiguration.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.config;
3 |
4 | import com.azure.ai.documentintelligence.DocumentIntelligenceClient;
5 | import com.azure.core.credential.TokenCredential;
6 | import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
7 | import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.AccountMCPAgent;
8 | import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.PaymentMCPAgent;
9 | import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.TransactionHistoryMCPAgent;
10 | import com.microsoft.openai.samples.assistant.proxy.BlobStorageProxy;
11 | import com.microsoft.openai.samples.assistant.security.LoggedUserService;
12 | import dev.langchain4j.model.chat.ChatLanguageModel;
13 | import org.springframework.beans.factory.annotation.Value;
14 | import org.springframework.context.annotation.Bean;
15 | import org.springframework.context.annotation.Configuration;
16 |
17 | @Configuration
18 | public class BlobStorageProxyConfiguration {
19 | @Value("${storage-account.service}")
20 | String storageAccountServiceName;
21 | @Value("${blob.container.name}")
22 | String containerName;
23 |
24 | @Bean
25 | public BlobStorageProxy blobStorageProxy(TokenCredential tokenCredential) {
26 | return new BlobStorageProxy(storageAccountServiceName,containerName,tokenCredential);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/config/AzureAuthenticationConfiguration.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.config;
3 |
4 | import com.azure.core.credential.TokenCredential;
5 | import com.azure.identity.AzureCliCredentialBuilder;
6 | import com.azure.identity.EnvironmentCredentialBuilder;
7 | import com.azure.identity.ManagedIdentityCredentialBuilder;
8 | import org.springframework.beans.factory.annotation.Value;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.context.annotation.Profile;
12 |
13 | @Configuration
14 | public class AzureAuthenticationConfiguration {
15 |
16 | @Value("${azure.identity.client-id}")
17 | String clientId;
18 |
19 | @Profile("dev")
20 | @Bean
21 | public TokenCredential localTokenCredential() {
22 | return new AzureCliCredentialBuilder().build();
23 | }
24 |
25 | @Profile("docker")
26 | @Bean
27 | public TokenCredential servicePrincipalTokenCredential() {
28 | return new EnvironmentCredentialBuilder().build();
29 | }
30 |
31 | @Bean
32 | @Profile("default")
33 | public TokenCredential managedIdentityTokenCredential() {
34 | if (this.clientId.equals("system-managed-identity"))
35 | return new ManagedIdentityCredentialBuilder().build();
36 | else
37 | return new ManagedIdentityCredentialBuilder().clientId(this.clientId).build();
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/copilot/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build
2 |
3 | WORKDIR /workspace/app
4 | EXPOSE 3100
5 |
6 | COPY mvnw .
7 | COPY .mvn .mvn
8 | COPY pom.xml .
9 | COPY copilot-backend copilot-backend
10 | COPY copilot-common copilot-common
11 | COPY langchain4j-agents langchain4j-agents
12 |
13 | RUN chmod +x ./mvnw
14 | # Convert CRLF to LF
15 | RUN sed -i 's/\r$//' ./mvnw
16 | RUN ./mvnw package -DskipTests
17 | RUN mkdir -p copilot-backend/target/dependency && (cd copilot-backend/target/dependency; jar -xf ../*.jar)
18 |
19 | #install curl
20 | RUN apt-get update && apt-get install -y curl
21 | RUN curl -LJ -o applicationinsights-agent-3.5.4.jar https://github.com/microsoft/ApplicationInsights-Java/releases/download/3.5.4/applicationinsights-agent-3.5.4.jar
22 | COPY applicationinsights.json .
23 |
24 | #for production deployment use mcr.microsoft.com/openjdk/jdk:17-distroless
25 | FROM mcr.microsoft.com/openjdk/jdk:17-distroless
26 |
27 | ARG DEPENDENCY=/workspace/app/copilot-backend/target/dependency/
28 | COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
29 | COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
30 | COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
31 | COPY --from=build /workspace/app/applicationinsights-agent-3.5.4.jar /app
32 | COPY --from=build /workspace/app/applicationinsights.json /app
33 | EXPOSE 8080
34 |
35 | ENTRYPOINT ["java","-javaagent:/app/applicationinsights-agent-3.5.4.jar","-noverify", "-XX:MaxRAMPercentage=70", "-XX:+UseParallelGC", "-XX:ActiveProcessorCount=2", "-cp","app:app/lib/*","com.microsoft.openai.samples.assistant.CopilotApplication"]
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/security/LoggedUserService.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.security;
2 |
3 | import org.springframework.security.authentication.AnonymousAuthenticationToken;
4 | import org.springframework.security.core.Authentication;
5 | import org.springframework.security.core.context.SecurityContextHolder;
6 | import org.springframework.stereotype.Component;
7 |
8 | @Component
9 | public class LoggedUserService {
10 |
11 | public LoggedUser getLoggedUser(){
12 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
13 |
14 | //this is always true in the PoC code
15 | if(authentication == null) {
16 | return getDefaultUser();
17 | }
18 | //this code is never executed in the PoC. It's a hook for future improvements requiring integration with authentication providers.
19 | if (!(authentication instanceof AnonymousAuthenticationToken)) {
20 | String currentUserName = authentication.getName();
21 |
22 | Object details = authentication.getDetails();
23 | //object should be cast to specific type based on the authentication provider
24 | return new LoggedUser(currentUserName, "changeme@contoso.com", "changeme", "changeme");
25 | }
26 | return getDefaultUser();
27 | }
28 |
29 | private LoggedUser getDefaultUser() {
30 | return new LoggedUser("bob.user@contoso.com", "bob.user@contoso.com", "generic", "Bob The User");
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/TransactionHistoryMCPAgentIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package dev.langchain4j.openapi.mcp;
2 |
3 | import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.TransactionHistoryMCPAgent;
4 | import dev.langchain4j.data.message.ChatMessage;
5 | import dev.langchain4j.data.message.UserMessage;
6 | import dev.langchain4j.model.azure.AzureOpenAiChatModel;
7 |
8 | import java.util.ArrayList;
9 |
10 | public class TransactionHistoryMCPAgentIntegrationTest {
11 |
12 | public static void main(String[] args) throws Exception {
13 |
14 | //Azure Open AI Chat Model
15 | var azureOpenAiChatModel = AzureOpenAiChatModel.builder()
16 | .apiKey(System.getenv("AZURE_OPENAI_KEY"))
17 | .endpoint(System.getenv("AZURE_OPENAI_ENDPOINT"))
18 | .deploymentName(System.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"))
19 | .temperature(0.3)
20 | .logRequestsAndResponses(true)
21 | .build();
22 |
23 | var transactionHistoryAgent = new TransactionHistoryMCPAgent(azureOpenAiChatModel,
24 | "bob.user@contoso.com",
25 | "http://localhost:8090/sse",
26 | "http://localhost:8070/sse");
27 |
28 | var chatHistory = new ArrayList();
29 |
30 |
31 | chatHistory.add(UserMessage.from("When was last time I've paid contoso?"));
32 | transactionHistoryAgent.invoke(chatHistory);
33 | System.out.println(chatHistory.get(chatHistory.size()-1));
34 |
35 |
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/business-api/account/src/main/java/com/microsoft/openai/samples/assistant/business/mcp/server/AccountMCPService.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.mcp.server;
2 |
3 | import com.microsoft.openai.samples.assistant.business.models.Account;
4 | import com.microsoft.openai.samples.assistant.business.models.Beneficiary;
5 | import com.microsoft.openai.samples.assistant.business.models.PaymentMethod;
6 | import com.microsoft.openai.samples.assistant.business.service.AccountService;
7 | import org.springframework.stereotype.Service;
8 | import org.springframework.ai.tool.annotation.Tool;
9 |
10 | import java.util.List;
11 |
12 | @Service
13 | public class AccountMCPService {
14 |
15 | private final AccountService accountService;
16 | public AccountMCPService(AccountService accountService) {
17 | this.accountService = accountService;
18 | }
19 |
20 | @Tool(description = "Get account details and available payment methods")
21 | public Account getAccountDetails(String accountId) {
22 | return this.accountService.getAccountDetails(accountId);
23 |
24 | }
25 |
26 | @Tool(description = "Get payment method detail with available balance")
27 | public PaymentMethod getPaymentMethodDetails(String paymentMethodId) {
28 | return this.accountService.getPaymentMethodDetails(paymentMethodId);
29 | }
30 |
31 | @Tool(description = "Get list of registered beneficiaries for a specific account")
32 | public List getRegisteredBeneficiary(String accountId) {
33 | return this.accountService.getRegisteredBeneficiary(accountId);
34 | }
35 | }
--------------------------------------------------------------------------------
/infra/shared/host/container-apps-environment.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Azure Container Apps environment.'
2 | param name string
3 | param location string = resourceGroup().location
4 | param tags object = {}
5 |
6 | @description('Name of the Application Insights resource')
7 | param applicationInsightsName string = ''
8 |
9 | @description('Specifies if Dapr is enabled')
10 | param daprEnabled bool = false
11 |
12 | @description('Name of the Log Analytics workspace')
13 | param logAnalyticsWorkspaceName string
14 |
15 | resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
16 | name: name
17 | location: location
18 | tags: tags
19 | properties: {
20 | appLogsConfiguration: {
21 | destination: 'log-analytics'
22 | logAnalyticsConfiguration: {
23 | customerId: logAnalyticsWorkspace.properties.customerId
24 | sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
25 | }
26 | }
27 | daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : ''
28 | }
29 | }
30 |
31 | resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
32 | name: logAnalyticsWorkspaceName
33 | }
34 |
35 | resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) {
36 | name: applicationInsightsName
37 | }
38 |
39 | output defaultDomain string = containerAppsEnvironment.properties.defaultDomain
40 | output id string = containerAppsEnvironment.id
41 | output name string = containerAppsEnvironment.name
42 |
--------------------------------------------------------------------------------
/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/AccountMCPAgentIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package dev.langchain4j.openapi.mcp;
2 |
3 | import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.AccountMCPAgent;
4 | import dev.langchain4j.data.message.ChatMessage;
5 | import dev.langchain4j.data.message.UserMessage;
6 | import dev.langchain4j.model.azure.AzureOpenAiChatModel;
7 |
8 | import java.util.ArrayList;
9 |
10 | public class AccountMCPAgentIntegrationTest {
11 |
12 | public static void main(String[] args) throws Exception {
13 |
14 | //Azure Open AI Chat Model
15 | var azureOpenAiChatModel = AzureOpenAiChatModel.builder()
16 | .apiKey(System.getenv("AZURE_OPENAI_KEY"))
17 | .endpoint(System.getenv("AZURE_OPENAI_ENDPOINT"))
18 | .deploymentName(System.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"))
19 | .temperature(0.3)
20 | .logRequestsAndResponses(true)
21 | .build();
22 |
23 | var accountAgent = new AccountMCPAgent(azureOpenAiChatModel,"bob.user@contoso.com","http://localhost:8070/sse");
24 |
25 | var chatHistory = new ArrayList();
26 | chatHistory.add(UserMessage.from("How much money do I have in my account?"));
27 |
28 | accountAgent.invoke(chatHistory);
29 | System.out.println(chatHistory.get(chatHistory.size()-1));
30 |
31 | chatHistory.add(UserMessage.from("what about my visa"));
32 | accountAgent.invoke(chatHistory);
33 | System.out.println(chatHistory.get(chatHistory.size()-1));
34 |
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/compose.yaml:
--------------------------------------------------------------------------------
1 | services:
2 | frontend:
3 | image: agent-openai-java-banking-assistant/frontend
4 | build: ./frontend
5 | environment:
6 | REACT_APP_API_BASE_URL: "http://copilot:8080"
7 | ports:
8 | - "80:80"
9 | copilot:
10 | image: agent-openai-java-banking-assistant/copilot-backend
11 | build: ./copilot
12 | environment:
13 | - AZURE_STORAGE_ACCOUNT=${AZURE_STORAGE_ACCOUNT}
14 | - AZURE_STORAGE_CONTAINER=${AZURE_STORAGE_CONTAINER}
15 | - AZURE_OPENAI_CHATGPT_MODEL=${AZURE_OPENAI_CHATGPT_MODEL}
16 | - AZURE_OPENAI_SERVICE=${AZURE_OPENAI_SERVICE}
17 | - AZURE_OPENAI_CHATGPT_DEPLOYMENT=${AZURE_OPENAI_CHATGPT_DEPLOYMENT}
18 | - AZURE_DOCUMENT_INTELLIGENCE_SERVICE=${AZURE_DOCUMENT_INTELLIGENCE_SERVICE}
19 | - spring_profiles_active=docker
20 | - ACCOUNTS_API_SERVER_URL=http://account:8080
21 | - PAYMENTS_API_SERVER_URL=http://payment:8080
22 | - TRANSACTIONS_API_SERVER_URL=http://transaction:8080
23 | - AZURE_CLIENT_ID=${servicePrincipal}
24 | - AZURE_CLIENT_SECRET=${servicePrincipalPassword}
25 | - AZURE_TENANT_ID=${servicePrincipalTenant}
26 | account:
27 | image: agent-openai-java-banking-assistant/business-account
28 | build:
29 | context: ./business-api/account
30 | payment:
31 | image: agent-openai-java-banking-assistant/business-payment
32 | build:
33 | context: ./business-api/payment
34 | environment:
35 | - TRANSACTIONS_API_SERVER_URL=http://transaction:8080
36 | transaction:
37 | image: agent-openai-java-banking-assistant/business-transaction-history
38 | build:
39 | context: ./business-api/transactions-history
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/business-api/transactions-history/src/main/java/com/microsoft/openai/samples/assistant/business/TransactionController.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.web.bind.annotation.*;
6 |
7 | import java.util.List;
8 |
9 | @RestController
10 | @RequestMapping("/transactions")
11 | public class TransactionController {
12 |
13 | private final TransactionService transactionService;
14 | private static final Logger logger = LoggerFactory.getLogger(TransactionController.class);
15 | public TransactionController(TransactionService transactionService) {
16 | this.transactionService = transactionService;
17 | }
18 |
19 | @GetMapping("/{accountId}")
20 | public List getTransactions(@PathVariable String accountId, @RequestParam(name = "recipient_name", required = false) String recipientName){
21 | logger.info("Received request to get transactions for accountid[{}]. Recipient filter is[{}]",accountId,recipientName);
22 | if(recipientName != null && !recipientName.isEmpty()){
23 | return transactionService.getTransactionsByRecipientName(accountId, recipientName);
24 | }
25 | else
26 | return transactionService.getlastTransactions(accountId);
27 | }
28 |
29 | @PostMapping("/{accountId}")
30 | public void notifyTransaction(@PathVariable String accountId, @RequestBody Transaction transaction){
31 | logger.info("Received request to notify transaction for accountid[{}]. {}", accountId,transaction);
32 | transactionService.notifyTransaction(accountId, transaction);
33 | }
34 |
35 |
36 | }
--------------------------------------------------------------------------------
/app/frontend/src/components/LoginButton/LoginButton.tsx:
--------------------------------------------------------------------------------
1 | import { DefaultButton } from "@fluentui/react";
2 | import { useMsal } from "@azure/msal-react";
3 |
4 | import styles from "./LoginButton.module.css";
5 | import { getRedirectUri, loginRequest } from "../../authConfig";
6 |
7 | export const LoginButton = () => {
8 | const { instance } = useMsal();
9 | const activeAccount = instance.getActiveAccount();
10 | const handleLoginPopup = () => {
11 | /**
12 | * When using popup and silent APIs, we recommend setting the redirectUri to a blank page or a page
13 | * that does not implement MSAL. Keep in mind that all redirect routes must be registered with the application
14 | * For more information, please follow this link: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/login-user.md#redirecturi-considerations
15 | */
16 | instance
17 | .loginPopup({
18 | ...loginRequest,
19 | redirectUri: getRedirectUri()
20 | })
21 | .catch(error => console.log(error));
22 | };
23 | const handleLogoutPopup = () => {
24 | instance
25 | .logoutPopup({
26 | mainWindowRedirectUri: "/", // redirects the top level app after logout
27 | account: instance.getActiveAccount()
28 | })
29 | .catch(error => console.log(error));
30 | };
31 | const logoutText = `Logout\n${activeAccount?.username}`;
32 | return (
33 |
38 | );
39 | };
40 |
--------------------------------------------------------------------------------
/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/tools/InvoiceScanTool.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.langchain4j.tools;
2 |
3 |
4 | import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
5 | import dev.langchain4j.agent.tool.P;
6 | import dev.langchain4j.agent.tool.Tool;
7 | import dev.langchain4j.agent.tool.ToolExecutionRequest;
8 | import dev.langchain4j.service.tool.ToolExecutor;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 |
16 | public class InvoiceScanTool {
17 |
18 | private static final Logger LOGGER = LoggerFactory.getLogger(InvoiceScanTool.class);
19 | private final DocumentIntelligenceInvoiceScanHelper documentIntelligenceInvoiceScanHelper;
20 | public InvoiceScanTool(DocumentIntelligenceInvoiceScanHelper documentIntelligenceInvoiceScanHelper) {
21 | this.documentIntelligenceInvoiceScanHelper = documentIntelligenceInvoiceScanHelper;
22 | }
23 | @Tool( "Extract the invoice or bill data scanning a photo or image")
24 | public String scanInvoice(
25 | @P("the path to the file containing the image or photo") String filePath) {
26 |
27 | Map scanData = null;
28 |
29 | try{
30 | scanData = documentIntelligenceInvoiceScanHelper.scan(filePath);
31 | } catch (Exception e) {
32 | LOGGER.warn("Error extracting data from invoice {}:", filePath,e);
33 | scanData = new HashMap<>();
34 | }
35 |
36 | LOGGER.info("scanInvoice tool: Data extracted {}:{}", filePath,scanData);
37 | return scanData.toString();
38 |
39 | }
40 |
41 |
42 |
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/config/DocumentIntelligenceInvoiceScanConfiguration.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.config;
3 |
4 | import com.azure.ai.documentintelligence.DocumentIntelligenceClient;
5 | import com.azure.ai.documentintelligence.DocumentIntelligenceClientBuilder;
6 | import com.azure.core.credential.TokenCredential;
7 | import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
8 | import com.microsoft.openai.samples.assistant.proxy.BlobStorageProxy;
9 | import org.springframework.beans.factory.annotation.Value;
10 | import org.springframework.context.annotation.Bean;
11 | import org.springframework.context.annotation.Configuration;
12 |
13 | @Configuration
14 | public class DocumentIntelligenceInvoiceScanConfiguration {
15 |
16 | @Value("${documentintelligence.service}")
17 | String documentIntelligenceServiceName;
18 |
19 | final TokenCredential tokenCredential;
20 |
21 | public DocumentIntelligenceInvoiceScanConfiguration(TokenCredential tokenCredential) {
22 | this.tokenCredential = tokenCredential;
23 | }
24 |
25 | @Bean
26 | public DocumentIntelligenceInvoiceScanHelper documentIntelligenceInvoiceScanHelper(BlobStorageProxy blobStorageProxy) {
27 |
28 | String endpoint = "https://%s.cognitiveservices.azure.com".formatted(documentIntelligenceServiceName);
29 |
30 | var documentIntelligenceClient = new DocumentIntelligenceClientBuilder()
31 | .credential(tokenCredential)
32 | .endpoint(endpoint)
33 | .buildClient();
34 |
35 | return new DocumentIntelligenceInvoiceScanHelper(documentIntelligenceClient,blobStorageProxy);
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/infra/app/web.bicep:
--------------------------------------------------------------------------------
1 | param name string
2 | param location string = resourceGroup().location
3 | param tags object = {}
4 |
5 | param identityName string
6 | param apiBaseUrl string
7 | param applicationInsightsName string
8 | param containerAppsEnvironmentName string
9 | param containerRegistryName string
10 | param serviceName string = 'web'
11 | param exists bool
12 |
13 | resource webIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
14 | name: identityName
15 | location: location
16 | }
17 |
18 | module app '../shared/host/container-app-upsert.bicep' = {
19 | name: '${serviceName}-container-app'
20 | params: {
21 | name: name
22 | location: location
23 | tags: union(tags, { 'azd-service-name': serviceName })
24 | identityType: 'UserAssigned'
25 | identityName: identityName
26 | exists: exists
27 | containerAppsEnvironmentName: containerAppsEnvironmentName
28 | containerRegistryName: containerRegistryName
29 | env: [
30 | {
31 | name: 'REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING'
32 | value: applicationInsights.properties.ConnectionString
33 | }
34 | {
35 | name: 'REACT_APP_API_BASE_URL'
36 | value: apiBaseUrl
37 | }
38 | {
39 | name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
40 | value: applicationInsights.properties.ConnectionString
41 | }
42 | ]
43 | targetPort: 80
44 | }
45 | }
46 |
47 | resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
48 | name: applicationInsightsName
49 | }
50 |
51 | output SERVICE_WEB_IDENTITY_PRINCIPAL_ID string = webIdentity.properties.principalId
52 | output SERVICE_WEB_NAME string = app.outputs.name
53 | output SERVICE_WEB_URI string = app.outputs.uri
54 | output SERVICE_WEB_IMAGE_NAME string = app.outputs.imageName
55 |
--------------------------------------------------------------------------------
/infra/shared/host/container-apps.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.'
2 | param name string
3 | param location string = resourceGroup().location
4 | param tags object = {}
5 |
6 | param containerAppsEnvironmentName string
7 | param containerRegistryName string
8 | param containerRegistryResourceGroupName string = ''
9 | param containerRegistryAdminUserEnabled bool = false
10 | param logAnalyticsWorkspaceName string
11 | param applicationInsightsName string = ''
12 | param daprEnabled bool = false
13 |
14 | var containerRegistryResourceGroupEvaluated = !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup()
15 |
16 | module containerAppsEnvironment 'container-apps-environment.bicep' = {
17 | name: '${name}-container-apps-environment'
18 | params: {
19 | name: containerAppsEnvironmentName
20 | location: location
21 | tags: tags
22 | logAnalyticsWorkspaceName: logAnalyticsWorkspaceName
23 | applicationInsightsName: applicationInsightsName
24 | daprEnabled: daprEnabled
25 | }
26 | }
27 |
28 | module containerRegistry 'container-registry.bicep' = {
29 | name: '${name}-container-registry'
30 | scope: containerRegistryResourceGroupEvaluated
31 | params: {
32 | name: containerRegistryName
33 | location: location
34 | adminUserEnabled: containerRegistryAdminUserEnabled
35 | tags: tags
36 | }
37 | }
38 |
39 | output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain
40 | output environmentName string = containerAppsEnvironment.outputs.name
41 | output environmentId string = containerAppsEnvironment.outputs.id
42 |
43 | output registryLoginServer string = containerRegistry.outputs.loginServer
44 | output registryName string = containerRegistry.outputs.name
45 |
46 |
--------------------------------------------------------------------------------
/app/business-api/payment/src/main/resources/payments.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.3
2 | info:
3 | title: Payment API
4 | version: 1.0.0
5 | paths:
6 | /payments:
7 | post:
8 | operationId: submitPayment
9 | summary: Submit a payment request
10 | requestBody:
11 | required: true
12 | content:
13 | application/json:
14 | schema:
15 | $ref: '#/components/schemas/Payment'
16 | responses:
17 | '201':
18 | description: Payment request submitted successfully
19 | '400':
20 | description: Invalid request body
21 | '500':
22 | description: Internal server error
23 | components:
24 | schemas:
25 | Payment:
26 | type: object
27 | properties:
28 | description:
29 | type: string
30 | description: Description of the payment
31 | recipientName:
32 | type: string
33 | description: Name of the recipient
34 | recipientId:
35 | type: string
36 | description: ID of the recipient
37 | recipientBankCode:
38 | type: string
39 | description: Bank code of the recipient
40 | accountId:
41 | type: string
42 | description: ID of the account
43 | paymentMethodId:
44 | type: string
45 | description: ID of the payment method
46 | amount:
47 | type: string
48 | description: Amount of the payment
49 | timestamp:
50 | type: string
51 | description: Timestamp of the payment
52 | required:
53 | - description
54 | - recipientName
55 | - recipientBankCode
56 | - accountId
57 | - paymentMethodId
58 | - amount
59 | - timestamp
--------------------------------------------------------------------------------
/infra/shared/ai/cognitiveservices.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Azure Cognitive Services instance.'
2 | param name string
3 | param location string = resourceGroup().location
4 | param tags object = {}
5 | @description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.')
6 | param customSubDomainName string = name
7 | param disableLocalAuth bool = true
8 | param deployments array = []
9 | param kind string = 'OpenAI'
10 |
11 | @allowed([ 'Enabled', 'Disabled' ])
12 | param publicNetworkAccess string = 'Enabled'
13 | param sku object = {
14 | name: 'S0'
15 | }
16 |
17 | param allowedIpRules array = []
18 | param networkAcls object = empty(allowedIpRules) ? {
19 | defaultAction: 'Allow'
20 | } : {
21 | ipRules: allowedIpRules
22 | defaultAction: 'Deny'
23 | }
24 |
25 | resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = {
26 | name: name
27 | location: location
28 | tags: tags
29 | kind: kind
30 | properties: {
31 | customSubDomainName: customSubDomainName
32 | publicNetworkAccess: publicNetworkAccess
33 | networkAcls: networkAcls
34 | disableLocalAuth: disableLocalAuth
35 | }
36 | sku: sku
37 | }
38 |
39 | @batchSize(1)
40 | resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: {
41 | parent: account
42 | name: deployment.name
43 | properties: {
44 | model: deployment.model
45 | raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null
46 | }
47 | sku: contains(deployment, 'sku') ? deployment.sku : {
48 | name: 'Standard'
49 | capacity: 20
50 | }
51 | }]
52 |
53 | output endpoint string = account.properties.endpoint
54 | output endpoints object = account.properties.endpoints
55 | output id string = account.id
56 | output name string = account.name
57 |
58 |
--------------------------------------------------------------------------------
/app/frontend/src/api/models.ts:
--------------------------------------------------------------------------------
1 | export const enum Approaches {
2 | JAVA_OPENAI_SDK = "jos",
3 | JAVA_SEMANTIC_KERNEL = "jsk",
4 | JAVA_SEMANTIC_KERNEL_PLANNER = "jskp"
5 | }
6 |
7 | export const enum RetrievalMode {
8 | Hybrid = "hybrid",
9 | Vectors = "vectors",
10 | Text = "text"
11 | }
12 |
13 | export const enum SKMode {
14 | Chains = "chains",
15 | Planner = "planner"
16 | }
17 |
18 | export type ChatAppRequestOverrides = {
19 | retrieval_mode?: RetrievalMode;
20 | semantic_ranker?: boolean;
21 | semantic_captions?: boolean;
22 | exclude_category?: string;
23 | top?: number;
24 | temperature?: number;
25 | prompt_template?: string;
26 | prompt_template_prefix?: string;
27 | prompt_template_suffix?: string;
28 | suggest_followup_questions?: boolean;
29 | use_oid_security_filter?: boolean;
30 | use_groups_security_filter?: boolean;
31 | semantic_kernel_mode?: SKMode;
32 | };
33 |
34 | export type ResponseMessage = {
35 | content: string;
36 | role: string;
37 | attachments?: string[];
38 | };
39 |
40 | export type ResponseContext = {
41 | thoughts: string | null;
42 | data_points: string[];
43 | };
44 |
45 | export type ResponseChoice = {
46 | index: number;
47 | message: ResponseMessage;
48 | context: ResponseContext;
49 | session_state: any;
50 | };
51 |
52 | export type ChatAppResponseOrError = {
53 | choices?: ResponseChoice[];
54 | error?: string;
55 | };
56 |
57 | export type ChatAppResponse = {
58 | choices: ResponseChoice[];
59 | };
60 |
61 | export type ChatAppRequestContext = {
62 | overrides?: ChatAppRequestOverrides;
63 | };
64 |
65 | export type ChatAppRequest = {
66 | messages: ResponseMessage[];
67 | approach: Approaches;
68 | context?: ChatAppRequestContext;
69 | stream?: boolean;
70 | session_state: any;
71 | };
72 |
--------------------------------------------------------------------------------
/.github/workflows/aca-deploy.yaml:
--------------------------------------------------------------------------------
1 | name: Reusable Azure Container Apps deploy
2 | on:
3 | workflow_call:
4 | inputs:
5 | env-name:
6 | required: true
7 | type: string
8 | image-name:
9 | required: true
10 | type: string
11 | container-app-name:
12 | required: true
13 | type: string
14 | container-app-env-name:
15 | required: true
16 | type: string
17 |
18 | # Set up permissions for deploying with secretless Azure federated credentials
19 | # https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Clinux#set-up-azure-login-with-openid-connect-authentication
20 | permissions:
21 | id-token: write
22 | contents: read
23 |
24 | jobs:
25 | deploy:
26 | runs-on: ubuntu-latest
27 | environment: ${{inputs.env-name}}
28 | steps:
29 | - name: Log in to Azure with service principal
30 | uses: azure/login@v2
31 | if: ${{ vars.AZURE_CLIENT_ID == '' }}
32 | with:
33 | creds: ${{ secrets.AZURE_CREDENTIALS }}
34 | - name: Log in with Azure (Federated Credentials)
35 | if: ${{ vars.AZURE_CLIENT_ID != '' }}
36 | uses: azure/login@v2
37 | with:
38 | client-id: ${{ vars.AZURE_CLIENT_ID }}
39 | tenant-id: ${{ vars.AZURE_TENANT_ID }}
40 | subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
41 | - name: Build and deploy Container App
42 | uses: azure/container-apps-deploy-action@v1
43 | with:
44 | acrName: ${{vars.ACR_NAME}}
45 | containerAppName: ${{inputs.container-app-name}}
46 | containerAppEnvironment: ${{inputs.container-app-env-name}}
47 | resourceGroup: ${{vars.RESOURCE_GROUP}}
48 | imageToDeploy: ${{vars.ACR_NAME}}.azurecr.io/${{inputs.image-name}}:${{github.sha}}
--------------------------------------------------------------------------------
/app/business-api/account/src/main/java/com/microsoft/openai/samples/assistant/business/service/UserService.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.service;
2 |
3 | import com.microsoft.openai.samples.assistant.business.models.Account;
4 | import org.springframework.stereotype.Service;
5 |
6 | import java.util.*;
7 |
8 |
9 | @Service
10 | public class UserService {
11 |
12 |
13 | private Map accounts = new HashMap<>();
14 |
15 | public UserService() {
16 | accounts.put(
17 | "alice.user@contoso.com",
18 | new Account(
19 | "1000",
20 | "alice.user@contoso.com",
21 | "Alice User",
22 | "USD",
23 | "2022-01-01",
24 | "5000",
25 | null
26 | )
27 | );
28 | accounts.put(
29 | "bob.user@contoso.com",
30 | new Account(
31 | "1010",
32 | "bob.user@contoso.com",
33 | "Bob User",
34 | "EUR",
35 | "2022-01-01",
36 | "10000",
37 | null
38 | )
39 | );
40 | accounts.put(
41 | "charlie.user@contoso.com",
42 | new Account(
43 | "1020",
44 | "charlie.user@contoso.com",
45 | "Charlie User",
46 | "EUR",
47 | "2022-01-01",
48 | "3000",
49 | null
50 | )
51 | );
52 |
53 | }
54 | public List getAccountsByUserName(String userName) {
55 | return Arrays.asList(accounts.get(userName));
56 | }
57 |
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/resources/payments.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.3
2 | info:
3 | title: Payment API
4 | version: 1.0.0
5 | paths:
6 | /payments:
7 | post:
8 | operationId: submitPayment
9 | summary: Submit a payment request
10 | description: Submit a payment request
11 | requestBody:
12 | required: true
13 | description: Payment to submit
14 | content:
15 | application/json:
16 | schema:
17 | $ref: '#/components/schemas/Payment'
18 | responses:
19 | '200':
20 | description: Payment request submitted successfully
21 | '400':
22 | description: Invalid request body
23 | '500':
24 | description: Internal server error
25 | components:
26 | schemas:
27 | Payment:
28 | type: object
29 | properties:
30 | description:
31 | type: string
32 | description: Description of the payment
33 | recipientName:
34 | type: string
35 | description: Name of the recipient
36 | recipientBankCode:
37 | type: string
38 | description: Bank code of the recipient
39 | accountId:
40 | type: string
41 | description: ID of the account
42 | paymentMethodId:
43 | type: string
44 | description: ID of the payment method
45 | paymentType:
46 | type: string
47 | description: 'The type of payment: creditcard, banktransfer, directdebit, visa, mastercard, paypal, etc.'
48 | amount:
49 | type: string
50 | description: Amount of the payment
51 | timestamp:
52 | type: string
53 | description: Timestamp of the payment
54 | requestBodies:
55 | Payment:
56 | content:
57 | application/json:
58 | schema:
59 | $ref: '#/components/schemas/Payment'
60 | description: Payment object to submit
--------------------------------------------------------------------------------
/app/copilot/langchain4j-agents/src/test/resources/payments.yaml:
--------------------------------------------------------------------------------
1 | openapi: 3.0.3
2 | info:
3 | title: Payment API
4 | version: 1.0.0
5 | paths:
6 | /payments:
7 | post:
8 | operationId: submitPayment
9 | summary: Submit a payment request
10 | description: Submit a payment request
11 | requestBody:
12 | required: true
13 | description: Payment to submit
14 | content:
15 | application/json:
16 | schema:
17 | $ref: '#/components/schemas/Payment'
18 | responses:
19 | '200':
20 | description: Payment request submitted successfully
21 | '400':
22 | description: Invalid request body
23 | '500':
24 | description: Internal server error
25 | components:
26 | schemas:
27 | Payment:
28 | type: object
29 | properties:
30 | description:
31 | type: string
32 | description: Description of the payment
33 | recipientName:
34 | type: string
35 | description: Name of the recipient
36 | recipientBankCode:
37 | type: string
38 | description: Bank code of the recipient
39 | accountId:
40 | type: string
41 | description: ID of the account
42 | paymentMethodId:
43 | type: string
44 | description: ID of the payment method
45 | paymentType:
46 | type: string
47 | description: 'The type of payment: creditcard, banktransfer, directdebit, visa, mastercard, paypal, etc.'
48 | amount:
49 | type: string
50 | description: Amount of the payment
51 | timestamp:
52 | type: string
53 | description: Timestamp of the payment
54 | requestBodies:
55 | Payment:
56 | content:
57 | application/json:
58 | schema:
59 | $ref: '#/components/schemas/Payment'
60 | description: Payment object to submit
--------------------------------------------------------------------------------
/infra/app/account.bicep:
--------------------------------------------------------------------------------
1 | param name string
2 | param location string = resourceGroup().location
3 | param tags object = {}
4 |
5 | param identityName string
6 | param applicationInsightsName string
7 | param containerAppsEnvironmentName string
8 | param containerRegistryName string
9 | param serviceName string = 'account'
10 | param corsAcaUrl string
11 | param exists bool
12 |
13 | @description('The environment variables for the container')
14 | param env array = []
15 |
16 | resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
17 | name: identityName
18 | location: location
19 | }
20 |
21 |
22 | module app '../shared/host/container-app-upsert.bicep' = {
23 | name: '${serviceName}-container-app'
24 | params: {
25 | name: name
26 | location: location
27 | tags: union(tags, { 'azd-service-name': serviceName })
28 | identityType: 'UserAssigned'
29 | identityName: apiIdentity.name
30 | exists: exists
31 | containerAppsEnvironmentName: containerAppsEnvironmentName
32 | containerRegistryName: containerRegistryName
33 | containerCpuCoreCount: '1.0'
34 | containerMemory: '2.0Gi'
35 | targetPort: 8080
36 | external:false
37 | env: union(env, [
38 | {
39 | name: 'AZURE_CLIENT_ID'
40 | value: apiIdentity.properties.clientId
41 | }
42 |
43 | {
44 | name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
45 | value: applicationInsights.properties.ConnectionString
46 | }
47 | {
48 | name: 'API_ALLOW_ORIGINS'
49 | value: corsAcaUrl
50 | }
51 | ])
52 |
53 | }
54 | }
55 |
56 | resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
57 | name: applicationInsightsName
58 | }
59 |
60 |
61 | output SERVICE_API_IDENTITY_PRINCIPAL_ID string = apiIdentity.properties.principalId
62 | output SERVICE_API_NAME string = app.outputs.name
63 | output SERVICE_API_URI string = app.outputs.uri
64 | output SERVICE_API_IMAGE_NAME string = app.outputs.imageName
65 |
--------------------------------------------------------------------------------
/infra/app/copilot.bicep:
--------------------------------------------------------------------------------
1 | param name string
2 | param location string = resourceGroup().location
3 | param tags object = {}
4 |
5 | param identityName string
6 | param applicationInsightsName string
7 | param containerAppsEnvironmentName string
8 | param containerRegistryName string
9 | param serviceName string = 'copilot'
10 | param corsAcaUrl string
11 | param exists bool
12 |
13 | @description('The environment variables for the container')
14 | param env array = []
15 |
16 | resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
17 | name: identityName
18 | location: location
19 | }
20 |
21 |
22 | module app '../shared/host/container-app-upsert.bicep' = {
23 | name: '${serviceName}-container-app'
24 | params: {
25 | name: name
26 | location: location
27 | tags: union(tags, { 'azd-service-name': serviceName })
28 | identityType: 'UserAssigned'
29 | identityName: apiIdentity.name
30 | exists: exists
31 | containerAppsEnvironmentName: containerAppsEnvironmentName
32 | containerRegistryName: containerRegistryName
33 | containerCpuCoreCount: '1.0'
34 | containerMemory: '2.0Gi'
35 | targetPort: 8080
36 | external:false
37 | env: union(env, [
38 | {
39 | name: 'AZURE_CLIENT_ID'
40 | value: apiIdentity.properties.clientId
41 | }
42 |
43 | {
44 | name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
45 | value: applicationInsights.properties.ConnectionString
46 | }
47 | {
48 | name: 'API_ALLOW_ORIGINS'
49 | value: corsAcaUrl
50 | }
51 | ])
52 |
53 | }
54 | }
55 |
56 | resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
57 | name: applicationInsightsName
58 | }
59 |
60 |
61 | output SERVICE_API_IDENTITY_PRINCIPAL_ID string = apiIdentity.properties.principalId
62 | output SERVICE_API_NAME string = app.outputs.name
63 | output SERVICE_API_URI string = app.outputs.uri
64 | output SERVICE_API_IMAGE_NAME string = app.outputs.imageName
65 |
--------------------------------------------------------------------------------
/infra/app/payment.bicep:
--------------------------------------------------------------------------------
1 | param name string
2 | param location string = resourceGroup().location
3 | param tags object = {}
4 |
5 | param identityName string
6 | param applicationInsightsName string
7 | param containerAppsEnvironmentName string
8 | param containerRegistryName string
9 | param serviceName string = 'payment'
10 | param corsAcaUrl string
11 | param exists bool
12 |
13 | @description('The environment variables for the container')
14 | param env array = []
15 |
16 | resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
17 | name: identityName
18 | location: location
19 | }
20 |
21 |
22 | module app '../shared/host/container-app-upsert.bicep' = {
23 | name: '${serviceName}-container-app'
24 | params: {
25 | name: name
26 | location: location
27 | tags: union(tags, { 'azd-service-name': serviceName })
28 | identityType: 'UserAssigned'
29 | identityName: apiIdentity.name
30 | exists: exists
31 | containerAppsEnvironmentName: containerAppsEnvironmentName
32 | containerRegistryName: containerRegistryName
33 | containerCpuCoreCount: '1.0'
34 | containerMemory: '2.0Gi'
35 | targetPort: 8080
36 | external:false
37 | env: union(env, [
38 | {
39 | name: 'AZURE_CLIENT_ID'
40 | value: apiIdentity.properties.clientId
41 | }
42 |
43 | {
44 | name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
45 | value: applicationInsights.properties.ConnectionString
46 | }
47 | {
48 | name: 'API_ALLOW_ORIGINS'
49 | value: corsAcaUrl
50 | }
51 | ])
52 |
53 | }
54 | }
55 |
56 | resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
57 | name: applicationInsightsName
58 | }
59 |
60 |
61 | output SERVICE_API_IDENTITY_PRINCIPAL_ID string = apiIdentity.properties.principalId
62 | output SERVICE_API_NAME string = app.outputs.name
63 | output SERVICE_API_URI string = app.outputs.uri
64 | output SERVICE_API_IMAGE_NAME string = app.outputs.imageName
65 |
--------------------------------------------------------------------------------
/infra/app/transaction.bicep:
--------------------------------------------------------------------------------
1 | param name string
2 | param location string = resourceGroup().location
3 | param tags object = {}
4 |
5 | param identityName string
6 | param applicationInsightsName string
7 | param containerAppsEnvironmentName string
8 | param containerRegistryName string
9 | param serviceName string = 'transaction'
10 | param corsAcaUrl string
11 | param exists bool
12 |
13 | @description('The environment variables for the container')
14 | param env array = []
15 |
16 | resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
17 | name: identityName
18 | location: location
19 | }
20 |
21 |
22 | module app '../shared/host/container-app-upsert.bicep' = {
23 | name: '${serviceName}-container-app'
24 | params: {
25 | name: name
26 | location: location
27 | tags: union(tags, { 'azd-service-name': serviceName })
28 | identityType: 'UserAssigned'
29 | identityName: apiIdentity.name
30 | exists: exists
31 | containerAppsEnvironmentName: containerAppsEnvironmentName
32 | containerRegistryName: containerRegistryName
33 | containerCpuCoreCount: '1.0'
34 | containerMemory: '2.0Gi'
35 | targetPort: 8080
36 | external:false
37 | env: union(env, [
38 | {
39 | name: 'AZURE_CLIENT_ID'
40 | value: apiIdentity.properties.clientId
41 | }
42 |
43 | {
44 | name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
45 | value: applicationInsights.properties.ConnectionString
46 | }
47 | {
48 | name: 'API_ALLOW_ORIGINS'
49 | value: corsAcaUrl
50 | }
51 | ])
52 |
53 | }
54 | }
55 |
56 | resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
57 | name: applicationInsightsName
58 | }
59 |
60 |
61 | output SERVICE_API_IDENTITY_PRINCIPAL_ID string = apiIdentity.properties.principalId
62 | output SERVICE_API_NAME string = app.outputs.name
63 | output SERVICE_API_URI string = app.outputs.uri
64 | output SERVICE_API_IMAGE_NAME string = app.outputs.imageName
65 |
--------------------------------------------------------------------------------
/app/copilot/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.microsoft.openai.samples.assistant
8 | copilot-parent
9 | 1.0.0-SNAPSHOT
10 | pom
11 |
12 |
13 | 17
14 | 17
15 | UTF-8
16 | 1.0.0-beta2
17 | 1.2.33
18 |
19 |
20 |
21 |
22 |
23 | com.azure
24 | azure-sdk-bom
25 | ${azure.sdk.version}
26 | pom
27 | import
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | org.apache.maven.plugins
36 | maven-compiler-plugin
37 |
38 | ${maven.compiler.source}
39 | ${maven.compiler.target}
40 |
41 |
42 |
43 |
44 |
45 |
46 | copilot-backend
47 | langchain4j-agents
48 | copilot-common
49 |
50 |
51 |
--------------------------------------------------------------------------------
/app/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { createHashRouter, RouterProvider } from "react-router-dom";
4 | import { initializeIcons } from "@fluentui/react";
5 | import { MsalProvider } from "@azure/msal-react";
6 | import { PublicClientApplication, EventType, AccountInfo } from "@azure/msal-browser";
7 | import { msalConfig, useLogin } from "./authConfig";
8 |
9 | import "./index.css";
10 |
11 | import Layout from "./pages/layout/Layout";
12 | import Chat from "./pages/chat/Chat";
13 |
14 | var layout;
15 | if (useLogin) {
16 | var msalInstance = new PublicClientApplication(msalConfig);
17 |
18 | // Default to using the first account if no account is active on page load
19 | if (!msalInstance.getActiveAccount() && msalInstance.getAllAccounts().length > 0) {
20 | // Account selection logic is app dependent. Adjust as needed for different use cases.
21 | msalInstance.setActiveAccount(msalInstance.getActiveAccount());
22 | }
23 |
24 | // Listen for sign-in event and set active account
25 | msalInstance.addEventCallback(event => {
26 | if (event.eventType === EventType.LOGIN_SUCCESS && event.payload) {
27 | const account = event.payload as AccountInfo;
28 | msalInstance.setActiveAccount(account);
29 | }
30 | });
31 |
32 | layout = (
33 |
34 |
35 |
36 | );
37 | } else {
38 | layout = ;
39 | }
40 |
41 | initializeIcons();
42 |
43 | const router = createHashRouter([
44 | {
45 | path: "/",
46 | element: layout,
47 | children: [
48 | {
49 | index: true,
50 | element:
51 | },
52 | {
53 | path: "*",
54 | lazy: () => import("./pages/NoPage")
55 | }
56 | ]
57 | }
58 | ]);
59 |
60 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
61 |
62 |
63 |
64 | );
65 |
--------------------------------------------------------------------------------
/docs/kusto-queries.md:
--------------------------------------------------------------------------------
1 | # Useful Kusto queries to inspect tools calls
2 | By default all Azure Open AI requests and response are logged in the java application logs.
3 | The following Kusto queries can be used to inspect the tools calls requests and responses.
4 |
5 |
6 | ### Inspect all Azure Open AI responses
7 | ```kusto
8 | traces
9 | | where cloud_RoleName == "copilot-api"
10 | | where message contains "openai.azure.com/openai/deployments"
11 | | extend chatmessage=parse_json(message)
12 | | where chatmessage["az.sdk.message"] == "HTTP response"
13 | | extend chatbody=parse_json(tostring(chatmessage.body)).choices[0]
14 | ```
15 | ### Inspect all Azure Open AI requests
16 | ```kusto
17 | traces
18 | | where cloud_RoleName == "copilot-api"
19 | | where message contains "openai.azure.com/openai/deployments"
20 | | extend chatrequest=parse_json(message)
21 | | where chatrequest.method == "POST"
22 | | extend chatbody=parse_json(tostring(parse_json(message).body))
23 | ```
24 |
25 | ### Inspect all tools calls requests
26 | ```kusto
27 | traces
28 | | where cloud_RoleName == "copilot-api"
29 | | where message contains "openai.azure.com/openai/deployments"
30 | | extend chatrequest=parse_json(message)
31 | | where chatrequest["az.sdk.message"] == "HTTP response"
32 | | extend response=parse_json(tostring(parse_json(tostring(chatrequest.body)))).choices[0]
33 | | where response.finish_reason=="tool_calls"
34 | | extend tool_calls=parse_json(tostring(parse_json(tostring(response.message)).tool_calls))
35 | ```
36 |
37 | ### Inspect all tools calls requests for TransactionHistoryPlugin function
38 | ```kusto
39 | traces
40 | | where cloud_RoleName == "copilot-api"
41 | | where message contains "openai.azure.com/openai/deployments"
42 | | extend chatrequest=parse_json(message)
43 | | where chatrequest["az.sdk.message"] == "HTTP response"
44 | | extend response=parse_json(tostring(parse_json(tostring(chatrequest.body)))).choices[0]
45 | | where response.finish_reason=="tool_calls" //and response.message contains "TransactionHistoryPlugin"
46 | | extend tool_calls=parse_json(tostring(parse_json(tostring(response.message)).tool_calls))
47 | ```
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/plugin/TransactionHistoryMockPlugin.java.sample:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.plugin;
2 |
3 | import com.microsoft.openai.samples.assistant.plugin.mock.TransactionServiceMock;
4 | import com.microsoft.semantickernel.semanticfunctions.annotations.DefineKernelFunction;
5 | import com.microsoft.semantickernel.semanticfunctions.annotations.KernelFunctionParameter;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | public class TransactionHistoryMockPlugin {
10 | private static final Logger LOGGER = LoggerFactory.getLogger(TransactionHistoryMockPlugin.class);
11 | private final TransactionServiceMock transactionService;
12 | public TransactionHistoryMockPlugin(){
13 | this.transactionService = new TransactionServiceMock();
14 | }
15 |
16 | @DefineKernelFunction(name = "getTransactionsByRecepient", description = "Gets the last payment transactions based on the payee, recipient name")
17 | public String getTransactionsByRecepient(
18 | @KernelFunctionParameter(name = "accountId", description = "The banking account id of the user") String accountId,
19 | @KernelFunctionParameter(name = "recipientName", description = "Name of the payee, recipient") String recipientName) {
20 | String transactionsByRecipient = transactionService.getTransactionsByRecipientName(accountId,recipientName).toString();
21 | LOGGER.info("Transactions for [{}]:{} ",recipientName,transactionsByRecipient);
22 | return transactionsByRecipient;
23 |
24 |
25 | }
26 |
27 |
28 | @DefineKernelFunction(name = "getTransactions", description = "Gets the last payment transactions")
29 | public String getTransactions(
30 | @KernelFunctionParameter(name = "accountId", description = "The banking account id of the user") String accountId
31 | ) {
32 | String lastTransactions = transactionService.getlastTransactions(accountId).toString();
33 | LOGGER.info("Last transactions:{} ",lastTransactions);
34 | return lastTransactions;
35 |
36 |
37 | }
38 |
39 |
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/app/business-api/account/src/main/java/com/microsoft/openai/samples/assistant/business/controller/AccountController.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.business.controller;
2 |
3 | import com.microsoft.openai.samples.assistant.business.models.Account;
4 | import com.microsoft.openai.samples.assistant.business.models.PaymentMethod;
5 | import com.microsoft.openai.samples.assistant.business.models.Beneficiary;
6 | import com.microsoft.openai.samples.assistant.business.service.AccountService;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.web.bind.annotation.GetMapping;
10 | import org.springframework.web.bind.annotation.PathVariable;
11 | import org.springframework.web.bind.annotation.RequestMapping;
12 | import org.springframework.web.bind.annotation.RestController;
13 |
14 | import java.util.List;
15 |
16 | @RestController
17 | @RequestMapping("/accounts")
18 | public class AccountController {
19 |
20 | private final AccountService accountService;
21 | private static final Logger logger = LoggerFactory.getLogger(AccountController.class);
22 |
23 | public AccountController(AccountService accountService) {
24 | this.accountService = accountService;
25 | }
26 |
27 | @GetMapping("/{accountId}")
28 | public Account getAccountDetails(@PathVariable String accountId) {
29 | logger.info("Received request to get account details for account id: {}", accountId);
30 | return accountService.getAccountDetails(accountId);
31 | }
32 |
33 | @GetMapping("/{accountId}/paymentmethods/{methodId}")
34 | public PaymentMethod getPaymentMethodDetails(@PathVariable String accountId, @PathVariable String methodId) {
35 | logger.info("Received request to get payment method details for account id: {} and method id: {}", accountId, methodId);
36 | return accountService.getPaymentMethodDetails(methodId);
37 | }
38 |
39 | @GetMapping("/{accountId}/registeredBeneficiaries")
40 | public List getBeneficiaryDetails(@PathVariable String accountId) {
41 | logger.info("Received request to get beneficiary details for account id: {}", accountId);
42 | return accountService.getRegisteredBeneficiary(accountId);
43 | }
44 | }
--------------------------------------------------------------------------------
/app/frontend/src/pages/layout/Layout.tsx:
--------------------------------------------------------------------------------
1 | import { Outlet, NavLink, Link } from "react-router-dom";
2 |
3 | import github from "../../assets/github.svg";
4 |
5 | import styles from "./Layout.module.css";
6 |
7 | import { useLogin } from "../../authConfig";
8 |
9 | import { LoginButton } from "../../components/LoginButton";
10 |
11 | const Layout = () => {
12 | return (
13 |
14 |
15 |
16 |
17 |
Agents Java Sample
18 |
19 |
41 |
Banking Assistance Copilot
42 | {useLogin &&
}
43 |
44 |
45 |
46 |
47 |
48 | );
49 | };
50 |
51 | export default Layout;
52 |
--------------------------------------------------------------------------------
/app/copilot/copilot-common/src/main/java/com/microsoft/openai/samples/assistant/proxy/BlobStorageProxy.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.proxy;
3 |
4 | import com.azure.core.credential.TokenCredential;
5 | import com.azure.storage.blob.BlobClient;
6 | import com.azure.storage.blob.BlobContainerClient;
7 | import com.azure.storage.blob.BlobContainerClientBuilder;
8 |
9 |
10 | import java.io.ByteArrayInputStream;
11 | import java.io.ByteArrayOutputStream;
12 | import java.io.IOException;
13 |
14 | /**
15 | * This class is a proxy to the Blob storage API. It is responsible for: - calling the API -
16 | * handling errors and retry strategy - add monitoring points - add circuit breaker with exponential
17 | * backoff
18 | */
19 |
20 | public class BlobStorageProxy {
21 |
22 | private final BlobContainerClient client;
23 |
24 | public BlobStorageProxy(
25 | String storageAccountServiceName,
26 | String containerName,
27 | TokenCredential tokenCredential) {
28 |
29 | String endpoint = "https://%s.blob.core.windows.net".formatted(storageAccountServiceName);
30 | this.client =
31 | new BlobContainerClientBuilder()
32 | .endpoint(endpoint)
33 | .credential(tokenCredential)
34 | .containerName(containerName)
35 | .buildClient();
36 | }
37 |
38 | public byte[] getFileAsBytes(String fileName) throws IOException {
39 | var blobClient = client.getBlobClient(fileName);
40 | int dataSize = (int) blobClient.getProperties().getBlobSize();
41 |
42 | // There is no need to close ByteArrayOutputStream.
43 | // https://docs.oracle.com/javase/8/docs/api/java/io/ByteArrayOutputStream.html
44 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(dataSize);
45 | blobClient.downloadStream(outputStream);
46 |
47 | return outputStream.toByteArray();
48 | }
49 |
50 | public void storeFile(byte[] bytes, String originalFilename) {
51 | BlobClient blobClient = client.getBlobClient(originalFilename);
52 | blobClient.upload(new ByteArrayInputStream(bytes), bytes.length, true);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/mcp/AccountMCPAgent.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.langchain4j.agent.mcp;
2 |
3 | import com.microsoft.langchain4j.agent.AgentMetadata;
4 | import com.microsoft.langchain4j.agent.mcp.MCPProtocolType;
5 | import com.microsoft.langchain4j.agent.mcp.MCPServerMetadata;
6 | import com.microsoft.langchain4j.agent.mcp.MCPToolAgent;
7 | import dev.langchain4j.model.chat.ChatLanguageModel;
8 | import dev.langchain4j.model.input.Prompt;
9 | import dev.langchain4j.model.input.PromptTemplate;
10 |
11 | import java.util.List;
12 | import java.util.Map;
13 |
14 | public class AccountMCPAgent extends MCPToolAgent {
15 |
16 | private final Prompt agentPrompt;
17 |
18 | private static final String ACCOUNT_AGENT_SYSTEM_MESSAGE = """
19 | you are a personal financial advisor who help the user to retrieve information about their bank accounts.
20 | Use html list or table to display the account information.
21 | Always use the below logged user details to retrieve account info:
22 | '{{loggedUserName}}'
23 | """;
24 |
25 | public AccountMCPAgent(ChatLanguageModel chatModel, String loggedUserName, String accountMCPServerUrl) {
26 | super(chatModel, List.of(new MCPServerMetadata("account", accountMCPServerUrl, MCPProtocolType.SSE)));
27 |
28 | if (loggedUserName == null || loggedUserName.isEmpty()) {
29 | throw new IllegalArgumentException("loggedUserName cannot be null or empty");
30 | }
31 |
32 | PromptTemplate promptTemplate = PromptTemplate.from(ACCOUNT_AGENT_SYSTEM_MESSAGE);
33 | this.agentPrompt = promptTemplate.apply(Map.of("loggedUserName", loggedUserName));
34 | }
35 |
36 | @Override
37 | public String getName() {
38 | return "AccountAgent";
39 | }
40 |
41 | @Override
42 | public AgentMetadata getMetadata() {
43 | return new AgentMetadata(
44 | "Personal financial advisor for retrieving bank account information.",
45 | List.of("RetrieveAccountInfo", "DisplayAccountDetails")
46 | );
47 | }
48 |
49 | @Override
50 | protected String getSystemMessage() {
51 | return agentPrompt.text();
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/app/business-api/account/src/test/java/org/springframework/ai/mcp/sample/client/AccountMCPClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 - 2024 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.springframework.ai.mcp.sample.client;
17 |
18 | import java.util.Map;
19 |
20 | import io.modelcontextprotocol.client.McpClient;
21 | import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
22 | import io.modelcontextprotocol.spec.McpClientTransport;
23 | import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
24 | import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
25 | import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;
26 |
27 | /**
28 | * @author Christian Tzolov
29 | */
30 |
31 | public class AccountMCPClient {
32 |
33 |
34 |
35 | public static void main(String[] args) {
36 | var transport = new HttpClientSseClientTransport("http://localhost:8070");
37 |
38 | var client = McpClient.sync(transport).build();
39 |
40 | client.initialize();
41 |
42 | client.ping();
43 |
44 | // List and demonstrate tools
45 | ListToolsResult toolsList = client.listTools();
46 | System.out.println("Available Tools = " + toolsList);
47 | toolsList.tools().stream().forEach(tool -> {
48 | System.out.println("Tool: " + tool.name() + ", description: " + tool.description() + ", schema: " + tool.inputSchema());
49 | });
50 |
51 | CallToolResult accountResult = client.callTool(new CallToolRequest("getAccountDetails",
52 | Map.of("accountId", "1010")));
53 | System.out.println("Account : " + accountResult);
54 |
55 | CallToolResult userResult = client.callTool(new CallToolRequest("getAccountsByUserName",
56 | Map.of("userName", "bob.user@contoso.com")));
57 | System.out.println("Account for Bob : " + userResult);
58 |
59 | client.closeGracefully();
60 |
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/app/frontend/src/components/Answer/AnswerParser.tsx:
--------------------------------------------------------------------------------
1 | import { renderToStaticMarkup } from "react-dom/server";
2 | import { getCitationFilePath } from "../../api";
3 |
4 | type HtmlParsedAnswer = {
5 | answerHtml: string;
6 | citations: string[];
7 | followupQuestions: string[];
8 | };
9 |
10 | export function parseAnswerToHtml(answer: string, isStreaming: boolean, onCitationClicked: (citationFilePath: string) => void): HtmlParsedAnswer {
11 | const citations: string[] = [];
12 | const followupQuestions: string[] = [];
13 |
14 | // Extract any follow-up questions that might be in the answer
15 | let parsedAnswer = answer.replace(/<<([^>>]+)>>/g, (match, content) => {
16 | followupQuestions.push(content);
17 | return "";
18 | });
19 |
20 | // trim any whitespace from the end of the answer after removing follow-up questions
21 | parsedAnswer = parsedAnswer.trim();
22 |
23 | // Omit a citation that is still being typed during streaming
24 | if (isStreaming) {
25 | let lastIndex = parsedAnswer.length;
26 | for (let i = parsedAnswer.length - 1; i >= 0; i--) {
27 | if (parsedAnswer[i] === "]") {
28 | break;
29 | } else if (parsedAnswer[i] === "[") {
30 | lastIndex = i;
31 | break;
32 | }
33 | }
34 | const truncatedAnswer = parsedAnswer.substring(0, lastIndex);
35 | parsedAnswer = truncatedAnswer;
36 | }
37 |
38 | const parts = parsedAnswer.split(/\[([^\]]+)\]/g);
39 |
40 | const fragments: string[] = parts.map((part, index) => {
41 | if (index % 2 === 0) {
42 | return part;
43 | } else {
44 | let citationIndex: number;
45 | if (citations.indexOf(part) !== -1) {
46 | citationIndex = citations.indexOf(part) + 1;
47 | } else {
48 | citations.push(part);
49 | citationIndex = citations.length;
50 | }
51 |
52 | const path = getCitationFilePath(part);
53 |
54 | return renderToStaticMarkup(
55 | onCitationClicked(path)}>
56 | {citationIndex}
57 |
58 | );
59 | }
60 | });
61 |
62 | return {
63 | answerHtml: fragments.join(""),
64 | citations,
65 | followupQuestions
66 | };
67 | }
68 |
--------------------------------------------------------------------------------
/app/frontend/src/pages/chat/Chat.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | flex: 1;
3 | display: flex;
4 | flex-direction: column;
5 | margin-top: 20px;
6 | }
7 |
8 | .chatRoot {
9 | flex: 1;
10 | display: flex;
11 | }
12 |
13 | .chatContainer {
14 | flex: 1;
15 | display: flex;
16 | flex-direction: column;
17 | align-items: center;
18 | width: 100%;
19 | }
20 |
21 | .chatEmptyState {
22 | flex-grow: 1;
23 | display: flex;
24 | flex-direction: column;
25 | justify-content: center;
26 | align-items: center;
27 | max-height: 1024px;
28 | padding-top: 60px;
29 | }
30 |
31 | .chatEmptyStateTitle {
32 | font-size: 4rem;
33 | font-weight: 600;
34 | margin-top: 0;
35 | margin-bottom: 30px;
36 | }
37 |
38 | .chatEmptyStateSubtitle {
39 | font-weight: 600;
40 | margin-bottom: 10px;
41 | }
42 |
43 | @media only screen and (max-height: 780px) {
44 | .chatEmptyState {
45 | padding-top: 0;
46 | }
47 |
48 | .chatEmptyStateTitle {
49 | font-size: 3rem;
50 | margin-bottom: 0px;
51 | }
52 | }
53 |
54 | .chatMessageStream {
55 | flex-grow: 1;
56 | max-height: 1024px;
57 | max-width: 1028px;
58 | width: 100%;
59 | overflow-y: auto;
60 | padding-left: 24px;
61 | padding-right: 24px;
62 | display: flex;
63 | flex-direction: column;
64 | }
65 |
66 | .chatMessageGpt {
67 | margin-bottom: 20px;
68 | max-width: 80%;
69 | display: flex;
70 | min-width: 500px;
71 | }
72 |
73 | .chatMessageGptMinWidth {
74 | max-width: 500px;
75 | margin-bottom: 20px;
76 | }
77 |
78 | .chatInput {
79 | position: sticky;
80 | bottom: 0;
81 | flex: 0 0 100px;
82 | padding-top: 12px;
83 | padding-bottom: 24px;
84 | padding-left: 24px;
85 | padding-right: 24px;
86 | width: 100%;
87 | max-width: 1028px;
88 | background: #f2f2f2;
89 | }
90 |
91 | .chatAnalysisPanel {
92 | flex: 1;
93 | overflow-y: auto;
94 | max-height: 89vh;
95 | margin-left: 20px;
96 | margin-right: 20px;
97 | }
98 |
99 | .chatSettingsSeparator {
100 | margin-top: 15px;
101 | }
102 |
103 | .loadingLogo {
104 | font-size: 28px;
105 | }
106 |
107 | .commandsContainer {
108 | display: flex;
109 | align-self: flex-end;
110 | }
111 |
112 | .commandButton {
113 | margin-right: 20px;
114 | margin-bottom: 20px;
115 | }
116 |
--------------------------------------------------------------------------------
/app/start-compose.sh:
--------------------------------------------------------------------------------
1 | echo ""
2 | echo "Loading azd .env file from current environment"
3 | echo ""
4 |
5 | while IFS='=' read -r key value; do
6 | value=$(echo "$value" | sed 's/^"//' | sed 's/"$//')
7 | export "$key=$value"
8 | echo "export $key=$value"
9 | done < {
7 | var headers: Record = {
8 | "Content-Type": "application/json"
9 | };
10 | // If using login, add the id token of the logged in account as the authorization
11 | if (useLogin) {
12 | if (idToken) {
13 | headers["Authorization"] = `Bearer ${idToken}`
14 | }
15 | }
16 |
17 | if (stream) {
18 | headers["Accept"] = "application/x-ndjson";
19 | } else {
20 | headers["Accept"] = "application/json";
21 | }
22 |
23 | return headers;
24 | }
25 |
26 | export async function askApi(request: ChatAppRequest, idToken: string | undefined): Promise {
27 | const response = await fetch(`${BACKEND_URI}/ask`, {
28 | method: "POST",
29 | headers: getHeaders(idToken, request.stream || false),
30 | body: JSON.stringify(request)
31 | });
32 |
33 | const parsedResponse: ChatAppResponseOrError = await response.json();
34 | if (response.status > 299 || !response.ok) {
35 | throw Error(parsedResponse.error || "Unknown error");
36 | }
37 |
38 | return parsedResponse as ChatAppResponse;
39 | }
40 |
41 | export async function chatApi(request: ChatAppRequest, idToken: string | undefined): Promise {
42 | return await fetch(`${BACKEND_URI}/chat`, {
43 | method: "POST",
44 | headers: getHeaders(idToken, request.stream || false),
45 | body: JSON.stringify(request)
46 | });
47 | }
48 |
49 | export function getCitationFilePath(citation: string): string {
50 | return `${BACKEND_URI}/content/${citation}`;
51 | }
52 |
53 | export function uploadAttachment(file: File): Promise {
54 | const formData = new FormData();
55 | formData.append("file", file);
56 |
57 | return fetch(`${BACKEND_URI}/content`, {
58 | method: "POST",
59 | body: formData
60 | }).then(response => {
61 | if (response.status > 299 || !response.ok) {
62 | throw Error("Failed to upload attachment");
63 | }
64 | return response.text();
65 | });
66 | }
67 |
68 | export function getImage(name: string): string {
69 | return `${BACKEND_URI}/content/${name}`;
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/infra/main.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "environmentName": {
6 | "value": "${AZURE_ENV_NAME}"
7 | },
8 | "resourceGroupName": {
9 | "value": "${AZURE_RESOURCE_GROUP}"
10 | },
11 | "location": {
12 | "value": "${AZURE_LOCATION}"
13 | },
14 | "openAiServiceName": {
15 | "value": "${AZURE_OPENAI_SERVICE}"
16 | },
17 | "openAiResourceGroupName": {
18 | "value": "${AZURE_OPENAI_RESOURCE_GROUP}"
19 | },
20 | "openAiResourceGroupLocation": {
21 | "value": "${AZURE_OPENAI_SERVICE_LOCATION=eastus}"
22 | },
23 | "openAiSkuName": {
24 | "value": "S0"
25 | },
26 | "documentIntelligenceServiceName": {
27 | "value": "${AZURE_DOCUMENT_INTELLIGENCE_SERVICE}"
28 | },
29 | "documentIntelligenceResourceGroupName": {
30 | "value": "${AZURE_DOCUMENT_INTELLIGENCE_RESOURCE_GROUP}"
31 | },
32 | "documentIntelligenceResourceGroupLocation": {
33 | "value": "${AZURE_DOCUMENT_INTELLIGENCE_RESOURCE_GROUP_LOCATION=eastus}"
34 | },
35 | "documentIntelligenceSkuName": {
36 | "value": "S0"
37 | },
38 | "storageAccountName": {
39 | "value": "${AZURE_STORAGE_ACCOUNT}"
40 | },
41 | "storageResourceGroupName": {
42 | "value": "${AZURE_STORAGE_RESOURCE_GROUP}"
43 | },
44 | "storageSkuName": {
45 | "value": "${AZURE_STORAGE_SKU=Standard_LRS}"
46 | },
47 | "chatGptModelName": {
48 | "value": "${AZURE_OPENAI_CHATGPT_MODEL=gpt-4o}"
49 | },
50 | "chatGptModelVersion": {
51 | "value": "${AZURE_OPENAI_CHATGPT_VERSION=2024-11-20}"
52 | },
53 | "chatGptDeploymentName": {
54 | "value": "${AZURE_OPENAI_CHATGPT_DEPLOYMENT=gpt-4o}"
55 | },
56 | "chatGptDeploymentCapacity": {
57 | "value": "${AZURE_OPENAI_CHATGPT_DEPLOYMENT_CAPACITY=30}"
58 | },
59 | "chatGptDeploymentSkuName": {
60 | "value": "${AZURE_OPENAI_CHATGPT_DEPLOYMENT_SKU_NAME=GlobalStandard}"
61 | },
62 | "useApplicationInsights": {
63 | "value": "${AZURE_USE_APPLICATION_INSIGHTS=true}"
64 | }
65 | ,
66 | "copilotAppExists": {
67 | "value": false
68 | },
69 | "webAppExists": {
70 | "value": false
71 | },
72 | "accountAppExists": {
73 | "value": false
74 | },
75 | "paymentAppExists": {
76 | "value": false
77 | },
78 | "transactionAppExists": {
79 | "value": false
80 | }
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/app/start-compose.ps1:
--------------------------------------------------------------------------------
1 | $output = azd -C ..\ env get-values
2 |
3 | foreach ($line in $output) {
4 | $name, $value = $line.Split("=")
5 | $value = $value -replace '^\"|\"$'
6 | [Environment]::SetEnvironmentVariable($name, $value)
7 | }
8 |
9 | Write-Host "Environment variables set."
10 | $roles = @(
11 | "a97b65f3-24c7-4388-baec-2e87135dc908",
12 | "5e0bd9bd-7b93-4f28-af87-19fc36ad61bd",
13 | "ba92f5b4-2d11-453d-a403-e96b0029c9fe"
14 | )
15 |
16 | # Check if service principal exists
17 | $servicePrincipal = $(az ad sp list --display-name "agent-java-banking-spi" --query [].appId --output tsv)
18 |
19 | if ([string]::IsNullOrEmpty($servicePrincipal)) {
20 | Write-Host "Service principal not found. Creating service principal.."
21 | $servicePrincipal = $(az ad sp create-for-rbac --name "agent-java-banking-spi" --role reader --scopes "/subscriptions/$($env:AZURE_SUBSCRIPTION_ID)/resourceGroups/$($env:AZURE_RESOURCE_GROUP)" --query appId --output tsv)
22 | if ($LASTEXITCODE -ne 0) {
23 | Write-Host "Failed to create service principal"
24 | exit $LASTEXITCODE
25 | }
26 | $servicePrincipalObjectId = $(az ad sp show --id $servicePrincipal --query id --output tsv)
27 | Write-Host "Assigning Roles to service principal agent-java-banking-spi with principal id: $servicePrincipal and object id[$servicePrincipalObjectId]"
28 | foreach ($role in $roles) {
29 | Write-Host "Assigning Role[$role] to principal id[$servicePrincipal] for resource[/subscriptions/$($env:AZURE_SUBSCRIPTION_ID)/resourceGroups/$($env:AZURE_RESOURCE_GROUP)]"
30 | az role assignment create `
31 | --role $role `
32 | --assignee-object-id $servicePrincipalObjectId `
33 | --scope "/subscriptions/$($env:AZURE_SUBSCRIPTION_ID)/resourceGroups/$($env:AZURE_RESOURCE_GROUP)" `
34 | --assignee-principal-type ServicePrincipal
35 | }
36 | }
37 |
38 | $servicePrincipalPassword = $(az ad sp credential reset --id $servicePrincipal --query password --output tsv)
39 | $servicePrincipalTenant = $(az ad sp show --id $servicePrincipal --query appOwnerOrganizationId --output tsv)
40 |
41 | # Set environment variables
42 | [Environment]::SetEnvironmentVariable("servicePrincipal", $servicePrincipal)
43 | [Environment]::SetEnvironmentVariable("servicePrincipalPassword", $servicePrincipalPassword)
44 | [Environment]::SetEnvironmentVariable("servicePrincipalTenant", $servicePrincipalTenant)
45 |
46 |
47 | Write-Host ""
48 | Write-Host "Starting solution locally using docker compose."
49 | Write-Host ""
50 |
51 | docker compose -f ./compose.yaml up
--------------------------------------------------------------------------------
/docs/multi-agents/introduction.md:
--------------------------------------------------------------------------------
1 | ### Agents Concepts and Architectures
2 |
3 |
4 |
5 | The development of a vertical multi-agent architecture for a personal banking assistant is inspired by a blend of cutting-edge research and practical applications in the field of generative AI and agent-based systems:
6 |
7 | 1. **Agent and Loop Mechanism**: An Agent is made up of three building blocks: Instructions (a.k.a prompt), Tools and LLM model to use. The core idea of agents is to use a language model like gpt4 to choose a sequence of actions, which can be supported by tools, in order to solve a task based on the instructions provided in the prompt. This can best be thought of as a loop where the agent iteratively processes user input, decides on actions or responses, and updates its internal state (agent scratchpad). This mechanism allows for complex interactions and task execution beyond simple text generation.
8 |
9 | 2. **Integration of AI Agents with LLMs and RAG**: AI agents enhance copilot application based on Retrieval Augmented Generation (RAG) pattern by enabling real-world task execution, decision-making, and real-time interaction. While in RAG a sequence of actions are well-known and are developed as a predefined LLM chain in app code, with agents a LLM is used as a reasoning engine to determine which actions to take and in which order.This integration is crucial for applications requiring dynamic responses and actions based on user inputs.
10 |
11 | 3. **Multi-Agent Architectures**: The debate between single and multi-agent systems highlights the versatility of multi-agent architectures in handling complex tasks requiring collaboration and multiple execution paths. Vertical and horizontal architectures represent two approaches, with most systems falling somewhere in between. Specifically, vertical architectures, where one agent acts as a leader coordinating with other specialized agents, are particularly relevant for building a personal banking assistant. This structure allows for a clear division of labor and efficient collaboration among agents with different functional domains.
12 |
13 | 5. **MicroAgents Concept**: The idea of MicroAgents, as proposed by the semantic kernel team at Microsoft, offers a practical approach for implementing vertical multi-agent systems. By partitioning agents by functional domain and associating each with a microservice, a banking assistant can leverage specialized knowledge and services (e.g., account management, transaction history, payments) to provide a comprehensive and user-friendly experience.
14 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 3.3.6
9 |
10 |
11 | com.microsoft.openai.samples.assistant
12 | personal-finance-assistant-copilot
13 | 1.0.0-SNAPSHOT
14 | personal-finance-assistant-copilot-langchain4j
15 | This sample demonstrate how to create a generative ai multi-agent solution for a banking personal assistant
16 |
17 |
18 | 17
19 | 17
20 |
21 | 5.20.0
22 | 4.5.1
23 | 1.0.0-beta2
24 |
25 |
26 |
27 |
28 |
29 | com.azure.spring
30 | spring-cloud-azure-dependencies
31 | ${spring-cloud-azure.version}
32 | pom
33 | import
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | com.microsoft.openai.samples.assistant
42 | langchain4j-agents
43 | 1.0.0-SNAPSHOT
44 |
45 |
46 | org.springframework.boot
47 | spring-boot-starter-web
48 |
49 |
50 | org.springframework.boot
51 | spring-boot-starter-security
52 |
53 |
54 | dev.langchain4j
55 | langchain4j-azure-open-ai
56 | ${langchain4j.version}
57 |
58 |
59 | com.azure
60 | azure-identity
61 |
62 |
63 |
64 |
65 |
66 |
67 | org.springframework.boot
68 | spring-boot-maven-plugin
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/app/frontend/src/components/AnalysisPanel/AnalysisPanel.tsx:
--------------------------------------------------------------------------------
1 | import { Pivot, PivotItem } from "@fluentui/react";
2 | import DOMPurify from "dompurify";
3 |
4 | import styles from "./AnalysisPanel.module.css";
5 |
6 | import { SupportingContent } from "../SupportingContent";
7 | import { ChatAppResponse } from "../../api";
8 | import { AnalysisPanelTabs } from "./AnalysisPanelTabs";
9 |
10 | interface Props {
11 | className: string;
12 | activeTab: AnalysisPanelTabs;
13 | onActiveTabChanged: (tab: AnalysisPanelTabs) => void;
14 | activeCitation: string | undefined;
15 | citationHeight: string;
16 | answer: ChatAppResponse;
17 | }
18 |
19 | const pivotItemDisabledStyle = { disabled: true, style: { color: "grey" } };
20 |
21 | export const AnalysisPanel = ({ answer, activeTab, activeCitation, citationHeight, className, onActiveTabChanged }: Props) => {
22 | const isDisabledThoughtProcessTab: boolean = !answer.choices[0].context.thoughts;
23 | const isDisabledSupportingContentTab: boolean = !answer.choices[0].context.data_points.length;
24 | const isDisabledCitationTab: boolean = !activeCitation;
25 |
26 | const sanitizedThoughts = DOMPurify.sanitize(answer.choices[0].context.thoughts!);
27 |
28 | return (
29 | pivotItem && onActiveTabChanged(pivotItem.props.itemKey! as AnalysisPanelTabs)}
33 | >
34 |
39 |
40 |
41 |
46 |
47 |
48 |
53 |
54 |
55 |
56 | );
57 | };
58 |
--------------------------------------------------------------------------------
/app/frontend/src/components/TokenClaimsDisplay/TokenClaimsDisplay.tsx:
--------------------------------------------------------------------------------
1 | import { Label } from "@fluentui/react";
2 | import { useMsal } from "@azure/msal-react";
3 | import {
4 | DataGridBody,
5 | DataGridRow,
6 | DataGrid,
7 | DataGridHeader,
8 | DataGridHeaderCell,
9 | DataGridCell,
10 | createTableColumn,
11 | TableColumnDefinition
12 | } from "@fluentui/react-table";
13 |
14 | type Claim = {
15 | name: string;
16 | value: string;
17 | };
18 |
19 | export const TokenClaimsDisplay = () => {
20 | const { instance } = useMsal();
21 | const activeAccount = instance.getActiveAccount();
22 |
23 | const ToString = (a: string | any) => {
24 | if (typeof a === "string") {
25 | return a;
26 | } else {
27 | return JSON.stringify(a);
28 | }
29 | };
30 |
31 | const items: Claim[] = activeAccount?.idTokenClaims
32 | ? Object.keys(activeAccount.idTokenClaims).map((key: string) => {
33 | return { name: key, value: ToString((activeAccount.idTokenClaims ?? {})[key]) };
34 | })
35 | : [];
36 |
37 | const columns: TableColumnDefinition[] = [
38 | createTableColumn({
39 | columnId: "name",
40 | compare: (a: Claim, b: Claim) => {
41 | return a.name.localeCompare(b.name);
42 | },
43 | renderHeaderCell: () => {
44 | return "Name";
45 | },
46 | renderCell: item => {
47 | return item.name;
48 | }
49 | }),
50 | createTableColumn({
51 | columnId: "value",
52 | compare: (a: Claim, b: Claim) => {
53 | return a.value.localeCompare(b.value);
54 | },
55 | renderHeaderCell: () => {
56 | return "Value";
57 | },
58 | renderCell: item => {
59 | return item.value;
60 | }
61 | })
62 | ];
63 |
64 | return (
65 |
66 |
67 | item.name}>
68 |
69 | {({ renderHeaderCell }) => {renderHeaderCell()}}
70 |
71 | >
72 | {({ item, rowId }) => key={rowId}>{({ renderCell }) => {renderCell(item)}}}
73 |
74 |
75 |
76 | );
77 | };
78 |
--------------------------------------------------------------------------------
/app/copilot/langchain4j-agents/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.microsoft.openai.samples.assistant
6 | copilot-parent
7 | 1.0.0-SNAPSHOT
8 | ../pom.xml
9 |
10 |
11 | langchain4j-agents
12 | agents implementation based on LangChain4j
13 |
14 |
15 |
16 |
17 | com.microsoft.openai.samples.assistant
18 | copilot-common
19 | 1.0.0-SNAPSHOT
20 |
21 |
22 | dev.langchain4j
23 | langchain4j-mcp
24 |
25 | ${langchain4j.version}
26 |
27 |
28 | dev.langchain4j
29 | langchain4j-azure-open-ai
30 | ${langchain4j.version}
31 | test
32 |
33 |
34 | com.azure
35 | azure-identity
36 |
37 | test
38 |
39 |
40 | org.junit.jupiter
41 | junit-jupiter-api
42 | 5.11.3
43 | test
44 |
45 |
46 | ch.qos.logback
47 | logback-classic
48 | 1.5.8
49 | test
50 |
51 |
52 | org.assertj
53 | assertj-core
54 | 3.27.3
55 | test
56 |
57 |
58 | org.wiremock
59 | wiremock
60 | 3.12.1
61 | test
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Security
4 |
5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](), please report it to us as described below.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
14 |
15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/msrc/pgp-key-msrc).
16 |
17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
18 |
19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20 |
21 | - Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | - Full paths of source file(s) related to the manifestation of the issue
23 | - The location of the affected source code (tag/branch/commit or direct URL)
24 | - Any special configuration required to reproduce the issue
25 | - Step-by-step instructions to reproduce the issue
26 | - Proof-of-concept or exploit code (if possible)
27 | - Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/msrc/cvd).
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/manifests/azd-env-configmap.yml:
--------------------------------------------------------------------------------
1 | # Updated 2024-04-18 16:01:23
2 | apiVersion: v1
3 | kind: ConfigMap
4 | metadata:
5 | name: azd-env-configmap
6 | data:
7 | API_ALLOW_ORIGINS: ""
8 | APPLICATIONINSIGHTS_CONNECTION_STRING: "InstrumentationKey=6fa478c2-3ea8-451e-8350-acafdaf6264a;IngestionEndpoint=https://francecentral-1.in.applicationinsights.azure.com/;LiveEndpoint=https://francecentral.livediagnostics.monitor.azure.com/;ApplicationId=cc2958e9-c1f3-4fc8-910f-49d81979c208"
9 | AZURE_AKS_CLUSTER_NAME: "aks-yvh3rjtnvwvvm"
10 | AZURE_CLIENT_ID: "19ccc9b6-37b0-47ce-b99e-efddf02bef26"
11 | AZURE_CONTAINER_REGISTRY_ENDPOINT: "cryvh3rjtnvwvvm.azurecr.io"
12 | AZURE_CONTAINER_REGISTRY_NAME: "cryvh3rjtnvwvvm"
13 | AZURE_ENV_NAME: "java-chat-12-aks-test"
14 | AZURE_FORMRECOGNIZER_RESOURCE_GROUP: "rg-java-chat-12-aks-test"
15 | AZURE_FORMRECOGNIZER_SERVICE: "cog-fr-yvh3rjtnvwvvm"
16 | AZURE_KEY_VAULT_ENDPOINT: "https://kv-yvh3rjtnvwvvm.vault.azure.net/"
17 | AZURE_LOCATION: "francecentral"
18 | AZURE_OPENAI_CHATGPT_DEPLOYMENT: "chat"
19 | AZURE_OPENAI_CHATGPT_MODEL: "gpt-35-turbo"
20 | AZURE_OPENAI_EMB_DEPLOYMENT: "embedding"
21 | AZURE_OPENAI_EMB_MODEL_NAME: "text-embedding-ada-002"
22 | AZURE_OPENAI_RESOURCE_GROUP: "rg-java-chat-12-aks-test"
23 | AZURE_OPENAI_SERVICE: "cog-yvh3rjtnvwvvm"
24 | AZURE_RESOURCE_GROUP: "rg-java-chat-12-aks-test"
25 | AZURE_SEARCH_INDEX: "gptkbindex"
26 | AZURE_SEARCH_SERVICE: "gptkb-yvh3rjtnvwvvm"
27 | AZURE_SEARCH_SERVICE_RESOURCE_GROUP: "rg-java-chat-12-aks-test"
28 | AZURE_SERVICEBUS_NAMESPACE: "sb-yvh3rjtnvwvvm"
29 | AZURE_SERVICEBUS_SKU_NAME: "Standard"
30 | AZURE_STORAGE_ACCOUNT: "styvh3rjtnvwvvm"
31 | AZURE_STORAGE_CONTAINER: "content"
32 | AZURE_STORAGE_RESOURCE_GROUP: "rg-java-chat-12-aks-test"
33 | AZURE_SUBSCRIPTION_ID: "8b82fc4d-aabe-4658-88f2-5674bf49eec0"
34 | AZURE_TENANT_ID: "fa3408a7-4997-49b5-bda2-62886815fa49"
35 | OPENAI_API_KEY: ""
36 | OPENAI_HOST: "azure"
37 | OPENAI_ORGANIZATION: ""
38 | REACT_APP_APPLICATIONINSIGHTS_CONNECTION_STRING: "InstrumentationKey=6fa478c2-3ea8-451e-8350-acafdaf6264a;IngestionEndpoint=https://francecentral-1.in.applicationinsights.azure.com/;LiveEndpoint=https://francecentral.livediagnostics.monitor.azure.com/;ApplicationId=cc2958e9-c1f3-4fc8-910f-49d81979c208"
39 | SERVICE_API_ENDPOINT_URL: "http://68.220.199.26"
40 | SERVICE_API_IMAGE_NAME: "crnq6gqbtyxqno2.azurecr.io/azure-search-openai-demo/api-java-chat-12-aks-test:azd-deploy-1710941647"
41 | SERVICE_FRONTEND_ENDPOINT_URL: "http://10.0.118.173:80"
42 | SERVICE_FRONTEND_IMAGE_NAME: "crnq6gqbtyxqno2.azurecr.io/azure-search-openai-demo/frontend-java-chat-12-aks-test:azd-deploy-1710941650"
43 | SERVICE_INDEXER_ENDPOINT_URL: "http://10.0.165.214:80"
44 | SERVICE_INDEXER_IMAGE_NAME: "crnq6gqbtyxqno2.azurecr.io/azure-search-openai-demo/indexer-java-chat-12-aks-test:azd-deploy-1710941756"
45 |
46 |
--------------------------------------------------------------------------------
/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/config/MCPAgentsConfiguration.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | package com.microsoft.openai.samples.assistant.config;
3 |
4 | import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
5 | import com.microsoft.openai.samples.assistant.langchain4j.agent.SupervisorAgent;
6 | import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.AccountMCPAgent;
7 | import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.PaymentMCPAgent;
8 | import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.TransactionHistoryMCPAgent;
9 | import com.microsoft.openai.samples.assistant.security.LoggedUserService;
10 | import dev.langchain4j.model.chat.ChatLanguageModel;
11 | import org.springframework.beans.factory.annotation.Value;
12 | import org.springframework.context.annotation.Bean;
13 | import org.springframework.context.annotation.Configuration;
14 |
15 | import java.util.List;
16 |
17 | @Configuration
18 | public class MCPAgentsConfiguration {
19 | @Value("${transactions.api.url}") String transactionsMCPServerUrl;
20 | @Value("${accounts.api.url}") String accountsMCPServerUrl;
21 | @Value("${payments.api.url}") String paymentsMCPServerUrl;
22 |
23 | private final ChatLanguageModel chatLanguageModel;
24 | private final LoggedUserService loggedUserService;
25 | private final DocumentIntelligenceInvoiceScanHelper documentIntelligenceInvoiceScanHelper;
26 |
27 | public MCPAgentsConfiguration(ChatLanguageModel chatLanguageModel, LoggedUserService loggedUserService, DocumentIntelligenceInvoiceScanHelper documentIntelligenceInvoiceScanHelper) {
28 | this.chatLanguageModel = chatLanguageModel;
29 | this.loggedUserService = loggedUserService;
30 | this.documentIntelligenceInvoiceScanHelper = documentIntelligenceInvoiceScanHelper;
31 | }
32 | @Bean
33 | public AccountMCPAgent accountMCPAgent() {
34 | return new AccountMCPAgent(chatLanguageModel, loggedUserService.getLoggedUser().username(), accountsMCPServerUrl);
35 | }
36 |
37 | @Bean
38 | public TransactionHistoryMCPAgent transactionHistoryMCPAgent() {
39 | return new TransactionHistoryMCPAgent(chatLanguageModel, loggedUserService.getLoggedUser().username(), transactionsMCPServerUrl,accountsMCPServerUrl);
40 | }
41 |
42 | @Bean
43 | public PaymentMCPAgent paymentMCPAgent() {
44 | return new PaymentMCPAgent(chatLanguageModel,documentIntelligenceInvoiceScanHelper, loggedUserService.getLoggedUser().username(),transactionsMCPServerUrl,accountsMCPServerUrl, paymentsMCPServerUrl);
45 | }
46 |
47 | @Bean
48 | public SupervisorAgent supervisorAgent(ChatLanguageModel chatLanguageModel){
49 | return new SupervisorAgent(chatLanguageModel,
50 | List.of(accountMCPAgent(),
51 | transactionHistoryMCPAgent(),
52 | paymentMCPAgent()));
53 |
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/app/frontend/src/components/Answer/Answer.module.css:
--------------------------------------------------------------------------------
1 | .answerContainer {
2 | padding: 20px;
3 | background: rgb(249, 249, 249);
4 | border-radius: 8px;
5 | box-shadow:
6 | 0px 2px 4px rgba(0, 0, 0, 0.14),
7 | 0px 0px 2px rgba(0, 0, 0, 0.12);
8 | outline: transparent solid 1px;
9 | }
10 |
11 | .answerLogo {
12 | font-size: 28px;
13 | }
14 |
15 | .answerText {
16 | font-size: 16px;
17 | font-weight: 400;
18 | line-height: 22px;
19 | padding-top: 16px;
20 | padding-bottom: 16px;
21 | white-space: pre-line;
22 | }
23 |
24 | .answerText table {
25 | border-collapse: collapse;
26 | }
27 |
28 | .answerText td,
29 | .answerText th {
30 | border: 1px solid;
31 | padding: 5px;
32 | }
33 |
34 | .selected {
35 | outline: 2px solid rgba(115, 118, 225, 1);
36 | }
37 |
38 | .citationLearnMore {
39 | margin-right: 5px;
40 | font-weight: 600;
41 | line-height: 24px;
42 | }
43 |
44 | .citation {
45 | font-weight: 500;
46 | line-height: 24px;
47 | text-align: center;
48 | border-radius: 4px;
49 | padding: 0px 8px;
50 | background: #d1dbfa;
51 | color: #123bb6;
52 | text-decoration: none;
53 | cursor: pointer;
54 | }
55 |
56 | .citation:hover {
57 | text-decoration: underline;
58 | }
59 |
60 | .followupQuestionsList {
61 | margin-top: 10px;
62 | }
63 |
64 | .followupQuestionLearnMore {
65 | margin-right: 5px;
66 | font-weight: 600;
67 | line-height: 24px;
68 | }
69 |
70 | .followupQuestion {
71 | font-weight: 600;
72 | line-height: 24px;
73 | text-align: center;
74 | border-radius: 4px;
75 | padding: 0px 8px;
76 | background: #e8ebfa;
77 | color: black;
78 | font-style: italic;
79 | text-decoration: none;
80 | cursor: pointer;
81 | }
82 |
83 | .supContainer {
84 | text-decoration: none;
85 | cursor: pointer;
86 | }
87 |
88 | .supContainer:hover {
89 | text-decoration: underline;
90 | }
91 |
92 | sup {
93 | position: relative;
94 | display: inline-flex;
95 | align-items: center;
96 | justify-content: center;
97 | font-size: 10px;
98 | font-weight: 600;
99 | vertical-align: top;
100 | top: -1;
101 | margin: 0px 2px;
102 | min-width: 14px;
103 | height: 14px;
104 | border-radius: 3px;
105 | background: #d1dbfa;
106 | color: #123bb6;
107 | text-decoration-color: transparent;
108 | outline: transparent solid 1px;
109 | cursor: pointer;
110 | }
111 |
112 | .retryButton {
113 | width: fit-content;
114 | }
115 |
116 | @keyframes loading {
117 | 0% {
118 | content: "";
119 | }
120 | 25% {
121 | content: ".";
122 | }
123 | 50% {
124 | content: "..";
125 | }
126 | 75% {
127 | content: "...";
128 | }
129 | 100% {
130 | content: "";
131 | }
132 | }
133 |
134 | .loadingdots::after {
135 | content: "";
136 | animation: loading 1s infinite;
137 | }
138 |
--------------------------------------------------------------------------------
/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/mcp/TransactionHistoryMCPAgent.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.openai.samples.assistant.langchain4j.agent.mcp;
2 |
3 | import com.microsoft.langchain4j.agent.AgentMetadata;
4 | import com.microsoft.langchain4j.agent.mcp.MCPProtocolType;
5 | import com.microsoft.langchain4j.agent.mcp.MCPServerMetadata;
6 | import com.microsoft.langchain4j.agent.mcp.MCPToolAgent;
7 | import dev.langchain4j.model.chat.ChatLanguageModel;
8 | import dev.langchain4j.model.input.Prompt;
9 | import dev.langchain4j.model.input.PromptTemplate;
10 |
11 | import java.util.List;
12 | import java.util.Map;
13 |
14 | public class TransactionHistoryMCPAgent extends MCPToolAgent {
15 |
16 | private final Prompt agentPrompt;
17 |
18 | private static final String TRANSACTION_HISTORY_AGENT_SYSTEM_MESSAGE = """
19 | you are a personal financial advisor who help the user with their recurrent bill payments. To search about the payments history you need to know the payee name and the account id.
20 | If the user doesn't provide the payee name, search the last 10 transactions order by date.
21 | If the user want to search last transactions for a specific payee, ask to provide the payee name.
22 | Use html list or table to display the transaction information.
23 | Always use the below logged user details to retrieve account info:
24 | '{{loggedUserName}}'
25 | Current timestamp:
26 | '{{currentDateTime}}'
27 | """;
28 |
29 | public TransactionHistoryMCPAgent(ChatLanguageModel chatModel, String loggedUserName, String transactionMCPServerUrl, String accountMCPServerUrl) {
30 | super(chatModel, List.of(new MCPServerMetadata("transaction-history", transactionMCPServerUrl, MCPProtocolType.SSE),
31 | new MCPServerMetadata("account", accountMCPServerUrl, MCPProtocolType.SSE)));
32 |
33 | if (loggedUserName == null || loggedUserName.isEmpty()) {
34 | throw new IllegalArgumentException("loggedUserName cannot be null or empty");
35 | }
36 |
37 | PromptTemplate promptTemplate = PromptTemplate.from(TRANSACTION_HISTORY_AGENT_SYSTEM_MESSAGE);
38 | var datetimeIso8601 = java.time.ZonedDateTime.now(java.time.ZoneId.of("UTC")).toInstant().toString();
39 |
40 | this.agentPrompt = promptTemplate.apply(Map.of(
41 | "loggedUserName", loggedUserName,
42 | "currentDateTime", datetimeIso8601
43 | ));
44 | }
45 |
46 | @Override
47 | public String getName() {
48 | return "TransactionHistoryAgent";
49 | }
50 |
51 | @Override
52 | public AgentMetadata getMetadata() {
53 | return new AgentMetadata(
54 | "Personal financial advisor for retrieving transaction history information.",
55 | List.of("RetrieveTransactionHistory", "DisplayTransactionDetails")
56 | );
57 | }
58 |
59 | @Override
60 | protected String getSystemMessage() {
61 | return agentPrompt.text();
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/app/business-api/account/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 3.3.6
9 |
10 |
11 | com.microsoft.openai.samples.assistant.business
12 | account
13 | 1.0.0-SNAPSHOT
14 |
15 |
16 | 17
17 | 17
18 | UTF-8
19 |
20 |
21 |
22 |
23 |
24 |
25 | org.springframework.ai
26 | spring-ai-bom
27 | 1.0.0-SNAPSHOT
28 | pom
29 | import
30 |
31 |
32 |
33 |
34 |
35 |
36 | org.springframework.ai
37 | spring-ai-starter-mcp-server-webmvc
38 |
39 |
40 | org.springframework.boot
41 | spring-boot-starter-web
42 |
43 |
44 |
45 |
46 |
47 |
48 | org.springframework.boot
49 | spring-boot-maven-plugin
50 |
51 |
52 |
53 |
54 |
55 |
56 | Central Portal Snapshots
57 | central-portal-snapshots
58 | https://central.sonatype.com/repository/maven-snapshots/
59 |
60 | false
61 |
62 |
63 | true
64 |
65 |
66 |
67 | spring-milestones
68 | Spring Milestones
69 | https://repo.spring.io/milestone
70 |
71 | false
72 |
73 |
74 |
75 | spring-snapshots
76 | Spring Snapshots
77 | https://repo.spring.io/snapshot
78 |
79 | false
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------