Home / Docs / Client

Client

Client

The FabrCore.Client package provides Blazor Server (Interactive Server) integration for communicating with the FabrCore agent cluster. It handles Orleans grain communication, observer subscriptions, and resource lifecycle management — designed specifically for Blazor's interactive server-side rendering model.

Setup

Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.AddFabrCoreClient();
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

var app = builder.Build();

app.UseFabrCoreClient();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.Run();
appsettings.json — Orleans Connection
{
  "Orleans": {
    "ClusteringMode": "Localhost",
    "ClusterId": "fabrcore-cluster",
    "ServiceId": "fabrcore-service"
  },
  "FabrHostUrl": "http://localhost:5000"
}

ClientContext

ClientContext encapsulates the complexity of Orleans communication, observer subscriptions, and resource lifecycle. Use it for custom chat interfaces.

Factory Interface

MethodDescriptionOwnership
CreateAsync(handle)Create a new, independent contextCaller responsible for disposal
GetOrCreateAsync(handle)Return cached context or create newFactory manages lifecycle
ReleaseAsync(handle)Remove and dispose cached contextN/A
HasContext(handle)Check if cached context existsN/A
Blazor Server Interactive Only

FabrCore.Client relies on persistent SignalR connections for Orleans observer subscriptions and real-time agent communication. This requires Blazor's Interactive Server render mode — it is not compatible with Blazor WebAssembly, static SSR, or standalone WebAPI projects.

Usage Pattern

Cached Context Per User
@inject IClientContextFactory ClientContextFactory
@implements IAsyncDisposable

@code {
    private IClientContext? _context;

    protected override async Task OnInitializedAsync()
    {
        _context = await ClientContextFactory
            .GetOrCreateAsync(userId);
        _context.AgentMessageReceived += OnMessageReceived;
    }

    public ValueTask DisposeAsync()
    {
        if (_context != null)
            _context.AgentMessageReceived -= OnMessageReceived;
        return ValueTask.CompletedTask;
    }
}

Direct Messaging

For lightweight fire-and-forget messages without full ClientContext overhead:

IDirectMessageSender
public class NotificationService
{
    private readonly IDirectMessageSender _sender;

    public async Task NotifyAgent(string handle, string text)
    {
        await _sender.SendMessageAsync(new AgentMessage
        {
            FromHandle = "notification-service",
            ToHandle = handle,
            Message = text,
            MessageType = "notification"
        });
    }
}

Health Monitoring

Check agent health from the client:

Health Check
var health = await context.GetAgentHealth(
    "my-agent", HealthDetailLevel.Full);

switch (health.State)
{
    case HealthState.Healthy:
        Console.WriteLine("Agent is ready");
        break;
    case HealthState.Degraded:
        Console.WriteLine($"Issues: {health.Message}");
        break;
}

Client State Persistence

The client automatically persists tracked agents and pending messages to Orleans grain storage:

DataWhen PersistedBehavior
Tracked AgentsImmediately on CreateAgent()Survives grain deactivation
Pending MessagesOn grain deactivationMessages older than 1 hour discarded on restore

OrleansClusterOptions

Configure the Orleans cluster connection used by the client to reach the FabrCore server.

PropertyTypeDefaultDescription
ClusterIdstring"fabrcore-cluster"Membership cluster ID — must match server
ServiceIdstring"fabrcore-service"Service ID — must match server
ClusteringModeClusteringModeLocalhostProvider: Localhost, SqlServer, AzureStorage
ConnectionStringstring?nullRequired for SqlServer and AzureStorage modes
ConnectionRetryCountint5Max retry attempts (0 = no retries)
ConnectionRetryDelayTimeSpan00:00:03Delay between connection retries
GatewayListRefreshPeriodTimeSpan00:00:30Gateway list refresh timeout
ClusteringMode Enum
public enum ClusteringMode
{
    Localhost,      // Development (in-memory)
    SqlServer,      // Production (SQL Server)
    AzureStorage    // Azure (Table Storage)
}

Connection Retry Configuration

appsettings.json — Full Options
{
  "Orleans": {
    "ClusterId": "fabrcore-cluster",
    "ServiceId": "fabrcore-service",
    "ClusteringMode": "Localhost",
    "ConnectionRetryCount": 10,
    "ConnectionRetryDelay": "00:00:05",
    "GatewayListRefreshPeriod": "00:00:30"
  }
}

IFabrHostApiClient

A typed HTTP client for the FabrCore Host REST API. Automatically registered when calling AddFabrCoreClient(). Configure the host URL in appsettings.json.

Interface Definition
public interface IFabrHostApiClient
{
    // Agent Operations
    Task<CreateAgentsResponse> CreateAgentsAsync(string userId,
        IEnumerable<AgentConfiguration> configs, ...);
    Task<AgentHealthStatus> GetAgentHealthAsync(string userId,
        string handle, ...);
    Task<AgentMessage> ChatAsync(string userId,
        string handle, string message, ...);
    Task<AgentsListResponse> GetAgentsAsync(...);
    Task<AgentInfo?> GetAgentAsync(string key, ...);
    Task<AgentStatisticsResponse> GetAgentStatisticsAsync(...);
    Task<PurgeAgentsResponse> PurgeOldAgentsAsync(int olderThanHours, ...);

    // Configuration
    Task<ModelConfigResponse> GetModelConfigAsync(string name, ...);
    Task<ApiKeyResponse> GetApiKeyAsync(string alias, ...);

    // File Operations
    Task<string> UploadFileAsync(Stream fileStream,
        string fileName, int ttlSeconds = 3600, ...);
    Task<Stream?> GetFileAsync(string fileId, ...);
    Task<FileMetadataResponse?> GetFileInfoAsync(string fileId, ...);

    // Embeddings
    Task<EmbeddingResponse> GetEmbeddingsAsync(string text, ...);
    Task<BatchEmbeddingResponse> GetBatchEmbeddingsAsync(
        List<BatchEmbeddingItem> items, ...);
}

Response Types

TypeProperties
ModelConfigResponseName, Provider, Uri, Model, ApiKeyAlias
ApiKeyResponseValue
AgentsListResponseCount, Agents
AgentStatisticsResponseStatistics (Dictionary)
PurgeAgentsResponsePurgedCount, Message
FileMetadataResponseFileId, OriginalFileName, ExpiresAt, FileSize, ContentType
EmbeddingResponseVector, Dimensions
BatchEmbeddingItemId, Text
BatchEmbeddingRequestItems (List<BatchEmbeddingItem>)
BatchEmbeddingResultItemId, Vector (float[]), Dimensions
BatchEmbeddingResponseResults (List<BatchEmbeddingResultItem>)
CreateAgentsResponseTotalRequested, SuccessCount, FailureCount, Results

Usage Example

AgentConfigService.cs
public class AgentConfigService
{
    private readonly IFabrHostApiClient _apiClient;

    public AgentConfigService(IFabrHostApiClient apiClient)
        => _apiClient = apiClient;

    public async Task<IChatClient> CreateChatClientAsync(string modelName)
    {
        var modelConfig = await _apiClient.GetModelConfigAsync(modelName);
        var apiKey = await _apiClient.GetApiKeyAsync(modelConfig.ApiKeyAlias);

        return new OpenAIChatClient(
            model: modelConfig.Model,
            apiKey: apiKey.Value);
    }
}

WebAPI Pattern

Use ClientContextFactory in WebAPI controllers for per-request agent communication:

ChatController.cs — Per-Request Context
[ApiController]
[Route("api/[controller]")]
public class ChatController : ControllerBase
{
    private readonly IClientContextFactory _factory;

    public ChatController(IClientContextFactory factory)
        => _factory = factory;

    [HttpPost("send")]
    public async Task<IActionResult> SendMessage(
        [FromBody] ChatRequest request)
    {
        var userId = User
            .FindFirst(ClaimTypes.NameIdentifier)?.Value
            ?? throw new UnauthorizedAccessException();

        await using var context =
            await _factory.CreateAsync(userId);

        var response = await context
            .SendAndReceiveMessage(new AgentMessage
            {
                FromHandle = userId,
                ToHandle = request.AgentHandle,
                Message = request.Message
            });

        return Ok(new { response.Message });
    }
}

Telemetry

FabrCore.Client includes comprehensive OpenTelemetry instrumentation for observability.

Activity Sources

SourceDescription
FabrCore.Client.ExtensionsClient configuration and setup
FabrCore.Client.ConnectionOrleans connection lifecycle
FabrCore.Client.FabrHostApiClientHTTP API client operations
FabrCore.Client.ClientContextContext operations
FabrCore.Client.ClientContextFactoryFactory operations
FabrCore.Client.DirectMessageSenderDirect messaging operations
FabrCore.Client.FabrCoreClientAgentProxyAgent proxy operations

Connection Metrics

MetricTypeDescription
fabrcore.client.connection.retriesCounterConnection retry attempts
fabrcore.client.connection.retry_attemptsCounterIndividual retry operations
fabrcore.client.connection.successCounterSuccessful connections
fabrcore.client.connection.failuresCounterFailed connections

API Client Metrics

MetricTypeDescription
fabrcore.api_client.requestsCounterAPI requests made
fabrcore.api_client.errorsCounterAPI errors
fabrcore.api_client.request.durationHistogramAPI request duration

ClientContextFactory Metrics

MetricTypeDescription
fabrcore.client.factory.contexts.createdCounterTotal contexts created
fabrcore.client.factory.contexts.cachedCounterContexts served from cache
fabrcore.client.factory.contexts.releasedCounterContexts released/disposed
fabrcore.client.factory.creation.durationHistogramContext creation time
fabrcore.client.factory.errorsCounterFactory errors

Direct Messaging Metrics

MetricTypeDescription
fabrcore.direct.messages.sentCounterDirect messages sent
fabrcore.direct.events.sentCounterDirect events sent
fabrcore.direct.errorsCounterDirect messaging errors

Enabling Telemetry

Program.cs
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddSource("FabrCore.Client.*"))
    .WithMetrics(metrics => metrics
        .AddMeter("FabrCore.Client.*"));
Documentation