Files
nexus/backend/Controllers/ChatController.cs
T
AzuTear f5552218bc
CI - Build & Test / Backend (.NET) (push) Successful in 42s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 2m46s
CI - Build & Test / Security Check (push) Successful in 3s
CI - Build & Test / Deploy Nexus (push) Successful in 56s
feat: ship agent-first mission control v0.2.57
2026-07-31 22:39:47 +02:00

79 lines
3.3 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
using System.Diagnostics;
using System.Security.Claims;
using Nexus.Api.DTOs;
using Nexus.Api.Services;
namespace Nexus.Api.Controllers;
[Authorize(Roles = "owner")]
[ApiController]
[Route("api/v1/chat")]
public class ChatController(
IOpenClawChatService chat,
ILogger<ChatController> logger) : ControllerBase
{
[HttpPost]
[EnableRateLimiting("agents")]
public async Task<IResult> Chat([FromBody] ChatRequest request, CancellationToken ct)
{
var message = request.Message?.Trim();
if (string.IsNullOrWhiteSpace(message) || message.Length > 8000)
return Results.ValidationProblem(new Dictionary<string, string[]> { ["message"] = ["Message must contain between 1 and 8000 characters."] });
var agentId = string.IsNullOrWhiteSpace(request.AgentId) ? "iris" : request.AgentId.Trim().ToLowerInvariant();
if (agentId is not ("iris" or "main"))
return Results.ValidationProblem(new Dictionary<string, string[]> { ["agentId"] = ["Only iris and main are supported."] });
var conversationId = string.IsNullOrWhiteSpace(request.ConversationId)
? $"nexus-{Guid.NewGuid():N}"
: request.ConversationId.Trim();
if (conversationId.Length > 160)
return Results.ValidationProblem(new Dictionary<string, string[]> { ["conversationId"] = ["Conversation id is too long."] });
try
{
var contextualMessage = MissionControlContextFormatter.Format(message, request.Context);
var invocationContext = OpenClawInvocationContextFactory.Create(
User.FindFirst("sub")?.Value
?? User.FindFirst(ClaimTypes.NameIdentifier)?.Value
?? User.Identity?.Name,
Request.Headers["Idempotency-Key"].FirstOrDefault(),
Request.Headers["X-Correlation-ID"].FirstOrDefault()
?? HttpContext.TraceIdentifier,
Request.Headers["traceparent"].FirstOrDefault()
?? Activity.Current?.Id);
var invocation = new Nexus.Api.Models.OpenClawInvocationMetadata(
invocationContext.IdempotencyKey,
invocationContext.CorrelationId,
invocationContext.Actor,
invocationContext.TraceParent);
Response.Headers["X-Correlation-ID"] = invocation.CorrelationId;
return Results.Ok(await chat.SendAsync(
contextualMessage,
conversationId,
agentId,
invocation,
ct));
}
catch (ArgumentException exception)
{
return Results.ValidationProblem(new Dictionary<string, string[]>
{
["requestMetadata"] = [exception.Message]
});
}
catch (Exception exception)
{
logger.LogWarning(exception, "OpenClaw chat request failed for agent {AgentId}", agentId);
return Results.Problem(
title: "OpenClaw chat unavailable",
detail: "The trusted OpenClaw chat endpoint is not enabled or reachable.",
statusCode: StatusCodes.Status503ServiceUnavailable);
}
}
}