Skip to content

Commit 989ae31

Browse files
committed
Add weather agent sample
1 parent 148c6d5 commit 989ae31

File tree

16 files changed

+436
-0
lines changed

16 files changed

+436
-0
lines changed

samples/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<module>secure-poem-multiple-models</module>
2323
<module>secure-sql-chatbot</module>
2424
<module>sql-chatbot</module>
25+
<module>weather-agent</module>
2526
</modules>
2627

2728
<build>

samples/weather-agent/README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Chatbot example
2+
3+
This example demonstrates how to create an AI agent using Quarkus LangChain4j.
4+
5+
## Running the example
6+
7+
A prerequisite to running this example is to provide your OpenAI API key.
8+
9+
```
10+
export QUARKUS_LANGCHAIN4J_OPENAI_API_KEY=<your-openai-api-key>
11+
```
12+
13+
Then, simply run the project in Dev mode:
14+
15+
```
16+
mvn quarkus:dev
17+
```
18+
19+
## Using the example
20+
21+
Execute:
22+
23+
```
24+
curl http://localhost:8080/weather?city=Athens
25+
```
26+
27+
and you should get a response a like so:
28+
29+
```
30+
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
31+
```
32+
33+
## Using other model providers
34+
35+
### Compatible OpenAI serving infrastructure
36+
37+
Add `quarkus.langchain4j.openai.base-url=http://yourerver` to `application.properties`.
38+
39+
In this case, `quarkus.langchain4j.openai.api-key` is generally not needed.
40+
41+
### Ollama
42+
43+
44+
Replace:
45+
46+
```xml
47+
<dependency>
48+
<groupId>io.quarkiverse.langchain4j</groupId>
49+
<artifactId>quarkus-langchain4j-openai</artifactId>
50+
<version>${quarkus-langchain4j.version}</version>
51+
</dependency>
52+
```
53+
54+
with
55+
56+
```xml
57+
<dependency>
58+
<groupId>io.quarkiverse.langchain4j</groupId>
59+
<artifactId>quarkus-langchain4j-ollama</artifactId>
60+
<version>${quarkus-langchain4j.version}</version>
61+
</dependency>
62+
```
63+

samples/weather-agent/pom.xml

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<groupId>io.quarkiverse.langchain4j</groupId>
6+
<artifactId>quarkus-langchain4j-sample-weather-agent</artifactId>
7+
<name>Quarkus LangChain4j - Sample - Weather Agent</name>
8+
<version>1.0-SNAPSHOT</version>
9+
10+
<properties>
11+
<compiler-plugin.version>3.13.0</compiler-plugin.version>
12+
<maven.compiler.parameters>true</maven.compiler.parameters>
13+
<maven.compiler.release>17</maven.compiler.release>
14+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
16+
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
17+
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
18+
<quarkus.platform.version>3.15.1</quarkus.platform.version>
19+
<skipITs>true</skipITs>
20+
<surefire-plugin.version>3.2.5</surefire-plugin.version>
21+
<quarkus-langchain4j.version>999-SNAPSHOT</quarkus-langchain4j.version>
22+
</properties>
23+
24+
<dependencyManagement>
25+
<dependencies>
26+
<dependency>
27+
<groupId>${quarkus.platform.group-id}</groupId>
28+
<artifactId>${quarkus.platform.artifact-id}</artifactId>
29+
<version>${quarkus.platform.version}</version>
30+
<type>pom</type>
31+
<scope>import</scope>
32+
</dependency>
33+
</dependencies>
34+
</dependencyManagement>
35+
36+
<dependencies>
37+
<dependency>
38+
<groupId>io.quarkus</groupId>
39+
<artifactId>quarkus-rest-jackson</artifactId>
40+
</dependency>
41+
<dependency>
42+
<groupId>io.quarkiverse.langchain4j</groupId>
43+
<artifactId>quarkus-langchain4j-openai</artifactId>
44+
<version>${quarkus-langchain4j.version}</version>
45+
</dependency>
46+
<dependency>
47+
<groupId>io.quarkus</groupId>
48+
<artifactId>quarkus-cache</artifactId>
49+
</dependency>
50+
51+
<!-- Minimal dependencies to constrain the build -->
52+
<dependency>
53+
<groupId>io.quarkiverse.langchain4j</groupId>
54+
<artifactId>quarkus-langchain4j-openai-deployment</artifactId>
55+
<version>${quarkus-langchain4j.version}</version>
56+
<scope>test</scope>
57+
<type>pom</type>
58+
<exclusions>
59+
<exclusion>
60+
<groupId>*</groupId>
61+
<artifactId>*</artifactId>
62+
</exclusion>
63+
</exclusions>
64+
</dependency>
65+
</dependencies>
66+
<build>
67+
<plugins>
68+
<plugin>
69+
<groupId>io.quarkus</groupId>
70+
<artifactId>quarkus-maven-plugin</artifactId>
71+
<version>${quarkus.platform.version}</version>
72+
<executions>
73+
<execution>
74+
<goals>
75+
<goal>build</goal>
76+
</goals>
77+
</execution>
78+
</executions>
79+
</plugin>
80+
<plugin>
81+
<artifactId>maven-compiler-plugin</artifactId>
82+
<version>${compiler-plugin.version}</version>
83+
</plugin>
84+
<plugin>
85+
<artifactId>maven-surefire-plugin</artifactId>
86+
<version>3.5.1</version>
87+
<configuration>
88+
<systemPropertyVariables>
89+
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
90+
<maven.home>${maven.home}</maven.home>
91+
</systemPropertyVariables>
92+
</configuration>
93+
</plugin>
94+
</plugins>
95+
</build>
96+
97+
<profiles>
98+
<profile>
99+
<id>native</id>
100+
<activation>
101+
<property>
102+
<name>native</name>
103+
</property>
104+
</activation>
105+
<build>
106+
<plugins>
107+
<plugin>
108+
<artifactId>maven-failsafe-plugin</artifactId>
109+
<version>3.5.1</version>
110+
<executions>
111+
<execution>
112+
<goals>
113+
<goal>integration-test</goal>
114+
<goal>verify</goal>
115+
</goals>
116+
<configuration>
117+
<systemPropertyVariables>
118+
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
119+
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
120+
<maven.home>${maven.home}</maven.home>
121+
</systemPropertyVariables>
122+
</configuration>
123+
</execution>
124+
</executions>
125+
</plugin>
126+
</plugins>
127+
</build>
128+
<properties>
129+
<quarkus.package.type>native</quarkus.package.type>
130+
</properties>
131+
</profile>
132+
</profiles>
133+
134+
135+
</project>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.quarkiverse.langchain4j.weather.agent;
2+
3+
import dev.langchain4j.agent.tool.Tool;
4+
import dev.langchain4j.service.UserMessage;
5+
import io.quarkiverse.langchain4j.RegisterAiService;
6+
import jakarta.enterprise.context.ApplicationScoped;
7+
8+
@ApplicationScoped
9+
@RegisterAiService(chatMemoryProviderSupplier = RegisterAiService.NoChatMemoryProviderSupplier.class)
10+
public interface CityExtractorAgent {
11+
12+
@UserMessage("""
13+
You are given one question and you have to extract city name from it
14+
Only reply the city name if it exists or reply 'unknown_city' if there is no city name in question
15+
16+
Here is the question: {question}
17+
""")
18+
@Tool("Extracts the city from a question")
19+
String extractCity(String question);
20+
21+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.quarkiverse.langchain4j.weather.agent;
2+
3+
import dev.langchain4j.service.SystemMessage;
4+
import io.quarkiverse.langchain4j.RegisterAiService;
5+
import io.quarkiverse.langchain4j.weather.agent.geo.GeoCodingService;
6+
import io.quarkiverse.langchain4j.weather.agent.weather.WeatherForecastService;
7+
8+
@RegisterAiService(tools = { CityExtractorAgent.class, WeatherForecastService.class, GeoCodingService.class})
9+
public interface WeatherForecastAgent {
10+
11+
@SystemMessage("""
12+
You are a meteorologist, and you need to answer questions asked by the user about weather using at most 3 lines.
13+
14+
The weather information is a JSON object and has the following fields:
15+
16+
maxTemperature is the maximum temperature of the day in Celsius degrees
17+
minTemperature is the minimum temperature of the day in Celsius degrees
18+
precipitation is the amount of water in mm
19+
windSpeed is the speed of wind in kilometers per hour
20+
weather is the overall weather.
21+
""")
22+
String chat(String query);
23+
24+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.quarkiverse.langchain4j.weather.agent;
2+
3+
import jakarta.ws.rs.DefaultValue;
4+
import jakarta.ws.rs.GET;
5+
import jakarta.ws.rs.Path;
6+
import jakarta.ws.rs.Produces;
7+
import jakarta.ws.rs.core.MediaType;
8+
import org.jboss.resteasy.reactive.RestQuery;
9+
10+
11+
@Path("/weather")
12+
public class WeatherResource {
13+
14+
private final WeatherForecastAgent agent;
15+
16+
public WeatherResource(WeatherForecastAgent agent) {
17+
this.agent = agent;
18+
}
19+
20+
@GET
21+
@Produces(MediaType.TEXT_PLAIN)
22+
public String getWeather(@RestQuery @DefaultValue("Manilla") String city) {
23+
return agent.chat(String.format("What is the weather in %s ?", city));
24+
}
25+
26+
27+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.quarkiverse.langchain4j.weather.agent.geo;
2+
3+
import dev.langchain4j.agent.tool.Tool;
4+
import io.quarkus.cache.CacheResult;
5+
import io.quarkus.rest.client.reactive.ClientQueryParam;
6+
import jakarta.ws.rs.GET;
7+
import jakarta.ws.rs.Path;
8+
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
9+
import org.jboss.resteasy.reactive.RestQuery;
10+
11+
@RegisterRestClient(configKey = "geocoding")
12+
@Path("/v1")
13+
public interface GeoCodingService {
14+
15+
@GET
16+
@Path("/search")
17+
@CacheResult(cacheName = "geo-results")
18+
@ClientQueryParam(name = "count", value = "1")
19+
@Tool("Finds the latitude and longitude of a given city")
20+
GeoResults search(@RestQuery String name);
21+
22+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package io.quarkiverse.langchain4j.weather.agent.geo;
2+
3+
public record GeoResult(double latitude, double longitude) {
4+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.quarkiverse.langchain4j.weather.agent.geo;
2+
3+
import java.util.List;
4+
5+
public record GeoResults(List<GeoResult> results) {
6+
7+
public GeoResult getFirst() {
8+
return results.get(0);
9+
}
10+
11+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.quarkiverse.langchain4j.weather.agent.weather;
2+
3+
import java.util.Arrays;
4+
5+
public record Daily(double[] temperature_2m_max,
6+
double[] temperature_2m_min,
7+
double[] precipitation_sum,
8+
double[] wind_speed_10m_max,
9+
int[] weather_code) {
10+
11+
public DailyWeatherData getFirstDay() {
12+
return new DailyWeatherData(temperature_2m_max[0],
13+
temperature_2m_min[0],
14+
precipitation_sum[0],
15+
wind_speed_10m_max[0],
16+
weather_code[0]);
17+
}
18+
19+
@Override
20+
public String toString() {
21+
return "Daily{" + "temperature_2m_max=" + Arrays.toString(temperature_2m_max)
22+
+ ", temperature_2m_min=" + Arrays.toString(temperature_2m_min)
23+
+ ", precipitation_sum=" + Arrays.toString(precipitation_sum)
24+
+ ", wind_speed_10m_max=" + Arrays.toString(wind_speed_10m_max)
25+
+ ", weather_code=" + Arrays.toString(weather_code)
26+
+ '}';
27+
}
28+
29+
}

0 commit comments

Comments
 (0)