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,361 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
ChevronDown,
|
|
4
|
+
ChevronUp,
|
|
5
|
+
Copy,
|
|
6
|
+
Check,
|
|
7
|
+
Terminal,
|
|
8
|
+
Code
|
|
9
|
+
} from 'lucide-react'
|
|
10
|
+
import { Button } from '@/components/ui/button'
|
|
11
|
+
import { cn } from '@/lib/utils'
|
|
12
|
+
import type { EndpointType } from '@/api/client'
|
|
13
|
+
|
|
14
|
+
export interface UsageGuideTarget {
|
|
15
|
+
id: string
|
|
16
|
+
type: EndpointType
|
|
17
|
+
models: Array<{ publicName: string }>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type TabId = 'curl' | 'python' | 'nodejs'
|
|
21
|
+
|
|
22
|
+
interface EndpointUsageGuideProps {
|
|
23
|
+
target: UsageGuideTarget
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function EndpointUsageGuide({ target }: EndpointUsageGuideProps) {
|
|
27
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
28
|
+
const [activeTab, setActiveTab] = useState<TabId>('curl')
|
|
29
|
+
const [copiedId, setCopiedId] = useState<string | null>(null)
|
|
30
|
+
|
|
31
|
+
// Get the base URL from the current window location
|
|
32
|
+
const baseUrl = typeof window !== 'undefined'
|
|
33
|
+
? `${window.location.protocol}//${window.location.host}`
|
|
34
|
+
: 'http://localhost:9469'
|
|
35
|
+
|
|
36
|
+
// Get the first model's public name (most common use case)
|
|
37
|
+
const modelName = target.models[0]?.publicName ?? 'model-name'
|
|
38
|
+
|
|
39
|
+
const copyToClipboard = async (text: string, id: string) => {
|
|
40
|
+
await navigator.clipboard.writeText(text)
|
|
41
|
+
setCopiedId(id)
|
|
42
|
+
setTimeout(() => setCopiedId(null), 2000)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const generateExamples = () => {
|
|
46
|
+
switch (target.type) {
|
|
47
|
+
case 'llm':
|
|
48
|
+
return generateLlmExamples()
|
|
49
|
+
case 'diffusion':
|
|
50
|
+
return generateDiffusionExamples()
|
|
51
|
+
case 'audio':
|
|
52
|
+
return generateAudioExamples()
|
|
53
|
+
case 'embedding':
|
|
54
|
+
return generateEmbeddingExamples()
|
|
55
|
+
default:
|
|
56
|
+
return generateLlmExamples()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const generateLlmExamples = () => ({
|
|
61
|
+
curl: `curl ${baseUrl}/v1/chat/completions \\
|
|
62
|
+
-H "Content-Type: application/json" \\
|
|
63
|
+
-d '{
|
|
64
|
+
"model": "${modelName}",
|
|
65
|
+
"messages": [
|
|
66
|
+
{"role": "user", "content": "Hello, how are you?"}
|
|
67
|
+
],
|
|
68
|
+
"stream": false
|
|
69
|
+
}'`,
|
|
70
|
+
python: `from openai import OpenAI
|
|
71
|
+
|
|
72
|
+
client = OpenAI(
|
|
73
|
+
base_url="${baseUrl}/v1",
|
|
74
|
+
api_key="not-needed" # No API key required for local proxy
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
response = client.chat.completions.create(
|
|
78
|
+
model="${modelName}",
|
|
79
|
+
messages=[
|
|
80
|
+
{"role": "user", "content": "Hello, how are you?"}
|
|
81
|
+
],
|
|
82
|
+
stream=False
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
print(response.choices[0].message.content)`,
|
|
86
|
+
nodejs: `import OpenAI from 'openai';
|
|
87
|
+
|
|
88
|
+
const client = new OpenAI({
|
|
89
|
+
baseURL: '${baseUrl}/v1',
|
|
90
|
+
apiKey: 'not-needed', // No API key required for local proxy
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
async function main() {
|
|
94
|
+
const response = await client.chat.completions.create({
|
|
95
|
+
model: '${modelName}',
|
|
96
|
+
messages: [
|
|
97
|
+
{ role: 'user', content: 'Hello, how are you?' }
|
|
98
|
+
],
|
|
99
|
+
stream: false,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
console.log(response.choices[0].message.content);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
main();`,
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
const generateDiffusionExamples = () => ({
|
|
109
|
+
curl: `curl ${baseUrl}/v1/images/generations \\
|
|
110
|
+
-H "Content-Type: application/json" \\
|
|
111
|
+
-d '{
|
|
112
|
+
"model": "${modelName}",
|
|
113
|
+
"prompt": "A beautiful sunset over mountains",
|
|
114
|
+
"n": 1,
|
|
115
|
+
"size": "1024x1024"
|
|
116
|
+
}'`,
|
|
117
|
+
python: `from openai import OpenAI
|
|
118
|
+
|
|
119
|
+
client = OpenAI(
|
|
120
|
+
base_url="${baseUrl}/v1",
|
|
121
|
+
api_key="not-needed"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
response = client.images.generate(
|
|
125
|
+
model="${modelName}",
|
|
126
|
+
prompt="A beautiful sunset over mountains",
|
|
127
|
+
n=1,
|
|
128
|
+
size="1024x1024"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
print(response.data[0].url)`,
|
|
132
|
+
nodejs: `import OpenAI from 'openai';
|
|
133
|
+
|
|
134
|
+
const client = new OpenAI({
|
|
135
|
+
baseURL: '${baseUrl}/v1',
|
|
136
|
+
apiKey: 'not-needed',
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
async function main() {
|
|
140
|
+
const response = await client.images.generate({
|
|
141
|
+
model: '${modelName}',
|
|
142
|
+
prompt: 'A beautiful sunset over mountains',
|
|
143
|
+
n: 1,
|
|
144
|
+
size: '1024x1024',
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
console.log(response.data[0].url);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
main();`,
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
const generateAudioExamples = () => ({
|
|
154
|
+
curl: `# Text-to-Speech
|
|
155
|
+
curl ${baseUrl}/v1/audio/speech \\
|
|
156
|
+
-H "Content-Type: application/json" \\
|
|
157
|
+
-d '{
|
|
158
|
+
"model": "${modelName}",
|
|
159
|
+
"input": "Hello, this is a test.",
|
|
160
|
+
"voice": "alloy"
|
|
161
|
+
}' \\
|
|
162
|
+
--output speech.mp3
|
|
163
|
+
|
|
164
|
+
# Speech-to-Text
|
|
165
|
+
curl ${baseUrl}/v1/audio/transcriptions \\
|
|
166
|
+
-F "model=${modelName}" \\
|
|
167
|
+
-F "file=@audio.mp3"`,
|
|
168
|
+
python: `from openai import OpenAI
|
|
169
|
+
|
|
170
|
+
client = OpenAI(
|
|
171
|
+
base_url="${baseUrl}/v1",
|
|
172
|
+
api_key="not-needed"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Text-to-Speech
|
|
176
|
+
response = client.audio.speech.create(
|
|
177
|
+
model="${modelName}",
|
|
178
|
+
input="Hello, this is a test.",
|
|
179
|
+
voice="alloy"
|
|
180
|
+
)
|
|
181
|
+
response.stream_to_file("speech.mp3")
|
|
182
|
+
|
|
183
|
+
# Speech-to-Text
|
|
184
|
+
with open("audio.mp3", "rb") as f:
|
|
185
|
+
transcription = client.audio.transcriptions.create(
|
|
186
|
+
model="${modelName}",
|
|
187
|
+
file=f
|
|
188
|
+
)
|
|
189
|
+
print(transcription.text)`,
|
|
190
|
+
nodejs: `import OpenAI from 'openai';
|
|
191
|
+
import fs from 'fs';
|
|
192
|
+
|
|
193
|
+
const client = new OpenAI({
|
|
194
|
+
baseURL: '${baseUrl}/v1',
|
|
195
|
+
apiKey: 'not-needed',
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
async function main() {
|
|
199
|
+
// Text-to-Speech
|
|
200
|
+
const mp3 = await client.audio.speech.create({
|
|
201
|
+
model: '${modelName}',
|
|
202
|
+
input: 'Hello, this is a test.',
|
|
203
|
+
voice: 'alloy',
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const buffer = Buffer.from(await mp3.arrayBuffer());
|
|
207
|
+
await fs.promises.writeFile('speech.mp3', buffer);
|
|
208
|
+
|
|
209
|
+
// Speech-to-Text
|
|
210
|
+
const transcription = await client.audio.transcriptions.create({
|
|
211
|
+
model: '${modelName}',
|
|
212
|
+
file: fs.createReadStream('audio.mp3'),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
console.log(transcription.text);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
main();`,
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
const generateEmbeddingExamples = () => ({
|
|
222
|
+
curl: `curl ${baseUrl}/v1/embeddings \\
|
|
223
|
+
-H "Content-Type: application/json" \\
|
|
224
|
+
-d '{
|
|
225
|
+
"model": "${modelName}",
|
|
226
|
+
"input": "The quick brown fox jumps over the lazy dog"
|
|
227
|
+
}'`,
|
|
228
|
+
python: `from openai import OpenAI
|
|
229
|
+
|
|
230
|
+
client = OpenAI(
|
|
231
|
+
base_url="${baseUrl}/v1",
|
|
232
|
+
api_key="not-needed"
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
response = client.embeddings.create(
|
|
236
|
+
model="${modelName}",
|
|
237
|
+
input="The quick brown fox jumps over the lazy dog"
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
print(f"Embedding dimension: {len(response.data[0].embedding)}")
|
|
241
|
+
print(f"First 5 values: {response.data[0].embedding[:5]}")`,
|
|
242
|
+
nodejs: `import OpenAI from 'openai';
|
|
243
|
+
|
|
244
|
+
const client = new OpenAI({
|
|
245
|
+
baseURL: '${baseUrl}/v1',
|
|
246
|
+
apiKey: 'not-needed',
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
async function main() {
|
|
250
|
+
const response = await client.embeddings.create({
|
|
251
|
+
model: '${modelName}',
|
|
252
|
+
input: 'The quick brown fox jumps over the lazy dog',
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
console.log('Embedding dimension:', response.data[0].embedding.length);
|
|
256
|
+
console.log('First 5 values:', response.data[0].embedding.slice(0, 5));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
main();`,
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
const examples = generateExamples()
|
|
263
|
+
|
|
264
|
+
const tabs: { id: TabId; label: string; icon: React.ElementType }[] = [
|
|
265
|
+
{ id: 'curl', label: 'cURL', icon: Terminal },
|
|
266
|
+
{ id: 'python', label: 'Python', icon: Code },
|
|
267
|
+
{ id: 'nodejs', label: 'Node.js', icon: Code },
|
|
268
|
+
]
|
|
269
|
+
|
|
270
|
+
return (
|
|
271
|
+
<div className="border-t border-border/50">
|
|
272
|
+
<button
|
|
273
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
274
|
+
className="w-full px-4 py-2 flex items-center gap-2 text-xs text-muted-foreground hover:text-foreground hover:bg-secondary/50 transition-colors"
|
|
275
|
+
>
|
|
276
|
+
<Code className="w-3.5 h-3.5" />
|
|
277
|
+
<span>Usage Guide</span>
|
|
278
|
+
<span className="flex-1" />
|
|
279
|
+
{isOpen ? (
|
|
280
|
+
<ChevronUp className="w-3.5 h-3.5" />
|
|
281
|
+
) : (
|
|
282
|
+
<ChevronDown className="w-3.5 h-3.5" />
|
|
283
|
+
)}
|
|
284
|
+
</button>
|
|
285
|
+
|
|
286
|
+
{isOpen && (
|
|
287
|
+
<div className="px-4 pb-4 space-y-3 animate-fade-in">
|
|
288
|
+
{/* Models List */}
|
|
289
|
+
<div className="space-y-1">
|
|
290
|
+
<p className="text-2xs font-mono uppercase text-muted-foreground">Available Models</p>
|
|
291
|
+
<div className="flex flex-wrap gap-1">
|
|
292
|
+
{target.models.map((model, i) => (
|
|
293
|
+
<span
|
|
294
|
+
key={i}
|
|
295
|
+
className="px-2 py-0.5 bg-secondary rounded text-xs font-mono"
|
|
296
|
+
>
|
|
297
|
+
{model.publicName}
|
|
298
|
+
</span>
|
|
299
|
+
))}
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
{/* Tabs */}
|
|
304
|
+
<div className="flex items-center gap-1 bg-secondary/50 rounded-md p-1">
|
|
305
|
+
{tabs.map((tab) => (
|
|
306
|
+
<button
|
|
307
|
+
key={tab.id}
|
|
308
|
+
onClick={() => setActiveTab(tab.id)}
|
|
309
|
+
className={cn(
|
|
310
|
+
'flex items-center gap-1.5 px-3 py-1 text-xs font-mono rounded transition-colors',
|
|
311
|
+
activeTab === tab.id
|
|
312
|
+
? 'bg-background text-foreground shadow-sm'
|
|
313
|
+
: 'text-muted-foreground hover:text-foreground'
|
|
314
|
+
)}
|
|
315
|
+
>
|
|
316
|
+
<tab.icon className="w-3 h-3" />
|
|
317
|
+
{tab.label}
|
|
318
|
+
</button>
|
|
319
|
+
))}
|
|
320
|
+
</div>
|
|
321
|
+
|
|
322
|
+
{/* Code Block */}
|
|
323
|
+
<div className="relative group">
|
|
324
|
+
<pre className="bg-zinc-950 text-zinc-100 rounded-lg p-4 text-xs font-mono overflow-x-auto leading-relaxed">
|
|
325
|
+
<code>{examples[activeTab]}</code>
|
|
326
|
+
</pre>
|
|
327
|
+
<Button
|
|
328
|
+
variant="ghost"
|
|
329
|
+
size="icon"
|
|
330
|
+
className="absolute top-2 right-2 h-7 w-7 opacity-0 group-hover:opacity-100 transition-opacity bg-zinc-800 hover:bg-zinc-700"
|
|
331
|
+
onClick={() => copyToClipboard(examples[activeTab], `${target.id}-${activeTab}`)}
|
|
332
|
+
>
|
|
333
|
+
{copiedId === `${target.id}-${activeTab}` ? (
|
|
334
|
+
<Check className="w-3.5 h-3.5 text-green-400" />
|
|
335
|
+
) : (
|
|
336
|
+
<Copy className="w-3.5 h-3.5 text-zinc-400" />
|
|
337
|
+
)}
|
|
338
|
+
</Button>
|
|
339
|
+
</div>
|
|
340
|
+
|
|
341
|
+
{/* Endpoint Info */}
|
|
342
|
+
<div className="text-2xs text-muted-foreground space-y-0.5">
|
|
343
|
+
<p>
|
|
344
|
+
<span className="font-medium">Base URL:</span>{' '}
|
|
345
|
+
<code className="bg-secondary px-1 py-0.5 rounded">{baseUrl}/v1</code>
|
|
346
|
+
</p>
|
|
347
|
+
<p>
|
|
348
|
+
<span className="font-medium">Endpoint Type:</span>{' '}
|
|
349
|
+
<span className="uppercase">{target.type}</span>
|
|
350
|
+
</p>
|
|
351
|
+
{target.type === 'llm' && (
|
|
352
|
+
<p className="text-muted-foreground/70">
|
|
353
|
+
Supports streaming via <code className="bg-secondary px-1 py-0.5 rounded">stream: true</code>
|
|
354
|
+
</p>
|
|
355
|
+
)}
|
|
356
|
+
</div>
|
|
357
|
+
</div>
|
|
358
|
+
)}
|
|
359
|
+
</div>
|
|
360
|
+
)
|
|
361
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Outlet, NavLink } from 'react-router-dom'
|
|
2
|
+
import {
|
|
3
|
+
MessageSquare,
|
|
4
|
+
LayoutDashboard,
|
|
5
|
+
Settings as SettingsIcon,
|
|
6
|
+
Radio,
|
|
7
|
+
Gauge,
|
|
8
|
+
Search
|
|
9
|
+
} from 'lucide-react'
|
|
10
|
+
import { cn } from '@/lib/utils'
|
|
11
|
+
import { useEffect, useState } from 'react'
|
|
12
|
+
import { getAdminMeta, listProviders } from '@/api/client'
|
|
13
|
+
|
|
14
|
+
const navItems = [
|
|
15
|
+
{ to: '/playground', icon: MessageSquare, label: 'Playground' },
|
|
16
|
+
{ to: '/benchmark', icon: Gauge, label: 'Benchmark' },
|
|
17
|
+
{ to: '/dashboard', icon: LayoutDashboard, label: 'Dashboard' },
|
|
18
|
+
{ to: '/peek', icon: Search, label: 'Peek' },
|
|
19
|
+
{ to: '/settings', icon: SettingsIcon, label: 'Settings' },
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
export function Layout() {
|
|
23
|
+
const [healthStatus, setHealthStatus] = useState<'live' | 'degraded' | 'down'>('down')
|
|
24
|
+
const [version, setVersion] = useState<string>('0.0.0')
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
async function checkHealth() {
|
|
28
|
+
try {
|
|
29
|
+
const [providers, meta] = await Promise.all([
|
|
30
|
+
listProviders(),
|
|
31
|
+
getAdminMeta(),
|
|
32
|
+
])
|
|
33
|
+
setVersion(meta.version)
|
|
34
|
+
|
|
35
|
+
if (providers.length === 0) {
|
|
36
|
+
setHealthStatus('down')
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const enabledProviders = providers.filter((provider) => provider.enabled)
|
|
41
|
+
const enabledModels = providers.reduce(
|
|
42
|
+
(sum, provider) => sum + provider.models.filter((model) => model.enabled !== false).length,
|
|
43
|
+
0
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if (enabledProviders.length === providers.length && enabledModels > 0) {
|
|
47
|
+
setHealthStatus('live')
|
|
48
|
+
} else if (enabledModels > 0) {
|
|
49
|
+
setHealthStatus('degraded')
|
|
50
|
+
} else {
|
|
51
|
+
setHealthStatus('down')
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
setHealthStatus('down')
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
checkHealth()
|
|
59
|
+
const interval = setInterval(checkHealth, 30000)
|
|
60
|
+
return () => clearInterval(interval)
|
|
61
|
+
}, [])
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className="h-screen overflow-hidden bg-background flex">
|
|
65
|
+
{/* Sidebar */}
|
|
66
|
+
<aside className="w-56 border-r border-border flex flex-col noise-overlay">
|
|
67
|
+
{/* Logo */}
|
|
68
|
+
<div className="h-14 border-b border-border flex items-center px-4 gap-3">
|
|
69
|
+
<div className="w-8 h-8 rounded overflow-hidden flex items-center justify-center">
|
|
70
|
+
<img src="/ui/assets/favicon-32.png" alt="Waypoi" className="w-8 h-8" />
|
|
71
|
+
</div>
|
|
72
|
+
<div>
|
|
73
|
+
<h1 className="font-mono font-semibold text-sm tracking-tight">WAYPOINT</h1>
|
|
74
|
+
<p className="text-2xs text-muted-foreground font-mono">v{version}</p>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
{/* Navigation */}
|
|
79
|
+
<nav className="flex-1 py-4 px-2 space-y-1">
|
|
80
|
+
{navItems.map(({ to, icon: Icon, label }) => (
|
|
81
|
+
<NavLink
|
|
82
|
+
key={to}
|
|
83
|
+
to={to}
|
|
84
|
+
className={({ isActive }) =>
|
|
85
|
+
cn(
|
|
86
|
+
'flex items-center gap-3 px-3 py-2 rounded-md text-sm font-medium transition-all duration-150',
|
|
87
|
+
isActive
|
|
88
|
+
? 'nav-active'
|
|
89
|
+
: 'text-muted-foreground hover:text-foreground hover:bg-secondary'
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
>
|
|
93
|
+
<Icon className="w-4 h-4" />
|
|
94
|
+
<span>{label}</span>
|
|
95
|
+
</NavLink>
|
|
96
|
+
))}
|
|
97
|
+
</nav>
|
|
98
|
+
|
|
99
|
+
{/* Status Footer */}
|
|
100
|
+
<div className="p-4 border-t border-border">
|
|
101
|
+
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
102
|
+
<Radio className="w-3 h-3" />
|
|
103
|
+
<span className="font-mono uppercase tracking-wider">Status</span>
|
|
104
|
+
<div className="flex-1" />
|
|
105
|
+
<div className="flex items-center gap-1.5">
|
|
106
|
+
<div className={cn(
|
|
107
|
+
'status-dot',
|
|
108
|
+
healthStatus === 'live' && 'status-dot-live',
|
|
109
|
+
healthStatus === 'degraded' && 'status-dot-degraded',
|
|
110
|
+
healthStatus === 'down' && 'status-dot-down',
|
|
111
|
+
)} />
|
|
112
|
+
<span className="font-mono capitalize">{healthStatus}</span>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</aside>
|
|
117
|
+
|
|
118
|
+
{/* Main Content */}
|
|
119
|
+
<main className="flex-1 flex flex-col overflow-hidden">
|
|
120
|
+
<Outlet />
|
|
121
|
+
</main>
|
|
122
|
+
</div>
|
|
123
|
+
)
|
|
124
|
+
}
|