Skip to content

Commit

Permalink
feat: Add support for TavilySearchResultsTool and TavilyAnswerTool (#467
Browse files Browse the repository at this point in the history
)
  • Loading branch information
davidmigloz committed Jun 20, 2024
1 parent 7c89060 commit a9f3575
Show file tree
Hide file tree
Showing 22 changed files with 494 additions and 35 deletions.
7 changes: 7 additions & 0 deletions examples/browser_summarizer/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
tavily_dart:
dependency: "direct overridden"
description:
path: "../../packages/tavily_dart"
relative: true
source: path
version: "0.0.1-dev.1"
term_glyph:
dependency: transitive
description:
Expand Down
4 changes: 3 additions & 1 deletion examples/browser_summarizer/pubspec_overrides.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# melos_managed_dependency_overrides: langchain,langchain_openai,openai_dart,langchain_core,langchain_community
# melos_managed_dependency_overrides: langchain,langchain_openai,openai_dart,langchain_core,langchain_community,tavily_dart
dependency_overrides:
langchain:
path: ../../packages/langchain
Expand All @@ -10,3 +10,5 @@ dependency_overrides:
path: ../../packages/langchain_openai
openai_dart:
path: ../../packages/openai_dart
tavily_dart:
path: ../../packages/tavily_dart
11 changes: 9 additions & 2 deletions examples/docs_examples/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,10 @@ packages:
dependency: transitive
description:
name: google_generative_ai
sha256: bb7d3480b05afb3b1f2459b52893cb22f69ded4e2fb853e212437123c457f1be
sha256: "76e35d93b8c1cd888f69a1a371f8c5dc54cec372b6c74a4c0a5d506e7cf82c1a"
url: "https://pub.dev"
source: hosted
version: "0.4.0"
version: "0.4.3"
google_identity_services_web:
dependency: transitive
description:
Expand Down Expand Up @@ -413,6 +413,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
tavily_dart:
dependency: "direct overridden"
description:
path: "../../packages/tavily_dart"
relative: true
source: path
version: "0.0.1-dev.1"
term_glyph:
dependency: transitive
description:
Expand Down
4 changes: 3 additions & 1 deletion examples/docs_examples/pubspec_overrides.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# melos_managed_dependency_overrides: chromadb,langchain,langchain_chroma,langchain_google,langchain_mistralai,langchain_ollama,langchain_openai,mistralai_dart,ollama_dart,openai_dart,vertex_ai,langchain_core,langchain_community
# melos_managed_dependency_overrides: chromadb,langchain,langchain_chroma,langchain_google,langchain_mistralai,langchain_ollama,langchain_openai,mistralai_dart,ollama_dart,openai_dart,vertex_ai,langchain_core,langchain_community,tavily_dart
dependency_overrides:
chromadb:
path: ../../packages/chromadb
Expand All @@ -24,5 +24,7 @@ dependency_overrides:
path: ../../packages/ollama_dart
openai_dart:
path: ../../packages/openai_dart
tavily_dart:
path: ../../packages/tavily_dart
vertex_ai:
path: ../../packages/vertex_ai
4 changes: 2 additions & 2 deletions examples/hello_world_flutter/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,10 @@ packages:
dependency: transitive
description:
name: google_generative_ai
sha256: bb7d3480b05afb3b1f2459b52893cb22f69ded4e2fb853e212437123c457f1be
sha256: "76e35d93b8c1cd888f69a1a371f8c5dc54cec372b6c74a4c0a5d506e7cf82c1a"
url: "https://pub.dev"
source: hosted
version: "0.4.0"
version: "0.4.3"
google_identity_services_web:
dependency: transitive
description:
Expand Down
7 changes: 7 additions & 0 deletions examples/wikivoyage_eu/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
tavily_dart:
dependency: "direct overridden"
description:
path: "../../packages/tavily_dart"
relative: true
source: path
version: "0.0.1-dev.1"
term_glyph:
dependency: transitive
description:
Expand Down
4 changes: 3 additions & 1 deletion examples/wikivoyage_eu/pubspec_overrides.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# melos_managed_dependency_overrides: langchain,langchain_core,langchain_community,langchain_ollama,ollama_dart
# melos_managed_dependency_overrides: langchain,langchain_core,langchain_community,langchain_ollama,ollama_dart,tavily_dart
dependency_overrides:
langchain:
path: ../../packages/langchain
Expand All @@ -10,3 +10,5 @@ dependency_overrides:
path: ../../packages/langchain_ollama
ollama_dart:
path: ../../packages/ollama_dart
tavily_dart:
path: ../../packages/tavily_dart
47 changes: 24 additions & 23 deletions packages/langchain/README.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion packages/langchain_chroma/pubspec_overrides.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# melos_managed_dependency_overrides: chromadb,langchain_openai,openai_dart,langchain_core,langchain_community,langchain
# melos_managed_dependency_overrides: chromadb,langchain_openai,openai_dart,langchain_core,langchain_community,langchain,tavily_dart
dependency_overrides:
chromadb:
path: ../chromadb
Expand All @@ -12,3 +12,5 @@ dependency_overrides:
path: ../langchain_openai
openai_dart:
path: ../openai_dart
tavily_dart:
path: ../tavily_dart
4 changes: 4 additions & 0 deletions packages/langchain_community/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ The most popular third-party integrations have their own packages (e.g. [langcha
* `WebBaseLoader`: for web pages.
- Tools:
* `CalculatorTool`: to calculate math expressions.
* `TavilySearchResultsTool`: returns a list of results for a query using the [Tavily](https://tavily.com) search engine.
* `TavilyAnswerTool`: returns an answer for a query using the [Tavily](https://tavily.com) search engine.
- Vector stores:
* `ObjectBoxVectorStore`: [ObjectBox](https://objectbox.io/) on-device vector database.

Check out the [API reference](https://pub.dev/documentation/langchain_community/latest) for more details.

Expand Down
21 changes: 21 additions & 0 deletions packages/langchain_community/lib/src/tools/tavily/mappers.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// ignore_for_file: public_member_api_docs
import 'package:tavily_dart/tavily_dart.dart';

import 'types.dart';

extension TavilySearchDepthX on TavilySearchDepth {
SearchRequestSearchDepth toSearchRequestSearchDepth() => switch (this) {
TavilySearchDepth.basic => SearchRequestSearchDepth.basic,
TavilySearchDepth.advanced => SearchRequestSearchDepth.advanced,
};
}

extension TavilySearchResultX on SearchResult {
TavilySearchResult toTavilySearchResult() => TavilySearchResult(
title: title,
url: url,
content: content,
rawContent: rawContent,
score: score,
);
}
3 changes: 3 additions & 0 deletions packages/langchain_community/lib/src/tools/tavily/tavily.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export 'tavily_answer.dart';
export 'tavily_search_results.dart';
export 'types.dart';
102 changes: 102 additions & 0 deletions packages/langchain_community/lib/src/tools/tavily/tavily_answer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import 'dart:async';

import 'package:http/http.dart' as http;
import 'package:langchain_core/tools.dart';
import 'package:tavily_dart/tavily_dart.dart';

import 'mappers.dart';
import 'tavily_search_results.dart';
import 'types.dart';

/// Tool that queries the [Tavily Search API](https://tavily.com) and
/// gets an answer to the search query.
///
/// The Tavily API uses API keys for authentication. Visit the
/// [Tavily console](https://app.tavily.com/) to retrieve the API key you'll
/// use in your requests.
///
/// If you want to get a list of search results instead, use the
/// [TavilySearchResultsTool] instead.
///
/// Example:
/// ```dart
/// final tool = TavilyAnswerTool(
/// apiKey: Platform.environment['TAVILY_API_KEY']!,
/// );
/// final res = await tool.invoke('What is the weather like in New York?');
/// print(res);
/// // The current weather in New York is clear with a temperature of 22.8°C (73.0°F)...
/// ```
final class TavilyAnswerTool extends StringTool<TavilyAnswerToolOptions> {
/// Creates a [TavilyAnswerTool] instance.
///
/// Main configuration options:
/// - `apiKey`: your Tavily API key. You can find your API key in the
/// [Tavily console](https://app.tavily.com/).
///
/// Advance configuration options:
/// - `baseUrl`: the base URL to use. Defaults to Tavily's API URL. You can
/// override this to use a different API URL, or to use a proxy.
/// - `headers`: global headers to send with every request. You can use
/// this to set custom headers, or to override the default headers.
/// - `queryParams`: global query parameters to send with every request. You
/// can use this to set custom query parameters (e.g. Azure OpenAI API
/// required to attach a `version` query parameter to every request).
/// - `client`: the HTTP client to use. You can set your own HTTP client if
/// you need further customization (e.g. to use a Socks5 proxy).
TavilyAnswerTool({
required this.apiKey,
final String? baseUrl,
final Map<String, String> headers = const {},
final Map<String, dynamic> queryParams = const {},
final http.Client? client,
super.defaultOptions = const TavilyAnswerToolOptions(),
}) : _client = TavilyClient(
baseUrl: baseUrl,
headers: headers,
queryParams: queryParams,
client: client,
),
super(
name: 'tavily_answer',
description:
'A search engine optimized for comprehensive, accurate, and trusted answers. '
'Useful for when you need to answer questions about current events. '
'The tool returns an answer to the search query - not the search results.',
inputDescription: 'The search query to get an answer to. '
'Eg: "What is the weather like in New York?"',
);

/// A client for interacting with Tavily API.
final TavilyClient _client;

/// Your Tavily API key.
String apiKey;

@override
Future<String> invokeInternal(
final String toolInput, {
final TavilyAnswerToolOptions? options,
}) async {
final res = await _client.search(
request: SearchRequest(
apiKey: apiKey,
query: toolInput,
includeAnswer: true,
searchDepth: (options?.searchDepth ?? defaultOptions.searchDepth)
.toSearchRequestSearchDepth(),
maxResults: options?.maxResults ?? defaultOptions.maxResults,
includeDomains:
options?.includeDomains ?? defaultOptions.includeDomains,
excludeDomains:
options?.excludeDomains ?? defaultOptions.excludeDomains,
),
);
return res.answer ?? '';
}

@override
void close() {
_client.endSession();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import 'dart:async';

import 'package:http/http.dart' as http;
import 'package:langchain_core/tools.dart';
import 'package:tavily_dart/tavily_dart.dart';

import 'mappers.dart';
import 'tavily_answer.dart';
import 'types.dart';

/// Tool that queries the [Tavily Search API](https://tavily.com) and
/// gets back a list of search results.
///
/// The Tavily API uses API keys for authentication. Visit the
/// [Tavily console](https://app.tavily.com/) to retrieve the API key you'll
/// use in your requests.
///
/// If you want to get directly an answer to a search query, use the
/// [TavilyAnswerTool] instead.
///
/// Example:
/// ```dart
/// final tool = TavilySearchResultsTool(
/// apiKey: Platform.environment['TAVILY_API_KEY']!,
/// );
/// final res = await tool.invoke('What is the weather like in New York?');
/// print(res);
/// // [
/// // {
/// // "title": "Weather in New York",
/// // "url": "https://www.weatherapi.com/",
/// // "content": "{'location': {'lat': 40.71, 'lon': -74.01}, 'current': {'last_updated': '2024-06-20 17:00', 'temp_c': 31.1, 'condition': {'text': 'Sunny', 'icon': '//cdn.weatherapi.com/weather/64x64/day/113.png'}, 'wind_mph': 2.2, 'wind_kph': 3.6, 'wind_degree': 161, 'wind_dir': 'SSE', 'pressure_mb': 1025.0, 'pressure_in': 30.26, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 48, 'cloud': 0, 'feelslike_c': 33.1, 'feelslike_f': 91.6, 'windchill_c': 29.5, 'windchill_f': 85.0, 'heatindex_c': 30.6, 'heatindex_f': 87.0, 'dewpoint_c': 17.7, 'dewpoint_f': 63.8, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 7.0, 'gust_mph': 16.4, 'gust_kph': 26.4}}",
/// // "score": 0.98855
/// // },
/// // ...
/// // ]
/// ```
final class TavilySearchResultsTool
extends Tool<String, TavilySearchResultsToolOptions, TavilySearchResults> {
/// Creates a [TavilySearchResultsTool] instance.
///
/// Main configuration options:
/// - `apiKey`: your Tavily API key. You can find your API key in the
/// [Tavily console](https://app.tavily.com/).
///
/// Advance configuration options:
/// - `baseUrl`: the base URL to use. Defaults to Tavily's API URL. You can
/// override this to use a different API URL, or to use a proxy.
/// - `headers`: global headers to send with every request. You can use
/// this to set custom headers, or to override the default headers.
/// - `queryParams`: global query parameters to send with every request. You
/// can use this to set custom query parameters (e.g. Azure OpenAI API
/// required to attach a `version` query parameter to every request).
/// - `client`: the HTTP client to use. You can set your own HTTP client if
/// you need further customization (e.g. to use a Socks5 proxy).
TavilySearchResultsTool({
required this.apiKey,
final String? baseUrl,
final Map<String, String> headers = const {},
final Map<String, dynamic> queryParams = const {},
final http.Client? client,
super.defaultOptions = const TavilySearchResultsToolOptions(),
}) : _client = TavilyClient(
baseUrl: baseUrl,
headers: headers,
queryParams: queryParams,
client: client,
),
super(
name: 'tavily_search_results',
description:
'A search engine optimized for comprehensive, accurate, and trusted results. '
'Useful for when you need to answer questions about current events. '
'The tool returns a JSON object with search results.',
inputJsonSchema: {
'type': 'object',
'properties': {
'query': {
'type': 'string',
'description': 'The search query to look up. '
'Eg: "What is the weather like in New York?"',
},
},
'required': ['query'],
},
);

/// A client for interacting with Tavily API.
final TavilyClient _client;

/// Your Tavily API key.
String apiKey;

@override
Future<TavilySearchResults> invokeInternal(
final String input, {
final TavilySearchResultsToolOptions? options,
}) async {
final res = await _client.search(
request: SearchRequest(
apiKey: apiKey,
query: input,
searchDepth: (options?.searchDepth ?? defaultOptions.searchDepth)
.toSearchRequestSearchDepth(),
maxResults: options?.maxResults ?? defaultOptions.maxResults,
includeRawContent:
options?.includeRawContent ?? defaultOptions.includeRawContent,
includeDomains:
options?.includeDomains ?? defaultOptions.includeDomains,
excludeDomains:
options?.excludeDomains ?? defaultOptions.excludeDomains,
),
);
return TavilySearchResults(
results: res.results
.map((r) => r.toTavilySearchResult())
.toList(growable: false),
);
}

@override
String getInputFromJson(final Map<String, dynamic> json) {
return json['query'] as String;
}

@override
void close() {
_client.endSession();
}
}
Loading

0 comments on commit a9f3575

Please sign in to comment.