@genesislcap/foundation-ai 15.33.1 → 15.34.0

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.
@@ -10,6 +10,10 @@ export { GeminiTransport, MalformedFunctionCallError } from './transports/gemini
10
10
  export { DEFAULT_PROVIDER_REFUSED_MESSAGE, PROVIDER_REFUSED_CODE, ProviderRefusedError, } from './transports/provider-refused';
11
11
  export type { ProviderRefusalKind } from './transports/provider-refused';
12
12
  export { ContextOverflowError, contextOverflowOf, DEFAULT_CONTEXT_OVERFLOW_MESSAGE, } from './transports/context-overflow-error';
13
+ export { AI_TIER_IDS, AI_TIER_VENDORS, DEFAULT_AI_TIERS, resolveAITiers } from './tiers/ai-tiers';
14
+ export type { AITierId, AITierOverrides, AITiers, AITierVendor, AnthropicTierConfig, GeminiTierConfig, } from './tiers/ai-tiers';
15
+ export { buildTierProviders } from './tiers/build-tier-providers';
16
+ export type { BuildTierProvidersOptions } from './tiers/build-tier-providers';
13
17
  export { AI_FEATURE_FLAG, isAIFeatureEnabled } from './utils/feature-flags';
14
18
  export { ChatTemperature } from './utils/temperature';
15
19
  export { ANTHROPIC_CACHE_READ_MULTIPLIER, ANTHROPIC_CACHE_WRITE_1H_MULTIPLIER, ANTHROPIC_CACHE_WRITE_5M_MULTIPLIER, anthropicRatesFor, anthropicTokenCost, GEMINI_CACHED_INPUT_MULTIPLIER, GEMINI_LONG_CONTEXT_THRESHOLD, geminiRatesFor, geminiTokenCost, } from './utils/token-cost';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,154 @@
1
+ import type { AnthropicModelId, GeminiModelId } from '../types';
2
+ /**
3
+ * The platform's tier → model table (GENC-1610).
4
+ *
5
+ * @remarks
6
+ * Pure data, and deliberately so: this module imports nothing but types, never
7
+ * `ai-provider-di` (the one `@microsoft/fast-foundation` importer) and never
8
+ * `@genesislcap/web-core`, so it loads in bare Node. It is re-exported by name from
9
+ * `@genesislcap/ai-assistant/chat-driver`, which a server-side host requires at runtime —
10
+ * an `export *` or a DOM import anywhere in its closure crashes that host at require time.
11
+ * Provider construction lives next door in `build-tier-providers.ts`, which is browser-only.
12
+ *
13
+ * @packageDocumentation
14
+ */
15
+ /**
16
+ * Every tier id, in ascending capability order.
17
+ *
18
+ * This array is the source of truth and {@link AITierId} is derived from it, so the two cannot
19
+ * drift: a tier exists exactly when it is listed here. That matters because the provider
20
+ * builders and the table's tests all loop over this array — a tier missing from it would get
21
+ * no provider and no test, with nothing to say so.
22
+ *
23
+ * @beta
24
+ */
25
+ export declare const AI_TIER_IDS: readonly ["low", "high", "reasoning"];
26
+ /**
27
+ * The tier a caller asks for, rather than a model id.
28
+ *
29
+ * Tier-shaped names are what let an app swap vendor (or re-bench a tier onto a
30
+ * newer model) without touching a single agent config — agents resolve `'high'`,
31
+ * not `claude-sonnet-5`. Derived from {@link AI_TIER_IDS}.
32
+ *
33
+ * @beta
34
+ */
35
+ export type AITierId = (typeof AI_TIER_IDS)[number];
36
+ /**
37
+ * Every vendor in the table — the two the platform's proxy meters. The source of truth for
38
+ * {@link AITierVendor}, for the same reason as {@link AI_TIER_IDS}.
39
+ *
40
+ * @beta
41
+ */
42
+ export declare const AI_TIER_VENDORS: readonly ["anthropic", "gemini"];
43
+ /**
44
+ * The cloud vendors the tier table covers. Derived from {@link AI_TIER_VENDORS}.
45
+ *
46
+ * @beta
47
+ */
48
+ export type AITierVendor = (typeof AI_TIER_VENDORS)[number];
49
+ /**
50
+ * What one tier is worth on Anthropic: the model, its output ceiling, and the two clocks.
51
+ *
52
+ * @beta
53
+ */
54
+ export interface AnthropicTierConfig {
55
+ model: AnthropicModelId;
56
+ /**
57
+ * `max_tokens` for every request on this tier. Part of the tier, not an optional
58
+ * extra: Anthropic requires the field and the transport defaults it to 4096, which
59
+ * truncates a large single-call file write mid-stream (`stop_reason: max_tokens`)
60
+ * and leaves the tool input unparseable. Values sit inside each model's documented
61
+ * output ceiling.
62
+ */
63
+ maxTokens: number;
64
+ /** Inactivity window on a stream that has started. */
65
+ stallTimeoutMs: number;
66
+ /** Ceiling on the wait for the FIRST byte — a different clock from the stall one. */
67
+ timeoutMs: number;
68
+ }
69
+ /**
70
+ * What one tier is worth on Gemini.
71
+ *
72
+ * No `maxTokens`: `GeminiAIConfig` has no such field and the provider's own (much
73
+ * larger) default applies, so Gemini writes never hit the Anthropic wall above.
74
+ *
75
+ * @beta
76
+ */
77
+ export interface GeminiTierConfig {
78
+ model: GeminiModelId;
79
+ /** Inactivity window on a stream that has started. */
80
+ stallTimeoutMs: number;
81
+ /** Ceiling on the wait for the FIRST byte — a different clock from the stall one. */
82
+ timeoutMs: number;
83
+ }
84
+ /**
85
+ * The whole table: every tier, for every vendor.
86
+ *
87
+ * Total records, not partials — a tier a vendor cannot serve is not a tier, it is a
88
+ * gap that only shows up when an agent resolves it at runtime.
89
+ *
90
+ * @beta
91
+ */
92
+ export interface AITiers {
93
+ anthropic: Record<AITierId, AnthropicTierConfig>;
94
+ gemini: Record<AITierId, GeminiTierConfig>;
95
+ }
96
+ /**
97
+ * The platform's default tier table.
98
+ *
99
+ * @remarks
100
+ * Frozen, because it is shared by every app in the process: {@link resolveAITiers} returns
101
+ * a fresh object for callers that override, and hands this back untouched for callers that
102
+ * do not.
103
+ *
104
+ * The models are the ones Genesis Create has been running these tiers on in production — except
105
+ * Anthropic's `reasoning`, moved to `claude-opus-4-8` (see its entry) — and they are a
106
+ * **benchmark result, not a preference**: re-bench before moving one. Two of the Gemini
107
+ * defaults carry a caveat recorded beside them, a preview model and an introductory rate. Two dates
108
+ * are already on the calendar for that: `gemini-3.8-flash` sits on an introductory rate that
109
+ * doubles on 2027-01-01 (at which point it is dearer on input than the model it replaced),
110
+ * and `token-cost.test.ts` fails the build that day; `ai-tiers.test.ts` fails alongside it
111
+ * saying the High tier is the thing to re-bench.
112
+ *
113
+ * Every model here must be in `SUPPORTED_*_MODEL_IDS` with a context limit and a price —
114
+ * pinned by tests, because an unpriced model silently bills at nothing.
115
+ *
116
+ * @beta
117
+ */
118
+ export declare const DEFAULT_AI_TIERS: AITiers;
119
+ /**
120
+ * Per-tier overrides, shallow-merged field by field over the defaults.
121
+ *
122
+ * @beta
123
+ */
124
+ export interface AITierOverrides {
125
+ anthropic?: Partial<Record<AITierId, Partial<AnthropicTierConfig>>>;
126
+ gemini?: Partial<Record<AITierId, Partial<GeminiTierConfig>>>;
127
+ }
128
+ /**
129
+ * The table an app should actually use: {@link DEFAULT_AI_TIERS} with `overrides` applied
130
+ * per field, so an app that wants one model moved says only that and keeps the platform's
131
+ * clocks and ceilings.
132
+ *
133
+ * @remarks
134
+ * An override field set to `undefined` is skipped, exactly as if it had been left out. The
135
+ * common caller passes an optional config value straight through
136
+ * (`{ high: { model: appConfig.model } }`), and copying `undefined` over the default would
137
+ * hand the transport no model at all — it then quietly falls back to its own default, with
138
+ * nothing logged.
139
+ *
140
+ * The result is frozen whether or not anything was overridden, so whether mutating it throws
141
+ * does not depend on what the caller passed. The default itself is returned untouched when
142
+ * there is nothing to override, and is never mutated, which matters because it is shared
143
+ * across every app in the process.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * // Keep everything, but run the reasoning tier on Fable.
148
+ * const tiers = resolveAITiers({ anthropic: { reasoning: { model: 'claude-fable-5' } } });
149
+ * ```
150
+ *
151
+ * @beta
152
+ */
153
+ export declare function resolveAITiers(overrides?: AITierOverrides): AITiers;
154
+ //# sourceMappingURL=ai-tiers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-tiers.d.ts","sourceRoot":"","sources":["../../../src/tiers/ai-tiers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEhE;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,uCAAwC,CAAC;AAEjE;;;;;;;;GAQG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,kCAAmC,CAAC;AAEhE;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,gBAAgB,CAAC;IACxB;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;IACvB,qFAAqF;IACrF,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,aAAa,CAAC;IACrB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;IACvB,qFAAqF;IACrF,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;IACjD,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;CAC5C;AAsBD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,gBAAgB,EAAE,OAsD7B,CAAC;AAEH;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;CAC/D;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,eAAe,GAAG,OAAO,CAMnE"}
@@ -0,0 +1,67 @@
1
+ import { type AIProvider } from '../ai-provider';
2
+ import type { AnthropicAIConfig, GeminiAIConfig } from '../types';
3
+ import { type AITierId, type AITiers, type AITierVendor } from './ai-tiers';
4
+ /**
5
+ * Turns the tier table into live providers (GENC-1610).
6
+ *
7
+ * @remarks
8
+ * Split from `ai-tiers.ts` because this half is **browser-only**: `createAIProvider` checks
9
+ * the AI feature flag, which reads `window.location`. The table is what a Node host needs
10
+ * (model ids and pricing); provider construction is not, so only the table is re-exported
11
+ * from the `@genesislcap/ai-assistant/chat-driver` Node entry.
12
+ *
13
+ * @packageDocumentation
14
+ */
15
+ /**
16
+ * Options for {@link buildTierProviders}.
17
+ *
18
+ * @beta
19
+ */
20
+ export interface BuildTierProvidersOptions {
21
+ /** Which vendor's models to build. */
22
+ vendor: AITierVendor;
23
+ /**
24
+ * The proxy endpoint every tier posts to — a string, or a function of the vendor for a
25
+ * caller that builds more than one vendor from one rule (see the example below).
26
+ *
27
+ * Always a server endpoint, never an API key: these providers are meant for an app whose
28
+ * keys stay on its server. An empty endpoint throws here rather than at the first turn.
29
+ */
30
+ serverEndpoint: string | ((vendor: AITierVendor) => string);
31
+ /** The table to build from. Defaults to {@link DEFAULT_AI_TIERS}. */
32
+ tiers?: AITiers;
33
+ }
34
+ /**
35
+ * Builds one {@link AIProvider} per tier for a vendor, ready to register under the tier
36
+ * names agents resolve (`low` / `high` / `reasoning`).
37
+ *
38
+ * @remarks
39
+ * Each provider carries its tier's model, output ceiling (Anthropic) and both clocks, so no
40
+ * tier can silently fall back to the transport defaults — which is how a large tool write
41
+ * ends up truncated at 4096 tokens.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * registerAIProviders(
46
+ * container,
47
+ * buildTierProviders({ vendor: 'gemini', serverEndpoint: (v) => `/gwf/ai-service/${v}/chat` }),
48
+ * { default: 'high' },
49
+ * );
50
+ * ```
51
+ *
52
+ * @beta
53
+ */
54
+ export declare function buildTierProviders(options: BuildTierProvidersOptions): Record<AITierId, AIProvider>;
55
+ /**
56
+ * The provider configs {@link buildTierProviders} builds from, before they reach
57
+ * `createAIProvider`.
58
+ *
59
+ * A seam rather than an inline literal, because what is being asserted is exactly what a
60
+ * constructed provider hides: a transport keeps `maxTokens` and both clocks private, so a
61
+ * tier silently falling back to the 4096-token default would be invisible from the outside
62
+ * — which is the failure this table exists to prevent.
63
+ *
64
+ * @internal
65
+ */
66
+ export declare function tierProviderConfigs(options: BuildTierProvidersOptions): Record<AITierId, AnthropicAIConfig | GeminiAIConfig>;
67
+ //# sourceMappingURL=build-tier-providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-tier-providers.d.ts","sourceRoot":"","sources":["../../../src/tiers/build-tier-providers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAClE,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,OAAO,EACZ,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;GAUG;AAEH;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,sCAAsC;IACtC,MAAM,EAAE,YAAY,CAAC;IACrB;;;;;;OAMG;IACH,cAAc,EAAE,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,YAAY,KAAK,MAAM,CAAC,CAAC;IAC5D,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,yBAAyB,GACjC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAO9B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,yBAAyB,GACjC,MAAM,CAAC,QAAQ,EAAE,iBAAiB,GAAG,cAAc,CAAC,CAkCtD"}
@@ -144,6 +144,20 @@ export declare const GEMINI_LONG_CONTEXT_THRESHOLD = 200000;
144
144
  * @beta
145
145
  */
146
146
  export declare const GEMINI_CACHED_INPUT_MULTIPLIER = 0.1;
147
+ /**
148
+ * The day `gemini-3.8-flash` leaves its introductory rate (see `GEMINI_PRICING` below).
149
+ *
150
+ * A constant rather than a literal in the test that enforces it, because a SECOND dated
151
+ * assertion now hangs off the same day: the High tier picked that model while it was the
152
+ * cheaper one, so the flip is also the trigger to re-bench the tier (`tiers/ai-tiers.test.ts`).
153
+ * Two hand-copied dates would drift the moment either is corrected, and a re-bench reminder
154
+ * that fires on the wrong day is no reminder at all.
155
+ *
156
+ * Not part of the package's public surface — it exists for those two tests.
157
+ *
158
+ * @internal
159
+ */
160
+ export declare const GEMINI_3_8_FLASH_INTRO_RATE_ENDS: number;
147
161
  /**
148
162
  * Resolve the per-million-token rates for a model given the request's prompt size,
149
163
  * selecting the long-context tier for tiered models when the prompt exceeds
@@ -1 +1 @@
1
- {"version":3,"file":"token-cost.d.ts","sourceRoot":"","sources":["../../../src/utils/token-cost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAkDhE;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,SAAS,EAAE,kBAAkB,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;CACtB;AAiCD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,UAAU,CAErE;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,+BAA+B,MAAM,CAAC;AACnD,YAAY;AACZ,eAAO,MAAM,mCAAmC,OAAO,CAAC;AACxD,YAAY;AACZ,eAAO,MAAM,mCAAmC,IAAI,CAAC;AAErD;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,wFAAwF;IACxF,mBAAmB,EAAE,MAAM,CAAC;IAC5B,4FAA4F;IAC5F,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,eAAe,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;;;;;;OASG;IACH,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,oBAAoB,GAC1B,SAAS,CAiCX;AAMD;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,SAAU,CAAC;AAErD;;;;;;;;;GASG;AACH,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAsDlD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,GAAG,UAAU,CAMrF;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,iBAAiB;IAChC,kFAAkF;IAClF,YAAY,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,iBAAiB,GAAG,SAAS,CAuBzF"}
1
+ {"version":3,"file":"token-cost.d.ts","sourceRoot":"","sources":["../../../src/utils/token-cost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAkDhE;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,SAAS,EAAE,kBAAkB,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;CACtB;AAiCD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,UAAU,CAErE;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,+BAA+B,MAAM,CAAC;AACnD,YAAY;AACZ,eAAO,MAAM,mCAAmC,OAAO,CAAC;AACxD,YAAY;AACZ,eAAO,MAAM,mCAAmC,IAAI,CAAC;AAErD;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,wFAAwF;IACxF,mBAAmB,EAAE,MAAM,CAAC;IAC5B,4FAA4F;IAC5F,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,eAAe,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;;;;;;OASG;IACH,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,oBAAoB,GAC1B,SAAS,CAiCX;AAMD;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,SAAU,CAAC;AAErD;;;;;;;;;GASG;AACH,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gCAAgC,QAAqC,CAAC;AAsDnF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,GAAG,UAAU,CAMrF;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,iBAAiB;IAChC,kFAAkF;IAClF,YAAY,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,iBAAiB,GAAG,SAAS,CAuBzF"}
package/dist/esm/index.js CHANGED
@@ -15,6 +15,13 @@ export { GeminiTransport, MalformedFunctionCallError } from './transports/gemini
15
15
  // first reword.
16
16
  export { DEFAULT_PROVIDER_REFUSED_MESSAGE, PROVIDER_REFUSED_CODE, ProviderRefusedError, } from './transports/provider-refused';
17
17
  export { ContextOverflowError, contextOverflowOf, DEFAULT_CONTEXT_OVERFLOW_MESSAGE, } from './transports/context-overflow-error';
18
+ // The platform tier table (GENC-1610) — what a `low` / `high` / `reasoning` slot resolves to
19
+ // per vendor, including the Anthropic output ceiling and both timeout clocks. Named exports,
20
+ // and the data half is deliberately free of DOM and DI imports so it also rides the
21
+ // `@genesislcap/ai-assistant/chat-driver` Node bundle; `buildTierProviders` does NOT, because
22
+ // it constructs providers through the feature-flag gate (see `tiers/build-tier-providers.ts`).
23
+ export { AI_TIER_IDS, AI_TIER_VENDORS, DEFAULT_AI_TIERS, resolveAITiers } from './tiers/ai-tiers';
24
+ export { buildTierProviders } from './tiers/build-tier-providers';
18
25
  export { AI_FEATURE_FLAG, isAIFeatureEnabled } from './utils/feature-flags';
19
26
  export { ChatTemperature } from './utils/temperature';
20
27
  // Request pricing. Exported so a host that prices its own calls — a server proxy, a usage ledger,
@@ -0,0 +1,173 @@
1
+ /**
2
+ * The platform's tier → model table (GENC-1610).
3
+ *
4
+ * @remarks
5
+ * Pure data, and deliberately so: this module imports nothing but types, never
6
+ * `ai-provider-di` (the one `@microsoft/fast-foundation` importer) and never
7
+ * `@genesislcap/web-core`, so it loads in bare Node. It is re-exported by name from
8
+ * `@genesislcap/ai-assistant/chat-driver`, which a server-side host requires at runtime —
9
+ * an `export *` or a DOM import anywhere in its closure crashes that host at require time.
10
+ * Provider construction lives next door in `build-tier-providers.ts`, which is browser-only.
11
+ *
12
+ * @packageDocumentation
13
+ */
14
+ /**
15
+ * Every tier id, in ascending capability order.
16
+ *
17
+ * This array is the source of truth and {@link AITierId} is derived from it, so the two cannot
18
+ * drift: a tier exists exactly when it is listed here. That matters because the provider
19
+ * builders and the table's tests all loop over this array — a tier missing from it would get
20
+ * no provider and no test, with nothing to say so.
21
+ *
22
+ * @beta
23
+ */
24
+ export const AI_TIER_IDS = ['low', 'high', 'reasoning'];
25
+ /**
26
+ * Every vendor in the table — the two the platform's proxy meters. The source of truth for
27
+ * {@link AITierVendor}, for the same reason as {@link AI_TIER_IDS}.
28
+ *
29
+ * @beta
30
+ */
31
+ export const AI_TIER_VENDORS = ['anthropic', 'gemini'];
32
+ /**
33
+ * 60s inactivity backstop on a stream that has already started.
34
+ *
35
+ * `foundation-ai` defaults this to 30s, which is TIGHTER than the platform proxy's own
36
+ * 45s mid-stream watchdog — so the client won every race and aborted the request before
37
+ * the server could notice, classify or log the stall. A stall is not retried; it surfaces
38
+ * as a dead turn. 60s puts the proxy back in charge of detecting upstream stalls.
39
+ */
40
+ const AI_STALL_TIMEOUT_MS = 60000;
41
+ /**
42
+ * 300s ceiling on the wait for the first byte — nothing has arrived yet.
43
+ *
44
+ * Distinct from {@link AI_STALL_TIMEOUT_MS}, which is only armed once the body starts.
45
+ * It stays generous because on the legacy (unframed) proxy path the response is buffered,
46
+ * so the first byte only lands when generation is complete; a tight value there would cap
47
+ * total generation time. Set explicitly rather than inherited, so it is a decision on the record.
48
+ */
49
+ const AI_NO_RESPONSE_TIMEOUT_MS = 300000;
50
+ /**
51
+ * The platform's default tier table.
52
+ *
53
+ * @remarks
54
+ * Frozen, because it is shared by every app in the process: {@link resolveAITiers} returns
55
+ * a fresh object for callers that override, and hands this back untouched for callers that
56
+ * do not.
57
+ *
58
+ * The models are the ones Genesis Create has been running these tiers on in production — except
59
+ * Anthropic's `reasoning`, moved to `claude-opus-4-8` (see its entry) — and they are a
60
+ * **benchmark result, not a preference**: re-bench before moving one. Two of the Gemini
61
+ * defaults carry a caveat recorded beside them, a preview model and an introductory rate. Two dates
62
+ * are already on the calendar for that: `gemini-3.8-flash` sits on an introductory rate that
63
+ * doubles on 2027-01-01 (at which point it is dearer on input than the model it replaced),
64
+ * and `token-cost.test.ts` fails the build that day; `ai-tiers.test.ts` fails alongside it
65
+ * saying the High tier is the thing to re-bench.
66
+ *
67
+ * Every model here must be in `SUPPORTED_*_MODEL_IDS` with a context limit and a price —
68
+ * pinned by tests, because an unpriced model silently bills at nothing.
69
+ *
70
+ * @beta
71
+ */
72
+ export const DEFAULT_AI_TIERS = Object.freeze({
73
+ anthropic: Object.freeze({
74
+ low: Object.freeze({
75
+ model: 'claude-haiku-4-5-20251001',
76
+ maxTokens: 16000,
77
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
78
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS,
79
+ }),
80
+ // The heavy tier: whole-file writes, and adaptive thinking out of the same budget
81
+ // wherever a caller leaves thinking on — hence Sonnet 5's full 128K ceiling rather
82
+ // than a figure that only fits one of the two.
83
+ high: Object.freeze({
84
+ model: 'claude-sonnet-5',
85
+ maxTokens: 128000,
86
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
87
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS,
88
+ }),
89
+ // One-shot judgement calls rather than tool loops, so a smaller ceiling is ample:
90
+ // `max_tokens` is only a cap, and this tier never emits a whole file.
91
+ //
92
+ // Opus 4.8 rather than the 4.7 Create benched: the same price ($5 / $25) and the same 1M
93
+ // window, but 4.8 supports native structured output where 4.7 takes the forced-tool
94
+ // fallback — and structured judgement is what this tier is asked for most (review of #2540).
95
+ reasoning: Object.freeze({
96
+ model: 'claude-opus-4-8',
97
+ maxTokens: 32000,
98
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
99
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS,
100
+ }),
101
+ }),
102
+ gemini: Object.freeze({
103
+ low: Object.freeze({
104
+ model: 'gemini-3.1-flash-lite',
105
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
106
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS,
107
+ }),
108
+ // On an INTRODUCTORY rate that doubles to $1.50 / $7.50 on 2027-01-01, from which point it
109
+ // is dearer on input than the gemini-3.5-flash it replaced. Chosen at the introductory rate
110
+ // knowingly; `ai-tiers.test.ts` fails that day to force the re-bench.
111
+ high: Object.freeze({
112
+ model: 'gemini-3.8-flash',
113
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
114
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS,
115
+ }),
116
+ // A PREVIEW model: no stability guarantee, and it may be withdrawn at short notice (its
117
+ // transport warns on every construction). Accepted knowingly as the platform default because
118
+ // no stable Gemini Pro was on offer when the tiers were benched; revisit as soon as Google
119
+ // promotes, deprecates or withdraws it, since every app on this default inherits the risk.
120
+ reasoning: Object.freeze({
121
+ model: 'gemini-3.1-pro-preview',
122
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
123
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS,
124
+ }),
125
+ }),
126
+ });
127
+ /**
128
+ * The table an app should actually use: {@link DEFAULT_AI_TIERS} with `overrides` applied
129
+ * per field, so an app that wants one model moved says only that and keeps the platform's
130
+ * clocks and ceilings.
131
+ *
132
+ * @remarks
133
+ * An override field set to `undefined` is skipped, exactly as if it had been left out. The
134
+ * common caller passes an optional config value straight through
135
+ * (`{ high: { model: appConfig.model } }`), and copying `undefined` over the default would
136
+ * hand the transport no model at all — it then quietly falls back to its own default, with
137
+ * nothing logged.
138
+ *
139
+ * The result is frozen whether or not anything was overridden, so whether mutating it throws
140
+ * does not depend on what the caller passed. The default itself is returned untouched when
141
+ * there is nothing to override, and is never mutated, which matters because it is shared
142
+ * across every app in the process.
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * // Keep everything, but run the reasoning tier on Fable.
147
+ * const tiers = resolveAITiers({ anthropic: { reasoning: { model: 'claude-fable-5' } } });
148
+ * ```
149
+ *
150
+ * @beta
151
+ */
152
+ export function resolveAITiers(overrides) {
153
+ if (!(overrides === null || overrides === void 0 ? void 0 : overrides.anthropic) && !(overrides === null || overrides === void 0 ? void 0 : overrides.gemini))
154
+ return DEFAULT_AI_TIERS;
155
+ return Object.freeze({
156
+ anthropic: mergeVendor(DEFAULT_AI_TIERS.anthropic, overrides.anthropic),
157
+ gemini: mergeVendor(DEFAULT_AI_TIERS.gemini, overrides.gemini),
158
+ });
159
+ }
160
+ function mergeVendor(defaults, overrides) {
161
+ var _a;
162
+ const merged = {};
163
+ for (const tier of AI_TIER_IDS) {
164
+ const defined = Object.fromEntries(Object.entries((_a = overrides === null || overrides === void 0 ? void 0 : overrides[tier]) !== null && _a !== void 0 ? _a : {}).filter(([, value]) => value !== undefined));
165
+ // Frozen in place rather than through `Object.freeze`'s return value, whose overloads
166
+ // widen a generic `T` to an index signature; the typed reference is the one we keep.
167
+ const entry = Object.assign(Object.assign({}, defaults[tier]), defined);
168
+ Object.freeze(entry);
169
+ merged[tier] = entry;
170
+ }
171
+ Object.freeze(merged);
172
+ return merged;
173
+ }
@@ -0,0 +1,72 @@
1
+ import { createAIProvider } from '../ai-provider';
2
+ import { AI_TIER_IDS, DEFAULT_AI_TIERS, } from './ai-tiers';
3
+ /**
4
+ * Builds one {@link AIProvider} per tier for a vendor, ready to register under the tier
5
+ * names agents resolve (`low` / `high` / `reasoning`).
6
+ *
7
+ * @remarks
8
+ * Each provider carries its tier's model, output ceiling (Anthropic) and both clocks, so no
9
+ * tier can silently fall back to the transport defaults — which is how a large tool write
10
+ * ends up truncated at 4096 tokens.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * registerAIProviders(
15
+ * container,
16
+ * buildTierProviders({ vendor: 'gemini', serverEndpoint: (v) => `/gwf/ai-service/${v}/chat` }),
17
+ * { default: 'high' },
18
+ * );
19
+ * ```
20
+ *
21
+ * @beta
22
+ */
23
+ export function buildTierProviders(options) {
24
+ const configs = tierProviderConfigs(options);
25
+ const providers = {};
26
+ for (const tier of AI_TIER_IDS) {
27
+ providers[tier] = createAIProvider(configs[tier]);
28
+ }
29
+ return providers;
30
+ }
31
+ /**
32
+ * The provider configs {@link buildTierProviders} builds from, before they reach
33
+ * `createAIProvider`.
34
+ *
35
+ * A seam rather than an inline literal, because what is being asserted is exactly what a
36
+ * constructed provider hides: a transport keeps `maxTokens` and both clocks private, so a
37
+ * tier silently falling back to the 4096-token default would be invisible from the outside
38
+ * — which is the failure this table exists to prevent.
39
+ *
40
+ * @internal
41
+ */
42
+ export function tierProviderConfigs(options) {
43
+ const { vendor, tiers = DEFAULT_AI_TIERS } = options;
44
+ const serverEndpoint = typeof options.serverEndpoint === 'function'
45
+ ? options.serverEndpoint(vendor)
46
+ : options.serverEndpoint;
47
+ if (!serverEndpoint) {
48
+ throw new Error(`buildTierProviders: no serverEndpoint for '${vendor}'. Every tier posts to your app's ` +
49
+ 'AI proxy; there is no browser-key mode here.');
50
+ }
51
+ const configs = {};
52
+ for (const tier of AI_TIER_IDS) {
53
+ configs[tier] =
54
+ vendor === 'anthropic'
55
+ ? {
56
+ providerType: 'anthropic',
57
+ model: tiers.anthropic[tier].model,
58
+ maxTokens: tiers.anthropic[tier].maxTokens,
59
+ serverEndpoint,
60
+ stallTimeout: tiers.anthropic[tier].stallTimeoutMs,
61
+ timeout: tiers.anthropic[tier].timeoutMs,
62
+ }
63
+ : {
64
+ providerType: 'gemini',
65
+ model: tiers.gemini[tier].model,
66
+ serverEndpoint,
67
+ stallTimeout: tiers.gemini[tier].stallTimeoutMs,
68
+ timeout: tiers.gemini[tier].timeoutMs,
69
+ };
70
+ }
71
+ return configs;
72
+ }
@@ -161,6 +161,20 @@ export const GEMINI_LONG_CONTEXT_THRESHOLD = 200000;
161
161
  * @beta
162
162
  */
163
163
  export const GEMINI_CACHED_INPUT_MULTIPLIER = 0.1;
164
+ /**
165
+ * The day `gemini-3.8-flash` leaves its introductory rate (see `GEMINI_PRICING` below).
166
+ *
167
+ * A constant rather than a literal in the test that enforces it, because a SECOND dated
168
+ * assertion now hangs off the same day: the High tier picked that model while it was the
169
+ * cheaper one, so the flip is also the trigger to re-bench the tier (`tiers/ai-tiers.test.ts`).
170
+ * Two hand-copied dates would drift the moment either is corrected, and a re-bench reminder
171
+ * that fires on the wrong day is no reminder at all.
172
+ *
173
+ * Not part of the package's public surface — it exists for those two tests.
174
+ *
175
+ * @internal
176
+ */
177
+ export const GEMINI_3_8_FLASH_INTRO_RATE_ENDS = Date.parse('2027-01-01T00:00:00Z');
164
178
  /**
165
179
  * Paid Standard tier rates (USD per million tokens) —
166
180
  * https://ai.google.dev/gemini-api/docs/pricing