Added tool calling support (#731)

Co-authored-by: krut_ni <nils.kruthoff@dlr.de>
Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-09-04 15:48:07 +02:00
1 parent b00c3f9ab3
commit 4d8d30e15e
266 files changed
+11186 -740

No files matched your search

@@ -13,6 +13,15 @@ public sealed partial class RustService
/// </remarks>
private static readonly TimeSpan SANITIZE_TIMEOUT = TimeSpan.FromSeconds(30);
/// <summary>
/// How long one batch of sanitize requests may take.
/// </summary>
/// <remarks>
/// A batch carries every text of one tool call, such as all pages of a web search, so it is
/// given more room than a single text.
/// </remarks>
private static readonly TimeSpan SANITIZE_BATCH_TIMEOUT = TimeSpan.FromSeconds(120);
/// <summary>
/// Asks the runtime to filter prompt injections out of a text.
/// </summary>
@@ -46,4 +55,53 @@ public sealed partial class RustService
return null;
}
}
/// <summary>
/// Asks the runtime to filter prompt injections out of several texts in one request.
/// </summary>
/// <remarks>
/// One tool call can produce many texts at once: a web search returns several pages, each
/// with its own content, title, description, and authors. Sending them together saves a
/// round trip per field.
/// </remarks>
/// <param name="texts">The contents to filter.</param>
/// <returns>
/// One result per text, in the same order, or null when the runtime could not be reached or
/// answered with a different number of results than were requested. Callers match results to
/// their texts by index, so a mismatched answer is unusable rather than partially usable.
/// </returns>
public async Task<IReadOnlyList<SanitizePromptInjectionsResponse>?> SanitizePromptInjectionsBatch(IReadOnlyList<string> texts)
{
if (texts.Count is 0)
return [];
try
{
using var timeoutTokenSource = new CancellationTokenSource(SANITIZE_BATCH_TIMEOUT);
using var response = await this.http.PostAsJsonAsync(
"/security/prompt-injection/sanitize-batch",
new SanitizePromptInjectionsBatchRequest(texts),
cancellationToken: timeoutTokenSource.Token);
if (!response.IsSuccessStatusCode)
{
this.logger?.LogError("Failed to check {TextCount} text(s) for prompt injections. Status: {StatusCode}, reason: '{ReasonPhrase}'", texts.Count, response.StatusCode, response.ReasonPhrase);
return null;
}
var batchResponse = await response.Content.ReadFromJsonAsync<SanitizePromptInjectionsBatchResponse>(timeoutTokenSource.Token);
if (batchResponse.Results is null || batchResponse.Results.Count != texts.Count)
{
this.logger?.LogError("The prompt injection filter answered with {ResultCount} result(s) for {TextCount} text(s).", batchResponse.Results?.Count ?? 0, texts.Count);
return null;
}
return batchResponse.Results;
}
catch (Exception exception)
{
this.logger?.LogError(exception, "Failed to check {TextCount} text(s) for prompt injections.", texts.Count);
return null;
}
}
}