@tokcalc/mcp-server 0.1.3 → 0.2.0-alpha.1

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 (193) hide show
  1. package/README.md +86 -406
  2. package/dist/http.js +23504 -0
  3. package/dist/index.js +21290 -0
  4. package/package.json +36 -92
  5. package/.zscripts/build.sh +0 -175
  6. package/.zscripts/database-runtime-build.sh +0 -33
  7. package/.zscripts/dev.pid +0 -1
  8. package/.zscripts/dev.sh +0 -154
  9. package/.zscripts/mini-services-build.sh +0 -78
  10. package/.zscripts/mini-services-install.sh +0 -65
  11. package/.zscripts/mini-services-start.sh +0 -123
  12. package/.zscripts/python-runtime-build.sh +0 -120
  13. package/.zscripts/start.sh +0 -145
  14. package/CAPACITY_STUDY.md +0 -283
  15. package/CODE_OF_CONDUCT.md +0 -55
  16. package/CONTRIBUTING.md +0 -177
  17. package/Caddyfile +0 -23
  18. package/LICENSE +0 -204
  19. package/bun.lock +0 -1965
  20. package/components.json +0 -21
  21. package/db/custom.db +0 -0
  22. package/download/README.md +0 -1
  23. package/download/tokcalc-dark-calculator.png +0 -0
  24. package/download/tokcalc-dark-default.png +0 -0
  25. package/download/tokcalc-demo.webm +0 -0
  26. package/download/tokcalc-github-link.png +0 -0
  27. package/download/tokcalc-hydration-fixed.png +0 -0
  28. package/download/tokcalc-issue-resolved.png +0 -0
  29. package/download/tokcalc-light-mode.png +0 -0
  30. package/download/tokcalc-light-reference.png +0 -0
  31. package/download/tokcalc-long-context-qwen.png +0 -0
  32. package/download/tokcalc-long-context.png +0 -0
  33. package/download/tokcalc-og-image-preview.png +0 -0
  34. package/download/tokcalc-phase2-3.png +0 -0
  35. package/download/tokcalc-plain-english.png +0 -0
  36. package/download/tokcalc-preview.png +0 -0
  37. package/download/tokcalc-share-bvb.png +0 -0
  38. package/download/tokcalc-share-feature.png +0 -0
  39. package/download/tokcalc-tab-build-vs-buy.png +0 -0
  40. package/download/tokcalc-tab-calculator.png +0 -0
  41. package/download/tokcalc-tab-reference.png +0 -0
  42. package/eslint.config.mjs +0 -50
  43. package/examples/websocket/frontend.tsx +0 -196
  44. package/examples/websocket/server.ts +0 -138
  45. package/mini-services/.gitkeep +0 -0
  46. package/mini-services/mcp-server/README.md +0 -86
  47. package/mini-services/mcp-server/bun.lock +0 -202
  48. package/mini-services/mcp-server/index.ts +0 -504
  49. package/mini-services/mcp-server/package.json +0 -40
  50. package/next.config.ts +0 -12
  51. package/postcss.config.mjs +0 -5
  52. package/prisma/schema.prisma +0 -32
  53. package/public/google6f58ca6be85fa903.html +0 -1
  54. package/public/logo.svg +0 -29
  55. package/public/manifest.json +0 -51
  56. package/public/og-icon-256.png +0 -0
  57. package/public/og.png +0 -0
  58. package/public/robots.txt +0 -25
  59. package/public/sitemap.xml +0 -23
  60. package/public/tokcalc-demo.gif +0 -0
  61. package/scripts/og-template.html +0 -120
  62. package/scripts/render-og.mjs +0 -43
  63. package/server.json +0 -21
  64. package/src/app/api/pricing/aws/route.ts +0 -186
  65. package/src/app/api/pricing/azure/route.ts +0 -168
  66. package/src/app/api/pricing/gcp/route.ts +0 -230
  67. package/src/app/api/pricing/vast-ai/route.ts +0 -164
  68. package/src/app/api/route.ts +0 -5
  69. package/src/app/compare/h100-vs-h200/layout.tsx +0 -30
  70. package/src/app/compare/h100-vs-h200/page.tsx +0 -328
  71. package/src/app/globals.css +0 -122
  72. package/src/app/layout.tsx +0 -276
  73. package/src/app/page.tsx +0 -2670
  74. package/src/components/azure-live-pricing.tsx +0 -185
  75. package/src/components/benchmark-import.tsx +0 -340
  76. package/src/components/confidence-badge.tsx +0 -116
  77. package/src/components/live-pricing-comparison.tsx +0 -241
  78. package/src/components/theme-provider.tsx +0 -11
  79. package/src/components/theme-toggle.tsx +0 -55
  80. package/src/components/ui/accordion.tsx +0 -66
  81. package/src/components/ui/alert-dialog.tsx +0 -157
  82. package/src/components/ui/alert.tsx +0 -66
  83. package/src/components/ui/aspect-ratio.tsx +0 -11
  84. package/src/components/ui/avatar.tsx +0 -53
  85. package/src/components/ui/badge.tsx +0 -46
  86. package/src/components/ui/breadcrumb.tsx +0 -109
  87. package/src/components/ui/button.tsx +0 -59
  88. package/src/components/ui/calendar.tsx +0 -213
  89. package/src/components/ui/card.tsx +0 -92
  90. package/src/components/ui/carousel.tsx +0 -241
  91. package/src/components/ui/chart.tsx +0 -353
  92. package/src/components/ui/checkbox.tsx +0 -32
  93. package/src/components/ui/collapsible.tsx +0 -33
  94. package/src/components/ui/command.tsx +0 -184
  95. package/src/components/ui/context-menu.tsx +0 -252
  96. package/src/components/ui/dialog.tsx +0 -143
  97. package/src/components/ui/drawer.tsx +0 -135
  98. package/src/components/ui/dropdown-menu.tsx +0 -257
  99. package/src/components/ui/form.tsx +0 -167
  100. package/src/components/ui/hover-card.tsx +0 -44
  101. package/src/components/ui/input-otp.tsx +0 -77
  102. package/src/components/ui/input.tsx +0 -21
  103. package/src/components/ui/label.tsx +0 -24
  104. package/src/components/ui/menubar.tsx +0 -276
  105. package/src/components/ui/navigation-menu.tsx +0 -168
  106. package/src/components/ui/pagination.tsx +0 -127
  107. package/src/components/ui/popover.tsx +0 -48
  108. package/src/components/ui/progress.tsx +0 -31
  109. package/src/components/ui/radio-group.tsx +0 -45
  110. package/src/components/ui/resizable.tsx +0 -56
  111. package/src/components/ui/scroll-area.tsx +0 -58
  112. package/src/components/ui/select.tsx +0 -185
  113. package/src/components/ui/separator.tsx +0 -28
  114. package/src/components/ui/sheet.tsx +0 -139
  115. package/src/components/ui/sidebar.tsx +0 -726
  116. package/src/components/ui/skeleton.tsx +0 -13
  117. package/src/components/ui/slider.tsx +0 -63
  118. package/src/components/ui/sonner.tsx +0 -25
  119. package/src/components/ui/switch.tsx +0 -31
  120. package/src/components/ui/table.tsx +0 -116
  121. package/src/components/ui/tabs.tsx +0 -66
  122. package/src/components/ui/textarea.tsx +0 -18
  123. package/src/components/ui/toast.tsx +0 -129
  124. package/src/components/ui/toaster.tsx +0 -35
  125. package/src/components/ui/toggle-group.tsx +0 -73
  126. package/src/components/ui/toggle.tsx +0 -47
  127. package/src/components/ui/tooltip.tsx +0 -61
  128. package/src/components/vast-ai-live-pricing.tsx +0 -176
  129. package/src/hooks/use-mobile.ts +0 -19
  130. package/src/hooks/use-toast.ts +0 -194
  131. package/src/lib/benchmark-parser-sglang.ts +0 -150
  132. package/src/lib/benchmark-parser-tokcalc.ts +0 -247
  133. package/src/lib/benchmark-parser-trtllm.ts +0 -152
  134. package/src/lib/benchmark-parser-vllm.ts +0 -198
  135. package/src/lib/benchmark-schema.ts +0 -263
  136. package/src/lib/db.ts +0 -13
  137. package/src/lib/engine-presets.ts +0 -183
  138. package/src/lib/price-schema.ts +0 -141
  139. package/src/lib/token-calc.ts +0 -808
  140. package/src/lib/track.ts +0 -31
  141. package/src/lib/url-state.ts +0 -256
  142. package/src/lib/utils.ts +0 -6
  143. package/tailwind.config.ts +0 -64
  144. package/tests/database-runtime-build.sh +0 -75
  145. package/tests/python-runtime-build.sh +0 -64
  146. package/tests/python-runtime-container.sh +0 -31
  147. package/tool-results/bash_1789888171144_2c5381860539.txt +0 -161
  148. package/tool-results/bash_1789888175925_49c53ba3c61b.txt +0 -191
  149. package/tool-results/bash_1789888181202_49c53ba3c61b.txt +0 -191
  150. package/tool-results/bash_1789888195219_4a86a5c91411.txt +0 -200
  151. package/tool-results/bash_1789888203128_6cca13c71b47.txt +0 -199
  152. package/tool-results/bash_1789929256963_2a52aff0d0a8.txt +0 -160
  153. package/tool-results/read_1789888151021_69f58eec6a5b.txt +0 -653
  154. package/tool-results/read_1789888153837_1d3a8bfc2a94.txt +0 -653
  155. package/tool-results/read_1789888163087_ccc406d47505.txt +0 -122
  156. package/tool-results/read_1789888167347_67d1d7c9830a.txt +0 -122
  157. package/tool-results/read_1789929252529_d90e8f383a25.txt +0 -285
  158. package/tsconfig.json +0 -42
  159. package/upload/Pasted Content_1789887800864.txt +0 -652
  160. package/upload/Pasted Content_1789887909561.txt +0 -652
  161. package/upload/Pasted Content_1789887918428.txt +0 -652
  162. package/upload/Pasted Content_1789887959420.txt +0 -652
  163. package/upload/Pasted Content_1789888020485.txt +0 -652
  164. package/upload/Pasted Content_1789888058079.txt +0 -652
  165. package/upload/Pasted Content_1789888885033.txt +0 -686
  166. package/upload/Pasted Content_1789928912741.txt +0 -285
  167. package/upload/Pasted Content_1789928938402.txt +0 -285
  168. package/upload/Pasted Content_1789929160389.txt +0 -285
  169. package/upload/Pasted Content_1789929176660.txt +0 -285
  170. package/upload/issue_vision.json +0 -28
  171. package/upload/pasted_image_1789883175209.png +0 -0
  172. package/upload/pasted_image_1789899056690.png +0 -0
  173. package/upload/pasted_image_1789900371483.png +0 -0
  174. package/upload/pasted_image_1789900472823.png +0 -0
  175. package/upload/pasted_image_1789900490374.png +0 -0
  176. package/upload/pasted_image_1789900585552.png +0 -0
  177. package/upload/pasted_image_1789900606519.png +0 -0
  178. package/upload/pasted_image_1789901598705.png +0 -0
  179. package/upload/pasted_image_1789901613545.png +0 -0
  180. package/upload/pasted_image_1789978382674.png +0 -0
  181. package/upload/pasted_image_1789978392749.png +0 -0
  182. package/upload/pasted_image_1789978474879.png +0 -0
  183. package/upload/pasted_image_1789978523652.png +0 -0
  184. package/upload/pasted_image_1789984219089.png +0 -0
  185. package/upload/pasted_image_1789984491896.png +0 -0
  186. package/upload/pasted_image_1789985017950.png +0 -0
  187. package/upload/pasted_image_1789985036765.png +0 -0
  188. package/upload/pasted_image_1789985049848.png +0 -0
  189. package/upload/pasted_image_1790002427833.png +0 -0
  190. package/upload/pasted_image_1790002659944.png +0 -0
  191. package/upload/pasted_image_1790037038476.png +0 -0
  192. package/upload/screenshot_analysis.json +0 -28
  193. package/upload/vision_output.json +0 -28
@@ -1,123 +0,0 @@
1
- #!/bin/sh
2
-
3
- # 配置项
4
- DIST_DIR="./mini-services-dist"
5
-
6
- # 存储所有子进程的 PID
7
- pids=""
8
-
9
- # 清理函数:优雅关闭所有服务
10
- cleanup() {
11
- echo ""
12
- echo "🛑 正在关闭所有服务..."
13
-
14
- # 发送 SIGTERM 信号给所有子进程
15
- for pid in $pids; do
16
- if kill -0 "$pid" 2>/dev/null; then
17
- service_name=$(ps -p "$pid" -o comm= 2>/dev/null || echo "unknown")
18
- echo " 关闭进程 $pid ($service_name)..."
19
- kill -TERM "$pid" 2>/dev/null
20
- fi
21
- done
22
-
23
- # 等待所有进程退出(最多等待 5 秒)
24
- sleep 1
25
- for pid in $pids; do
26
- if kill -0 "$pid" 2>/dev/null; then
27
- # 如果还在运行,等待最多 4 秒
28
- timeout=4
29
- while [ $timeout -gt 0 ] && kill -0 "$pid" 2>/dev/null; do
30
- sleep 1
31
- timeout=$((timeout - 1))
32
- done
33
- # 如果仍然在运行,强制关闭
34
- if kill -0 "$pid" 2>/dev/null; then
35
- echo " 强制关闭进程 $pid..."
36
- kill -KILL "$pid" 2>/dev/null
37
- fi
38
- fi
39
- done
40
-
41
- echo "✅ 所有服务已关闭"
42
- }
43
-
44
- main() {
45
- echo "🚀 开始启动所有 mini services..."
46
-
47
- # 检查 dist 目录是否存在
48
- if [ ! -d "$DIST_DIR" ]; then
49
- echo "ℹ️ 目录 $DIST_DIR 不存在"
50
- return
51
- fi
52
-
53
- # 查找所有 mini-service-*.js 文件
54
- service_files=""
55
- for file in "$DIST_DIR"/mini-service-*.js; do
56
- if [ -f "$file" ]; then
57
- if [ -z "$service_files" ]; then
58
- service_files="$file"
59
- else
60
- service_files="$service_files $file"
61
- fi
62
- fi
63
- done
64
-
65
- # 计算服务文件数量
66
- service_count=0
67
- for file in $service_files; do
68
- service_count=$((service_count + 1))
69
- done
70
-
71
- if [ $service_count -eq 0 ]; then
72
- echo "ℹ️ 未找到任何 mini service 文件"
73
- return
74
- fi
75
-
76
- echo "📦 找到 $service_count 个服务,开始启动..."
77
- echo ""
78
-
79
- # 启动每个服务
80
- for file in $service_files; do
81
- service_name=$(basename "$file" .js | sed 's/mini-service-//')
82
- echo "▶️ 启动服务: $service_name..."
83
-
84
- # 使用 bun 运行服务(后台运行)
85
- bun "$file" &
86
- pid=$!
87
- if [ -z "$pids" ]; then
88
- pids="$pid"
89
- else
90
- pids="$pids $pid"
91
- fi
92
-
93
- # 等待一小段时间检查进程是否成功启动
94
- sleep 0.5
95
- if ! kill -0 "$pid" 2>/dev/null; then
96
- echo "❌ $service_name 启动失败"
97
- # 从字符串中移除失败的 PID
98
- pids=$(echo "$pids" | sed "s/\b$pid\b//" | sed 's/ */ /g' | sed 's/^ *//' | sed 's/ *$//')
99
- else
100
- echo "✅ $service_name 已启动 (PID: $pid)"
101
- fi
102
- done
103
-
104
- # 计算运行中的服务数量
105
- running_count=0
106
- for pid in $pids; do
107
- if kill -0 "$pid" 2>/dev/null; then
108
- running_count=$((running_count + 1))
109
- fi
110
- done
111
-
112
- echo ""
113
- echo "🎉 所有服务已启动!共 $running_count 个服务正在运行"
114
- echo ""
115
- echo "💡 按 Ctrl+C 停止所有服务"
116
- echo ""
117
-
118
- # 等待所有后台进程
119
- wait
120
- }
121
-
122
- main
123
-
@@ -1,120 +0,0 @@
1
- #!/bin/bash
2
-
3
- set -euo pipefail
4
-
5
- PROJECT_DIR="${PROJECT_DIR:-/home/z/my-project}"
6
- BUILD_DIR="${BUILD_DIR:?BUILD_DIR is required}"
7
- PYTHON_VERSION="${PYTHON_VERSION:-3.12}"
8
- NEXT_DIST_DIR="$BUILD_DIR/next-service-dist"
9
- PYTHON_RUNTIME_DIR="$BUILD_DIR/python-runtime"
10
- PYTHON_PACKAGES_DIR="$PYTHON_RUNTIME_DIR/site-packages"
11
-
12
- has_python_sources() {
13
- find "$PROJECT_DIR" \
14
- \( -type d \( -name '.git' \
15
- -o -name '.next' \
16
- -o -name '.venv' \
17
- -o -name 'node_modules' \
18
- -o -name '__pycache__' \
19
- -o -name 'mini-services' \
20
- -o -name 'upload' \
21
- -o -name 'download' \
22
- \) -prune \) \
23
- -o -type f \( -name '*.py' -o -name '*.pyi' \) -print -quit | grep -q .
24
- }
25
-
26
- if ! has_python_sources \
27
- && [ ! -f "$PROJECT_DIR/requirements.txt" ] \
28
- && [ ! -f "$PROJECT_DIR/pyproject.toml" ]; then
29
- echo "ℹ️ 未检测到 Python 源码或依赖清单,跳过 Python runtime 构建"
30
- exit 0
31
- fi
32
-
33
- if ! command -v uv >/dev/null 2>&1; then
34
- echo "❌ 检测到 Python 项目,但构建环境中没有 uv"
35
- exit 1
36
- fi
37
-
38
- echo "🐍 检测到 Python runtime,目标版本: $PYTHON_VERSION"
39
- mkdir -p "$NEXT_DIST_DIR" "$PYTHON_PACKAGES_DIR"
40
-
41
- install_requirements() {
42
- local requirements_file="$1"
43
- local target_dir="${2:-$PYTHON_PACKAGES_DIR}"
44
- if [ ! -s "$requirements_file" ]; then
45
- echo "ℹ️ Python 依赖清单为空,跳过依赖安装"
46
- return 0
47
- fi
48
-
49
- echo "📦 根据 $(basename "$requirements_file") 固化 Python 生产依赖..."
50
- uv pip install \
51
- --python "$PYTHON_VERSION" \
52
- --target "$target_dir" \
53
- --requirements "$requirements_file"
54
-
55
- # --target 生成的 console scripts 会保留构建机 Python 的绝对 shebang。
56
- # 改成 Runner 内可解析的 python,并由 start scripts 将该 bin 目录加入 PATH。
57
- if [ -d "$target_dir/bin" ]; then
58
- for script in "$target_dir"/bin/*; do
59
- [ -f "$script" ] || continue
60
- perl -0pi -e 's/\A#![^\n]*python[^\n]*\n/#!\/usr\/bin\/env python\n/' "$script"
61
- done
62
- fi
63
- }
64
-
65
- install_pyproject() {
66
- local project_dir="$1"
67
- local target_dir="$2"
68
- local output_name="$3"
69
- local requirements_file="$PYTHON_RUNTIME_DIR/$output_name"
70
-
71
- if [ -f "$project_dir/uv.lock" ]; then
72
- uv export \
73
- --project "$project_dir" \
74
- --frozen \
75
- --no-dev \
76
- --no-emit-project \
77
- --format requirements.txt \
78
- --output-file "$requirements_file"
79
- else
80
- uv pip compile \
81
- "$project_dir/pyproject.toml" \
82
- --python-version "$PYTHON_VERSION" \
83
- --output-file "$requirements_file"
84
- fi
85
- install_requirements "$requirements_file" "$target_dir"
86
- }
87
-
88
- if [ -f "$PROJECT_DIR/pyproject.toml" ] && [ -f "$PROJECT_DIR/uv.lock" ]; then
89
- echo "🔒 使用 pyproject.toml + uv.lock 导出生产依赖..."
90
- install_pyproject "$PROJECT_DIR" "$PYTHON_PACKAGES_DIR" "requirements.lock.txt"
91
- elif [ -f "$PROJECT_DIR/requirements.txt" ]; then
92
- cp "$PROJECT_DIR/requirements.txt" "$PYTHON_RUNTIME_DIR/requirements.txt"
93
- install_requirements "$PYTHON_RUNTIME_DIR/requirements.txt"
94
- elif [ -f "$PROJECT_DIR/pyproject.toml" ]; then
95
- echo "📦 pyproject.toml 未配套 uv.lock,解析生产依赖..."
96
- install_pyproject "$PROJECT_DIR" "$PYTHON_PACKAGES_DIR" "requirements.txt"
97
- else
98
- echo "⚠️ 检测到 Python 源码,但没有 requirements.txt 或 pyproject.toml;仅支持 Python 标准库"
99
- fi
100
-
101
- if has_python_sources; then
102
- echo "📄 复制 Python 源码到部署项目,保持相对路径..."
103
- (
104
- cd "$PROJECT_DIR"
105
- find . \
106
- \( -type d \( -name '.git' \
107
- -o -name '.next' \
108
- -o -name '.venv' \
109
- -o -name 'node_modules' \
110
- -o -name '__pycache__' \
111
- -o -name 'mini-services' \
112
- -o -name 'upload' \
113
- -o -name 'download' \
114
- \) -prune \) \
115
- -o -type f \( -name '*.py' -o -name '*.pyi' \) -print0 \
116
- | tar --null -T - -cf -
117
- ) | tar -C "$NEXT_DIST_DIR" -xf -
118
- fi
119
-
120
- echo "✅ Python runtime 已固化到部署产物"
@@ -1,145 +0,0 @@
1
- #!/bin/sh
2
-
3
- set -e
4
-
5
- # 获取脚本所在目录
6
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7
- BUILD_DIR="$SCRIPT_DIR"
8
-
9
- # 存储所有子进程的 PID
10
- pids=""
11
-
12
- # 清理函数:优雅关闭所有服务
13
- cleanup() {
14
- echo ""
15
- echo "🛑 正在关闭所有服务..."
16
-
17
- # 发送 SIGTERM 信号给所有子进程
18
- for pid in $pids; do
19
- if kill -0 "$pid" 2>/dev/null; then
20
- service_name=$(ps -p "$pid" -o comm= 2>/dev/null || echo "unknown")
21
- echo " 关闭进程 $pid ($service_name)..."
22
- kill -TERM "$pid" 2>/dev/null
23
- fi
24
- done
25
-
26
- # 等待所有进程退出(最多等待 5 秒)
27
- sleep 1
28
- for pid in $pids; do
29
- if kill -0 "$pid" 2>/dev/null; then
30
- # 如果还在运行,等待最多 4 秒
31
- timeout=4
32
- while [ $timeout -gt 0 ] && kill -0 "$pid" 2>/dev/null; do
33
- sleep 1
34
- timeout=$((timeout - 1))
35
- done
36
- # 如果仍然在运行,强制关闭
37
- if kill -0 "$pid" 2>/dev/null; then
38
- echo " 强制关闭进程 $pid..."
39
- kill -KILL "$pid" 2>/dev/null
40
- fi
41
- fi
42
- done
43
-
44
- echo "✅ 所有服务已关闭"
45
- exit 0
46
- }
47
-
48
- echo "🚀 开始启动所有服务..."
49
- echo ""
50
-
51
- # 切换到构建目录
52
- cd "$BUILD_DIR" || exit 1
53
-
54
- ls -lah
55
-
56
- DEFAULT_PACKAGED_DB_PATH="/app/db/custom.db"
57
- DEFAULT_PACKAGED_DATABASE_URL="file:$DEFAULT_PACKAGED_DB_PATH"
58
-
59
- # Python 依赖在构建阶段安装进部署产物,不复用 Sandbox 的 /home/z/.venv。
60
- # Next.js 及其启动的子进程都会继承这组路径。
61
- if [ -d "/app/python-runtime/site-packages" ]; then
62
- export PYTHONPATH="/app/python-runtime/site-packages:/app/next-service-dist${PYTHONPATH:+:$PYTHONPATH}"
63
- export PATH="/app/python-runtime/site-packages/bin:$PATH"
64
- export PYTHONDONTWRITEBYTECODE=1
65
- export PYTHONUNBUFFERED=1
66
- echo "🐍 已启用部署包内 Python runtime: $(python --version 2>&1)"
67
- fi
68
-
69
- # 启动 Next.js 服务器
70
- if [ -f "./next-service-dist/server.js" ]; then
71
- echo "🚀 启动 Next.js 服务器..."
72
- cd next-service-dist/ || exit 1
73
-
74
- # 设置环境变量
75
- export NODE_ENV=production
76
- export PORT="${PORT:-3000}"
77
- export HOSTNAME="${HOSTNAME:-0.0.0.0}"
78
- export DATABASE_URL="${DATABASE_URL:-$DEFAULT_PACKAGED_DATABASE_URL}"
79
-
80
- if [ "$DATABASE_URL" = "$DEFAULT_PACKAGED_DATABASE_URL" ]; then
81
- if [ ! -f "$DEFAULT_PACKAGED_DB_PATH" ]; then
82
- echo "❌ 未找到打包后的数据库文件 $DEFAULT_PACKAGED_DB_PATH"
83
- echo " 为避免生产环境启动到空数据库,启动已终止"
84
- exit 1
85
- fi
86
-
87
- echo "🗄️ 当前使用打包数据库: $DEFAULT_PACKAGED_DB_PATH"
88
- else
89
- echo "🗄️ 当前使用外部指定数据库: $DATABASE_URL"
90
- fi
91
-
92
- # 后台启动 Next.js
93
- bun server.js &
94
- NEXT_PID=$!
95
- pids="$NEXT_PID"
96
-
97
- # 等待一小段时间检查进程是否成功启动
98
- sleep 1
99
- if ! kill -0 "$NEXT_PID" 2>/dev/null; then
100
- echo "❌ Next.js 服务器启动失败"
101
- exit 1
102
- else
103
- echo "✅ Next.js 服务器已启动 (PID: $NEXT_PID, Port: $PORT)"
104
- fi
105
-
106
- cd ../
107
- else
108
- echo "⚠️ 未找到 Next.js 服务器文件: ./next-service-dist/server.js"
109
- fi
110
-
111
- # 启动 mini-services
112
- if [ -f "./mini-services-start.sh" ]; then
113
- echo "🚀 启动 mini-services..."
114
-
115
- # 运行启动脚本(从根目录运行,脚本内部会处理 mini-services-dist 目录)
116
- sh ./mini-services-start.sh &
117
- MINI_PID=$!
118
- pids="$pids $MINI_PID"
119
-
120
- # 等待一小段时间检查进程是否成功启动
121
- sleep 1
122
- if ! kill -0 "$MINI_PID" 2>/dev/null; then
123
- echo "⚠️ mini-services 可能启动失败,但继续运行..."
124
- else
125
- echo "✅ mini-services 已启动 (PID: $MINI_PID)"
126
- fi
127
- elif [ -d "./mini-services-dist" ]; then
128
- echo "⚠️ 未找到 mini-services 启动脚本,但目录存在"
129
- else
130
- echo "ℹ️ mini-services 目录不存在,跳过"
131
- fi
132
-
133
- # 启动 Caddy(如果存在 Caddyfile)
134
- echo "🚀 启动 Caddy..."
135
-
136
- # Caddy 作为前台进程运行(主进程)
137
- echo "✅ Caddy 已启动(前台运行)"
138
- echo ""
139
- echo "🎉 所有服务已启动!"
140
- echo ""
141
- echo "💡 按 Ctrl+C 停止所有服务"
142
- echo ""
143
-
144
- # Caddy 作为主进程运行
145
- exec caddy run --config Caddyfile --adapter caddyfile
package/CAPACITY_STUDY.md DELETED
@@ -1,283 +0,0 @@
1
- # How context length, KV cache, batching, and latency SLOs change the number of concurrent users an LLM deployment can actually support
2
-
3
- ### A capacity-planning analysis across RTX 4090, H100, H200, and B200 for Llama 3.3 70B
4
-
5
- **By the tokcalc maintainer** · September 2026 · [Live calculator](https://tokcalc.vercel.app/#t=calculator&m=llama3-3-70b&g=h200-sxm&q=fp8&n=1&b=1&p=50000&o=500&cb=1&cbm=1.5) · [GitHub](https://github.com/stevecrates489-commits/tokcalc)
6
-
7
- ---
8
-
9
- ## Why this analysis exists
10
-
11
- Every "tokens per second" calculator I've found answers the wrong question. They tell you how fast a GPU can generate tokens in isolation. They don't tell you whether you can actually serve your traffic pattern — your context lengths, your concurrency, your latency SLOs — on a real GPU fleet.
12
-
13
- This analysis asks a different question:
14
-
15
- > **"How many concurrent users can one GPU (or a small fleet) actually support for Llama 3.3 70B, as context length grows from 4K to 128K?"**
16
-
17
- The answer is not a single number. It's a curve that drops sharply with context length — because KV cache memory grows linearly, and eventually eats all your VRAM headroom.
18
-
19
- I built [tokcalc](https://tokcalc.vercel.app) to model this. This article uses the same formulas, same data, same code. Every number below is computed from the open-source calculator — you can reproduce every result by opening the links.
20
-
21
- ---
22
-
23
- ## The setup
24
-
25
- **Model**: Llama 3.3 70B (80 layers, 8 KV heads, 128 head dim, GQA, 128K max context) — the most-deployed open-weights model in production as of late 2026.
26
-
27
- **Quantization**: FP8 (1 byte/param) — native on H100/H200/B200, roughly 1.5× faster than FP16 thanks to dedicated FP8 tensor cores, with negligible quality loss per cited benchmarks.
28
-
29
- **Model weights**: 70.6 GB (70.6B params × 1 byte)
30
-
31
- **GPUs tested**:
32
-
33
- | GPU | VRAM | HBM Bandwidth | FP8 Efficiency | Notes |
34
- |---|---|---|---|---|
35
- | 4× RTX 4090 | 4×24 = 96 GB | 4×1008 = 4032 GB/s | 1.5× (consumer) | No NVLink (PCIe only) — TP efficiency ~0.6 |
36
- | H100 SXM5 | 80 GB | 3350 GB/s | 1.5× | NVLink 900 GB/s |
37
- | H200 SXM5 | 141 GB | 4800 GB/s | 1.5× | NVLink 900 GB/s |
38
- | B200 SXM | 192 GB | 8000 GB/s | 1.5× (est.) | FP16 dense TFLOPS not published by NVIDIA |
39
-
40
- **KV cache formula** (per request, per token):
41
-
42
- ```
43
- KV bytes/token = 2 × layers × kv_heads × head_dim × bytes_per_kv_value
44
- = 2 × 80 × 8 × 128 × 2
45
- = 327,680 bytes = 0.32 MB/token
46
- ```
47
-
48
- The factor of 2 stores both Keys and Values. GQA (Grouped-Query Attention) means only 8 KV heads (not 64 query heads) are cached per layer — this is what makes 70B Llama models feasible at long context.
49
-
50
- **Max concurrent users** at a given context length:
51
-
52
- ```
53
- max_users = floor((total_vram - model_weights) / (kv_per_token × context_length))
54
- ```
55
-
56
- ---
57
-
58
- ## Finding 1: Context length is the #1 capacity killer — doubling context halves concurrency
59
-
60
- | Context | KV/request | H100 max users | H200 max users | B200 max users |
61
- |---|---|---|---|---|
62
- | 4K | 1.31 GB | 7 | 53 | 92 |
63
- | 8K | 2.62 GB | 3 | 26 | 46 |
64
- | 16K | 5.24 GB | 1 | 13 | 23 |
65
- | 32K | 10.49 GB | 0 | 6 | 11 |
66
- | 64K | 20.97 GB | 0 | 3 | 5 |
67
- | 128K | 41.94 GB | 0 | 1 | 2 |
68
-
69
- Every time you double context length, you halve the number of concurrent users you can serve — because KV cache memory doubles and eats into the VRAM headroom left after loading model weights.
70
-
71
- This is the most important graph in this analysis. If your users send 32K-token prompts, your H100 fleet can't serve even 1 user — despite the model fitting comfortably in VRAM.
72
-
73
- [Try this on tokcalc →](https://tokcalc.vercel.app/#t=calculator&m=llama3-3-70b&g=h100-sxm&q=fp8&n=1&b=1&p=32768&o=500)
74
-
75
- ---
76
-
77
- ## Finding 2: H200 doubles H100 capacity at every context length — VRAM is the bottleneck, not bandwidth
78
-
79
- | Context | H100 (80 GB) | H200 (141 GB) | Ratio |
80
- |---|---|---|---|
81
- | 4K | 7 | 53 | 7.6× |
82
- | 8K | 3 | 26 | 8.7× |
83
- | 16K | 1 | 13 | 13× |
84
- | 32K | 0 | 6 | ∞ |
85
- | 64K | 0 | 3 | ∞ |
86
- | 128K | 0 | 1 | ∞ |
87
-
88
- H200 has 1.76× more VRAM (141 vs 80 GB) and 1.43× more bandwidth (4800 vs 3350 GB/s). But the concurrency improvement is **7-13×** — not 1.76×.
89
-
90
- Why? Because at 4K context, H100 has only 9.4 GB of free VRAM after loading weights (80 - 70.6 = 9.4 GB). H200 has 70.4 GB free. The ratio of free VRAM is **7.5×** — which matches the concurrency ratio almost exactly.
91
-
92
- **The implication**: if you're paying for H100s and your users send 8K+ context, you're wasting 80% of your GPU's compute. The bottleneck is memory capacity, not memory bandwidth or FLOPS.
93
-
94
- [Try H100 vs H200 on tokcalc →](https://tokcalc.vercel.app/#t=calculator&m=llama3-3-70b&g=h200-sxm&q=fp8&n=1&b=1&p=32768&o=500)
95
-
96
- ---
97
-
98
- ## Finding 3: B200 nearly triples H200 concurrency — but FP16 dense FLOPS remain unpublished
99
-
100
- | Context | H200 (141 GB) | B200 (192 GB) | Ratio |
101
- |---|---|---|---|
102
- | 4K | 53 | 92 | 1.7× |
103
- | 8K | 26 | 46 | 1.8× |
104
- | 16K | 13 | 23 | 1.8× |
105
- | 32K | 6 | 11 | 1.8× |
106
- | 64K | 3 | 5 | 1.7× |
107
- | 128K | 1 | 2 | 2× |
108
-
109
- B200 has 1.36× more VRAM (192 vs 141 GB) and 1.67× more bandwidth (8000 vs 4800 GB/s). The concurrency improvement is ~1.8× — roughly proportional to the VRAM increase (free VRAM: 121.4 vs 70.4 GB = 1.72×).
110
-
111
- **The important caveat**: NVIDIA does not publish dense FP16/BF16 TFLOPS for B200. The official spec page only exposes FP4 and FP8 metrics. In tokcalc, B200's `flopsTflops` field is set to `null` with a "FP4/FP8 specs only; FP16 dense not publicly reported" note — the calculator uses a conservative fallback estimate.
112
-
113
- This means B200's **decode throughput** estimate (110 tok/s for Llama 70B FP8) should be treated as an **inferred** value (🟡 confidence dot in tokcalc), not a measured one. If you have real B200 benchmark numbers, [submit them](https://github.com/stevecrates489-commits/tokcalc/issues/new?labels=benchmark+needed).
114
-
115
- ---
116
-
117
- ## Finding 4: 32K is the practical inflection point for single-GPU H100 serving
118
-
119
- At 32K context, Llama 3.3 70B FP8 on a single H100:
120
-
121
- ```
122
- model weights: 70.6 GB
123
- KV cache (32K): 10.49 GB
124
- total: 81.09 GB
125
- available VRAM: 80 GB
126
- → does not fit (over by 1.09 GB)
127
- ```
128
-
129
- 32K is the wall. Below 32K, H100 can serve 1-7 concurrent users. Above 32K, H100 can't serve even 1 user at full context — you need tensor parallelism (TP×2+) or context parallelism (RingAttention).
130
-
131
- **Practical implication**: If your RAG pipeline injects 30K tokens of document context, a single H100 can barely fit one request. You need:
132
- - **TP×2** (2× H100, 160 GB total): 6 users at 32K
133
- - **TP×4** (4× H100, 320 GB total): 23 users at 32K
134
- - Or switch to **H200** (141 GB): 6 users at 32K on a single GPU
135
-
136
- [Try 32K context on tokcalc →](https://tokcalc.vercel.app/#t=calculator&m=llama3-3-70b&g=h100-sxm&q=fp8&n=1&b=1&p=32768&o=500)
137
-
138
- ---
139
-
140
- ## Finding 5: 4× RTX 4090 ≈ 1× H100 for throughput — but worse for concurrency and topology
141
-
142
- | Metric | 4× RTX 4090 (PCIe) | 1× H100 (SXM) |
143
- |---|---|---|
144
- | Total VRAM | 96 GB | 80 GB |
145
- | Aggregate HBM BW | 4032 GB/s | 3350 GB/s |
146
- | TP efficiency (PCIe vs NVLink) | ~0.60 | 1.0 (single GPU) |
147
- | Effective BW | ~2419 GB/s | 3350 GB/s |
148
- | Decode tok/s (Llama 70B FP8) | ~51 tok/s | ~46 tok/s |
149
- | Max users @ 4K | 19 | 7 |
150
- | Max users @ 8K | 9 | 3 |
151
- | Max users @ 16K | 4 | 1 |
152
- | Max users @ 32K | 2 | 0 |
153
-
154
- 4× 4090 has more raw VRAM (96 vs 80 GB) and more raw bandwidth (4032 vs 3350 GB/s), so it can serve more concurrent users at short context. But PCIe interconnect means the TP efficiency drops to ~0.60 (vs 1.0 for a single H100) — so per-user throughput is comparable.
155
-
156
- **The hidden cost**: 4× 4090 draws ~1800W total, requires a 2000W+ PSU, and the PCIe topology creates memory transfer bottlenecks that worsen at longer contexts. For production serving, H100's NVLink topology is dramatically better for multi-GPU workloads.
157
-
158
- **Bottom line**: 4× 4090 is viable for local inference / hobbyist deployments. For production serving at scale, a single H100 or H200 is a better investment.
159
-
160
- ---
161
-
162
- ## Finding 6: 128K context requires H200 or B200 — and prefill latency becomes the bottleneck
163
-
164
- At 128K context, Llama 3.3 70B FP8:
165
-
166
- | GPU | VRAM | Fits? | Max users | Prefill time | Decode tok/s |
167
- |---|---|---|---|---|---|
168
- | H100 (80 GB) | 80 | ❌ (needs 112 GB) | 0 | — | — |
169
- | H200 (141 GB) | 141 | ✅ | 1 | ~12s | 66 tok/s |
170
- | B200 (192 GB) | 192 | ✅ | 2 | ~7s | 110 tok/s |
171
-
172
- At 128K context, **prefill latency dominates the user experience**. Even on B200, it takes ~7 seconds to process a 128K-token prompt before the first output token appears. This is because prefill cost grows superlinearly with context length — the attention computation is O(N²) for dense attention:
173
-
174
- ```
175
- prefill_flops = 2 × N × T + T² × kv_heads × head_dim × layers
176
- = linear matmul cost + quadratic attention cost
177
- ```
178
-
179
- At 128K tokens, the attention term (T²) dominates the linear term (T). For models with blockwise attention (RingAttention, FlashAttention-3), the real cost is lower than this estimate — but the fundamental scaling remains superlinear.
180
-
181
- **Practical implication**: for 128K+ context, you need:
182
- 1. **VRAM**: H200 (141 GB) or B200 (192 GB) — H100 can't fit it
183
- 2. **Topology**: TP×2 minimum — 1 GPU can serve only 1-2 concurrent users
184
- 3. **Latency budget**: accept 5-15s prefill, or use chunked prefill + continuous batching
185
- 4. **Consider**: context parallel (RingAttention) if you need >2 concurrent users at 128K
186
-
187
- [Try 128K context on tokcalc →](https://tokcalc.vercel.app/#t=calculator&m=llama3-3-70b&g=h200-sxm&q=fp8&n=1&b=1&p=131072&o=500)
188
-
189
- ---
190
-
191
- ## The master chart
192
-
193
- **Max concurrent users for Llama 3.3 70B FP8, by GPU and context length:**
194
-
195
- ```
196
- Context 4×4090 H100 H200 B200
197
- ─────── ─────── ────── ────── ──────
198
- 4K 19 7 53 92
199
- 8K 9 3 26 46
200
- 16K 4 1 13 23
201
- 32K 2 0 6 11
202
- 64K 1 0 3 5
203
- 128K 0 0 1 2
204
- ```
205
-
206
- Context doubles → concurrency halves. This is the fundamental scaling law of LLM serving capacity.
207
-
208
- ---
209
-
210
- ## Methodology
211
-
212
- All numbers are computed by [tokcalc](https://tokcalc.vercel.app) — an open-source LLM serving capacity planner (Apache 2.0). The formulas are:
213
-
214
- 1. **KV cache per request**: `2 × layers × kv_heads × head_dim × bytes_per_kv_value × context_length × batch_size`
215
- 2. **Max concurrency**: `floor((total_vram - model_weights) / kv_per_request)`
216
- 3. **Decode throughput**: `(HBM_BW × η_mem × quant_eff) / model_size` where `η_mem = 0.65` (real-world memory utilization)
217
- 4. **Prefill throughput**: `(FLOPS × η_compute) / (2 × active_params)` where `η_compute = 0.50`
218
- 5. **Long-context prefill correction**: adds O(N²) attention cost beyond 32K context: `attention_flops = T² × kv_heads × head_dim × layers`
219
-
220
- **Confidence levels** (visible in tokcalc UI as colored dots):
221
- - 🟢 **Measured**: GPU specs, model architecture fields, API pricing — sourced from official docs
222
- - 🟢 **Modeled**: Throughput, latency, KV cache, cost — derived from physics-based formulas
223
- - 🟡 **Inferred**: Continuous batching multiplier (1.5× default), long-context attention correction, B200 FP16 fallback — heuristics with known error bars
224
-
225
- ---
226
-
227
- ## Limitations
228
-
229
- 1. **No real benchmark data**: All throughput numbers are **modeled estimates**, not measured. Real-world performance depends on engine (vLLM, SGLang, TensorRT-LLM), model revision, driver version, kernel optimizations, and request mix. Expect 20-40% deviation from these estimates. The goal is ballpark capacity planning, not precise performance prediction.
230
-
231
- 2. **Continuous batching multiplier is a heuristic**: The 1.5× default is conservative. vLLM reported 14-24× vs HF Transformers and 2.2-2.5× vs TGI in their original benchmarks. The SOSP paper found 2-4× typical vs FasterTransformer/Orca. Real-world gains depend on request arrival distribution, prompt/output ratio, and scheduler configuration.
232
-
233
- 3. **Attention cost is approximate**: The O(N²) correction assumes dense attention. Real models use FlashAttention-3, blockwise attention, or RingAttention — which reduce the actual cost. For RingAttention specifically, the prefill time estimate is pessimistic.
234
-
235
- 4. **B200 FP16 dense FLOPS is unpublished**: NVIDIA only publishes FP4/FP8 specs for Blackwell. tokcalc uses a conservative fallback estimate — B200's decode throughput should be treated as 🟡 inferred, not measured.
236
-
237
- 5. **No engine-aware presets yet**: tokcalc currently models engine-agnostic formulas. vLLM, SGLang, and TensorRT-LLM each have different kernel optimizations, scheduler defaults, and quantization support that materially affect real-world numbers. Engine-aware presets are on the roadmap.
238
-
239
- 6. **MoE models**: DeepSeek V3 (671B total / 37B active) and Qwen 3 235B-A22B are MoE — tokcalc correctly uses active params for throughput but total params for VRAM. The KV cache formula is the same.
240
-
241
- ---
242
-
243
- ## Disclosure
244
-
245
- I built and maintain tokcalc. I'm posting this analysis because I want feedback on the workload assumptions and benchmark methodology. tokcalc is Apache-2.0 and free to use. Every formula is in the [source code](https://github.com/stevecrates489-commits/tokcalc/blob/main/src/lib/token-calc.ts) — nothing is hidden.
246
-
247
- If any of these numbers look wrong enough to be dangerous, please [open an issue](https://github.com/stevecrates489-commits/tokcalc/issues/new) with:
248
- - Which GPU × model × context combination you tested
249
- - What engine you used (vLLM, SGLang, TensorRT-LLM, llama.cpp)
250
- - Your measured throughput / latency
251
- - Your configuration flags
252
-
253
- I'll calibrate the formulas against your data and credit you in the contributor list.
254
-
255
- ---
256
-
257
- ## Try it yourself
258
-
259
- Every scenario in this article is a clickable link that opens tokcalc with the exact configuration pre-loaded:
260
-
261
- - [Llama 3.3 70B FP8 on H100, 4K context](https://tokcalc.vercel.app/#t=calculator&m=llama3-3-70b&g=h100-sxm&q=fp8&n=1&b=1&p=4096&o=500)
262
- - [Llama 3.3 70B FP8 on H200, 32K context](https://tokcalc.vercel.app/#t=calculator&m=llama3-3-70b&g=h200-sxm&q=fp8&n=1&b=1&p=32768&o=500)
263
- - [Llama 3.3 70B FP8 on B200, 128K context](https://tokcalc.vercel.app/#t=calculator&m=llama3-3-70b&g=b200-sxm&q=fp8&n=1&b=1&p=131072&o=500)
264
- - [Build-vs-Buy: Llama 3.3 70B self-host vs GPT-4o API](https://tokcalc.vercel.app/#t=build-vs-buy&sm=llama3-3-70b&sg=h200-sxm&sq=fp8&sn=1&u=50&b=8&i=5000&o=500&r=10000&ap=openai&am=gpt-4o)
265
-
266
- Change any input and the URL updates live. Share the URL with colleagues — they'll see your exact scenario.
267
-
268
- ---
269
-
270
- ## References
271
-
272
- - **PagedAttention / vLLM paper**: Kwon et al., "Efficient Memory Management for Large Language Model Serving with PagedAttention", SOSP 2023. [arxiv.org/abs/2309.06180](https://arxiv.org/abs/2309.06180)
273
- - **Continuous batching**: "Continuous Batching for LLM Inference", Anyscale, 2023. [anyscale.com/blog/continuous-batching-llm-inference](https://www.anyscale.com/blog/continuous-batching-llm-inference)
274
- - **RingAttention**: Liu et al., "Ring Attention with Blockwise Attention for Near-Infinite Context", 2023. [arxiv.org/abs/2310.01889](https://arxiv.org/abs/2310.01889)
275
- - **H100 specs**: [nvidia.com/en-us/data-center/h100](https://www.nvidia.com/en-us/data-center/h100)
276
- - **H200 specs**: [nvidia.com/en-us/data-center/h200](https://www.nvidia.com/en-us/data-center/h200)
277
- - **B200 specs**: [nvidia.com/en-us/data-center/blackwell-architecture](https://www.nvidia.com/en-us/data-center/blackwell-architecture) (FP4/FP8 only; FP16 dense not published)
278
- - **Llama 3.3 70B config**: [huggingface.co/meta-llama/Llama-3.3-70B-Instruct](https://huggingface.co/meta-llama/Llama-3.3-70B-Instruct)
279
- - **FP8 on H100**: "FP8 Quantization: The Power of the Tensor Float 32 Data Format", NVIDIA, 2024.
280
-
281
- ---
282
-
283
- *tokcalc is open-source (Apache 2.0). If you found this analysis useful, [star the repo](https://github.com/stevecrates489-commits/tokcalc) and share it with someone planning an LLM deployment.*