Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement experimental ai-powered search #398

Merged
merged 13 commits into from
Jan 11, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ First of all, thank you for contributing to Meilisearch! The goal of this docume
- [Setup ](#setup-)
- [Tests and Linter ](#tests-and-linter-)
- [Updating code samples](#updating-code-samples)
- [Run unit tests for embedders](#run-unit-tests-for-embedders)
- [Git Guidelines](#git-guidelines)
- [Git Branches ](#git-branches-)
- [Git Commits ](#git-commits-)
Expand Down Expand Up @@ -103,6 +104,22 @@ The process to define a new code sample is as follows:
```bash
dart run ./tool/bin/meili.dart update-samples --fail-on-change
```

### Run unit tests for embedders

Unit tests for embedders are located in `test/search_test.dart`

#### OpenAI Model Integration
The tests utilize OpenAI models for embedding functionalities. Ensure you have a valid OpenAI API key to run these tests.

- Generate an OpenAI API Key
- Provide the API Key
- You can provide the OpenAI API key in one of two ways:
- Pass the key via terminal by setting an environment variable: `export OPEN_AI_API_KEY=your_openai_api_key`
- This will not work for flutter web when running unit tests
- Set the key directly in the `openAiKey` variable located in: `test/utils/client.dart`
- You can run and find the embedders unit tests in `test/search_test.dart`

## Git Guidelines

### Git Branches <!-- omit in TOC -->
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,6 @@ final res = await index

This package guarantees compatibility with [version v1.x of Meilisearch](https://github.com/meilisearch/meilisearch/releases/tag/latest), but some features may not be present. Please check the [issues](https://github.com/meilisearch/meilisearch-dart/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22+label%3Aenhancement) for more info.

⚠️ This package is not compatible with the [`vectoreStore` experimental feature](https://www.meilisearch.com/docs/learn/experimental/vector_search) of Meilisearch v1.6.0 and later. More information on this [issue](https://github.com/meilisearch/meilisearch-dart/issues/369).

## 💡 Learn more

The following sections in our main documentation website may interest you:
Expand Down
2 changes: 2 additions & 0 deletions lib/meilisearch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ export 'src/client.dart';
export 'src/index.dart';
export 'src/exception.dart';
export 'src/facade.dart';
export 'src/settings/distribution.dart';
export 'src/settings/embedder.dart';
29 changes: 29 additions & 0 deletions lib/src/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,35 @@ class MeiliSearchIndex {
);
}

/// Get the embedders settings of a Meilisearch index.
@RequiredMeiliServerVersion('1.6.0')
Future<Map<String, Embedder>?> getEmbedders() async {
final response = await http
.getMethod<Map<String, Object?>>('/indexes/$uid/settings/embedders');

return response.data?.map(
(k, v) => MapEntry(k, Embedder.fromMap(v as Map<String, Object?>)));
}

/// Update the embedders settings. Overwrite the old settings.
@RequiredMeiliServerVersion('1.6.0')
Future<Task> updateEmbedders(Map<String, Embedder>? embedders) async {
return await _getTask(
http.putMethod(
'/indexes/$uid/settings/embedders',
data: embedders?.map((k, v) => MapEntry(k, v.toMap())),
),
);
}

/// Reset the embedders settings to its default value
@RequiredMeiliServerVersion('1.6.0')
Future<Task> resetEmbedders() async {
return await _getTask(
http.deleteMethod('/indexes/$uid/settings/embedders'),
);
}

//
// StopWords endpoints
//
Expand Down
1 change: 1 addition & 0 deletions lib/src/query_parameters/_exports.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export 'multi_search_query.dart';
export 'delete_documents_query.dart';
export 'facet_search_query.dart';
export 'swap_index.dart';
export 'hybrid_search.dart';
17 changes: 17 additions & 0 deletions lib/src/query_parameters/hybrid_search.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class HybridSearch {
final String embedder;
final double semanticRatio;

const HybridSearch({
required this.embedder,
required this.semanticRatio,
}) : assert(
semanticRatio >= 0.0 && semanticRatio <= 1.0,
"'semanticRatio' must be between 0.0 and 1.0",
);

Map<String, Object?> toMap() => {
'embedder': embedder,
'semanticRatio': semanticRatio,
};
}
4 changes: 4 additions & 0 deletions lib/src/query_parameters/index_search_query.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import '../annotations.dart';
import '../filter_builder/_exports.dart';
import '../results/matching_strategy_enum.dart';
import 'hybrid_search.dart';
import 'search_query.dart';

@RequiredMeiliServerVersion('1.1.0')
Expand Down Expand Up @@ -29,6 +30,7 @@ class IndexSearchQuery extends SearchQuery {
super.highlightPostTag,
super.matchingStrategy,
super.attributesToSearchOn,
super.hybrid,
super.showRankingScore,
super.vector,
super.showRankingScoreDetails,
Expand Down Expand Up @@ -65,6 +67,7 @@ class IndexSearchQuery extends SearchQuery {
String? highlightPostTag,
MatchingStrategy? matchingStrategy,
List<String>? attributesToSearchOn,
HybridSearch? hybrid,
bool? showRankingScore,
List<dynamic /* double | List<double> */ >? vector,
bool? showRankingScoreDetails,
Expand All @@ -91,6 +94,7 @@ class IndexSearchQuery extends SearchQuery {
highlightPostTag: highlightPostTag ?? this.highlightPostTag,
matchingStrategy: matchingStrategy ?? this.matchingStrategy,
attributesToSearchOn: attributesToSearchOn ?? this.attributesToSearchOn,
hybrid: hybrid ?? this.hybrid,
showRankingScore: showRankingScore ?? this.showRankingScore,
vector: vector ?? this.vector,
showRankingScoreDetails:
Expand Down
6 changes: 6 additions & 0 deletions lib/src/query_parameters/search_query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class SearchQuery extends Queryable {
final String? highlightPostTag;
final MatchingStrategy? matchingStrategy;
final List<String>? attributesToSearchOn;
@RequiredMeiliServerVersion('1.6.0')
final HybridSearch? hybrid;
@RequiredMeiliServerVersion('1.3.0')
final bool? showRankingScore;
@RequiredMeiliServerVersion('1.3.0')
Expand All @@ -47,6 +49,7 @@ class SearchQuery extends Queryable {
this.highlightPostTag,
this.matchingStrategy,
this.attributesToSearchOn,
this.hybrid,
this.showRankingScore,
this.showRankingScoreDetails,
this.vector,
Expand All @@ -72,6 +75,7 @@ class SearchQuery extends Queryable {
'highlightPostTag': highlightPostTag,
'matchingStrategy': matchingStrategy?.name,
'attributesToSearchOn': attributesToSearchOn,
'hybrid': hybrid?.toMap(),
'showRankingScore': showRankingScore,
'showRankingScoreDetails': showRankingScoreDetails,
'vector': vector,
Expand All @@ -97,6 +101,7 @@ class SearchQuery extends Queryable {
String? highlightPostTag,
MatchingStrategy? matchingStrategy,
List<String>? attributesToSearchOn,
HybridSearch? hybrid,
bool? showRankingScore,
List<dynamic>? vector,
bool? showRankingScoreDetails,
Expand All @@ -121,6 +126,7 @@ class SearchQuery extends Queryable {
highlightPostTag: highlightPostTag ?? this.highlightPostTag,
matchingStrategy: matchingStrategy ?? this.matchingStrategy,
attributesToSearchOn: attributesToSearchOn ?? this.attributesToSearchOn,
hybrid: hybrid ?? this.hybrid,
showRankingScore: showRankingScore ?? this.showRankingScore,
vector: vector ?? this.vector,
showRankingScoreDetails:
Expand Down
21 changes: 21 additions & 0 deletions lib/src/settings/distribution.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Distribution {
final double mean;
final double sigma;

Distribution({
required this.mean,
required this.sigma,
});

factory Distribution.fromMap(Map<String, Object?> map) {
return Distribution(
mean: map['mean'] as double,
sigma: map['sigma'] as double,
);
}

Map<String, Object?> toMap() => {
'mean': mean,
'sigma': sigma,
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this is used in query parameters, it should automatically exclude null values (as per meilisearch conventions), the logic for this is already present in Queryable class, so it can be uesd.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Distribution isn't used in the query parameters; it is a field of some embedders, and the fields in this class aren't actually nullable.

}
Loading
Loading