Skip to content

Commit a7c89f8

Browse files
committed
fix(tools): enhance Foundry Models pricing guidance and clarify skuName decoding
1 parent 688ff2b commit a7c89f8

1 file changed

Lines changed: 29 additions & 3 deletions

File tree

‎src/Dashboard/AI/Tools/RetailPricingTools.cs‎

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,27 @@ CRITICAL FILTERING (always provide as much as possible to keep results small):
2727
2828
Returns up to $top items (default 50, max 100). Note: the API sometimes ignores $top and returns a full page (~1000 rows) — always filter aggressively. For broad surveys, aggregate client-side; never call without filters.
2929
30-
FOUNDRY MODELS (serviceName='Foundry Models') — productName is a FAMILY bucket, the specific model + I/O direction lives in skuName/meterName:
30+
FOUNDRY MODELS (serviceName='Foundry Models') — productName is a FAMILY bucket; the specific model, I/O direction, residency zone, and deployment type all live INSIDE skuName/meterName:
3131
- productName values: 'Azure OpenAI GPT5', 'Azure OpenAI Reasoning', 'Azure OpenAI Embedding', 'Azure OpenAI Media', 'Azure OpenAI', 'Azure OpenAI PP FT GPT4s' (fine-tune), 'Azure OpenAI OSS Models', 'Azure Phi Models', 'Azure Llama Models', 'Azure Mistral Models', 'Azure Grok Models', 'Azure Deepseek Models', 'Azure Fireworks Models', 'Azure BFL Flux Models', 'Azure Kimi', 'Cohere Models', 'Qwen models', 'MAI Models', 'Azure AI Foundry Provisioned Throughput Reservation', 'Managed Compute'.
32-
- skuName examples: 'gpt 4.1 Inp regnl', 'gpt 4.1 Outp glbl', 'o3 Inp regnl' — encodes model, Inp/Outp, regnl(regional)/glbl(global)/dtstr(data zone).
3332
- Use productNameContains='GPT' / 'Llama' / 'Phi' / 'OpenAI' to scope. Do NOT pass model strings like 'gpt-4' to productNameContains — they won't match.
3433
34+
DECODE skuName TOKEN-BY-TOKEN BEFORE QUOTING ANY PRICE (this is where wrong prices come from — every token changes the price):
35+
- Direction: 'Inp'/'Input' = input · 'Outp'/'Opt'/'Output' = output · 'cd Inp' = CACHED input (only applies to cache hits, far cheaper than normal input).
36+
- Zone: ends in 'Gl' (or 'glbl') = Global · 'Dz' (or 'dtstr') = Data Zone · 'regnl' = Regional. Different zones are different products at different prices.
37+
- Deployment: contains 'Batch' = Batch API (async, ~50% off, NOT real-time). NO 'Batch' token = Standard (real-time) — this is the default/headline.
38+
- Context tier (gpt-5.5 family): 'ShortCo' = short-context · 'LongCo' = long-context · 'PP' = priority processing. Each is a separate price.
39+
40+
HOW TO PICK THE RIGHT ROW (the #1 mistake is reporting a cheaper variant as the headline price):
41+
- HEADLINE pay-as-you-go = Standard + Global: skuName has NO 'Batch', ends in 'Gl', and for input uses 'Inp' (NOT 'cd Inp'). Example gpt-5.4-nano: 'X nano Inp Gl' = input, 'X nano cd Inp Gl' = cached input, 'X nano Opt Gl' = output.
42+
- prices.azure.com repeats the SAME price across every armRegionName (the value is flat per zone) AND returns every Batch / Data Zone / Regional / cached variant in the same response. So the result set legitimately contains many rows at DIFFERENT prices for one model.
43+
- NEVER take the minimum retailPrice across rows. The lowest row is almost always Batch + cached — not the real price. Instead, match the EXACT skuName for the variant asked about (default Standard + Global) and report THAT row's retailPrice.
44+
- ALWAYS state the variant you are quoting, e.g. 'gpt-5.4-nano, Global Standard — input $0.20, cached input $0.02, output $1.25 per 1M tokens'. Mention Batch / Data Zone only as clearly-labelled separate options, never as the headline.
45+
3546
Common queries:
3647
- Compare regions: serviceName='Virtual Machines' + armSkuName='Standard_D4s_v5' + priceType='Consumption'
3748
- RI vs PAYG: serviceName='Virtual Machines' + armSkuName='Standard_D4s_v5' + armRegionName='eastus' (returns both)
3849
- Storage tier costs: serviceName='Storage' + armRegionName='eastus' + meterName contains 'LRS'
39-
- GPT-5 per-token: serviceName='Foundry Models' + armRegionName='eastus' + productNameContains='GPT' (then filter skuName client-side)
50+
- GPT model per-token: serviceName='Foundry Models' + productNameContains='GPT' (returns ALL variants — then pick the exact skuName: no 'Batch', ends 'Gl', 'Inp'/'Opt'/'cd Inp' — for Global Standard; do NOT min across rows)
4051
- Llama / Phi / Mistral: serviceName='Foundry Models' + productNameContains='Llama' (or 'Phi', 'Mistral')
4152
- Spot vs on-demand: serviceName='Virtual Machines' + armSkuName='Standard_D4s_v5' + meterName contains 'Spot'");
4253
}
@@ -56,6 +67,7 @@ private static async Task<string> GetAzureRetailPricing(
5667

5768
top = Math.Clamp(top, 1, 100);
5869
currencyCode = string.IsNullOrWhiteSpace(currencyCode) ? "USD" : currencyCode.Trim().ToUpperInvariant();
70+
var isFoundry = serviceName.Trim().Equals("Foundry Models", StringComparison.OrdinalIgnoreCase);
5971

6072
var filters = new List<string> { $"serviceName eq '{Esc(serviceName)}'" };
6173
if (!string.IsNullOrWhiteSpace(armRegionName)) filters.Add($"armRegionName eq '{Esc(armRegionName.Trim().ToLowerInvariant())}'");
@@ -113,6 +125,20 @@ private static async Task<string> GetAzureRetailPricing(
113125
activity?.SetTag("pricing.response_length", body.Length);
114126

115127
var header = $"HTTP {(int)res.StatusCode} {res.StatusCode}\nQuery: {filter} (top={top}, currency={currencyCode})\nUTC: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}\n";
128+
129+
// Foundry pricing is the one place an agent reliably mis-reads the raw rows: the response
130+
// mixes Standard/Batch, Global/DataZone/Regional, and cached/non-cached SKUs (all at
131+
// different prices, each repeated per region). The full JSON is still returned below — we
132+
// only PREPEND a reminder so the model picks the right skuName instead of the cheapest row.
133+
// This is guidance text, not response parsing (raw-JSON contract preserved).
134+
if (isFoundry)
135+
header +=
136+
"FOUNDRY PRICING — READ skuName BEFORE QUOTING: headline PAYG = Standard + Global "
137+
+ "(skuName has NO 'Batch', ends in 'Gl', input is 'Inp' not 'cd Inp'). The same price "
138+
+ "repeats across every region, and Batch/DataZone/Regional/cached variants are mixed in "
139+
+ "at different prices. Match the EXACT variant asked for (default Standard+Global) and "
140+
+ "state which one you quote. NEVER report the minimum retailPrice across rows.\n";
141+
116142
if (body.Length > 200_000)
117143
return header + body[..200_000] + $"\n\n[TRUNCATED — {body.Length / 1024}KB total. Add more filters (armRegionName, armSkuName, priceType) to narrow results.]";
118144
return header + body;

0 commit comments

Comments
 (0)