@genesislcap/foundation-ai 15.33.0 → 15.33.1-GENC-1601.2

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,145 @@
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, and
105
+ * they are a **benchmark result, not a preference** — re-bench before moving one. Two dates
106
+ * are already on the calendar for that: `gemini-3.8-flash` sits on an introductory rate that
107
+ * doubles on 2027-01-01 (at which point it is dearer on input than the model it replaced),
108
+ * and `token-cost.test.ts` fails the build that day; `ai-tiers.test.ts` fails alongside it
109
+ * saying the High tier is the thing to re-bench.
110
+ *
111
+ * Every model here must be in `SUPPORTED_*_MODEL_IDS` with a context limit and a price —
112
+ * pinned by tests, because an unpriced model silently bills at nothing.
113
+ *
114
+ * @beta
115
+ */
116
+ export declare const DEFAULT_AI_TIERS: AITiers;
117
+ /**
118
+ * Per-tier overrides, shallow-merged field by field over the defaults.
119
+ *
120
+ * @beta
121
+ */
122
+ export interface AITierOverrides {
123
+ anthropic?: Partial<Record<AITierId, Partial<AnthropicTierConfig>>>;
124
+ gemini?: Partial<Record<AITierId, Partial<GeminiTierConfig>>>;
125
+ }
126
+ /**
127
+ * The table an app should actually use: {@link DEFAULT_AI_TIERS} with `overrides` applied
128
+ * per field, so an app that wants one model moved says only that and keeps the platform's
129
+ * clocks and ceilings.
130
+ *
131
+ * @remarks
132
+ * Returns the frozen default itself when there is nothing to override, and a fresh
133
+ * (unfrozen) object otherwise. Either way the default is never mutated, which matters
134
+ * because it is shared across every app in the process.
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * // Keep everything, but run the reasoning tier on the newest Opus.
139
+ * const tiers = resolveAITiers({ anthropic: { reasoning: { model: 'claude-opus-4-8' } } });
140
+ * ```
141
+ *
142
+ * @beta
143
+ */
144
+ export declare function resolveAITiers(overrides?: AITierOverrides): AITiers;
145
+ //# 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;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,gBAAgB,EAAE,OA2ClB,CAAC;AAEd;;;;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;;;;;;;;;;;;;;;;;GAiBG;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,146 @@
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, and
59
+ * they are a **benchmark result, not a preference** — re-bench before moving one. Two dates
60
+ * are already on the calendar for that: `gemini-3.8-flash` sits on an introductory rate that
61
+ * doubles on 2027-01-01 (at which point it is dearer on input than the model it replaced),
62
+ * and `token-cost.test.ts` fails the build that day; `ai-tiers.test.ts` fails alongside it
63
+ * saying the High tier is the thing to re-bench.
64
+ *
65
+ * Every model here must be in `SUPPORTED_*_MODEL_IDS` with a context limit and a price —
66
+ * pinned by tests, because an unpriced model silently bills at nothing.
67
+ *
68
+ * @beta
69
+ */
70
+ export const DEFAULT_AI_TIERS = Object.freeze({
71
+ anthropic: Object.freeze({
72
+ low: Object.freeze({
73
+ model: 'claude-haiku-4-5-20251001',
74
+ maxTokens: 16000,
75
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
76
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS,
77
+ }),
78
+ // The heavy tier: whole-file writes, and adaptive thinking out of the same budget
79
+ // wherever a caller leaves thinking on — hence Sonnet 5's full 128K ceiling rather
80
+ // than a figure that only fits one of the two.
81
+ high: Object.freeze({
82
+ model: 'claude-sonnet-5',
83
+ maxTokens: 128000,
84
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
85
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS,
86
+ }),
87
+ // One-shot judgement calls rather than tool loops, so a smaller ceiling is ample:
88
+ // `max_tokens` is only a cap, and this tier never emits a whole file.
89
+ reasoning: Object.freeze({
90
+ model: 'claude-opus-4-7',
91
+ maxTokens: 32000,
92
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
93
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS,
94
+ }),
95
+ }),
96
+ gemini: Object.freeze({
97
+ low: Object.freeze({
98
+ model: 'gemini-3.1-flash-lite',
99
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
100
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS,
101
+ }),
102
+ high: Object.freeze({
103
+ model: 'gemini-3.8-flash',
104
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
105
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS,
106
+ }),
107
+ reasoning: Object.freeze({
108
+ model: 'gemini-3.1-pro-preview',
109
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
110
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS,
111
+ }),
112
+ }),
113
+ });
114
+ /**
115
+ * The table an app should actually use: {@link DEFAULT_AI_TIERS} with `overrides` applied
116
+ * per field, so an app that wants one model moved says only that and keeps the platform's
117
+ * clocks and ceilings.
118
+ *
119
+ * @remarks
120
+ * Returns the frozen default itself when there is nothing to override, and a fresh
121
+ * (unfrozen) object otherwise. Either way the default is never mutated, which matters
122
+ * because it is shared across every app in the process.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * // Keep everything, but run the reasoning tier on the newest Opus.
127
+ * const tiers = resolveAITiers({ anthropic: { reasoning: { model: 'claude-opus-4-8' } } });
128
+ * ```
129
+ *
130
+ * @beta
131
+ */
132
+ export function resolveAITiers(overrides) {
133
+ if (!(overrides === null || overrides === void 0 ? void 0 : overrides.anthropic) && !(overrides === null || overrides === void 0 ? void 0 : overrides.gemini))
134
+ return DEFAULT_AI_TIERS;
135
+ return {
136
+ anthropic: mergeVendor(DEFAULT_AI_TIERS.anthropic, overrides.anthropic),
137
+ gemini: mergeVendor(DEFAULT_AI_TIERS.gemini, overrides.gemini),
138
+ };
139
+ }
140
+ function mergeVendor(defaults, overrides) {
141
+ const merged = {};
142
+ for (const tier of AI_TIER_IDS) {
143
+ merged[tier] = Object.assign(Object.assign({}, defaults[tier]), overrides === null || overrides === void 0 ? void 0 : overrides[tier]);
144
+ }
145
+ return merged;
146
+ }
@@ -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