@fgv/ts-extras 5.1.0-55 → 5.1.0-57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CAPABILITIES.md +96 -0
- package/dist/packlets/ai-assist/cacheRequest.js +118 -0
- package/dist/packlets/ai-assist/cacheRequest.js.map +1 -0
- package/dist/packlets/ai-assist/chatRequestBuilders.js +72 -7
- package/dist/packlets/ai-assist/chatRequestBuilders.js.map +1 -1
- package/dist/packlets/ai-assist/completionClient.js +195 -58
- package/dist/packlets/ai-assist/completionClient.js.map +1 -1
- package/dist/packlets/ai-assist/converters.js +54 -0
- package/dist/packlets/ai-assist/converters.js.map +1 -1
- package/dist/packlets/ai-assist/embeddingClient.js +16 -1
- package/dist/packlets/ai-assist/embeddingClient.js.map +1 -1
- package/dist/packlets/ai-assist/imageGenerationClient.js +17 -2
- package/dist/packlets/ai-assist/imageGenerationClient.js.map +1 -1
- package/dist/packlets/ai-assist/index.js +3 -1
- package/dist/packlets/ai-assist/index.js.map +1 -1
- package/dist/packlets/ai-assist/listModelsClient.js +19 -1
- package/dist/packlets/ai-assist/listModelsClient.js.map +1 -1
- package/dist/packlets/ai-assist/model.js.map +1 -1
- package/dist/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.js +200 -0
- package/dist/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.js.map +1 -0
- package/dist/packlets/ai-assist/promptCacheObservabilityHarness.js +137 -0
- package/dist/packlets/ai-assist/promptCacheObservabilityHarness.js.map +1 -0
- package/dist/packlets/ai-assist/promptCacheRoutingAbHarness.js +156 -0
- package/dist/packlets/ai-assist/promptCacheRoutingAbHarness.js.map +1 -0
- package/dist/packlets/ai-assist/promptCacheUsageMath.js +124 -0
- package/dist/packlets/ai-assist/promptCacheUsageMath.js.map +1 -0
- package/dist/packlets/ai-assist/registry.js +9 -11
- package/dist/packlets/ai-assist/registry.js.map +1 -1
- package/dist/packlets/ai-assist/streamUsageCapability.js +134 -0
- package/dist/packlets/ai-assist/streamUsageCapability.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +32 -9
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +81 -35
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/common.js +25 -1
- package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js +12 -5
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +45 -11
- package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +33 -22
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js +62 -4
- package/dist/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
- package/dist/packlets/ai-assist/streamingClient.js +20 -5
- package/dist/packlets/ai-assist/streamingClient.js.map +1 -1
- package/dist/packlets/ai-assist/thinkingOptionsResolver.js +23 -5
- package/dist/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -1
- package/dist/packlets/ai-assist/toolFormats.js +80 -0
- package/dist/packlets/ai-assist/toolFormats.js.map +1 -1
- package/dist/packlets/ai-assist/toolTypes.js +21 -0
- package/dist/packlets/ai-assist/toolTypes.js.map +1 -0
- package/dist/packlets/ai-assist/usageNormalization.js +178 -0
- package/dist/packlets/ai-assist/usageNormalization.js.map +1 -0
- package/dist/packlets/ai-assist/usageTypes.js +21 -0
- package/dist/packlets/ai-assist/usageTypes.js.map +1 -0
- package/dist/packlets/mustache/index.js.map +1 -1
- package/dist/packlets/mustache/interfaces.js.map +1 -1
- package/dist/packlets/mustache/mustacheTemplate.js +91 -0
- package/dist/packlets/mustache/mustacheTemplate.js.map +1 -1
- package/dist/ts-extras.d.ts +764 -48
- package/lib/packlets/ai-assist/cacheRequest.d.ts +100 -0
- package/lib/packlets/ai-assist/cacheRequest.d.ts.map +1 -0
- package/lib/packlets/ai-assist/cacheRequest.js +123 -0
- package/lib/packlets/ai-assist/cacheRequest.js.map +1 -0
- package/lib/packlets/ai-assist/chatRequestBuilders.d.ts +36 -6
- package/lib/packlets/ai-assist/chatRequestBuilders.d.ts.map +1 -1
- package/lib/packlets/ai-assist/chatRequestBuilders.js +75 -7
- package/lib/packlets/ai-assist/chatRequestBuilders.js.map +1 -1
- package/lib/packlets/ai-assist/completionClient.d.ts +19 -0
- package/lib/packlets/ai-assist/completionClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/completionClient.js +194 -57
- package/lib/packlets/ai-assist/completionClient.js.map +1 -1
- package/lib/packlets/ai-assist/converters.d.ts +46 -0
- package/lib/packlets/ai-assist/converters.d.ts.map +1 -1
- package/lib/packlets/ai-assist/converters.js +55 -1
- package/lib/packlets/ai-assist/converters.js.map +1 -1
- package/lib/packlets/ai-assist/embeddingClient.d.ts +9 -0
- package/lib/packlets/ai-assist/embeddingClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/embeddingClient.js +16 -1
- package/lib/packlets/ai-assist/embeddingClient.js.map +1 -1
- package/lib/packlets/ai-assist/imageGenerationClient.d.ts +9 -0
- package/lib/packlets/ai-assist/imageGenerationClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/imageGenerationClient.js +17 -2
- package/lib/packlets/ai-assist/imageGenerationClient.js.map +1 -1
- package/lib/packlets/ai-assist/index.d.ts +7 -2
- package/lib/packlets/ai-assist/index.d.ts.map +1 -1
- package/lib/packlets/ai-assist/index.js +11 -1
- package/lib/packlets/ai-assist/index.js.map +1 -1
- package/lib/packlets/ai-assist/listModelsClient.d.ts +12 -0
- package/lib/packlets/ai-assist/listModelsClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/listModelsClient.js +19 -1
- package/lib/packlets/ai-assist/listModelsClient.js.map +1 -1
- package/lib/packlets/ai-assist/model.d.ts +81 -295
- package/lib/packlets/ai-assist/model.d.ts.map +1 -1
- package/lib/packlets/ai-assist/model.js.map +1 -1
- package/lib/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.d.ts +124 -0
- package/lib/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.d.ts.map +1 -0
- package/lib/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.js +208 -0
- package/lib/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.js.map +1 -0
- package/lib/packlets/ai-assist/promptCacheObservabilityHarness.d.ts +77 -0
- package/lib/packlets/ai-assist/promptCacheObservabilityHarness.d.ts.map +1 -0
- package/lib/packlets/ai-assist/promptCacheObservabilityHarness.js +143 -0
- package/lib/packlets/ai-assist/promptCacheObservabilityHarness.js.map +1 -0
- package/lib/packlets/ai-assist/promptCacheRoutingAbHarness.d.ts +84 -0
- package/lib/packlets/ai-assist/promptCacheRoutingAbHarness.d.ts.map +1 -0
- package/lib/packlets/ai-assist/promptCacheRoutingAbHarness.js +163 -0
- package/lib/packlets/ai-assist/promptCacheRoutingAbHarness.js.map +1 -0
- package/lib/packlets/ai-assist/promptCacheUsageMath.d.ts +105 -0
- package/lib/packlets/ai-assist/promptCacheUsageMath.d.ts.map +1 -0
- package/lib/packlets/ai-assist/promptCacheUsageMath.js +130 -0
- package/lib/packlets/ai-assist/promptCacheUsageMath.js.map +1 -0
- package/lib/packlets/ai-assist/registry.d.ts.map +1 -1
- package/lib/packlets/ai-assist/registry.js +9 -11
- package/lib/packlets/ai-assist/registry.js.map +1 -1
- package/lib/packlets/ai-assist/streamUsageCapability.d.ts +129 -0
- package/lib/packlets/ai-assist/streamUsageCapability.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamUsageCapability.js +140 -0
- package/lib/packlets/ai-assist/streamUsageCapability.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +2 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +29 -6
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts +58 -14
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +81 -36
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +38 -3
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.js +25 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js +12 -5
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +2 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +42 -8
- package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +2 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +30 -19
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts +22 -0
- package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js +62 -4
- package/lib/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
- package/lib/packlets/ai-assist/streamingClient.d.ts +5 -0
- package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingClient.js +20 -5
- package/lib/packlets/ai-assist/streamingClient.js.map +1 -1
- package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts +11 -3
- package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts.map +1 -1
- package/lib/packlets/ai-assist/thinkingOptionsResolver.js +23 -5
- package/lib/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -1
- package/lib/packlets/ai-assist/toolFormats.d.ts +55 -1
- package/lib/packlets/ai-assist/toolFormats.d.ts.map +1 -1
- package/lib/packlets/ai-assist/toolFormats.js +82 -0
- package/lib/packlets/ai-assist/toolFormats.js.map +1 -1
- package/lib/packlets/ai-assist/toolTypes.d.ts +386 -0
- package/lib/packlets/ai-assist/toolTypes.d.ts.map +1 -0
- package/lib/packlets/ai-assist/toolTypes.js +22 -0
- package/lib/packlets/ai-assist/toolTypes.js.map +1 -0
- package/lib/packlets/ai-assist/usageNormalization.d.ts +58 -0
- package/lib/packlets/ai-assist/usageNormalization.d.ts.map +1 -0
- package/lib/packlets/ai-assist/usageNormalization.js +184 -0
- package/lib/packlets/ai-assist/usageNormalization.js.map +1 -0
- package/lib/packlets/ai-assist/usageTypes.d.ts +84 -0
- package/lib/packlets/ai-assist/usageTypes.d.ts.map +1 -0
- package/lib/packlets/ai-assist/usageTypes.js +22 -0
- package/lib/packlets/ai-assist/usageTypes.js.map +1 -0
- package/lib/packlets/mustache/index.d.ts +1 -1
- package/lib/packlets/mustache/index.d.ts.map +1 -1
- package/lib/packlets/mustache/index.js.map +1 -1
- package/lib/packlets/mustache/interfaces.d.ts +30 -0
- package/lib/packlets/mustache/interfaces.d.ts.map +1 -1
- package/lib/packlets/mustache/interfaces.js.map +1 -1
- package/lib/packlets/mustache/mustacheTemplate.d.ts +56 -1
- package/lib/packlets/mustache/mustacheTemplate.d.ts.map +1 -1
- package/lib/packlets/mustache/mustacheTemplate.js +91 -0
- package/lib/packlets/mustache/mustacheTemplate.js.map +1 -1
- package/package.json +16 -16
- package/perf/promptCacheAnthropicBreakpoint.js +135 -0
- package/perf/promptCacheObservability.js +107 -0
- package/perf/promptCacheRoutingAb.js +105 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// Copyright (c) 2026 Erik Fortune
|
|
2
|
+
//
|
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
// in the Software without restriction, including without limitation the rights
|
|
6
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
// furnished to do so, subject to the following conditions:
|
|
9
|
+
//
|
|
10
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
// copies or substantial portions of the Software.
|
|
12
|
+
//
|
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
// SOFTWARE.
|
|
20
|
+
/**
|
|
21
|
+
* Computes {@link ICacheUsageRatio} from a completion's usage block.
|
|
22
|
+
*
|
|
23
|
+
* @remarks
|
|
24
|
+
* The two harnesses this was extracted from each computed this ratio independently, inline, and
|
|
25
|
+
* disagreed with each other — and with this function — on edge cases that neither had a test for:
|
|
26
|
+
*
|
|
27
|
+
* - `promptCacheObservability.js`'s `summarizeUsage` divided by a `0` total without a guard,
|
|
28
|
+
* printing `NaN%` whenever a response reported `uncachedInputTokens: 0, cachedInputTokens: 0`.
|
|
29
|
+
* - `promptCacheRoutingAb.js`'s inline `pct()` coalesced a missing `cachedInputTokens` **or**
|
|
30
|
+
* `uncachedInputTokens` to `0` (`(u.cachedInputTokens ?? 0) + (u.uncachedInputTokens ?? 0)`,
|
|
31
|
+
* `100 * (u.cachedInputTokens ?? 0) / total`) — and which field was missing decided the
|
|
32
|
+
* direction of the fabrication: a missing `cachedInputTokens` fabricated a false low ratio
|
|
33
|
+
* (feeding a false `MISSES`), while a missing `uncachedInputTokens` fabricated a false **100%**
|
|
34
|
+
* (feeding a false `CONFIRMS`) — this is not a one-directional bug.
|
|
35
|
+
* - `promptCacheRoutingAb.js`'s `describeUsage` also special-cased a *genuinely reported*
|
|
36
|
+
* `cachedInputTokens: 0, uncachedInputTokens: 0` (both fields present) as `0.0%`, and the
|
|
37
|
+
* verdict's `pct()` scored that same input as a hard `0`, feeding `MISSES`. This function scores
|
|
38
|
+
* a real zero-input total as not computable, same as observability's zero-total case above —
|
|
39
|
+
* there were no bytes to have a ratio over, so `0.0%` claims more than the data supports.
|
|
40
|
+
*
|
|
41
|
+
* All three are fixed here rather than reproduced: this function returns `undefined` — never a
|
|
42
|
+
* fabricated number — whenever the ratio cannot honestly be computed, matching the same
|
|
43
|
+
* unknown-is-unknown rule the normalized {@link IAiCompletionUsage} type itself follows.
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
export function computeCacheUsageRatio(usage) {
|
|
47
|
+
if (usage === undefined) {
|
|
48
|
+
return { totalInputTokens: undefined, cachedRatio: undefined };
|
|
49
|
+
}
|
|
50
|
+
const { cachedInputTokens, uncachedInputTokens } = usage;
|
|
51
|
+
if (cachedInputTokens === undefined || uncachedInputTokens === undefined) {
|
|
52
|
+
return { totalInputTokens: undefined, cachedRatio: undefined };
|
|
53
|
+
}
|
|
54
|
+
// A *present but out-of-range* component is not the same hazard as an absent one, and the
|
|
55
|
+
// absent case above does not cover it. A token count cannot be negative or non-finite, so one
|
|
56
|
+
// that is means the usage block is malformed — and computing anyway yields a ratio outside
|
|
57
|
+
// [0, 1] that this function's own contract promises cannot happen. That ratio then reaches
|
|
58
|
+
// `compareCacheHitRatios`, where a skewed baseline can fabricate a favourable verdict from an
|
|
59
|
+
// unremarkable candidate. Reachable from real wire data, not just hand-built input:
|
|
60
|
+
// `usageNormalization.ts` derives `uncachedInputTokens` as `promptTokens - cachedInputTokens`
|
|
61
|
+
// on both the Chat Completions and Responses routes with no guard, so any provider reporting
|
|
62
|
+
// `cached_tokens > prompt_tokens` produces exactly this shape.
|
|
63
|
+
if (!Number.isFinite(cachedInputTokens) ||
|
|
64
|
+
!Number.isFinite(uncachedInputTokens) ||
|
|
65
|
+
cachedInputTokens < 0 ||
|
|
66
|
+
uncachedInputTokens < 0) {
|
|
67
|
+
return { totalInputTokens: undefined, cachedRatio: undefined };
|
|
68
|
+
}
|
|
69
|
+
const totalInputTokens = cachedInputTokens + uncachedInputTokens;
|
|
70
|
+
if (totalInputTokens <= 0) {
|
|
71
|
+
return { totalInputTokens, cachedRatio: undefined };
|
|
72
|
+
}
|
|
73
|
+
return { totalInputTokens, cachedRatio: cachedInputTokens / totalInputTokens };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Formats a cache-hit ratio as a percentage string, or `'n/a'` when {@link computeCacheUsageRatio}
|
|
77
|
+
* could not compute one.
|
|
78
|
+
* @internal
|
|
79
|
+
*/
|
|
80
|
+
export function formatCacheRatioPercent(ratio) {
|
|
81
|
+
return ratio === undefined ? 'n/a' : `${(ratio * 100).toFixed(1)}%`;
|
|
82
|
+
}
|
|
83
|
+
/** A candidate ratio at or below this percentage cannot count as a cache hit. @internal */
|
|
84
|
+
export const CACHE_HIT_FLOOR_PERCENT = 50;
|
|
85
|
+
/**
|
|
86
|
+
* The candidate ratio must clear the baseline ratio by at least this multiple to be attributed to
|
|
87
|
+
* something other than routing luck.
|
|
88
|
+
* @internal
|
|
89
|
+
*/
|
|
90
|
+
export const CACHE_HIT_MIN_IMPROVEMENT_FACTOR = 5;
|
|
91
|
+
/**
|
|
92
|
+
* Classifies a baseline/candidate pair of usage blocks by their cache-hit ratio.
|
|
93
|
+
*
|
|
94
|
+
* @remarks
|
|
95
|
+
* Ported from `promptCacheRoutingAb.js`'s inline verdict —
|
|
96
|
+
* `if (b > 50 && b > a * 5) CONFIRMS; else if (b <= 50) MISSES; else AMBIGUOUS` — now driven off
|
|
97
|
+
* {@link computeCacheUsageRatio} rather than a hand-rolled `pct()` that coalesced missing fields
|
|
98
|
+
* to `0`. `'inconclusive'` is new: it is what an unclassifiable ratio (baseline or candidate
|
|
99
|
+
* `undefined`) reports now, in place of the original's silent `0`, which could otherwise produce
|
|
100
|
+
* a false `MISSES` verdict from data that never said "no cache hit" — it said nothing at all.
|
|
101
|
+
*
|
|
102
|
+
* The same classification also drives `promptCacheObservability.js`'s cold/warm comparison
|
|
103
|
+
* (baseline = cold call, candidate = warm call), which previously had no computed verdict of any
|
|
104
|
+
* kind — only two printed summaries and a static reminder for a human to judge by eye.
|
|
105
|
+
* @internal
|
|
106
|
+
*/
|
|
107
|
+
export function compareCacheHitRatios(baseline, candidate) {
|
|
108
|
+
const baselineRatio = computeCacheUsageRatio(baseline).cachedRatio;
|
|
109
|
+
const candidateRatio = computeCacheUsageRatio(candidate).cachedRatio;
|
|
110
|
+
if (baselineRatio === undefined || candidateRatio === undefined) {
|
|
111
|
+
return { baselineRatio, candidateRatio, verdict: 'inconclusive' };
|
|
112
|
+
}
|
|
113
|
+
const baselinePercent = baselineRatio * 100;
|
|
114
|
+
const candidatePercent = candidateRatio * 100;
|
|
115
|
+
if (candidatePercent > CACHE_HIT_FLOOR_PERCENT &&
|
|
116
|
+
candidatePercent > baselinePercent * CACHE_HIT_MIN_IMPROVEMENT_FACTOR) {
|
|
117
|
+
return { baselineRatio, candidateRatio, verdict: 'confirms' };
|
|
118
|
+
}
|
|
119
|
+
if (candidatePercent <= CACHE_HIT_FLOOR_PERCENT) {
|
|
120
|
+
return { baselineRatio, candidateRatio, verdict: 'misses' };
|
|
121
|
+
}
|
|
122
|
+
return { baselineRatio, candidateRatio, verdict: 'ambiguous' };
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=promptCacheUsageMath.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptCacheUsageMath.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/promptCacheUsageMath.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Cache-hit ratio arithmetic and cold/warm verdict classification shared by the two prompt-cache\n * measurement harnesses under `perf/` (`promptCacheObservability.js`,\n * `promptCacheRoutingAb.js`).\n *\n * @remarks\n * Extracted per design.md §8's addendum (\"Preferred remedy: keep `perf/` placement, add the\n * seam\") and `TESTING_GUIDELINES.md` § \"Measurement Harnesses\" — before this module existed,\n * both harnesses computed this math inline, disagreed with each other on how to treat missing or\n * zero-valued fields, and had no test of either version. See {@link computeCacheUsageRatio} and\n * {@link compareCacheHitRatios} for what each inline version did and what changed.\n * @packageDocumentation\n */\n\nimport { type IAiCompletionUsage } from './usageTypes';\n\n/**\n * A cache-hit ratio derived from one completion's usage block.\n * @internal\n */\nexport interface ICacheUsageRatio {\n /** `cachedInputTokens + uncachedInputTokens`, when both are known. `0` is a valid value. */\n readonly totalInputTokens: number | undefined;\n /**\n * `cachedInputTokens / totalInputTokens`, in `[0, 1]`.\n *\n * @remarks\n * `undefined` whenever the ratio is not a meaningful number: the usage block itself is absent,\n * either token count is unreported, or the total is `0` (there is no input to have a ratio\n * over). Per design.md's governing rule R-c, unknown is reported as unknown, never defaulted to\n * a number.\n */\n readonly cachedRatio: number | undefined;\n}\n\n/**\n * Computes {@link ICacheUsageRatio} from a completion's usage block.\n *\n * @remarks\n * The two harnesses this was extracted from each computed this ratio independently, inline, and\n * disagreed with each other — and with this function — on edge cases that neither had a test for:\n *\n * - `promptCacheObservability.js`'s `summarizeUsage` divided by a `0` total without a guard,\n * printing `NaN%` whenever a response reported `uncachedInputTokens: 0, cachedInputTokens: 0`.\n * - `promptCacheRoutingAb.js`'s inline `pct()` coalesced a missing `cachedInputTokens` **or**\n * `uncachedInputTokens` to `0` (`(u.cachedInputTokens ?? 0) + (u.uncachedInputTokens ?? 0)`,\n * `100 * (u.cachedInputTokens ?? 0) / total`) — and which field was missing decided the\n * direction of the fabrication: a missing `cachedInputTokens` fabricated a false low ratio\n * (feeding a false `MISSES`), while a missing `uncachedInputTokens` fabricated a false **100%**\n * (feeding a false `CONFIRMS`) — this is not a one-directional bug.\n * - `promptCacheRoutingAb.js`'s `describeUsage` also special-cased a *genuinely reported*\n * `cachedInputTokens: 0, uncachedInputTokens: 0` (both fields present) as `0.0%`, and the\n * verdict's `pct()` scored that same input as a hard `0`, feeding `MISSES`. This function scores\n * a real zero-input total as not computable, same as observability's zero-total case above —\n * there were no bytes to have a ratio over, so `0.0%` claims more than the data supports.\n *\n * All three are fixed here rather than reproduced: this function returns `undefined` — never a\n * fabricated number — whenever the ratio cannot honestly be computed, matching the same\n * unknown-is-unknown rule the normalized {@link IAiCompletionUsage} type itself follows.\n * @internal\n */\nexport function computeCacheUsageRatio(usage: IAiCompletionUsage | undefined): ICacheUsageRatio {\n if (usage === undefined) {\n return { totalInputTokens: undefined, cachedRatio: undefined };\n }\n const { cachedInputTokens, uncachedInputTokens } = usage;\n if (cachedInputTokens === undefined || uncachedInputTokens === undefined) {\n return { totalInputTokens: undefined, cachedRatio: undefined };\n }\n // A *present but out-of-range* component is not the same hazard as an absent one, and the\n // absent case above does not cover it. A token count cannot be negative or non-finite, so one\n // that is means the usage block is malformed — and computing anyway yields a ratio outside\n // [0, 1] that this function's own contract promises cannot happen. That ratio then reaches\n // `compareCacheHitRatios`, where a skewed baseline can fabricate a favourable verdict from an\n // unremarkable candidate. Reachable from real wire data, not just hand-built input:\n // `usageNormalization.ts` derives `uncachedInputTokens` as `promptTokens - cachedInputTokens`\n // on both the Chat Completions and Responses routes with no guard, so any provider reporting\n // `cached_tokens > prompt_tokens` produces exactly this shape.\n if (\n !Number.isFinite(cachedInputTokens) ||\n !Number.isFinite(uncachedInputTokens) ||\n cachedInputTokens < 0 ||\n uncachedInputTokens < 0\n ) {\n return { totalInputTokens: undefined, cachedRatio: undefined };\n }\n const totalInputTokens = cachedInputTokens + uncachedInputTokens;\n if (totalInputTokens <= 0) {\n return { totalInputTokens, cachedRatio: undefined };\n }\n return { totalInputTokens, cachedRatio: cachedInputTokens / totalInputTokens };\n}\n\n/**\n * Formats a cache-hit ratio as a percentage string, or `'n/a'` when {@link computeCacheUsageRatio}\n * could not compute one.\n * @internal\n */\nexport function formatCacheRatioPercent(ratio: number | undefined): string {\n return ratio === undefined ? 'n/a' : `${(ratio * 100).toFixed(1)}%`;\n}\n\n/**\n * Verdict for a baseline/candidate pair of cache-hit ratios.\n *\n * @remarks\n * `'inconclusive'` has no equivalent in the original inline logic — see {@link compareCacheHitRatios}.\n * @internal\n */\nexport type CacheHitComparisonVerdict = 'confirms' | 'misses' | 'ambiguous' | 'inconclusive';\n\n/** A candidate ratio at or below this percentage cannot count as a cache hit. @internal */\nexport const CACHE_HIT_FLOOR_PERCENT: number = 50;\n\n/**\n * The candidate ratio must clear the baseline ratio by at least this multiple to be attributed to\n * something other than routing luck.\n * @internal\n */\nexport const CACHE_HIT_MIN_IMPROVEMENT_FACTOR: number = 5;\n\n/** The outcome of comparing a baseline usage block against a candidate one. @internal */\nexport interface ICacheHitComparison {\n readonly baselineRatio: number | undefined;\n readonly candidateRatio: number | undefined;\n readonly verdict: CacheHitComparisonVerdict;\n}\n\n/**\n * Classifies a baseline/candidate pair of usage blocks by their cache-hit ratio.\n *\n * @remarks\n * Ported from `promptCacheRoutingAb.js`'s inline verdict —\n * `if (b > 50 && b > a * 5) CONFIRMS; else if (b <= 50) MISSES; else AMBIGUOUS` — now driven off\n * {@link computeCacheUsageRatio} rather than a hand-rolled `pct()` that coalesced missing fields\n * to `0`. `'inconclusive'` is new: it is what an unclassifiable ratio (baseline or candidate\n * `undefined`) reports now, in place of the original's silent `0`, which could otherwise produce\n * a false `MISSES` verdict from data that never said \"no cache hit\" — it said nothing at all.\n *\n * The same classification also drives `promptCacheObservability.js`'s cold/warm comparison\n * (baseline = cold call, candidate = warm call), which previously had no computed verdict of any\n * kind — only two printed summaries and a static reminder for a human to judge by eye.\n * @internal\n */\nexport function compareCacheHitRatios(\n baseline: IAiCompletionUsage | undefined,\n candidate: IAiCompletionUsage | undefined\n): ICacheHitComparison {\n const baselineRatio = computeCacheUsageRatio(baseline).cachedRatio;\n const candidateRatio = computeCacheUsageRatio(candidate).cachedRatio;\n if (baselineRatio === undefined || candidateRatio === undefined) {\n return { baselineRatio, candidateRatio, verdict: 'inconclusive' };\n }\n const baselinePercent = baselineRatio * 100;\n const candidatePercent = candidateRatio * 100;\n if (\n candidatePercent > CACHE_HIT_FLOOR_PERCENT &&\n candidatePercent > baselinePercent * CACHE_HIT_MIN_IMPROVEMENT_FACTOR\n ) {\n return { baselineRatio, candidateRatio, verdict: 'confirms' };\n }\n if (candidatePercent <= CACHE_HIT_FLOOR_PERCENT) {\n return { baselineRatio, candidateRatio, verdict: 'misses' };\n }\n return { baselineRatio, candidateRatio, verdict: 'ambiguous' };\n}\n"]}
|
|
@@ -42,8 +42,7 @@ const BUILTIN_PROVIDERS = [
|
|
|
42
42
|
supportedTools: [],
|
|
43
43
|
corsRestricted: false,
|
|
44
44
|
streamingCorsRestricted: false,
|
|
45
|
-
acceptsImageInput: false
|
|
46
|
-
thinkingMode: 'unsupported'
|
|
45
|
+
acceptsImageInput: false
|
|
47
46
|
},
|
|
48
47
|
{
|
|
49
48
|
id: 'anthropic',
|
|
@@ -70,7 +69,6 @@ const BUILTIN_PROVIDERS = [
|
|
|
70
69
|
corsRestricted: false,
|
|
71
70
|
streamingCorsRestricted: false,
|
|
72
71
|
acceptsImageInput: true,
|
|
73
|
-
thinkingMode: 'optional',
|
|
74
72
|
// Claude 5 family requires the adaptive thinking wire shape (thinking.type: 'adaptive' +
|
|
75
73
|
// output_config.effort) and 400s on the legacy thinking.type: 'enabled' + budget_tokens
|
|
76
74
|
// shape; see AiAssist.isAdaptiveThinkingModel.
|
|
@@ -105,10 +103,16 @@ const BUILTIN_PROVIDERS = [
|
|
|
105
103
|
'@google-gemini:embedding': 'gemini-embedding-001' // NOT deprecated — aliased for uniformity only
|
|
106
104
|
},
|
|
107
105
|
supportedTools: ['web_search'],
|
|
106
|
+
// `generateContent` HTTP-400s (INVALID_ARGUMENT) when built-in grounding
|
|
107
|
+
// (`web_search` → `google_search`) and `function_declarations` are combined in
|
|
108
|
+
// one request. The only descriptor in the registry that declares an exclusion;
|
|
109
|
+
// keep it that way deliberately — a new entry here changes what a host gets
|
|
110
|
+
// back without the host changing anything, so it wants the same evidence a
|
|
111
|
+
// capability claim wants.
|
|
112
|
+
serverToolsExclusiveWithClientTools: ['web_search'],
|
|
108
113
|
corsRestricted: false,
|
|
109
114
|
streamingCorsRestricted: false,
|
|
110
115
|
acceptsImageInput: true,
|
|
111
|
-
thinkingMode: 'optional',
|
|
112
116
|
// Gemini carries the constraint inside `generationConfig`
|
|
113
117
|
// (responseMimeType + responseSchema), uniform across the family.
|
|
114
118
|
structuredOutput: [{ modelPrefix: '', format: 'gemini-response-schema' }],
|
|
@@ -145,8 +149,7 @@ const BUILTIN_PROVIDERS = [
|
|
|
145
149
|
supportedTools: [],
|
|
146
150
|
corsRestricted: false,
|
|
147
151
|
streamingCorsRestricted: false,
|
|
148
|
-
acceptsImageInput: false
|
|
149
|
-
thinkingMode: 'unsupported'
|
|
152
|
+
acceptsImageInput: false
|
|
150
153
|
},
|
|
151
154
|
{
|
|
152
155
|
id: 'mistral',
|
|
@@ -160,7 +163,6 @@ const BUILTIN_PROVIDERS = [
|
|
|
160
163
|
corsRestricted: false,
|
|
161
164
|
streamingCorsRestricted: false,
|
|
162
165
|
acceptsImageInput: false,
|
|
163
|
-
thinkingMode: 'unsupported',
|
|
164
166
|
structuredOutput: [{ modelPrefix: '', format: 'openai-json-schema' }],
|
|
165
167
|
embedding: [{ modelPrefix: '', format: 'openai-embeddings' }]
|
|
166
168
|
},
|
|
@@ -176,7 +178,6 @@ const BUILTIN_PROVIDERS = [
|
|
|
176
178
|
corsRestricted: false,
|
|
177
179
|
streamingCorsRestricted: false,
|
|
178
180
|
acceptsImageInput: false,
|
|
179
|
-
thinkingMode: 'unsupported',
|
|
180
181
|
embedding: [{ modelPrefix: '', format: 'openai-embeddings' }]
|
|
181
182
|
},
|
|
182
183
|
{
|
|
@@ -210,7 +211,6 @@ const BUILTIN_PROVIDERS = [
|
|
|
210
211
|
corsRestricted: false,
|
|
211
212
|
streamingCorsRestricted: false,
|
|
212
213
|
acceptsImageInput: true,
|
|
213
|
-
thinkingMode: 'optional',
|
|
214
214
|
responsesOnlyModelPrefixes: ['gpt-5.5-pro'],
|
|
215
215
|
// Declared once for the whole line. The Chat-Completions-vs-Responses split is
|
|
216
216
|
// NOT declared here on purpose: the route depends on whether the call carries
|
|
@@ -262,7 +262,6 @@ const BUILTIN_PROVIDERS = [
|
|
|
262
262
|
corsRestricted: false,
|
|
263
263
|
streamingCorsRestricted: false,
|
|
264
264
|
acceptsImageInput: false,
|
|
265
|
-
thinkingMode: 'unsupported',
|
|
266
265
|
embedding: [{ modelPrefix: '', format: 'openai-embeddings' }]
|
|
267
266
|
},
|
|
268
267
|
{
|
|
@@ -287,7 +286,6 @@ const BUILTIN_PROVIDERS = [
|
|
|
287
286
|
corsRestricted: true,
|
|
288
287
|
streamingCorsRestricted: true,
|
|
289
288
|
acceptsImageInput: true,
|
|
290
|
-
thinkingMode: 'optional',
|
|
291
289
|
structuredOutput: [{ modelPrefix: '', format: 'openai-json-schema' }],
|
|
292
290
|
imageGeneration: [
|
|
293
291
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/registry.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Centralized provider registry — single source of truth for all AI provider metadata.\n * @packageDocumentation\n */\n\nimport { fail, Result, succeed } from '@fgv/ts-utils';\n\nimport {\n type AiProviderId,\n type IAiEmbeddingModelCapability,\n type IAiImageModelCapability,\n type IAiModelCapabilityConfig,\n type IAiProviderDescriptor,\n resolveModelAlias\n} from './model';\nimport type { IAiStructuredOutputCapability } from './structuredOutputTypes';\n\n// ============================================================================\n// Built-in providers\n// ============================================================================\n\n/**\n * All known AI provider descriptors. Copy-paste first, then alphabetical.\n * @internal\n */\nconst BUILTIN_PROVIDERS: ReadonlyArray<IAiProviderDescriptor> = [\n {\n id: 'copy-paste',\n label: 'Copy / Paste',\n buttonLabel: 'AI Assist | Copy',\n needsSecret: false,\n apiFormat: 'openai',\n baseUrl: '',\n defaultModel: '',\n supportedTools: [],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: false,\n thinkingMode: 'unsupported'\n },\n {\n id: 'anthropic',\n label: 'Anthropic Claude',\n buttonLabel: 'AI Assist | Claude',\n needsSecret: true,\n apiFormat: 'anthropic',\n baseUrl: 'https://api.anthropic.com/v1',\n defaultModel: {\n base: '@anthropic:sonnet', // claude-sonnet-5 (was 'claude-sonnet-4-5-20250929')\n advanced: '@anthropic:opus' // claude-opus-5\n // no frontier key → a frontier request cascades advanced → opus (see resolveModel)\n },\n aliases: {\n '@anthropic:sonnet': 'claude-sonnet-5', // base tier\n '@anthropic:opus': 'claude-opus-5', // advanced tier (was claude-opus-4-8; opus-5 is the drop-in successor at the same price)\n '@anthropic:haiku': 'claude-haiku-4-5-20251001', // NON-tier alias; modelOverride only\n '@anthropic:fable': 'claude-fable-5' // NON-tier alias; modelOverride only\n // NOTE: no thinking/image/embedding keys — Anthropic completions are all text; base\n // (sonnet-5) and advanced (opus-5) are both thinking-capable, so a thinking-context\n // call flat-falls to base safely.\n },\n supportedTools: ['web_search'],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: true,\n thinkingMode: 'optional',\n // Claude 5 family requires the adaptive thinking wire shape (thinking.type: 'adaptive' +\n // output_config.effort) and 400s on the legacy thinking.type: 'enabled' + budget_tokens\n // shape; see AiAssist.isAdaptiveThinkingModel.\n adaptiveThinkingModelPrefixes: ['claude-sonnet-5', 'claude-opus-5', 'claude-fable-5'],\n // Anthropic has no response-format field; forced tool use is the mechanism, and\n // it is uniform across the family — hence one catch-all entry.\n structuredOutput: [{ modelPrefix: '', format: 'anthropic-tool-forced' }]\n },\n {\n id: 'google-gemini',\n label: 'Google Gemini',\n buttonLabel: 'AI Assist | Gemini',\n needsSecret: true,\n apiFormat: 'gemini',\n baseUrl: 'https://generativelanguage.googleapis.com/v1beta',\n defaultModel: {\n base: '@google-gemini:flash',\n advanced: '@google-gemini:pro', // reuses the existing @google-gemini:pro alias (no new alias entry)\n image: '@google-gemini:flash-image',\n embedding: '@google-gemini:embedding'\n // no frontier key → a frontier request cascades advanced → pro (see resolveModel)\n },\n aliases: {\n // NOTE: the base flash line is at 3.5 while pro / flash-lite / flash-image are at 3.1 — this is\n // NOT a typo. The targets come verbatim from Google's official deprecation table: the flash base\n // line advanced to 3.5 while the other roles are on the 3.1 generation. The per-role version\n // split is exactly why the alias layer exists — consumers never see these numbers.\n '@google-gemini:flash': 'gemini-3.5-flash', // base (was gemini-2.5-flash, shutdown 2026-10-16)\n '@google-gemini:pro': 'gemini-3.1-pro-preview', // advanced-tier role (wired to the 'advanced' defaultModel key); also the frontier cascade target (was gemini-2.5-pro, 2026-10-16)\n '@google-gemini:flash-lite': 'gemini-3.1-flash-lite', // cheaper thinking-capable line; available via modelOverride only (was gemini-2.5-flash-lite, 2026-10-16)\n '@google-gemini:flash-image': 'gemini-3.1-flash-image', // image (GA id; was gemini-3.1-flash-image-preview, retired 2026-06-25)\n '@google-gemini:embedding': 'gemini-embedding-001' // NOT deprecated — aliased for uniformity only\n },\n supportedTools: ['web_search'],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: true,\n thinkingMode: 'optional',\n // Gemini carries the constraint inside `generationConfig`\n // (responseMimeType + responseSchema), uniform across the family.\n structuredOutput: [{ modelPrefix: '', format: 'gemini-response-schema' }],\n embedding: [\n {\n modelPrefix: '',\n format: 'gemini-embeddings',\n supportsDimensions: true,\n supportsTaskType: true,\n defaultDimensions: 3072\n }\n ],\n imageGeneration: [\n {\n // Gemini Flash Image: chat-style generateContent\n modelPrefix: '',\n format: 'gemini-image-out',\n acceptsImageReferenceInput: true,\n supportsQualityParam: false,\n maxCount: 1,\n outputParamStyle: 'none',\n defaultOutputMimeType: 'image/jpeg'\n }\n ]\n },\n {\n id: 'groq',\n label: 'Groq',\n buttonLabel: 'AI Assist | Groq',\n needsSecret: true,\n apiFormat: 'openai',\n baseUrl: 'https://api.groq.com/openai/v1',\n defaultModel: 'llama-3.3-70b-versatile',\n supportedTools: [],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: false,\n thinkingMode: 'unsupported'\n },\n {\n id: 'mistral',\n label: 'Mistral',\n buttonLabel: 'AI Assist | Mistral',\n needsSecret: true,\n apiFormat: 'openai',\n baseUrl: 'https://api.mistral.ai/v1',\n defaultModel: { base: 'mistral-large-latest', embedding: 'mistral-embed' },\n supportedTools: [],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: false,\n thinkingMode: 'unsupported',\n structuredOutput: [{ modelPrefix: '', format: 'openai-json-schema' }],\n embedding: [{ modelPrefix: '', format: 'openai-embeddings' }]\n },\n {\n id: 'ollama',\n label: 'Ollama (self-hosted)',\n buttonLabel: 'AI Assist | Ollama',\n needsSecret: false,\n apiFormat: 'openai',\n baseUrl: 'http://localhost:11434/v1',\n defaultModel: '',\n supportedTools: [],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: false,\n thinkingMode: 'unsupported',\n embedding: [{ modelPrefix: '', format: 'openai-embeddings' }]\n },\n {\n id: 'openai',\n label: 'OpenAI',\n buttonLabel: 'AI Assist | OpenAI',\n needsSecret: true,\n apiFormat: 'openai',\n baseUrl: 'https://api.openai.com/v1',\n defaultModel: {\n base: '@openai:mini', // gpt-5.6-luna (was gpt-5.4-mini; before that 'gpt-4o' — EOL-behind)\n advanced: '@openai:flagship', // gpt-5.6-terra\n // The gpt-5.6 family works on BOTH Chat Completions and the Responses API, so the\n // frontier tier no longer needs Responses-only routing. gpt-5.5-pro (the previous\n // frontier target) remains Responses-API-only and reachable via modelOverride; the\n // `responsesOnlyModelPrefixes` marker below still routes it correctly.\n frontier: '@openai:pro', // gpt-5.6-sol\n image: '@openai:image', // gpt-image-2 (was gpt-image-1.5; before that 'dall-e-3' — EOL 2026-05-12)\n embedding: '@openai:embedding' // text-embedding-3-small (unchanged, aliased for uniformity)\n },\n aliases: {\n '@openai:mini': 'gpt-5.6-luna', // base tier (was gpt-5.4-mini)\n '@openai:flagship': 'gpt-5.6-terra', // advanced tier (was gpt-5.5)\n '@openai:pro': 'gpt-5.6-sol', // frontier tier (was gpt-5.5-pro, which was Responses-API-only; 5.6 works on chat completions)\n '@openai:nano': 'gpt-5.4-nano', // NON-tier alias; modelOverride only\n '@openai:image': 'gpt-image-2', // image (matches the gpt-image- capability prefix; was gpt-image-1.5)\n '@openai:embedding': 'text-embedding-3-small' // NOT deprecated — aliased for uniformity\n // NOTE: gpt-5.1 deliberately absent — retired March 2026.\n },\n supportedTools: ['web_search'],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: true,\n thinkingMode: 'optional',\n responsesOnlyModelPrefixes: ['gpt-5.5-pro'],\n // Declared once for the whole line. The Chat-Completions-vs-Responses split is\n // NOT declared here on purpose: the route depends on whether the call carries\n // server tools as well as on the model, so the same model takes both endpoints\n // on different calls. The dispatcher supplies that axis; a second declaration\n // of it here could only ever disagree.\n structuredOutput: [{ modelPrefix: '', format: 'openai-json-schema' }],\n embedding: [\n {\n modelPrefix: 'text-embedding-3',\n format: 'openai-embeddings',\n supportsDimensions: true,\n maxBatchSize: 2048\n },\n {\n modelPrefix: '',\n format: 'openai-embeddings'\n }\n ],\n imageGeneration: [\n {\n modelPrefix: 'gpt-image-',\n format: 'openai-images',\n acceptsImageReferenceInput: true,\n acceptedSizes: ['1024x1024', '1536x1024', '1024x1536', 'auto'],\n supportsQualityParam: true,\n acceptedQualities: ['low', 'medium', 'high', 'auto'],\n maxCount: 10,\n outputParamStyle: 'output-format',\n defaultOutputMimeType: 'image/png'\n },\n {\n modelPrefix: '',\n format: 'openai-images',\n outputParamStyle: 'response-format',\n defaultOutputMimeType: 'image/png'\n }\n ]\n },\n {\n id: 'openai-compat',\n label: 'OpenAI-compatible (self-hosted)',\n buttonLabel: 'AI Assist | OpenAI-compat',\n needsSecret: false,\n apiFormat: 'openai',\n baseUrl: '',\n defaultModel: '',\n supportedTools: [],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: false,\n thinkingMode: 'unsupported',\n embedding: [{ modelPrefix: '', format: 'openai-embeddings' }]\n },\n {\n id: 'xai-grok',\n label: 'xAI Grok',\n buttonLabel: 'AI Assist | Grok',\n needsSecret: true,\n apiFormat: 'openai',\n baseUrl: 'https://api.x.ai/v1',\n defaultModel: {\n base: '@xai-grok:standard', // grok-4.3 (was the raw id; the cheap line + retirement-wave redirect target)\n advanced: '@xai-grok:flagship', // grok-4.5 (flagship since 2026-07-08; $2/$6 sits in the advanced band)\n image: '@xai-grok:imagine' // grok-imagine-image-quality (was the raw id)\n // no frontier key → a frontier request cascades advanced → grok-4.5 (see resolveModel)\n },\n aliases: {\n '@xai-grok:standard': 'grok-4.3', // base tier; NOT deprecated — superseded as flagship by grok-4.5\n '@xai-grok:flagship': 'grok-4.5', // advanced tier; configurable reasoning effort (default high)\n '@xai-grok:imagine': 'grok-imagine-image-quality' // image tier; current (redirect target for the retired -pro)\n },\n supportedTools: ['web_search'],\n corsRestricted: true,\n streamingCorsRestricted: true,\n acceptsImageInput: true,\n thinkingMode: 'optional',\n structuredOutput: [{ modelPrefix: '', format: 'openai-json-schema' }],\n imageGeneration: [\n {\n // grok-imagine models use JSON edits with image_url objects (different wire format)\n modelPrefix: 'grok-imagine-',\n format: 'xai-images-edits',\n acceptsImageReferenceInput: true,\n supportsQualityParam: false,\n maxCount: 10,\n outputParamStyle: 'response-format',\n defaultOutputMimeType: 'image/jpeg'\n },\n {\n // catch-all for other xai image models\n modelPrefix: '',\n format: 'xai-images',\n acceptsImageReferenceInput: false,\n supportsQualityParam: false,\n maxCount: 10,\n outputParamStyle: 'response-format',\n defaultOutputMimeType: 'image/jpeg'\n }\n ]\n }\n];\n\n/**\n * Index for O(1) lookup by id.\n * @internal\n */\nconst PROVIDER_BY_ID: ReadonlyMap<string, IAiProviderDescriptor> = new Map(\n BUILTIN_PROVIDERS.map((d) => [d.id, d])\n);\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * All valid provider ID values, in the same order as the registry.\n * @public\n */\nexport const allProviderIds: ReadonlyArray<AiProviderId> = BUILTIN_PROVIDERS.map((d) => d.id);\n\n/**\n * Get all known provider descriptors. Copy-paste first, then alphabetical.\n * @returns All built-in provider descriptors\n * @public\n */\nexport function getProviderDescriptors(): ReadonlyArray<IAiProviderDescriptor> {\n return BUILTIN_PROVIDERS;\n}\n\n/**\n * Get a provider descriptor by id.\n * @param id - The provider identifier\n * @returns The descriptor, or a failure if the provider is unknown\n * @public\n */\nexport function getProviderDescriptor(id: string): Result<IAiProviderDescriptor> {\n const descriptor = PROVIDER_BY_ID.get(id);\n if (!descriptor) {\n return fail(`unknown AI provider: ${id}`);\n }\n return succeed(descriptor);\n}\n\n/**\n * Whether a provider declares any image-generation capability at all.\n *\n * @param descriptor - The provider descriptor\n * @returns `true` when {@link IAiProviderDescriptor.imageGeneration} has at\n * least one entry; `false` otherwise.\n * @public\n */\nexport function supportsImageGeneration(descriptor: IAiProviderDescriptor): boolean {\n return (descriptor.imageGeneration?.length ?? 0) > 0;\n}\n\n/**\n * Shared longest-prefix capability match, alias-guarded.\n *\n * @remarks\n * `modelId` is resolved through `resolveModelAlias` **before** any prefix\n * matching, so an fgv alias (`@<provider>:<role>`) and the concrete id it names\n * select the same capability. Without this step an alias would fall through to\n * the `modelPrefix: ''` catch-all every provider declares and yield a\n * confidently wrong capability rather than no capability.\n *\n * An unresolvable alias (sigil-prefixed but unregistered, or cyclic) names no\n * model, so no capability applies and the match is skipped entirely — it must\n * never be prefix-matched verbatim. Callers already treat `undefined` as \"this\n * provider has no capability for this model\" and fail with that message.\n *\n * A concrete (non-sigil) id passes through `resolveModelAlias` verbatim, so\n * behavior for every raw provider id is unchanged.\n * @internal\n */\nfunction resolveCapabilityForModel<TCapability extends { readonly modelPrefix: string }>(\n descriptor: IAiProviderDescriptor,\n modelId: string,\n capabilities: ReadonlyArray<TCapability> | undefined\n): TCapability | undefined {\n const concreteModelId = resolveModelAlias(descriptor, modelId).orDefault();\n if (concreteModelId === undefined) {\n return undefined;\n }\n return (capabilities ?? [])\n .filter((cap) => concreteModelId.startsWith(cap.modelPrefix))\n .reduce<TCapability | undefined>(\n (best, cap) => (best && best.modelPrefix.length >= cap.modelPrefix.length ? best : cap),\n undefined\n );\n}\n\n/**\n * Resolve the image-generation capability that applies to a given model id\n * for a provider. Returns the entry from\n * {@link IAiProviderDescriptor.imageGeneration} whose `modelPrefix` is the\n * longest prefix of `modelId`. Ties are broken by first-encountered, so rule\n * order does not matter for correctness — only for tie-breaking among rules\n * with identical-length prefixes (an unusual case).\n *\n * @remarks\n * `modelId` may be either a concrete provider model id or an fgv model alias\n * (`@<provider>:<role>`, see `MODEL_ALIAS_SIGIL`) — it is resolved via\n * `resolveModelAlias` against `descriptor.aliases` before prefix matching,\n * so both forms select the same capability. A raw provider id passes through\n * unchanged. An alias that is not registered on `descriptor` (or is cyclic)\n * names no model and yields `undefined` rather than falling through to the\n * `modelPrefix: ''` catch-all.\n *\n * @param descriptor - The provider descriptor\n * @param modelId - The image model id — concrete or an fgv alias\n * @returns The matching capability, or `undefined` when no rule matches, the\n * provider declares no image-generation capabilities, or `modelId` is an\n * unresolvable alias.\n * @public\n */\nexport function resolveImageCapability(\n descriptor: IAiProviderDescriptor,\n modelId: string\n): IAiImageModelCapability | undefined {\n return resolveCapabilityForModel(descriptor, modelId, descriptor.imageGeneration);\n}\n\n/**\n * Whether a provider declares any embedding capability at all.\n *\n * @param descriptor - The provider descriptor\n * @returns `true` when {@link IAiProviderDescriptor.embedding} has at least one\n * entry; `false` otherwise.\n * @public\n */\nexport function supportsEmbedding(descriptor: IAiProviderDescriptor): boolean {\n return (descriptor.embedding?.length ?? 0) > 0;\n}\n\n/**\n * The structured-output capability for `modelId` under `descriptor`, or\n * `undefined` when the model can enforce nothing.\n *\n * @remarks\n * Alias-first, exactly like its `imageGeneration` / `embedding` siblings — an\n * unresolved alias returns `undefined` rather than prefix-matching a catch-all\n * `modelPrefix: ''`, which is the defect this helper was written to prevent.\n *\n * @param descriptor - The provider descriptor.\n * @param modelId - A concrete model id or an `@provider:role` alias.\n * @public\n */\nexport function resolveStructuredOutputCapability(\n descriptor: IAiProviderDescriptor,\n modelId: string\n): IAiStructuredOutputCapability | undefined {\n return resolveCapabilityForModel(descriptor, modelId, descriptor.structuredOutput);\n}\n\n/**\n * Whether `descriptor` declares any structured-output capability at all.\n * @public\n */\nexport function supportsStructuredOutput(descriptor: IAiProviderDescriptor): boolean {\n return (descriptor.structuredOutput?.length ?? 0) > 0;\n}\n\n/**\n * Resolve the embedding capability that applies to a given model id for a\n * provider. Returns the entry from {@link IAiProviderDescriptor.embedding} whose\n * `modelPrefix` is the longest prefix of `modelId`. Ties are broken by\n * first-encountered.\n *\n * @remarks\n * `modelId` may be either a concrete provider model id or an fgv model alias\n * (`@<provider>:<role>`, see `MODEL_ALIAS_SIGIL`) — it is resolved via\n * `resolveModelAlias` against `descriptor.aliases` before prefix matching,\n * so both forms select the same capability. A raw provider id passes through\n * unchanged. An alias that is not registered on `descriptor` (or is cyclic)\n * names no model and yields `undefined` rather than falling through to the\n * `modelPrefix: ''` catch-all.\n *\n * @param descriptor - The provider descriptor\n * @param modelId - The embedding model id — concrete or an fgv alias\n * @returns The matching capability, or `undefined` when no rule matches, the\n * provider declares no embedding capabilities, or `modelId` is an\n * unresolvable alias.\n * @public\n */\nexport function resolveEmbeddingCapability(\n descriptor: IAiProviderDescriptor,\n modelId: string\n): IAiEmbeddingModelCapability | undefined {\n return resolveCapabilityForModel(descriptor, modelId, descriptor.embedding);\n}\n\n// ============================================================================\n// Default model capability config\n// ============================================================================\n\n/**\n * Default capability config used by `callProviderListModels` when callers\n * don't supply their own. Patterns are intentionally narrow — false\n * positives are worse than missing a model. Caller can override per call\n * via {@link IProviderListModelsParams.capabilityConfig}.\n *\n * @public\n */\nexport const DEFAULT_MODEL_CAPABILITY_CONFIG: IAiModelCapabilityConfig = {\n perProvider: {\n openai: [\n { idPattern: /^gpt-image/, capabilities: ['image-generation'] },\n { idPattern: /^text-embedding/, capabilities: ['embedding'] },\n { idPattern: /^gpt-5/, capabilities: ['chat', 'tools', 'vision', 'thinking'] },\n { idPattern: /^gpt-4/, capabilities: ['chat', 'tools', 'vision'] },\n { idPattern: /^gpt-3\\.5/, capabilities: ['chat'] },\n { idPattern: /^o\\d/, capabilities: ['chat', 'tools', 'thinking'] }\n ],\n 'xai-grok': [\n { idPattern: /-image/, capabilities: ['image-generation'] },\n // grok-4.5 needs its own rule: it would otherwise hit only /^grok-4/ and lose\n // thinking, yet it has configurable reasoning effort. Detection accumulates across\n // matching rules, so /^grok-4/ still contributes vision (same as grok-4.3).\n { idPattern: /^grok-4\\.5/, capabilities: ['chat', 'tools', 'thinking'] },\n { idPattern: /^grok-4\\.3/, capabilities: ['chat', 'tools', 'thinking'] },\n { idPattern: /^grok-4$/, capabilities: ['chat', 'tools', 'thinking'] },\n { idPattern: /^grok-4/, capabilities: ['chat', 'tools', 'vision'] },\n { idPattern: /^grok-3-mini/, capabilities: ['chat', 'tools', 'thinking'] },\n { idPattern: /^grok-3/, capabilities: ['chat', 'tools'] },\n { idPattern: /^grok-2/, capabilities: ['chat', 'vision'] }\n ],\n 'google-gemini': [\n { idPattern: /^imagen/, capabilities: ['image-generation'] },\n { idPattern: /^gemini-.*-image/, capabilities: ['image-generation'] },\n { idPattern: /embedding/, capabilities: ['embedding'] },\n { idPattern: /^gemini-3/, capabilities: ['chat', 'tools', 'vision', 'thinking'] },\n { idPattern: /^gemini-2\\.5/, capabilities: ['chat', 'tools', 'vision', 'thinking'] },\n { idPattern: /^gemini-/, capabilities: ['chat', 'tools', 'vision'] }\n ],\n anthropic: [\n // Broadened from /^claude-opus-4/ and /^claude-sonnet-4/ so the sonnet-5+ / opus-5+ lines\n // are detected as thinking-capable. Detection accumulates across matching rules, so this is\n // purely additive: every existing opus-4 / sonnet-4 id still matches. Both broadenings are\n // now load-bearing: claude-sonnet-5 and claude-opus-5 (the advanced-tier target) would\n // otherwise hit only /^claude-/ and lose thinking. The fable rule keeps the\n // AnthropicThinkingModelNames union honest — claude-fable-5 (modelOverride-only) would\n // otherwise fall to the /^claude-/ catch-all and be detected without thinking.\n { idPattern: /^claude-opus-/, capabilities: ['chat', 'tools', 'vision', 'thinking'] },\n { idPattern: /^claude-sonnet-/, capabilities: ['chat', 'tools', 'vision', 'thinking'] },\n { idPattern: /^claude-fable-/, capabilities: ['chat', 'tools', 'vision', 'thinking'] },\n { idPattern: /^claude-/, capabilities: ['chat', 'tools', 'vision'] }\n ],\n groq: [{ idPattern: /./, capabilities: ['chat'] }],\n mistral: [\n { idPattern: /embed/, capabilities: ['embedding'] },\n { idPattern: /./, capabilities: ['chat'] }\n ],\n // Self-hosted OpenAI-compatible servers (Ollama, LM Studio, llama.cpp) serve\n // arbitrary, caller-chosen models whose ids we can't enumerate ahead of time.\n // The catch-all `/./` intentionally departs from the \"narrow patterns\" rule\n // above: assume `chat` for everything and let the caller override via\n // `capabilityConfig` when they know their deployment serves image/embedding models.\n ollama: [{ idPattern: /./, capabilities: ['chat'] }],\n 'openai-compat': [{ idPattern: /./, capabilities: ['chat'] }]\n }\n};\n"]}
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/registry.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;AAEZ;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAU,OAAO,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,EAML,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAGjB,+EAA+E;AAC/E,qBAAqB;AACrB,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,iBAAiB,GAAyC;IAC9D;QACE,EAAE,EAAE,YAAY;QAChB,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,kBAAkB;QAC/B,WAAW,EAAE,KAAK;QAClB,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,EAAE;QACX,YAAY,EAAE,EAAE;QAChB,cAAc,EAAE,EAAE;QAClB,cAAc,EAAE,KAAK;QACrB,uBAAuB,EAAE,KAAK;QAC9B,iBAAiB,EAAE,KAAK;KACzB;IACD;QACE,EAAE,EAAE,WAAW;QACf,KAAK,EAAE,kBAAkB;QACzB,WAAW,EAAE,oBAAoB;QACjC,WAAW,EAAE,IAAI;QACjB,SAAS,EAAE,WAAW;QACtB,OAAO,EAAE,8BAA8B;QACvC,YAAY,EAAE;YACZ,IAAI,EAAE,mBAAmB,EAAE,qDAAqD;YAChF,QAAQ,EAAE,iBAAiB,CAAC,gBAAgB;YAC5C,mFAAmF;SACpF;QACD,OAAO,EAAE;YACP,mBAAmB,EAAE,iBAAiB,EAAE,YAAY;YACpD,iBAAiB,EAAE,eAAe,EAAE,yFAAyF;YAC7H,kBAAkB,EAAE,2BAA2B,EAAE,qCAAqC;YACtF,kBAAkB,EAAE,gBAAgB,CAAC,qCAAqC;YAC1E,oFAAoF;YACpF,oFAAoF;YACpF,kCAAkC;SACnC;QACD,cAAc,EAAE,CAAC,YAAY,CAAC;QAC9B,cAAc,EAAE,KAAK;QACrB,uBAAuB,EAAE,KAAK;QAC9B,iBAAiB,EAAE,IAAI;QACvB,yFAAyF;QACzF,wFAAwF;QACxF,+CAA+C;QAC/C,6BAA6B,EAAE,CAAC,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,CAAC;QACrF,gFAAgF;QAChF,+DAA+D;QAC/D,gBAAgB,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;KACzE;IACD;QACE,EAAE,EAAE,eAAe;QACnB,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,oBAAoB;QACjC,WAAW,EAAE,IAAI;QACjB,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,kDAAkD;QAC3D,YAAY,EAAE;YACZ,IAAI,EAAE,sBAAsB;YAC5B,QAAQ,EAAE,oBAAoB,EAAE,oEAAoE;YACpG,KAAK,EAAE,4BAA4B;YACnC,SAAS,EAAE,0BAA0B;YACrC,kFAAkF;SACnF;QACD,OAAO,EAAE;YACP,gGAAgG;YAChG,iGAAiG;YACjG,6FAA6F;YAC7F,mFAAmF;YACnF,sBAAsB,EAAE,kBAAkB,EAAE,mDAAmD;YAC/F,oBAAoB,EAAE,wBAAwB,EAAE,mIAAmI;YACnL,2BAA2B,EAAE,uBAAuB,EAAE,0GAA0G;YAChK,4BAA4B,EAAE,wBAAwB,EAAE,wEAAwE;YAChI,0BAA0B,EAAE,sBAAsB,CAAC,+CAA+C;SACnG;QACD,cAAc,EAAE,CAAC,YAAY,CAAC;QAC9B,yEAAyE;QACzE,+EAA+E;QAC/E,+EAA+E;QAC/E,4EAA4E;QAC5E,2EAA2E;QAC3E,0BAA0B;QAC1B,mCAAmC,EAAE,CAAC,YAAY,CAAC;QACnD,cAAc,EAAE,KAAK;QACrB,uBAAuB,EAAE,KAAK;QAC9B,iBAAiB,EAAE,IAAI;QACvB,0DAA0D;QAC1D,kEAAkE;QAClE,gBAAgB,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC;QACzE,SAAS,EAAE;YACT;gBACE,WAAW,EAAE,EAAE;gBACf,MAAM,EAAE,mBAAmB;gBAC3B,kBAAkB,EAAE,IAAI;gBACxB,gBAAgB,EAAE,IAAI;gBACtB,iBAAiB,EAAE,IAAI;aACxB;SACF;QACD,eAAe,EAAE;YACf;gBACE,iDAAiD;gBACjD,WAAW,EAAE,EAAE;gBACf,MAAM,EAAE,kBAAkB;gBAC1B,0BAA0B,EAAE,IAAI;gBAChC,oBAAoB,EAAE,KAAK;gBAC3B,QAAQ,EAAE,CAAC;gBACX,gBAAgB,EAAE,MAAM;gBACxB,qBAAqB,EAAE,YAAY;aACpC;SACF;KACF;IACD;QACE,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,kBAAkB;QAC/B,WAAW,EAAE,IAAI;QACjB,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,gCAAgC;QACzC,YAAY,EAAE,yBAAyB;QACvC,cAAc,EAAE,EAAE;QAClB,cAAc,EAAE,KAAK;QACrB,uBAAuB,EAAE,KAAK;QAC9B,iBAAiB,EAAE,KAAK;KACzB;IACD;QACE,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,SAAS;QAChB,WAAW,EAAE,qBAAqB;QAClC,WAAW,EAAE,IAAI;QACjB,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,2BAA2B;QACpC,YAAY,EAAE,EAAE,IAAI,EAAE,sBAAsB,EAAE,SAAS,EAAE,eAAe,EAAE;QAC1E,cAAc,EAAE,EAAE;QAClB,cAAc,EAAE,KAAK;QACrB,uBAAuB,EAAE,KAAK;QAC9B,iBAAiB,EAAE,KAAK;QACxB,gBAAgB,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;QACrE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;KAC9D;IACD;QACE,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EAAE,oBAAoB;QACjC,WAAW,EAAE,KAAK;QAClB,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,2BAA2B;QACpC,YAAY,EAAE,EAAE;QAChB,cAAc,EAAE,EAAE;QAClB,cAAc,EAAE,KAAK;QACrB,uBAAuB,EAAE,KAAK;QAC9B,iBAAiB,EAAE,KAAK;QACxB,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;KAC9D;IACD;QACE,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,QAAQ;QACf,WAAW,EAAE,oBAAoB;QACjC,WAAW,EAAE,IAAI;QACjB,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,2BAA2B;QACpC,YAAY,EAAE;YACZ,IAAI,EAAE,cAAc,EAAE,qEAAqE;YAC3F,QAAQ,EAAE,kBAAkB,EAAE,gBAAgB;YAC9C,kFAAkF;YAClF,kFAAkF;YAClF,mFAAmF;YACnF,uEAAuE;YACvE,QAAQ,EAAE,aAAa,EAAE,cAAc;YACvC,KAAK,EAAE,eAAe,EAAE,2EAA2E;YACnG,SAAS,EAAE,mBAAmB,CAAC,6DAA6D;SAC7F;QACD,OAAO,EAAE;YACP,cAAc,EAAE,cAAc,EAAE,+BAA+B;YAC/D,kBAAkB,EAAE,eAAe,EAAE,8BAA8B;YACnE,aAAa,EAAE,aAAa,EAAE,+FAA+F;YAC7H,cAAc,EAAE,cAAc,EAAE,qCAAqC;YACrE,eAAe,EAAE,aAAa,EAAE,sEAAsE;YACtG,mBAAmB,EAAE,wBAAwB,CAAC,0CAA0C;YACxF,0DAA0D;SAC3D;QACD,cAAc,EAAE,CAAC,YAAY,CAAC;QAC9B,cAAc,EAAE,KAAK;QACrB,uBAAuB,EAAE,KAAK;QAC9B,iBAAiB,EAAE,IAAI;QACvB,0BAA0B,EAAE,CAAC,aAAa,CAAC;QAC3C,+EAA+E;QAC/E,8EAA8E;QAC9E,+EAA+E;QAC/E,8EAA8E;QAC9E,uCAAuC;QACvC,gBAAgB,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;QACrE,SAAS,EAAE;YACT;gBACE,WAAW,EAAE,kBAAkB;gBAC/B,MAAM,EAAE,mBAAmB;gBAC3B,kBAAkB,EAAE,IAAI;gBACxB,YAAY,EAAE,IAAI;aACnB;YACD;gBACE,WAAW,EAAE,EAAE;gBACf,MAAM,EAAE,mBAAmB;aAC5B;SACF;QACD,eAAe,EAAE;YACf;gBACE,WAAW,EAAE,YAAY;gBACzB,MAAM,EAAE,eAAe;gBACvB,0BAA0B,EAAE,IAAI;gBAChC,aAAa,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,CAAC;gBAC9D,oBAAoB,EAAE,IAAI;gBAC1B,iBAAiB,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;gBACpD,QAAQ,EAAE,EAAE;gBACZ,gBAAgB,EAAE,eAAe;gBACjC,qBAAqB,EAAE,WAAW;aACnC;YACD;gBACE,WAAW,EAAE,EAAE;gBACf,MAAM,EAAE,eAAe;gBACvB,gBAAgB,EAAE,iBAAiB;gBACnC,qBAAqB,EAAE,WAAW;aACnC;SACF;KACF;IACD;QACE,EAAE,EAAE,eAAe;QACnB,KAAK,EAAE,iCAAiC;QACxC,WAAW,EAAE,2BAA2B;QACxC,WAAW,EAAE,KAAK;QAClB,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,EAAE;QACX,YAAY,EAAE,EAAE;QAChB,cAAc,EAAE,EAAE;QAClB,cAAc,EAAE,KAAK;QACrB,uBAAuB,EAAE,KAAK;QAC9B,iBAAiB,EAAE,KAAK;QACxB,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;KAC9D;IACD;QACE,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,UAAU;QACjB,WAAW,EAAE,kBAAkB;QAC/B,WAAW,EAAE,IAAI;QACjB,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,qBAAqB;QAC9B,YAAY,EAAE;YACZ,IAAI,EAAE,oBAAoB,EAAE,8EAA8E;YAC1G,QAAQ,EAAE,oBAAoB,EAAE,wEAAwE;YACxG,KAAK,EAAE,mBAAmB,CAAC,8CAA8C;YACzE,uFAAuF;SACxF;QACD,OAAO,EAAE;YACP,oBAAoB,EAAE,UAAU,EAAE,iEAAiE;YACnG,oBAAoB,EAAE,UAAU,EAAE,8DAA8D;YAChG,mBAAmB,EAAE,4BAA4B,CAAC,6DAA6D;SAChH;QACD,cAAc,EAAE,CAAC,YAAY,CAAC;QAC9B,cAAc,EAAE,IAAI;QACpB,uBAAuB,EAAE,IAAI;QAC7B,iBAAiB,EAAE,IAAI;QACvB,gBAAgB,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;QACrE,eAAe,EAAE;YACf;gBACE,oFAAoF;gBACpF,WAAW,EAAE,eAAe;gBAC5B,MAAM,EAAE,kBAAkB;gBAC1B,0BAA0B,EAAE,IAAI;gBAChC,oBAAoB,EAAE,KAAK;gBAC3B,QAAQ,EAAE,EAAE;gBACZ,gBAAgB,EAAE,iBAAiB;gBACnC,qBAAqB,EAAE,YAAY;aACpC;YACD;gBACE,uCAAuC;gBACvC,WAAW,EAAE,EAAE;gBACf,MAAM,EAAE,YAAY;gBACpB,0BAA0B,EAAE,KAAK;gBACjC,oBAAoB,EAAE,KAAK;gBAC3B,QAAQ,EAAE,EAAE;gBACZ,gBAAgB,EAAE,iBAAiB;gBACnC,qBAAqB,EAAE,YAAY;aACpC;SACF;KACF;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,cAAc,GAA+C,IAAI,GAAG,CACxE,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CACxC,CAAC;AAEF,+EAA+E;AAC/E,aAAa;AACb,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAgC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAE9F;;;;GAIG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,EAAU;IAC9C,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAAiC;;IACvE,OAAO,CAAC,MAAA,MAAA,UAAU,CAAC,eAAe,0CAAE,MAAM,mCAAI,CAAC,CAAC,GAAG,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAS,yBAAyB,CAChC,UAAiC,EACjC,OAAe,EACf,YAAoD;IAEpD,MAAM,eAAe,GAAG,iBAAiB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC;IAC3E,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,CAAC,YAAY,aAAZ,YAAY,cAAZ,YAAY,GAAI,EAAE,CAAC;SACxB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,eAAe,CAAC,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;SAC5D,MAAM,CACL,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EACvF,SAAS,CACV,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,sBAAsB,CACpC,UAAiC,EACjC,OAAe;IAEf,OAAO,yBAAyB,CAAC,UAAU,EAAE,OAAO,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC;AACpF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAiC;;IACjE,OAAO,CAAC,MAAA,MAAA,UAAU,CAAC,SAAS,0CAAE,MAAM,mCAAI,CAAC,CAAC,GAAG,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,iCAAiC,CAC/C,UAAiC,EACjC,OAAe;IAEf,OAAO,yBAAyB,CAAC,UAAU,EAAE,OAAO,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;AACrF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,UAAiC;;IACxE,OAAO,CAAC,MAAA,MAAA,UAAU,CAAC,gBAAgB,0CAAE,MAAM,mCAAI,CAAC,CAAC,GAAG,CAAC,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,0BAA0B,CACxC,UAAiC,EACjC,OAAe;IAEf,OAAO,yBAAyB,CAAC,UAAU,EAAE,OAAO,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;AAC9E,CAAC;AAED,+EAA+E;AAC/E,kCAAkC;AAClC,+EAA+E;AAE/E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAA6B;IACvE,WAAW,EAAE;QACX,MAAM,EAAE;YACN,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC,kBAAkB,CAAC,EAAE;YAC/D,EAAE,SAAS,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAC,WAAW,CAAC,EAAE;YAC7D,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE;YAC9E,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE;YAClE,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC,MAAM,CAAC,EAAE;YAClD,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE;SACnE;QACD,UAAU,EAAE;YACV,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,kBAAkB,CAAC,EAAE;YAC3D,8EAA8E;YAC9E,mFAAmF;YACnF,4EAA4E;YAC5E,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE;YACxE,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE;YACxE,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE;YACtE,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE;YACnE,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE;YAC1E,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;YACzD,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE;SAC3D;QACD,eAAe,EAAE;YACf,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,kBAAkB,CAAC,EAAE;YAC5D,EAAE,SAAS,EAAE,kBAAkB,EAAE,YAAY,EAAE,CAAC,kBAAkB,CAAC,EAAE;YACrE,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC,WAAW,CAAC,EAAE;YACvD,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE;YACjF,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE;YACpF,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE;SACrE;QACD,SAAS,EAAE;YACT,0FAA0F;YAC1F,4FAA4F;YAC5F,2FAA2F;YAC3F,uFAAuF;YACvF,4EAA4E;YAC5E,uFAAuF;YACvF,+EAA+E;YAC/E,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE;YACrF,EAAE,SAAS,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE;YACvF,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE;YACtF,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE;SACrE;QACD,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,OAAO,EAAE;YACP,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,WAAW,CAAC,EAAE;YACnD,EAAE,SAAS,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,MAAM,CAAC,EAAE;SAC3C;QACD,6EAA6E;QAC7E,8EAA8E;QAC9E,4EAA4E;QAC5E,sEAAsE;QACtE,oFAAoF;QACpF,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;QACpD,eAAe,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;KAC9D;CACF,CAAC","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Centralized provider registry — single source of truth for all AI provider metadata.\n * @packageDocumentation\n */\n\nimport { fail, Result, succeed } from '@fgv/ts-utils';\n\nimport {\n type AiProviderId,\n type IAiEmbeddingModelCapability,\n type IAiImageModelCapability,\n type IAiModelCapabilityConfig,\n type IAiProviderDescriptor,\n resolveModelAlias\n} from './model';\nimport type { IAiStructuredOutputCapability } from './structuredOutputTypes';\n\n// ============================================================================\n// Built-in providers\n// ============================================================================\n\n/**\n * All known AI provider descriptors. Copy-paste first, then alphabetical.\n * @internal\n */\nconst BUILTIN_PROVIDERS: ReadonlyArray<IAiProviderDescriptor> = [\n {\n id: 'copy-paste',\n label: 'Copy / Paste',\n buttonLabel: 'AI Assist | Copy',\n needsSecret: false,\n apiFormat: 'openai',\n baseUrl: '',\n defaultModel: '',\n supportedTools: [],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: false\n },\n {\n id: 'anthropic',\n label: 'Anthropic Claude',\n buttonLabel: 'AI Assist | Claude',\n needsSecret: true,\n apiFormat: 'anthropic',\n baseUrl: 'https://api.anthropic.com/v1',\n defaultModel: {\n base: '@anthropic:sonnet', // claude-sonnet-5 (was 'claude-sonnet-4-5-20250929')\n advanced: '@anthropic:opus' // claude-opus-5\n // no frontier key → a frontier request cascades advanced → opus (see resolveModel)\n },\n aliases: {\n '@anthropic:sonnet': 'claude-sonnet-5', // base tier\n '@anthropic:opus': 'claude-opus-5', // advanced tier (was claude-opus-4-8; opus-5 is the drop-in successor at the same price)\n '@anthropic:haiku': 'claude-haiku-4-5-20251001', // NON-tier alias; modelOverride only\n '@anthropic:fable': 'claude-fable-5' // NON-tier alias; modelOverride only\n // NOTE: no thinking/image/embedding keys — Anthropic completions are all text; base\n // (sonnet-5) and advanced (opus-5) are both thinking-capable, so a thinking-context\n // call flat-falls to base safely.\n },\n supportedTools: ['web_search'],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: true,\n // Claude 5 family requires the adaptive thinking wire shape (thinking.type: 'adaptive' +\n // output_config.effort) and 400s on the legacy thinking.type: 'enabled' + budget_tokens\n // shape; see AiAssist.isAdaptiveThinkingModel.\n adaptiveThinkingModelPrefixes: ['claude-sonnet-5', 'claude-opus-5', 'claude-fable-5'],\n // Anthropic has no response-format field; forced tool use is the mechanism, and\n // it is uniform across the family — hence one catch-all entry.\n structuredOutput: [{ modelPrefix: '', format: 'anthropic-tool-forced' }]\n },\n {\n id: 'google-gemini',\n label: 'Google Gemini',\n buttonLabel: 'AI Assist | Gemini',\n needsSecret: true,\n apiFormat: 'gemini',\n baseUrl: 'https://generativelanguage.googleapis.com/v1beta',\n defaultModel: {\n base: '@google-gemini:flash',\n advanced: '@google-gemini:pro', // reuses the existing @google-gemini:pro alias (no new alias entry)\n image: '@google-gemini:flash-image',\n embedding: '@google-gemini:embedding'\n // no frontier key → a frontier request cascades advanced → pro (see resolveModel)\n },\n aliases: {\n // NOTE: the base flash line is at 3.5 while pro / flash-lite / flash-image are at 3.1 — this is\n // NOT a typo. The targets come verbatim from Google's official deprecation table: the flash base\n // line advanced to 3.5 while the other roles are on the 3.1 generation. The per-role version\n // split is exactly why the alias layer exists — consumers never see these numbers.\n '@google-gemini:flash': 'gemini-3.5-flash', // base (was gemini-2.5-flash, shutdown 2026-10-16)\n '@google-gemini:pro': 'gemini-3.1-pro-preview', // advanced-tier role (wired to the 'advanced' defaultModel key); also the frontier cascade target (was gemini-2.5-pro, 2026-10-16)\n '@google-gemini:flash-lite': 'gemini-3.1-flash-lite', // cheaper thinking-capable line; available via modelOverride only (was gemini-2.5-flash-lite, 2026-10-16)\n '@google-gemini:flash-image': 'gemini-3.1-flash-image', // image (GA id; was gemini-3.1-flash-image-preview, retired 2026-06-25)\n '@google-gemini:embedding': 'gemini-embedding-001' // NOT deprecated — aliased for uniformity only\n },\n supportedTools: ['web_search'],\n // `generateContent` HTTP-400s (INVALID_ARGUMENT) when built-in grounding\n // (`web_search` → `google_search`) and `function_declarations` are combined in\n // one request. The only descriptor in the registry that declares an exclusion;\n // keep it that way deliberately — a new entry here changes what a host gets\n // back without the host changing anything, so it wants the same evidence a\n // capability claim wants.\n serverToolsExclusiveWithClientTools: ['web_search'],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: true,\n // Gemini carries the constraint inside `generationConfig`\n // (responseMimeType + responseSchema), uniform across the family.\n structuredOutput: [{ modelPrefix: '', format: 'gemini-response-schema' }],\n embedding: [\n {\n modelPrefix: '',\n format: 'gemini-embeddings',\n supportsDimensions: true,\n supportsTaskType: true,\n defaultDimensions: 3072\n }\n ],\n imageGeneration: [\n {\n // Gemini Flash Image: chat-style generateContent\n modelPrefix: '',\n format: 'gemini-image-out',\n acceptsImageReferenceInput: true,\n supportsQualityParam: false,\n maxCount: 1,\n outputParamStyle: 'none',\n defaultOutputMimeType: 'image/jpeg'\n }\n ]\n },\n {\n id: 'groq',\n label: 'Groq',\n buttonLabel: 'AI Assist | Groq',\n needsSecret: true,\n apiFormat: 'openai',\n baseUrl: 'https://api.groq.com/openai/v1',\n defaultModel: 'llama-3.3-70b-versatile',\n supportedTools: [],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: false\n },\n {\n id: 'mistral',\n label: 'Mistral',\n buttonLabel: 'AI Assist | Mistral',\n needsSecret: true,\n apiFormat: 'openai',\n baseUrl: 'https://api.mistral.ai/v1',\n defaultModel: { base: 'mistral-large-latest', embedding: 'mistral-embed' },\n supportedTools: [],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: false,\n structuredOutput: [{ modelPrefix: '', format: 'openai-json-schema' }],\n embedding: [{ modelPrefix: '', format: 'openai-embeddings' }]\n },\n {\n id: 'ollama',\n label: 'Ollama (self-hosted)',\n buttonLabel: 'AI Assist | Ollama',\n needsSecret: false,\n apiFormat: 'openai',\n baseUrl: 'http://localhost:11434/v1',\n defaultModel: '',\n supportedTools: [],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: false,\n embedding: [{ modelPrefix: '', format: 'openai-embeddings' }]\n },\n {\n id: 'openai',\n label: 'OpenAI',\n buttonLabel: 'AI Assist | OpenAI',\n needsSecret: true,\n apiFormat: 'openai',\n baseUrl: 'https://api.openai.com/v1',\n defaultModel: {\n base: '@openai:mini', // gpt-5.6-luna (was gpt-5.4-mini; before that 'gpt-4o' — EOL-behind)\n advanced: '@openai:flagship', // gpt-5.6-terra\n // The gpt-5.6 family works on BOTH Chat Completions and the Responses API, so the\n // frontier tier no longer needs Responses-only routing. gpt-5.5-pro (the previous\n // frontier target) remains Responses-API-only and reachable via modelOverride; the\n // `responsesOnlyModelPrefixes` marker below still routes it correctly.\n frontier: '@openai:pro', // gpt-5.6-sol\n image: '@openai:image', // gpt-image-2 (was gpt-image-1.5; before that 'dall-e-3' — EOL 2026-05-12)\n embedding: '@openai:embedding' // text-embedding-3-small (unchanged, aliased for uniformity)\n },\n aliases: {\n '@openai:mini': 'gpt-5.6-luna', // base tier (was gpt-5.4-mini)\n '@openai:flagship': 'gpt-5.6-terra', // advanced tier (was gpt-5.5)\n '@openai:pro': 'gpt-5.6-sol', // frontier tier (was gpt-5.5-pro, which was Responses-API-only; 5.6 works on chat completions)\n '@openai:nano': 'gpt-5.4-nano', // NON-tier alias; modelOverride only\n '@openai:image': 'gpt-image-2', // image (matches the gpt-image- capability prefix; was gpt-image-1.5)\n '@openai:embedding': 'text-embedding-3-small' // NOT deprecated — aliased for uniformity\n // NOTE: gpt-5.1 deliberately absent — retired March 2026.\n },\n supportedTools: ['web_search'],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: true,\n responsesOnlyModelPrefixes: ['gpt-5.5-pro'],\n // Declared once for the whole line. The Chat-Completions-vs-Responses split is\n // NOT declared here on purpose: the route depends on whether the call carries\n // server tools as well as on the model, so the same model takes both endpoints\n // on different calls. The dispatcher supplies that axis; a second declaration\n // of it here could only ever disagree.\n structuredOutput: [{ modelPrefix: '', format: 'openai-json-schema' }],\n embedding: [\n {\n modelPrefix: 'text-embedding-3',\n format: 'openai-embeddings',\n supportsDimensions: true,\n maxBatchSize: 2048\n },\n {\n modelPrefix: '',\n format: 'openai-embeddings'\n }\n ],\n imageGeneration: [\n {\n modelPrefix: 'gpt-image-',\n format: 'openai-images',\n acceptsImageReferenceInput: true,\n acceptedSizes: ['1024x1024', '1536x1024', '1024x1536', 'auto'],\n supportsQualityParam: true,\n acceptedQualities: ['low', 'medium', 'high', 'auto'],\n maxCount: 10,\n outputParamStyle: 'output-format',\n defaultOutputMimeType: 'image/png'\n },\n {\n modelPrefix: '',\n format: 'openai-images',\n outputParamStyle: 'response-format',\n defaultOutputMimeType: 'image/png'\n }\n ]\n },\n {\n id: 'openai-compat',\n label: 'OpenAI-compatible (self-hosted)',\n buttonLabel: 'AI Assist | OpenAI-compat',\n needsSecret: false,\n apiFormat: 'openai',\n baseUrl: '',\n defaultModel: '',\n supportedTools: [],\n corsRestricted: false,\n streamingCorsRestricted: false,\n acceptsImageInput: false,\n embedding: [{ modelPrefix: '', format: 'openai-embeddings' }]\n },\n {\n id: 'xai-grok',\n label: 'xAI Grok',\n buttonLabel: 'AI Assist | Grok',\n needsSecret: true,\n apiFormat: 'openai',\n baseUrl: 'https://api.x.ai/v1',\n defaultModel: {\n base: '@xai-grok:standard', // grok-4.3 (was the raw id; the cheap line + retirement-wave redirect target)\n advanced: '@xai-grok:flagship', // grok-4.5 (flagship since 2026-07-08; $2/$6 sits in the advanced band)\n image: '@xai-grok:imagine' // grok-imagine-image-quality (was the raw id)\n // no frontier key → a frontier request cascades advanced → grok-4.5 (see resolveModel)\n },\n aliases: {\n '@xai-grok:standard': 'grok-4.3', // base tier; NOT deprecated — superseded as flagship by grok-4.5\n '@xai-grok:flagship': 'grok-4.5', // advanced tier; configurable reasoning effort (default high)\n '@xai-grok:imagine': 'grok-imagine-image-quality' // image tier; current (redirect target for the retired -pro)\n },\n supportedTools: ['web_search'],\n corsRestricted: true,\n streamingCorsRestricted: true,\n acceptsImageInput: true,\n structuredOutput: [{ modelPrefix: '', format: 'openai-json-schema' }],\n imageGeneration: [\n {\n // grok-imagine models use JSON edits with image_url objects (different wire format)\n modelPrefix: 'grok-imagine-',\n format: 'xai-images-edits',\n acceptsImageReferenceInput: true,\n supportsQualityParam: false,\n maxCount: 10,\n outputParamStyle: 'response-format',\n defaultOutputMimeType: 'image/jpeg'\n },\n {\n // catch-all for other xai image models\n modelPrefix: '',\n format: 'xai-images',\n acceptsImageReferenceInput: false,\n supportsQualityParam: false,\n maxCount: 10,\n outputParamStyle: 'response-format',\n defaultOutputMimeType: 'image/jpeg'\n }\n ]\n }\n];\n\n/**\n * Index for O(1) lookup by id.\n * @internal\n */\nconst PROVIDER_BY_ID: ReadonlyMap<string, IAiProviderDescriptor> = new Map(\n BUILTIN_PROVIDERS.map((d) => [d.id, d])\n);\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * All valid provider ID values, in the same order as the registry.\n * @public\n */\nexport const allProviderIds: ReadonlyArray<AiProviderId> = BUILTIN_PROVIDERS.map((d) => d.id);\n\n/**\n * Get all known provider descriptors. Copy-paste first, then alphabetical.\n * @returns All built-in provider descriptors\n * @public\n */\nexport function getProviderDescriptors(): ReadonlyArray<IAiProviderDescriptor> {\n return BUILTIN_PROVIDERS;\n}\n\n/**\n * Get a provider descriptor by id.\n * @param id - The provider identifier\n * @returns The descriptor, or a failure if the provider is unknown\n * @public\n */\nexport function getProviderDescriptor(id: string): Result<IAiProviderDescriptor> {\n const descriptor = PROVIDER_BY_ID.get(id);\n if (!descriptor) {\n return fail(`unknown AI provider: ${id}`);\n }\n return succeed(descriptor);\n}\n\n/**\n * Whether a provider declares any image-generation capability at all.\n *\n * @param descriptor - The provider descriptor\n * @returns `true` when {@link IAiProviderDescriptor.imageGeneration} has at\n * least one entry; `false` otherwise.\n * @public\n */\nexport function supportsImageGeneration(descriptor: IAiProviderDescriptor): boolean {\n return (descriptor.imageGeneration?.length ?? 0) > 0;\n}\n\n/**\n * Shared longest-prefix capability match, alias-guarded.\n *\n * @remarks\n * `modelId` is resolved through `resolveModelAlias` **before** any prefix\n * matching, so an fgv alias (`@<provider>:<role>`) and the concrete id it names\n * select the same capability. Without this step an alias would fall through to\n * the `modelPrefix: ''` catch-all every provider declares and yield a\n * confidently wrong capability rather than no capability.\n *\n * An unresolvable alias (sigil-prefixed but unregistered, or cyclic) names no\n * model, so no capability applies and the match is skipped entirely — it must\n * never be prefix-matched verbatim. Callers already treat `undefined` as \"this\n * provider has no capability for this model\" and fail with that message.\n *\n * A concrete (non-sigil) id passes through `resolveModelAlias` verbatim, so\n * behavior for every raw provider id is unchanged.\n * @internal\n */\nfunction resolveCapabilityForModel<TCapability extends { readonly modelPrefix: string }>(\n descriptor: IAiProviderDescriptor,\n modelId: string,\n capabilities: ReadonlyArray<TCapability> | undefined\n): TCapability | undefined {\n const concreteModelId = resolveModelAlias(descriptor, modelId).orDefault();\n if (concreteModelId === undefined) {\n return undefined;\n }\n return (capabilities ?? [])\n .filter((cap) => concreteModelId.startsWith(cap.modelPrefix))\n .reduce<TCapability | undefined>(\n (best, cap) => (best && best.modelPrefix.length >= cap.modelPrefix.length ? best : cap),\n undefined\n );\n}\n\n/**\n * Resolve the image-generation capability that applies to a given model id\n * for a provider. Returns the entry from\n * {@link IAiProviderDescriptor.imageGeneration} whose `modelPrefix` is the\n * longest prefix of `modelId`. Ties are broken by first-encountered, so rule\n * order does not matter for correctness — only for tie-breaking among rules\n * with identical-length prefixes (an unusual case).\n *\n * @remarks\n * `modelId` may be either a concrete provider model id or an fgv model alias\n * (`@<provider>:<role>`, see `MODEL_ALIAS_SIGIL`) — it is resolved via\n * `resolveModelAlias` against `descriptor.aliases` before prefix matching,\n * so both forms select the same capability. A raw provider id passes through\n * unchanged. An alias that is not registered on `descriptor` (or is cyclic)\n * names no model and yields `undefined` rather than falling through to the\n * `modelPrefix: ''` catch-all.\n *\n * @param descriptor - The provider descriptor\n * @param modelId - The image model id — concrete or an fgv alias\n * @returns The matching capability, or `undefined` when no rule matches, the\n * provider declares no image-generation capabilities, or `modelId` is an\n * unresolvable alias.\n * @public\n */\nexport function resolveImageCapability(\n descriptor: IAiProviderDescriptor,\n modelId: string\n): IAiImageModelCapability | undefined {\n return resolveCapabilityForModel(descriptor, modelId, descriptor.imageGeneration);\n}\n\n/**\n * Whether a provider declares any embedding capability at all.\n *\n * @param descriptor - The provider descriptor\n * @returns `true` when {@link IAiProviderDescriptor.embedding} has at least one\n * entry; `false` otherwise.\n * @public\n */\nexport function supportsEmbedding(descriptor: IAiProviderDescriptor): boolean {\n return (descriptor.embedding?.length ?? 0) > 0;\n}\n\n/**\n * The structured-output capability for `modelId` under `descriptor`, or\n * `undefined` when the model can enforce nothing.\n *\n * @remarks\n * Alias-first, exactly like its `imageGeneration` / `embedding` siblings — an\n * unresolved alias returns `undefined` rather than prefix-matching a catch-all\n * `modelPrefix: ''`, which is the defect this helper was written to prevent.\n *\n * @param descriptor - The provider descriptor.\n * @param modelId - A concrete model id or an `@provider:role` alias.\n * @public\n */\nexport function resolveStructuredOutputCapability(\n descriptor: IAiProviderDescriptor,\n modelId: string\n): IAiStructuredOutputCapability | undefined {\n return resolveCapabilityForModel(descriptor, modelId, descriptor.structuredOutput);\n}\n\n/**\n * Whether `descriptor` declares any structured-output capability at all.\n * @public\n */\nexport function supportsStructuredOutput(descriptor: IAiProviderDescriptor): boolean {\n return (descriptor.structuredOutput?.length ?? 0) > 0;\n}\n\n/**\n * Resolve the embedding capability that applies to a given model id for a\n * provider. Returns the entry from {@link IAiProviderDescriptor.embedding} whose\n * `modelPrefix` is the longest prefix of `modelId`. Ties are broken by\n * first-encountered.\n *\n * @remarks\n * `modelId` may be either a concrete provider model id or an fgv model alias\n * (`@<provider>:<role>`, see `MODEL_ALIAS_SIGIL`) — it is resolved via\n * `resolveModelAlias` against `descriptor.aliases` before prefix matching,\n * so both forms select the same capability. A raw provider id passes through\n * unchanged. An alias that is not registered on `descriptor` (or is cyclic)\n * names no model and yields `undefined` rather than falling through to the\n * `modelPrefix: ''` catch-all.\n *\n * @param descriptor - The provider descriptor\n * @param modelId - The embedding model id — concrete or an fgv alias\n * @returns The matching capability, or `undefined` when no rule matches, the\n * provider declares no embedding capabilities, or `modelId` is an\n * unresolvable alias.\n * @public\n */\nexport function resolveEmbeddingCapability(\n descriptor: IAiProviderDescriptor,\n modelId: string\n): IAiEmbeddingModelCapability | undefined {\n return resolveCapabilityForModel(descriptor, modelId, descriptor.embedding);\n}\n\n// ============================================================================\n// Default model capability config\n// ============================================================================\n\n/**\n * Default capability config used by `callProviderListModels` when callers\n * don't supply their own. Patterns are intentionally narrow — false\n * positives are worse than missing a model. Caller can override per call\n * via {@link IProviderListModelsParams.capabilityConfig}.\n *\n * @public\n */\nexport const DEFAULT_MODEL_CAPABILITY_CONFIG: IAiModelCapabilityConfig = {\n perProvider: {\n openai: [\n { idPattern: /^gpt-image/, capabilities: ['image-generation'] },\n { idPattern: /^text-embedding/, capabilities: ['embedding'] },\n { idPattern: /^gpt-5/, capabilities: ['chat', 'tools', 'vision', 'thinking'] },\n { idPattern: /^gpt-4/, capabilities: ['chat', 'tools', 'vision'] },\n { idPattern: /^gpt-3\\.5/, capabilities: ['chat'] },\n { idPattern: /^o\\d/, capabilities: ['chat', 'tools', 'thinking'] }\n ],\n 'xai-grok': [\n { idPattern: /-image/, capabilities: ['image-generation'] },\n // grok-4.5 needs its own rule: it would otherwise hit only /^grok-4/ and lose\n // thinking, yet it has configurable reasoning effort. Detection accumulates across\n // matching rules, so /^grok-4/ still contributes vision (same as grok-4.3).\n { idPattern: /^grok-4\\.5/, capabilities: ['chat', 'tools', 'thinking'] },\n { idPattern: /^grok-4\\.3/, capabilities: ['chat', 'tools', 'thinking'] },\n { idPattern: /^grok-4$/, capabilities: ['chat', 'tools', 'thinking'] },\n { idPattern: /^grok-4/, capabilities: ['chat', 'tools', 'vision'] },\n { idPattern: /^grok-3-mini/, capabilities: ['chat', 'tools', 'thinking'] },\n { idPattern: /^grok-3/, capabilities: ['chat', 'tools'] },\n { idPattern: /^grok-2/, capabilities: ['chat', 'vision'] }\n ],\n 'google-gemini': [\n { idPattern: /^imagen/, capabilities: ['image-generation'] },\n { idPattern: /^gemini-.*-image/, capabilities: ['image-generation'] },\n { idPattern: /embedding/, capabilities: ['embedding'] },\n { idPattern: /^gemini-3/, capabilities: ['chat', 'tools', 'vision', 'thinking'] },\n { idPattern: /^gemini-2\\.5/, capabilities: ['chat', 'tools', 'vision', 'thinking'] },\n { idPattern: /^gemini-/, capabilities: ['chat', 'tools', 'vision'] }\n ],\n anthropic: [\n // Broadened from /^claude-opus-4/ and /^claude-sonnet-4/ so the sonnet-5+ / opus-5+ lines\n // are detected as thinking-capable. Detection accumulates across matching rules, so this is\n // purely additive: every existing opus-4 / sonnet-4 id still matches. Both broadenings are\n // now load-bearing: claude-sonnet-5 and claude-opus-5 (the advanced-tier target) would\n // otherwise hit only /^claude-/ and lose thinking. The fable rule keeps the\n // AnthropicThinkingModelNames union honest — claude-fable-5 (modelOverride-only) would\n // otherwise fall to the /^claude-/ catch-all and be detected without thinking.\n { idPattern: /^claude-opus-/, capabilities: ['chat', 'tools', 'vision', 'thinking'] },\n { idPattern: /^claude-sonnet-/, capabilities: ['chat', 'tools', 'vision', 'thinking'] },\n { idPattern: /^claude-fable-/, capabilities: ['chat', 'tools', 'vision', 'thinking'] },\n { idPattern: /^claude-/, capabilities: ['chat', 'tools', 'vision'] }\n ],\n groq: [{ idPattern: /./, capabilities: ['chat'] }],\n mistral: [\n { idPattern: /embed/, capabilities: ['embedding'] },\n { idPattern: /./, capabilities: ['chat'] }\n ],\n // Self-hosted OpenAI-compatible servers (Ollama, LM Studio, llama.cpp) serve\n // arbitrary, caller-chosen models whose ids we can't enumerate ahead of time.\n // The catch-all `/./` intentionally departs from the \"narrow patterns\" rule\n // above: assume `chat` for everything and let the caller override via\n // `capabilityConfig` when they know their deployment serves image/embedding models.\n ollama: [{ idPattern: /./, capabilities: ['chat'] }],\n 'openai-compat': [{ idPattern: /./, capabilities: ['chat'] }]\n }\n};\n"]}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// Copyright (c) 2026 Erik Fortune
|
|
2
|
+
//
|
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
// in the Software without restriction, including without limitation the rights
|
|
6
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
// furnished to do so, subject to the following conditions:
|
|
9
|
+
//
|
|
10
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
// copies or substantial portions of the Software.
|
|
12
|
+
//
|
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
// SOFTWARE.
|
|
20
|
+
/**
|
|
21
|
+
* Whether a provider's streaming Chat Completions request should carry
|
|
22
|
+
* `stream_options: { include_usage: true }`. A request-shape gate, not a
|
|
23
|
+
* capability guess: the shared adapter also carries providers (xAI, Groq,
|
|
24
|
+
* Mistral, Ollama, self-hosted `openai-compat`) not verified to tolerate an
|
|
25
|
+
* unrecognized field, so this is `true` only for `'openai'`; every other
|
|
26
|
+
* provider simply never gets a streaming `usage` block — a safe degradation,
|
|
27
|
+
* since `IAiStreamDone.usage` is optional everywhere.
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
export function supportsStreamUsageOption(descriptor) {
|
|
31
|
+
return descriptor.id === 'openai';
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Whether a provider has confirmed prompt-cache-relevant `usage` reporting on
|
|
35
|
+
* the shared Chat Completions / Responses completion paths (streaming and
|
|
36
|
+
* non-streaming alike).
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* The completion paths it gates — `callOpenAiCompletion`,
|
|
40
|
+
* `callOpenAiResponsesCompletion`, the Responses streaming adapter, and
|
|
41
|
+
* `executeClientToolTurn`'s direct Responses call — are shared by every
|
|
42
|
+
* `apiFormat: 'openai'` descriptor — xAI Grok, Groq, Mistral, Ollama, and self-hosted
|
|
43
|
+
* `openai-compat` all route through the same adapter as OpenAI. Only OpenAI
|
|
44
|
+
* and xAI Grok are confirmed to report cache-relevant `usage` fields; an
|
|
45
|
+
* ordinary `usage` block from one of the other descriptors (e.g. Groq's plain
|
|
46
|
+
* `prompt_tokens` / `completion_tokens`, with no cache sub-object at all)
|
|
47
|
+
* carries no cache information. Normalizing it anyway would stamp
|
|
48
|
+
* `reports: 'reads'` on a provider that has no prompt-caching concept —
|
|
49
|
+
* a false cache-reporting signal, not an absent one. So this gate is checked
|
|
50
|
+
* before every normalize call it governs; when it's `false`, `usage` stays
|
|
51
|
+
* absent entirely rather than being guessed from whatever shape the wire
|
|
52
|
+
* happens to send.
|
|
53
|
+
*
|
|
54
|
+
* **Streaming Chat Completions is gated by a different predicate.** That path
|
|
55
|
+
* consults {@link AiAssist.supportsStreamUsageOption} instead, because the
|
|
56
|
+
* request has to opt in via `stream_options: { include_usage: true }` before
|
|
57
|
+
* there is any usage to normalize — and that opt-in is `'openai'`-only, since
|
|
58
|
+
* the other descriptors' tolerance for the unrecognized field is unverified.
|
|
59
|
+
* The practical consequence is that xAI Grok, which *is* confirmed here, still
|
|
60
|
+
* gets no `usage` on streaming Chat Completions. If you are tracing a missing
|
|
61
|
+
* xAI streaming figure, that predicate is the one to read, not this one.
|
|
62
|
+
* @public
|
|
63
|
+
*/
|
|
64
|
+
export function supportsCacheUsageReporting(descriptor) {
|
|
65
|
+
return descriptor.id === 'openai' || descriptor.id === 'xai-grok';
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Whether a provider has confirmed tolerance for the request-shape change
|
|
69
|
+
* {@link AiAssist.IAiCacheRequest.systemBreakpoints} produces on the shared Chat Completions /
|
|
70
|
+
* Responses paths: splitting `system` into content parts each carrying a
|
|
71
|
+
* `prompt_cache_breakpoint` field.
|
|
72
|
+
*
|
|
73
|
+
* This governs **breakpoints only**. The routing key is a separate capability with a different
|
|
74
|
+
* support set — see {@link AiAssist.supportsPromptCacheRouting}. An earlier version of this
|
|
75
|
+
* predicate gated both, which withheld the routing key from xAI, the one provider that most
|
|
76
|
+
* needs it.
|
|
77
|
+
*
|
|
78
|
+
* @remarks
|
|
79
|
+
* Same sharing problem as {@link AiAssist.supportsStreamUsageOption}, on the write side instead of the
|
|
80
|
+
* read side: `callOpenAiCompletion` and `callOpenAiResponsesCompletion` are shared by every
|
|
81
|
+
* `apiFormat: 'openai'` descriptor (xAI Grok, Groq, Mistral, Ollama, self-hosted
|
|
82
|
+
* `openai-compat`), but only OpenAI's own API is confirmed to accept a `system`/first-item
|
|
83
|
+
* `content` restructured into an array of parts each carrying an unrecognized
|
|
84
|
+
* `prompt_cache_breakpoint` field. A
|
|
85
|
+
* schema-strict server on one of the other descriptors could 400 on it — the identical
|
|
86
|
+
* failure mode `supportsStreamUsageOption` was written to avoid for `stream_options`. So `cache`
|
|
87
|
+
* is gated to `descriptor.id === 'openai'` at the dispatch site before it ever reaches these
|
|
88
|
+
* builders; every other descriptor gets the same request body it would have gotten had the
|
|
89
|
+
* caller passed no `cache` at all.
|
|
90
|
+
* @public
|
|
91
|
+
*/
|
|
92
|
+
export function supportsPromptCacheBreakpoints(descriptor) {
|
|
93
|
+
return descriptor.id === 'openai';
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Whether a provider accepts an opaque prompt-cache **routing** key, and by what transport.
|
|
97
|
+
*
|
|
98
|
+
* @remarks
|
|
99
|
+
* Distinct from {@link AiAssist.supportsPromptCacheBreakpoints}, and deliberately so. A breakpoint
|
|
100
|
+
* restructures the request body and needs the server to tolerate an unrecognized field inside
|
|
101
|
+
* `content`; a routing key is a single opaque string whose only job is to make repeated requests
|
|
102
|
+
* with a shared prefix land on the same cache-holding server.
|
|
103
|
+
*
|
|
104
|
+
* The two were originally gated together, which had a perverse effect: xAI does byte-for-byte
|
|
105
|
+
* prefix matching from the start of the `messages` array, and its cache is **per-server and
|
|
106
|
+
* evictable**, so without a routing key a caller can miss on an identical prefix simply by being
|
|
107
|
+
* routed elsewhere. Measured on `grok-4.3`: two requests sharing an 8,684-token system prefix but
|
|
108
|
+
* differing in their final user turn returned 2.2% cached, while a byte-identical pair returned
|
|
109
|
+
* 99.5%. Withholding the routing key from xAI withheld it from the provider whose caching depends
|
|
110
|
+
* on it most.
|
|
111
|
+
*
|
|
112
|
+
* The transport differs by route, which is why this returns a descriptor rather than a boolean:
|
|
113
|
+
*
|
|
114
|
+
* | provider | Chat Completions | Responses |
|
|
115
|
+
* |---|---|---|
|
|
116
|
+
* | `openai` | `prompt_cache_key` body field | `prompt_cache_key` body field |
|
|
117
|
+
* | `xai-grok` | `x-grok-conv-id` **header** | `prompt_cache_key` body field |
|
|
118
|
+
*
|
|
119
|
+
* Every other `apiFormat: 'openai'` descriptor (Groq, Mistral, Ollama, self-hosted
|
|
120
|
+
* `openai-compat`) returns `undefined` and gets the request it would have gotten had the caller
|
|
121
|
+
* passed no `cache` at all — the same unconfirmed-tolerance reasoning as
|
|
122
|
+
* {@link AiAssist.supportsStreamUsageOption}.
|
|
123
|
+
* @public
|
|
124
|
+
*/
|
|
125
|
+
export function supportsPromptCacheRouting(descriptor) {
|
|
126
|
+
if (descriptor.id === 'openai') {
|
|
127
|
+
return { chatCompletionsHeader: undefined };
|
|
128
|
+
}
|
|
129
|
+
if (descriptor.id === 'xai-grok') {
|
|
130
|
+
return { chatCompletionsHeader: 'x-grok-conv-id' };
|
|
131
|
+
}
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=streamUsageCapability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamUsageCapability.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/streamUsageCapability.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;AAgBZ;;;;;;;;;GASG;AACH,MAAM,UAAU,yBAAyB,CAAC,UAAiC;IACzE,OAAO,UAAU,CAAC,EAAE,KAAK,QAAQ,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,2BAA2B,CAAC,UAAiC;IAC3E,OAAO,UAAU,CAAC,EAAE,KAAK,QAAQ,IAAI,UAAU,CAAC,EAAE,KAAK,UAAU,CAAC;AACpE,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,8BAA8B,CAAC,UAAiC;IAC9E,OAAO,UAAU,CAAC,EAAE,KAAK,QAAQ,CAAC;AACpC,CAAC;AAmBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,0BAA0B,CACxC,UAAiC;IAEjC,IAAI,UAAU,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,EAAE,qBAAqB,EAAE,SAAS,EAAE,CAAC;IAC9C,CAAC;IACD,IAAI,UAAU,CAAC,EAAE,KAAK,UAAU,EAAE,CAAC;QACjC,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,CAAC;IACrD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Provider-capability gates for `usage` reporting and cache-breakpoint emission on the shared\n * `apiFormat: 'openai'` completion paths (Chat Completions and Responses, streaming and\n * non-streaming).\n *\n * @remarks\n * Its own module rather than part of `model.ts` because `model.ts` was at the\n * `max-lines` cap. Same rationale as `structuredOutputTypes.ts` and\n * `usageTypes.ts`.\n * @packageDocumentation\n */\n\nimport type { IAiProviderDescriptor } from './model';\n\n/**\n * Whether a provider's streaming Chat Completions request should carry\n * `stream_options: { include_usage: true }`. A request-shape gate, not a\n * capability guess: the shared adapter also carries providers (xAI, Groq,\n * Mistral, Ollama, self-hosted `openai-compat`) not verified to tolerate an\n * unrecognized field, so this is `true` only for `'openai'`; every other\n * provider simply never gets a streaming `usage` block — a safe degradation,\n * since `IAiStreamDone.usage` is optional everywhere.\n * @public\n */\nexport function supportsStreamUsageOption(descriptor: IAiProviderDescriptor): boolean {\n return descriptor.id === 'openai';\n}\n\n/**\n * Whether a provider has confirmed prompt-cache-relevant `usage` reporting on\n * the shared Chat Completions / Responses completion paths (streaming and\n * non-streaming alike).\n *\n * @remarks\n * The completion paths it gates — `callOpenAiCompletion`,\n * `callOpenAiResponsesCompletion`, the Responses streaming adapter, and\n * `executeClientToolTurn`'s direct Responses call — are shared by every\n * `apiFormat: 'openai'` descriptor — xAI Grok, Groq, Mistral, Ollama, and self-hosted\n * `openai-compat` all route through the same adapter as OpenAI. Only OpenAI\n * and xAI Grok are confirmed to report cache-relevant `usage` fields; an\n * ordinary `usage` block from one of the other descriptors (e.g. Groq's plain\n * `prompt_tokens` / `completion_tokens`, with no cache sub-object at all)\n * carries no cache information. Normalizing it anyway would stamp\n * `reports: 'reads'` on a provider that has no prompt-caching concept —\n * a false cache-reporting signal, not an absent one. So this gate is checked\n * before every normalize call it governs; when it's `false`, `usage` stays\n * absent entirely rather than being guessed from whatever shape the wire\n * happens to send.\n *\n * **Streaming Chat Completions is gated by a different predicate.** That path\n * consults {@link AiAssist.supportsStreamUsageOption} instead, because the\n * request has to opt in via `stream_options: { include_usage: true }` before\n * there is any usage to normalize — and that opt-in is `'openai'`-only, since\n * the other descriptors' tolerance for the unrecognized field is unverified.\n * The practical consequence is that xAI Grok, which *is* confirmed here, still\n * gets no `usage` on streaming Chat Completions. If you are tracing a missing\n * xAI streaming figure, that predicate is the one to read, not this one.\n * @public\n */\nexport function supportsCacheUsageReporting(descriptor: IAiProviderDescriptor): boolean {\n return descriptor.id === 'openai' || descriptor.id === 'xai-grok';\n}\n\n/**\n * Whether a provider has confirmed tolerance for the request-shape change\n * {@link AiAssist.IAiCacheRequest.systemBreakpoints} produces on the shared Chat Completions /\n * Responses paths: splitting `system` into content parts each carrying a\n * `prompt_cache_breakpoint` field.\n *\n * This governs **breakpoints only**. The routing key is a separate capability with a different\n * support set — see {@link AiAssist.supportsPromptCacheRouting}. An earlier version of this\n * predicate gated both, which withheld the routing key from xAI, the one provider that most\n * needs it.\n *\n * @remarks\n * Same sharing problem as {@link AiAssist.supportsStreamUsageOption}, on the write side instead of the\n * read side: `callOpenAiCompletion` and `callOpenAiResponsesCompletion` are shared by every\n * `apiFormat: 'openai'` descriptor (xAI Grok, Groq, Mistral, Ollama, self-hosted\n * `openai-compat`), but only OpenAI's own API is confirmed to accept a `system`/first-item\n * `content` restructured into an array of parts each carrying an unrecognized\n * `prompt_cache_breakpoint` field. A\n * schema-strict server on one of the other descriptors could 400 on it — the identical\n * failure mode `supportsStreamUsageOption` was written to avoid for `stream_options`. So `cache`\n * is gated to `descriptor.id === 'openai'` at the dispatch site before it ever reaches these\n * builders; every other descriptor gets the same request body it would have gotten had the\n * caller passed no `cache` at all.\n * @public\n */\nexport function supportsPromptCacheBreakpoints(descriptor: IAiProviderDescriptor): boolean {\n return descriptor.id === 'openai';\n}\n\n/**\n * How a provider carries an {@link AiAssist.IAiCacheRequest.cacheKey} on the wire.\n *\n * @remarks\n * Returned by {@link AiAssist.supportsPromptCacheRouting}. The Responses API takes the key as a\n * `prompt_cache_key` body field on every provider that supports it, so only the Chat Completions\n * transport varies and only that is described here.\n * @public\n */\nexport interface IAiPromptCacheRoutingSupport {\n /**\n * Header name to carry the key on Chat Completions, or `undefined` when the provider takes it\n * as a `prompt_cache_key` body field there (OpenAI). xAI uses `x-grok-conv-id`.\n */\n readonly chatCompletionsHeader: string | undefined;\n}\n\n/**\n * Whether a provider accepts an opaque prompt-cache **routing** key, and by what transport.\n *\n * @remarks\n * Distinct from {@link AiAssist.supportsPromptCacheBreakpoints}, and deliberately so. A breakpoint\n * restructures the request body and needs the server to tolerate an unrecognized field inside\n * `content`; a routing key is a single opaque string whose only job is to make repeated requests\n * with a shared prefix land on the same cache-holding server.\n *\n * The two were originally gated together, which had a perverse effect: xAI does byte-for-byte\n * prefix matching from the start of the `messages` array, and its cache is **per-server and\n * evictable**, so without a routing key a caller can miss on an identical prefix simply by being\n * routed elsewhere. Measured on `grok-4.3`: two requests sharing an 8,684-token system prefix but\n * differing in their final user turn returned 2.2% cached, while a byte-identical pair returned\n * 99.5%. Withholding the routing key from xAI withheld it from the provider whose caching depends\n * on it most.\n *\n * The transport differs by route, which is why this returns a descriptor rather than a boolean:\n *\n * | provider | Chat Completions | Responses |\n * |---|---|---|\n * | `openai` | `prompt_cache_key` body field | `prompt_cache_key` body field |\n * | `xai-grok` | `x-grok-conv-id` **header** | `prompt_cache_key` body field |\n *\n * Every other `apiFormat: 'openai'` descriptor (Groq, Mistral, Ollama, self-hosted\n * `openai-compat`) returns `undefined` and gets the request it would have gotten had the caller\n * passed no `cache` at all — the same unconfirmed-tolerance reasoning as\n * {@link AiAssist.supportsStreamUsageOption}.\n * @public\n */\nexport function supportsPromptCacheRouting(\n descriptor: IAiProviderDescriptor\n): IAiPromptCacheRoutingSupport | undefined {\n if (descriptor.id === 'openai') {\n return { chatCompletionsHeader: undefined };\n }\n if (descriptor.id === 'xai-grok') {\n return { chatCompletionsHeader: 'x-grok-conv-id' };\n }\n return undefined;\n}\n"]}
|