waypoi 0.0.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 (260) hide show
  1. package/.github/instructions/ui.instructions.md +42 -0
  2. package/.github/workflows/ci.yml +35 -0
  3. package/.github/workflows/publish.yml +71 -0
  4. package/.github/workflows/release.yml +48 -0
  5. package/.playwright-mcp/console-2026-04-04T01-41-10-746Z.log +2 -0
  6. package/.playwright-mcp/console-2026-04-04T01-41-28-799Z.log +3 -0
  7. package/.playwright-mcp/console-2026-04-05T02-26-51-909Z.log +76 -0
  8. package/.playwright-mcp/page-2026-04-04T01-41-10-816Z.yml +1 -0
  9. package/.playwright-mcp/page-2026-04-04T01-41-29-141Z.yml +77 -0
  10. package/.playwright-mcp/page-2026-04-04T01-41-42-633Z.yml +190 -0
  11. package/.playwright-mcp/page-2026-04-04T01-42-03-929Z.yml +262 -0
  12. package/.playwright-mcp/page-2026-04-04T02-12-54-813Z.yml +6 -0
  13. package/.playwright-mcp/page-2026-04-04T02-14-58-600Z.yml +190 -0
  14. package/.playwright-mcp/page-2026-04-04T02-15-03-923Z.yml +190 -0
  15. package/.playwright-mcp/page-2026-04-04T02-15-07-426Z.yml +190 -0
  16. package/.playwright-mcp/page-2026-04-04T02-15-25-729Z.yml +262 -0
  17. package/.playwright-mcp/page-2026-04-04T02-16-22-984Z.yml +262 -0
  18. package/.playwright-mcp/page-2026-04-04T02-17-00-599Z.yml +190 -0
  19. package/.playwright-mcp/page-2026-04-04T02-17-50-874Z.yml +190 -0
  20. package/.playwright-mcp/page-2026-04-05T02-26-55-570Z.yml +6 -0
  21. package/AGENTS.md +48 -0
  22. package/CHANGELOG.md +131 -0
  23. package/README.md +552 -0
  24. package/assets/agent-mode.png +0 -0
  25. package/assets/categorize.png +0 -0
  26. package/assets/dashboard.png +0 -0
  27. package/assets/endpoint-proxy.png +0 -0
  28. package/assets/icon.png +0 -0
  29. package/assets/mcp-generate-image.png +0 -0
  30. package/assets/mcp-understand-image.png +0 -0
  31. package/assets/peek-token-flow.png +0 -0
  32. package/assets/playground.png +0 -0
  33. package/assets/sankey.png +0 -0
  34. package/cli/index.ts +2805 -0
  35. package/cli/legacyRewrite.ts +108 -0
  36. package/cli/modelRef.ts +24 -0
  37. package/dist/cli/index.js +2536 -0
  38. package/dist/cli/legacyRewrite.js +92 -0
  39. package/dist/cli/modelRef.js +20 -0
  40. package/dist/src/benchmark/artifacts.js +131 -0
  41. package/dist/src/benchmark/capabilityClassifier.js +81 -0
  42. package/dist/src/benchmark/capabilityStore.js +144 -0
  43. package/dist/src/benchmark/config.js +238 -0
  44. package/dist/src/benchmark/gates.js +118 -0
  45. package/dist/src/benchmark/jobs.js +252 -0
  46. package/dist/src/benchmark/runner.js +1847 -0
  47. package/dist/src/benchmark/schema.js +353 -0
  48. package/dist/src/benchmark/suites.js +314 -0
  49. package/dist/src/benchmark/tinyQaDataset.js +422 -0
  50. package/dist/src/benchmark/types.js +25 -0
  51. package/dist/src/config.js +47 -0
  52. package/dist/src/index.js +178 -0
  53. package/dist/src/mcp/client.js +215 -0
  54. package/dist/src/mcp/discovery.js +226 -0
  55. package/dist/src/mcp/policy.js +65 -0
  56. package/dist/src/mcp/registry.js +129 -0
  57. package/dist/src/mcp/service.js +460 -0
  58. package/dist/src/middleware/auth.js +179 -0
  59. package/dist/src/middleware/requestCapture.js +192 -0
  60. package/dist/src/middleware/requestStats.js +118 -0
  61. package/dist/src/pools/builder.js +132 -0
  62. package/dist/src/pools/repository.js +69 -0
  63. package/dist/src/pools/scheduler.js +360 -0
  64. package/dist/src/pools/types.js +2 -0
  65. package/dist/src/protocols/adapters/dashscope.js +267 -0
  66. package/dist/src/protocols/adapters/inferenceV2.js +346 -0
  67. package/dist/src/protocols/adapters/openai.js +27 -0
  68. package/dist/src/protocols/registry.js +99 -0
  69. package/dist/src/protocols/types.js +2 -0
  70. package/dist/src/providers/health.js +153 -0
  71. package/dist/src/providers/importer.js +289 -0
  72. package/dist/src/providers/modelRegistry.js +313 -0
  73. package/dist/src/providers/repository.js +361 -0
  74. package/dist/src/providers/types.js +2 -0
  75. package/dist/src/routes/admin.js +531 -0
  76. package/dist/src/routes/audio.js +295 -0
  77. package/dist/src/routes/chat.js +240 -0
  78. package/dist/src/routes/embeddings.js +157 -0
  79. package/dist/src/routes/images.js +288 -0
  80. package/dist/src/routes/mcp.js +256 -0
  81. package/dist/src/routes/mcpService.js +100 -0
  82. package/dist/src/routes/models.js +48 -0
  83. package/dist/src/routes/responses.js +711 -0
  84. package/dist/src/routes/sessions.js +450 -0
  85. package/dist/src/routes/stats.js +270 -0
  86. package/dist/src/routes/ui.js +97 -0
  87. package/dist/src/routes/videos.js +107 -0
  88. package/dist/src/routing/router.js +338 -0
  89. package/dist/src/services/imageGeneration.js +280 -0
  90. package/dist/src/services/imageUnderstanding.js +352 -0
  91. package/dist/src/services/videoGeneration.js +79 -0
  92. package/dist/src/storage/captureRepository.js +1591 -0
  93. package/dist/src/storage/files.js +157 -0
  94. package/dist/src/storage/imageCache.js +346 -0
  95. package/dist/src/storage/repositories.js +388 -0
  96. package/dist/src/storage/sessionRepository.js +370 -0
  97. package/dist/src/storage/statsRepository.js +204 -0
  98. package/dist/src/transport/httpClient.js +126 -0
  99. package/dist/src/types.js +2 -0
  100. package/dist/src/utils/messageMedia.js +285 -0
  101. package/dist/src/utils/modelCapabilities.js +108 -0
  102. package/dist/src/utils/modelDiscovery.js +170 -0
  103. package/dist/src/version.js +5 -0
  104. package/dist/src/workers/captureRetention.js +25 -0
  105. package/dist/src/workers/configWatcher.js +91 -0
  106. package/dist/src/workers/healthChecker.js +21 -0
  107. package/dist/src/workers/statsRotation.js +41 -0
  108. package/docs/LLM/output_schema.md +312 -0
  109. package/docs/benchmark.md +208 -0
  110. package/docs/mcp-guidelines.md +125 -0
  111. package/docs/mcp-service.md +178 -0
  112. package/docs/opencode.md +86 -0
  113. package/docs/providers.md +79 -0
  114. package/examples/benchmark.config.yaml +28 -0
  115. package/examples/providers/alibaba-dashscope.yaml +88 -0
  116. package/examples/providers/alibaba-llm.yaml +64 -0
  117. package/examples/providers/alibaba-registry.yaml +7 -0
  118. package/examples/providers/inference-v2-ray.yaml +29 -0
  119. package/examples/scenarios/assets/omni-call-sample.wav +0 -0
  120. package/examples/scenarios/custom.jsonl +5 -0
  121. package/examples/scenarios/custom.yaml +40 -0
  122. package/model-form-v2.png +0 -0
  123. package/package.json +66 -0
  124. package/provider-form-v2.png +0 -0
  125. package/provider-form.png +0 -0
  126. package/scripts/manual-test.sh +11 -0
  127. package/scripts/version-from-git.js +23 -0
  128. package/src/benchmark/artifacts.ts +149 -0
  129. package/src/benchmark/capabilityClassifier.ts +99 -0
  130. package/src/benchmark/capabilityStore.ts +174 -0
  131. package/src/benchmark/config.ts +337 -0
  132. package/src/benchmark/gates.ts +164 -0
  133. package/src/benchmark/jobs.ts +312 -0
  134. package/src/benchmark/runner.ts +2519 -0
  135. package/src/benchmark/schema.ts +443 -0
  136. package/src/benchmark/suites.ts +323 -0
  137. package/src/benchmark/tinyQaDataset.ts +428 -0
  138. package/src/benchmark/types.ts +442 -0
  139. package/src/config.ts +44 -0
  140. package/src/index.ts +195 -0
  141. package/src/mcp/client.ts +305 -0
  142. package/src/mcp/discovery.ts +266 -0
  143. package/src/mcp/policy.ts +105 -0
  144. package/src/mcp/registry.ts +164 -0
  145. package/src/mcp/service.ts +611 -0
  146. package/src/middleware/auth.ts +251 -0
  147. package/src/middleware/requestCapture.ts +245 -0
  148. package/src/middleware/requestStats.ts +163 -0
  149. package/src/pools/builder.ts +159 -0
  150. package/src/pools/repository.ts +71 -0
  151. package/src/pools/scheduler.ts +425 -0
  152. package/src/pools/types.ts +117 -0
  153. package/src/protocols/adapters/dashscope.ts +335 -0
  154. package/src/protocols/adapters/inferenceV2.ts +428 -0
  155. package/src/protocols/adapters/openai.ts +32 -0
  156. package/src/protocols/registry.ts +117 -0
  157. package/src/protocols/types.ts +81 -0
  158. package/src/providers/health.ts +207 -0
  159. package/src/providers/importer.ts +402 -0
  160. package/src/providers/modelRegistry.ts +415 -0
  161. package/src/providers/repository.ts +439 -0
  162. package/src/providers/types.ts +113 -0
  163. package/src/routes/admin.ts +666 -0
  164. package/src/routes/audio.ts +372 -0
  165. package/src/routes/chat.ts +301 -0
  166. package/src/routes/embeddings.ts +197 -0
  167. package/src/routes/images.ts +356 -0
  168. package/src/routes/mcp.ts +320 -0
  169. package/src/routes/mcpService.ts +114 -0
  170. package/src/routes/models.ts +50 -0
  171. package/src/routes/responses.ts +872 -0
  172. package/src/routes/sessions.ts +558 -0
  173. package/src/routes/stats.ts +312 -0
  174. package/src/routes/ui.ts +96 -0
  175. package/src/routes/videos.ts +132 -0
  176. package/src/routing/router.ts +501 -0
  177. package/src/services/imageGeneration.ts +396 -0
  178. package/src/services/imageUnderstanding.ts +449 -0
  179. package/src/services/videoGeneration.ts +127 -0
  180. package/src/storage/captureRepository.ts +1835 -0
  181. package/src/storage/files.ts +178 -0
  182. package/src/storage/imageCache.ts +405 -0
  183. package/src/storage/repositories.ts +494 -0
  184. package/src/storage/sessionRepository.ts +419 -0
  185. package/src/storage/statsRepository.ts +238 -0
  186. package/src/transport/httpClient.ts +145 -0
  187. package/src/types.ts +322 -0
  188. package/src/utils/messageMedia.ts +293 -0
  189. package/src/utils/modelCapabilities.ts +161 -0
  190. package/src/utils/modelDiscovery.ts +203 -0
  191. package/src/workers/captureRetention.ts +25 -0
  192. package/src/workers/configWatcher.ts +115 -0
  193. package/src/workers/healthChecker.ts +22 -0
  194. package/src/workers/statsRotation.ts +49 -0
  195. package/tests/benchmarkAdminRoutes.test.ts +82 -0
  196. package/tests/benchmarkBasics.test.ts +116 -0
  197. package/tests/captureAdminRoutes.test.ts +420 -0
  198. package/tests/captureRepository.test.ts +797 -0
  199. package/tests/cliLegacyRewrite.test.ts +45 -0
  200. package/tests/imageGeneration.service.test.ts +107 -0
  201. package/tests/imageUnderstanding.service.test.ts +123 -0
  202. package/tests/mcpPolicy.test.ts +105 -0
  203. package/tests/mcpService.test.ts +1245 -0
  204. package/tests/modelRef.test.ts +23 -0
  205. package/tests/modelsRoutes.test.ts +154 -0
  206. package/tests/sessionMediaCache.test.ts +167 -0
  207. package/tests/statsRoutes.test.ts +323 -0
  208. package/tsconfig.json +15 -0
  209. package/ui/index.html +16 -0
  210. package/ui/package-lock.json +8521 -0
  211. package/ui/package.json +52 -0
  212. package/ui/postcss.config.js +6 -0
  213. package/ui/public/assets/apple-touch-icon.png +0 -0
  214. package/ui/public/assets/favicon-16.png +0 -0
  215. package/ui/public/assets/favicon-32.png +0 -0
  216. package/ui/public/assets/icon-192.png +0 -0
  217. package/ui/public/assets/icon-512.png +0 -0
  218. package/ui/src/App.tsx +27 -0
  219. package/ui/src/api/client.ts +1503 -0
  220. package/ui/src/components/EndpointUsageGuide.tsx +361 -0
  221. package/ui/src/components/Layout.tsx +124 -0
  222. package/ui/src/components/MessageContent.tsx +365 -0
  223. package/ui/src/components/ToolCallMessage.tsx +179 -0
  224. package/ui/src/components/ToolPicker.tsx +442 -0
  225. package/ui/src/components/messageContentParser.test.ts +41 -0
  226. package/ui/src/components/messageContentParser.ts +73 -0
  227. package/ui/src/components/thinkingPreview.test.ts +27 -0
  228. package/ui/src/components/thinkingPreview.ts +15 -0
  229. package/ui/src/components/toMermaidSankey.test.ts +78 -0
  230. package/ui/src/components/toMermaidSankey.ts +56 -0
  231. package/ui/src/components/ui/button.tsx +58 -0
  232. package/ui/src/components/ui/input.tsx +21 -0
  233. package/ui/src/components/ui/textarea.tsx +21 -0
  234. package/ui/src/lib/utils.ts +6 -0
  235. package/ui/src/main.tsx +9 -0
  236. package/ui/src/pages/AgentPlayground.tsx +2010 -0
  237. package/ui/src/pages/Benchmark.tsx +988 -0
  238. package/ui/src/pages/Dashboard.tsx +581 -0
  239. package/ui/src/pages/Peek.tsx +962 -0
  240. package/ui/src/pages/Settings.tsx +2013 -0
  241. package/ui/src/pages/agentPlaygroundPayload.test.ts +109 -0
  242. package/ui/src/pages/agentPlaygroundPayload.ts +97 -0
  243. package/ui/src/pages/agentThinkingContent.test.ts +50 -0
  244. package/ui/src/pages/agentThinkingContent.ts +57 -0
  245. package/ui/src/pages/dashboardTokenUsage.test.ts +66 -0
  246. package/ui/src/pages/dashboardTokenUsage.ts +36 -0
  247. package/ui/src/pages/imageUpload.test.ts +39 -0
  248. package/ui/src/pages/imageUpload.ts +71 -0
  249. package/ui/src/pages/peekFilters.test.ts +29 -0
  250. package/ui/src/pages/peekFilters.ts +13 -0
  251. package/ui/src/pages/peekMedia.test.ts +58 -0
  252. package/ui/src/pages/peekMedia.ts +148 -0
  253. package/ui/src/pages/sessionAutoTitle.test.ts +128 -0
  254. package/ui/src/pages/sessionAutoTitle.ts +106 -0
  255. package/ui/src/stores/settings.ts +58 -0
  256. package/ui/src/styles/globals.css +223 -0
  257. package/ui/src/vite-env.d.ts +8 -0
  258. package/ui/tailwind.config.js +106 -0
  259. package/ui/tsconfig.json +32 -0
  260. package/ui/vite.config.ts +37 -0
@@ -0,0 +1,289 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.importProviders = importProviders;
7
+ const fs_1 = require("fs");
8
+ const path_1 = __importDefault(require("path"));
9
+ const yaml_1 = __importDefault(require("yaml"));
10
+ const registry_1 = require("../protocols/registry");
11
+ const repository_1 = require("./repository");
12
+ async function importProviders(paths, options) {
13
+ const registryPath = path_1.default.resolve(options.registryPath);
14
+ const registryDir = path_1.default.dirname(registryPath);
15
+ const registryRaw = await fs_1.promises.readFile(registryPath, "utf8");
16
+ const registry = (yaml_1.default.parse(registryRaw) ?? {});
17
+ const envMap = await loadEnvMap(options.envFilePath);
18
+ const warnings = [];
19
+ const providers = [];
20
+ const existing = await (0, repository_1.loadProviderStore)(paths);
21
+ const existingById = new Map(existing.providers.map((provider) => [provider.id, provider]));
22
+ for (const entry of registry.providers ?? []) {
23
+ if (!entry?.id || !entry.file) {
24
+ warnings.push(`Skipping malformed registry entry: ${JSON.stringify(entry)}`);
25
+ continue;
26
+ }
27
+ const providerConfigPath = path_1.default.resolve(registryDir, entry.file);
28
+ let configRaw;
29
+ try {
30
+ configRaw = await fs_1.promises.readFile(providerConfigPath, "utf8");
31
+ }
32
+ catch (error) {
33
+ warnings.push(`Failed to load provider config ${providerConfigPath}: ${error.message}`);
34
+ continue;
35
+ }
36
+ const config = (yaml_1.default.parse(configRaw) ?? {});
37
+ const protocolRaw = (config.endpoint?.protocol ?? entry.protocol ?? "unknown").toLowerCase();
38
+ const normalizedProtocol = (0, registry_1.canonicalizeProtocol)(protocolRaw);
39
+ const protocol = normalizedProtocol === "openai" || normalizedProtocol === "inference_v2" || normalizedProtocol === "dashscope"
40
+ ? normalizedProtocol
41
+ : "unknown";
42
+ const auth = parseAuthConfig(config.auth);
43
+ const protocolConfig = parseProtocolConfig(config, protocol);
44
+ let supportsRouting = (0, registry_1.hasProtocolAdapter)(protocol);
45
+ const providerId = config.$id ?? entry.id;
46
+ const previous = existingById.get(providerId);
47
+ const envVar = typeof config.env === "string" ? config.env : undefined;
48
+ const apiKey = resolveApiKey(envVar, envMap, previous?.apiKey, options.overwriteAuth ?? false);
49
+ const baseUrl = config.endpoint?.baseUrl;
50
+ if (!baseUrl) {
51
+ warnings.push(`Provider '${providerId}' has no endpoint.baseUrl; skipped.`);
52
+ continue;
53
+ }
54
+ if (protocol === "inference_v2" && !protocolConfig?.router) {
55
+ warnings.push(`Provider '${providerId}' protocol '${protocolRaw}' requires endpoint.router; imported non-routable.`);
56
+ supportsRouting = false;
57
+ }
58
+ if (!supportsRouting) {
59
+ warnings.push(`Provider '${providerId}' protocol '${protocolRaw}' imported as non-routable in v1.`);
60
+ }
61
+ const models = (config.models ?? [])
62
+ .map((model) => {
63
+ if (!model.id) {
64
+ warnings.push(`Provider '${providerId}' has model without id; skipped.`);
65
+ return null;
66
+ }
67
+ const providerModelId = `${providerId}/${model.id}`;
68
+ const capabilities = toCapabilities(model.modalities ?? [], model.capabilities ?? {});
69
+ return {
70
+ providerModelId,
71
+ providerId,
72
+ modelId: model.id,
73
+ upstreamModel: model.upstream ?? model.id,
74
+ baseUrl: typeof model.baseUrl === "string" ? model.baseUrl : undefined,
75
+ apiKey: typeof model.apiKey === "string" ? model.apiKey : undefined,
76
+ insecureTls: model.insecureTls === true,
77
+ enabled: model.enabled !== false,
78
+ aliases: [],
79
+ free: model.free !== false,
80
+ modalities: model.modalities ?? [],
81
+ capabilities,
82
+ endpointType: inferEndpointType(capabilities),
83
+ benchmark: normalizeBenchmark(model.benchmark),
84
+ limits: model.limits,
85
+ };
86
+ })
87
+ .filter((model) => model !== null);
88
+ providers.push({
89
+ id: providerId,
90
+ name: config.name ?? entry.name ?? providerId,
91
+ description: config.description,
92
+ docs: config.docs,
93
+ protocol,
94
+ protocolRaw,
95
+ protocolConfig,
96
+ baseUrl,
97
+ insecureTls: previous?.insecureTls ?? (config.endpoint?.insecureTls === true),
98
+ autoInsecureTlsDomains: previous?.autoInsecureTlsDomains ?? (0, repository_1.normalizeDomainSuffixes)(config.autoInsecureTlsDomains),
99
+ enabled: previous?.enabled ?? true,
100
+ supportsRouting,
101
+ auth,
102
+ envVar,
103
+ apiKey,
104
+ limits: config.limits,
105
+ models,
106
+ warnings: supportsRouting ? undefined : [`Unsupported protocol: ${protocolRaw}`],
107
+ importedAt: new Date().toISOString(),
108
+ });
109
+ }
110
+ await (0, repository_1.saveProviderStore)(paths, providers);
111
+ return {
112
+ importedProviders: providers.length,
113
+ importedModels: providers.reduce((sum, provider) => sum + provider.models.length, 0),
114
+ warnings,
115
+ providers,
116
+ };
117
+ }
118
+ function parseAuthConfig(auth) {
119
+ if (!auth || typeof auth !== "object") {
120
+ return undefined;
121
+ }
122
+ const type = typeof auth.type === "string" ? auth.type.trim().toLowerCase() : "";
123
+ if (!type || !["bearer", "query", "header", "none"].includes(type)) {
124
+ return undefined;
125
+ }
126
+ return {
127
+ type: type,
128
+ keyParam: typeof auth.keyParam === "string" ? auth.keyParam : undefined,
129
+ headerName: typeof auth.headerName === "string" ? auth.headerName : undefined,
130
+ keyPrefix: typeof auth.keyPrefix === "string" ? auth.keyPrefix : undefined,
131
+ };
132
+ }
133
+ function parseProtocolConfig(config, protocol) {
134
+ if (protocol !== "inference_v2") {
135
+ return undefined;
136
+ }
137
+ const router = typeof config.endpoint?.router === "string"
138
+ ? config.endpoint.router.trim()
139
+ : undefined;
140
+ const responseTextPaths = extractStringArray(config.endpoint?.responseTextPaths ?? config.responseTextPaths);
141
+ return {
142
+ router: router && router.length > 0 ? router : undefined,
143
+ responseTextPaths: responseTextPaths.length > 0 ? responseTextPaths : undefined,
144
+ };
145
+ }
146
+ function extractStringArray(value) {
147
+ if (!Array.isArray(value)) {
148
+ return [];
149
+ }
150
+ return value
151
+ .map((entry) => (typeof entry === "string" ? entry.trim() : ""))
152
+ .filter((entry) => entry.length > 0);
153
+ }
154
+ function resolveApiKey(envVar, envMap, previous, overwriteAuth) {
155
+ if (!envVar) {
156
+ return previous;
157
+ }
158
+ const fromEnvFile = envMap[envVar];
159
+ const fromProcess = process.env[envVar];
160
+ const value = fromEnvFile ?? fromProcess;
161
+ if (!value && !previous) {
162
+ return undefined;
163
+ }
164
+ if (!overwriteAuth && previous) {
165
+ return previous;
166
+ }
167
+ return value ?? previous;
168
+ }
169
+ async function loadEnvMap(envFilePath) {
170
+ const loaded = {};
171
+ const candidates = envFilePath ? [envFilePath] : [];
172
+ for (const candidate of candidates) {
173
+ const fullPath = path_1.default.resolve(candidate);
174
+ try {
175
+ const raw = await fs_1.promises.readFile(fullPath, "utf8");
176
+ parseEnvInto(raw, loaded);
177
+ break;
178
+ }
179
+ catch (error) {
180
+ if (error.code !== "ENOENT") {
181
+ throw error;
182
+ }
183
+ }
184
+ }
185
+ return loaded;
186
+ }
187
+ function parseEnvInto(raw, out) {
188
+ const lines = raw.split("\n");
189
+ for (const line of lines) {
190
+ const trimmed = line.trim();
191
+ if (!trimmed || trimmed.startsWith("#")) {
192
+ continue;
193
+ }
194
+ const equals = trimmed.indexOf("=");
195
+ if (equals <= 0) {
196
+ continue;
197
+ }
198
+ const key = trimmed.slice(0, equals).trim();
199
+ let value = trimmed.slice(equals + 1).trim();
200
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
201
+ value = value.slice(1, -1);
202
+ }
203
+ if (key) {
204
+ out[key] = value;
205
+ }
206
+ }
207
+ }
208
+ function normalizeBenchmark(benchmark) {
209
+ if (!benchmark || typeof benchmark.livebench !== "number" || !Number.isFinite(benchmark.livebench)) {
210
+ return undefined;
211
+ }
212
+ return { livebench: benchmark.livebench };
213
+ }
214
+ function toCapabilities(modalities, caps) {
215
+ const input = new Set();
216
+ const output = new Set();
217
+ for (const modality of modalities) {
218
+ switch (modality) {
219
+ case "text-to-text":
220
+ input.add("text");
221
+ output.add("text");
222
+ break;
223
+ case "image-to-text":
224
+ input.add("image");
225
+ input.add("text");
226
+ output.add("text");
227
+ break;
228
+ case "text-to-image":
229
+ input.add("text");
230
+ output.add("image");
231
+ break;
232
+ case "audio-to-text":
233
+ input.add("audio");
234
+ output.add("text");
235
+ break;
236
+ case "text-to-audio":
237
+ input.add("text");
238
+ output.add("audio");
239
+ break;
240
+ case "text-to-embedding":
241
+ input.add("text");
242
+ output.add("embedding");
243
+ break;
244
+ case "text-to-video":
245
+ input.add("text");
246
+ output.add("video");
247
+ break;
248
+ case "image-to-video":
249
+ input.add("text");
250
+ input.add("image");
251
+ output.add("video");
252
+ break;
253
+ case "text-image-to-video":
254
+ input.add("text");
255
+ input.add("image");
256
+ output.add("video");
257
+ break;
258
+ }
259
+ }
260
+ if (input.size === 0 || output.size === 0) {
261
+ input.add("text");
262
+ output.add(caps.vision ? "text" : "text");
263
+ }
264
+ if (caps.vision && !input.has("image")) {
265
+ input.add("image");
266
+ }
267
+ return {
268
+ input: Array.from(input),
269
+ output: Array.from(output),
270
+ supportsTools: caps.tools,
271
+ supportsStreaming: caps.streaming,
272
+ source: "configured",
273
+ };
274
+ }
275
+ function inferEndpointType(capabilities) {
276
+ if (capabilities.output.includes("embedding")) {
277
+ return "embedding";
278
+ }
279
+ if (capabilities.output.includes("video")) {
280
+ return "video";
281
+ }
282
+ if (capabilities.output.includes("image")) {
283
+ return "diffusion";
284
+ }
285
+ if (capabilities.output.includes("audio")) {
286
+ return "audio";
287
+ }
288
+ return "llm";
289
+ }
@@ -0,0 +1,313 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listModelsForApi = listModelsForApi;
4
+ exports.resolveModel = resolveModel;
5
+ exports.pickBestProviderModelByCapabilities = pickBestProviderModelByCapabilities;
6
+ exports.listModelAliases = listModelAliases;
7
+ exports.getAvailableSmartPool = getAvailableSmartPool;
8
+ const repository_1 = require("../pools/repository");
9
+ const registry_1 = require("../protocols/registry");
10
+ const modelCapabilities_1 = require("../utils/modelCapabilities");
11
+ const repository_2 = require("./repository");
12
+ const health_1 = require("./health");
13
+ const SCORE_FALLBACK = 20;
14
+ const SMART_ALIAS = "smart";
15
+ async function listModelsForApi(paths, options) {
16
+ const providers = await (0, repository_2.listProviders)(paths);
17
+ const healthMap = await (0, health_1.getProviderModelHealthMap)(paths);
18
+ const entries = [];
19
+ for (const provider of providers) {
20
+ for (const model of provider.models) {
21
+ const health = healthMap[model.providerModelId];
22
+ const status = health?.status ?? "unknown";
23
+ if (options?.availableOnly && status === "down") {
24
+ continue;
25
+ }
26
+ const canonicalId = (0, repository_2.canonicalProviderModelId)(provider.id, model.modelId);
27
+ entries.push({
28
+ id: canonicalId,
29
+ provider_id: provider.id,
30
+ model_id: model.modelId,
31
+ provider_model_id: model.providerModelId,
32
+ object: "model",
33
+ owned_by: "waypoi",
34
+ endpoint_type: model.endpointType,
35
+ capabilities: model.capabilities,
36
+ enabled: provider.enabled && model.enabled !== false,
37
+ aliases: model.aliases ?? [],
38
+ slug: canonicalId,
39
+ waypoi_health: {
40
+ status,
41
+ lastCheckedAt: health?.lastCheckedAt ? new Date(health.lastCheckedAt).toISOString() : undefined,
42
+ consecutiveFailures: health?.consecutiveFailures,
43
+ latencyMsEwma: health?.latencyMsEwma,
44
+ },
45
+ });
46
+ }
47
+ }
48
+ return entries.sort((a, b) => a.id.localeCompare(b.id));
49
+ }
50
+ async function resolveModel(paths, inputId, requirements, routing) {
51
+ const pool = await (0, repository_1.getPoolByAlias)(paths, inputId);
52
+ if (pool) {
53
+ return { kind: "pool", alias: inputId };
54
+ }
55
+ const models = await flattenProviderModels(paths);
56
+ const healthMap = await (0, health_1.getProviderModelHealthMap)(paths);
57
+ const canonicalMatch = models.find((entry) => {
58
+ return entry.canonicalId === inputId || entry.model.providerModelId === inputId;
59
+ });
60
+ if (canonicalMatch) {
61
+ const candidates = await buildAndFilterCandidates(paths, [canonicalMatch], requirements, routing, healthMap);
62
+ return {
63
+ kind: "direct",
64
+ canonicalId: canonicalMatch.canonicalId,
65
+ candidates,
66
+ };
67
+ }
68
+ const aliasMatches = models.filter((entry) => {
69
+ if (entry.model.modelId === inputId) {
70
+ return true;
71
+ }
72
+ return Boolean(entry.model.aliases?.includes(inputId));
73
+ });
74
+ if (aliasMatches.length === 0) {
75
+ return { kind: "none", input: inputId };
76
+ }
77
+ if (aliasMatches.length > 1) {
78
+ return {
79
+ kind: "ambiguous",
80
+ input: inputId,
81
+ matches: aliasMatches.map((entry) => entry.canonicalId).sort(),
82
+ };
83
+ }
84
+ const winner = aliasMatches[0];
85
+ const candidates = await buildAndFilterCandidates(paths, [winner], requirements, routing, healthMap);
86
+ return {
87
+ kind: "direct",
88
+ canonicalId: winner.canonicalId,
89
+ candidates,
90
+ };
91
+ }
92
+ async function pickBestProviderModelByCapabilities(paths, requirements, preferredEndpointType) {
93
+ const all = await flattenProviderModels(paths);
94
+ const healthMap = await (0, health_1.getProviderModelHealthMap)(paths);
95
+ const filtered = all
96
+ .filter((entry) => entry.provider.enabled && entry.model.enabled !== false)
97
+ .filter((entry) => {
98
+ const health = healthMap[entry.model.providerModelId];
99
+ return health?.status !== "down";
100
+ })
101
+ .filter((entry) => (0, modelCapabilities_1.supportsRequirements)(entry.model.capabilities, requirements));
102
+ if (filtered.length === 0) {
103
+ return null;
104
+ }
105
+ const ranked = filtered.sort((a, b) => {
106
+ if (preferredEndpointType) {
107
+ const aPreferred = a.model.endpointType === preferredEndpointType ? 1 : 0;
108
+ const bPreferred = b.model.endpointType === preferredEndpointType ? 1 : 0;
109
+ if (aPreferred !== bPreferred) {
110
+ return bPreferred - aPreferred;
111
+ }
112
+ }
113
+ const aScore = typeof a.model.benchmark?.livebench === "number" ? a.model.benchmark.livebench : SCORE_FALLBACK;
114
+ const bScore = typeof b.model.benchmark?.livebench === "number" ? b.model.benchmark.livebench : SCORE_FALLBACK;
115
+ if (aScore !== bScore) {
116
+ return bScore - aScore;
117
+ }
118
+ return a.canonicalId.localeCompare(b.canonicalId);
119
+ });
120
+ return ranked[0].canonicalId;
121
+ }
122
+ async function listModelAliases(paths) {
123
+ const providers = await (0, repository_2.listProviders)(paths);
124
+ const aliases = new Set();
125
+ for (const provider of providers) {
126
+ for (const model of provider.models) {
127
+ for (const alias of model.aliases ?? []) {
128
+ aliases.add(alias);
129
+ }
130
+ }
131
+ }
132
+ const pools = await (0, repository_1.listPools)(paths);
133
+ for (const pool of pools) {
134
+ for (const alias of pool.aliases) {
135
+ if (alias === SMART_ALIAS) {
136
+ aliases.add(alias);
137
+ }
138
+ }
139
+ }
140
+ return Array.from(aliases).sort();
141
+ }
142
+ async function getAvailableSmartPool(paths) {
143
+ const pool = await (0, repository_1.getPoolByAlias)(paths, SMART_ALIAS);
144
+ if (!pool) {
145
+ return null;
146
+ }
147
+ const healthMap = await (0, health_1.getProviderModelHealthMap)(paths);
148
+ const candidates = pool.candidates.filter((candidate) => {
149
+ if (!candidate.providerEnabled || !candidate.modelEnabled || !candidate.supportsRouting) {
150
+ return false;
151
+ }
152
+ if (!candidate.baseUrl) {
153
+ return false;
154
+ }
155
+ if (!(0, registry_1.getProtocolAdapter)(candidate.protocol)) {
156
+ return false;
157
+ }
158
+ if (candidate.providerModelId) {
159
+ const health = healthMap[candidate.providerModelId];
160
+ if (health?.status === "down") {
161
+ return false;
162
+ }
163
+ }
164
+ const requiresApiKey = (candidate.auth?.type ?? "bearer") !== "none";
165
+ if (requiresApiKey && !candidate.apiKey) {
166
+ return false;
167
+ }
168
+ return true;
169
+ });
170
+ if (candidates.length === 0) {
171
+ return null;
172
+ }
173
+ return {
174
+ id: pool.id,
175
+ alias: SMART_ALIAS,
176
+ strategy: pool.strategy,
177
+ candidateCount: candidates.length,
178
+ capabilities: unionCapabilities(candidates),
179
+ };
180
+ }
181
+ async function flattenProviderModels(paths) {
182
+ const providers = await (0, repository_2.listProviders)(paths);
183
+ const flattened = [];
184
+ for (const provider of providers) {
185
+ for (const model of provider.models) {
186
+ flattened.push({
187
+ provider,
188
+ model,
189
+ canonicalId: (0, repository_2.canonicalProviderModelId)(provider.id, model.modelId),
190
+ });
191
+ }
192
+ }
193
+ return flattened;
194
+ }
195
+ async function buildAndFilterCandidates(paths, entries, requirements, routing, healthMap) {
196
+ const accepted = [];
197
+ const modelHealth = healthMap ?? (await (0, health_1.getProviderModelHealthMap)(paths));
198
+ for (const entry of entries) {
199
+ const candidate = buildCandidate(entry.provider, entry.model);
200
+ if (!candidate.providerEnabled || !candidate.modelEnabled) {
201
+ continue;
202
+ }
203
+ if (candidate.providerModelId) {
204
+ const health = modelHealth[candidate.providerModelId];
205
+ if (health?.status === "down") {
206
+ continue;
207
+ }
208
+ }
209
+ if (!candidate.baseUrl) {
210
+ continue;
211
+ }
212
+ const adapter = (0, registry_1.getProtocolAdapter)(candidate.protocol);
213
+ if (!candidate.supportsRouting || !adapter) {
214
+ continue;
215
+ }
216
+ const requiresApiKey = (candidate.auth?.type ?? "bearer") !== "none";
217
+ if (requiresApiKey && !candidate.apiKey) {
218
+ continue;
219
+ }
220
+ if (!(0, modelCapabilities_1.supportsRequirements)(candidate.capabilities, requirements)) {
221
+ continue;
222
+ }
223
+ if (routing) {
224
+ const support = adapter.supports({
225
+ operation: routing.operation,
226
+ stream: routing.stream,
227
+ capabilities: candidate.capabilities,
228
+ requiredInput: requirements.requiredInput,
229
+ requiredOutput: requirements.requiredOutput,
230
+ });
231
+ if (!support.supported) {
232
+ continue;
233
+ }
234
+ }
235
+ accepted.push(candidate);
236
+ }
237
+ return accepted.sort((a, b) => {
238
+ if (b.score !== a.score) {
239
+ return b.score - a.score;
240
+ }
241
+ return a.id.localeCompare(b.id);
242
+ });
243
+ }
244
+ function buildCandidate(provider, model) {
245
+ const score = model.benchmark?.livebench;
246
+ const baseUrl = model.baseUrl ?? provider.baseUrl;
247
+ return {
248
+ id: model.providerModelId,
249
+ providerModelId: model.providerModelId,
250
+ providerId: provider.id,
251
+ providerName: provider.name,
252
+ providerEnabled: provider.enabled,
253
+ modelEnabled: model.enabled !== false,
254
+ modelId: model.modelId,
255
+ aliases: model.aliases ?? [],
256
+ upstreamModel: model.upstreamModel,
257
+ baseUrl: baseUrl ?? "",
258
+ apiKey: model.apiKey ?? provider.apiKey,
259
+ insecureTls: (0, repository_2.getEffectiveModelInsecureTls)(provider, model),
260
+ autoInsecureTlsDomains: provider.autoInsecureTlsDomains ?? [],
261
+ protocol: provider.protocol,
262
+ protocolConfig: provider.protocolConfig,
263
+ auth: provider.auth,
264
+ supportsRouting: provider.supportsRouting,
265
+ free: model.free,
266
+ endpointType: model.endpointType,
267
+ capabilities: model.capabilities,
268
+ score: typeof score === "number" ? score : SCORE_FALLBACK,
269
+ scoreSource: typeof score === "number" ? "benchmark.livebench" : "fallback",
270
+ limits: {
271
+ requestsPerMinute: model.limits?.requests?.perMinute ?? provider.limits?.requests?.perMinute,
272
+ requestsPerHour: model.limits?.requests?.perHour ?? provider.limits?.requests?.perHour,
273
+ requestsPerDay: model.limits?.requests?.perDay ?? provider.limits?.requests?.perDay,
274
+ requestsPerWeek: model.limits?.requests?.perWeek ?? provider.limits?.requests?.perWeek,
275
+ tokensPerMinute: model.limits?.tokens?.perMinute ?? provider.limits?.tokens?.perMinute,
276
+ tokensPerHour: model.limits?.tokens?.perHour ?? provider.limits?.tokens?.perHour,
277
+ tokensPerDay: model.limits?.tokens?.perDay ?? provider.limits?.tokens?.perDay,
278
+ tokensPerWeek: model.limits?.tokens?.perWeek ?? provider.limits?.tokens?.perWeek,
279
+ },
280
+ };
281
+ }
282
+ function unionCapabilities(candidates) {
283
+ const input = new Set();
284
+ const output = new Set();
285
+ let supportsTools = false;
286
+ let supportsStreaming = false;
287
+ for (const candidate of candidates) {
288
+ for (const modality of candidate.capabilities.input) {
289
+ input.add(modality);
290
+ }
291
+ for (const modality of candidate.capabilities.output) {
292
+ output.add(modality);
293
+ }
294
+ if (candidate.capabilities.supportsTools) {
295
+ supportsTools = true;
296
+ }
297
+ if (candidate.capabilities.supportsStreaming) {
298
+ supportsStreaming = true;
299
+ }
300
+ }
301
+ const capabilities = {
302
+ input: Array.from(input).sort(),
303
+ output: Array.from(output).sort(),
304
+ source: "inferred",
305
+ };
306
+ if (supportsTools) {
307
+ capabilities.supportsTools = true;
308
+ }
309
+ if (supportsStreaming) {
310
+ capabilities.supportsStreaming = true;
311
+ }
312
+ return capabilities;
313
+ }