From cf7bfebba408410332de80b32ee17cfdad31946c Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 1 May 2024 18:51:26 +0100 Subject: [PATCH] Add secure-fraud-detection demo --- pom.xml | 1 + samples/secure-fraud-detection/README.md | 127 +++++++++++++++++ samples/secure-fraud-detection/pom.xml | 134 ++++++++++++++++++ .../langchain4j/sample/Customer.java | 17 +++ .../sample/CustomerRepository.java | 23 +++ .../langchain4j/sample/FraudDetectionAi.java | 73 ++++++++++ .../sample/FraudDetectionResource.java | 37 +++++ .../langchain4j/sample/LoginResource.java | 33 +++++ .../langchain4j/sample/LogoutResource.java | 29 ++++ .../sample/SecureMemoryIdProvider.java | 80 +++++++++++ .../quarkiverse/langchain4j/sample/Setup.java | 68 +++++++++ .../langchain4j/sample/Transaction.java | 26 ++++ .../sample/TransactionRepository.java | 26 ++++ .../META-INF/resources/images/google.png | Bin 0 -> 1768 bytes .../resources/META-INF/resources/index.html | 133 +++++++++++++++++ ...se.langchain4j.spi.DefaultMemoryIdProvider | 1 + .../src/main/resources/application.properties | 12 ++ .../resources/templates/fraudDetection.html | 23 +++ 18 files changed, 843 insertions(+) create mode 100644 samples/secure-fraud-detection/README.md create mode 100644 samples/secure-fraud-detection/pom.xml create mode 100644 samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/Customer.java create mode 100644 samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/CustomerRepository.java create mode 100644 samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/FraudDetectionAi.java create mode 100644 samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/FraudDetectionResource.java create mode 100644 samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/LoginResource.java create mode 100644 samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/LogoutResource.java create mode 100644 samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/SecureMemoryIdProvider.java create mode 100644 samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/Setup.java create mode 100644 samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/Transaction.java create mode 100644 samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/TransactionRepository.java create mode 100644 samples/secure-fraud-detection/src/main/resources/META-INF/resources/images/google.png create mode 100644 samples/secure-fraud-detection/src/main/resources/META-INF/resources/index.html create mode 100644 samples/secure-fraud-detection/src/main/resources/META-INF/services/io.quarkiverse.langchain4j.spi.DefaultMemoryIdProvider create mode 100644 samples/secure-fraud-detection/src/main/resources/application.properties create mode 100644 samples/secure-fraud-detection/src/main/resources/templates/fraudDetection.html diff --git a/pom.xml b/pom.xml index b927f6b2b..2c51b8762 100644 --- a/pom.xml +++ b/pom.xml @@ -192,6 +192,7 @@ samples/cli-translator samples/review-triage samples/fraud-detection + samples/secure-fraud-detection samples/chatbot samples/chatbot-easy-rag samples/csv-chatbot diff --git a/samples/secure-fraud-detection/README.md b/samples/secure-fraud-detection/README.md new file mode 100644 index 000000000..475d80aa5 --- /dev/null +++ b/samples/secure-fraud-detection/README.md @@ -0,0 +1,127 @@ +# Secure Fraud Detection Demo + +This demo showcases the implementation of a simple fraud detection system using LLMs (GPT-4 in this case) for users authenticated with Google. + +## The Demo + +### Setup + +The demo is based on fictional random data generated when the application starts, which includes: + +- 3 users +- 50 transactions +- For each transaction, a random amount between 1 and 1000 is generated and assigned to a random user. A random city is also assigned to each transaction. + +The setup is defined in the [Setup.java](./src/main/java/io/quarkiverse/langchain4j/samples/Setup.java) class. + +The users and transactions are stored in a PostgreSQL database. When running the demo in dev mode (recommended), the database is automatically created and populated. + +### Tools + +To enable fraud detection, we provide the LLM with access to customer and transaction data through two Panache repositories: + +- [CustomerRepository.java](./src/main/java/io/quarkiverse/langchain4j/samples/CustomerRepository.java) +- [TransactionRepository.java](./src/main/java/io/quarkiverse/langchain4j/samples/TransactionRepository.java) + +### AI Service + +This demo leverages the AI service abstraction, with the interaction between the LLM and the application handled through the AIService interface. + +The interface uses specific annotations to define the LLM and the tools to be used: + +```java +@RegisterAiService(chatMemorySupplier = AiConfig.MemoryProvider.class, + tools = { TransactionRepository.class }) +``` + +For each message, the prompt is engineered to help the LLM understand the context and answer the request: + +```java +@SystemMessage(""" + You are a bank account fraud detection AI. You have to detect frauds in transactions. + """) +@UserMessage(""" + Your task is to detect whether a fraud was committed for the customer {{customerName}}. + + To detect a fraud, perform the following actions: + 1. Retrieve the transactions for the customer {{customerName}} with the {{customerEmail}} email address for the last 15 minutes. + 2. Sum the amount of all these transactions. Ensure the sum is correct. + 3. If the amount is greater than 10000, a fraud is detected. + + Answer with a **single** JSON document containing: + - the customer name in the 'customer-name' key + - the computed sum in the 'total' key + - the 'fraud' key set to a boolean value indicating if a fraud was detected + - the 'transactions' key containing the list of transaction amounts + - the 'explanation' key containing an explanation of your answer, especially how the sum is computed. + - if there is a fraud, the 'email' key containing an email to the customer {{customerName}} to warn him about the fraud. The text must be formal and polite. It must ask the customer to contact the bank ASAP. + + Your response must be just the JSON document, nothing else. + """) +@Timeout(value = 2, unit = ChronoUnit.MINUTES) +String detectAmountFraudForCustomer(long customerId); +``` + +_Note:_ You can also use fault tolerance annotations in combination with the prompt annotations. + +### Using the AI service + +Once defined, you can inject the AI service as a regular bean, and use it: + +```java +package io.quarkiverse.langchain4j.sample; + +import java.util.List; + +import org.eclipse.microprofile.jwt.Claims; +import org.eclipse.microprofile.jwt.JsonWebToken; + +import io.quarkus.oidc.IdToken; +import io.quarkus.security.Authenticated; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +@Path("/fraud") +@Authenticated +public class FraudDetectionResource { + + private final FraudDetectionAi service; + private final TransactionRepository transactions; + + @Inject + @IdToken + private JsonWebToken idToken; + + public FraudDetectionResource(FraudDetectionAi service, TransactionRepository transactions) { + this.service = service; + this.transactions = transactions; + } + + @GET + @Path("/distance") + public String detectBasedOnDistance() { + return service.detectDistanceFraudForCustomer(idToken.getName(), idToken.getClaim(Claims.email)); + } + + @GET + @Path("/amount") + public String detectBaseOnAmount() { + return service.detectAmountFraudForCustomer(idToken.getName(), idToken.getClaim(Claims.email)); + } +} +``` + +## Running the Demo + +To run the demo, use the following commands: + +```shell +mvn quarkus:dev +``` +Then, issue requests: + +```shell +http ":8080/fraud/amount?customerId=1" +http ":8080/fraud/distance?customerId=1" +``` diff --git a/samples/secure-fraud-detection/pom.xml b/samples/secure-fraud-detection/pom.xml new file mode 100644 index 000000000..aeb0f03e3 --- /dev/null +++ b/samples/secure-fraud-detection/pom.xml @@ -0,0 +1,134 @@ + + + 4.0.0 + + io.quarkiverse.langchain4j + quarkus-langchain4j-sample-secure-fraud-detection + Quarkus LangChain4j - Sample - Secure Fraud Detection + 1.0-SNAPSHOT + + + 3.13.0 + true + 17 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus + 3.8.2 + true + 3.2.5 + 0.14.0 + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + io.quarkus + quarkus-oidc + + + io.quarkiverse.langchain4j + quarkus-langchain4j-openai + ${quarkus-langchain4j.version} + + + io.quarkus + quarkus-smallrye-fault-tolerance + + + io.quarkus + quarkus-jdbc-postgresql + + + io.quarkus + quarkus-hibernate-orm-panache + + + io.quarkus + quarkus-resteasy-reactive-qute + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.platform.version} + + + + build + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + maven-surefire-plugin + 3.2.5 + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + native + + + + + + maven-failsafe-plugin + 3.2.5 + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + + diff --git a/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/Customer.java b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/Customer.java new file mode 100644 index 000000000..5f4b9a91a --- /dev/null +++ b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/Customer.java @@ -0,0 +1,17 @@ +package io.quarkiverse.langchain4j.sample; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +@Entity +public class Customer { + + @Id + @GeneratedValue + public Long id; + public String name; + public String email; + public int transactionLimit; + public int distanceLimit; +} diff --git a/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/CustomerRepository.java b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/CustomerRepository.java new file mode 100644 index 000000000..61d29b705 --- /dev/null +++ b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/CustomerRepository.java @@ -0,0 +1,23 @@ +package io.quarkiverse.langchain4j.sample; + +import dev.langchain4j.agent.tool.Tool; +import io.quarkus.hibernate.orm.panache.PanacheRepository; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class CustomerRepository implements PanacheRepository { + + @Tool("Get the transaction limit for a given customer") + public int getTransactionLimit(String customerName, String customerEmail) { + return find("name = ?1 and email = ?2", + customerName, + customerEmail).firstResult().transactionLimit; + } + + @Tool("Get the distance limit for a given customer") + public int getDistanceLimit(String customerName, String customerEmail) { + return find("name = ?1 and email = ?2", + customerName, + customerEmail).firstResult().distanceLimit; + } +} diff --git a/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/FraudDetectionAi.java b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/FraudDetectionAi.java new file mode 100644 index 000000000..63ad005bd --- /dev/null +++ b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/FraudDetectionAi.java @@ -0,0 +1,73 @@ +package io.quarkiverse.langchain4j.sample; + +import java.time.temporal.ChronoUnit; + +import org.eclipse.microprofile.faulttolerance.Timeout; + +import dev.langchain4j.service.SystemMessage; +import dev.langchain4j.service.UserMessage; +import io.quarkiverse.langchain4j.RegisterAiService; + +@RegisterAiService(tools = { TransactionRepository.class, CustomerRepository.class }) +public interface FraudDetectionAi { + + @SystemMessage(""" + You are a bank account fraud detection AI. You have to detect frauds in transactions. + """) + @UserMessage(""" + Your task is to detect whether a fraud was committed for the customer {{customerName}}. + + To detect a fraud, perform the following actions: + 1 - Retrieve the transaction limit for the customer {{customerName}} with the {{customerEmail}} email address. + 2 - Retrieve the transactions for the customer {{customerName}} with the {{customerEmail}} email address for the last 15 minutes. + 3 - Sum the amount of all of these transactions. Make sure the sum is correct. + 4 - If the amount is greater than the transaction limit for this customer, a fraud is detected. + + Answer with a **single** JSON document containing: + - the customer name in the 'customer-name' key + - the 'returning-customer' key set to a boolean value indicating if the same query was already issued before + - the transaction limit in the 'transaction-limit' key + - the computed sum in the 'total' key + - the 'fraud' key set to a boolean value indicating if a fraud was detected + - the 'transactions' key containing the list of transaction amounts + - the 'explanation' key containing a explanation of your answer, including how the sum is computed. + - if there is a fraud, the 'email' key containing an email to the customer {{customerName}} to warn about the fraud. + + Your response must be just the raw JSON document, without ```json, ``` or anything else. Do not use null JSON properties. + """) + @Timeout(value = 2, unit = ChronoUnit.MINUTES) + String detectAmountFraudForCustomer(String customerName, String customerEmail); + + @SystemMessage(""" + You are a bank account fraud detection AI. You have to detect frauds in transactions. + """) + @UserMessage(""" + Detect frauds based on the distance between two transactions for the customer: {{customerName}}. + + To detect a fraud, perform the following actions: + 1 - Retrieve the distance limit in kilometers for the customer {{customerName}} with the {{customerEmail}} email address. + 2 - Retrieve the transactions for the customer {{customerName}} with the {{customerEmail}} email address for the last 15 minutes. + 3 - Retrieve the city for each transaction. + 4 - Check if the distance between 2 cities is greater than the distance limit, if so, a fraud is detected. + 5 - If a fraud is detected, find the two transactions associated with these cities. + + Answer with a **single** JSON document containing: + - the customer name in the 'customer-name' key + - the distance limit in the 'distance-limit' key + - the amount of the first transaction in the 'first-amount' key + - the amount of the second transaction in the 'second-amount' key + - the city of the first transaction in the 'first-city' key + - the city of the second transaction in the 'second-city' key + - the 'fraud' key set to a boolean value indicating if a fraud was detected (so the distance is greater than the distance limit) + - the 'distance' key set to the distance between the two cities + - the 'explanation' key containing a explanation of your answer. + - the 'cities' key containing all the cities for the transactions for the customer {{customerName}} in the last 15 minutes. + - if there is a fraud, the 'email' key containing an email to the customer {{customerName}} to warn about the fraud. + + Your response must be just the raw JSON document, without ```json, ``` or anything else. Do not use null JSON properties. + + """) + @Timeout(value = 2, unit = ChronoUnit.MINUTES) + String detectDistanceFraudForCustomer(String customerName, String customerEmail); + +} diff --git a/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/FraudDetectionResource.java b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/FraudDetectionResource.java new file mode 100644 index 000000000..7c26a5165 --- /dev/null +++ b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/FraudDetectionResource.java @@ -0,0 +1,37 @@ +package io.quarkiverse.langchain4j.sample; + +import org.eclipse.microprofile.jwt.Claims; +import org.eclipse.microprofile.jwt.JsonWebToken; + +import io.quarkus.oidc.IdToken; +import io.quarkus.security.Authenticated; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +@Path("/fraud") +@Authenticated +public class FraudDetectionResource { + + private final FraudDetectionAi service; + + @Inject + @IdToken + private JsonWebToken idToken; + + public FraudDetectionResource(FraudDetectionAi service) { + this.service = service; + } + + @GET + @Path("/amount") + public String detectBaseOnAmount() { + return service.detectAmountFraudForCustomer(idToken.getName(), idToken.getClaim(Claims.email)); + } + + @GET + @Path("/distance") + public String detectBasedOnDistance() { + return service.detectDistanceFraudForCustomer(idToken.getName(), idToken.getClaim(Claims.email)); + } +} diff --git a/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/LoginResource.java b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/LoginResource.java new file mode 100644 index 000000000..baf1e6d0c --- /dev/null +++ b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/LoginResource.java @@ -0,0 +1,33 @@ +package io.quarkiverse.langchain4j.sample; + +import org.eclipse.microprofile.jwt.JsonWebToken; + +import io.quarkus.oidc.IdToken; +import io.quarkus.qute.Template; +import io.quarkus.qute.TemplateInstance; +import io.quarkus.security.Authenticated; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; + +/** + * Login resource which returns a fraud detection page to the authenticated user + */ +@Path("/login") +@Authenticated +public class LoginResource { + + @Inject + @IdToken + JsonWebToken idToken; + + @Inject + Template fraudDetection; + + @GET + @Produces("text/html") + public TemplateInstance fraudDetection() { + return fraudDetection.data("name", idToken.getName()); + } +} diff --git a/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/LogoutResource.java b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/LogoutResource.java new file mode 100644 index 000000000..e2635772e --- /dev/null +++ b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/LogoutResource.java @@ -0,0 +1,29 @@ +package io.quarkiverse.langchain4j.sample; + +import io.quarkus.oidc.OidcSession; +import io.quarkus.security.Authenticated; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; + +/** + * Logout resource + */ +@Path("/logout") +@Authenticated +public class LogoutResource { + + @Inject + OidcSession session; + + @GET + public Response logout(@Context UriInfo uriInfo) { + // remove the local session cookie + session.logout().await().indefinitely(); + // redirect to the login page + return Response.seeOther(uriInfo.getBaseUriBuilder().path("login").build()).build(); + } +} diff --git a/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/SecureMemoryIdProvider.java b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/SecureMemoryIdProvider.java new file mode 100644 index 000000000..50342a5e4 --- /dev/null +++ b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/SecureMemoryIdProvider.java @@ -0,0 +1,80 @@ +package io.quarkiverse.langchain4j.sample; + +import java.util.Objects; + +import org.eclipse.microprofile.jwt.Claims; +import org.eclipse.microprofile.jwt.JsonWebToken; +import org.jboss.logging.Logger; + +import io.quarkiverse.langchain4j.spi.DefaultMemoryIdProvider; +import io.quarkus.arc.Arc; +import io.quarkus.arc.ArcContainer; +import io.quarkus.arc.InstanceHandle; +import io.quarkus.security.identity.SecurityIdentity; +import jakarta.enterprise.context.ContextNotActiveException; + +public class SecureMemoryIdProvider implements DefaultMemoryIdProvider { + + private static final Logger log = Logger.getLogger(SecureMemoryIdProvider.class); + + @Override + public int priority() { + return -1; + } + + @Override + public Object getMemoryId() { + ArcContainer container = Arc.container(); + if (container == null) { + log.info("Arc container is null"); + return null; + } + InstanceHandle instance = container.instance(SecurityIdentity.class); + if (instance.isAvailable()) { + try { + JsonWebToken jwt = (JsonWebToken)instance.get().getPrincipal(); + return new UserNameAndEmail(jwt.getName(), jwt.getClaim(Claims.email)); + } catch (ContextNotActiveException ignored) { + log.info("CDI context is null"); + // this means that either the session or request scope was not active, so we can't provide a value + return null; + } + } + log.info("SecurityIdentity is unavailable"); + return null; + } + + public static class UserNameAndEmail { + private final String name; + private final String email; + UserNameAndEmail(String name, String email) { + this.name = Objects.requireNonNull(name); + this.email = Objects.requireNonNull(email); + } + public String getName() { + return name; + } + public String getEmail() { + return email; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof UserNameAndEmail)) { + return false; + } + UserNameAndEmail otherMemoryId = (UserNameAndEmail)other; + return name.equals(otherMemoryId.name) && email.equals(otherMemoryId.email); + } + + @Override + public int hashCode() { + return Objects.hash(name, email); + } + + @Override + public String toString() { + return name + ":" + email; + } + } +} diff --git a/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/Setup.java b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/Setup.java new file mode 100644 index 000000000..1cdd85d9d --- /dev/null +++ b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/Setup.java @@ -0,0 +1,68 @@ +package io.quarkiverse.langchain4j.sample; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Random; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.transaction.Transactional; + +import io.quarkus.runtime.StartupEvent; + +@ApplicationScoped +public class Setup { + + public static List CITIES = List.of("Paris", "Lyon", "Marseille", "Bordeaux", "Toulouse", "Nantes", "Brest", + "Clermont-Ferrand", "La Rochelle", "Lille", "Metz", "Strasbourg", "Nancy", "Valence", "Avignon", "Montpellier", + "Nime", "Arles", "Nice", "Cannes"); + + public static String getARandomCity() { + return CITIES.get(new Random().nextInt(CITIES.size())); + } + + @Transactional + public void init(@Observes StartupEvent ev, CustomerRepository customers, TransactionRepository transactions) { + Random random = new Random(); + if (customers.count() == 0) { + var customer1 = new Customer(); + customer1.name = "Clement Escofier"; + customer1.email = "cescofier@mail.com"; + customer1.transactionLimit = 10000; + customer1.distanceLimit = 500; + customers.persist(customer1); + + var customer2 = new Customer(); + customer2.name = "Georgios Andrianakis"; + customer2.email = "geoand@mail.com"; + customer2.transactionLimit = 1000; + customer1.distanceLimit = 300; + customers.persist(customer2); + + var customer3 = new Customer(); + customer3.name = "Sergey Beryozkin"; + customer3.email = "sberyozkin@gmail.com"; + customer1.transactionLimit = 500; + customer1.distanceLimit = 100; + customers.persist(customer3); + } + + transactions.deleteAll(); // Delete all transactions + for (int i = 0; i < 50; i++) { + var transaction = new Transaction(); + // Get a random customer + var idx = random.nextInt((int) customers.count()); + Customer customer = customers.findAll().page(idx, 1).firstResult(); + transaction.customerName = customer.name; + transaction.customerEmail = customer.email; + transaction.amount = random.nextInt(1000) + 1; + transaction.time = LocalDateTime.now().minusMinutes(random.nextInt(20)); + transaction.city = getARandomCity(); + transactions.persist(transaction); + } + + for (Customer customer : customers.listAll()) { + System.out.println("Customer: " + customer.name + " - " + customer.id); + } + } +} diff --git a/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/Transaction.java b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/Transaction.java new file mode 100644 index 000000000..1a4c554ad --- /dev/null +++ b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/Transaction.java @@ -0,0 +1,26 @@ +package io.quarkiverse.langchain4j.sample; + +import java.time.LocalDateTime; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +@Entity +public class Transaction { + + @Id + @GeneratedValue + public Long id; + + public double amount; + + public String customerName; + + public String customerEmail; + + public String city; + + public LocalDateTime time; + +} diff --git a/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/TransactionRepository.java b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/TransactionRepository.java new file mode 100644 index 000000000..478adaa9c --- /dev/null +++ b/samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/TransactionRepository.java @@ -0,0 +1,26 @@ +package io.quarkiverse.langchain4j.sample; + +import java.time.LocalDateTime; +import java.util.List; + +import dev.langchain4j.agent.tool.Tool; +import io.quarkus.hibernate.orm.panache.PanacheRepository; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class TransactionRepository implements PanacheRepository { + + @Tool("Get the transactions for a given customer for the last 15 minutes") + public List getTransactionsForCustomer(String customerName, String customerEmail) { + return find("customerName = ?1 and customerEmail = ?2 and time > ?3", + customerName, + customerEmail, + LocalDateTime.now().minusMinutes(15)).list(); + } + + @Tool("Get the city for a given transaction id") + public String getCityForTransaction(long transactionId) { + return find("id = ?1", transactionId).firstResult().city; + } + +} diff --git a/samples/secure-fraud-detection/src/main/resources/META-INF/resources/images/google.png b/samples/secure-fraud-detection/src/main/resources/META-INF/resources/images/google.png new file mode 100644 index 0000000000000000000000000000000000000000..e2503df4e1444b9873941468508ccd199936d6c6 GIT binary patch literal 1768 zcmVP)Jou1o8{pKi)?@R z$HYuUmTlRh$s9G-NtJA%umqQ(h5F#3DAYk8%);GwE*34;-b+=oUs8I%bI*70k8{57 zeCM2U@Do{ic_|ojC2*W#5gR555P0^@-@R?+01Lob1P)Dr!dR-;8{r4Iu_zZ56!86a z`xbD**68SHJ~AQ_>>H8c5eNzjf?BN>^_)&8dU|@$W9>z2Yb!39FQKcm)52~Um7KCr zuh(1TzEc)Kk(k4tuU1dkx?uhS%+e)6qtRe2=6kSo5OaJ$gO^3 z^%EmU2vP@O!}@gSbh-pGh&LVOXaTu3_W9V?-dej3q9`($Os3;_7{Tnk z>NDPb4=R-k;|Z-+i}Ov5$%%=&+hDvD)U6ML=Ox(p}y%2s<8(B{-!2y#IeZ`-MBz-{{7LMTc8M2Ae&fcxx z4Hn2Xw5vrIll8RGTG{0KD?Z2VT{)8E#AkPP;-BJ=nWc|H4#?H>*@4jSv~Q)cdkv;6 zNllaW09)lg*ngS=}QtZ{$3Q#`r~! zpk-+s3ppt&mZ?OAYC@%FbpI@VJ(YG1ipc{QQf~4|?okmL9!Vi4p5kDXCRBR;2@cE1 zwR(0pl!@LK{n`pUq>#QK5KCY1eNw4|)a={0b4&-xeT+rYnPz z1n+X<75JZn2wEw6OMz2Id5nd=CishKlcg(-yPT{lrfPLMoRV(lLgFDfE#mbNhZbFy zHe^2ipx%j{!4Jr`f9n+8>PFaPX~$7%z_F^3G#lGH+9fA&QV^aaV>|n&K3Y5nazNIj?t>D_LK=%jQ(ax1 zbJmMVnm|=xG9|8Z5Tl90nwVEak}TmspfV7*oWo@;$zpiAzYO`wA3!%X7IHy`y6{)? z{$hP`C5`XZBm;v5tap;mN=}|q9l2OGBSri?4v_T>27`tAHg4HS*-2KMJ8$m5-xph6 zPD)BbfGPmv30huzzp8+9z_E>4&xp|k31>`a>SpQ^H2r=3$y7{_Ck`GugsWGtl$R8h z=p~g|*g08MT_ubc=v}OfcHSEouOZzvaOd^ zWaeg;QH$(W&n@9`<68SQvHiv@SLTa@($~$irnA53k`gc1)|wL7P&{d<)%z$Qs~L?( z96Wr8Wl@eDrNyNMzVv0afT{D~f~!Eoho)1UOiN9LtUBgCQ2Iot)o-<@G2nETGnW>4 z>j(Npa5Pa)P%H|M2#+GN)I<;EHK4l2ol}yPSdNMI&JMAO-oes2mYrBxL@%GHo>(S_ zm|q0%2Nl(9kEKxAN|hmoKlgry;5aMm4m7gmc8W5lQhlire&9b-ai9neJ9jz&0000< KMNUMnLSTYO@I>?g literal 0 HcmV?d00001 diff --git a/samples/secure-fraud-detection/src/main/resources/META-INF/resources/index.html b/samples/secure-fraud-detection/src/main/resources/META-INF/resources/index.html new file mode 100644 index 000000000..6fd3636ed --- /dev/null +++ b/samples/secure-fraud-detection/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,133 @@ + + + + + Secure Fraud Detection + + + + + + +
+
+
+

Login

+ + + + +
Login with Google
+ +
+
+
+ +
+ +
+ + + + diff --git a/samples/secure-fraud-detection/src/main/resources/META-INF/services/io.quarkiverse.langchain4j.spi.DefaultMemoryIdProvider b/samples/secure-fraud-detection/src/main/resources/META-INF/services/io.quarkiverse.langchain4j.spi.DefaultMemoryIdProvider new file mode 100644 index 000000000..4a8d26572 --- /dev/null +++ b/samples/secure-fraud-detection/src/main/resources/META-INF/services/io.quarkiverse.langchain4j.spi.DefaultMemoryIdProvider @@ -0,0 +1 @@ +io.quarkiverse.langchain4j.sample.SecureMemoryIdProvider \ No newline at end of file diff --git a/samples/secure-fraud-detection/src/main/resources/application.properties b/samples/secure-fraud-detection/src/main/resources/application.properties new file mode 100644 index 000000000..e87a9c8e5 --- /dev/null +++ b/samples/secure-fraud-detection/src/main/resources/application.properties @@ -0,0 +1,12 @@ +quarkus.langchain4j.openai.timeout=60s +quarkus.langchain4j.openai.chat-model.temperature=0 +quarkus.langchain4j.openai.api-key=${OPENAI_API_KEY} + +quarkus.oidc.provider=google +quarkus.oidc.client-id=${GOOGLE_CLIENT_ID} +quarkus.oidc.credentials.secret=${GOOGLE_CLIENT_SECRET} +quarkus.oidc.authentication.redirect-path=/login + +#quarkus.langchain4j.openai.log-requests=true +#quarkus.langchain4j.openai.log-responses=true + diff --git a/samples/secure-fraud-detection/src/main/resources/templates/fraudDetection.html b/samples/secure-fraud-detection/src/main/resources/templates/fraudDetection.html new file mode 100644 index 000000000..7f63cdb48 --- /dev/null +++ b/samples/secure-fraud-detection/src/main/resources/templates/fraudDetection.html @@ -0,0 +1,23 @@ + + + + +Secure Fraud Detection + + +

Hello {name}, please check fraud occurrences by amount or distance:

+ + + + + + + +
+ Fraud by amount +
+ Fraud by distance +
+ Logout + +