getlastTransactions(String accountId) {
-
- return lastTransactions.get(accountId);
- }
-
-}
diff --git a/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/proxy/OpenAIProxy.java b/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/proxy/OpenAIProxy.java
deleted file mode 100644
index 182cd9d..0000000
--- a/app/copilot/copilot-backend/src/main/java/com/microsoft/openai/samples/assistant/proxy/OpenAIProxy.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-package com.microsoft.openai.samples.assistant.proxy;
-
-import com.azure.ai.openai.OpenAIClient;
-import com.azure.ai.openai.models.*;
-import com.azure.core.exception.HttpResponseException;
-import com.azure.core.util.IterableStream;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-import org.springframework.web.server.ResponseStatusException;
-
-import java.util.List;
-
-/**
- * This class is a proxy to the OpenAI API to simplify cross-cutting concerns management (security,
- * load balancing, monitoring, resiliency). It is responsible for: - calling the OpenAI API -
- * handling errors and retry strategy - load balance requests across open AI instances - add
- * monitoring points - add circuit breaker with exponential backoff
- *
- * It also makes unit testing easy using mockito to provide mock implementation for this bean.
- */
-@Component
-public class OpenAIProxy {
-
- private final OpenAIClient client;
-
- @Value("${openai.chatgpt.deployment}")
- private String gptChatDeploymentModelId;
-
- @Value("${openai.embedding.deployment}")
- private String embeddingDeploymentModelId;
-
- public OpenAIProxy(OpenAIClient client) {
- this.client = client;
- }
-
- public Completions getCompletions(CompletionsOptions completionsOptions) {
- Completions completions;
- try {
- completions = client.getCompletions(this.gptChatDeploymentModelId, completionsOptions);
- } catch (HttpResponseException e) {
- throw new ResponseStatusException(
- e.getResponse().getStatusCode(), "Error calling OpenAI API:" + e.getValue(), e);
- }
- return completions;
- }
-
- public Completions getCompletions(String prompt) {
-
- Completions completions;
- try {
- completions = client.getCompletions(this.gptChatDeploymentModelId, prompt);
- } catch (HttpResponseException e) {
- throw new ResponseStatusException(
- e.getResponse().getStatusCode(),
- "Error calling OpenAI API:" + e.getMessage(),
- e);
- }
- return completions;
- }
-
- public ChatCompletions getChatCompletions(ChatCompletionsOptions chatCompletionsOptions) {
- ChatCompletions chatCompletions;
- try {
- chatCompletions =
- client.getChatCompletions(
- this.gptChatDeploymentModelId, chatCompletionsOptions);
- } catch (HttpResponseException e) {
- throw new ResponseStatusException(
- e.getResponse().getStatusCode(),
- "Error calling OpenAI API:" + e.getMessage(),
- e);
- }
- return chatCompletions;
- }
-
- public IterableStream getChatCompletionsStream(
- ChatCompletionsOptions chatCompletionsOptions) {
- try {
- return client.getChatCompletionsStream(
- this.gptChatDeploymentModelId, chatCompletionsOptions);
- } catch (HttpResponseException e) {
- throw new ResponseStatusException(
- e.getResponse().getStatusCode(),
- "Error calling OpenAI API:" + e.getMessage(),
- e);
- }
- }
-
- public Embeddings getEmbeddings(List texts) {
- Embeddings embeddings;
- try {
- EmbeddingsOptions embeddingsOptions = new EmbeddingsOptions(texts);
- embeddingsOptions.setUser("search-openai-demo-java");
- embeddingsOptions.setModel(this.embeddingDeploymentModelId);
- embeddingsOptions.setInputType("query");
- embeddings = client.getEmbeddings(this.embeddingDeploymentModelId, embeddingsOptions);
- } catch (HttpResponseException e) {
- throw new ResponseStatusException(
- e.getResponse().getStatusCode(),
- "Error calling OpenAI API:" + e.getMessage(),
- e);
- }
- return embeddings;
- }
-}
diff --git a/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/AccountAgentIntegrationTest.java b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/AccountAgentIntegrationTest.java
index e86eff4..cf03a7e 100644
--- a/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/AccountAgentIntegrationTest.java
+++ b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/AccountAgentIntegrationTest.java
@@ -1,8 +1,54 @@
package com.microsoft.openai.samples.assistant;
+import com.google.adk.events.Event;
+import com.google.adk.runner.InMemoryRunner;
+import com.google.adk.sessions.Session;
+import com.google.genai.types.Content;
+import com.google.genai.types.Part;
+import com.microsoft.openai.samples.assistant.config.agent.AccountAgent;
+import dev.langchain4j.model.chat.ChatModel;
+import io.reactivex.rxjava3.core.Flowable;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
public class AccountAgentIntegrationTest {
+ private static String APPNAME = "AccountAgentTest";
+ private static String USER= "user1";
public static void main(String[] args) {
+ String accountMCPServerURL = System.getenv("ACCOUNT_MCP_SERVER");
+
+ ChatModel langchain4jModel =
+ AzureOpenAILangchain4J.buildChatModel();
+
+ var agent = new AccountAgent(langchain4jModel,accountMCPServerURL);
+
+ var runner = new InMemoryRunner(agent.getAgent(), APPNAME);
+
+ var initialState = new ConcurrentHashMap();
+ initialState.put("loggedUserName", "bob.user@contoso.com");
+
+ Session session =
+ runner.sessionService()
+ .createSession(APPNAME, USER,initialState,null)
+ .blockingGet();
+
+
+ Content userMsg = Content.fromParts(Part.fromText("What is my account balance?"));
+ System.out.print("\nYou > "+ userMsg.text());
+ Flowable events = runner.runAsync(USER, session.id(), userMsg);
+
+ System.out.print("\nAgent > ");
+ events.blockingForEach(event -> System.out.println(event.stringifyContent()));
+
+ userMsg = Content.fromParts(Part.fromText("What about my visa ?"));
+ System.out.print("\nYou > "+userMsg.text());
+
+ events = runner.runAsync(USER, session.id(), userMsg);
+
+ System.out.print("\nAgent > ");
+ events.blockingForEach(event -> System.out.println(event.stringifyContent()));
}
}
diff --git a/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/AzureOpenAILangchain4J.java b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/AzureOpenAILangchain4J.java
new file mode 100644
index 0000000..1be9f10
--- /dev/null
+++ b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/AzureOpenAILangchain4J.java
@@ -0,0 +1,41 @@
+package com.microsoft.openai.samples.assistant;
+
+import com.azure.ai.openai.OpenAIClient;
+import com.azure.ai.openai.OpenAIClientBuilder;
+import com.azure.core.credential.TokenCredential;
+import com.azure.core.http.policy.HttpLogDetailLevel;
+import com.azure.core.http.policy.HttpLogOptions;
+import com.azure.identity.AzureCliCredentialBuilder;
+import dev.langchain4j.model.azure.AzureOpenAiChatModel;
+import dev.langchain4j.model.chat.ChatModel;
+
+public class AzureOpenAILangchain4J {
+
+ public static ChatModel buildChatModel(){
+ String azureOpenAIName = System.getenv("AZURE_OPENAI_NAME");
+ String chatDeploymentName = System.getenv("AZURE_OPENAI_DEPLOYMENT_ID");
+ if (azureOpenAIName == null || azureOpenAIName.isEmpty())
+ throw new IllegalArgumentException("AZURE_OPENAI_NAME environment variable is not set.");
+
+
+ TokenCredential tokenCredential = new AzureCliCredentialBuilder().build();
+
+ String endpoint = "https://%s.openai.azure.com".formatted(azureOpenAIName);
+
+ var httpLogOptions = new HttpLogOptions();
+ // httpLogOptions.setPrettyPrintBody(true);
+ httpLogOptions.setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS);
+
+ OpenAIClient client = new OpenAIClientBuilder()
+ .endpoint(endpoint)
+ .credential(tokenCredential)
+ .httpLogOptions(httpLogOptions)
+ .buildClient();
+
+ return AzureOpenAiChatModel.builder()
+ .openAIClient(client)
+ .logRequestsAndResponses(true)
+ .deploymentName(chatDeploymentName)
+ .build();
+ }
+}
diff --git a/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/EvaluationLoopIntegrationTest.java b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/EvaluationLoopIntegrationTest.java
new file mode 100644
index 0000000..1113900
--- /dev/null
+++ b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/EvaluationLoopIntegrationTest.java
@@ -0,0 +1,124 @@
+package com.microsoft.openai.samples.assistant;
+
+import com.azure.ai.documentintelligence.DocumentIntelligenceClient;
+import com.azure.ai.documentintelligence.DocumentIntelligenceClientBuilder;
+import com.azure.identity.AzureCliCredentialBuilder;
+import com.google.adk.agents.LlmAgent;
+import com.google.adk.agents.LoopAgent;
+import com.google.adk.events.Event;
+import com.google.adk.runner.InMemoryRunner;
+import com.google.adk.sessions.Session;
+import com.google.genai.types.Content;
+import com.google.genai.types.Part;
+import com.microsoft.openai.samples.assistant.config.agent.*;
+import com.microsoft.openai.samples.assistant.config.agent.isolated.CollaborationEvaluatorAgent;
+import com.microsoft.openai.samples.assistant.config.agent.isolated.PaymentAgentNoDependencies;
+import com.microsoft.openai.samples.assistant.config.agent.isolated.TransactionAgentNoDependencies;
+import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
+import com.microsoft.openai.samples.assistant.proxy.BlobStorageProxy;
+import dev.langchain4j.model.chat.ChatModel;
+import io.reactivex.rxjava3.core.Flowable;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Scanner;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class EvaluationLoopIntegrationTest {
+ private static String APPNAME = "PaymentAgentTest";
+ private static String USER= "user1";
+
+ public static void main(String[] args) {
+
+ ChatModel langchain4jModel =
+ AzureOpenAILangchain4J.buildChatModel();
+
+ LlmAgent accountAgent = buildAccountAgent(langchain4jModel);
+ LlmAgent transactionsAgent = buildTransactionsAgent(langchain4jModel);
+ LlmAgent paymentAgent = buildPaymentAgent(langchain4jModel);
+ LlmAgent evaluatorAgent = buildEvaluatorAgent(langchain4jModel);
+
+
+ var supervisorAgent = new SupervisorAgent(langchain4jModel,accountAgent,transactionsAgent,paymentAgent);
+
+ LoopAgent loopAgent = LoopAgent.builder()
+ .name("AgentGroupChatLoop")
+ .description("Repeatedly evaluate agents collaboration")
+ .subAgents(supervisorAgent.getAgent(), evaluatorAgent)
+ .maxIterations(5)
+ .build();
+
+ InMemoryRunner runner = new InMemoryRunner(loopAgent, APPNAME);
+
+ ConcurrentMap initialState = new ConcurrentHashMap<>();
+ // initialState.put("accountId", "1010");
+ initialState.put("loggedUserName", "bob.user@contoso.com");
+ var datetimeIso8601 = java.time.ZonedDateTime.now(java.time.ZoneId.of("UTC")).toInstant().toString();
+ initialState.put("timestamp", datetimeIso8601);
+
+ Session session =
+ runner.sessionService()
+ .createSession(APPNAME, USER,initialState,null)
+ .blockingGet();
+
+
+ try (Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8)) {
+ while (true) {
+ System.out.print("\nYou > ");
+ String userInput = scanner.nextLine();
+
+ if ("quit".equalsIgnoreCase(userInput)) {
+ break;
+ }
+
+ Content userMsg = Content.fromParts(Part.fromText(userInput));
+ Flowable events = runner.runAsync(USER, session.id(), userMsg);
+
+ events.blockingForEach(event -> System.out.println("\n"+event.author()+(event.finalResponse()?"[Final]" :"") +" > " +event.stringifyContent()));
+ }
+ }
+ }
+
+ private static LlmAgent buildEvaluatorAgent(ChatModel langchain4jModel) {
+ return new CollaborationEvaluatorAgent(langchain4jModel).getAgent();
+ }
+
+ private static LlmAgent buildPaymentAgent(ChatModel langchain4jModel) {
+ String paymentMCPServerURL = System.getenv("PAYMENT_MCP_SERVER");
+
+ var documentIntelligenceInvoiceScanHelper = new DocumentIntelligenceInvoiceScanHelper(getDocumentIntelligenceClient(),getBlobStorageProxyClient());
+
+ var paymentAgent = new PaymentAgentNoDependencies(langchain4jModel,paymentMCPServerURL,documentIntelligenceInvoiceScanHelper);
+ return paymentAgent.getAgent();
+ }
+
+ private static LlmAgent buildTransactionsAgent(ChatModel langchain4jModel) {
+ String transactionsMCPServerURL = System.getenv("TRANSACTION_MCP_SERVER");
+ var transactionsAgent = new TransactionAgentNoDependencies(langchain4jModel,transactionsMCPServerURL);
+ return transactionsAgent.getAgent();
+ }
+
+ private static LlmAgent buildAccountAgent(ChatModel langchain4jModel) {
+
+ String accountMCPServerURL = System.getenv("ACCOUNT_MCP_SERVER");
+ var accountAgent = new AccountAgent(langchain4jModel,accountMCPServerURL);
+ return accountAgent.getAgent();
+
+ }
+
+
+ private static BlobStorageProxy getBlobStorageProxyClient() {
+
+ String containerName = "content";
+ return new BlobStorageProxy(System.getenv("AZURE_STORAGE_ACCOUNT"),containerName,new AzureCliCredentialBuilder().build());
+ }
+
+ private static DocumentIntelligenceClient getDocumentIntelligenceClient() {
+ String endpoint = "https://%s.cognitiveservices.azure.com".formatted(System.getenv("AZURE_DOCUMENT_INTELLIGENCE_SERVICE"));
+
+ return new DocumentIntelligenceClientBuilder()
+ .credential(new AzureCliCredentialBuilder().build())
+ .endpoint(endpoint)
+ .buildClient();
+ }
+}
diff --git a/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/PaymentAgentIntegrationTest.java b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/PaymentAgentIntegrationTest.java
new file mode 100644
index 0000000..068e016
--- /dev/null
+++ b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/PaymentAgentIntegrationTest.java
@@ -0,0 +1,82 @@
+package com.microsoft.openai.samples.assistant;
+
+import com.azure.ai.documentintelligence.DocumentIntelligenceClient;
+import com.azure.ai.documentintelligence.DocumentIntelligenceClientBuilder;
+import com.azure.identity.AzureCliCredentialBuilder;
+import com.google.adk.events.Event;
+import com.google.adk.runner.InMemoryRunner;
+import com.google.adk.sessions.Session;
+import com.google.genai.types.Content;
+import com.google.genai.types.Part;
+import com.microsoft.openai.samples.assistant.config.agent.isolated.PaymentAgentNoDependencies;
+import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
+import com.microsoft.openai.samples.assistant.proxy.BlobStorageProxy;
+import dev.langchain4j.model.chat.ChatModel;
+import io.reactivex.rxjava3.core.Flowable;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Scanner;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class PaymentAgentIntegrationTest {
+ private static String APPNAME = "PaymentAgentTest";
+ private static String USER= "user1";
+
+ public static void main(String[] args) {
+
+ String paymentMCPServerURL = System.getenv("PAYMENT_MCP_SERVER");
+
+ ChatModel langchain4jModel =
+ AzureOpenAILangchain4J.buildChatModel();
+
+ var documentIntelligenceInvoiceScanHelper = new DocumentIntelligenceInvoiceScanHelper(getDocumentIntelligenceClient(),getBlobStorageProxyClient());
+
+ var agent = new PaymentAgentNoDependencies(langchain4jModel,paymentMCPServerURL,documentIntelligenceInvoiceScanHelper);
+
+ InMemoryRunner runner = new InMemoryRunner(agent.getAgent(), APPNAME);
+
+ ConcurrentMap initialState = new ConcurrentHashMap<>();
+ initialState.put("accountId", "1010");
+ var datetimeIso8601 = java.time.ZonedDateTime.now(java.time.ZoneId.of("UTC")).toInstant().toString();
+ initialState.put("timestamp", datetimeIso8601);
+
+ Session session =
+ runner.sessionService()
+ .createSession(APPNAME, USER,initialState,null)
+ .blockingGet();
+
+
+ try (Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8)) {
+ while (true) {
+ System.out.print("\nYou > ");
+ String userInput = scanner.nextLine();
+
+ if ("quit".equalsIgnoreCase(userInput)) {
+ break;
+ }
+
+ Content userMsg = Content.fromParts(Part.fromText(userInput));
+ Flowable events = runner.runAsync(USER, session.id(), userMsg);
+
+ System.out.print("\nAgent > ");
+ events.blockingForEach(event -> System.out.println(event.stringifyContent()));
+ }
+ }
+ }
+
+ private static BlobStorageProxy getBlobStorageProxyClient() {
+
+ String containerName = "content";
+ return new BlobStorageProxy(System.getenv("AZURE_STORAGE_ACCOUNT"),containerName,new AzureCliCredentialBuilder().build());
+ }
+
+ private static DocumentIntelligenceClient getDocumentIntelligenceClient() {
+ String endpoint = "https://%s.cognitiveservices.azure.com".formatted(System.getenv("AZURE_DOCUMENT_INTELLIGENCE_SERVICE"));
+
+ return new DocumentIntelligenceClientBuilder()
+ .credential(new AzureCliCredentialBuilder().build())
+ .endpoint(endpoint)
+ .buildClient();
+ }
+}
diff --git a/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/PaymentOrchestrationIntegrationTest.java b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/PaymentOrchestrationIntegrationTest.java
new file mode 100644
index 0000000..4960b55
--- /dev/null
+++ b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/PaymentOrchestrationIntegrationTest.java
@@ -0,0 +1,104 @@
+package com.microsoft.openai.samples.assistant;
+
+import com.azure.ai.documentintelligence.DocumentIntelligenceClient;
+import com.azure.ai.documentintelligence.DocumentIntelligenceClientBuilder;
+import com.azure.identity.AzureCliCredentialBuilder;
+import com.google.adk.agents.LlmAgent;
+import com.google.adk.events.Event;
+import com.google.adk.runner.InMemoryRunner;
+import com.google.adk.sessions.Session;
+import com.google.genai.types.Content;
+import com.google.genai.types.Part;
+import com.microsoft.openai.samples.assistant.config.agent.AccountAgent;
+import com.microsoft.openai.samples.assistant.config.agent.PaymentAgent;
+import com.microsoft.openai.samples.assistant.config.agent.TransactionAgent;
+import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
+import com.microsoft.openai.samples.assistant.proxy.BlobStorageProxy;
+import dev.langchain4j.model.chat.ChatModel;
+import io.reactivex.rxjava3.core.Flowable;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Scanner;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class PaymentOrchestrationIntegrationTest {
+ private static String APPNAME = "PaymentAgentTest";
+ private static String USER= "user1";
+
+ public static void main(String[] args) {
+
+ ChatModel langchain4jModel =
+ AzureOpenAILangchain4J.buildChatModel();
+
+ LlmAgent accountAgent = buildAccountAgent(langchain4jModel);
+ LlmAgent transactionsAgent = buildTransactionsAgent(langchain4jModel,accountAgent);
+
+ String paymentMCPServerURL = System.getenv("PAYMENT_MCP_SERVER");
+
+ var documentIntelligenceInvoiceScanHelper = new DocumentIntelligenceInvoiceScanHelper(getDocumentIntelligenceClient(),getBlobStorageProxyClient());
+
+ var agent = new PaymentAgent(langchain4jModel,paymentMCPServerURL,documentIntelligenceInvoiceScanHelper,accountAgent,transactionsAgent);
+
+ InMemoryRunner runner = new InMemoryRunner(agent.getAgent(), APPNAME);
+
+ ConcurrentMap initialState = new ConcurrentHashMap<>();
+ // initialState.put("accountId", "1010");
+ initialState.put("loggedUserName", "bob.user@contoso.com");
+ var datetimeIso8601 = java.time.ZonedDateTime.now(java.time.ZoneId.of("UTC")).toInstant().toString();
+ initialState.put("timestamp", datetimeIso8601);
+
+ Session session =
+ runner.sessionService()
+ .createSession(APPNAME, USER,initialState,null)
+ .blockingGet();
+
+
+ try (Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8)) {
+ while (true) {
+ System.out.print("\nYou > ");
+ String userInput = scanner.nextLine();
+
+ if ("quit".equalsIgnoreCase(userInput)) {
+ break;
+ }
+
+ Content userMsg = Content.fromParts(Part.fromText(userInput));
+ Flowable events = runner.runAsync(USER, session.id(), userMsg);
+
+ System.out.print("\nAgent > ");
+ events.blockingForEach(event -> System.out.println(event.stringifyContent()));
+ }
+ }
+ }
+
+ private static LlmAgent buildTransactionsAgent(ChatModel langchain4jModel,LlmAgent accountAgent) {
+ String transactionsMCPServerURL = System.getenv("TRANSACTION_MCP_SERVER");
+ var transactionsAgent = new TransactionAgent(langchain4jModel,transactionsMCPServerURL,accountAgent);
+ return transactionsAgent.getAgent();
+ }
+
+ private static LlmAgent buildAccountAgent(ChatModel langchain4jModel) {
+
+ String accountMCPServerURL = System.getenv("ACCOUNT_MCP_SERVER");
+ var accountAgent = new AccountAgent(langchain4jModel,accountMCPServerURL);
+ return accountAgent.getAgent();
+
+ }
+
+
+ private static BlobStorageProxy getBlobStorageProxyClient() {
+
+ String containerName = "content";
+ return new BlobStorageProxy(System.getenv("AZURE_STORAGE_ACCOUNT"),containerName,new AzureCliCredentialBuilder().build());
+ }
+
+ private static DocumentIntelligenceClient getDocumentIntelligenceClient() {
+ String endpoint = "https://%s.cognitiveservices.azure.com".formatted(System.getenv("AZURE_DOCUMENT_INTELLIGENCE_SERVICE"));
+
+ return new DocumentIntelligenceClientBuilder()
+ .credential(new AzureCliCredentialBuilder().build())
+ .endpoint(endpoint)
+ .buildClient();
+ }
+}
diff --git a/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/SupervisorAgentIntegrationTest.java b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/SupervisorAgentIntegrationTest.java
new file mode 100644
index 0000000..17820be
--- /dev/null
+++ b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/SupervisorAgentIntegrationTest.java
@@ -0,0 +1,113 @@
+package com.microsoft.openai.samples.assistant;
+
+import com.azure.ai.documentintelligence.DocumentIntelligenceClient;
+import com.azure.ai.documentintelligence.DocumentIntelligenceClientBuilder;
+import com.azure.identity.AzureCliCredentialBuilder;
+import com.google.adk.agents.LlmAgent;
+import com.google.adk.events.Event;
+import com.google.adk.runner.InMemoryRunner;
+import com.google.adk.sessions.Session;
+import com.google.genai.types.Content;
+import com.google.genai.types.Part;
+import com.microsoft.openai.samples.assistant.config.agent.*;
+import com.microsoft.openai.samples.assistant.config.agent.isolated.PaymentAgentNoDependencies;
+import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
+import com.microsoft.openai.samples.assistant.proxy.BlobStorageProxy;
+import dev.langchain4j.model.chat.ChatModel;
+import io.reactivex.rxjava3.core.Flowable;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Scanner;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class SupervisorAgentIntegrationTest {
+ private static String APPNAME = "SupervisorTest";
+ private static String USER= "user1";
+
+ public static void main(String[] args) {
+
+ ChatModel langchain4jModel =
+ AzureOpenAILangchain4J.buildChatModel();
+
+ LlmAgent accountAgent = buildAccountAgent(langchain4jModel);
+ LlmAgent transactionsAgent = buildTransactionsAgent(langchain4jModel,accountAgent);
+ LlmAgent paymentAgent = buildPaymentAgent(langchain4jModel,accountAgent,transactionsAgent);
+
+ var supervisorAgent = new SupervisorAgent(langchain4jModel,accountAgent,transactionsAgent,paymentAgent);
+
+ InMemoryRunner runner = new InMemoryRunner(supervisorAgent.getAgent(), APPNAME);
+
+ ConcurrentMap initialState = new ConcurrentHashMap<>();
+ // initialState.put("accountId", "1010");
+ initialState.put("loggedUserName", "bob.user@contoso.com");
+ var datetimeIso8601 = java.time.ZonedDateTime.now(java.time.ZoneId.of("UTC")).toInstant().toString();
+ initialState.put("timestamp", datetimeIso8601);
+
+ Session session =
+ runner.sessionService()
+ .createSession(APPNAME, USER,initialState,null)
+ .blockingGet();
+
+
+ try (Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8)) {
+ while (true) {
+ System.out.print("\nYou > ");
+ String userInput = scanner.nextLine();
+
+ if ("quit".equalsIgnoreCase(userInput)) {
+ break;
+ }
+
+ Content userMsg = Content.fromParts(Part.fromText(userInput));
+ Flowable events = runner.runAsync(USER, session.id(), userMsg);
+
+ //events.blockingForEach(event -> System.out.println("\n"+event.author()+" > " +event.stringifyContent()));
+ events.blockingIterable().forEach(event -> System.out.println("\n"+event.author()+(event.finalResponse()?"[Final]" :"") +" > " +event.stringifyContent()));
+ }
+ }
+ }
+
+ private static LlmAgent buildPaymentAgent(ChatModel langchain4jModel,LlmAgent accountAgent,LlmAgent transactionsAgent) {
+ String paymentMCPServerURL = System.getenv("PAYMENT_MCP_SERVER");
+
+ var documentIntelligenceInvoiceScanHelper = new DocumentIntelligenceInvoiceScanHelper(getDocumentIntelligenceClient(),getBlobStorageProxyClient());
+
+ var paymentAgent = new PaymentAgent(langchain4jModel,
+ paymentMCPServerURL,
+ documentIntelligenceInvoiceScanHelper,
+ accountAgent,
+ transactionsAgent);
+ return paymentAgent.getAgent();
+ }
+
+ private static LlmAgent buildTransactionsAgent(ChatModel langchain4jModel,LlmAgent accountAgent) {
+ String transactionsMCPServerURL = System.getenv("TRANSACTION_MCP_SERVER");
+ var transactionsAgent = new TransactionAgent(langchain4jModel,transactionsMCPServerURL,accountAgent);
+ return transactionsAgent.getAgent();
+ }
+
+ private static LlmAgent buildAccountAgent(ChatModel langchain4jModel) {
+
+ String accountMCPServerURL = System.getenv("ACCOUNT_MCP_SERVER");
+ var accountAgent = new AccountAgent(langchain4jModel,accountMCPServerURL);
+ return accountAgent.getAgent();
+
+ }
+
+
+ private static BlobStorageProxy getBlobStorageProxyClient() {
+
+ String containerName = "content";
+ return new BlobStorageProxy(System.getenv("AZURE_STORAGE_ACCOUNT"),containerName,new AzureCliCredentialBuilder().build());
+ }
+
+ private static DocumentIntelligenceClient getDocumentIntelligenceClient() {
+ String endpoint = "https://%s.cognitiveservices.azure.com".formatted(System.getenv("AZURE_DOCUMENT_INTELLIGENCE_SERVICE"));
+
+ return new DocumentIntelligenceClientBuilder()
+ .credential(new AzureCliCredentialBuilder().build())
+ .endpoint(endpoint)
+ .buildClient();
+ }
+}
diff --git a/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/TransactionAgentIntegrationTest.java b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/TransactionAgentIntegrationTest.java
new file mode 100644
index 0000000..83337e8
--- /dev/null
+++ b/app/copilot/copilot-backend/src/test/java/com/microsoft/openai/samples/assistant/TransactionAgentIntegrationTest.java
@@ -0,0 +1,68 @@
+package com.microsoft.openai.samples.assistant;
+
+import com.google.adk.agents.LlmAgent;
+import com.google.adk.events.Event;
+import com.google.adk.runner.InMemoryRunner;
+import com.google.adk.sessions.Session;
+import com.google.genai.types.Content;
+import com.google.genai.types.Part;
+import com.microsoft.openai.samples.assistant.config.agent.AccountAgent;
+import com.microsoft.openai.samples.assistant.config.agent.TransactionAgent;
+import dev.langchain4j.model.chat.ChatModel;
+import io.reactivex.rxjava3.core.Flowable;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class TransactionAgentIntegrationTest {
+ private static String APPNAME = "TransactionAgentTest";
+ private static String USER= "user1";
+
+ public static void main(String[] args) {
+
+ String transactionMCPServerURL = System.getenv("TRANSACTION_MCP_SERVER");
+
+ ChatModel langchain4jModel =
+ AzureOpenAILangchain4J.buildChatModel();
+
+ LlmAgent accountAgent = buildAccountAgent(langchain4jModel);
+
+ var agent = new TransactionAgent(langchain4jModel,transactionMCPServerURL,accountAgent);
+
+ InMemoryRunner runner = new InMemoryRunner(agent.getAgent(), APPNAME);
+
+ ConcurrentMap initialState = new ConcurrentHashMap<>();
+ initialState.put("loggedUserName", "bob.user@contoso.com");
+ var datetimeIso8601 = java.time.ZonedDateTime.now(java.time.ZoneId.of("UTC")).toInstant().toString();
+ initialState.put("timestamp", datetimeIso8601);
+
+ Session session =
+ runner.sessionService()
+ .createSession(APPNAME, USER,initialState,null)
+ .blockingGet();
+
+
+ Content userMsg = Content.fromParts(Part.fromText("When was last time I've paid contoso?"));
+ System.out.print("\nYou > "+ userMsg.text());
+ Flowable events = runner.runAsync(USER, session.id(), userMsg);
+
+ System.out.print("\nAgent > ");
+ events.blockingForEach(event -> System.out.println(event.stringifyContent()));
+
+ userMsg = Content.fromParts(Part.fromText("ok, what about my last transactions"));
+ System.out.print("\nYou > "+userMsg.text());
+
+ events = runner.runAsync(USER, session.id(), userMsg);
+
+ System.out.print("\nAgent > ");
+ events.blockingForEach(event -> System.out.println(event.stringifyContent()));
+ }
+
+ private static LlmAgent buildAccountAgent(ChatModel langchain4jModel) {
+
+ String accountMCPServerURL = System.getenv("ACCOUNT_MCP_SERVER");
+ var accountAgent = new AccountAgent(langchain4jModel,accountMCPServerURL);
+ return accountAgent.getAgent();
+
+ }
+}
diff --git a/app/copilot/langchain4j-agents/pom.xml b/app/copilot/langchain4j-agents/pom.xml
deleted file mode 100644
index 0d84ef1..0000000
--- a/app/copilot/langchain4j-agents/pom.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
- 4.0.0
-
- com.microsoft.openai.samples.assistant
- copilot-parent
- 1.0.0-SNAPSHOT
- ../pom.xml
-
-
- langchain4j-agents
- agents implementation based on LangChain4j
-
-
-
-
- com.microsoft.openai.samples.assistant
- copilot-common
- 1.0.0-SNAPSHOT
-
-
- dev.langchain4j
- langchain4j-mcp
-
- ${langchain4j.version}
-
-
- dev.langchain4j
- langchain4j-azure-open-ai
- ${langchain4j.version}
- test
-
-
- com.azure
- azure-identity
-
- test
-
-
- org.junit.jupiter
- junit-jupiter-api
- 5.11.3
- test
-
-
- ch.qos.logback
- logback-classic
- 1.5.8
- test
-
-
- org.assertj
- assertj-core
- 3.27.3
- test
-
-
- org.wiremock
- wiremock
- 3.12.1
- test
-
-
-
-
\ No newline at end of file
diff --git a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/AbstractReActAgent.java b/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/AbstractReActAgent.java
deleted file mode 100644
index 9fe57b8..0000000
--- a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/AbstractReActAgent.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package com.microsoft.langchain4j.agent;
-
-import dev.langchain4j.agent.tool.ToolSpecification;
-import dev.langchain4j.data.message.ChatMessage;
-import dev.langchain4j.data.message.SystemMessage;
-import dev.langchain4j.memory.ChatMemory;
-import dev.langchain4j.memory.chat.MessageWindowChatMemory;
-import dev.langchain4j.agent.tool.ToolExecutionRequest;
-import dev.langchain4j.data.message.ToolExecutionResultMessage;
-import dev.langchain4j.model.chat.ChatLanguageModel;
-import dev.langchain4j.model.chat.request.ChatRequest;
-import dev.langchain4j.model.chat.request.ChatRequestParameters;
-import dev.langchain4j.service.tool.ToolExecutor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-public abstract class AbstractReActAgent implements Agent {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(AbstractReActAgent.class);
-
- protected final ChatLanguageModel chatModel;
-
- protected AbstractReActAgent(ChatLanguageModel chatModel) {
- if (chatModel == null) {
- throw new IllegalArgumentException("chatModel cannot be null");
- }
- this.chatModel = chatModel;
- }
-
- @Override
- public List invoke(List chatHistory) throws AgentExecutionException {
- LOGGER.info("------------- {} -------------", this.getName());
-
- try {
- var internalChatMemory = buildInternalChat(chatHistory);
-
- ChatRequestParameters parameters = ChatRequestParameters.builder()
- .toolSpecifications(getToolSpecifications())
- .build();
-
- ChatRequest request = ChatRequest.builder()
- .messages(internalChatMemory.messages())
- .parameters(parameters)
- .build();
-
- var aiMessage = chatModel.chat(request).aiMessage();
-
- // ReAct planning with tools
- while (aiMessage != null && aiMessage.hasToolExecutionRequests()) {
- List toolExecutionResultMessages = executeToolRequests(aiMessage.toolExecutionRequests());
-
- internalChatMemory.add(aiMessage);
- toolExecutionResultMessages.forEach(internalChatMemory::add);
-
- ChatRequest toolExecutionResultResponseRequest = ChatRequest.builder()
- .messages(internalChatMemory.messages())
- .parameters(parameters)
- .build();
-
- aiMessage = chatModel.chat(toolExecutionResultResponseRequest).aiMessage();
- }
-
- LOGGER.info("Agent response: {}", aiMessage.text());
-
- // add last ai message to agent internal memory
- internalChatMemory.add(aiMessage);
- return buildResponse(chatHistory, internalChatMemory);
- } catch (Exception e) {
- throw new AgentExecutionException("Error during agent [%s] invocation".formatted(this.getName()), e);
- }
- }
-
- protected List buildResponse(List chatHistory, ChatMemory internalChatMemory) {
- return internalChatMemory.messages()
- .stream()
- .filter(m -> !(m instanceof SystemMessage))
- .collect(Collectors.toList());
- }
-
- protected List executeToolRequests(List toolExecutionRequests) {
- List toolExecutionResultMessages = new ArrayList<>();
- for (ToolExecutionRequest toolExecutionRequest : toolExecutionRequests) {
- var toolExecutor = getToolExecutor(toolExecutionRequest.name());
- LOGGER.info("Executing {} with params {}", toolExecutionRequest.name(), toolExecutionRequest.arguments());
- String result = toolExecutor.execute(toolExecutionRequest, null);
- LOGGER.info("Response from {}: {}", toolExecutionRequest.name(), result);
- if (result == null || result.isEmpty()) {
- LOGGER.warn("Tool {} returned empty result but successfully completed. Setting result=ok.", toolExecutionRequest.name());
- result = "ok";
- }
- toolExecutionResultMessages.add(ToolExecutionResultMessage.from(toolExecutionRequest, result));
- }
- return toolExecutionResultMessages;
- }
-
- protected ChatMemory buildInternalChat(List chatHistory) {
- var internalChatMemory = MessageWindowChatMemory.builder()
- .id("default")
- .maxMessages(20)
- .build();
-
- internalChatMemory.add(SystemMessage.from(getSystemMessage()));
- chatHistory.forEach(internalChatMemory::add);
- return internalChatMemory;
- }
-
- protected abstract String getSystemMessage();
-
- protected abstract List getToolSpecifications();
-
- protected abstract ToolExecutor getToolExecutor(String toolName);
-}
\ No newline at end of file
diff --git a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/Agent.java b/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/Agent.java
deleted file mode 100644
index 631d58f..0000000
--- a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/Agent.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.microsoft.langchain4j.agent;
-
-import dev.langchain4j.data.message.ChatMessage;
-
-import java.util.List;
-
-public interface Agent {
-
- String getName();
- AgentMetadata getMetadata();
- List invoke(List chatHistory) throws AgentExecutionException;
-}
diff --git a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/AgentExecutionException.java b/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/AgentExecutionException.java
deleted file mode 100644
index 6b5cf5d..0000000
--- a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/AgentExecutionException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.microsoft.langchain4j.agent;
-
-public class AgentExecutionException extends RuntimeException {
- public AgentExecutionException(String message) {
- super(message);
- }
-
- public AgentExecutionException(String message, Throwable cause) {
- super(message, cause);
- }
-}
\ No newline at end of file
diff --git a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/AgentMetadata.java b/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/AgentMetadata.java
deleted file mode 100644
index 26eb16f..0000000
--- a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/AgentMetadata.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.microsoft.langchain4j.agent;
-
-import java.util.List;
-
-public record AgentMetadata(String description, List intents) {
-}
-
diff --git a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/mcp/MCPProtocolType.java b/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/mcp/MCPProtocolType.java
deleted file mode 100644
index 5247ce1..0000000
--- a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/mcp/MCPProtocolType.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.microsoft.langchain4j.agent.mcp;
-
-public enum MCPProtocolType {
- SSE,
- STDIO
-}
diff --git a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/mcp/MCPServerMetadata.java b/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/mcp/MCPServerMetadata.java
deleted file mode 100644
index 9bbdb96..0000000
--- a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/mcp/MCPServerMetadata.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.microsoft.langchain4j.agent.mcp;
-
-public record MCPServerMetadata(String serverName, String url, MCPProtocolType protocolType) {
-}
-
diff --git a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/mcp/MCPToolAgent.java b/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/mcp/MCPToolAgent.java
deleted file mode 100644
index 69a3cc7..0000000
--- a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/langchain4j/agent/mcp/MCPToolAgent.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package com.microsoft.langchain4j.agent.mcp;
-
-import com.microsoft.langchain4j.agent.AbstractReActAgent;
-
-import com.microsoft.langchain4j.agent.AgentExecutionException;
-import dev.langchain4j.agent.tool.ToolExecutionRequest;
-import dev.langchain4j.agent.tool.ToolSpecification;
-import dev.langchain4j.data.message.ToolExecutionResultMessage;
-
-import dev.langchain4j.mcp.client.DefaultMcpClient;
-import dev.langchain4j.mcp.client.McpClient;
-import dev.langchain4j.mcp.client.transport.McpTransport;
-import dev.langchain4j.mcp.client.transport.http.HttpMcpTransport;
-import dev.langchain4j.model.chat.ChatLanguageModel;
-
-import dev.langchain4j.service.tool.ToolExecutor;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public abstract class MCPToolAgent extends AbstractReActAgent {
- private static final Logger LOGGER = LoggerFactory.getLogger(MCPToolAgent.class);
-
- protected List toolSpecifications;
- protected Map extendedExecutorMap;
- protected List mcpClients;
- protected Map tool2ClientMap;
-
- protected MCPToolAgent(ChatLanguageModel chatModel, List mcpServerMetadata) {
- super(chatModel);
- this.mcpClients = new ArrayList<>();
- this.tool2ClientMap = new HashMap<>();
- this.toolSpecifications = new ArrayList<>();
- this.extendedExecutorMap = new HashMap<>();
-
- mcpServerMetadata.forEach(metadata -> {
- //only SSE is supported
- if(metadata.protocolType().equals(MCPProtocolType.SSE)){
- McpTransport transport = new HttpMcpTransport.Builder()
- .sseUrl(metadata.url())
- .logRequests(true) // if you want to see the traffic in the log
- .logResponses(true)
- .timeout(Duration.ofHours(3))
- .build();
-
- McpClient mcpClient = new DefaultMcpClient.Builder()
- .transport(transport)
- .build();
- mcpClient
- .listTools()
- .forEach(toolSpecification -> {
- this.tool2ClientMap.put(toolSpecification.name(),mcpClient);
- this.toolSpecifications.add(toolSpecification);
- }
- );
- this.mcpClients.add(mcpClient);
-
- }
-
- });
-
- }
-
- @Override
- protected List getToolSpecifications() {
- return this.toolSpecifications;
- }
-
-
- @Override
- protected ToolExecutor getToolExecutor(String toolName) {
- throw new AgentExecutionException("getToolExecutor not required when using MCP. if you landed here please review your agent code");
- }
-
- protected List executeToolRequests(List toolExecutionRequests) {
- List toolExecutionResultMessages = new ArrayList<>();
- for (ToolExecutionRequest toolExecutionRequest : toolExecutionRequests) {
-
- String result = "ko";
-
- // try first the extended executors
- var toolExecutor = extendedExecutorMap.get(toolExecutionRequest.name());
- if( toolExecutor != null){
- LOGGER.info("Executing {} with params {}", toolExecutionRequest.name(), toolExecutionRequest.arguments());
- result = toolExecutor.execute(toolExecutionRequest,null);
- LOGGER.info("Response from {}: {}", toolExecutionRequest.name(), result);
-
- }else{
- var mcpClient = tool2ClientMap.get(toolExecutionRequest.name());
- if (mcpClient == null) {
- throw new IllegalArgumentException("No MCP executor found for tool name: " + toolExecutionRequest.name());
- }
- LOGGER.info("Executing {} with params {}", toolExecutionRequest.name(), toolExecutionRequest.arguments());
- result = mcpClient.executeTool(toolExecutionRequest);
- LOGGER.info("Response from {}: {}", toolExecutionRequest.name(), result);
- }
-
- if (result == null || result.isEmpty()) {
- LOGGER.warn("Tool {} returned empty result but successfully completed. Setting result=ok.", toolExecutionRequest.name());
- result = "ok";
- }
- toolExecutionResultMessages.add(ToolExecutionResultMessage.from(toolExecutionRequest, result));
- }
- return toolExecutionResultMessages;
- }
-}
\ No newline at end of file
diff --git a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/SupervisorAgent.java b/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/SupervisorAgent.java
deleted file mode 100644
index 1616899..0000000
--- a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/SupervisorAgent.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package com.microsoft.openai.samples.assistant.langchain4j.agent;
-
-
-import com.microsoft.langchain4j.agent.Agent;
-import com.microsoft.langchain4j.agent.AgentExecutionException;
-import com.microsoft.langchain4j.agent.AgentMetadata;
-import dev.langchain4j.data.message.AiMessage;
-import dev.langchain4j.data.message.ChatMessage;
-import dev.langchain4j.data.message.ToolExecutionResultMessage;
-import dev.langchain4j.memory.ChatMemory;
-import dev.langchain4j.memory.chat.MessageWindowChatMemory;
-import dev.langchain4j.model.chat.ChatLanguageModel;
-import dev.langchain4j.model.chat.request.ChatRequest;
-import dev.langchain4j.model.input.Prompt;
-import dev.langchain4j.model.input.PromptTemplate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-public class SupervisorAgent {
-
- private final Logger LOGGER = LoggerFactory.getLogger(SupervisorAgent.class);
- private final ChatLanguageModel chatLanguageModel;
- private final List agents;
- private final Map agentsMetadata;
- private final Prompt agentPrompt;
- //When false only detect the next agent but doesn't route to it. It will answer with the agent name.
- private Boolean routing = true;
-
- private final String SUPERVISOR_AGENT_SINGLETURN_SYSTEM_MESSAGE = """
- You are a banking customer support agent triaging conversation and select the best agent name that can solve the customer need.
- Use the below list of agents metadata to select the best one for the customer request:
- {{agentsMetadata}}
- Answer only with the agent name.
- if you are not able to select an agent answer with none.
- """;
-
- public SupervisorAgent(ChatLanguageModel chatLanguageModel, List agents, Boolean routing) {
- this.chatLanguageModel = chatLanguageModel;
- this.agents = agents;
- this.routing = routing;
-
- this.agentsMetadata = agents.stream()
- .collect(Collectors.toMap(Agent::getName, Agent::getMetadata));
-
- PromptTemplate promptTemplate = PromptTemplate.from(SUPERVISOR_AGENT_SINGLETURN_SYSTEM_MESSAGE);
- agentPrompt =promptTemplate.apply(Map.of("agentsMetadata", this.agentsMetadata));
-
- }
- public SupervisorAgent(ChatLanguageModel chatLanguageModel, List agents) {
- this(chatLanguageModel, agents, true);
- }
-
-
- public List invoke(List chatHistory) {
- LOGGER.info("------------- SupervisorAgent -------------");
-
- var internalChatMemory = buildInternalChat(chatHistory);
-
- ChatRequest request = ChatRequest.builder()
- .messages(internalChatMemory.messages())
- .build();
-
- AiMessage aiMessage = chatLanguageModel.chat(request).aiMessage();
- String nextAgent = aiMessage.text();
- LOGGER.info("Supervisor Agent handoff to [{}]", nextAgent);
-
- if (routing) {
- return singleTurnRouting(nextAgent, chatHistory);
- }
-
- return new ArrayList<>();
- }
-
-
- protected List singleTurnRouting(String nextAgent, List chatHistory) {
- if("none".equalsIgnoreCase(nextAgent)){
- LOGGER.info("Gracefully handle clarification.. ");
- AiMessage clarificationMessage = AiMessage.builder().
- text(" I'm not sure about your request. Can you please clarify?")
- .build();
- chatHistory.add(clarificationMessage);
- return chatHistory;
- }
-
- Agent agent = agents.stream()
- .filter(a -> a.getName().equals(nextAgent))
- .findFirst()
- .orElseThrow(() -> new AgentExecutionException("Agent not found: " + nextAgent));
-
- return agent.invoke(chatHistory);
- }
-
-
-
- private ChatMemory buildInternalChat(List chatHistory) {
- //build a new chat memory to preserve order of messages otherwise the model hallucinate.
- var internalChatMemory = MessageWindowChatMemory.builder()
- .id("default")
- .maxMessages(20)
- .build();
-
- internalChatMemory.add(dev.langchain4j.data.message.SystemMessage.from(agentPrompt.text()));
- // filter out tool requests and tool execution results
- chatHistory.stream()
- .filter(chatMessage -> {
- if (chatMessage instanceof ToolExecutionResultMessage) {
- return false;
- }
- if (chatMessage instanceof AiMessage) {
- return !((AiMessage) chatMessage).hasToolExecutionRequests();
- }
- return true;
- })
- .forEach(internalChatMemory::add);
- return internalChatMemory;
- }
-}
diff --git a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/mcp/AccountMCPAgent.java b/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/mcp/AccountMCPAgent.java
deleted file mode 100644
index 729d853..0000000
--- a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/mcp/AccountMCPAgent.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.microsoft.openai.samples.assistant.langchain4j.agent.mcp;
-
-import com.microsoft.langchain4j.agent.AgentMetadata;
-import com.microsoft.langchain4j.agent.mcp.MCPProtocolType;
-import com.microsoft.langchain4j.agent.mcp.MCPServerMetadata;
-import com.microsoft.langchain4j.agent.mcp.MCPToolAgent;
-import dev.langchain4j.model.chat.ChatLanguageModel;
-import dev.langchain4j.model.input.Prompt;
-import dev.langchain4j.model.input.PromptTemplate;
-
-import java.util.List;
-import java.util.Map;
-
-public class AccountMCPAgent extends MCPToolAgent {
-
- private final Prompt agentPrompt;
-
- private static final String ACCOUNT_AGENT_SYSTEM_MESSAGE = """
- you are a personal financial advisor who help the user to retrieve information about their bank accounts.
- Use html list or table to display the account information.
- Always use the below logged user details to retrieve account info:
- '{{loggedUserName}}'
- """;
-
- public AccountMCPAgent(ChatLanguageModel chatModel, String loggedUserName, String accountMCPServerUrl) {
- super(chatModel, List.of(new MCPServerMetadata("account", accountMCPServerUrl, MCPProtocolType.SSE)));
-
- if (loggedUserName == null || loggedUserName.isEmpty()) {
- throw new IllegalArgumentException("loggedUserName cannot be null or empty");
- }
-
- PromptTemplate promptTemplate = PromptTemplate.from(ACCOUNT_AGENT_SYSTEM_MESSAGE);
- this.agentPrompt = promptTemplate.apply(Map.of("loggedUserName", loggedUserName));
- }
-
- @Override
- public String getName() {
- return "AccountAgent";
- }
-
- @Override
- public AgentMetadata getMetadata() {
- return new AgentMetadata(
- "Personal financial advisor for retrieving bank account information.",
- List.of("RetrieveAccountInfo", "DisplayAccountDetails")
- );
- }
-
- @Override
- protected String getSystemMessage() {
- return agentPrompt.text();
- }
-
-}
diff --git a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/mcp/PaymentMCPAgent.java b/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/mcp/PaymentMCPAgent.java
deleted file mode 100644
index 35f0d1c..0000000
--- a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/mcp/PaymentMCPAgent.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package com.microsoft.openai.samples.assistant.langchain4j.agent.mcp;
-
-import com.microsoft.langchain4j.agent.AgentExecutionException;
-import com.microsoft.langchain4j.agent.AgentMetadata;
-import com.microsoft.langchain4j.agent.mcp.MCPProtocolType;
-import com.microsoft.langchain4j.agent.mcp.MCPServerMetadata;
-import com.microsoft.langchain4j.agent.mcp.MCPToolAgent;
-import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
-import com.microsoft.openai.samples.assistant.langchain4j.tools.InvoiceScanTool;
-import dev.langchain4j.agent.tool.ToolSpecifications;
-import dev.langchain4j.model.chat.ChatLanguageModel;
-import dev.langchain4j.model.input.Prompt;
-import dev.langchain4j.model.input.PromptTemplate;
-import dev.langchain4j.service.tool.DefaultToolExecutor;
-
-import java.lang.reflect.Method;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.util.List;
-import java.util.Map;
-
-public class PaymentMCPAgent extends MCPToolAgent {
-
- private final Prompt agentPrompt;
-
- private static final String PAYMENT_AGENT_SYSTEM_MESSAGE = """
- you are a personal financial advisor who help the user with their recurrent bill payments. The user may want to pay the bill uploading a photo of the bill, or it may start the payment checking transactions history for a specific payee.
- For the bill payment you need to know the: bill id or invoice number, payee name, the total amount.
- If you don't have enough information to pay the bill ask the user to provide the missing information.
- If the user submit a photo, always ask the user to confirm the extracted data from the photo.
- Always check if the bill has been paid already based on payment history before asking to execute the bill payment.
- Ask for the payment method to use based on the available methods on the user account.
- if the user wants to pay using bank transfer, check if the payee is in account registered beneficiaries list. If not ask the user to provide the payee bank code.
- Check if the payment method selected by the user has enough funds to pay the bill. Don't use the account balance to evaluate the funds.
- Before submitting the payment to the system ask the user confirmation providing the payment details.
- Include in the payment description the invoice id or bill id as following: payment for invoice 1527248.
- When submitting payment always use the available functions to retrieve accountId, paymentMethodId.
- If the payment succeeds provide the user with the payment confirmation. If not provide the user with the error message.
- Use HTML list or table to display bill extracted data, payments, account or transaction details.
- Always use the below logged user details to retrieve account info:
- '{{loggedUserName}}'
- Current timestamp:
- '{{currentDateTime}}'
- Don't try to guess accountId,paymentMethodId from the conversation.When submitting payment always use functions to retrieve accountId, paymentMethodId.
-
- ### Output format
- - Example of showing Payment information:
-
-
- | Payee Name |
- contoso |
-
-
- | Invoice ID |
- 9524011000817857 |
-
-
- | Amount |
- €85.20 |
-
-
- | Payment Method |
- Visa (Card Number: ***477) |
-
-
- | Description |
- Payment for invoice 9524011000817857 |
-
-
-
- - Example of showing Payment methods:
-
- - Bank Transfer
- - Visa (Card Number: ***3667)
-
-
- """;
-
- public PaymentMCPAgent(ChatLanguageModel chatModel, DocumentIntelligenceInvoiceScanHelper documentIntelligenceInvoiceScanHelper, String loggedUserName, String transactionMCPServerURL, String accountMCPServerUrl, String paymentsMCPServerUrl) {
- super(chatModel, List.of(new MCPServerMetadata("payment", paymentsMCPServerUrl, MCPProtocolType.SSE),
- new MCPServerMetadata("transaction", transactionMCPServerURL, MCPProtocolType.SSE),
- new MCPServerMetadata("account", accountMCPServerUrl, MCPProtocolType.SSE)));
-
- if (loggedUserName == null || loggedUserName.isEmpty()) {
- throw new IllegalArgumentException("loggedUserName cannot be null or empty");
- }
-
- extendToolMap(documentIntelligenceInvoiceScanHelper);
-
- PromptTemplate promptTemplate = PromptTemplate.from(PAYMENT_AGENT_SYSTEM_MESSAGE);
- var datetimeIso8601 = ZonedDateTime.now(ZoneId.of("UTC")).toInstant().toString();
-
- this.agentPrompt = promptTemplate.apply(Map.of(
- "loggedUserName", loggedUserName,
- "currentDateTime", datetimeIso8601
- ));
- }
-
- @Override
- public String getName() {
- return "PaymentAgent";
- }
-
- @Override
- public AgentMetadata getMetadata() {
- return new AgentMetadata(
- "Personal financial advisor for submitting payment request.",
- List.of("RetrievePaymentInfo", "DisplayPaymentDetails", "SubmitPayment")
- );
- }
-
- @Override
- protected String getSystemMessage() {
- return agentPrompt.text();
- }
-
- protected void extendToolMap(DocumentIntelligenceInvoiceScanHelper documentIntelligenceInvoiceScanHelper) {
- try {
- Method scanInvoiceMethod = InvoiceScanTool.class.getMethod("scanInvoice", String.class);
- InvoiceScanTool invoiceScanTool = new InvoiceScanTool(documentIntelligenceInvoiceScanHelper);
-
- this.toolSpecifications.addAll(ToolSpecifications.toolSpecificationsFrom(InvoiceScanTool.class));
- this.extendedExecutorMap.put("scanInvoice", new DefaultToolExecutor(invoiceScanTool, scanInvoiceMethod));
- } catch (NoSuchMethodException e) {
- throw new AgentExecutionException("scanInvoice method not found in InvoiceScanTool class. Align class code to be used by Payment Agent", e);
- }
- }
-}
\ No newline at end of file
diff --git a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/mcp/TransactionHistoryMCPAgent.java b/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/mcp/TransactionHistoryMCPAgent.java
deleted file mode 100644
index 9f97d3c..0000000
--- a/app/copilot/langchain4j-agents/src/main/java/com/microsoft/openai/samples/assistant/langchain4j/agent/mcp/TransactionHistoryMCPAgent.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.microsoft.openai.samples.assistant.langchain4j.agent.mcp;
-
-import com.microsoft.langchain4j.agent.AgentMetadata;
-import com.microsoft.langchain4j.agent.mcp.MCPProtocolType;
-import com.microsoft.langchain4j.agent.mcp.MCPServerMetadata;
-import com.microsoft.langchain4j.agent.mcp.MCPToolAgent;
-import dev.langchain4j.model.chat.ChatLanguageModel;
-import dev.langchain4j.model.input.Prompt;
-import dev.langchain4j.model.input.PromptTemplate;
-
-import java.util.List;
-import java.util.Map;
-
-public class TransactionHistoryMCPAgent extends MCPToolAgent {
-
- private final Prompt agentPrompt;
-
- private static final String TRANSACTION_HISTORY_AGENT_SYSTEM_MESSAGE = """
- 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.
- If the user doesn't provide the payee name, search the last 10 transactions order by date.
- If the user want to search last transactions for a specific payee, ask to provide the payee name.
- Use html list or table to display the transaction information.
- Always use the below logged user details to retrieve account info:
- '{{loggedUserName}}'
- Current timestamp:
- '{{currentDateTime}}'
- """;
-
- public TransactionHistoryMCPAgent(ChatLanguageModel chatModel, String loggedUserName, String transactionMCPServerUrl, String accountMCPServerUrl) {
- super(chatModel, List.of(new MCPServerMetadata("transaction-history", transactionMCPServerUrl, MCPProtocolType.SSE),
- new MCPServerMetadata("account", accountMCPServerUrl, MCPProtocolType.SSE)));
-
- if (loggedUserName == null || loggedUserName.isEmpty()) {
- throw new IllegalArgumentException("loggedUserName cannot be null or empty");
- }
-
- PromptTemplate promptTemplate = PromptTemplate.from(TRANSACTION_HISTORY_AGENT_SYSTEM_MESSAGE);
- var datetimeIso8601 = java.time.ZonedDateTime.now(java.time.ZoneId.of("UTC")).toInstant().toString();
-
- this.agentPrompt = promptTemplate.apply(Map.of(
- "loggedUserName", loggedUserName,
- "currentDateTime", datetimeIso8601
- ));
- }
-
- @Override
- public String getName() {
- return "TransactionHistoryAgent";
- }
-
- @Override
- public AgentMetadata getMetadata() {
- return new AgentMetadata(
- "Personal financial advisor for retrieving transaction history information.",
- List.of("RetrieveTransactionHistory", "DisplayTransactionDetails")
- );
- }
-
- @Override
- protected String getSystemMessage() {
- return agentPrompt.text();
- }
-
-}
\ No newline at end of file
diff --git a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/AccountMCPAgentIntegrationTest.java b/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/AccountMCPAgentIntegrationTest.java
deleted file mode 100644
index 25d9977..0000000
--- a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/AccountMCPAgentIntegrationTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package dev.langchain4j.openapi.mcp;
-
-import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.AccountMCPAgent;
-import dev.langchain4j.data.message.ChatMessage;
-import dev.langchain4j.data.message.UserMessage;
-import dev.langchain4j.model.azure.AzureOpenAiChatModel;
-
-import java.util.ArrayList;
-
-public class AccountMCPAgentIntegrationTest {
-
- public static void main(String[] args) throws Exception {
-
- //Azure Open AI Chat Model
- var azureOpenAiChatModel = AzureOpenAiChatModel.builder()
- .apiKey(System.getenv("AZURE_OPENAI_KEY"))
- .endpoint(System.getenv("AZURE_OPENAI_ENDPOINT"))
- .deploymentName(System.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"))
- .temperature(0.3)
- .logRequestsAndResponses(true)
- .build();
-
- var accountAgent = new AccountMCPAgent(azureOpenAiChatModel,"bob.user@contoso.com","http://localhost:8070/sse");
-
- var chatHistory = new ArrayList();
- chatHistory.add(UserMessage.from("How much money do I have in my account?"));
-
- accountAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
- chatHistory.add(UserMessage.from("what about my visa"));
- accountAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
- }
-}
diff --git a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/PaymentMCPAgentIntegrationTest.java b/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/PaymentMCPAgentIntegrationTest.java
deleted file mode 100644
index b18e4eb..0000000
--- a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/PaymentMCPAgentIntegrationTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package dev.langchain4j.openapi.mcp;
-
-import com.azure.ai.documentintelligence.DocumentIntelligenceClient;
-import com.azure.ai.documentintelligence.DocumentIntelligenceClientBuilder;
-import com.azure.identity.AzureCliCredentialBuilder;
-import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.PaymentMCPAgent;
-import com.microsoft.openai.samples.assistant.proxy.BlobStorageProxy;
-import dev.langchain4j.data.message.ChatMessage;
-import dev.langchain4j.data.message.UserMessage;
-import dev.langchain4j.model.azure.AzureOpenAiChatModel;
-
-import java.util.ArrayList;
-
-public class PaymentMCPAgentIntegrationTest {
-
- public static void main(String[] args) throws Exception {
-
- //Azure Open AI Chat Model
- var azureOpenAiChatModel = AzureOpenAiChatModel.builder()
- .apiKey(System.getenv("AZURE_OPENAI_KEY"))
- .endpoint(System.getenv("AZURE_OPENAI_ENDPOINT"))
- .deploymentName(System.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"))
- .temperature(0.3)
- .logRequestsAndResponses(true)
- .build();
-
- var documentIntelligenceInvoiceScanHelper = new DocumentIntelligenceInvoiceScanHelper(getDocumentIntelligenceClient(),getBlobStorageProxyClient());
-
- var paymentAgent = new PaymentMCPAgent(azureOpenAiChatModel,
- documentIntelligenceInvoiceScanHelper,
- "bob.user@contoso.com",
- "http://localhost:8090/sse",
- "http://localhost:8070/sse",
- "http://localhost:8060/sse");
-
- var chatHistory = new ArrayList();
- chatHistory.add(UserMessage.from("Please pay the bill: bill id 1234, payee name contoso, total amount 30."));
-
-
- paymentAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
-
- chatHistory.add(UserMessage.from("use my visa"));
- paymentAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
-
- chatHistory.add(UserMessage.from("yes please proceed with payment"));
- paymentAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
-
-
- }
-
- private static BlobStorageProxy getBlobStorageProxyClient() {
-
- String storageAccountService = "https://%s.blob.core.windows.net".formatted(System.getenv("AZURE_STORAGE_ACCOUNT"));
- String containerName = "content";
- return new BlobStorageProxy(storageAccountService,containerName,new AzureCliCredentialBuilder().build());
- }
-
- private static DocumentIntelligenceClient getDocumentIntelligenceClient() {
- String endpoint = "https://%s.cognitiveservices.azure.com".formatted(System.getenv("AZURE_STORAGE_ACCOUNT"));
-
- return new DocumentIntelligenceClientBuilder()
- .credential(new AzureCliCredentialBuilder().build())
- .endpoint(endpoint)
- .buildClient();
- }
-}
diff --git a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/PaymentMCPAgentIntegrationWithImageTest.java b/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/PaymentMCPAgentIntegrationWithImageTest.java
deleted file mode 100644
index 14cd6a4..0000000
--- a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/PaymentMCPAgentIntegrationWithImageTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package dev.langchain4j.openapi.mcp;
-
-import com.azure.ai.documentintelligence.DocumentIntelligenceClient;
-import com.azure.ai.documentintelligence.DocumentIntelligenceClientBuilder;
-import com.azure.identity.AzureCliCredentialBuilder;
-import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.PaymentMCPAgent;
-import com.microsoft.openai.samples.assistant.proxy.BlobStorageProxy;
-import dev.langchain4j.data.message.ChatMessage;
-import dev.langchain4j.data.message.UserMessage;
-import dev.langchain4j.model.azure.AzureOpenAiChatModel;
-
-import java.util.ArrayList;
-
-public class PaymentMCPAgentIntegrationWithImageTest {
-
- public static void main(String[] args) throws Exception {
-
- //Azure Open AI Chat Model
- var azureOpenAiChatModel = AzureOpenAiChatModel.builder()
- .apiKey(System.getenv("AZURE_OPENAI_KEY"))
- .endpoint(System.getenv("AZURE_OPENAI_ENDPOINT"))
- .deploymentName(System.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"))
- .temperature(0.3)
- .logRequestsAndResponses(true)
- .build();
-
- var documentIntelligenceInvoiceScanHelper = new DocumentIntelligenceInvoiceScanHelper(getDocumentIntelligenceClient(),getBlobStorageProxyClient());
-
- var paymentAgent = new PaymentMCPAgent(azureOpenAiChatModel,
- documentIntelligenceInvoiceScanHelper,
- "bob.user@contoso.com",
- "http://localhost:8090",
- "http://localhost:8070",
- "http://localhost:8060");
-
- var chatHistory = new ArrayList();
- chatHistory.add(UserMessage.from("Please pay this bill gori.png"));
-
- //this flow should activate the scanInvoice tool
-
- paymentAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
- chatHistory.add(UserMessage.from("yep, they are correct"));
- paymentAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
-
- chatHistory.add(UserMessage.from("use my visa"));
- paymentAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
-
- chatHistory.add(UserMessage.from("yes please proceed with payment"));
- paymentAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
-
-
- }
-
- private static BlobStorageProxy getBlobStorageProxyClient() {
-
- String containerName = "content";
- return new BlobStorageProxy(System.getenv("AZURE_STORAGE_ACCOUNT"),containerName,new AzureCliCredentialBuilder().build());
- }
-
- private static DocumentIntelligenceClient getDocumentIntelligenceClient() {
- String endpoint = "https://%s.cognitiveservices.azure.com".formatted(System.getenv("AZURE_DOCUMENT_INTELLIGENCE_SERVICE"));
-
- return new DocumentIntelligenceClientBuilder()
- .credential(new AzureCliCredentialBuilder().build())
- .endpoint(endpoint)
- .buildClient();
- }
-}
diff --git a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/SupervisorAgentLongConversationIntegrationTest.java b/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/SupervisorAgentLongConversationIntegrationTest.java
deleted file mode 100644
index 7ccdc1a..0000000
--- a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/SupervisorAgentLongConversationIntegrationTest.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package dev.langchain4j.openapi.mcp;
-
-import com.azure.ai.documentintelligence.DocumentIntelligenceClient;
-import com.azure.ai.documentintelligence.DocumentIntelligenceClientBuilder;
-import com.azure.identity.AzureCliCredentialBuilder;
-import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.SupervisorAgent;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.AccountMCPAgent;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.PaymentMCPAgent;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.TransactionHistoryMCPAgent;
-import com.microsoft.openai.samples.assistant.proxy.BlobStorageProxy;
-import dev.langchain4j.data.message.ChatMessage;
-import dev.langchain4j.data.message.UserMessage;
-import dev.langchain4j.model.azure.AzureOpenAiChatModel;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class SupervisorAgentLongConversationIntegrationTest {
-
- public static void main(String[] args) throws Exception {
-
- //Azure Open AI Chat Model
- var azureOpenAiChatModel = AzureOpenAiChatModel.builder()
- .apiKey(System.getenv("AZURE_OPENAI_KEY"))
- .endpoint(System.getenv("AZURE_OPENAI_ENDPOINT"))
- .deploymentName(System.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"))
- .temperature(0.3)
- .logRequestsAndResponses(true)
- .build();
-
- var documentIntelligenceInvoiceScanHelper = new DocumentIntelligenceInvoiceScanHelper(getDocumentIntelligenceClient(),getBlobStorageProxyClient());
-
- var accountAgent = new AccountMCPAgent(azureOpenAiChatModel,
- "bob.user@contoso.com",
- "http://localhost:8070/sse");
- var transactionHistoryAgent = new TransactionHistoryMCPAgent(azureOpenAiChatModel,
- "bob.user@contoso.com",
- "http://localhost:8090/sse",
- "http://localhost:8070/sse");
- var paymentAgent = new PaymentMCPAgent(azureOpenAiChatModel,
- documentIntelligenceInvoiceScanHelper,
- "bob.user@contoso.com",
- "http://localhost:8090/sse",
- "http://localhost:8070/sse",
- "http://localhost:8060/sse");
-
- var supervisorAgent = new SupervisorAgent(azureOpenAiChatModel, List.of(accountAgent,transactionHistoryAgent,paymentAgent));
- var chatHistory = new ArrayList();
-
-
- chatHistory.add(UserMessage.from("How much money do I have in my account?"));
- System.out.println(chatHistory.get(chatHistory.size()-1));
- supervisorAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
- chatHistory.add(UserMessage.from("what about my visa"));
- System.out.println(chatHistory.get(chatHistory.size()-1));
- supervisorAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
- chatHistory.add(UserMessage.from("When was last time I've paid contoso?"));
- System.out.println(chatHistory.get(chatHistory.size()-1));
- supervisorAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
- chatHistory.add(UserMessage.from("Please pay this bill gori.png"));
-
- //this flow should activate the scanInvoice tool
- System.out.println(chatHistory.get(chatHistory.size()-1));
- supervisorAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
- chatHistory.add(UserMessage.from("yep, they are correct"));
- System.out.println(chatHistory.get(chatHistory.size()-1));
- supervisorAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
-
- chatHistory.add(UserMessage.from("use my visa"));
- System.out.println(chatHistory.get(chatHistory.size()-1));
- supervisorAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
-
- chatHistory.add(UserMessage.from("yes please proceed with payment"));
- System.out.println(chatHistory.get(chatHistory.size()-1));
- supervisorAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
- }
- private static BlobStorageProxy getBlobStorageProxyClient() {
-
- String containerName = "content";
- return new BlobStorageProxy(System.getenv("AZURE_STORAGE_ACCOUNT"),containerName,new AzureCliCredentialBuilder().build());
- }
-
- private static DocumentIntelligenceClient getDocumentIntelligenceClient() {
- String endpoint = "https://%s.cognitiveservices.azure.com".formatted(System.getenv("AZURE_DOCUMENT_INTELLIGENCE_SERVICE"));
-
- return new DocumentIntelligenceClientBuilder()
- .credential(new AzureCliCredentialBuilder().build())
- .endpoint(endpoint)
- .buildClient();
- }
-}
diff --git a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/SupervisorAgentNoRoutingIntegrationTest.java b/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/SupervisorAgentNoRoutingIntegrationTest.java
deleted file mode 100644
index db0cebc..0000000
--- a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/SupervisorAgentNoRoutingIntegrationTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package dev.langchain4j.openapi.mcp;
-
-import com.azure.ai.documentintelligence.DocumentIntelligenceClient;
-import com.azure.ai.documentintelligence.DocumentIntelligenceClientBuilder;
-import com.azure.identity.AzureCliCredentialBuilder;
-import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.SupervisorAgent;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.AccountMCPAgent;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.PaymentMCPAgent;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.TransactionHistoryMCPAgent;
-import com.microsoft.openai.samples.assistant.proxy.BlobStorageProxy;
-import dev.langchain4j.data.message.ChatMessage;
-import dev.langchain4j.data.message.UserMessage;
-import dev.langchain4j.model.azure.AzureOpenAiChatModel;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class SupervisorAgentNoRoutingIntegrationTest {
-
- public static void main(String[] args) throws Exception {
-
- //Azure Open AI Chat Model
- var azureOpenAiChatModel = AzureOpenAiChatModel.builder()
- .apiKey(System.getenv("AZURE_OPENAI_KEY"))
- .endpoint(System.getenv("AZURE_OPENAI_ENDPOINT"))
- .deploymentName(System.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"))
- .temperature(0.3)
- .logRequestsAndResponses(true)
- .build();
-
- var documentIntelligenceInvoiceScanHelper = new DocumentIntelligenceInvoiceScanHelper(getDocumentIntelligenceClient(),getBlobStorageProxyClient());
-
- var accountAgent = new AccountMCPAgent(azureOpenAiChatModel,"bob.user@contoso.com/sse","http://localhost:8070/sse");
- var transactionHistoryAgent = new TransactionHistoryMCPAgent(azureOpenAiChatModel,
- "bob.user@contoso.com",
- "http://localhost:8090/sse",
- "http://localhost:8070/sse");
- var paymentAgent = new PaymentMCPAgent(azureOpenAiChatModel,
- documentIntelligenceInvoiceScanHelper,
- "bob.user@contoso.com",
- "http://localhost:8090/sse",
- "http://localhost:8070/sse",
- "http://localhost:8060/sse");
-
- var supervisorAgent = new SupervisorAgent(azureOpenAiChatModel, List.of(accountAgent,transactionHistoryAgent,paymentAgent), false);
- var chatHistory = new ArrayList();
-
- chatHistory.add(UserMessage.from("How much money do I have in my account?"));
- supervisorAgent.invoke(chatHistory);
-
- chatHistory.add(UserMessage.from("you have 1000 on your account"));
-
- chatHistory.add(UserMessage.from("what about my visa"));
- supervisorAgent.invoke(chatHistory);
- chatHistory.add(UserMessage.from("these are the data for your visa card: id 1717171, expiration date 12/2023, cvv 123 balance 500"));
-
- chatHistory.add(UserMessage.from("When was last time I've paid contoso?"));
- supervisorAgent.invoke(chatHistory);
-
- chatHistory.add(UserMessage.from("Can you help me plan an investement?"));
- supervisorAgent.invoke(chatHistory);
-
- chatHistory.add(UserMessage.from("Ok so can you pay this bill for me?"));
- supervisorAgent.invoke(chatHistory);
-
- }
-
- private static BlobStorageProxy getBlobStorageProxyClient() {
-
- String containerName = "content";
- return new BlobStorageProxy(System.getenv("AZURE_STORAGE_ACCOUNT"),containerName,new AzureCliCredentialBuilder().build());
- }
-
- private static DocumentIntelligenceClient getDocumentIntelligenceClient() {
- String endpoint = "https://%s.cognitiveservices.azure.com".formatted(System.getenv("AZURE_DOCUMENT_INTELLIGENCE_SERVICE"));
-
- return new DocumentIntelligenceClientBuilder()
- .credential(new AzureCliCredentialBuilder().build())
- .endpoint(endpoint)
- .buildClient();
- }
-}
diff --git a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/SupervisorAgentRoutingIntegrationTest.java b/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/SupervisorAgentRoutingIntegrationTest.java
deleted file mode 100644
index 3bef60d..0000000
--- a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/SupervisorAgentRoutingIntegrationTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package dev.langchain4j.openapi.mcp;
-
-import com.azure.ai.documentintelligence.DocumentIntelligenceClient;
-import com.azure.ai.documentintelligence.DocumentIntelligenceClientBuilder;
-import com.azure.identity.AzureCliCredentialBuilder;
-import com.microsoft.openai.samples.assistant.invoice.DocumentIntelligenceInvoiceScanHelper;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.SupervisorAgent;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.AccountMCPAgent;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.PaymentMCPAgent;
-import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.TransactionHistoryMCPAgent;
-import com.microsoft.openai.samples.assistant.proxy.BlobStorageProxy;
-import dev.langchain4j.data.message.ChatMessage;
-import dev.langchain4j.data.message.UserMessage;
-import dev.langchain4j.model.azure.AzureOpenAiChatModel;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class SupervisorAgentRoutingIntegrationTest {
-
- public static void main(String[] args) throws Exception {
-
- //Azure Open AI Chat Model
- var azureOpenAiChatModel = AzureOpenAiChatModel.builder()
- .apiKey(System.getenv("AZURE_OPENAI_KEY"))
- .endpoint(System.getenv("AZURE_OPENAI_ENDPOINT"))
- .deploymentName(System.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"))
- .temperature(0.3)
- .logRequestsAndResponses(true)
- .build();
-
- var documentIntelligenceInvoiceScanHelper = new DocumentIntelligenceInvoiceScanHelper(getDocumentIntelligenceClient(),getBlobStorageProxyClient());
-
- var accountAgent = new AccountMCPAgent(azureOpenAiChatModel,"bob.user@contoso.com",
- "http://localhost:8070/sse");
- var transactionHistoryAgent = new TransactionHistoryMCPAgent(azureOpenAiChatModel,
- "bob.user@contoso.com",
- "http://localhost:8090/sse",
- "http://localhost:8070/sse");
- var paymentAgent = new PaymentMCPAgent(azureOpenAiChatModel,
- documentIntelligenceInvoiceScanHelper,
- "bob.user@contoso.com",
- "http://localhost:8090/sse",
- "http://localhost:8070/sse",
- "http://localhost:8060/sse");
-
- var supervisorAgent = new SupervisorAgent(azureOpenAiChatModel, List.of(accountAgent,transactionHistoryAgent,paymentAgent));
- var chatHistory = new ArrayList();
-
-
- chatHistory.add(UserMessage.from("How much money do I have in my account?"));
- supervisorAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
- chatHistory.add(UserMessage.from("what about my visa"));
- supervisorAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
- chatHistory.add(UserMessage.from("When was las time I've paid contoso?"));
- supervisorAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
- }
-
- private static BlobStorageProxy getBlobStorageProxyClient() {
-
- String containerName = "content";
- return new BlobStorageProxy(System.getenv("AZURE_STORAGE_ACCOUNT"),containerName,new AzureCliCredentialBuilder().build());
- }
-
- private static DocumentIntelligenceClient getDocumentIntelligenceClient() {
- String endpoint = "https://%s.cognitiveservices.azure.com".formatted(System.getenv("AZURE_DOCUMENT_INTELLIGENCE_SERVICE"));
-
- return new DocumentIntelligenceClientBuilder()
- .credential(new AzureCliCredentialBuilder().build())
- .endpoint(endpoint)
- .buildClient();
- }
-}
diff --git a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/TransactionHistoryMCPAgentIntegrationTest.java b/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/TransactionHistoryMCPAgentIntegrationTest.java
deleted file mode 100644
index aa41b94..0000000
--- a/app/copilot/langchain4j-agents/src/test/java/dev/langchain4j/openapi/mcp/TransactionHistoryMCPAgentIntegrationTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package dev.langchain4j.openapi.mcp;
-
-import com.microsoft.openai.samples.assistant.langchain4j.agent.mcp.TransactionHistoryMCPAgent;
-import dev.langchain4j.data.message.ChatMessage;
-import dev.langchain4j.data.message.UserMessage;
-import dev.langchain4j.model.azure.AzureOpenAiChatModel;
-
-import java.util.ArrayList;
-
-public class TransactionHistoryMCPAgentIntegrationTest {
-
- public static void main(String[] args) throws Exception {
-
- //Azure Open AI Chat Model
- var azureOpenAiChatModel = AzureOpenAiChatModel.builder()
- .apiKey(System.getenv("AZURE_OPENAI_KEY"))
- .endpoint(System.getenv("AZURE_OPENAI_ENDPOINT"))
- .deploymentName(System.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"))
- .temperature(0.3)
- .logRequestsAndResponses(true)
- .build();
-
- var transactionHistoryAgent = new TransactionHistoryMCPAgent(azureOpenAiChatModel,
- "bob.user@contoso.com",
- "http://localhost:8090/sse",
- "http://localhost:8070/sse");
-
- var chatHistory = new ArrayList();
-
-
- chatHistory.add(UserMessage.from("When was last time I've paid contoso?"));
- transactionHistoryAgent.invoke(chatHistory);
- System.out.println(chatHistory.get(chatHistory.size()-1));
-
-
- }
-}
diff --git a/app/copilot/langchain4j-agents/src/test/resources/account.yaml b/app/copilot/langchain4j-agents/src/test/resources/account.yaml
deleted file mode 100644
index 0708a9c..0000000
--- a/app/copilot/langchain4j-agents/src/test/resources/account.yaml
+++ /dev/null
@@ -1,178 +0,0 @@
-openapi: 3.0.3
-info:
- title: Account API
- version: 1.0.0
-paths:
- /users/{user_name}/accounts:
- get:
- summary: Get the list of all accounts for a specific user
- description: Get the list of all accounts for a specific user
- operationId: getAccountsByUserName
- parameters:
- - name: user_name
- description: userName once the user has logged.
- in: path
- required: true
- schema:
- type: string
- responses:
- '200':
- description: A list of accounts
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/Account'
- /accounts/{accountid}:
- get:
- summary: Get account details and available payment methods
- description: Get account details and available payment methods
- operationId: getAccountDetails
- parameters:
- - name: accountid
- description: id of specific account.
- in: path
- required: true
- schema:
- type: integer
- example: 123456
- responses:
- '200':
- description: Account details
- content:
- application/json:
- schema:
- type: object
- items:
- $ref: '#/components/schemas/Account'
- /accounts/{accountid}/paymentmethods/{methodid}:
- get:
- summary: Get payment method detail with available balance
- description: Get payment method detail with available balance
- operationId: getPaymentMethodDetails
- parameters:
- - name: accountid
- description: id of specific account.
- in: path
- required: true
- schema:
- type: integer
- example: 123456
- - name: methodid
- description: id of specific payment method available for the account id.
- in: path
- required: true
- schema:
- type: integer
- example: 74839113
- responses:
- '200':
- description: Payment method details
- content:
- application/json:
- schema:
- type: object
- items:
- $ref: '#/components/schemas/PaymentMethod'
- /accounts/{accountid}/registeredBeneficiaries:
- get:
- summary: Get list of registered beneficiaries for a specific account
- description: Get list of registered beneficiaries for a specific account
- operationId: getBeneficiaryMethodDetails
- parameters:
- - name: accountid
- description: id of specific account.
- in: path
- required: true
- schema:
- type: integer
- example: 123456
- responses:
- '200':
- description: Payment method details
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/Beneficiary'
-components:
- schemas:
- Account:
- type: object
- properties:
- id:
- type: string
- description: The unique identifier for the account
- userName:
- type: string
- description: The unique identifier for the user
- accountHolderFullName:
- type: string
- description: The full name of the account holder
- currency:
- type: string
- description: The currency of the account
- activationDate:
- type: string
- description: The date when the account was activated
- balance:
- type: string
- description: The current balance of the account
- paymentMethods:
- type: array
- items:
- $ref: '#/components/schemas/PaymentMethod'
- description: The list of payment methods associated with the account
- PaymentMethodSummary:
- type: object
- properties:
- id:
- type: string
- description: The unique identifier for the payment method
- type:
- type: string
- description: The type of the payment method
- activationDate:
- type: string
- description: The date when the payment method was activated
- expirationDate:
- type: string
- description: The date when the payment method will expire
- PaymentMethod:
- type: object
- properties:
- id:
- type: string
- description: The unique identifier for the payment method
- type:
- type: string
- description: The type of the payment method
- activationDate:
- type: string
- description: The date when the payment method was activated
- expirationDate:
- type: string
- description: The date when the payment method will expire
- availableBalance:
- type: string
- description: The available balance of the payment method
- cardNumber:
- type: string
- description: The card number of the payment method
- Beneficiary:
- type: object
- properties:
- id:
- type: string
- description: The unique identifier for the beneficiary
- fullName:
- type: string
- description: The full name of the beneficiary
- bankCode:
- type: string
- description: The bank code of the beneficiary's bank
- bankName:
- type: string
- description: The name of the beneficiary's bank
\ No newline at end of file
diff --git a/app/copilot/langchain4j-agents/src/test/resources/logback.xml b/app/copilot/langchain4j-agents/src/test/resources/logback.xml
deleted file mode 100644
index 4447925..0000000
--- a/app/copilot/langchain4j-agents/src/test/resources/logback.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
- %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/copilot/langchain4j-agents/src/test/resources/payments.yaml b/app/copilot/langchain4j-agents/src/test/resources/payments.yaml
deleted file mode 100644
index 49ab21b..0000000
--- a/app/copilot/langchain4j-agents/src/test/resources/payments.yaml
+++ /dev/null
@@ -1,60 +0,0 @@
-openapi: 3.0.3
-info:
- title: Payment API
- version: 1.0.0
-paths:
- /payments:
- post:
- operationId: submitPayment
- summary: Submit a payment request
- description: Submit a payment request
- requestBody:
- required: true
- description: Payment to submit
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Payment'
- responses:
- '200':
- description: Payment request submitted successfully
- '400':
- description: Invalid request body
- '500':
- description: Internal server error
-components:
- schemas:
- Payment:
- type: object
- properties:
- description:
- type: string
- description: Description of the payment
- recipientName:
- type: string
- description: Name of the recipient
- recipientBankCode:
- type: string
- description: Bank code of the recipient
- accountId:
- type: string
- description: ID of the account
- paymentMethodId:
- type: string
- description: ID of the payment method
- paymentType:
- type: string
- description: 'The type of payment: creditcard, banktransfer, directdebit, visa, mastercard, paypal, etc.'
- amount:
- type: string
- description: Amount of the payment
- timestamp:
- type: string
- description: Timestamp of the payment
- requestBodies:
- Payment:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Payment'
- description: Payment object to submit
\ No newline at end of file
diff --git a/app/copilot/langchain4j-agents/src/test/resources/transaction-history.yaml b/app/copilot/langchain4j-agents/src/test/resources/transaction-history.yaml
deleted file mode 100644
index 200771f..0000000
--- a/app/copilot/langchain4j-agents/src/test/resources/transaction-history.yaml
+++ /dev/null
@@ -1,94 +0,0 @@
-openapi: 3.0.3
-info:
- title: Transaction History and Reporting API
- version: 1.0.0
-paths:
- /transactions/{accountid}:
- get:
- summary: Get transactions list.
- description: Gets the transactions lists. They can be filtered based on recipient name
- operationId: getTransactionsByRecipientName
- parameters:
- - name: accountid
- description: id of specific account.
- in: path
- required: true
- schema:
- type: integer
- example: 123456
- - name: recipient_name
- description: Name of the payee, recipient
- in: query
- required: false
- schema:
- type: string
- example: contoso
-
- responses:
- '200':
- description: A list of transactions for a specific recipient
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/Transaction'
- post:
- operationId: notifyTransaction
- summary: Notify the banking transaction so that it's being stored in the history
- description: Notify the banking transaction so that it's being stored in the history
- parameters:
- - name: accountid
- description: id of specific account.
- in: path
- required: true
- schema:
- type: integer
- example: 123456
- requestBody:
- required: true
- description: transaction to notify
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Transaction'
- responses:
- '200':
- description: Payment request submitted successfully
- '400':
- description: Invalid request body
- '500':
- description: Internal server error
-components:
- schemas:
- Transaction:
- type: object
- properties:
- id:
- type: string
- description: 'The unique identifier for the transaction'
- description:
- type: string
- description: 'The description of the transaction which contains reason for the payment and other details'
- type:
- type: string
- description: 'The transaction type expressed as income or outcome transaction'
- recipientName:
- type: string
- description: 'The name of the recipient'
- recipientBankReference:
- type: string
- description: 'The bank reference of the recipient'
- accountId:
- type: string
- description: 'The account ID associated with the transaction'
- paymentType:
- type: string
- description: 'The type of payment: creditcard, banktransfer, directdebit, visa, mastercard, paypal, etc.'
- amount:
- type: string
- description: 'The amount of the transaction'
- timestamp:
- type: string
- format: date-time
- description: 'The timestamp of the transaction'
\ No newline at end of file
diff --git a/app/copilot/pom.xml b/app/copilot/pom.xml
index 0279d82..45d48c3 100644
--- a/app/copilot/pom.xml
+++ b/app/copilot/pom.xml
@@ -13,7 +13,7 @@
17
17
UTF-8
- 1.0.0-beta2
+ 1.1.0
1.2.33
@@ -44,7 +44,6 @@
copilot-backend
- langchain4j-agents
copilot-common
diff --git a/app/frontend/src/pages/chat/Chat.tsx b/app/frontend/src/pages/chat/Chat.tsx
index e4abe90..44b89c1 100644
--- a/app/frontend/src/pages/chat/Chat.tsx
+++ b/app/frontend/src/pages/chat/Chat.tsx
@@ -1,481 +1,488 @@
-import { useRef, useState, useEffect } from "react";
-import { Checkbox, ChoiceGroup, Panel, DefaultButton, TextField, SpinButton, Dropdown, IDropdownOption, IChoiceGroupOption } from "@fluentui/react";
-import { SparkleFilled } from "@fluentui/react-icons";
-import readNDJSONStream from "ndjson-readablestream";
-
-
-import styles from "./Chat.module.css";
-
-import {
- chatApi,
- RetrievalMode,
- ChatAppResponse,
- ChatAppResponseOrError,
- ChatAppRequest,
- ResponseMessage,
- Approaches,
- SKMode
-} from "../../api";
-import { Answer, AnswerError, AnswerLoading } from "../../components/Answer";
-import { QuestionInput } from "../../components/QuestionInput";
-import { QuestionContextType } from "../../components/QuestionInput/QuestionContext";
-import { ExampleList } from "../../components/Example";
-import { UserChatMessage } from "../../components/UserChatMessage";
-import { AnalysisPanel, AnalysisPanelTabs } from "../../components/AnalysisPanel";
-import { SettingsButton } from "../../components/SettingsButton";
-import { ClearChatButton } from "../../components/ClearChatButton";
-import { useLogin, getToken } from "../../authConfig";
-import { useMsal } from "@azure/msal-react";
-import { TokenClaimsDisplay } from "../../components/TokenClaimsDisplay";
-import { AttachmentType } from "../../components/AttachmentType";
-
-
-const Chat = () => {
- const [isConfigPanelOpen, setIsConfigPanelOpen] = useState(false);
- const [approach, setApproach] = useState(Approaches.JAVA_OPENAI_SDK);
- const [skMode, setSKMode] = useState(SKMode.Chains);
- const [promptTemplate, setPromptTemplate] = useState("");
- const [retrieveCount, setRetrieveCount] = useState(3);
- const [retrievalMode, setRetrievalMode] = useState(RetrievalMode.Hybrid);
- const [useSemanticRanker, setUseSemanticRanker] = useState(true);
- const [shouldStream, setShouldStream] = useState(false);
- const [streamAvailable, setStreamAvailable] = useState(true);
- const [useSemanticCaptions, setUseSemanticCaptions] = useState(false);
- const [excludeCategory, setExcludeCategory] = useState("");
- const [useSuggestFollowupQuestions, setUseSuggestFollowupQuestions] = useState(false);
- const [useOidSecurityFilter, setUseOidSecurityFilter] = useState(false);
- const [useGroupsSecurityFilter, setUseGroupsSecurityFilter] = useState(false);
-
- const lastQuestionRef = useRef("");
- const lastAttachementsRef = useRef([]);
- const chatMessageStreamEnd = useRef(null);
-
- const [isLoading, setIsLoading] = useState(false);
- const [isStreaming, setIsStreaming] = useState(false);
- const [error, setError] = useState();
-
- const [activeCitation, setActiveCitation] = useState();
- const [activeAnalysisPanelTab, setActiveAnalysisPanelTab] = useState(undefined);
-
- const [selectedAnswer, setSelectedAnswer] = useState(0);
- const [answers, setAnswers] = useState<[user: string, attachments: string[], response: ChatAppResponse][]>([]);
- const [streamedAnswers, setStreamedAnswers] = useState<[user: string, attachments: string[], response: ChatAppResponse][]>([]);
-
- const handleAsyncRequest = async (question: string, attachments: string[], answers: [string, string[],ChatAppResponse][], setAnswers: Function, responseBody: ReadableStream) => {
- let answer: string = "";
- let askResponse: ChatAppResponse = {} as ChatAppResponse;
-
- const updateState = (newContent: string) => {
- return new Promise(resolve => {
- setTimeout(() => {
- answer += newContent;
- const latestResponse: ChatAppResponse = {
- ...askResponse,
- choices: [{ ...askResponse.choices[0], message: { content: answer, role: askResponse.choices[0].message.role } }]
- };
- setStreamedAnswers([...answers, [question,attachments, latestResponse]]);
- resolve(null);
- }, 33);
- });
- };
- try {
- setIsStreaming(true);
- for await (const event of readNDJSONStream(responseBody)) {
- if (event["choices"] && event["choices"][0]["context"] && event["choices"][0]["context"]["data_points"]) {
- event["choices"][0]["message"] = event["choices"][0]["delta"];
- askResponse = event;
- answer = askResponse["choices"][0]["message"]["content"];
- } else if (event["choices"] && event["choices"][0]["delta"]["content"]) {
- setIsLoading(false);
- await updateState(event["choices"][0]["delta"]["content"]);
- }
- }
- } finally {
- setIsStreaming(false);
- }
- const fullResponse: ChatAppResponse = {
- ...askResponse,
- choices: [{ ...askResponse.choices[0], message: { content: answer, role: askResponse.choices[0].message.role } }]
- };
- return fullResponse;
- };
-
- const client = useLogin ? useMsal().instance : undefined;
-
- const makeApiRequest = async (questionContext: QuestionContextType) => {
- lastQuestionRef.current = questionContext.question;
- lastAttachementsRef.current = questionContext.attachments || [];
-
- error && setError(undefined);
- setIsLoading(true);
- setActiveCitation(undefined);
- setActiveAnalysisPanelTab(undefined);
-
- const token = client ? await getToken(client) : undefined;
-
- try {
- const messages: ResponseMessage[] = answers.flatMap(a => [
- { content: a[0], role: "user", attachments: a[1]},
- { content: a[2].choices[0].message.content, role: "assistant" }
- ]);
-
- const stream = streamAvailable && shouldStream;
- const request: ChatAppRequest = {
- messages: [...messages, { content: questionContext.question, role: "user", attachments: questionContext.attachments }],
- stream: stream,
- context: {
- overrides: {
- prompt_template: promptTemplate.length === 0 ? undefined : promptTemplate,
- exclude_category: excludeCategory.length === 0 ? undefined : excludeCategory,
- top: retrieveCount,
- retrieval_mode: retrievalMode,
- semantic_ranker: useSemanticRanker,
- semantic_captions: useSemanticCaptions,
- suggest_followup_questions: useSuggestFollowupQuestions,
- use_oid_security_filter: useOidSecurityFilter,
- use_groups_security_filter: useGroupsSecurityFilter,
- semantic_kernel_mode: skMode
- }
- },
- approach: approach,
- // ChatAppProtocol: Client must pass on any session state received from the server
- session_state: answers.length ? answers[answers.length - 1][2].choices[0].session_state : null
- };
-
- const response = await chatApi(request, token?.accessToken);
- if (!response.body) {
- throw Error("No response body");
- }
- if (stream) {
- const parsedResponse: ChatAppResponse = await handleAsyncRequest(questionContext.question,questionContext.attachments || [], answers, setAnswers, response.body);
- setAnswers([...answers, [questionContext.question,questionContext.attachments || [], parsedResponse]]);
- } else {
- const parsedResponse: ChatAppResponseOrError = await response.json();
- if (response.status > 299 || !response.ok) {
- throw Error(parsedResponse.error || "Unknown error");
- }
- setAnswers([...answers, [questionContext.question,questionContext.attachments || [], parsedResponse as ChatAppResponse]]);
- }
- } catch (e) {
- setError(e);
- } finally {
- setIsLoading(false);
- }
- };
-
- const clearChat = () => {
- lastQuestionRef.current = "";
- lastAttachementsRef.current = [];
- error && setError(undefined);
- setActiveCitation(undefined);
- setActiveAnalysisPanelTab(undefined);
- setAnswers([]);
- setStreamedAnswers([]);
- setIsLoading(false);
- setIsStreaming(false);
- };
-
- useEffect(() => chatMessageStreamEnd.current?.scrollIntoView({ behavior: "smooth" }), [isLoading]);
- useEffect(() => chatMessageStreamEnd.current?.scrollIntoView({ behavior: "auto" }), [streamedAnswers]);
-
- const onPromptTemplateChange = (_ev?: React.FormEvent, newValue?: string) => {
- setPromptTemplate(newValue || "");
- };
-
- const onRetrieveCountChange = (_ev?: React.SyntheticEvent, newValue?: string) => {
- setRetrieveCount(parseInt(newValue || "3"));
- };
-
- const onRetrievalModeChange = (_ev: React.FormEvent, option?: IDropdownOption | undefined, index?: number | undefined) => {
- setRetrievalMode(option?.data || RetrievalMode.Hybrid);
- };
-
- const onSKModeChange = (_ev: React.FormEvent, option?: IDropdownOption | undefined, index?: number | undefined) => {
- setSKMode(option?.data || SKMode.Chains);
- };
-
- const onApproachChange = (_ev?: React.FormEvent, option?: IChoiceGroupOption) => {
- const newApproach = (option?.key as Approaches);
- setApproach(newApproach || Approaches.JAVA_OPENAI_SDK);
- setStreamAvailable(newApproach === Approaches.JAVA_OPENAI_SDK);
- };
-
- const onUseSemanticRankerChange = (_ev?: React.FormEvent, checked?: boolean) => {
- setUseSemanticRanker(!!checked);
- };
-
- const onUseSemanticCaptionsChange = (_ev?: React.FormEvent, checked?: boolean) => {
- setUseSemanticCaptions(!!checked);
- };
-
- const onShouldStreamChange = (_ev?: React.FormEvent, checked?: boolean) => {
- setShouldStream(!!checked);
- };
-
- const onExcludeCategoryChanged = (_ev?: React.FormEvent, newValue?: string) => {
- setExcludeCategory(newValue || "");
- };
-
- const onUseSuggestFollowupQuestionsChange = (_ev?: React.FormEvent, checked?: boolean) => {
- setUseSuggestFollowupQuestions(!!checked);
- };
-
- const onUseOidSecurityFilterChange = (_ev?: React.FormEvent, checked?: boolean) => {
- setUseOidSecurityFilter(!!checked);
- };
-
- const onUseGroupsSecurityFilterChange = (_ev?: React.FormEvent, checked?: boolean) => {
- setUseGroupsSecurityFilter(!!checked);
- };
-
- const onExampleClicked = (example: string) => {
- makeApiRequest({question:example});
- };
-
- const onShowCitation = (citation: string, index: number) => {
- if (activeCitation === citation && activeAnalysisPanelTab === AnalysisPanelTabs.CitationTab && selectedAnswer === index) {
- setActiveAnalysisPanelTab(undefined);
- } else {
- setActiveCitation(citation);
- setActiveAnalysisPanelTab(AnalysisPanelTabs.CitationTab);
- }
-
- setSelectedAnswer(index);
- };
-
- const onToggleTab = (tab: AnalysisPanelTabs, index: number) => {
- if (activeAnalysisPanelTab === tab && selectedAnswer === index) {
- setActiveAnalysisPanelTab(undefined);
- } else {
- setActiveAnalysisPanelTab(tab);
- }
-
- setSelectedAnswer(index);
- };
-
- const approaches: IChoiceGroupOption[] = [
- {
- key: Approaches.JAVA_OPENAI_SDK,
- text: "Java Azure Open AI SDK"
- },
- /* Pending Semantic Kernel Memory implementation in V1.0.0
- {
- key: Approaches.JAVA_SEMANTIC_KERNEL,
- text: "Java Semantic Kernel - Memory"
- },*/
- {
- key: Approaches.JAVA_SEMANTIC_KERNEL_PLANNER,
- text: "Java Semantic Kernel - Orchestration"
- }
- ];
-
- return (
-
-
-
-
-
-
-
- {!lastQuestionRef.current ? (
-
-
-
Chat with your personal assistant
- Ask anything about your banking account details and payments or try an example
-
-
- ) : (
-
- {isStreaming &&
- streamedAnswers.map((streamedAnswer, index) => (
-
-
-
-
onShowCitation(c, index)}
- onThoughtProcessClicked={() => onToggleTab(AnalysisPanelTabs.ThoughtProcessTab, index)}
- onSupportingContentClicked={() => onToggleTab(AnalysisPanelTabs.SupportingContentTab, index)}
- onFollowupQuestionClicked={q => makeApiRequest({question:q})}
- showFollowupQuestions={useSuggestFollowupQuestions && answers.length - 1 === index}
- />
-
-
- ))}
- {!isStreaming &&
- answers.map((answer, index) => (
-
-
-
-
onShowCitation(c, index)}
- onThoughtProcessClicked={() => onToggleTab(AnalysisPanelTabs.ThoughtProcessTab, index)}
- onSupportingContentClicked={() => onToggleTab(AnalysisPanelTabs.SupportingContentTab, index)}
- onFollowupQuestionClicked={q => makeApiRequest({question:q})}
- showFollowupQuestions={useSuggestFollowupQuestions && answers.length - 1 === index}
- />
-
-
- ))}
- {isLoading && (
- <>
-
-
- >
- )}
- {error ? (
- <>
-
-
-
makeApiRequest({question:lastQuestionRef.current})} />
-
- >
- ) : null}
-
-
- )}
-
-
- makeApiRequest(question)}
- />
-
-
-
- {answers.length > 0 && activeAnalysisPanelTab && (
-
onToggleTab(x, selectedAnswer)}
- citationHeight="810px"
- answer={answers[selectedAnswer][2]}
- activeTab={activeAnalysisPanelTab}
- />
- )}
-
- setIsConfigPanelOpen(false)}
- closeButtonAriaLabel="Close"
- onRenderFooterContent={() => setIsConfigPanelOpen(false)}>Close}
- isFooterAtBottom={true}
- >
-
-
- {(approach === Approaches.JAVA_OPENAI_SDK || approach === Approaches.JAVA_SEMANTIC_KERNEL) && (
-
- )}
- {(approach === Approaches.JAVA_SEMANTIC_KERNEL_PLANNER) && (
-
- )}
-
-
-
-
-
-
- {useLogin && (
-
- )}
- {useLogin && (
-
- )}
-
- {streamAvailable &&
-
- }
-
- {useLogin && }
-
-
-
- );
-};
-
-export default Chat;
+import { useRef, useState, useEffect } from "react";
+import { Checkbox, ChoiceGroup, Panel, DefaultButton, TextField, SpinButton, Dropdown, IDropdownOption, IChoiceGroupOption } from "@fluentui/react";
+import { SparkleFilled } from "@fluentui/react-icons";
+import readNDJSONStream from "ndjson-readablestream";
+
+
+import styles from "./Chat.module.css";
+
+import {
+ chatApi,
+ RetrievalMode,
+ ChatAppResponse,
+ ChatAppResponseOrError,
+ ChatAppRequest,
+ ResponseMessage,
+ Approaches,
+ SKMode
+} from "../../api";
+import { Answer, AnswerError, AnswerLoading } from "../../components/Answer";
+import { QuestionInput } from "../../components/QuestionInput";
+import { QuestionContextType } from "../../components/QuestionInput/QuestionContext";
+import { ExampleList } from "../../components/Example";
+import { UserChatMessage } from "../../components/UserChatMessage";
+import { AnalysisPanel, AnalysisPanelTabs } from "../../components/AnalysisPanel";
+import { SettingsButton } from "../../components/SettingsButton";
+import { ClearChatButton } from "../../components/ClearChatButton";
+import { useLogin, getToken } from "../../authConfig";
+import { useMsal } from "@azure/msal-react";
+import { TokenClaimsDisplay } from "../../components/TokenClaimsDisplay";
+import { AttachmentType } from "../../components/AttachmentType";
+
+
+const Chat = () => {
+ const [isConfigPanelOpen, setIsConfigPanelOpen] = useState(false);
+ const [approach, setApproach] = useState(Approaches.JAVA_OPENAI_SDK);
+ const [skMode, setSKMode] = useState(SKMode.Chains);
+ const [promptTemplate, setPromptTemplate] = useState("");
+ const [retrieveCount, setRetrieveCount] = useState(3);
+ const [retrievalMode, setRetrievalMode] = useState(RetrievalMode.Hybrid);
+ const [useSemanticRanker, setUseSemanticRanker] = useState(true);
+ const [shouldStream, setShouldStream] = useState(false);
+ const [streamAvailable, setStreamAvailable] = useState(true);
+ const [useSemanticCaptions, setUseSemanticCaptions] = useState(false);
+ const [excludeCategory, setExcludeCategory] = useState("");
+ const [useSuggestFollowupQuestions, setUseSuggestFollowupQuestions] = useState(false);
+ const [useOidSecurityFilter, setUseOidSecurityFilter] = useState(false);
+ const [useGroupsSecurityFilter, setUseGroupsSecurityFilter] = useState(false);
+ const [threadId, setThreadId] = useState(undefined); // Updated type to match ChatAppRequest
+
+ const lastQuestionRef = useRef("");
+ const lastAttachementsRef = useRef([]);
+ const chatMessageStreamEnd = useRef(null);
+
+ const [isLoading, setIsLoading] = useState(false);
+ const [isStreaming, setIsStreaming] = useState(false);
+ const [error, setError] = useState();
+
+ const [activeCitation, setActiveCitation] = useState();
+ const [activeAnalysisPanelTab, setActiveAnalysisPanelTab] = useState(undefined);
+
+ const [selectedAnswer, setSelectedAnswer] = useState(0);
+ const [answers, setAnswers] = useState<[user: string, attachments: string[], response: ChatAppResponse][]>([]);
+ const [streamedAnswers, setStreamedAnswers] = useState<[user: string, attachments: string[], response: ChatAppResponse][]>([]);
+
+ const handleAsyncRequest = async (question: string, attachments: string[], answers: [string, string[],ChatAppResponse][], setAnswers: Function, responseBody: ReadableStream) => {
+ let answer: string = "";
+ let askResponse: ChatAppResponse = {} as ChatAppResponse;
+
+ const updateState = (newContent: string) => {
+ return new Promise(resolve => {
+ setTimeout(() => {
+ answer += newContent;
+ const latestResponse: ChatAppResponse = {
+ ...askResponse,
+ choices: [{ ...askResponse.choices[0], message: { content: answer, role: askResponse.choices[0].message.role } }]
+ };
+ setStreamedAnswers([...answers, [question,attachments, latestResponse]]);
+ resolve(null);
+ }, 33);
+ });
+ };
+ try {
+ setIsStreaming(true);
+ for await (const event of readNDJSONStream(responseBody)) {
+ if (event["choices"] && event["choices"][0]["context"] && event["choices"][0]["context"]["data_points"]) {
+ event["choices"][0]["message"] = event["choices"][0]["delta"];
+ askResponse = event;
+ answer = askResponse["choices"][0]["message"]["content"];
+ } else if (event["choices"] && event["choices"][0]["delta"]["content"]) {
+ setIsLoading(false);
+ await updateState(event["choices"][0]["delta"]["content"]);
+ }
+ }
+ } finally {
+ setIsStreaming(false);
+ }
+ const fullResponse: ChatAppResponse = {
+ ...askResponse,
+ choices: [{ ...askResponse.choices[0], message: { content: answer, role: askResponse.choices[0].message.role } }]
+ };
+ return fullResponse;
+ };
+
+ const client = useLogin ? useMsal().instance : undefined;
+
+ const makeApiRequest = async (questionContext: QuestionContextType) => {
+ lastQuestionRef.current = questionContext.question;
+ lastAttachementsRef.current = questionContext.attachments || [];
+
+ error && setError(undefined);
+ setIsLoading(true);
+ setActiveCitation(undefined);
+ setActiveAnalysisPanelTab(undefined);
+
+ const token = client ? await getToken(client) : undefined;
+
+
+ try {
+ /**
+ const messages: ResponseMessage[] = answers.flatMap(a => [
+ { content: a[0], role: "user", attachments: a[1]},
+ { content: a[2].choices[0].message.content, role: "assistant" }
+ ]);
+
+ */
+ const stream = streamAvailable && shouldStream;
+ const request: ChatAppRequest = {
+ messages: [{ content: questionContext.question, role: "user", attachments: questionContext.attachments }],
+ stream: stream,
+ context: {
+ overrides: {
+ prompt_template: promptTemplate.length === 0 ? undefined : promptTemplate,
+ exclude_category: excludeCategory.length === 0 ? undefined : excludeCategory,
+ top: retrieveCount,
+ retrieval_mode: retrievalMode,
+ semantic_ranker: useSemanticRanker,
+ semantic_captions: useSemanticCaptions,
+ suggest_followup_questions: useSuggestFollowupQuestions,
+ use_oid_security_filter: useOidSecurityFilter,
+ use_groups_security_filter: useGroupsSecurityFilter,
+ semantic_kernel_mode: skMode
+ }
+ },
+ approach: approach,
+ session_state: answers.length ? answers[answers.length - 1][2].choices[0].session_state : null,
+ threadId: threadId // Include threadId in the request
+ };
+
+ const response = await chatApi(request, token?.accessToken);
+ if (!response.body) {
+ throw Error("No response body");
+ }
+ if (stream) {
+ const parsedResponse: ChatAppResponse = await handleAsyncRequest(questionContext.question, questionContext.attachments || [], answers, setAnswers, response.body);
+ setAnswers([...answers, [questionContext.question, questionContext.attachments || [], parsedResponse]]);
+ setThreadId(parsedResponse.threadId || undefined); // Update threadId from the response
+ } else {
+ const parsedResponse: ChatAppResponseOrError = await response.json();
+ if (response.status > 299 || !response.ok) {
+ throw Error(parsedResponse.error || "Unknown error");
+ }
+ setAnswers([...answers, [questionContext.question, questionContext.attachments || [], parsedResponse as ChatAppResponse]]);
+ setThreadId((parsedResponse as ChatAppResponse).threadId || undefined); // Update threadId from the response
+ }
+ } catch (e) {
+ setError(e);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const clearChat = () => {
+ lastQuestionRef.current = "";
+ lastAttachementsRef.current = [];
+ error && setError(undefined);
+ setActiveCitation(undefined);
+ setActiveAnalysisPanelTab(undefined);
+ setAnswers([]);
+ setStreamedAnswers([]);
+ setIsLoading(false);
+ setIsStreaming(false);
+ setThreadId(undefined); // Reset threadId
+ };
+
+ useEffect(() => chatMessageStreamEnd.current?.scrollIntoView({ behavior: "smooth" }), [isLoading]);
+ useEffect(() => chatMessageStreamEnd.current?.scrollIntoView({ behavior: "auto" }), [streamedAnswers]);
+
+ const onPromptTemplateChange = (_ev?: React.FormEvent, newValue?: string) => {
+ setPromptTemplate(newValue || "");
+ };
+
+ const onRetrieveCountChange = (_ev?: React.SyntheticEvent, newValue?: string) => {
+ setRetrieveCount(parseInt(newValue || "3"));
+ };
+
+ const onRetrievalModeChange = (_ev: React.FormEvent, option?: IDropdownOption | undefined, index?: number | undefined) => {
+ setRetrievalMode(option?.data || RetrievalMode.Hybrid);
+ };
+
+ const onSKModeChange = (_ev: React.FormEvent, option?: IDropdownOption | undefined, index?: number | undefined) => {
+ setSKMode(option?.data || SKMode.Chains);
+ };
+
+ const onApproachChange = (_ev?: React.FormEvent, option?: IChoiceGroupOption) => {
+ const newApproach = (option?.key as Approaches);
+ setApproach(newApproach || Approaches.JAVA_OPENAI_SDK);
+ setStreamAvailable(newApproach === Approaches.JAVA_OPENAI_SDK);
+ };
+
+ const onUseSemanticRankerChange = (_ev?: React.FormEvent, checked?: boolean) => {
+ setUseSemanticRanker(!!checked);
+ };
+
+ const onUseSemanticCaptionsChange = (_ev?: React.FormEvent, checked?: boolean) => {
+ setUseSemanticCaptions(!!checked);
+ };
+
+ const onShouldStreamChange = (_ev?: React.FormEvent, checked?: boolean) => {
+ setShouldStream(!!checked);
+ };
+
+ const onExcludeCategoryChanged = (_ev?: React.FormEvent, newValue?: string) => {
+ setExcludeCategory(newValue || "");
+ };
+
+ const onUseSuggestFollowupQuestionsChange = (_ev?: React.FormEvent, checked?: boolean) => {
+ setUseSuggestFollowupQuestions(!!checked);
+ };
+
+ const onUseOidSecurityFilterChange = (_ev?: React.FormEvent, checked?: boolean) => {
+ setUseOidSecurityFilter(!!checked);
+ };
+
+ const onUseGroupsSecurityFilterChange = (_ev?: React.FormEvent, checked?: boolean) => {
+ setUseGroupsSecurityFilter(!!checked);
+ };
+
+ const onExampleClicked = (example: string) => {
+ makeApiRequest({question:example});
+ };
+
+ const onShowCitation = (citation: string, index: number) => {
+ if (activeCitation === citation && activeAnalysisPanelTab === AnalysisPanelTabs.CitationTab && selectedAnswer === index) {
+ setActiveAnalysisPanelTab(undefined);
+ } else {
+ setActiveCitation(citation);
+ setActiveAnalysisPanelTab(AnalysisPanelTabs.CitationTab);
+ }
+
+ setSelectedAnswer(index);
+ };
+
+ const onToggleTab = (tab: AnalysisPanelTabs, index: number) => {
+ if (activeAnalysisPanelTab === tab && selectedAnswer === index) {
+ setActiveAnalysisPanelTab(undefined);
+ } else {
+ setActiveAnalysisPanelTab(tab);
+ }
+
+ setSelectedAnswer(index);
+ };
+
+ const approaches: IChoiceGroupOption[] = [
+ {
+ key: Approaches.JAVA_OPENAI_SDK,
+ text: "Java Azure Open AI SDK"
+ },
+ /* Pending Semantic Kernel Memory implementation in V1.0.0
+ {
+ key: Approaches.JAVA_SEMANTIC_KERNEL,
+ text: "Java Semantic Kernel - Memory"
+ },*/
+ {
+ key: Approaches.JAVA_SEMANTIC_KERNEL_PLANNER,
+ text: "Java Semantic Kernel - Orchestration"
+ }
+ ];
+
+ return (
+
+
+
+
+
+
+
+ {!lastQuestionRef.current ? (
+
+
+
Chat with your personal assistant
+ Ask anything about your banking account details and payments or try an example
+
+
+ ) : (
+
+ {isStreaming &&
+ streamedAnswers.map((streamedAnswer, index) => (
+
+
+
+
onShowCitation(c, index)}
+ onThoughtProcessClicked={() => onToggleTab(AnalysisPanelTabs.ThoughtProcessTab, index)}
+ onSupportingContentClicked={() => onToggleTab(AnalysisPanelTabs.SupportingContentTab, index)}
+ onFollowupQuestionClicked={q => makeApiRequest({question:q})}
+ showFollowupQuestions={useSuggestFollowupQuestions && answers.length - 1 === index}
+ />
+
+
+ ))}
+ {!isStreaming &&
+ answers.map((answer, index) => (
+
+
+
+
onShowCitation(c, index)}
+ onThoughtProcessClicked={() => onToggleTab(AnalysisPanelTabs.ThoughtProcessTab, index)}
+ onSupportingContentClicked={() => onToggleTab(AnalysisPanelTabs.SupportingContentTab, index)}
+ onFollowupQuestionClicked={q => makeApiRequest({question:q})}
+ showFollowupQuestions={useSuggestFollowupQuestions && answers.length - 1 === index}
+ />
+
+
+ ))}
+ {isLoading && (
+ <>
+
+
+ >
+ )}
+ {error ? (
+ <>
+
+
+
makeApiRequest({question:lastQuestionRef.current})} />
+
+ >
+ ) : null}
+
+
+ )}
+
+
+ makeApiRequest(question)}
+ />
+
+
+
+ {answers.length > 0 && activeAnalysisPanelTab && (
+
onToggleTab(x, selectedAnswer)}
+ citationHeight="810px"
+ answer={answers[selectedAnswer][2]}
+ activeTab={activeAnalysisPanelTab}
+ />
+ )}
+
+ setIsConfigPanelOpen(false)}
+ closeButtonAriaLabel="Close"
+ onRenderFooterContent={() => setIsConfigPanelOpen(false)}>Close}
+ isFooterAtBottom={true}
+ >
+
+
+ {(approach === Approaches.JAVA_OPENAI_SDK || approach === Approaches.JAVA_SEMANTIC_KERNEL) && (
+
+ )}
+ {(approach === Approaches.JAVA_SEMANTIC_KERNEL_PLANNER) && (
+
+ )}
+
+
+
+
+
+
+ {useLogin && (
+
+ )}
+ {useLogin && (
+
+ )}
+
+ {streamAvailable &&
+
+ }
+
+ {useLogin && }
+
+
+
+ );
+};
+
+export default Chat;
diff --git a/infra/shared/host/container-apps.bicep b/infra/shared/host/container-apps.bicep
index 10691aa..a886c16 100644
--- a/infra/shared/host/container-apps.bicep
+++ b/infra/shared/host/container-apps.bicep
@@ -1,43 +1,45 @@
-metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.'
-param name string
-param location string = resourceGroup().location
-param tags object = {}
-
-param containerAppsEnvironmentName string
-param containerRegistryName string
-param containerRegistryResourceGroupName string = ''
-param containerRegistryAdminUserEnabled bool = false
-param logAnalyticsWorkspaceName string
-param applicationInsightsName string = ''
-param daprEnabled bool = false
-
-module containerAppsEnvironment 'container-apps-environment.bicep' = {
- name: '${name}-container-apps-environment'
- params: {
- name: containerAppsEnvironmentName
- location: location
- tags: tags
- logAnalyticsWorkspaceName: logAnalyticsWorkspaceName
- applicationInsightsName: applicationInsightsName
- daprEnabled: daprEnabled
- }
-}
-
-module containerRegistry 'container-registry.bicep' = {
- name: '${name}-container-registry'
- scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup()
- params: {
- name: containerRegistryName
- location: location
- adminUserEnabled: containerRegistryAdminUserEnabled
- tags: tags
- }
-}
-
-output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain
-output environmentName string = containerAppsEnvironment.outputs.name
-output environmentId string = containerAppsEnvironment.outputs.id
-
-output registryLoginServer string = containerRegistry.outputs.loginServer
-output registryName string = containerRegistry.outputs.name
-
+metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.'
+param name string
+param location string = resourceGroup().location
+param tags object = {}
+
+param containerAppsEnvironmentName string
+param containerRegistryName string
+param containerRegistryResourceGroupName string = ''
+param containerRegistryAdminUserEnabled bool = false
+param logAnalyticsWorkspaceName string
+param applicationInsightsName string = ''
+param daprEnabled bool = false
+
+var containerRegistryResourceGroupEvaluated = !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup()
+
+module containerAppsEnvironment 'container-apps-environment.bicep' = {
+ name: '${name}-container-apps-environment'
+ params: {
+ name: containerAppsEnvironmentName
+ location: location
+ tags: tags
+ logAnalyticsWorkspaceName: logAnalyticsWorkspaceName
+ applicationInsightsName: applicationInsightsName
+ daprEnabled: daprEnabled
+ }
+}
+
+module containerRegistry 'container-registry.bicep' = {
+ name: '${name}-container-registry'
+ scope: containerRegistryResourceGroupEvaluated
+ params: {
+ name: containerRegistryName
+ location: location
+ adminUserEnabled: containerRegistryAdminUserEnabled
+ tags: tags
+ }
+}
+
+output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain
+output environmentName string = containerAppsEnvironment.outputs.name
+output environmentId string = containerAppsEnvironment.outputs.id
+
+output registryLoginServer string = containerRegistry.outputs.loginServer
+output registryName string = containerRegistry.outputs.name
+