@x12i/ai-profiles 1.2.7 → 1.5.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.
Files changed (144) hide show
  1. package/README.md +278 -18
  2. package/dist/buildCatalogFromOpenRouterApi.d.ts +29 -0
  3. package/dist/buildCatalogFromOpenRouterApi.d.ts.map +1 -0
  4. package/dist/buildCatalogFromOpenRouterApi.js +113 -0
  5. package/dist/buildCatalogFromOpenRouterApi.js.map +1 -0
  6. package/dist/buildModelsRegistry.d.ts +25 -0
  7. package/dist/buildModelsRegistry.d.ts.map +1 -0
  8. package/dist/buildModelsRegistry.js +190 -0
  9. package/dist/buildModelsRegistry.js.map +1 -0
  10. package/dist/buildResolvedProfile.d.ts +4 -1
  11. package/dist/buildResolvedProfile.d.ts.map +1 -1
  12. package/dist/buildResolvedProfile.js +102 -15
  13. package/dist/buildResolvedProfile.js.map +1 -1
  14. package/dist/catalogEnrichment.d.ts +19 -0
  15. package/dist/catalogEnrichment.d.ts.map +1 -0
  16. package/dist/catalogEnrichment.js +87 -0
  17. package/dist/catalogEnrichment.js.map +1 -0
  18. package/dist/computeRegistryStatistics.d.ts +15 -0
  19. package/dist/computeRegistryStatistics.d.ts.map +1 -0
  20. package/dist/computeRegistryStatistics.js +168 -0
  21. package/dist/computeRegistryStatistics.js.map +1 -0
  22. package/dist/constants.d.ts +6 -0
  23. package/dist/constants.d.ts.map +1 -1
  24. package/dist/constants.js +10 -0
  25. package/dist/constants.js.map +1 -1
  26. package/dist/data/models-catalog.json +2219 -0
  27. package/dist/data/models-profiles.json +1361 -193
  28. package/dist/data/models-profiles.schema.json +130 -0
  29. package/dist/data/models-registry.json +1606 -0
  30. package/dist/data/openrouter-models-catalog.json +857 -0
  31. package/dist/data/profiles-shortcuts.json +101 -2
  32. package/dist/errors.d.ts +1 -1
  33. package/dist/errors.d.ts.map +1 -1
  34. package/dist/errors.js.map +1 -1
  35. package/dist/formatRegistryStatistics.d.ts +4 -0
  36. package/dist/formatRegistryStatistics.d.ts.map +1 -0
  37. package/dist/formatRegistryStatistics.js +34 -0
  38. package/dist/formatRegistryStatistics.js.map +1 -0
  39. package/dist/getAIRegistryStatistics.d.ts +11 -0
  40. package/dist/getAIRegistryStatistics.d.ts.map +1 -0
  41. package/dist/getAIRegistryStatistics.js +52 -0
  42. package/dist/getAIRegistryStatistics.js.map +1 -0
  43. package/dist/index.d.ts +24 -1
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +20 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/listAIProfiles.d.ts +6 -0
  48. package/dist/listAIProfiles.d.ts.map +1 -1
  49. package/dist/listAIProfiles.js +6 -0
  50. package/dist/listAIProfiles.js.map +1 -1
  51. package/dist/loadModelsRegistry.d.ts +6 -0
  52. package/dist/loadModelsRegistry.d.ts.map +1 -0
  53. package/dist/loadModelsRegistry.js +43 -0
  54. package/dist/loadModelsRegistry.js.map +1 -0
  55. package/dist/loader/catalogMemoryCache.d.ts +12 -0
  56. package/dist/loader/catalogMemoryCache.d.ts.map +1 -0
  57. package/dist/loader/catalogMemoryCache.js +14 -0
  58. package/dist/loader/catalogMemoryCache.js.map +1 -0
  59. package/dist/loader/loadBundledModelsCatalog.d.ts +3 -0
  60. package/dist/loader/loadBundledModelsCatalog.d.ts.map +1 -0
  61. package/dist/loader/loadBundledModelsCatalog.js +20 -0
  62. package/dist/loader/loadBundledModelsCatalog.js.map +1 -0
  63. package/dist/loader/loadBundledModelsProfiles.d.ts +4 -0
  64. package/dist/loader/loadBundledModelsProfiles.d.ts.map +1 -0
  65. package/dist/loader/loadBundledModelsProfiles.js +14 -0
  66. package/dist/loader/loadBundledModelsProfiles.js.map +1 -0
  67. package/dist/loader/loadBundledRegistry.d.ts.map +1 -1
  68. package/dist/loader/loadBundledRegistry.js +2 -1
  69. package/dist/loader/loadBundledRegistry.js.map +1 -1
  70. package/dist/loader/loadModelsCatalog.d.ts +9 -0
  71. package/dist/loader/loadModelsCatalog.d.ts.map +1 -0
  72. package/dist/loader/loadModelsCatalog.js +72 -0
  73. package/dist/loader/loadModelsCatalog.js.map +1 -0
  74. package/dist/loader/loadModelsProfilesJson.d.ts +3 -0
  75. package/dist/loader/loadModelsProfilesJson.d.ts.map +1 -0
  76. package/dist/loader/loadModelsProfilesJson.js +18 -0
  77. package/dist/loader/loadModelsProfilesJson.js.map +1 -0
  78. package/dist/loader/loadRemoteModelsCatalog.d.ts +3 -0
  79. package/dist/loader/loadRemoteModelsCatalog.d.ts.map +1 -0
  80. package/dist/loader/loadRemoteModelsCatalog.js +23 -0
  81. package/dist/loader/loadRemoteModelsCatalog.js.map +1 -0
  82. package/dist/loader/loadRemoteModelsProfiles.d.ts +3 -0
  83. package/dist/loader/loadRemoteModelsProfiles.d.ts.map +1 -0
  84. package/dist/loader/loadRemoteModelsProfiles.js +21 -0
  85. package/dist/loader/loadRemoteModelsProfiles.js.map +1 -0
  86. package/dist/loader/loadRemoteRegistry.d.ts.map +1 -1
  87. package/dist/loader/loadRemoteRegistry.js +2 -1
  88. package/dist/loader/loadRemoteRegistry.js.map +1 -1
  89. package/dist/mergeCatalogSupplement.d.ts +4 -0
  90. package/dist/mergeCatalogSupplement.d.ts.map +1 -0
  91. package/dist/mergeCatalogSupplement.js +26 -0
  92. package/dist/mergeCatalogSupplement.js.map +1 -0
  93. package/dist/modelIdentity.d.ts +33 -0
  94. package/dist/modelIdentity.d.ts.map +1 -0
  95. package/dist/modelIdentity.js +150 -0
  96. package/dist/modelIdentity.js.map +1 -0
  97. package/dist/modelsCatalog.d.ts +21 -0
  98. package/dist/modelsCatalog.d.ts.map +1 -0
  99. package/dist/modelsCatalog.js +187 -0
  100. package/dist/modelsCatalog.js.map +1 -0
  101. package/dist/profileCatalogSupplement.d.ts +7 -0
  102. package/dist/profileCatalogSupplement.d.ts.map +1 -0
  103. package/dist/profileCatalogSupplement.js +292 -0
  104. package/dist/profileCatalogSupplement.js.map +1 -0
  105. package/dist/profileRegistryV2.d.ts +19 -0
  106. package/dist/profileRegistryV2.d.ts.map +1 -0
  107. package/dist/profileRegistryV2.js +257 -0
  108. package/dist/profileRegistryV2.js.map +1 -0
  109. package/dist/profileRegistryV3.d.ts +11 -0
  110. package/dist/profileRegistryV3.d.ts.map +1 -0
  111. package/dist/profileRegistryV3.js +526 -0
  112. package/dist/profileRegistryV3.js.map +1 -0
  113. package/dist/resolveAIProfile.d.ts +8 -0
  114. package/dist/resolveAIProfile.d.ts.map +1 -1
  115. package/dist/resolveAIProfile.js +63 -18
  116. package/dist/resolveAIProfile.js.map +1 -1
  117. package/dist/resolveModelRouting.d.ts +20 -0
  118. package/dist/resolveModelRouting.d.ts.map +1 -0
  119. package/dist/resolveModelRouting.js +70 -0
  120. package/dist/resolveModelRouting.js.map +1 -0
  121. package/dist/types.d.ts +348 -4
  122. package/dist/types.d.ts.map +1 -1
  123. package/dist/types.js +10 -0
  124. package/dist/types.js.map +1 -1
  125. package/dist/validateProfileVendorCoverage.d.ts +12 -0
  126. package/dist/validateProfileVendorCoverage.d.ts.map +1 -0
  127. package/dist/validateProfileVendorCoverage.js +58 -0
  128. package/dist/validateProfileVendorCoverage.js.map +1 -0
  129. package/dist/validation/openRouterAlignment.d.ts +6 -15
  130. package/dist/validation/openRouterAlignment.d.ts.map +1 -1
  131. package/dist/validation/openRouterAlignment.js +9 -57
  132. package/dist/validation/openRouterAlignment.js.map +1 -1
  133. package/dist/validation/validateModelsCatalog.d.ts +3 -0
  134. package/dist/validation/validateModelsCatalog.d.ts.map +1 -0
  135. package/dist/validation/validateModelsCatalog.js +42 -0
  136. package/dist/validation/validateModelsCatalog.js.map +1 -0
  137. package/dist/validation/validateModelsProfilesJson.d.ts.map +1 -1
  138. package/dist/validation/validateModelsProfilesJson.js +38 -2
  139. package/dist/validation/validateModelsProfilesJson.js.map +1 -1
  140. package/dist/vendorPolicy.d.ts +20 -0
  141. package/dist/vendorPolicy.d.ts.map +1 -0
  142. package/dist/vendorPolicy.js +71 -0
  143. package/dist/vendorPolicy.js.map +1 -0
  144. package/package.json +15 -2
@@ -1,35 +1,50 @@
1
1
  import { buildResolvedProfile } from "./buildResolvedProfile.js";
2
2
  import { AIProfilesError } from "./errors.js";
3
3
  import { loadAIProfilesRegistryWithWarnings } from "./loader/loadAIProfilesRegistry.js";
4
+ import { loadModelsCatalogWithWarnings } from "./loader/loadModelsCatalog.js";
4
5
  import { matchProfileInput } from "./profileInputMatch.js";
6
+ import { pickProfileChoice } from "./vendorPolicy.js";
7
+ /**
8
+ * Resolve a profile primary key, alias, or shortcut to a concrete model and runtime config.
9
+ *
10
+ * `input` names **intent** (e.g. `balanced`, `cheapest`, `cost-sensitive`), not a vendor model id.
11
+ * Use `options.choice` to pick an alternative within the profile (e.g. `cheap` + `safer_provider`).
12
+ *
13
+ * @see README.md — “Why AI profiles”
14
+ */
5
15
  export async function resolveAIProfile(input, options = {}) {
6
- const { registry, warnings } = await loadAIProfilesRegistryWithWarnings({
7
- source: options.source,
8
- refresh: options.refresh,
9
- });
16
+ const [{ registry, warnings }, catalogResult] = await Promise.all([
17
+ loadAIProfilesRegistryWithWarnings({
18
+ source: options.source,
19
+ refresh: options.refresh,
20
+ }),
21
+ loadModelsCatalogWithWarnings({
22
+ source: options.source,
23
+ refresh: options.refresh,
24
+ }),
25
+ ]);
26
+ const loaderWarnings = [...warnings, ...catalogResult.warnings];
10
27
  const match = matchProfileInput(registry, input);
11
28
  if (!match) {
12
29
  throw new AIProfilesError("UNKNOWN_PROFILE", `Unknown AI profile, shortcut, or alias: ${input}`, { input });
13
30
  }
14
31
  let profileKey;
15
- let choiceKey;
32
+ let requestedChoiceKey;
16
33
  let matchedShortcut;
17
34
  switch (match.kind) {
18
35
  case "profile":
19
36
  profileKey = match.profileKey;
20
- choiceKey =
21
- options.choice ?? registry.profiles[profileKey].defaultChoice;
37
+ requestedChoiceKey = options.choice;
22
38
  matchedShortcut = undefined;
23
39
  break;
24
40
  case "shortcut":
25
41
  profileKey = match.profileKey;
26
- choiceKey = options.choice ?? match.shortcut.choice;
42
+ requestedChoiceKey = options.choice ?? match.shortcut.choice;
27
43
  matchedShortcut = match.shortcut;
28
44
  break;
29
45
  case "alias":
30
46
  profileKey = match.profileKey;
31
- choiceKey =
32
- options.choice ?? registry.profiles[profileKey].defaultChoice;
47
+ requestedChoiceKey = options.choice;
33
48
  matchedShortcut = undefined;
34
49
  break;
35
50
  }
@@ -37,15 +52,42 @@ export async function resolveAIProfile(input, options = {}) {
37
52
  if (!profile) {
38
53
  throw new AIProfilesError("UNKNOWN_PROFILE", `Unknown AI profile: ${profileKey}`, { input, profile: profileKey });
39
54
  }
40
- const finalChoiceKey = choiceKey ?? profile.defaultChoice;
41
- const choice = profile.choices[finalChoiceKey];
42
- if (!choice) {
43
- if (options.allowFallbackToDefaultChoice) {
44
- return resolveAIProfile(profileKey, {
45
- ...options,
46
- choice: profile.defaultChoice,
55
+ let finalChoiceKey;
56
+ let choice;
57
+ try {
58
+ const picked = pickProfileChoice(profile, {
59
+ choiceKey: requestedChoiceKey,
60
+ allowedProviders: options.allowedProviders,
61
+ preferLeadingVendors: options.preferLeadingVendors ?? true,
62
+ explicitChoiceRequested: options.choice !== undefined,
63
+ });
64
+ finalChoiceKey = picked.choiceKey;
65
+ choice = picked.choice;
66
+ }
67
+ catch (error) {
68
+ const message = error instanceof Error ? error.message : String(error);
69
+ if (message.includes("allowedProviders")) {
70
+ throw new AIProfilesError("NO_ALLOWED_VENDOR", message, {
71
+ input,
72
+ profile: profileKey,
73
+ allowedProviders: options.allowedProviders,
74
+ availableProviders: [
75
+ ...new Set(Object.values(profile.choices).map((c) => c.provider)),
76
+ ],
47
77
  });
48
78
  }
79
+ if (message.startsWith("Unknown choice")) {
80
+ if (options.allowFallbackToDefaultChoice) {
81
+ return resolveAIProfile(profileKey, {
82
+ ...options,
83
+ choice: profile.defaultChoice,
84
+ });
85
+ }
86
+ throw new AIProfilesError("UNKNOWN_CHOICE", message, { input, profile: profileKey, choice: requestedChoiceKey });
87
+ }
88
+ throw error;
89
+ }
90
+ if (!profile.choices[finalChoiceKey]) {
49
91
  throw new AIProfilesError("UNKNOWN_CHOICE", `Unknown choice '${finalChoiceKey}' for AI profile '${profileKey}'`, { input, profile: profileKey, choice: finalChoiceKey });
50
92
  }
51
93
  return buildResolvedProfile({
@@ -53,9 +95,12 @@ export async function resolveAIProfile(input, options = {}) {
53
95
  shortcutKey: match.kind === "shortcut" ? match.shortcutKey : undefined,
54
96
  profile,
55
97
  choice,
98
+ choiceKey: finalChoiceKey,
56
99
  shortcut: matchedShortcut,
57
100
  registry,
58
- loaderWarnings: warnings,
101
+ loaderWarnings,
102
+ useOpenRouter: options.useOpenRouter,
103
+ catalog: catalogResult.catalog,
59
104
  });
60
105
  }
61
106
  //# sourceMappingURL=resolveAIProfile.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolveAIProfile.js","sourceRoot":"","sources":["../src/resolveAIProfile.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolveAIProfile.js","sourceRoot":"","sources":["../src/resolveAIProfile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { AIProfileBackend, AIProfileChoice, AIProvider } from "./types.js";
2
+ import { buildOpenRouterModelId } from "./modelIdentity.js";
3
+ export type AIModelRoutingMode = "openrouter" | "direct";
4
+ /** Reads `USE_OPENROUTER` from process env (loading `.env` once when unset). */
5
+ export declare function readUseOpenRouterFromEnv(): boolean | undefined;
6
+ /**
7
+ * Resolves whether to route via OpenRouter.
8
+ * Explicit option wins, then `USE_OPENROUTER` env, then default `true`.
9
+ */
10
+ export declare function resolveUseOpenRouter(explicit?: boolean): boolean;
11
+ export declare function isRemoteProfileChoice(choice: AIProfileChoice): boolean;
12
+ export { buildOpenRouterModelId };
13
+ export interface AppliedModelRouting {
14
+ provider: AIProvider;
15
+ modelId: string;
16
+ backend: AIProfileBackend;
17
+ routing: AIModelRoutingMode;
18
+ }
19
+ export declare function applyModelRouting(choice: AIProfileChoice, backend: AIProfileBackend, useOpenRouter: boolean): AppliedModelRouting;
20
+ //# sourceMappingURL=resolveModelRouting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveModelRouting.d.ts","sourceRoot":"","sources":["../src/resolveModelRouting.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAO5D,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,QAAQ,CAAC;AAyBzD,gFAAgF;AAChF,wBAAgB,wBAAwB,IAAI,OAAO,GAAG,SAAS,CAU9D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAMhE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAUtE;AAED,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAElC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,UAAU,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,OAAO,EAAE,kBAAkB,CAAC;CAC7B;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,gBAAgB,EACzB,aAAa,EAAE,OAAO,GACrB,mBAAmB,CAuBrB"}
@@ -0,0 +1,70 @@
1
+ import { loadDotenv } from "@x12i/env";
2
+ import { buildOpenRouterModelId } from "./modelIdentity.js";
3
+ import { buildModelInvocationFromChoice, getChoiceOpenRouterModelId, getChoiceVendorModelId, } from "./profileRegistryV2.js";
4
+ let dotenvLoaded = false;
5
+ function ensureDotenvLoaded() {
6
+ if (!dotenvLoaded) {
7
+ loadDotenv();
8
+ dotenvLoaded = true;
9
+ }
10
+ }
11
+ function parseBooleanEnv(value) {
12
+ const normalized = value.trim().toLowerCase();
13
+ if (["1", "true", "yes", "on"].includes(normalized)) {
14
+ return true;
15
+ }
16
+ if (["0", "false", "no", "off"].includes(normalized)) {
17
+ return false;
18
+ }
19
+ return undefined;
20
+ }
21
+ /** Reads `USE_OPENROUTER` from process env (loading `.env` once when unset). */
22
+ export function readUseOpenRouterFromEnv() {
23
+ ensureDotenvLoaded();
24
+ const raw = process.env.USE_OPENROUTER;
25
+ if (raw === undefined || raw.trim() === "") {
26
+ return undefined;
27
+ }
28
+ return parseBooleanEnv(raw);
29
+ }
30
+ /**
31
+ * Resolves whether to route via OpenRouter.
32
+ * Explicit option wins, then `USE_OPENROUTER` env, then default `true`.
33
+ */
34
+ export function resolveUseOpenRouter(explicit) {
35
+ if (explicit !== undefined) {
36
+ return explicit;
37
+ }
38
+ return readUseOpenRouterFromEnv() ?? true;
39
+ }
40
+ export function isRemoteProfileChoice(choice) {
41
+ if (choice.provider === "local") {
42
+ return false;
43
+ }
44
+ if (choice.modelId.startsWith("funcx:") || choice.modelId.startsWith("Xenova/")) {
45
+ return false;
46
+ }
47
+ return true;
48
+ }
49
+ export { buildOpenRouterModelId };
50
+ export function applyModelRouting(choice, backend, useOpenRouter) {
51
+ const vendorModelId = getChoiceVendorModelId(choice);
52
+ if (!isRemoteProfileChoice(choice) || !useOpenRouter) {
53
+ return {
54
+ provider: choice.provider,
55
+ modelId: vendorModelId,
56
+ backend: useOpenRouter ? backend : backend === "openrouter" ? "custom" : backend,
57
+ routing: "direct",
58
+ };
59
+ }
60
+ const openRouterModelId = getChoiceOpenRouterModelId(choice) ??
61
+ buildModelInvocationFromChoice(choice).openrouter?.modelId ??
62
+ buildOpenRouterModelId(choice.provider, vendorModelId);
63
+ return {
64
+ provider: "openrouter",
65
+ modelId: openRouterModelId,
66
+ backend: "openrouter",
67
+ routing: "openrouter",
68
+ };
69
+ }
70
+ //# sourceMappingURL=resolveModelRouting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveModelRouting.js","sourceRoot":"","sources":["../src/resolveModelRouting.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,4 +1,14 @@
1
- export type AIProvider = "openai" | "anthropic" | "google" | "deepseek" | "mistral" | "groq" | "xai" | "local" | "custom";
1
+ /**
2
+ * Registry types for {@link https://github.com/x12i/ai-profiles | @x12i/ai-profiles}.
3
+ *
4
+ * An **AI profile** is a capability contract (e.g. `cheap`, `deep`, `json`) — what kind of
5
+ * work is needed — not a specific model. Each profile has a **default choice** and optional
6
+ * **choices** (alternatives) that map intent to `provider`, `modelId`, pricing, and runtime
7
+ * defaults. Tasks should reference profiles (and optional choices), not vendor model names.
8
+ *
9
+ * @see README.md — “Why AI profiles” for the full design rationale.
10
+ */
11
+ export type AIProvider = "openai" | "anthropic" | "google" | "deepseek" | "mistral" | "groq" | "together" | "xai" | "local" | "custom" | "openrouter";
2
12
  export type InstructionTier = "default" | "cpu" | "reasoning";
3
13
  export type AIProfileBackend = "openrouter" | "llama-cpp" | "transformersjs" | "custom";
4
14
  export interface AIModelPricing {
@@ -31,27 +41,121 @@ export interface AIRuntimeDefaults {
31
41
  instructionTier?: InstructionTier;
32
42
  backend?: AIProfileBackend;
33
43
  }
44
+ /**
45
+ * One implementation option within a profile (e.g. `default`, `cost_floor`, `openai_deep`).
46
+ * Choices let the same profile intent use different providers, cost/quality trade-offs, or
47
+ * capabilities (long context, batch execution) without changing caller code.
48
+ */
49
+ export interface ProfileChoiceApi {
50
+ defaultTransport: "openrouter" | "direct";
51
+ vendorModelId: string;
52
+ openRouterModelId?: string | null;
53
+ }
54
+ export interface ProfileChoicePricingV2 {
55
+ billingModel: "token" | "self_hosted";
56
+ currency: "USD";
57
+ unit: "1M_tokens";
58
+ priceStatus?: "standard" | "promotional" | "estimated";
59
+ notes?: string;
60
+ standard?: {
61
+ input?: number;
62
+ output?: number;
63
+ cachedInput?: number;
64
+ };
65
+ batch?: {
66
+ input?: number;
67
+ output?: number;
68
+ };
69
+ cache?: {
70
+ write5m?: number;
71
+ write1h?: number;
72
+ hit?: number;
73
+ write?: number;
74
+ };
75
+ futureStandard?: {
76
+ input?: number;
77
+ output?: number;
78
+ validUntil?: string;
79
+ notes?: string;
80
+ };
81
+ }
82
+ export interface ProfileChoiceVerification {
83
+ lastVerifiedAt?: string;
84
+ sourceRefs?: string[];
85
+ }
86
+ export interface ProfileChoiceLimits {
87
+ contextWindow?: number;
88
+ maxOutputTokens?: number;
89
+ }
90
+ export type ModelAvailabilityStatus = "verified" | "predicted";
91
+ export type DeploymentType = "proprietary" | "openWeightsHosted" | "selfHosted";
92
+ export interface ProfileRequiredCapabilities {
93
+ reasoning?: "minimal" | "low" | "medium" | "high" | "max";
94
+ coding?: boolean;
95
+ context?: "standard" | "large" | "xlarge";
96
+ cost?: "low" | "medium" | "high";
97
+ speed?: "high";
98
+ latency?: "low";
99
+ quality?: "high" | "premium";
100
+ structuredOutput?: boolean;
101
+ supportsJsonSchema?: boolean;
102
+ supportsVision?: boolean;
103
+ supportsTools?: boolean;
104
+ }
105
+ export interface ModelCapabilitiesSnapshot {
106
+ contextWindow?: number;
107
+ maxOutputTokens?: number;
108
+ supportsVision?: boolean;
109
+ supportsTools?: boolean;
110
+ supportsReasoning?: boolean;
111
+ supportsJsonSchema?: boolean;
112
+ supportsCaching?: boolean;
113
+ supportsBatch?: boolean;
114
+ supportsStreaming?: boolean;
115
+ }
34
116
  export interface AIProfileChoice {
35
117
  choice: string;
36
118
  displayName: string;
37
119
  description: string;
120
+ /** Canonical vendor-native model identifier. */
38
121
  provider: AIProvider;
39
122
  modelId: string;
40
- pricing?: AIModelPricing;
123
+ /** Whether the SKU is production-stable or a forward-looking placeholder. */
124
+ modelStatus?: ModelAvailabilityStatus;
125
+ /** How the model is operated when not a proprietary vendor API. */
126
+ deploymentType?: DeploymentType;
127
+ /** Transport mapping (v2 registry). */
128
+ api?: ProfileChoiceApi;
129
+ /** Legacy flat pricing and/or v2 nested pricing blocks. */
130
+ pricing?: AIModelPricing | ProfileChoicePricingV2;
41
131
  runtime?: AIRuntimeDefaults;
42
132
  reason?: string;
43
133
  status?: "active" | "experimental" | "deprecated";
134
+ verification?: ProfileChoiceVerification;
135
+ limits?: ProfileChoiceLimits;
44
136
  metadata?: Record<string, unknown>;
45
137
  }
138
+ /**
139
+ * Capability contract for a class of AI work (cost, speed, reasoning, structured output,
140
+ * agentic tools, research, local inference). Resolved via {@link resolveAIProfile}.
141
+ */
46
142
  export interface AIProfileDefinition {
47
143
  profile: string;
48
144
  displayName: string;
49
145
  description: string;
146
+ /** Grouping for filtering (`cost`, `speed`, `standard`, `reasoning`, `output`, `agentic`, `research`, `local`). */
50
147
  category: string;
51
148
  /** Many alternate names for this profile (see `matchProfileInput`). */
52
149
  aliases?: string[];
53
150
  defaultChoice: string;
54
151
  choices: Record<string, AIProfileChoice>;
152
+ /** Intent contract for dynamic model selection (Optimixer). */
153
+ requiredCapabilities?: ProfileRequiredCapabilities;
154
+ /** Profile-level deployment strategy (e.g. open weights). */
155
+ deploymentType?: DeploymentType;
156
+ /** Deterministic failover order when trimming vendors or choices. */
157
+ fallbackChoiceOrder?: string[];
158
+ intendedUseCases?: string[];
55
159
  status?: "active" | "experimental" | "deprecated";
56
160
  runtime?: AIRuntimeDefaults;
57
161
  metadata?: Record<string, unknown>;
@@ -61,11 +165,25 @@ export interface AIProfileShortcut {
61
165
  choice?: string;
62
166
  description?: string;
63
167
  }
168
+ export type ModelsProfilesSchemaId = "x12i.ai-profiles.models-profiles" | "x12i.ai-profiles.model-registry.v2" | "x12i.ai-profiles.model-registry.v3";
64
169
  export interface ModelsProfilesJson {
65
- schema: "x12i.ai-profiles.models-profiles";
170
+ schema: ModelsProfilesSchemaId;
171
+ registryId?: string;
66
172
  /** Optional label in source JSON; not used for compatibility or selection logic. */
67
173
  version?: string;
68
174
  generatedAt?: string;
175
+ lastVerifiedAt?: string;
176
+ namingConvention?: string;
177
+ semantics?: Record<string, string>;
178
+ sourceCatalog?: {
179
+ providers: Array<{
180
+ id: string;
181
+ docsUrl?: string;
182
+ }>;
183
+ modelsCatalogUrl?: string;
184
+ /** @deprecated Use modelsCatalogUrl */
185
+ openRouterCatalogUrl?: string;
186
+ };
69
187
  currency: "USD";
70
188
  pricingUnit: "1M_tokens";
71
189
  profiles: Record<string, AIProfileDefinition>;
@@ -111,9 +229,149 @@ export interface AIShortcutSummary {
111
229
  description?: string;
112
230
  }
113
231
  export interface AIProfileWarning {
114
- code: "USING_BUNDLED_FALLBACK" | "PROFILE_DEPRECATED" | "CHOICE_DEPRECATED" | "PRICING_MISSING" | "REMOTE_REFRESH_FAILED_USING_CACHE";
232
+ code: "USING_BUNDLED_FALLBACK" | "PROFILE_DEPRECATED" | "CHOICE_DEPRECATED" | "PRICING_MISSING" | "MODEL_PREDICTED" | "PRICING_FROM_CATALOG" | "REMOTE_REFRESH_FAILED_USING_CACHE" | "OPENROUTER_UNAVAILABLE";
115
233
  message: string;
116
234
  }
235
+ export interface ModelInvocationEndpoint {
236
+ provider: AIProvider;
237
+ modelId: string;
238
+ }
239
+ export interface ModelInvocation {
240
+ direct: ModelInvocationEndpoint;
241
+ openrouter: ModelInvocationEndpoint | null;
242
+ }
243
+ export type ModelRegistryKind = "linked" | "openrouter-only" | "local-only" | "faulty";
244
+ export type ModelLinkageStatus = "matched" | "faulty" | "skipped" | "catalog-miss";
245
+ export interface ModelRegistryLinkage {
246
+ status: ModelLinkageStatus;
247
+ openRouterCatalogId?: string;
248
+ canonicalSlug?: string;
249
+ matchMethod?: string;
250
+ exemptReason?: string;
251
+ }
252
+ export interface ModelRegistryEntry {
253
+ registryKey: string;
254
+ kind: ModelRegistryKind;
255
+ vendor: ModelInvocationEndpoint;
256
+ invocation: ModelInvocation;
257
+ linkage: ModelRegistryLinkage;
258
+ usage: {
259
+ profileChoices: Array<{
260
+ profile: string;
261
+ choice: string;
262
+ }>;
263
+ };
264
+ }
265
+ export interface ModelsRegistryJson {
266
+ schema: "x12i.ai-profiles.models-registry";
267
+ version?: string;
268
+ generatedAt?: string;
269
+ inputs?: {
270
+ openRouterCatalog?: {
271
+ schema?: string;
272
+ version?: string;
273
+ generatedAt?: string;
274
+ modelCount?: number;
275
+ };
276
+ modelsProfiles?: {
277
+ version?: string;
278
+ generatedAt?: string;
279
+ choiceCount?: number;
280
+ };
281
+ };
282
+ models: Record<string, ModelRegistryEntry>;
283
+ }
284
+ export interface CatalogOpenRouterPricing {
285
+ input?: number;
286
+ output?: number;
287
+ cachedInput?: number;
288
+ cacheWrite?: number;
289
+ batchInput?: number;
290
+ batchOutput?: number;
291
+ imageInput?: number;
292
+ audioInput?: number;
293
+ internalReasoning?: number;
294
+ webSearchPerRequest?: number;
295
+ }
296
+ export interface CatalogOpenRouterCapabilities {
297
+ reasoning?: boolean;
298
+ toolCalling?: boolean;
299
+ structuredOutputs?: boolean;
300
+ imageGeneration?: boolean;
301
+ }
302
+ export interface CatalogDirectSlice {
303
+ provider: AIProvider;
304
+ modelId: string;
305
+ /**
306
+ * OpenRouter slug prefix when `provider` is `custom` (e.g. `qwen` for `qwen/qwen3.7-max`).
307
+ * Not used for API calls — documents the upstream vendor namespace.
308
+ */
309
+ vendorNamespace?: string;
310
+ }
311
+ export interface CatalogOpenRouterSlice {
312
+ provider: "openrouter";
313
+ modelId: string;
314
+ canonicalSlug?: string;
315
+ status?: string;
316
+ modalities?: {
317
+ input?: string[];
318
+ output?: string[];
319
+ };
320
+ contextWindow?: number;
321
+ maxCompletionTokens?: number;
322
+ knowledgeCutoff?: string;
323
+ pricing?: CatalogOpenRouterPricing;
324
+ capabilities?: CatalogOpenRouterCapabilities;
325
+ }
326
+ /**
327
+ * Unified catalog model: two explicit call shapes (never conflate them).
328
+ * - `direct` — vendor-native / direct API identity
329
+ * - `openrouter` — OpenRouter transport (`provider` is always `openrouter`)
330
+ */
331
+ export interface CatalogModelEntry {
332
+ direct: CatalogDirectSlice;
333
+ openrouter: CatalogOpenRouterSlice;
334
+ displayName?: string;
335
+ family?: string;
336
+ status?: string;
337
+ modalities?: CatalogOpenRouterSlice["modalities"];
338
+ contextWindow?: number;
339
+ maxCompletionTokens?: number;
340
+ knowledgeCutoff?: string;
341
+ }
342
+ export interface ModelsCatalogJson {
343
+ schema: "x12i.ai-profiles.models-catalog";
344
+ version?: string;
345
+ generatedAt?: string;
346
+ currency: "USD";
347
+ pricingUnit: "1M_tokens";
348
+ sources: {
349
+ openrouter: {
350
+ catalogUrl: string;
351
+ pricingUrl?: string;
352
+ verifiedAt?: string;
353
+ normalization?: string;
354
+ };
355
+ };
356
+ notes?: string[];
357
+ models: CatalogModelEntry[];
358
+ }
359
+ /** @deprecated Use {@link ModelsCatalogJson} */
360
+ export type OpenRouterModelsCatalogJson = ModelsCatalogJson;
361
+ export interface ProfileVendorCoverageResult {
362
+ profile: string;
363
+ leadingVendorsPresent: AIProvider[];
364
+ leadingVendorsMissing: AIProvider[];
365
+ nonLeadingChoices: Array<{
366
+ choice: string;
367
+ provider: AIProvider;
368
+ }>;
369
+ }
370
+ export interface ValidateProfileVendorCoverageResult {
371
+ ok: boolean;
372
+ profiles: ProfileVendorCoverageResult[];
373
+ }
374
+ /** Materialized profile + choice: model, pricing, runtime, and routing after registry resolution. */
117
375
  export interface ResolvedAIProfile {
118
376
  input: string;
119
377
  profile: string;
@@ -125,12 +383,20 @@ export interface ResolvedAIProfile {
125
383
  choiceDescription: string;
126
384
  provider: AIProvider;
127
385
  modelId: string;
386
+ /** How the resolved provider/model were routed (`openrouter` vs vendor-direct). */
387
+ routing: "openrouter" | "direct";
128
388
  instructionTier: InstructionTier;
129
389
  backend: AIProfileBackend;
130
390
  pricing?: AIModelPricing & {
131
391
  currency: "USD";
132
392
  unit: "1M_tokens";
133
393
  };
394
+ /** Echo of choice modelStatus when present. */
395
+ modelStatus?: ModelAvailabilityStatus;
396
+ limits?: ProfileChoiceLimits;
397
+ capabilities?: ModelCapabilitiesSnapshot;
398
+ /** Profile intent contract (from registry definition). */
399
+ requiredCapabilities?: ProfileRequiredCapabilities;
134
400
  runtime: Required<AIRuntimeDefaults>;
135
401
  metadata?: Record<string, unknown>;
136
402
  reason?: string;
@@ -144,6 +410,8 @@ export interface ResolvedAIProfile {
144
410
  profile: string;
145
411
  choice?: string;
146
412
  };
413
+ /** Direct and OpenRouter call shapes for the resolved choice. */
414
+ invocation: ModelInvocation;
147
415
  warnings?: AIProfileWarning[];
148
416
  }
149
417
  export interface ListAIProfilesOptions {
@@ -159,6 +427,17 @@ export interface ResolveAIProfileOptions {
159
427
  refresh?: boolean;
160
428
  allowFallbackToDefaultChoice?: boolean;
161
429
  strict?: boolean;
430
+ /**
431
+ * When true (default), remote models resolve via OpenRouter (`provider: "openrouter"`,
432
+ * `modelId: "{vendor}/{model}"`). When false, use the choice's vendor provider and model id.
433
+ * Falls back to `USE_OPENROUTER` in `.env` when omitted (`1`/`true` → OpenRouter,
434
+ * `0`/`false` → direct). Azure and AWS routing are not supported yet.
435
+ */
436
+ useOpenRouter?: boolean;
437
+ /** Restrict resolution to these vendors; auto-picks when default is outside the set. */
438
+ allowedProviders?: AIProvider[];
439
+ /** When auto-picking, rank {@link LEADING_VENDORS} ahead of other providers (default true). */
440
+ preferLeadingVendors?: boolean;
162
441
  }
163
442
  export interface LoadRegistryOptions {
164
443
  source?: RegistrySourceMode;
@@ -168,4 +447,69 @@ export interface LoadRegistryResult {
168
447
  registry: AIProfilesRegistry;
169
448
  warnings: AIProfileWarning[];
170
449
  }
450
+ export interface ProfileStatisticsFilter {
451
+ includeDeprecated?: boolean;
452
+ includeExperimental?: boolean;
453
+ category?: string;
454
+ }
455
+ export interface GetAIRegistryStatisticsOptions extends LoadRegistryOptions {
456
+ /** Include models catalog counts (default true). */
457
+ includeCatalog?: boolean;
458
+ /** Include profile↔catalog linkage counts (default true). */
459
+ includeLinkage?: boolean;
460
+ includeDeprecated?: boolean;
461
+ includeExperimental?: boolean;
462
+ category?: string;
463
+ }
464
+ export interface AIRegistryStatistics {
465
+ schema: "x12i.ai-profiles.registry-statistics";
466
+ generatedAt: string;
467
+ sources: {
468
+ profiles: "remote" | "bundled";
469
+ catalog?: "remote" | "bundled";
470
+ };
471
+ registry: {
472
+ version?: string;
473
+ loadedAt: string;
474
+ currency: "USD";
475
+ pricingUnit: "1M_tokens";
476
+ };
477
+ profiles: {
478
+ total: number;
479
+ choices: number;
480
+ aliases: number;
481
+ uniqueProviders: number;
482
+ providers: AIProvider[];
483
+ uniqueModelKeys: number;
484
+ uniqueVendorModelIds: number;
485
+ byStatus: Record<string, number>;
486
+ byCategory: Record<string, number>;
487
+ categories: string[];
488
+ averageChoicesPerProfile: number;
489
+ };
490
+ shortcuts: {
491
+ total: number;
492
+ };
493
+ catalog?: {
494
+ totalModels: number;
495
+ uniqueDirectProviders: number;
496
+ directProviders: string[];
497
+ active: number;
498
+ deprecated: number;
499
+ withPricing: number;
500
+ withReasoningCapability: number;
501
+ catalogVersion?: string;
502
+ generatedAt?: string;
503
+ };
504
+ linkage?: {
505
+ registryEntries: number;
506
+ profileChoiceReferences: number;
507
+ byLinkageStatus: Record<string, number>;
508
+ byKind: Record<string, number>;
509
+ matched: number;
510
+ faulty: number;
511
+ catalogMiss: number;
512
+ skipped: number;
513
+ };
514
+ }
171
515
  //# sourceMappingURL=types.d.ts.map