Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
45 changes: 44 additions & 1 deletion src/Aevatar.AI.Core/Chat/ChatRuntimeRequestBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,14 @@ public static LLMRequest Build(
catalogProof?.AssertMatchesExactTools(exactTools ?? []);
}

var callerContext = ResolveCallerContext(baseRequest.CallerContext, effectiveToolContext);

return new LLMRequest
{
Messages = baseRequest.Messages,
RequestId = string.IsNullOrWhiteSpace(requestId) ? baseRequest.RequestId : requestId.Trim(),
Metadata = AgentToolExecutionContextMapper.StripOwnedControlKeys(mergedMetadata),
CallerContext = baseRequest.CallerContext,
CallerContext = callerContext,
ToolContext = effectiveToolContext,
RoutingContext = effectiveLlmControl?.ToRoutingContext(baseRequest.RoutingContext) ?? baseRequest.RoutingContext,
LlmControl = effectiveLlmControl,
Expand Down Expand Up @@ -181,6 +183,47 @@ internal static AuthorizationFence CaptureAuthorizationFence(LLMRequest request)
return merged;
}

private static LLMRequestCallerContext? ResolveCallerContext(
LLMRequestCallerContext? baseCallerContext,
AgentToolExecutionContext toolContext)
{
var credential = string.IsNullOrWhiteSpace(baseCallerContext?.Credentials?.NyxIdBearer)
? ResolvePromotableNyxIdCredential(toolContext.Credentials)
: baseCallerContext.Credentials.NyxIdBearer.Trim();
Comment thread
louis4li marked this conversation as resolved.
var credentials = string.IsNullOrWhiteSpace(credential)
? baseCallerContext?.Credentials
: new LLMRequestCallerCredentials(credential);
if (baseCallerContext is not null)
{
return baseCallerContext with
{
Credentials = credentials,
};
}

if (credentials is null &&
string.IsNullOrWhiteSpace(toolContext.Caller.ScopeId) &&
string.IsNullOrWhiteSpace(toolContext.Caller.OwnerSubject) &&
string.IsNullOrWhiteSpace(toolContext.Caller.ResponseId))
{
return null;
}

return new LLMRequestCallerContext(
Normalize(toolContext.Caller.ScopeId) ?? string.Empty,
Normalize(toolContext.Caller.OwnerSubject) ?? string.Empty,
Normalize(toolContext.Caller.ResponseId),
credentials);
}

private static string? ResolvePromotableNyxIdCredential(AgentToolCredentials credentials) =>
credentials.NyxIdCredentialKind == AgentToolNyxIdCredentialKind.AgentKey
? Normalize(credentials.NyxIdAccessToken)
: null;

private static string? Normalize(string? value) =>
string.IsNullOrWhiteSpace(value) ? null : value.Trim();

internal sealed class AuthorizationFence
{
private readonly IReadOnlyDictionary<string, IAgentTool> _schemaTools;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Aevatar.AI.Abstractions.LLMProviders;
using Aevatar.AI.Abstractions.ToolProviders;
using Aevatar.AI.Core.Chat;
using FluentAssertions;

namespace Aevatar.AI.Tests;

public sealed class ChatRuntimeRequestBuilderCredentialTests
{
[Fact]
public void Build_WhenToolContextHasAgentKeyCredential_ShouldPromoteCallerBearer()
{
var request = BuildRequest(AgentToolNyxIdCredentialKind.AgentKey);

request.CallerContext.Should().NotBeNull();
request.CallerContext!.Credentials.Should().NotBeNull();
request.CallerContext.Credentials!.NyxIdBearer.Should().Be("nyxid_ag_alpha");
}

[Fact]
public void Build_WhenToolContextCredentialKindIsUnspecified_ShouldNotPromoteCallerBearer()
{
var request = BuildRequest(AgentToolNyxIdCredentialKind.Unspecified);

request.CallerContext.Should().NotBeNull();
request.CallerContext!.Credentials.Should().BeNull();
}

private static LLMRequest BuildRequest(AgentToolNyxIdCredentialKind credentialKind)
{
var toolContext = AgentToolExecutionContext.Empty with
{
Credentials = new AgentToolCredentials(
" nyxid_ag_alpha ",
null,
null,
credentialKind),
Caller = new AgentToolCallerContext(" scope-alpha ", " owner-alpha ", " response-alpha "),
};

return ChatRuntimeRequestBuilder.Build(
new LLMRequest
{
Messages = [new ChatMessage { Role = "user", Content = "hello" }],
},
null,
null,
toolContext,
null,
null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ await agent.HandleWorkflowLlmExecutionIntent(new WorkflowLlmExecutionIntent
provider.LastRequest.LlmControl.Should().NotBeNull();
provider.LastRequest.LlmControl!.NyxIdAccessToken.Should().BeNull(
"the durable Agent Key should only enter the ephemeral tool context");
provider.LastRequest.CallerContext.Should().NotBeNull();
provider.LastRequest.CallerContext!.Credentials.Should().NotBeNull();
provider.LastRequest.CallerContext.Credentials!.NyxIdBearer.Should().Be(agentKey);
}

[Fact]
Expand Down Expand Up @@ -217,6 +220,9 @@ await agent.HandleWorkflowLlmExecutionIntent(new WorkflowLlmExecutionIntent
provider.LastRequest.LlmControl.Should().NotBeNull();
provider.LastRequest.LlmControl!.NyxIdAccessToken.Should().BeNull(
"the durable Agent Key should only enter the ephemeral tool context");
provider.LastRequest.CallerContext.Should().NotBeNull();
provider.LastRequest.CallerContext!.Credentials.Should().NotBeNull();
provider.LastRequest.CallerContext.Credentials!.NyxIdBearer.Should().Be(agentKey);
}

[Fact]
Expand Down
Loading