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,360 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.selectPoolCandidates = selectPoolCandidates;
4
+ exports.markPoolAttempt = markPoolAttempt;
5
+ exports.markPoolSuccess = markPoolSuccess;
6
+ exports.markPoolFailure = markPoolFailure;
7
+ exports.buildEndpointFromCandidate = buildEndpointFromCandidate;
8
+ exports.estimateTokensFromPayload = estimateTokensFromPayload;
9
+ exports.deriveCooldownMsFromHeaders = deriveCooldownMsFromHeaders;
10
+ const files_1 = require("../storage/files");
11
+ const modelCapabilities_1 = require("../utils/modelCapabilities");
12
+ const registry_1 = require("../protocols/registry");
13
+ const repository_1 = require("./repository");
14
+ const health_1 = require("../providers/health");
15
+ const DEFAULT_COOLDOWN_MS = 60_000;
16
+ async function selectPoolCandidates(paths, alias, requirements, routing) {
17
+ const pool = await (0, repository_1.getPoolByAlias)(paths, alias);
18
+ if (!pool) {
19
+ return null;
20
+ }
21
+ const healthMap = await (0, health_1.getProviderModelHealthMap)(paths);
22
+ const state = await (0, repository_1.loadPoolState)(paths);
23
+ const now = Date.now();
24
+ const skipped = [];
25
+ const candidates = [];
26
+ for (const candidate of pool.candidates) {
27
+ const candidateState = getCandidateState(state.candidates, candidate.id);
28
+ refreshWindows(candidateState, now);
29
+ const adapter = (0, registry_1.getProtocolAdapter)(candidate.protocol);
30
+ if (!candidate.supportsRouting || !adapter) {
31
+ skipped.push({ candidateId: candidate.id, reason: "unsupported_protocol" });
32
+ continue;
33
+ }
34
+ if (!candidate.baseUrl) {
35
+ skipped.push({ candidateId: candidate.id, reason: "missing_base_url" });
36
+ continue;
37
+ }
38
+ if (routing) {
39
+ const support = adapter.supports({
40
+ operation: routing.operation,
41
+ stream: routing.stream,
42
+ capabilities: candidate.capabilities,
43
+ requiredInput: requirements?.requiredInput,
44
+ requiredOutput: requirements?.requiredOutput,
45
+ });
46
+ if (!support.supported) {
47
+ skipped.push({
48
+ candidateId: candidate.id,
49
+ reason: support.reason ?? "unsupported_operation",
50
+ });
51
+ continue;
52
+ }
53
+ }
54
+ const requiresApiKey = (candidate.auth?.type ?? "bearer") !== "none";
55
+ if (requiresApiKey && !candidate.apiKey) {
56
+ skipped.push({ candidateId: candidate.id, reason: "missing_api_key" });
57
+ continue;
58
+ }
59
+ if (!candidate.providerEnabled) {
60
+ skipped.push({ candidateId: candidate.id, reason: "provider_disabled" });
61
+ continue;
62
+ }
63
+ if (!candidate.modelEnabled) {
64
+ skipped.push({ candidateId: candidate.id, reason: "model_disabled" });
65
+ continue;
66
+ }
67
+ if (candidate.providerModelId) {
68
+ const health = healthMap[candidate.providerModelId];
69
+ if (health?.status === "down") {
70
+ skipped.push({ candidateId: candidate.id, reason: "health_down" });
71
+ continue;
72
+ }
73
+ }
74
+ if (requirements &&
75
+ !(0, modelCapabilities_1.supportsRequirements)(candidate.capabilities, {
76
+ requiredInput: requirements.requiredInput,
77
+ requiredOutput: requirements.requiredOutput,
78
+ })) {
79
+ skipped.push({ candidateId: candidate.id, reason: "capability_mismatch" });
80
+ continue;
81
+ }
82
+ if (candidateState.cooldownUntil && new Date(candidateState.cooldownUntil).getTime() > now) {
83
+ skipped.push({ candidateId: candidate.id, reason: "cooldown" });
84
+ continue;
85
+ }
86
+ if (isRequestBudgetExhausted(candidate, candidateState)) {
87
+ skipped.push({ candidateId: candidate.id, reason: "request_budget_exhausted" });
88
+ continue;
89
+ }
90
+ candidates.push(candidate);
91
+ }
92
+ candidates.sort((a, b) => {
93
+ if (b.score !== a.score) {
94
+ return b.score - a.score;
95
+ }
96
+ const aState = getCandidateState(state.candidates, a.id);
97
+ const bState = getCandidateState(state.candidates, b.id);
98
+ const aFailureRatio = failureRatio(aState);
99
+ const bFailureRatio = failureRatio(bState);
100
+ if (aFailureRatio !== bFailureRatio) {
101
+ return aFailureRatio - bFailureRatio;
102
+ }
103
+ const aLatency = aState.latencyMsEwma ?? Number.POSITIVE_INFINITY;
104
+ const bLatency = bState.latencyMsEwma ?? Number.POSITIVE_INFINITY;
105
+ if (aLatency !== bLatency) {
106
+ return aLatency - bLatency;
107
+ }
108
+ return `${a.providerId}/${a.modelId}`.localeCompare(`${b.providerId}/${b.modelId}`);
109
+ });
110
+ await (0, repository_1.savePoolState)(paths, state);
111
+ return {
112
+ pool,
113
+ candidates,
114
+ skipped,
115
+ };
116
+ }
117
+ async function markPoolAttempt(paths, candidate, estimatedTokens) {
118
+ const state = await (0, repository_1.loadPoolState)(paths);
119
+ const entry = getCandidateState(state.candidates, candidate.id);
120
+ const now = Date.now();
121
+ refreshWindows(entry, now);
122
+ entry.attempts += 1;
123
+ entry.minuteRequests += 1;
124
+ entry.dayRequests += 1;
125
+ if (estimatedTokens > 0) {
126
+ entry.minuteTokens += estimatedTokens;
127
+ entry.dayTokens += estimatedTokens;
128
+ }
129
+ entry.lastUsedAt = new Date(now).toISOString();
130
+ await (0, repository_1.savePoolState)(paths, state);
131
+ }
132
+ async function markPoolSuccess(paths, candidate, latencyMs, consumedTokens = 0) {
133
+ const state = await (0, repository_1.loadPoolState)(paths);
134
+ const entry = getCandidateState(state.candidates, candidate.id);
135
+ const now = Date.now();
136
+ refreshWindows(entry, now);
137
+ entry.successes += 1;
138
+ entry.lastError = undefined;
139
+ entry.cooldownUntil = undefined;
140
+ entry.latencyMsEwma = ewma(entry.latencyMsEwma, latencyMs);
141
+ entry.lastUsedAt = new Date(now).toISOString();
142
+ if (consumedTokens > 0) {
143
+ entry.minuteTokens += consumedTokens;
144
+ entry.dayTokens += consumedTokens;
145
+ }
146
+ await (0, repository_1.savePoolState)(paths, state);
147
+ }
148
+ async function markPoolFailure(paths, candidate, details) {
149
+ const state = await (0, repository_1.loadPoolState)(paths);
150
+ const entry = getCandidateState(state.candidates, candidate.id);
151
+ const now = Date.now();
152
+ refreshWindows(entry, now);
153
+ entry.failures += 1;
154
+ entry.lastError = details.error;
155
+ entry.lastUsedAt = new Date(now).toISOString();
156
+ if (details.rateLimited) {
157
+ entry.rateLimitHits += 1;
158
+ const cooldownMs = deriveCooldownMsFromHeaders(details.headers ?? {}, DEFAULT_COOLDOWN_MS);
159
+ entry.cooldownUntil = new Date(now + cooldownMs).toISOString();
160
+ }
161
+ else if (entry.failures >= 3) {
162
+ entry.cooldownUntil = new Date(now + 30_000).toISOString();
163
+ }
164
+ await (0, repository_1.savePoolState)(paths, state);
165
+ }
166
+ function buildEndpointFromCandidate(candidate) {
167
+ const now = new Date();
168
+ return {
169
+ id: `pool:${candidate.id}`,
170
+ name: `${candidate.providerName}/${candidate.modelId}`,
171
+ baseUrl: candidate.baseUrl,
172
+ apiKey: candidate.apiKey,
173
+ disabled: false,
174
+ insecureTls: candidate.insecureTls === true,
175
+ priority: 0,
176
+ type: candidate.endpointType,
177
+ models: [
178
+ {
179
+ publicName: candidate.modelId,
180
+ upstreamModel: candidate.upstreamModel,
181
+ capabilities: candidate.capabilities,
182
+ },
183
+ ],
184
+ health: (0, files_1.defaultHealth)(),
185
+ createdAt: now,
186
+ updatedAt: now,
187
+ };
188
+ }
189
+ function estimateTokensFromPayload(payload) {
190
+ const maxTokens = payload.max_tokens;
191
+ if (typeof maxTokens === "number" && Number.isFinite(maxTokens) && maxTokens > 0) {
192
+ return Math.floor(maxTokens);
193
+ }
194
+ return 0;
195
+ }
196
+ function deriveCooldownMsFromHeaders(headers, fallbackMs) {
197
+ const retryAfter = headerValue(headers, "retry-after");
198
+ if (retryAfter) {
199
+ const asNumber = Number(retryAfter);
200
+ if (Number.isFinite(asNumber) && asNumber >= 0) {
201
+ return Math.max(1_000, asNumber * 1_000);
202
+ }
203
+ const asDate = Date.parse(retryAfter);
204
+ if (Number.isFinite(asDate)) {
205
+ return Math.max(1_000, asDate - Date.now());
206
+ }
207
+ }
208
+ const resetKeys = ["x-ratelimit-reset", "x-ratelimit-reset-requests", "ratelimit-reset"];
209
+ for (const key of resetKeys) {
210
+ const value = headerValue(headers, key);
211
+ if (!value) {
212
+ continue;
213
+ }
214
+ const parsed = Number(value);
215
+ if (!Number.isFinite(parsed)) {
216
+ continue;
217
+ }
218
+ if (parsed > 1_000_000_000) {
219
+ return Math.max(1_000, parsed * 1_000 - Date.now());
220
+ }
221
+ return Math.max(1_000, parsed * 1_000);
222
+ }
223
+ return fallbackMs;
224
+ }
225
+ function headerValue(headers, key) {
226
+ const exact = headers[key];
227
+ if (typeof exact === "string") {
228
+ return exact;
229
+ }
230
+ if (Array.isArray(exact) && exact.length > 0) {
231
+ return exact[0];
232
+ }
233
+ const found = Object.entries(headers).find(([name]) => name.toLowerCase() === key.toLowerCase());
234
+ if (!found) {
235
+ return undefined;
236
+ }
237
+ const value = found[1];
238
+ if (typeof value === "string") {
239
+ return value;
240
+ }
241
+ if (Array.isArray(value) && value.length > 0) {
242
+ return value[0];
243
+ }
244
+ return undefined;
245
+ }
246
+ function getCandidateState(states, candidateId) {
247
+ const existing = states[candidateId];
248
+ if (existing) {
249
+ return existing;
250
+ }
251
+ const created = {
252
+ candidateId,
253
+ attempts: 0,
254
+ successes: 0,
255
+ failures: 0,
256
+ rateLimitHits: 0,
257
+ minuteRequests: 0,
258
+ minuteTokens: 0,
259
+ hourRequests: 0,
260
+ hourTokens: 0,
261
+ dayRequests: 0,
262
+ dayTokens: 0,
263
+ weekRequests: 0,
264
+ weekTokens: 0,
265
+ };
266
+ states[candidateId] = created;
267
+ return created;
268
+ }
269
+ function refreshWindows(state, now) {
270
+ const minuteStart = state.minuteWindowStartedAt
271
+ ? new Date(state.minuteWindowStartedAt).getTime()
272
+ : Number.NaN;
273
+ if (!Number.isFinite(minuteStart) || now - minuteStart >= 60_000) {
274
+ state.minuteWindowStartedAt = new Date(now).toISOString();
275
+ state.minuteRequests = 0;
276
+ state.minuteTokens = 0;
277
+ }
278
+ const hourStart = state.hourWindowStartedAt
279
+ ? new Date(state.hourWindowStartedAt).getTime()
280
+ : Number.NaN;
281
+ if (!Number.isFinite(hourStart) || now - hourStart >= 3_600_000) {
282
+ state.hourWindowStartedAt = new Date(now).toISOString();
283
+ state.hourRequests = 0;
284
+ state.hourTokens = 0;
285
+ }
286
+ const dayStart = state.dayWindowStartedAt
287
+ ? new Date(state.dayWindowStartedAt).getTime()
288
+ : Number.NaN;
289
+ if (!Number.isFinite(dayStart) || now - dayStart >= 86_400_000) {
290
+ state.dayWindowStartedAt = new Date(now).toISOString();
291
+ state.dayRequests = 0;
292
+ state.dayTokens = 0;
293
+ }
294
+ const weekStart = state.weekWindowStartedAt
295
+ ? new Date(state.weekWindowStartedAt).getTime()
296
+ : Number.NaN;
297
+ if (!Number.isFinite(weekStart) || now - weekStart >= 604_800_000) {
298
+ state.weekWindowStartedAt = new Date(now).toISOString();
299
+ state.weekRequests = 0;
300
+ state.weekTokens = 0;
301
+ }
302
+ if (state.cooldownUntil && new Date(state.cooldownUntil).getTime() <= now) {
303
+ state.cooldownUntil = undefined;
304
+ }
305
+ }
306
+ function isRequestBudgetExhausted(candidate, state) {
307
+ if (typeof candidate.limits?.requestsPerMinute === "number") {
308
+ if (state.minuteRequests >= candidate.limits.requestsPerMinute) {
309
+ return true;
310
+ }
311
+ }
312
+ if (typeof candidate.limits?.requestsPerHour === "number") {
313
+ if (state.hourRequests >= candidate.limits.requestsPerHour) {
314
+ return true;
315
+ }
316
+ }
317
+ if (typeof candidate.limits?.requestsPerDay === "number") {
318
+ if (state.dayRequests >= candidate.limits.requestsPerDay) {
319
+ return true;
320
+ }
321
+ }
322
+ if (typeof candidate.limits?.requestsPerWeek === "number") {
323
+ if (state.weekRequests >= candidate.limits.requestsPerWeek) {
324
+ return true;
325
+ }
326
+ }
327
+ if (typeof candidate.limits?.tokensPerMinute === "number") {
328
+ if (state.minuteTokens >= candidate.limits.tokensPerMinute) {
329
+ return true;
330
+ }
331
+ }
332
+ if (typeof candidate.limits?.tokensPerHour === "number") {
333
+ if (state.hourTokens >= candidate.limits.tokensPerHour) {
334
+ return true;
335
+ }
336
+ }
337
+ if (typeof candidate.limits?.tokensPerDay === "number") {
338
+ if (state.dayTokens >= candidate.limits.tokensPerDay) {
339
+ return true;
340
+ }
341
+ }
342
+ if (typeof candidate.limits?.tokensPerWeek === "number") {
343
+ if (state.weekTokens >= candidate.limits.tokensPerWeek) {
344
+ return true;
345
+ }
346
+ }
347
+ return false;
348
+ }
349
+ function failureRatio(state) {
350
+ if (state.attempts <= 0) {
351
+ return 0;
352
+ }
353
+ return state.failures / state.attempts;
354
+ }
355
+ function ewma(previous, next, alpha = 0.2) {
356
+ if (previous === undefined) {
357
+ return next;
358
+ }
359
+ return alpha * next + (1 - alpha) * previous;
360
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dashscopeProtocolAdapter = void 0;
4
+ const ALL_OPERATIONS = [
5
+ "images_generation",
6
+ "images_edits",
7
+ "video_generations",
8
+ ];
9
+ const STREAM_OPERATIONS = [];
10
+ const DASHSCOPE_IMAGE_GEN_PATH = "/api/v1/services/aigc/image-generation/generation";
11
+ const DASHSCOPE_VIDEO_GEN_PATH = "/api/v1/services/aigc/video-generation/video-synthesis";
12
+ const DASHSCOPE_TASK_QUERY_PATH = "/api/v1/tasks";
13
+ const DASHSCOPE_MULTIMODAL_GEN_PATH = "/api/v1/services/aigc/multimodal-generation/generation";
14
+ const TASK_POLL_INTERVAL_MS = 5000;
15
+ const TASK_POLL_TIMEOUT_MS = 300000;
16
+ exports.dashscopeProtocolAdapter = {
17
+ id: "dashscope",
18
+ supportedOperations: [...ALL_OPERATIONS],
19
+ streamSupportedOperations: [...STREAM_OPERATIONS],
20
+ supports(context) {
21
+ if (!ALL_OPERATIONS.includes(context.operation)) {
22
+ return { supported: false, reason: "unsupported_operation" };
23
+ }
24
+ if (context.stream) {
25
+ return { supported: false, reason: "stream_unsupported" };
26
+ }
27
+ return { supported: true };
28
+ },
29
+ async buildRequest(context) {
30
+ const { operation, payload, upstreamModel } = context;
31
+ if (operation === "images_generation" || operation === "images_edits") {
32
+ return buildImageRequest(payload, upstreamModel);
33
+ }
34
+ if (operation === "video_generations") {
35
+ return buildVideoRequest(payload, upstreamModel);
36
+ }
37
+ throw new Error(`Unsupported operation for dashscope: ${operation}`);
38
+ },
39
+ async normalizeResponse(context) {
40
+ const { operation, upstreamResult } = context;
41
+ if (operation === "images_generation" || operation === "images_edits") {
42
+ return normalizeImageResponse(upstreamResult);
43
+ }
44
+ if (operation === "video_generations") {
45
+ return normalizeVideoResponse(upstreamResult);
46
+ }
47
+ throw new Error(`Unsupported operation for dashscope: ${operation}`);
48
+ },
49
+ };
50
+ function buildImageRequest(payload, model) {
51
+ const prompt = payload.prompt ?? "";
52
+ const negativePrompt = payload.negative_prompt ?? "";
53
+ const imageUrl = payload.image_url;
54
+ const n = payload.n ?? 1;
55
+ const size = payload.size ?? "1K";
56
+ const seed = payload.seed;
57
+ const watermark = payload.watermark ?? false;
58
+ const promptExtend = payload.prompt_extend ?? true;
59
+ const content = [];
60
+ if (prompt) {
61
+ content.push({ text: prompt });
62
+ }
63
+ if (imageUrl) {
64
+ content.push({ image: imageUrl });
65
+ }
66
+ const body = {
67
+ model,
68
+ input: {
69
+ messages: [
70
+ {
71
+ role: "user",
72
+ content,
73
+ },
74
+ ],
75
+ },
76
+ parameters: {
77
+ n,
78
+ size,
79
+ watermark,
80
+ prompt_extend: promptExtend,
81
+ ...(negativePrompt ? { negative_prompt: negativePrompt } : {}),
82
+ ...(seed !== undefined ? { seed } : {}),
83
+ },
84
+ };
85
+ return {
86
+ path: DASHSCOPE_IMAGE_GEN_PATH,
87
+ payload: body,
88
+ headers: {
89
+ "X-DashScope-Async": "enable",
90
+ },
91
+ };
92
+ }
93
+ function buildVideoRequest(payload, model) {
94
+ const prompt = payload.prompt ?? "";
95
+ const negativePrompt = payload.negative_prompt ?? "";
96
+ const imageUrl = payload.image_url;
97
+ const audioUrl = payload.audio_url;
98
+ const duration = payload.duration ?? 5;
99
+ const resolution = payload.resolution ?? "720P";
100
+ const seed = payload.seed;
101
+ const watermark = payload.watermark ?? false;
102
+ const promptExtend = payload.prompt_extend ?? true;
103
+ const input = {
104
+ prompt,
105
+ };
106
+ if (imageUrl) {
107
+ input.img_url = imageUrl;
108
+ }
109
+ if (audioUrl) {
110
+ input.audio_url = audioUrl;
111
+ }
112
+ if (negativePrompt) {
113
+ input.negative_prompt = negativePrompt;
114
+ }
115
+ const body = {
116
+ model,
117
+ input,
118
+ parameters: {
119
+ resolution,
120
+ duration,
121
+ prompt_extend: promptExtend,
122
+ watermark,
123
+ ...(seed !== undefined ? { seed } : {}),
124
+ },
125
+ };
126
+ return {
127
+ path: DASHSCOPE_VIDEO_GEN_PATH,
128
+ payload: body,
129
+ headers: {
130
+ "X-DashScope-Async": "enable",
131
+ },
132
+ };
133
+ }
134
+ async function normalizeImageResponse(upstreamResult) {
135
+ const chunks = [];
136
+ for await (const chunk of upstreamResult.body) {
137
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
138
+ }
139
+ const buffer = Buffer.concat(chunks);
140
+ const body = JSON.parse(buffer.toString("utf8"));
141
+ if (body.code || body.output?.task_status === "FAILED") {
142
+ throw new Error(`DashScope task failed: ${body.message ?? body.output?.message ?? "Unknown error"}`);
143
+ }
144
+ const taskId = body.output?.task_id;
145
+ if (!taskId) {
146
+ throw new Error("No task_id in DashScope response");
147
+ }
148
+ const taskResult = await pollForTaskCompletion(taskId, upstreamResult.headers);
149
+ const output = taskResult.output;
150
+ const choices = output?.choices ?? [];
151
+ const data = [];
152
+ for (const choice of choices) {
153
+ const messageContent = choice.message?.content ?? [];
154
+ for (const item of messageContent) {
155
+ if (item.type === "image" && item.image) {
156
+ data.push({ url: item.image });
157
+ }
158
+ }
159
+ }
160
+ const usage = (taskResult.usage ?? {});
161
+ const normalizedBody = {
162
+ created: Math.floor(Date.now() / 1000),
163
+ data,
164
+ usage: {
165
+ image_count: usage.image_count ?? data.length,
166
+ size: usage.size ?? "",
167
+ },
168
+ dashscope_request_id: taskResult.request_id,
169
+ };
170
+ const normalizedBuffer = Buffer.from(JSON.stringify(normalizedBody));
171
+ return {
172
+ statusCode: 200,
173
+ headers: upstreamResult.headers,
174
+ body: createReadStream(normalizedBuffer),
175
+ };
176
+ }
177
+ async function normalizeVideoResponse(upstreamResult) {
178
+ const chunks = [];
179
+ for await (const chunk of upstreamResult.body) {
180
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
181
+ }
182
+ const buffer = Buffer.concat(chunks);
183
+ const body = JSON.parse(buffer.toString("utf8"));
184
+ if (body.code || body.output?.task_status === "FAILED") {
185
+ throw new Error(`DashScope task failed: ${body.message ?? body.output?.message ?? "Unknown error"}`);
186
+ }
187
+ const taskId = body.output?.task_id;
188
+ if (!taskId) {
189
+ throw new Error("No task_id in DashScope response");
190
+ }
191
+ const taskResult = await pollForTaskCompletion(taskId, upstreamResult.headers);
192
+ const output = taskResult.output;
193
+ if (output?.task_status !== "SUCCEEDED") {
194
+ throw new Error(`DashScope task did not succeed: ${output?.task_status}`);
195
+ }
196
+ const videoUrl = output?.video_url;
197
+ const data = [];
198
+ if (videoUrl) {
199
+ data.push({ url: videoUrl, revised_prompt: output?.orig_prompt });
200
+ }
201
+ const usage = (taskResult.usage ?? {});
202
+ const normalizedBody = {
203
+ created: Math.floor(Date.now() / 1000),
204
+ data,
205
+ usage: {
206
+ video_count: usage.video_count ?? 1,
207
+ duration: usage.duration ?? 0,
208
+ resolution: usage.SR ?? "",
209
+ },
210
+ dashscope_request_id: taskResult.request_id,
211
+ };
212
+ const normalizedBuffer = Buffer.from(JSON.stringify(normalizedBody));
213
+ return {
214
+ statusCode: 200,
215
+ headers: upstreamResult.headers,
216
+ body: createReadStream(normalizedBuffer),
217
+ };
218
+ }
219
+ async function pollForTaskCompletion(taskId, headers) {
220
+ const apiKey = extractApiKey(headers);
221
+ if (!apiKey) {
222
+ throw new Error("No API key available for task polling");
223
+ }
224
+ const baseUrl = extractBaseUrl(headers);
225
+ const startTime = Date.now();
226
+ while (Date.now() - startTime < TASK_POLL_TIMEOUT_MS) {
227
+ await sleep(TASK_POLL_INTERVAL_MS);
228
+ const queryUrl = `${baseUrl}${DASHSCOPE_TASK_QUERY_PATH}/${taskId}`;
229
+ const response = await fetch(queryUrl, {
230
+ method: "GET",
231
+ headers: {
232
+ Authorization: `Bearer ${apiKey}`,
233
+ },
234
+ });
235
+ if (!response.ok) {
236
+ const errorText = await response.text();
237
+ throw new Error(`Task query failed: ${response.status} ${errorText}`);
238
+ }
239
+ const result = (await response.json());
240
+ const taskStatus = result?.output?.task_status;
241
+ if (taskStatus === "SUCCEEDED" || taskStatus === "FAILED") {
242
+ return result;
243
+ }
244
+ }
245
+ throw new Error(`Task polling timed out after ${TASK_POLL_TIMEOUT_MS}ms`);
246
+ }
247
+ function extractApiKey(headers) {
248
+ const auth = headers["authorization"] ?? headers["Authorization"];
249
+ if (typeof auth === "string" && auth.startsWith("Bearer ")) {
250
+ return auth.slice(7);
251
+ }
252
+ return null;
253
+ }
254
+ function extractBaseUrl(headers) {
255
+ const xBaseUrl = headers["x-dashscope-base-url"] ?? headers["X-DashScope-Base-Url"];
256
+ if (typeof xBaseUrl === "string") {
257
+ return xBaseUrl;
258
+ }
259
+ return "https://dashscope-intl.aliyuncs.com";
260
+ }
261
+ function sleep(ms) {
262
+ return new Promise((resolve) => setTimeout(resolve, ms));
263
+ }
264
+ function createReadStream(buffer) {
265
+ const { Readable } = require("stream");
266
+ return Readable.from(buffer);
267
+ }