Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
MEILISEARCH_VERSION=v1.9.0
MEILISEARCH_VERSION=v1.13
PROXIED_MEILISEARCH=http://nginx/api/
MEILISEARCH_URL=http://meilisearch:7700
51 changes: 51 additions & 0 deletions src/Meilisearch/Converters/EmbedderSourceConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Meilisearch.Converters
{
internal class EmbedderSourceConverter : JsonConverter<EmbedderSource>
{
public override EmbedderSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
var enumValue = reader.GetString();
if (Enum.TryParse<EmbedderSource>(enumValue, true, out var embedderSource))
{
return embedderSource;
}
}

return EmbedderSource.Unknown;
}

public override void Write(Utf8JsonWriter writer, EmbedderSource value, JsonSerializerOptions options)
{
string source;
switch (value)
{
case EmbedderSource.OpenAi:
source = "openAi";
break;
case EmbedderSource.HuggingFace:
source = "huggingFace";
break;
case EmbedderSource.Ollama:
source = "ollama";
break;
case EmbedderSource.Rest:
source = "rest";
break;
case EmbedderSource.UserProvided:
source = "userProvided";
break;
case EmbedderSource.Unknown:
default:
throw new ArgumentOutOfRangeException(nameof(value), value, null);
}

writer.WriteStringValue(source);
}
}
}
84 changes: 84 additions & 0 deletions src/Meilisearch/Embedder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace Meilisearch
{
/// <summary>
/// Embedder configuration.
/// </summary>
public class Embedder
{
/// <summary>
/// Gets or sets the source.
/// </summary>
[JsonPropertyName("source")]
public EmbedderSource Source { get; set; }

/// <summary>
/// Gets or sets the URL.
/// </summary>
[JsonPropertyName("url")]
public string Url { get; set; }

/// <summary>
/// Gets or sets the API key.
/// </summary>
[JsonPropertyName("apiKey")]
public string ApiKey { get; set; }
Comment on lines +26 to +27
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Consider security implications for ApiKey property

The ApiKey property likely contains sensitive information. Consider:

  1. Adding attributes or methods to prevent accidental logging or serialization in contexts where it might be exposed
  2. Implementing secure storage mechanisms or providing guidance on how to handle this sensitive data

Also, consider adding documentation to warn users about the sensitivity of this property.


🏁 Script executed:

#!/bin/bash
# Check for any existing security measures for API keys in the codebase
grep -r --include="*.cs" "ApiKey" --include="*.cs" "api key" src/

Length of output: 1890


Secure handling of Embedder.ApiKey

Ensure the ApiKey in src/Meilisearch/Embedder.cs cannot be accidentally serialized or logged:

  • File: src/Meilisearch/Embedder.cs (around lines 27–28)
    • Remove the [JsonPropertyName("apiKey")] attribute.
    • Add [JsonIgnore] to exclude it from JSON serialization and logging.
    • Make the setter internal or private if external mutation isn’t required.
  • Add XML documentation on the ApiKey property to warn that it holds sensitive credentials and should only be supplied via secure configuration (e.g., environment variables).

Example diff:

--- a/src/Meilisearch/Embedder.cs
+++ b/src/Meilisearch/Embedder.cs
@@ -25,8 +25,11 @@ namespace Meilisearch
-        [JsonPropertyName("apiKey")]
-        public string ApiKey { get; set; }
+        /// <summary>
+        /// API key for authentication (Sensitive: excluded from JSON; only sent via HTTP header).
+        /// </summary>
+        [JsonIgnore]
+        public string ApiKey { get; internal set; }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[JsonPropertyName("apiKey")]
public string ApiKey { get; set; }
/// <summary>
/// API key for authentication (Sensitive: excluded from JSON; only sent via HTTP header).
/// </summary>
[JsonIgnore]
public string ApiKey { get; internal set; }


/// <summary>
/// Gets or sets the model.
/// </summary>
[JsonPropertyName("model")]
public string Model { get; set; }

/// <summary>
/// Gets or sets the document template.
/// </summary>
[JsonPropertyName("documentTemplate")]
public string DocumentTemplate { get; set; }

/// <summary>
/// Gets or sets the document template max bytes.
/// </summary>
[JsonPropertyName("documentTemplateMaxBytes")]
public int? DocumentTemplateMaxBytes { get; set; }

/// <summary>
/// Gets or sets the dimensions.
/// </summary>
[JsonPropertyName("dimensions")]
public int? Dimensions { get; set; }

/// <summary>
/// Gets or sets the revision.
/// </summary>
[JsonPropertyName("revision")]
public string Revision { get; set; }

/// <summary>
/// Gets or sets the distribution.
/// </summary>
[JsonPropertyName("distribution")]
public EmbedderDistribution Distribution { get; set; }

/// <summary>
/// Gets or sets the request.
/// </summary>
[JsonPropertyName("request")]
public Dictionary<string, object> Request { get; set; }

/// <summary>
/// Gets or sets the response.
/// </summary>
[JsonPropertyName("response")]
public Dictionary<string, object> Response { get; set; }

/// <summary>
/// Gets or sets whether the vectors should be compressed.
/// </summary>
[JsonPropertyName("binaryQuantized")]
public bool? BinaryQuantized { get; set; }
}
}
48 changes: 48 additions & 0 deletions src/Meilisearch/EmbedderDistribution.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Text.Json.Serialization;

namespace Meilisearch
{
/// <summary>
/// Embedder distribution.
/// </summary>
public class EmbedderDistribution
{
/// <summary>
/// Creates a new instance of <see cref="EmbedderDistribution"/>.
/// </summary>
/// <param name="mean">Mean value between 0 and 1.</param>
/// <param name="sigma">Sigma value between 0 and 1.</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public EmbedderDistribution(double mean, double sigma)
{
if (mean < 0 || mean > 1)
{
throw new ArgumentOutOfRangeException(nameof(mean), "Mean must be between 0 and 1.");
}

if (sigma < 0 || sigma > 1)
{
throw new ArgumentOutOfRangeException(nameof(sigma), "Sigma must be between 0 and 1.");
}
}

/// <summary>
/// Gets or sets the mean.
/// </summary>
[JsonPropertyName("mean")]
public double Mean { get; set; }

/// <summary>
/// Gets or sets the sigma.
/// </summary>
[JsonPropertyName("sigma")]
public double Sigma { get; set; }

/// <summary>
/// Creates a new instance of <see cref="EmbedderDistribution"/> with a uniform distribution.
/// </summary>
/// <returns></returns>
public static EmbedderDistribution Uniform() => new EmbedderDistribution(0.5, 0.5);
}
}
43 changes: 43 additions & 0 deletions src/Meilisearch/EmbedderSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Text.Json.Serialization;

using Meilisearch.Converters;

namespace Meilisearch
{
/// <summary>
/// Embedder source.
/// </summary>
[JsonConverter(typeof(EmbedderSourceConverter))]
public enum EmbedderSource
{
/// <summary>
/// OpenAI
/// </summary>
OpenAi,

/// <summary>
/// Hugging Face
/// </summary>
HuggingFace,

/// <summary>
/// Ollama
/// </summary>
Ollama,

/// <summary>
/// REST
/// </summary>
Rest,

/// <summary>
/// User-provided
/// </summary>
UserProvided,

/// <summary>
/// Unknown
/// </summary>
Unknown
}
}
19 changes: 19 additions & 0 deletions src/Meilisearch/HybridSearch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Text.Json.Serialization;

namespace Meilisearch
{
public class HybridSearch
{
/// <summary>
/// Gets or sets the embedder.
/// </summary>
[JsonPropertyName("embedder")]
public string Embedder { get; set; }

/// <summary>
/// Gets or sets the semantic ratio.
/// </summary>
[JsonPropertyName("semanticRatio")]
public double SemanticRatio { get; set; }
}
}
63 changes: 63 additions & 0 deletions src/Meilisearch/Index.Embedders.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.Net.Http.Json;
using System.Threading;
using System.Threading.Tasks;

using Meilisearch.Extensions;

namespace Meilisearch
{
public partial class Index
{
/// <summary>
/// Gets the embedders setting.
/// </summary>
/// <param name="cancellationToken">The cancellation token for this call.</param>
/// <returns>Returns the embedders setting.</returns>
public async Task<Dictionary<string, Embedder>> GetEmbeddersAsync(CancellationToken cancellationToken = default)
{
return await _http
.GetFromJsonAsync<Dictionary<string, Embedder>>(
$"indexes/{Uid}/settings/embedders",
cancellationToken: cancellationToken)
.ConfigureAwait(false);
}

/// <summary>
/// Updates the embedders setting.
/// </summary>
/// <param name="embedders">Collection of embedders.</param>
/// <param name="cancellationToken">The cancellation token for this call.</param>
/// <returns>Returns the task info of the asynchronous task.</returns>
public async Task<TaskInfo> UpdateEmbeddersAsync(Dictionary<string, Embedder> embedders, CancellationToken cancellationToken = default)
{
var responseMessage =
await _http.PatchAsJsonAsync(
$"indexes/{Uid}/settings/embedders",
embedders,
Constants.JsonSerializerOptionsRemoveNulls,
cancellationToken: cancellationToken)
.ConfigureAwait(false);

return await responseMessage.Content
.ReadFromJsonAsync<TaskInfo>(cancellationToken: cancellationToken)
.ConfigureAwait(false);
}

/// <summary>
/// Resets the embedders setting.
/// </summary>
/// <param name="cancellationToken">The cancellation token for this call.</param>
/// <returns>Returns the task info of the asynchronous task.</returns>
public async Task<TaskInfo> ResetEmbeddersAsync(CancellationToken cancellationToken = default)
{
var response = await _http
.DeleteAsync($"indexes/{Uid}/settings/embedders", cancellationToken)
.ConfigureAwait(false);

return await response.Content
.ReadFromJsonAsync<TaskInfo>(cancellationToken: cancellationToken)
.ConfigureAwait(false);
}
}
}
33 changes: 33 additions & 0 deletions src/Meilisearch/Index.SimilarDocuments.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Net.Http.Json;
using System.Threading;
using System.Threading.Tasks;

namespace Meilisearch
{
public partial class Index
{
/// <summary>
/// Search for similar documents.
/// </summary>
/// <param name="query">The query to search for similar documents.</param>
/// <param name="cancellationToken">The cancellation token for this call.</param>
/// <typeparam name="T">The type of the documents to return.</typeparam>
/// <returns>Returns the similar documents.</returns>
public async Task<SimilarDocumentsResult<T>> SearchSimilarDocumentsAsync<T>(
SimilarDocumentsQuery query,
CancellationToken cancellationToken = default)
{
var responseMessage = await _http
.PostAsJsonAsync(
$"indexes/{Uid}/similar",
query,
Constants.JsonSerializerOptionsRemoveNulls,
cancellationToken: cancellationToken)
.ConfigureAwait(false);

return await responseMessage.Content
.ReadFromJsonAsync<SimilarDocumentsResult<T>>(cancellationToken: cancellationToken)
.ConfigureAwait(false);
}
}
}
21 changes: 21 additions & 0 deletions src/Meilisearch/Meilisearch.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,26 @@
<Compile Update="Index.SearchCutoffMs.cs">
<DependentUpon>Index.cs</DependentUpon>
</Compile>
<Compile Update="Index.Faceting.cs">
<DependentUpon>Index.cs</DependentUpon>
</Compile>
<Compile Update="Index.NonSeparatorTokens.cs">
<DependentUpon>Index.cs</DependentUpon>
</Compile>
<Compile Update="Index.Pagination.cs">
<DependentUpon>Index.cs</DependentUpon>
</Compile>
<Compile Update="Index.ProximityPrecision.cs">
<DependentUpon>Index.cs</DependentUpon>
</Compile>
<Compile Update="Index.SeparatorTokens.cs">
<DependentUpon>Index.cs</DependentUpon>
</Compile>
<Compile Update="Index.Embedders.cs">
<DependentUpon>Index.cs</DependentUpon>
</Compile>
<Compile Update="Index.SimilarDocuments.cs">
<DependentUpon>Index.cs</DependentUpon>
</Compile>
</ItemGroup>
</Project>
Loading
Loading