LLM model metadata catalog and cost calculator for .NET.
Provides context windows, pricing, capability flags (vision, audio, reasoning, tool calling, prompt caching), and thinking/reasoning format metadata for 12+ providers including OpenAI, Anthropic, Google, xAI, Mistral, DeepSeek, and more.
| Package | Description |
|---|---|
TokenMeter |
Full model catalog + cost calculation |
dotnet add package TokenMeter// Find a model by ID or alias
var model = ModelCatalog.FindModel("claude-sonnet-4-6");
Console.WriteLine(model?.ContextWindow); // 1000000
Console.WriteLine(model?.ReasoningMode); // Optional
Console.WriteLine(model?.ThinkingFormat); // Block
Console.WriteLine(model?.PromptCachingMode); // Explicit
Console.WriteLine(model?.ToolCallingFormat); // Anthropic
Console.WriteLine(model?.SupportsMcpToolUse); // TrueNote — fuzzy matching:
FindModelresolves aliases in 4 passes (exact → alias exact → prefix → contains) to absorb cloud-specific ID variants (Bedrock/Vertex prefixes, date suffixes). Local/self-hosted deployment names that embed a public model name (e.g.deepseek-r1-distill-qwen-7b) can therefore match a catalog entry whose context window and pricing do not describe your deployment. For self-hosted models, take the effective context length from your deployment configuration (e.g. llama.cppn_ctx), not from the catalog.
// When correctness matters more than recall, bound the fuzziness:
ModelCatalog.FindModel("deepseek-r1-distill-qwen-7b", AliasMatchType.Exact); // null — no false positive
ModelCatalog.FindModel("us.anthropic.claude-sonnet-4.6-v1"); // matched via contains alias
// Or inspect which pass produced the match and apply confidence-based fallback:
var match = ModelCatalog.FindModelMatch("deepseek-r1-distill-qwen-7b");
Console.WriteLine(match?.MatchKind); // Prefix — a fuzzy inference, not an exact hit
Console.WriteLine(match?.Model.ModelId);// Basic cost (input + output tokens)
var cost = model?.CalculateCost(inputTokens: 500_000, outputTokens: 200_000);
// Cost including prompt cache tokens
var costWithCache = model?.CalculateCost(
inputTokens: 100_000,
outputTokens: 50_000,
cacheReadTokens: 400_000,
cacheWriteTokens: 50_000);
// Via CostCalculator (DI-friendly)
ICostCalculator calc = CostCalculator.Default();
var price = calc.CalculateCost("gpt-4o", inputTokens: 1_000, outputTokens: 500);// By provider — typed convenience property
foreach (var m in ModelCatalog.Anthropic.Values)
Console.WriteLine($"{m.ModelId}: ctx={m.ContextWindow}, ${m.InputPricePerMillion}/M");
// By provider — string-keyed (when the name is only known at runtime)
var openai = ModelCatalog.GetProvider("OpenAI"); // dict, empty if unknown
// By model type (the built-in catalog currently contains Chat models only)
var chatModels = ModelCatalog.GetByType(ModelType.Chat);
// All providers
var providers = ModelCatalog.GetProviderNames();var calc = CostCalculator.Default();
calc.RegisterModel(new ModelInfo
{
ModelId = "my-fine-tuned-model",
Provider = "MyCompany",
InputPricePerMillion = 2.00m,
OutputPricePerMillion = 8.00m,
ContextWindow = 128_000,
SupportsToolCalling = true,
ToolCallingFormat = ToolCallingFormat.OpenAI
});
var cost = calc.CalculateCost("my-fine-tuned-model", 10_000, 5_000);ModelInfo provides the following metadata:
| Property | Type | Description |
|---|---|---|
ModelId |
string |
Canonical model identifier for API calls |
Provider |
string? |
Provider name (e.g., "OpenAI", "Anthropic") |
DisplayName |
string? |
Human-readable name |
ModelType |
ModelType |
Chat, Embedding, Reranker, ImageGeneration, TextToSpeech, SpeechToText |
IsInstructTuned |
bool |
Instruction-tuned vs. base model |
| Property | Type | Description |
|---|---|---|
ContextWindow |
int? |
Maximum input tokens |
MaxOutputTokens |
int? |
Maximum generated tokens per response |
| Property | Description |
|---|---|
InputPricePerMillion |
Standard input token price |
OutputPricePerMillion |
Standard output token price |
CacheReadPricePerMillion |
Prompt cache hit price (often 90% discount) |
CacheWritePricePerMillion |
Prompt cache population price |
ImageInputPrice |
Per-image input cost |
AudioInputPricePerSecond |
Audio input cost per second |
Note — one rate per model: these fields hold a provider's standard rate for a model. Where a provider charges more above a prompt-length threshold (several now publish a second, higher tier for long-context requests), the catalog carries the base tier only, so cost for a request past that threshold is understated. Cache-write cost falls back to the input rate when a provider does not price it separately, which matches how automatic prompt caching is normally billed.
| Property | Description |
|---|---|
SupportsImageInput |
Accepts image data |
SupportsAudioInput |
Accepts audio data |
SupportsVideoInput |
Accepts video data |
SupportsDocumentInput |
Accepts PDF/document files natively |
| Property | Description |
|---|---|
SupportsToolCalling |
Tool/function calling |
SupportsParallelToolCalling |
Multiple tools per turn |
SupportsStructuredOutput |
JSON Schema-enforced output |
SupportsJsonMode |
JSON-guided output (soft) |
SupportsStreaming |
SSE streaming |
PromptCachingMode |
None / Explicit / Automatic |
SupportsMcpToolUse |
Native MCP tool support |
| Property | Description |
|---|---|
ReasoningMode |
None / Optional / Always |
ThinkingFormat |
None / Block / InlineTag / SeparateField |
ThinkingTagPattern |
Tag pattern (e.g., <think>...</think>) for InlineTag format |
ThinkingFieldName |
Field name (e.g., reasoning_content) for SeparateField format |
SupportsInterleavedThinking |
Reasoning between tool calls |
MaxThinkingTokens |
Maximum reasoning budget |
| Value | Description |
|---|---|
ToolCallingFormat.OpenAI |
tool_calls / tool role (default) |
ToolCallingFormat.Anthropic |
tool_use / tool_result content blocks |
ToolCallingFormat.Gemini |
Google Gemini format |
| Provider | Models |
|---|---|
| OpenAI | GPT-5.x, GPT-4.1, GPT-4o, o1, o3, o4-mini series |
| Anthropic | Claude 5, 4.x, 3.x families (Fable, Mythos, Opus, Sonnet, Haiku) |
| Gemini 3.x, 2.5, 2.0, 1.5 families | |
| xAI | Grok 4.x, 3.x series |
| Azure | Azure OpenAI equivalents |
| Mistral | Large, Medium, Small, Magistral, Pixtral |
| DeepSeek | V4 (Flash, Pro), R1 (reasoning), V3, Coder |
| Amazon Nova | Premier, Pro, Lite, Micro |
| Cohere | Command A, R+, R, R7B |
| Meta Llama | Maverick, Scout |
| Perplexity | Sonar Pro, Deep Research, Reasoning |
| Qwen | Max, Plus, Turbo |
Console.WriteLine(ModelCatalog.LastUpdated); // 2026-08-01
Console.WriteLine(ModelCatalog.DataAgeDays); // days since last update
Console.WriteLine(ModelCatalog.IsDataStale()); // true if > 90 days oldLastUpdated is derived from the lastUpdated field each bundled provider file declares, and
reports the most recent of them. Providers are refreshed independently, so an individual
provider's data can be considerably older than this value.
Note — what the signal does and does not tell you: it reports when this catalog was last refreshed, not whether a provider has changed its prices since. A vendor can cut a rate the day after a refresh, and
IsDataStale()will still answerfalsewhile the bundled figure is wrong. Treat catalog pricing as a good default for estimation and budgeting, and read authoritative figures from your provider's billing data when they have to be exact.
The following APIs were removed in 0.4.0:
TokenMeter.Abstractionspackage (removed entirely)ITokenCounter,TokenCounter— use your own tokenizer libraryIUsageTracker,UsageTracker,UsageRecord,UsageStatistics— implement in your applicationModelPricing→ replaced byModelInfoModelPricingData→ replaced byModelCatalogICostCalculator.GetPricing()→GetModel()ICostCalculator.RegisterPricing()→RegisterModel()
- .NET 10.0 or later
MIT