// @title Findings summary // @summary One forwardable page: what this check established, what it could not establish and why, and the ranked candidates. // @posture observe // @posture-note Restates what the other tiles measured. Applies no threshold, attaches no currency, names no savings class, and reaches no verdict. // // THE LAST TILE, AND THE ONE THAT LEAVES THE ROOM. // // Every tile above answers one question well and is useless to anyone who // was not looking at the screen. This one is built to be forwarded: a // platform owner runs the check, exports this grid, and sends it to the // person who has to decide something. It is the whole output of the free // surface in a form that survives being read by someone who was not there. // // WHAT IT DELIBERATELY DOES NOT DO. It states no threshold, attaches no // currency to consumption, and calls nothing recoverable — see SCOPE.md. // Section 3 is the reason it can be trusted: a summary that reports only // what it found, and never what it missed, is a sales document. // // Read-only. Runs in your Log Analytics workspace; sends nothing anywhere. let _startTime = ago(30d); let _endTime = now(); let llm = ApiManagementGatewayLlmLog | where TimeGenerated between (_startTime .. _endTime) | summarize TimeGenerated = min(TimeGenerated), ModelNameSet = make_set(ModelName, 2), PromptTokensSet = make_set(PromptTokens, 2), CompletionTokensSet = make_set(CompletionTokens, 2), TotalTokensSet = make_set(TotalTokens, 2) by CorrelationId | extend ModelName = iff(array_length(ModelNameSet) == 1, tostring(ModelNameSet[0]), ''), PromptTokens = iff(array_length(PromptTokensSet) == 1, tolong(PromptTokensSet[0]), long(null)), CompletionTokens = iff(array_length(CompletionTokensSet) == 1, tolong(CompletionTokensSet[0]), long(null)), TotalTokens = iff(array_length(TotalTokensSet) == 1, tolong(TotalTokensSet[0]), long(null)) | project CorrelationId, TimeGenerated, ModelName, PromptTokens, CompletionTokens, TotalTokens; let gateway = ApiManagementGatewayLogs | where TimeGenerated between (_startTime .. _endTime) | summarize ApiIdSet = make_set(ApiId, 2), OperationIdSet = make_set(OperationId, 2), ResponseCodeSet = make_set(ResponseCode, 2) by CorrelationId | extend ApiId = iff(array_length(ApiIdSet) == 1, tostring(ApiIdSet[0]), ''), OperationId = iff(array_length(OperationIdSet) == 1, tostring(OperationIdSet[0]), ''), ResponseCode = iff(array_length(ResponseCodeSet) == 1, toint(ResponseCodeSet[0]), int(null)), HasGateway = true | project CorrelationId, ApiId, OperationId, ResponseCode, HasGateway; let joined = llm | join kind=leftouter (gateway) on CorrelationId | extend HasGateway = coalesce(HasGateway, false), ApiId = coalesce(ApiId, ''), OperationId = coalesce(OperationId, ''); let requests = toscalar(joined | count); let withUsage = toscalar(joined | where isnotnull(TotalTokens) | count); let unattributed = toscalar(joined | where not(HasGateway) or (isempty(ApiId) and isempty(OperationId)) | count); let partial = toscalar(joined | where HasGateway and isnotempty(ApiId) and isempty(OperationId) | count); let inputTokens = toscalar(joined | summarize sum(PromptTokens)); let outputTokens = toscalar(joined | summarize sum(CompletionTokens)); let models = toscalar(joined | where isnotempty(ModelName) | summarize dcount(ModelName)); let firstSeen = toscalar(joined | summarize min(TimeGenerated)); let lastSeen = toscalar(joined | summarize max(TimeGenerated)); let failedRequests = toscalar(joined | where ResponseCode >= 500 or ResponseCode == 429 | count); let failedTokens = toscalar(joined | where ResponseCode >= 500 or ResponseCode == 429 | summarize sum(TotalTokens)); let pct = (n:long, d:long) { iff(d > 0, strcat(tostring(round(100.0 * n / d, 1)), '%'), 'n/a') }; let num = (n:long) { iff(isnull(n), 'not established', tostring(n)) }; let candidates = joined | where isnotnull(PromptTokens) and isnotnull(CompletionTokens) | extend ApiLabel = iff(isempty(ApiId), '(unattributed)', ApiId), OpLabel = iff(isempty(OperationId), '(unattributed)', OperationId), Model = iff(isempty(ModelName), '(not reported)', ModelName) | summarize Requests = count(), InputTokens = sum(PromptTokens), OutputTokens = sum(CompletionTokens) by ApiLabel, OpLabel, Model | extend Ratio = iff(OutputTokens > 0, round(1.0 * InputTokens / OutputTokens, 1), real(null)) | order by InputTokens desc | serialize Rank = row_number() | where Rank <= 5 | project Order = 400 + Rank, Section = '4 · CANDIDATES WORTH TESTING', Item = strcat(tostring(Rank), '. ', ApiLabel, ' / ', OpLabel, ' / ', Model), Detail = strcat( tostring(InputTokens), ' input tokens over ', tostring(Requests), iff(Requests == 1, ' request · ', ' requests · '), iff(isnull(Ratio), 'no output produced', strcat(tostring(Ratio), ':1 input-to-output'))); union (print Order = 100, Section = '1 · SCOPE OF THIS CHECK', Item = 'Window examined', Detail = strcat(format_datetime(_startTime, 'yyyy-MM-dd'), ' to ', format_datetime(_endTime, 'yyyy-MM-dd'))), (print Order = 101, Section = '1 · SCOPE OF THIS CHECK', Item = 'Traffic actually seen', Detail = iff(isnull(firstSeen), 'none in this window', strcat(format_datetime(firstSeen, 'yyyy-MM-dd'), ' to ', format_datetime(lastSeen, 'yyyy-MM-dd')))), (print Order = 102, Section = '1 · SCOPE OF THIS CHECK', Item = 'Source', Detail = 'Azure API Management diagnostics in this Log Analytics workspace. Read-only. Nothing transmitted.'), (print Order = 200, Section = '2 · WHAT WAS OBSERVED', Item = 'Requests', Detail = num(requests)), (print Order = 201, Section = '2 · WHAT WAS OBSERVED', Item = 'Input tokens', Detail = num(inputTokens)), (print Order = 202, Section = '2 · WHAT WAS OBSERVED', Item = 'Output tokens', Detail = num(outputTokens)), (print Order = 203, Section = '2 · WHAT WAS OBSERVED', Item = 'Distinct models in use', Detail = num(models)), (print Order = 204, Section = '2 · WHAT WAS OBSERVED', Item = 'Requests that did not succeed', Detail = strcat(num(failedRequests), ' (', pct(failedRequests, requests), ') consuming ', num(failedTokens), ' tokens')), (print Order = 300, Section = '3 · WHAT COULD NOT BE ESTABLISHED', Item = 'Requests without usable token data', Detail = strcat(num(requests - withUsage), ' (', pct(requests - withUsage, requests), ') — no usage reported, or records disagreed. Token totals above are a floor, not an estimate.')), (print Order = 301, Section = '3 · WHAT COULD NOT BE ESTABLISHED', Item = 'Consumption with no accountable workload', Detail = strcat(num(unattributed), ' (', pct(unattributed, requests), ') — no API or operation identity, so this cannot be assigned to an owner.')), (print Order = 302, Section = '3 · WHAT COULD NOT BE ESTABLISHED', Item = 'Partially attributed', Detail = strcat(num(partial), ' (', pct(partial, requests), ') — API known, operation not.')), (print Order = 303, Section = '3 · WHAT COULD NOT BE ESTABLISHED', Item = 'What this check does not determine', Detail = 'What any of it costs, whether any figure is high or low, and whether a candidate is worth acting on. Those require validation against your own requirements.'), candidates | order by Order asc | project Section, Item, Detail