Skip to content

AI Endpoints — REST API for Workspaces, Usage & Inference

Granit.AI.Endpoints exposes the AI module’s capabilities via Minimal API endpoints. It covers workspace administration, usage analytics, and inference proxy — all protected by granular permission policies.

  • DirectoryGranit.AI.Endpoints/
    • DirectoryEndpoints/
      • AIWorkspaceEndpoints.cs Workspace CRUD (list, get, create, update, delete)
      • AIChatEndpoints.cs Chat completion (sync + SSE streaming)
      • AIEmbeddingEndpoints.cs Embedding generation proxy
    • DirectoryQueries/
      • AIUsageRecordQueryDefinition.cs Usage tracking via Granit.QueryEngine
    • DirectoryDtos/ Request/response records
    • DirectoryValidators/ FluentValidation (auto-applied via MapGranitGroup)
    • DirectoryPermissions/ Granular AI.* permission constants
    • DirectoryOptions/ AIEndpointsOptions (route prefix, roles, limits)
    • DirectoryLocalization/ 17 cultures
Program.cs
builder.AddGranitAI(); // Core abstractions
builder.AddGranitAIEntityFrameworkCore(o => ...) // Persistence (workspaces + usage)
builder.AddGranitAIOpenAI(); // Provider (or AzureOpenAI, Ollama...)
// Route registration
app.MapAIEndpoints();
// With custom options:
app.MapAIEndpoints(opts =>
{
opts.RoutePrefix = "api/ai";
opts.AdminRole = "platform-admin";
opts.UserRole = "ai-consumer";
});

Two authorization policies, configurable via AIEndpointsOptions:

PolicyDefault roleEndpoints
Admingranit-ai-adminWorkspace CRUD, usage query
Usergranit-ai-userChat completion, embedding generation
MethodRouteDescription
GET/ai/workspacesList all workspaces (system + dynamic)
GET/ai/workspaces/{name}Get a workspace by name
POST/ai/workspacesCreate a dynamic workspace
PUT/ai/workspaces/{name}Update a dynamic workspace
DELETE/ai/workspaces/{name}Soft-delete a dynamic workspace

System workspaces (declared in code) are read-only. Attempting to modify or delete them returns 422 Unprocessable Entity with AI:Workspace:SystemImmutable.

Usage tracking (Admin) via Granit.QueryEngine

Section titled “Usage tracking (Admin) via Granit.QueryEngine”

Instead of custom listing endpoints, usage records are exposed through MapQueryEndpoints<AIUsageRecord> — providing filtering, sorting, pagination, groupBy with aggregates, saved views, and metadata out of the box.

RouteFeatures
GET /ai/usage/queryPaginated query with filters
GET /ai/usage/query/metaQuery metadata for frontend
GET /ai/usage/query/saved-viewsUser’s saved views

Query definition schema:

FeatureFields
FilterableWorkspaceName, Provider, Model, Timestamp
SortableTimestamp (default: -timestamp), InputTokens, OutputTokens, EstimatedCostUsd
GlobalSearchWorkspaceName, Provider, Model
GroupByWorkspaceName, Provider, Model
AggregatesSum(InputTokens), Sum(OutputTokens), Sum(EstimatedCostUsd)
DateFilterTimestamp (ThisMonth default)

Usage summary is achieved via ?groupBy=provider or ?groupBy=workspaceName — the query engine computes aggregates automatically.

MethodRouteDescription
POST/ai/chat/{workspaceName}Synchronous chat completion
POST/ai/chat/{workspaceName}/streamSSE streaming completion
// POST /ai/chat/my-gpt4
{
"messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "What is Granit?" }
]
}
// Response 200
{
"workspaceName": "my-gpt4",
"model": "gpt-4o",
"content": "Granit is a modular .NET framework...",
"usage": { "inputTokens": 42, "outputTokens": 128, "estimatedCostUsd": null },
"duration": "00:00:01.234"
}

After each successful completion, IAIUsageTracker.RecordAsync logs the interaction for ISO 27001 audit trail and cost monitoring.

MethodRouteDescription
POST/ai/embeddings/{workspaceName}Generate embeddings for text inputs
// POST /ai/embeddings/ada-embed
{ "inputs": ["Hello world", "Granit framework"] }
// Response 200
{
"workspaceName": "ada-embed",
"model": "text-embedding-3-small",
"embeddings": [
{ "index": 0, "vector": [0.0123, -0.0456, ...] },
{ "index": 1, "vector": [0.0789, -0.0012, ...] }
]
}

All errors follow RFC 7807 (ProblemDetails):

ScenarioHTTPDetail key
Workspace not found404AI:Workspace:NotFound
Provider not registered502AI:Provider:NotRegistered
Rate limit exceeded429AI:RateLimit:Exceeded
Provider unavailable503AI:Service:Unavailable
System workspace modification422AI:Workspace:SystemImmutable
Validation failure400Auto via FluentValidationAutoEndpointFilter

All request DTOs are auto-validated via MapGranitGroup:

DTORules
AIWorkspaceCreateRequestName: ^[a-z0-9][a-z0-9-]*$, max 128; Provider/Model: required; Temperature: 0–2; MaxOutputTokens: > 0
AIWorkspaceUpdateRequestSame as create (without name)
AIChatRequestMessages: required, max 100; Role: user/assistant/system; Content: max 128k
AIEmbeddingRequestInputs: required, max 50; each max 32k
AIPermissions.Workspaces.View // "AI.Workspaces.View"
AIPermissions.Workspaces.Create // "AI.Workspaces.Create"
AIPermissions.Workspaces.Update // "AI.Workspaces.Update"
AIPermissions.Workspaces.Delete // "AI.Workspaces.Delete"
AIPermissions.Usage.View // "AI.Usage.View"
AIPermissions.Chat.Execute // "AI.Chat.Execute"
AIPermissions.Embeddings.Execute // "AI.Embeddings.Execute"
{
"AIEndpoints": {
"RoutePrefix": "ai",
"AdminRole": "granit-ai-admin",
"UserRole": "granit-ai-user",
"MaxChatMessages": 100,
"MaxEmbeddingInputs": 50
}
}