[FEATURE] Adds SearchApi as WebSearchEngine and Tool (#1216)

This commit is contained in:
LangChain4j 2024-08-22 11:16:06 +02:00
parent 99ed696f05
commit 2e47b126be
6 changed files with 40 additions and 17 deletions

View File

@ -15,14 +15,14 @@ Add the following dependencies to your project's `pom.xml`:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-web-search-engine-searchapi</artifactId>
<version>{your-version}</version> <!-- Specify langchain4j version here -->
<version>0.34.0</version>
</dependency>
```
or project's `build.gradle`:
```groovy
implementation 'dev.langchain4j:langchain4j-web-search-engine-searchapi:{your-version}'
implementation 'dev.langchain4j:langchain4j-web-search-engine-searchapi:0.34.0'
```
### Example code:
@ -40,7 +40,7 @@ import dev.langchain4j.web.search.searchapi.SearchApiWebSearchEngine;
public class SearchApiTool {
interface Assistant {
@dev.langchain4j.service.SystemMessage({
@SystemMessage({
"You are a web search support agent.",
"If there is any event that has not happened yet",
"You MUST create a web search request with user query and",

View File

@ -11,12 +11,14 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
import static java.net.HttpURLConnection.HTTP_OK;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
/**
* Utility methods.
@ -273,4 +275,20 @@ public class Utils {
return unmodifiableList(list);
}
/**
* Returns an (unmodifiable) copy of the provided map.
* Returns <code>null</code> if the provided map is <code>null</code>.
*
* @param map The map to copy.
* @return The copy of the provided map.
*/
public static <K,V> Map<K,V> copyIfNotNull(Map<K,V> map) {
if (map == null) {
return null;
}
return unmodifiableMap(map);
}
}

View File

@ -9,17 +9,13 @@ import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.UUID;
import java.util.*;
import java.util.stream.Stream;
import static dev.langchain4j.internal.Utils.quoted;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static java.util.Collections.*;
import static org.assertj.core.api.Assertions.*;
@SuppressWarnings({"ObviousNullCheck", "ConstantValue"})
class UtilsTest {
@ -215,13 +211,20 @@ class UtilsTest {
}
@Test
void test_copyIfNotNull() {
assertThat(Utils.copyIfNotNull(null)).isNull();
void test_copyIfNotNull_List() {
assertThat(Utils.copyIfNotNull((List<?>) null)).isNull();
assertThat(Utils.copyIfNotNull(emptyList())).isEmpty();
assertThat(Utils.copyIfNotNull(singletonList("one"))).containsExactly("one");
assertThat(Utils.copyIfNotNull(asList("one", "two"))).containsExactly("one", "two");
}
@Test
void test_copyIfNotNull_Map() {
assertThat(Utils.copyIfNotNull((Map<?, ?>)null)).isNull();
assertThat(Utils.copyIfNotNull(emptyMap())).isEmpty();
assertThat(Utils.copyIfNotNull(singletonMap("key", "value"))).containsExactly(entry("key", "value"));
}
@Test
void test_ensureTrailingForwardSlash() {
assertThat(Utils.ensureTrailingForwardSlash("https://example.com")).isEqualTo("https://example.com/");

View File

@ -9,7 +9,7 @@ import java.util.Map;
interface SearchApi {
@GET("/api/v1/search")
@GET("api/v1/search")
Call<SearchApiWebSearchResponse> search(@QueryMap Map<String, Object> params,
@Header("Authorization") String bearerToken);
}

View File

@ -14,6 +14,7 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static dev.langchain4j.internal.Utils.copyIfNotNull;
import static dev.langchain4j.internal.Utils.getOrDefault;
import static dev.langchain4j.internal.ValidationUtils.ensureNotBlank;
import static java.time.Duration.ofSeconds;
@ -25,7 +26,7 @@ import static java.time.Duration.ofSeconds;
*/
public class SearchApiWebSearchEngine implements WebSearchEngine {
private static final String BASE_URL = "https://www.searchapi.io";
private static final String DEFAULT_BASE_URL = "https://www.searchapi.io";
private static final String DEFAULT_ENGINE = "google";
private final String apiKey;
@ -54,10 +55,10 @@ public class SearchApiWebSearchEngine implements WebSearchEngine {
Map<String, Object> optionalParameters) {
this.apiKey = ensureNotBlank(apiKey, "apiKey");
this.engine = getOrDefault(engine, DEFAULT_ENGINE);
this.optionalParameters = getOrDefault(optionalParameters, new HashMap<>());
this.optionalParameters = getOrDefault(copyIfNotNull(optionalParameters), new HashMap<>());
this.client = SearchApiClient.builder()
.timeout(getOrDefault(timeout, ofSeconds(30)))
.baseUrl(getOrDefault(baseUrl, BASE_URL))
.baseUrl(getOrDefault(baseUrl, DEFAULT_BASE_URL))
.build();
}

View File

@ -3,6 +3,7 @@ package dev.langchain4j.web.search.searchapi;
import lombok.Builder;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
@Getter
@ -25,7 +26,7 @@ class SearchApiWebSearchRequest {
this.engine = engine;
this.apiKey = apiKey;
this.query = query;
this.finalOptionalParameters = optionalParameters;
this.finalOptionalParameters = new HashMap<>(optionalParameters);
if (additionalRequestParameters != null) {
finalOptionalParameters.putAll(additionalRequestParameters);
}