Skip to content

Commit

Permalink
Add weather agent sample
Browse files Browse the repository at this point in the history
  • Loading branch information
geoand committed Dec 13, 2024
1 parent db76724 commit 050d444
Show file tree
Hide file tree
Showing 16 changed files with 436 additions and 0 deletions.
1 change: 1 addition & 0 deletions samples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<module>secure-poem-multiple-models</module>
<module>secure-sql-chatbot</module>
<module>sql-chatbot</module>
<module>weather-agent</module>
</modules>

<build>
Expand Down
63 changes: 63 additions & 0 deletions samples/weather-agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Chatbot example

This example demonstrates how to create an AI agent using Quarkus LangChain4j.

## Running the example

A prerequisite to running this example is to provide your OpenAI API key.

```
export QUARKUS_LANGCHAIN4J_OPENAI_API_KEY=<your-openai-api-key>
```

Then, simply run the project in Dev mode:

```
mvn quarkus:dev
```

## Using the example
Execute:

```
curl http://localhost:8080/weather?city=Athens
```

and you should get a response a like so:

```
The weather in Athens today is mostly cloudy, with a maximum temperature of 15.6°C and a minimum of 7.4°C. There is no expected precipitation and wind speeds can reach up to 8.1 km/h
```

## Using other model providers

### Compatible OpenAI serving infrastructure

Add `quarkus.langchain4j.openai.base-url=http://yourerver` to `application.properties`.

In this case, `quarkus.langchain4j.openai.api-key` is generally not needed.

### Ollama


Replace:

```xml
<dependency>
<groupId>io.quarkiverse.langchain4j</groupId>
<artifactId>quarkus-langchain4j-openai</artifactId>
<version>${quarkus-langchain4j.version}</version>
</dependency>
```

with

```xml
<dependency>
<groupId>io.quarkiverse.langchain4j</groupId>
<artifactId>quarkus-langchain4j-ollama</artifactId>
<version>${quarkus-langchain4j.version}</version>
</dependency>
```

135 changes: 135 additions & 0 deletions samples/weather-agent/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>io.quarkiverse.langchain4j</groupId>
<artifactId>quarkus-langchain4j-sample-weather-agent</artifactId>
<name>Quarkus LangChain4j - Sample - Weather Agent</name>
<version>1.0-SNAPSHOT</version>

<properties>
<compiler-plugin.version>3.13.0</compiler-plugin.version>
<maven.compiler.parameters>true</maven.compiler.parameters>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
<quarkus.platform.version>3.15.1</quarkus.platform.version>
<skipITs>true</skipITs>
<surefire-plugin.version>3.2.5</surefire-plugin.version>
<quarkus-langchain4j.version>999-SNAPSHOT</quarkus-langchain4j.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkiverse.langchain4j</groupId>
<artifactId>quarkus-langchain4j-openai</artifactId>
<version>${quarkus-langchain4j.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-cache</artifactId>
</dependency>

<!-- Minimal dependencies to constrain the build -->
<dependency>
<groupId>io.quarkiverse.langchain4j</groupId>
<artifactId>quarkus-langchain4j-openai-deployment</artifactId>
<version>${quarkus-langchain4j.version}</version>
<scope>test</scope>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>


</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.quarkiverse.langchain4j.weather.agent;

import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.service.UserMessage;
import io.quarkiverse.langchain4j.RegisterAiService;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
@RegisterAiService(chatMemoryProviderSupplier = RegisterAiService.NoChatMemoryProviderSupplier.class)
public interface CityExtractorAgent {

@UserMessage("""
You are given one question and you have to extract city name from it
Only reply the city name if it exists or reply 'unknown_city' if there is no city name in question
Here is the question: {question}
""")
@Tool("Extracts the city from a question")
String extractCity(String question);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.quarkiverse.langchain4j.weather.agent;

import dev.langchain4j.service.SystemMessage;
import io.quarkiverse.langchain4j.RegisterAiService;
import io.quarkiverse.langchain4j.weather.agent.geo.GeoCodingService;
import io.quarkiverse.langchain4j.weather.agent.weather.WeatherForecastService;

@RegisterAiService(tools = { CityExtractorAgent.class, WeatherForecastService.class, GeoCodingService.class})
public interface WeatherForecastAgent {

@SystemMessage("""
You are a meteorologist, and you need to answer questions asked by the user about weather using at most 3 lines.
The weather information is a JSON object and has the following fields:
maxTemperature is the maximum temperature of the day in Celsius degrees
minTemperature is the minimum temperature of the day in Celsius degrees
precipitation is the amount of water in mm
windSpeed is the speed of wind in kilometers per hour
weather is the overall weather.
""")
String chat(String query);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.quarkiverse.langchain4j.weather.agent;

import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.jboss.resteasy.reactive.RestQuery;


@Path("/weather")
public class WeatherResource {

private final WeatherForecastAgent agent;

public WeatherResource(WeatherForecastAgent agent) {
this.agent = agent;
}

@GET
@Produces(MediaType.TEXT_PLAIN)
public String getWeather(@RestQuery @DefaultValue("Manilla") String city) {
return agent.chat(String.format("What is the weather in %s ?", city));
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.quarkiverse.langchain4j.weather.agent.geo;

import dev.langchain4j.agent.tool.Tool;
import io.quarkus.cache.CacheResult;
import io.quarkus.rest.client.reactive.ClientQueryParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.reactive.RestQuery;

@RegisterRestClient(configKey = "geocoding")
@Path("/v1")
public interface GeoCodingService {

@GET
@Path("/search")
@CacheResult(cacheName = "geo-results")
@ClientQueryParam(name = "count", value = "1")
@Tool("Finds the latitude and longitude of a given city")
GeoResults search(@RestQuery String name);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.quarkiverse.langchain4j.weather.agent.geo;

public record GeoResult(double latitude, double longitude) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkiverse.langchain4j.weather.agent.geo;

import java.util.List;

public record GeoResults(List<GeoResult> results) {

public GeoResult getFirst() {
return results.get(0);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.quarkiverse.langchain4j.weather.agent.weather;

import java.util.Arrays;

public record Daily(double[] temperature_2m_max,
double[] temperature_2m_min,
double[] precipitation_sum,
double[] wind_speed_10m_max,
int[] weather_code) {

public DailyWeatherData getFirstDay() {
return new DailyWeatherData(temperature_2m_max[0],
temperature_2m_min[0],
precipitation_sum[0],
wind_speed_10m_max[0],
weather_code[0]);
}

@Override
public String toString() {
return "Daily{" + "temperature_2m_max=" + Arrays.toString(temperature_2m_max)
+ ", temperature_2m_min=" + Arrays.toString(temperature_2m_min)
+ ", precipitation_sum=" + Arrays.toString(precipitation_sum)
+ ", wind_speed_10m_max=" + Arrays.toString(wind_speed_10m_max)
+ ", weather_code=" + Arrays.toString(weather_code)
+ '}';
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.quarkiverse.langchain4j.weather.agent.weather;

public record DailyUnits(String time,
String temperature_2m_max,
String precipitation_sum,
String wind_speed_10m_max) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.quarkiverse.langchain4j.weather.agent.weather;

import io.vertx.core.json.JsonObject;

public record DailyWeatherData(double temperature_2m_max,
double temperature_2m_min,
double precipitation_sum,
double wind_speed_10m_max,
int weather_code) {


public JsonObject toJson() {
JsonObject json = new JsonObject();
json.put("maxTemperature", temperature_2m_max());
json.put("minTemperature", temperature_2m_min());
json.put("precipitation", precipitation_sum());
json.put("windSpeed", wind_speed_10m_max());
json.put("weather", WmoCode.translate(weather_code()));

return json;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.quarkiverse.langchain4j.weather.agent.weather;

public record WeatherForecast(DailyUnits daily_units, Daily daily) {


}
Loading

0 comments on commit 050d444

Please sign in to comment.