FabrCore Goes Multi-Model — Grok, Gemini, OpenAI, and Azure in One Runtime
Not every task needs the same model. A fast classifier can run on GPT-4o-mini, a complex reasoning chain fits better on o1, and a cost-sensitive batch job might target Grok or Gemini. With FabrCore's ModelConfigurations in fabrcore.json, you define all your LLM providers in one file and agents reference them by name — no code changes when you swap providers.
The Configuration File
All LLM provider configuration lives in fabrcore.json, placed in the server project root. The file has two sections: ModelConfigurations (one entry per model) and ApiKeys (referenced by alias). This file should be added to .gitignore since it contains API keys.
{
"ModelConfigurations": [
{
"Name": "string", // Required: unique model name
"Provider": "string", // Required: OpenAI | Azure | Grok | Gemini
"Uri": "string", // Required for Azure, optional for others
"Model": "string", // Required: model identifier
"ApiKeyAlias": "string", // Required: references an ApiKeys entry
"TimeoutSeconds": 120, // Optional: HTTP timeout
"MaxOutputTokens": 16384, // Optional: max response tokens
"ContextWindowTokens": 128000 // Optional: total context window
}
],
"ApiKeys": [
{
"Alias": "string",
"Value": "string"
}
]
}
Each model entry also supports compaction settings: CompactionEnabled, CompactionKeepLastN, and CompactionThreshold. These control when chat history is summarized to stay within the context window.
Configuring Each Provider
FabrCore supports five providers out of the box. Any OpenAI-compatible endpoint can also be used by setting Provider: "OpenAI" with a custom Uri.
| Provider | Uri Required | Notes |
|---|---|---|
OpenAI | No | Uses default OpenAI endpoint |
Azure | Yes | Azure OpenAI resource URL |
OpenRouter | No | Uses OpenRouter endpoint |
Grok | No | xAI Grok models |
Gemini | No | Google Gemini models |
OpenAI
{
"ModelConfigurations": [
{
"Name": "default",
"Provider": "OpenAI",
"Model": "gpt-4o",
"ApiKeyAlias": "openai-key",
"TimeoutSeconds": 120,
"MaxOutputTokens": 16384,
"ContextWindowTokens": 128000
}
],
"ApiKeys": [
{ "Alias": "openai-key", "Value": "sk-..." }
]
}
Azure OpenAI
{
"ModelConfigurations": [
{
"Name": "azure-gpt4",
"Provider": "Azure",
"Uri": "https://your-resource.openai.azure.com/",
"Model": "gpt-4o",
"ApiKeyAlias": "azure-key"
}
],
"ApiKeys": [
{ "Alias": "azure-key", "Value": "your-key-here" }
]
}
Grok & Gemini
{
"ModelConfigurations": [
{
"Name": "grok",
"Provider": "Grok",
"Model": "grok-3",
"ApiKeyAlias": "xai-key"
},
{
"Name": "gemini",
"Provider": "Gemini",
"Model": "gemini-2.0-flash",
"ApiKeyAlias": "google-key"
}
],
"ApiKeys": [
{ "Alias": "xai-key", "Value": "xai-..." },
{ "Alias": "google-key", "Value": "AIza..." }
]
}
Using Models in Agents
Agents reference models by their Name field through the config.Models property. When you call CreateChatClientAgent in OnInitialize, you pass the model name and the framework resolves it from fabrcore.json:
public override async Task OnInitialize()
{
var tools = await ResolveConfiguredToolsAsync();
// config.Models comes from AgentConfiguration — e.g., "grok" or "gemini"
var result = await CreateChatClientAgent(
chatClientConfigName: config.Models ?? "default",
threadId: config.Handle ?? fabrcoreAgentHost.GetHandle(),
tools: tools);
_agent = result.Agent;
_session = result.Session;
}
The AgentConfiguration is where the model name is assigned. You can set it when creating agents via the REST API, in system agent startup code, or from ChatDock's AdditionalArgs:
// Fast classifier on GPT-4o-mini
await _agentService.ConfigureSystemAgentAsync(new AgentConfiguration
{
Handle = "classifier",
AgentType = "classifier-agent",
Models = "fast",
SystemPrompt = "Classify incoming requests into categories."
});
// Deep reasoning on Grok
await _agentService.ConfigureSystemAgentAsync(new AgentConfiguration
{
Handle = "analyst",
AgentType = "analysis-agent",
Models = "grok",
SystemPrompt = "Perform deep analysis on financial data."
});
This separation of model configuration from agent code means you can switch an agent from OpenAI to Gemini by changing one string in the configuration — no recompilation, no code changes. Different agents in the same runtime can use different providers simultaneously.
Putting It All Together
A production fabrcore.json typically defines several models with different cost/performance tradeoffs. Here is an example with four providers and an embeddings model:
{
"ModelConfigurations": [
{
"Name": "default",
"Provider": "OpenAI",
"Model": "gpt-4o",
"ApiKeyAlias": "openai",
"ContextWindowTokens": 128000
},
{
"Name": "fast",
"Provider": "OpenAI",
"Model": "gpt-4o-mini",
"ApiKeyAlias": "openai",
"CompactionKeepLastN": 10,
"CompactionThreshold": 0.6
},
{
"Name": "grok",
"Provider": "Grok",
"Model": "grok-3",
"ApiKeyAlias": "xai"
},
{
"Name": "gemini",
"Provider": "Gemini",
"Model": "gemini-2.0-flash",
"ApiKeyAlias": "google"
},
{
"Name": "embeddings",
"Provider": "OpenAI",
"Model": "text-embedding-3-small",
"ApiKeyAlias": "openai"
}
],
"ApiKeys": [
{ "Alias": "openai", "Value": "sk-..." },
{ "Alias": "xai", "Value": "xai-..." },
{ "Alias": "google", "Value": "AIza..." }
]
}
API keys are shared across model entries via ApiKeyAlias — define a key once and reference it from multiple models. The "embeddings" named model is special: it is automatically picked up by the IEmbeddings service and the /fabrcoreapi/embeddings API endpoint.
With this setup, a router agent running on the "fast" model can classify requests and delegate to a Grok-backed analyst or a Gemini-backed summarizer — all within the same Orleans cluster, sharing the same infrastructure, with each agent using the model best suited to its task.
Built with FabrCore on .NET 10.
Builder of FabrCore and OpenCaddis.