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.
- package/.github/instructions/ui.instructions.md +42 -0
- package/.github/workflows/ci.yml +35 -0
- package/.github/workflows/publish.yml +71 -0
- package/.github/workflows/release.yml +48 -0
- package/.playwright-mcp/console-2026-04-04T01-41-10-746Z.log +2 -0
- package/.playwright-mcp/console-2026-04-04T01-41-28-799Z.log +3 -0
- package/.playwright-mcp/console-2026-04-05T02-26-51-909Z.log +76 -0
- package/.playwright-mcp/page-2026-04-04T01-41-10-816Z.yml +1 -0
- package/.playwright-mcp/page-2026-04-04T01-41-29-141Z.yml +77 -0
- package/.playwright-mcp/page-2026-04-04T01-41-42-633Z.yml +190 -0
- package/.playwright-mcp/page-2026-04-04T01-42-03-929Z.yml +262 -0
- package/.playwright-mcp/page-2026-04-04T02-12-54-813Z.yml +6 -0
- package/.playwright-mcp/page-2026-04-04T02-14-58-600Z.yml +190 -0
- package/.playwright-mcp/page-2026-04-04T02-15-03-923Z.yml +190 -0
- package/.playwright-mcp/page-2026-04-04T02-15-07-426Z.yml +190 -0
- package/.playwright-mcp/page-2026-04-04T02-15-25-729Z.yml +262 -0
- package/.playwright-mcp/page-2026-04-04T02-16-22-984Z.yml +262 -0
- package/.playwright-mcp/page-2026-04-04T02-17-00-599Z.yml +190 -0
- package/.playwright-mcp/page-2026-04-04T02-17-50-874Z.yml +190 -0
- package/.playwright-mcp/page-2026-04-05T02-26-55-570Z.yml +6 -0
- package/AGENTS.md +48 -0
- package/CHANGELOG.md +131 -0
- package/README.md +552 -0
- package/assets/agent-mode.png +0 -0
- package/assets/categorize.png +0 -0
- package/assets/dashboard.png +0 -0
- package/assets/endpoint-proxy.png +0 -0
- package/assets/icon.png +0 -0
- package/assets/mcp-generate-image.png +0 -0
- package/assets/mcp-understand-image.png +0 -0
- package/assets/peek-token-flow.png +0 -0
- package/assets/playground.png +0 -0
- package/assets/sankey.png +0 -0
- package/cli/index.ts +2805 -0
- package/cli/legacyRewrite.ts +108 -0
- package/cli/modelRef.ts +24 -0
- package/dist/cli/index.js +2536 -0
- package/dist/cli/legacyRewrite.js +92 -0
- package/dist/cli/modelRef.js +20 -0
- package/dist/src/benchmark/artifacts.js +131 -0
- package/dist/src/benchmark/capabilityClassifier.js +81 -0
- package/dist/src/benchmark/capabilityStore.js +144 -0
- package/dist/src/benchmark/config.js +238 -0
- package/dist/src/benchmark/gates.js +118 -0
- package/dist/src/benchmark/jobs.js +252 -0
- package/dist/src/benchmark/runner.js +1847 -0
- package/dist/src/benchmark/schema.js +353 -0
- package/dist/src/benchmark/suites.js +314 -0
- package/dist/src/benchmark/tinyQaDataset.js +422 -0
- package/dist/src/benchmark/types.js +25 -0
- package/dist/src/config.js +47 -0
- package/dist/src/index.js +178 -0
- package/dist/src/mcp/client.js +215 -0
- package/dist/src/mcp/discovery.js +226 -0
- package/dist/src/mcp/policy.js +65 -0
- package/dist/src/mcp/registry.js +129 -0
- package/dist/src/mcp/service.js +460 -0
- package/dist/src/middleware/auth.js +179 -0
- package/dist/src/middleware/requestCapture.js +192 -0
- package/dist/src/middleware/requestStats.js +118 -0
- package/dist/src/pools/builder.js +132 -0
- package/dist/src/pools/repository.js +69 -0
- package/dist/src/pools/scheduler.js +360 -0
- package/dist/src/pools/types.js +2 -0
- package/dist/src/protocols/adapters/dashscope.js +267 -0
- package/dist/src/protocols/adapters/inferenceV2.js +346 -0
- package/dist/src/protocols/adapters/openai.js +27 -0
- package/dist/src/protocols/registry.js +99 -0
- package/dist/src/protocols/types.js +2 -0
- package/dist/src/providers/health.js +153 -0
- package/dist/src/providers/importer.js +289 -0
- package/dist/src/providers/modelRegistry.js +313 -0
- package/dist/src/providers/repository.js +361 -0
- package/dist/src/providers/types.js +2 -0
- package/dist/src/routes/admin.js +531 -0
- package/dist/src/routes/audio.js +295 -0
- package/dist/src/routes/chat.js +240 -0
- package/dist/src/routes/embeddings.js +157 -0
- package/dist/src/routes/images.js +288 -0
- package/dist/src/routes/mcp.js +256 -0
- package/dist/src/routes/mcpService.js +100 -0
- package/dist/src/routes/models.js +48 -0
- package/dist/src/routes/responses.js +711 -0
- package/dist/src/routes/sessions.js +450 -0
- package/dist/src/routes/stats.js +270 -0
- package/dist/src/routes/ui.js +97 -0
- package/dist/src/routes/videos.js +107 -0
- package/dist/src/routing/router.js +338 -0
- package/dist/src/services/imageGeneration.js +280 -0
- package/dist/src/services/imageUnderstanding.js +352 -0
- package/dist/src/services/videoGeneration.js +79 -0
- package/dist/src/storage/captureRepository.js +1591 -0
- package/dist/src/storage/files.js +157 -0
- package/dist/src/storage/imageCache.js +346 -0
- package/dist/src/storage/repositories.js +388 -0
- package/dist/src/storage/sessionRepository.js +370 -0
- package/dist/src/storage/statsRepository.js +204 -0
- package/dist/src/transport/httpClient.js +126 -0
- package/dist/src/types.js +2 -0
- package/dist/src/utils/messageMedia.js +285 -0
- package/dist/src/utils/modelCapabilities.js +108 -0
- package/dist/src/utils/modelDiscovery.js +170 -0
- package/dist/src/version.js +5 -0
- package/dist/src/workers/captureRetention.js +25 -0
- package/dist/src/workers/configWatcher.js +91 -0
- package/dist/src/workers/healthChecker.js +21 -0
- package/dist/src/workers/statsRotation.js +41 -0
- package/docs/LLM/output_schema.md +312 -0
- package/docs/benchmark.md +208 -0
- package/docs/mcp-guidelines.md +125 -0
- package/docs/mcp-service.md +178 -0
- package/docs/opencode.md +86 -0
- package/docs/providers.md +79 -0
- package/examples/benchmark.config.yaml +28 -0
- package/examples/providers/alibaba-dashscope.yaml +88 -0
- package/examples/providers/alibaba-llm.yaml +64 -0
- package/examples/providers/alibaba-registry.yaml +7 -0
- package/examples/providers/inference-v2-ray.yaml +29 -0
- package/examples/scenarios/assets/omni-call-sample.wav +0 -0
- package/examples/scenarios/custom.jsonl +5 -0
- package/examples/scenarios/custom.yaml +40 -0
- package/model-form-v2.png +0 -0
- package/package.json +66 -0
- package/provider-form-v2.png +0 -0
- package/provider-form.png +0 -0
- package/scripts/manual-test.sh +11 -0
- package/scripts/version-from-git.js +23 -0
- package/src/benchmark/artifacts.ts +149 -0
- package/src/benchmark/capabilityClassifier.ts +99 -0
- package/src/benchmark/capabilityStore.ts +174 -0
- package/src/benchmark/config.ts +337 -0
- package/src/benchmark/gates.ts +164 -0
- package/src/benchmark/jobs.ts +312 -0
- package/src/benchmark/runner.ts +2519 -0
- package/src/benchmark/schema.ts +443 -0
- package/src/benchmark/suites.ts +323 -0
- package/src/benchmark/tinyQaDataset.ts +428 -0
- package/src/benchmark/types.ts +442 -0
- package/src/config.ts +44 -0
- package/src/index.ts +195 -0
- package/src/mcp/client.ts +305 -0
- package/src/mcp/discovery.ts +266 -0
- package/src/mcp/policy.ts +105 -0
- package/src/mcp/registry.ts +164 -0
- package/src/mcp/service.ts +611 -0
- package/src/middleware/auth.ts +251 -0
- package/src/middleware/requestCapture.ts +245 -0
- package/src/middleware/requestStats.ts +163 -0
- package/src/pools/builder.ts +159 -0
- package/src/pools/repository.ts +71 -0
- package/src/pools/scheduler.ts +425 -0
- package/src/pools/types.ts +117 -0
- package/src/protocols/adapters/dashscope.ts +335 -0
- package/src/protocols/adapters/inferenceV2.ts +428 -0
- package/src/protocols/adapters/openai.ts +32 -0
- package/src/protocols/registry.ts +117 -0
- package/src/protocols/types.ts +81 -0
- package/src/providers/health.ts +207 -0
- package/src/providers/importer.ts +402 -0
- package/src/providers/modelRegistry.ts +415 -0
- package/src/providers/repository.ts +439 -0
- package/src/providers/types.ts +113 -0
- package/src/routes/admin.ts +666 -0
- package/src/routes/audio.ts +372 -0
- package/src/routes/chat.ts +301 -0
- package/src/routes/embeddings.ts +197 -0
- package/src/routes/images.ts +356 -0
- package/src/routes/mcp.ts +320 -0
- package/src/routes/mcpService.ts +114 -0
- package/src/routes/models.ts +50 -0
- package/src/routes/responses.ts +872 -0
- package/src/routes/sessions.ts +558 -0
- package/src/routes/stats.ts +312 -0
- package/src/routes/ui.ts +96 -0
- package/src/routes/videos.ts +132 -0
- package/src/routing/router.ts +501 -0
- package/src/services/imageGeneration.ts +396 -0
- package/src/services/imageUnderstanding.ts +449 -0
- package/src/services/videoGeneration.ts +127 -0
- package/src/storage/captureRepository.ts +1835 -0
- package/src/storage/files.ts +178 -0
- package/src/storage/imageCache.ts +405 -0
- package/src/storage/repositories.ts +494 -0
- package/src/storage/sessionRepository.ts +419 -0
- package/src/storage/statsRepository.ts +238 -0
- package/src/transport/httpClient.ts +145 -0
- package/src/types.ts +322 -0
- package/src/utils/messageMedia.ts +293 -0
- package/src/utils/modelCapabilities.ts +161 -0
- package/src/utils/modelDiscovery.ts +203 -0
- package/src/workers/captureRetention.ts +25 -0
- package/src/workers/configWatcher.ts +115 -0
- package/src/workers/healthChecker.ts +22 -0
- package/src/workers/statsRotation.ts +49 -0
- package/tests/benchmarkAdminRoutes.test.ts +82 -0
- package/tests/benchmarkBasics.test.ts +116 -0
- package/tests/captureAdminRoutes.test.ts +420 -0
- package/tests/captureRepository.test.ts +797 -0
- package/tests/cliLegacyRewrite.test.ts +45 -0
- package/tests/imageGeneration.service.test.ts +107 -0
- package/tests/imageUnderstanding.service.test.ts +123 -0
- package/tests/mcpPolicy.test.ts +105 -0
- package/tests/mcpService.test.ts +1245 -0
- package/tests/modelRef.test.ts +23 -0
- package/tests/modelsRoutes.test.ts +154 -0
- package/tests/sessionMediaCache.test.ts +167 -0
- package/tests/statsRoutes.test.ts +323 -0
- package/tsconfig.json +15 -0
- package/ui/index.html +16 -0
- package/ui/package-lock.json +8521 -0
- package/ui/package.json +52 -0
- package/ui/postcss.config.js +6 -0
- package/ui/public/assets/apple-touch-icon.png +0 -0
- package/ui/public/assets/favicon-16.png +0 -0
- package/ui/public/assets/favicon-32.png +0 -0
- package/ui/public/assets/icon-192.png +0 -0
- package/ui/public/assets/icon-512.png +0 -0
- package/ui/src/App.tsx +27 -0
- package/ui/src/api/client.ts +1503 -0
- package/ui/src/components/EndpointUsageGuide.tsx +361 -0
- package/ui/src/components/Layout.tsx +124 -0
- package/ui/src/components/MessageContent.tsx +365 -0
- package/ui/src/components/ToolCallMessage.tsx +179 -0
- package/ui/src/components/ToolPicker.tsx +442 -0
- package/ui/src/components/messageContentParser.test.ts +41 -0
- package/ui/src/components/messageContentParser.ts +73 -0
- package/ui/src/components/thinkingPreview.test.ts +27 -0
- package/ui/src/components/thinkingPreview.ts +15 -0
- package/ui/src/components/toMermaidSankey.test.ts +78 -0
- package/ui/src/components/toMermaidSankey.ts +56 -0
- package/ui/src/components/ui/button.tsx +58 -0
- package/ui/src/components/ui/input.tsx +21 -0
- package/ui/src/components/ui/textarea.tsx +21 -0
- package/ui/src/lib/utils.ts +6 -0
- package/ui/src/main.tsx +9 -0
- package/ui/src/pages/AgentPlayground.tsx +2010 -0
- package/ui/src/pages/Benchmark.tsx +988 -0
- package/ui/src/pages/Dashboard.tsx +581 -0
- package/ui/src/pages/Peek.tsx +962 -0
- package/ui/src/pages/Settings.tsx +2013 -0
- package/ui/src/pages/agentPlaygroundPayload.test.ts +109 -0
- package/ui/src/pages/agentPlaygroundPayload.ts +97 -0
- package/ui/src/pages/agentThinkingContent.test.ts +50 -0
- package/ui/src/pages/agentThinkingContent.ts +57 -0
- package/ui/src/pages/dashboardTokenUsage.test.ts +66 -0
- package/ui/src/pages/dashboardTokenUsage.ts +36 -0
- package/ui/src/pages/imageUpload.test.ts +39 -0
- package/ui/src/pages/imageUpload.ts +71 -0
- package/ui/src/pages/peekFilters.test.ts +29 -0
- package/ui/src/pages/peekFilters.ts +13 -0
- package/ui/src/pages/peekMedia.test.ts +58 -0
- package/ui/src/pages/peekMedia.ts +148 -0
- package/ui/src/pages/sessionAutoTitle.test.ts +128 -0
- package/ui/src/pages/sessionAutoTitle.ts +106 -0
- package/ui/src/stores/settings.ts +58 -0
- package/ui/src/styles/globals.css +223 -0
- package/ui/src/vite-env.d.ts +8 -0
- package/ui/tailwind.config.js +106 -0
- package/ui/tsconfig.json +32 -0
- package/ui/vite.config.ts +37 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { watch, FSWatcher } from "fs";
|
|
2
|
+
import { EventEmitter } from "events";
|
|
3
|
+
import { StoragePaths } from "../storage/files";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Config Watcher
|
|
7
|
+
*
|
|
8
|
+
* Watches config.yaml for changes and emits events when the file is modified.
|
|
9
|
+
* Uses debouncing to handle rapid successive changes (common with text editors).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export interface ConfigWatcher extends EventEmitter {
|
|
13
|
+
on(event: "config:updated", listener: () => void): this;
|
|
14
|
+
on(event: "error", listener: (error: Error) => void): this;
|
|
15
|
+
emit(event: "config:updated"): boolean;
|
|
16
|
+
emit(event: "error", error: Error): boolean;
|
|
17
|
+
stop(): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const DEBOUNCE_MS = 500;
|
|
21
|
+
|
|
22
|
+
class ConfigWatcherImpl extends EventEmitter implements ConfigWatcher {
|
|
23
|
+
private watcher: FSWatcher | null = null;
|
|
24
|
+
private debounceTimer: NodeJS.Timeout | null = null;
|
|
25
|
+
private isWatching = false;
|
|
26
|
+
private configPath: string;
|
|
27
|
+
|
|
28
|
+
constructor(configPath: string) {
|
|
29
|
+
super();
|
|
30
|
+
this.configPath = configPath;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
start(): void {
|
|
34
|
+
if (this.isWatching) return;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
this.watcher = watch(this.configPath, (eventType) => {
|
|
38
|
+
// Handle both 'change' and 'rename' events
|
|
39
|
+
// Some editors do atomic saves (write temp → rename)
|
|
40
|
+
if (eventType === "change" || eventType === "rename") {
|
|
41
|
+
this.debouncedEmit();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.watcher.on("error", (error) => {
|
|
46
|
+
this.emit("error", error);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
this.isWatching = true;
|
|
50
|
+
console.log("[config-watcher] Watching for config changes");
|
|
51
|
+
} catch (error) {
|
|
52
|
+
// File might not exist yet, that's okay
|
|
53
|
+
console.log("[config-watcher] Config file not found, will retry on next access");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private debouncedEmit(): void {
|
|
58
|
+
if (this.debounceTimer) {
|
|
59
|
+
clearTimeout(this.debounceTimer);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
this.debounceTimer = setTimeout(() => {
|
|
63
|
+
console.log("[config-watcher] Config file changed, triggering reload");
|
|
64
|
+
this.emit("config:updated");
|
|
65
|
+
this.debounceTimer = null;
|
|
66
|
+
}, DEBOUNCE_MS);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
stop(): void {
|
|
70
|
+
if (this.debounceTimer) {
|
|
71
|
+
clearTimeout(this.debounceTimer);
|
|
72
|
+
this.debounceTimer = null;
|
|
73
|
+
}
|
|
74
|
+
if (this.watcher) {
|
|
75
|
+
this.watcher.close();
|
|
76
|
+
this.watcher = null;
|
|
77
|
+
}
|
|
78
|
+
this.isWatching = false;
|
|
79
|
+
console.log("[config-watcher] Stopped watching");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let globalWatcher: ConfigWatcher | null = null;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Start watching the config file for changes.
|
|
87
|
+
* Returns an EventEmitter that emits "config:updated" when the file changes.
|
|
88
|
+
*/
|
|
89
|
+
export function startConfigWatcher(paths: StoragePaths): ConfigWatcher {
|
|
90
|
+
if (globalWatcher) {
|
|
91
|
+
return globalWatcher;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const watcher = new ConfigWatcherImpl(paths.configPath);
|
|
95
|
+
watcher.start();
|
|
96
|
+
globalWatcher = watcher;
|
|
97
|
+
return watcher;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Stop the config watcher.
|
|
102
|
+
*/
|
|
103
|
+
export function stopConfigWatcher(): void {
|
|
104
|
+
if (globalWatcher) {
|
|
105
|
+
globalWatcher.stop();
|
|
106
|
+
globalWatcher = null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get the current config watcher instance (if running).
|
|
112
|
+
*/
|
|
113
|
+
export function getConfigWatcher(): ConfigWatcher | null {
|
|
114
|
+
return globalWatcher;
|
|
115
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { StoragePaths } from "../storage/files";
|
|
2
|
+
import { probeProviderModels } from "../providers/health";
|
|
3
|
+
|
|
4
|
+
let healthTimer: NodeJS.Timeout | null = null;
|
|
5
|
+
|
|
6
|
+
export function startHealthChecker(paths: StoragePaths): void {
|
|
7
|
+
const intervalMs = 30_000;
|
|
8
|
+
const run = async () => {
|
|
9
|
+
await probeProviderModels(paths);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
healthTimer = setInterval(run, intervalMs);
|
|
13
|
+
healthTimer.unref();
|
|
14
|
+
void run();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function stopHealthChecker(): void {
|
|
18
|
+
if (healthTimer) {
|
|
19
|
+
clearInterval(healthTimer);
|
|
20
|
+
healthTimer = null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { rotateStats } from "../storage/statsRepository";
|
|
2
|
+
import { StoragePaths } from "../storage/files";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Stats Rotation Worker
|
|
6
|
+
*
|
|
7
|
+
* Periodically cleans up stats files older than retention period.
|
|
8
|
+
* Runs daily by default.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const ROTATION_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
12
|
+
const DEFAULT_RETENTION_DAYS = 30;
|
|
13
|
+
|
|
14
|
+
let rotationTimer: NodeJS.Timeout | null = null;
|
|
15
|
+
|
|
16
|
+
export function startStatsRotation(
|
|
17
|
+
paths: StoragePaths,
|
|
18
|
+
retentionDays: number = DEFAULT_RETENTION_DAYS
|
|
19
|
+
): void {
|
|
20
|
+
if (rotationTimer) {
|
|
21
|
+
return; // Already running
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function runRotation(): Promise<void> {
|
|
25
|
+
try {
|
|
26
|
+
const deleted = await rotateStats(paths, retentionDays);
|
|
27
|
+
if (deleted > 0) {
|
|
28
|
+
console.log(`[stats-rotation] Deleted ${deleted} stats file(s) older than ${retentionDays} days`);
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error("[stats-rotation] Error rotating stats:", error);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Run immediately on startup, then periodically
|
|
36
|
+
runRotation();
|
|
37
|
+
|
|
38
|
+
rotationTimer = setInterval(runRotation, ROTATION_INTERVAL_MS);
|
|
39
|
+
|
|
40
|
+
// Prevent timer from keeping process alive
|
|
41
|
+
rotationTimer.unref();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function stopStatsRotation(): void {
|
|
45
|
+
if (rotationTimer) {
|
|
46
|
+
clearInterval(rotationTimer);
|
|
47
|
+
rotationTimer = null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { promises as fs } from "fs";
|
|
5
|
+
import Fastify from "fastify";
|
|
6
|
+
import { registerAdminRoutes } from "../src/routes/admin";
|
|
7
|
+
import type { StoragePaths } from "../src/storage/files";
|
|
8
|
+
|
|
9
|
+
function makePaths(baseDir: string): StoragePaths {
|
|
10
|
+
return {
|
|
11
|
+
baseDir,
|
|
12
|
+
configPath: path.join(baseDir, "config.yaml"),
|
|
13
|
+
healthPath: path.join(baseDir, "health.json"),
|
|
14
|
+
providerHealthPath: path.join(baseDir, "providers_health.json"),
|
|
15
|
+
requestLogPath: path.join(baseDir, "request_logs.jsonl"),
|
|
16
|
+
providersPath: path.join(baseDir, "providers.json"),
|
|
17
|
+
poolsPath: path.join(baseDir, "pools.json"),
|
|
18
|
+
poolStatePath: path.join(baseDir, "pool_state.json"),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function makeWorkspaceTempDir(prefix: string): Promise<string> {
|
|
23
|
+
const base = path.join(process.cwd(), "tmp");
|
|
24
|
+
await fs.mkdir(base, { recursive: true });
|
|
25
|
+
return fs.mkdtemp(path.join(base, prefix));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
test("/admin/benchmarks/runs forwards execution + tuning fields", async () => {
|
|
29
|
+
const baseDir = await makeWorkspaceTempDir("waypoi-benchmark-admin-test-");
|
|
30
|
+
const paths = makePaths(baseDir);
|
|
31
|
+
const app = Fastify();
|
|
32
|
+
await registerAdminRoutes(app, paths, { adminToken: "test-token", version: "0.0.0" });
|
|
33
|
+
|
|
34
|
+
const res = await app.inject({
|
|
35
|
+
method: "POST",
|
|
36
|
+
url: "/admin/benchmarks/runs",
|
|
37
|
+
headers: {
|
|
38
|
+
authorization: "Bearer test-token",
|
|
39
|
+
"content-type": "application/json",
|
|
40
|
+
},
|
|
41
|
+
payload: {
|
|
42
|
+
suite: "showcase",
|
|
43
|
+
exampleId: "showcase-tinyqa-001",
|
|
44
|
+
executionMode: "showcase",
|
|
45
|
+
profile: "local",
|
|
46
|
+
temperature: 0.25,
|
|
47
|
+
top_p: 0.8,
|
|
48
|
+
max_tokens: 128,
|
|
49
|
+
presence_penalty: 0.3,
|
|
50
|
+
frequency_penalty: -0.2,
|
|
51
|
+
seed: 7,
|
|
52
|
+
stop: ["END", "STOP"],
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
assert.equal(res.statusCode, 202);
|
|
57
|
+
const body = res.json() as {
|
|
58
|
+
request: {
|
|
59
|
+
exampleId?: string;
|
|
60
|
+
executionMode?: string;
|
|
61
|
+
temperature?: number;
|
|
62
|
+
top_p?: number;
|
|
63
|
+
max_tokens?: number;
|
|
64
|
+
presence_penalty?: number;
|
|
65
|
+
frequency_penalty?: number;
|
|
66
|
+
seed?: number;
|
|
67
|
+
stop?: string[];
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
assert.equal(body.request.exampleId, "showcase-tinyqa-001");
|
|
72
|
+
assert.equal(body.request.executionMode, "showcase");
|
|
73
|
+
assert.equal(body.request.temperature, 0.25);
|
|
74
|
+
assert.equal(body.request.top_p, 0.8);
|
|
75
|
+
assert.equal(body.request.max_tokens, 128);
|
|
76
|
+
assert.equal(body.request.presence_penalty, 0.3);
|
|
77
|
+
assert.equal(body.request.frequency_penalty, -0.2);
|
|
78
|
+
assert.equal(body.request.seed, 7);
|
|
79
|
+
assert.deepEqual(body.request.stop, ["END", "STOP"]);
|
|
80
|
+
|
|
81
|
+
await app.close();
|
|
82
|
+
});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import test from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import os from 'os'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import { promises as fs } from 'fs'
|
|
6
|
+
import { resolveBenchmarkConfig } from '../src/benchmark/config'
|
|
7
|
+
import { validateScenarioCollection } from '../src/benchmark/schema'
|
|
8
|
+
import { listBuiltInSuites, listSuiteExamples } from '../src/benchmark/suites'
|
|
9
|
+
import type { StoragePaths } from '../src/storage/files'
|
|
10
|
+
|
|
11
|
+
function makePaths(baseDir: string): StoragePaths {
|
|
12
|
+
return {
|
|
13
|
+
baseDir,
|
|
14
|
+
configPath: path.join(baseDir, 'config.yaml'),
|
|
15
|
+
healthPath: path.join(baseDir, 'health.json'),
|
|
16
|
+
providerHealthPath: path.join(baseDir, 'providers_health.json'),
|
|
17
|
+
requestLogPath: path.join(baseDir, 'request_logs.jsonl'),
|
|
18
|
+
providersPath: path.join(baseDir, 'providers.json'),
|
|
19
|
+
poolsPath: path.join(baseDir, 'pools.json'),
|
|
20
|
+
poolStatePath: path.join(baseDir, 'pool_state.json'),
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
test('benchmark config defaults to showcase execution mode', async () => {
|
|
25
|
+
const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), 'waypoi-bench-config-'))
|
|
26
|
+
const resolved = await resolveBenchmarkConfig(makePaths(baseDir), {})
|
|
27
|
+
assert.equal(resolved.run.suite, 'showcase')
|
|
28
|
+
assert.equal(resolved.run.executionMode, 'showcase')
|
|
29
|
+
assert.equal(resolved.defaults.maxIterations, 6)
|
|
30
|
+
assert.equal(resolved.defaults.top_p, 1)
|
|
31
|
+
assert.equal(resolved.defaults.presence_penalty, 0)
|
|
32
|
+
assert.equal(resolved.defaults.frequency_penalty, 0)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('showcase example catalog is tiny-qa-backed and capability suite has no concurrent probes', () => {
|
|
36
|
+
const suites = listBuiltInSuites()
|
|
37
|
+
assert.ok(suites.includes('showcase'))
|
|
38
|
+
|
|
39
|
+
const showcase = listSuiteExamples('showcase')
|
|
40
|
+
assert.ok(showcase.length >= 50)
|
|
41
|
+
assert.ok(showcase.every((example) => example.exampleSource === 'huggingface'))
|
|
42
|
+
assert.ok(showcase.some((example) => example.id === 'showcase-tinyqa-001'))
|
|
43
|
+
assert.ok(showcase.every((example) => example.mode === 'chat'))
|
|
44
|
+
|
|
45
|
+
const capabilityIds = listSuiteExamples('capabilities').map((example) => example.id)
|
|
46
|
+
assert.ok(!capabilityIds.some((id) => id.includes('concurrent') || id.includes('under_load')))
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('benchmark config accepts run-level generation overrides', async () => {
|
|
50
|
+
const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), 'waypoi-bench-config-'))
|
|
51
|
+
const resolved = await resolveBenchmarkConfig(makePaths(baseDir), {
|
|
52
|
+
temperature: 0.4,
|
|
53
|
+
top_p: 0.8,
|
|
54
|
+
max_tokens: 256,
|
|
55
|
+
presence_penalty: 0.3,
|
|
56
|
+
frequency_penalty: -0.4,
|
|
57
|
+
seed: 9,
|
|
58
|
+
stop: ['END', 'STOP'],
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
assert.equal(resolved.run.temperature, 0.4)
|
|
62
|
+
assert.equal(resolved.run.top_p, 0.8)
|
|
63
|
+
assert.equal(resolved.run.max_tokens, 256)
|
|
64
|
+
assert.equal(resolved.run.presence_penalty, 0.3)
|
|
65
|
+
assert.equal(resolved.run.frequency_penalty, -0.4)
|
|
66
|
+
assert.equal(resolved.run.seed, 9)
|
|
67
|
+
assert.deepEqual(resolved.run.stop, ['END', 'STOP'])
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('scenario validation accepts showcase metadata, responses mode, and tool-name assertions', () => {
|
|
71
|
+
const outcome = validateScenarioCollection(
|
|
72
|
+
[
|
|
73
|
+
{
|
|
74
|
+
id: 'responses-demo',
|
|
75
|
+
mode: 'responses',
|
|
76
|
+
title: 'Responses Demo',
|
|
77
|
+
summary: 'Compatibility example',
|
|
78
|
+
userVisibleGoal: 'Show Responses API behavior',
|
|
79
|
+
exampleSource: 'huggingface',
|
|
80
|
+
inputPreview: 'Say hello from responses.',
|
|
81
|
+
successCriteria: 'Returns HTTP 200',
|
|
82
|
+
expectedHighlights: ['wire request', 'output_text'],
|
|
83
|
+
prompt: 'Say hello from responses.',
|
|
84
|
+
temperature: 0.3,
|
|
85
|
+
top_p: 0.9,
|
|
86
|
+
max_tokens: 128,
|
|
87
|
+
presence_penalty: 0.2,
|
|
88
|
+
frequency_penalty: -0.1,
|
|
89
|
+
seed: 42,
|
|
90
|
+
stop: ['END'],
|
|
91
|
+
assertions: {
|
|
92
|
+
statusCode: 200,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'agent-demo',
|
|
97
|
+
mode: 'agent',
|
|
98
|
+
prompt: 'Use the weather tool.',
|
|
99
|
+
requiresAvailableTools: true,
|
|
100
|
+
assertions: {
|
|
101
|
+
statusCode: 200,
|
|
102
|
+
minToolCalls: 1,
|
|
103
|
+
requiredToolNames: ['weather'],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
'inline'
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
assert.equal(outcome.scenarios.length, 2)
|
|
111
|
+
assert.equal(outcome.scenarios[0].mode, 'responses')
|
|
112
|
+
assert.equal(outcome.scenarios[0].top_p, 0.9)
|
|
113
|
+
assert.deepEqual(outcome.scenarios[0].stop, ['END'])
|
|
114
|
+
assert.deepEqual(outcome.scenarios[1].assertions.requiredToolNames, ['weather'])
|
|
115
|
+
assert.equal(outcome.scenarios[1].requiresAvailableTools, true)
|
|
116
|
+
})
|