@fugood/bricks-ctor 2.25.0-beta.60 → 2.25.0-beta.62

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 (192) hide show
  1. package/package.json +4 -28
  2. package/tools/__tests__/legacy-forwarder.test.js +91 -0
  3. package/tools/_forward.ts +26 -0
  4. package/tools/deploy.ts +3 -175
  5. package/tools/mcp-server.ts +3 -35
  6. package/tools/postinstall.ts +3 -291
  7. package/tools/pull.ts +3 -198
  8. package/tools/push-config.ts +3 -113
  9. package/tools/simulator.ts +3 -149
  10. package/compile/__tests__/config-diff.test.js +0 -100
  11. package/compile/__tests__/index.test.js +0 -461
  12. package/compile/__tests__/util.test.js +0 -450
  13. package/compile/action-name-map.ts +0 -1079
  14. package/compile/config-diff.ts +0 -155
  15. package/compile/index.ts +0 -1594
  16. package/compile/util.ts +0 -482
  17. package/index.ts +0 -6
  18. package/skills/bricks-ctor/SKILL.md +0 -38
  19. package/skills/bricks-ctor/references/animation.md +0 -160
  20. package/skills/bricks-ctor/references/architecture-patterns.md +0 -88
  21. package/skills/bricks-ctor/references/automations.md +0 -232
  22. package/skills/bricks-ctor/references/buttress.md +0 -245
  23. package/skills/bricks-ctor/references/data-calculation.md +0 -252
  24. package/skills/bricks-ctor/references/local-sync.md +0 -129
  25. package/skills/bricks-ctor/references/media-flow.md +0 -165
  26. package/skills/bricks-ctor/references/remote-data-bank.md +0 -196
  27. package/skills/bricks-ctor/references/simulator.md +0 -132
  28. package/skills/bricks-ctor/references/source-editing-tools.md +0 -81
  29. package/skills/bricks-ctor/references/standby-transition.md +0 -124
  30. package/skills/bricks-ctor/references/verification-toolchain.md +0 -200
  31. package/skills/bricks-design/SKILL.md +0 -171
  32. package/skills/bricks-design/references/architecture-truths.md +0 -132
  33. package/skills/bricks-design/references/avoiding-complexity.md +0 -91
  34. package/skills/bricks-design/references/design-critique.md +0 -195
  35. package/skills/bricks-design/references/design-languages.md +0 -265
  36. package/skills/bricks-design/references/performance.md +0 -116
  37. package/skills/bricks-design/references/presentation-and-slideshow.md +0 -137
  38. package/skills/bricks-design/references/translating-inputs.md +0 -152
  39. package/skills/bricks-design/references/variations-and-tweaks.md +0 -124
  40. package/skills/bricks-design/references/when-the-brief-is-branded.md +0 -284
  41. package/skills/bricks-design/references/when-the-brief-is-vague.md +0 -85
  42. package/skills/bricks-design/references/workflow.md +0 -134
  43. package/skills/bricks-ux/SKILL.md +0 -114
  44. package/skills/bricks-ux/references/accessibility.md +0 -162
  45. package/skills/bricks-ux/references/flow-states.md +0 -175
  46. package/skills/bricks-ux/references/interaction-archetypes.md +0 -189
  47. package/skills/bricks-ux/references/monitoring-screens.md +0 -153
  48. package/skills/bricks-ux/references/pressable-composition.md +0 -126
  49. package/skills/bricks-ux/references/user-journey.md +0 -168
  50. package/skills/bricks-ux/references/ux-critique.md +0 -256
  51. package/skills/rive-marketplace/SKILL.md +0 -99
  52. package/tools/__tests__/_cli-error.test.ts +0 -35
  53. package/tools/__tests__/_mcp-config.test.ts +0 -67
  54. package/tools/__tests__/pull.test.ts +0 -108
  55. package/tools/_cli-error.ts +0 -17
  56. package/tools/_edits-log.ts +0 -41
  57. package/tools/_git-author.ts +0 -37
  58. package/tools/_last-pushed-commit.ts +0 -28
  59. package/tools/_mcp-config.ts +0 -42
  60. package/tools/_shell.ts +0 -180
  61. package/tools/icons/.gitattributes +0 -1
  62. package/tools/icons/fa6pro-glyphmap.json +0 -4686
  63. package/tools/icons/fa6pro-meta.json +0 -1
  64. package/tools/mcp-env.ts +0 -13
  65. package/tools/mcp-tools/__tests__/data-calc-editing.test.js +0 -516
  66. package/tools/mcp-tools/__tests__/entry-editing.test.js +0 -866
  67. package/tools/mcp-tools/__tests__/huggingface.test.ts +0 -49
  68. package/tools/mcp-tools/__tests__/icons.test.ts +0 -21
  69. package/tools/mcp-tools/__tests__/mcp-env.test.js +0 -19
  70. package/tools/mcp-tools/_editing-helpers.ts +0 -98
  71. package/tools/mcp-tools/_verify.ts +0 -50
  72. package/tools/mcp-tools/compile.ts +0 -104
  73. package/tools/mcp-tools/data-calc-editing.ts +0 -1311
  74. package/tools/mcp-tools/entry-editing.ts +0 -2297
  75. package/tools/mcp-tools/huggingface.ts +0 -772
  76. package/tools/mcp-tools/icons.ts +0 -97
  77. package/tools/mcp-tools/lottie.ts +0 -102
  78. package/tools/mcp-tools/media.ts +0 -113
  79. package/tools/simulator-main.mjs +0 -488
  80. package/tools/simulator-preload.cjs +0 -16
  81. package/types/animation.d.ts +0 -116
  82. package/types/automation.d.ts +0 -231
  83. package/types/brick-base.d.ts +0 -80
  84. package/types/bricks/Camera.d.ts +0 -246
  85. package/types/bricks/Chart.d.ts +0 -372
  86. package/types/bricks/GenerativeMedia.d.ts +0 -290
  87. package/types/bricks/Icon.d.ts +0 -98
  88. package/types/bricks/Image.d.ts +0 -126
  89. package/types/bricks/Items.d.ts +0 -480
  90. package/types/bricks/Lottie.d.ts +0 -168
  91. package/types/bricks/Maps.d.ts +0 -262
  92. package/types/bricks/QrCode.d.ts +0 -117
  93. package/types/bricks/Rect.d.ts +0 -150
  94. package/types/bricks/RichText.d.ts +0 -131
  95. package/types/bricks/Rive.d.ts +0 -220
  96. package/types/bricks/Scene3D.d.ts +0 -676
  97. package/types/bricks/Sketch.d.ts +0 -256
  98. package/types/bricks/Slideshow.d.ts +0 -201
  99. package/types/bricks/Svg.d.ts +0 -99
  100. package/types/bricks/Text.d.ts +0 -148
  101. package/types/bricks/TextInput.d.ts +0 -242
  102. package/types/bricks/Video.d.ts +0 -242
  103. package/types/bricks/VideoStreaming.d.ts +0 -112
  104. package/types/bricks/WebRtcStream.d.ts +0 -65
  105. package/types/bricks/WebView.d.ts +0 -168
  106. package/types/bricks/index.d.ts +0 -23
  107. package/types/canvas.d.ts +0 -82
  108. package/types/common.d.ts +0 -141
  109. package/types/data-calc-command/base.d.ts +0 -57
  110. package/types/data-calc-command/collection.d.ts +0 -418
  111. package/types/data-calc-command/color.d.ts +0 -432
  112. package/types/data-calc-command/constant.d.ts +0 -50
  113. package/types/data-calc-command/datetime.d.ts +0 -147
  114. package/types/data-calc-command/file.d.ts +0 -129
  115. package/types/data-calc-command/index.d.ts +0 -13
  116. package/types/data-calc-command/iteratee.d.ts +0 -23
  117. package/types/data-calc-command/logictype.d.ts +0 -190
  118. package/types/data-calc-command/math.d.ts +0 -275
  119. package/types/data-calc-command/object.d.ts +0 -119
  120. package/types/data-calc-command/sandbox.d.ts +0 -66
  121. package/types/data-calc-command/string.d.ts +0 -407
  122. package/types/data-calc-script.d.ts +0 -21
  123. package/types/data-calc.d.ts +0 -12
  124. package/types/data.d.ts +0 -97
  125. package/types/generators/AlarmClock.d.ts +0 -110
  126. package/types/generators/Assistant.d.ts +0 -640
  127. package/types/generators/BleCentral.d.ts +0 -247
  128. package/types/generators/BlePeripheral.d.ts +0 -208
  129. package/types/generators/CanvasMap.d.ts +0 -74
  130. package/types/generators/CastlesPay.d.ts +0 -87
  131. package/types/generators/DataBank.d.ts +0 -160
  132. package/types/generators/File.d.ts +0 -432
  133. package/types/generators/GraphQl.d.ts +0 -132
  134. package/types/generators/Http.d.ts +0 -222
  135. package/types/generators/HttpServer.d.ts +0 -230
  136. package/types/generators/Information.d.ts +0 -103
  137. package/types/generators/Intent.d.ts +0 -168
  138. package/types/generators/Iterator.d.ts +0 -108
  139. package/types/generators/Keyboard.d.ts +0 -105
  140. package/types/generators/LlmAnthropicCompat.d.ts +0 -212
  141. package/types/generators/LlmAppleBuiltin.d.ts +0 -159
  142. package/types/generators/LlmGgml.d.ts +0 -903
  143. package/types/generators/LlmMediaTekNeuroPilot.d.ts +0 -235
  144. package/types/generators/LlmMlx.d.ts +0 -228
  145. package/types/generators/LlmOnnx.d.ts +0 -213
  146. package/types/generators/LlmOpenAiCompat.d.ts +0 -312
  147. package/types/generators/LlmQualcommAiEngine.d.ts +0 -247
  148. package/types/generators/Mcp.d.ts +0 -637
  149. package/types/generators/McpServer.d.ts +0 -289
  150. package/types/generators/MediaFlow.d.ts +0 -170
  151. package/types/generators/MqttBroker.d.ts +0 -141
  152. package/types/generators/MqttClient.d.ts +0 -141
  153. package/types/generators/Question.d.ts +0 -408
  154. package/types/generators/RealtimeTranscription.d.ts +0 -287
  155. package/types/generators/RerankerGgml.d.ts +0 -195
  156. package/types/generators/SerialPort.d.ts +0 -151
  157. package/types/generators/SoundPlayer.d.ts +0 -94
  158. package/types/generators/SoundRecorder.d.ts +0 -139
  159. package/types/generators/SpeechToTextGgml.d.ts +0 -424
  160. package/types/generators/SpeechToTextOnnx.d.ts +0 -236
  161. package/types/generators/SpeechToTextPlatform.d.ts +0 -85
  162. package/types/generators/SqLite.d.ts +0 -159
  163. package/types/generators/Step.d.ts +0 -107
  164. package/types/generators/SttAppleBuiltin.d.ts +0 -153
  165. package/types/generators/Tcp.d.ts +0 -126
  166. package/types/generators/TcpServer.d.ts +0 -147
  167. package/types/generators/TextToSpeechAppleBuiltin.d.ts +0 -127
  168. package/types/generators/TextToSpeechGgml.d.ts +0 -221
  169. package/types/generators/TextToSpeechOnnx.d.ts +0 -178
  170. package/types/generators/TextToSpeechOpenAiLike.d.ts +0 -121
  171. package/types/generators/ThermalPrinter.d.ts +0 -193
  172. package/types/generators/Tick.d.ts +0 -83
  173. package/types/generators/Udp.d.ts +0 -120
  174. package/types/generators/VadGgml.d.ts +0 -260
  175. package/types/generators/VadOnnx.d.ts +0 -231
  176. package/types/generators/VadTraditional.d.ts +0 -138
  177. package/types/generators/VectorStore.d.ts +0 -257
  178. package/types/generators/Watchdog.d.ts +0 -107
  179. package/types/generators/WebCrawler.d.ts +0 -103
  180. package/types/generators/WebRtc.d.ts +0 -181
  181. package/types/generators/WebSocket.d.ts +0 -148
  182. package/types/generators/index.d.ts +0 -57
  183. package/types/index.d.ts +0 -13
  184. package/types/subspace.d.ts +0 -60
  185. package/types/switch.d.ts +0 -51
  186. package/types/system.d.ts +0 -707
  187. package/utils/__tests__/calc.test.js +0 -25
  188. package/utils/__tests__/id.test.js +0 -154
  189. package/utils/calc.ts +0 -130
  190. package/utils/data.ts +0 -495
  191. package/utils/event-props.ts +0 -912
  192. package/utils/id.ts +0 -133
@@ -1,772 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
- import { z } from 'zod'
3
- import JSON5 from 'json5'
4
- import * as TOON from '@toon-format/toon'
5
- import { gguf } from '@huggingface/gguf'
6
-
7
- // Hugging Face API configuration
8
- const HF_API_URL = 'https://huggingface.co/api'
9
- const { HF_TOKEN } = process.env
10
-
11
- // Helper function to convert BigInt to number for JSON serialization
12
- const convertBigIntToNumber = (value: unknown): number | unknown => {
13
- if (typeof value === 'bigint') {
14
- return Number(value)
15
- }
16
- return value
17
- }
18
-
19
- // Extract GGUF metadata from a GGUF file URL with 10s timeout
20
- const extractGGUFMetadata = async (url: string) => {
21
- const timeoutPromise = new Promise<never>((_, reject) =>
22
- setTimeout(() => reject(new Error('GGUF metadata extraction timeout')), 10000),
23
- )
24
-
25
- try {
26
- const { metadata } = (await Promise.race([gguf(url), timeoutPromise])) as { metadata: any }
27
- const architecture = metadata['general.architecture']
28
-
29
- const ggufSimplifiedMetadata: Record<string, unknown> = {
30
- name: metadata['general.name'],
31
- size_label: metadata['general.size_label'],
32
- basename: metadata['general.basename'],
33
- architecture: metadata['general.architecture'],
34
- file_type: convertBigIntToNumber(metadata['general.file_type']),
35
- quantization_version: convertBigIntToNumber(metadata['general.quantization_version']),
36
- }
37
-
38
- if (!architecture) return ggufSimplifiedMetadata
39
-
40
- // Helper to add converted value if defined
41
- const addIfDefined = (target: Record<string, unknown>, key: string, value: unknown) => {
42
- if (value !== undefined) target[key] = convertBigIntToNumber(value)
43
- }
44
-
45
- // Architecture-specific transformer parameters
46
- addIfDefined(ggufSimplifiedMetadata, 'n_ctx_train', metadata[`${architecture}.context_length`])
47
- addIfDefined(ggufSimplifiedMetadata, 'n_layer', metadata[`${architecture}.block_count`])
48
- addIfDefined(ggufSimplifiedMetadata, 'n_embd', metadata[`${architecture}.embedding_length`])
49
- addIfDefined(ggufSimplifiedMetadata, 'n_head', metadata[`${architecture}.attention.head_count`])
50
- addIfDefined(
51
- ggufSimplifiedMetadata,
52
- 'n_head_kv',
53
- metadata[`${architecture}.attention.head_count_kv`],
54
- )
55
- addIfDefined(
56
- ggufSimplifiedMetadata,
57
- 'n_embd_head_k',
58
- metadata[`${architecture}.attention.key_length`],
59
- )
60
- addIfDefined(
61
- ggufSimplifiedMetadata,
62
- 'n_embd_head_v',
63
- metadata[`${architecture}.attention.value_length`],
64
- )
65
- addIfDefined(
66
- ggufSimplifiedMetadata,
67
- 'n_swa',
68
- metadata[`${architecture}.attention.sliding_window`],
69
- )
70
-
71
- // SSM (Mamba) parameters for recurrent/hybrid models
72
- addIfDefined(ggufSimplifiedMetadata, 'ssm_d_conv', metadata[`${architecture}.ssm.conv_kernel`])
73
- addIfDefined(ggufSimplifiedMetadata, 'ssm_d_state', metadata[`${architecture}.ssm.state_size`])
74
- addIfDefined(ggufSimplifiedMetadata, 'ssm_d_inner', metadata[`${architecture}.ssm.inner_size`])
75
- addIfDefined(ggufSimplifiedMetadata, 'ssm_n_group', metadata[`${architecture}.ssm.group_count`])
76
- addIfDefined(
77
- ggufSimplifiedMetadata,
78
- 'ssm_dt_rank',
79
- metadata[`${architecture}.ssm.time_step_rank`],
80
- )
81
-
82
- // RWKV parameters
83
- addIfDefined(
84
- ggufSimplifiedMetadata,
85
- 'rwkv_head_size',
86
- metadata[`${architecture}.rwkv.head_size`],
87
- )
88
- addIfDefined(
89
- ggufSimplifiedMetadata,
90
- 'rwkv_token_shift_count',
91
- metadata[`${architecture}.rwkv.token_shift_count`],
92
- )
93
-
94
- return ggufSimplifiedMetadata
95
- } catch (error) {
96
- console.error('Failed to extract GGUF metadata:', error)
97
- return null
98
- }
99
- }
100
-
101
- type HFSibling = {
102
- rfilename: string
103
- size?: number
104
- lfs?: { sha256?: string }
105
- blobId?: string
106
- }
107
-
108
- type HFModel = {
109
- id: string
110
- author?: string
111
- downloads?: number
112
- likes?: number
113
- tags?: string[]
114
- pipeline_tag?: string
115
- siblings?: HFSibling[]
116
- config?: { model_type?: string }
117
- cardData?: { model_type?: string }
118
- }
119
-
120
- const supportedLlmTasks = [
121
- 'text-generation',
122
- 'image-text-to-text',
123
- 'text2text-generation',
124
- 'conversational',
125
- ]
126
-
127
- type GeneratorType =
128
- | 'GeneratorLLM'
129
- | 'GeneratorVectorStore'
130
- | 'GeneratorReranker'
131
- | 'GeneratorGGMLTTS'
132
- | 'GeneratorGGMLTTSVocoder'
133
- | 'GeneratorOnnxLLM'
134
- | 'GeneratorOnnxSTT'
135
- | 'GeneratorTTS'
136
- | 'GeneratorMlxLLM'
137
-
138
- type ModelKind = 'gguf' | 'onnx' | 'mlx'
139
-
140
- interface GeneratorConfig {
141
- modelKind: ModelKind
142
- filter: string
143
- taskFilter?: string[]
144
- filePattern?: RegExp
145
- hasValidStructure?: (siblings: HFSibling[]) => boolean
146
- }
147
-
148
- // Helper to check valid ONNX structure
149
- const hasValidOnnxStructure = (siblings: HFSibling[]): boolean => {
150
- const hasConfigJson = siblings.some((file) => file.rfilename === 'config.json')
151
- const hasOnnxModel = siblings.some(
152
- (file) => file.rfilename.includes('onnx/') && file.rfilename.endsWith('.onnx'),
153
- )
154
- return hasConfigJson && hasOnnxModel
155
- }
156
-
157
- // Detect quantization types from ONNX files
158
- const detectOnnxQuantizationTypes = (siblings: HFSibling[]): string[] => {
159
- const onnxFiles = siblings.filter((file) => file.rfilename.endsWith('.onnx'))
160
- const quantTypes = new Set<string>()
161
-
162
- onnxFiles.forEach((file) => {
163
- const filename = file.rfilename
164
- if (!filename.endsWith('.onnx')) return
165
- const postfix = /_(q8|q4|q4f16|fp16|int8|int4|uint8|bnb4|quantized)\.onnx$/.exec(filename)?.[1]
166
- if (!postfix) {
167
- quantTypes.add('auto')
168
- quantTypes.add('none')
169
- } else {
170
- quantTypes.add(postfix === 'quantized' ? 'q8' : postfix)
171
- }
172
- })
173
-
174
- return Array.from(quantTypes)
175
- }
176
-
177
- // Find speaker embedding files for TTS models
178
- const findSpeakerEmbedFiles = (siblings: HFSibling[]): HFSibling[] =>
179
- siblings.filter((file) => file.rfilename.startsWith('voices/') && file.rfilename.endsWith('.bin'))
180
-
181
- const generatorConfigs: Record<GeneratorType, GeneratorConfig> = {
182
- GeneratorLLM: {
183
- modelKind: 'gguf',
184
- filter: 'gguf',
185
- taskFilter: supportedLlmTasks,
186
- filePattern: /\.gguf$/,
187
- },
188
- GeneratorVectorStore: {
189
- modelKind: 'gguf',
190
- filter: 'gguf',
191
- filePattern: /\.gguf$/,
192
- },
193
- GeneratorReranker: {
194
- modelKind: 'gguf',
195
- filter: 'gguf,reranker',
196
- filePattern: /\.gguf$/,
197
- },
198
- GeneratorGGMLTTS: {
199
- modelKind: 'gguf',
200
- filter: 'gguf,text-to-speech',
201
- filePattern: /\.gguf$/,
202
- },
203
- GeneratorGGMLTTSVocoder: {
204
- modelKind: 'gguf',
205
- filter: 'gguf,feature-extraction',
206
- filePattern: /\.gguf$/,
207
- },
208
- GeneratorOnnxLLM: {
209
- modelKind: 'onnx',
210
- filter: 'onnx',
211
- taskFilter: supportedLlmTasks,
212
- hasValidStructure: hasValidOnnxStructure,
213
- },
214
- GeneratorOnnxSTT: {
215
- modelKind: 'onnx',
216
- filter: 'onnx,automatic-speech-recognition',
217
- hasValidStructure: hasValidOnnxStructure,
218
- },
219
- GeneratorTTS: {
220
- modelKind: 'onnx',
221
- filter: 'onnx,text-to-speech',
222
- hasValidStructure: hasValidOnnxStructure,
223
- },
224
- GeneratorMlxLLM: {
225
- modelKind: 'mlx',
226
- filter: 'mlx',
227
- taskFilter: supportedLlmTasks,
228
- },
229
- }
230
-
231
- const searchHFModels = async (filter: string, search?: string, limit = 50): Promise<HFModel[]> => {
232
- const params = new URLSearchParams({
233
- limit: String(limit),
234
- full: 'true',
235
- config: 'true',
236
- sort: 'likes',
237
- direction: '-1',
238
- })
239
- if (filter) params.set('filter', filter)
240
- if (search) params.set('search', search)
241
-
242
- const headers: Record<string, string> = {}
243
- if (HF_TOKEN) {
244
- headers['Authorization'] = `Bearer ${HF_TOKEN}`
245
- }
246
-
247
- const response = await fetch(`${HF_API_URL}/models?${params.toString()}`, { headers })
248
- if (!response.ok) {
249
- throw new Error(`Hugging Face API error: ${response.status} ${response.statusText}`)
250
- }
251
- return response.json()
252
- }
253
-
254
- const fetchHFModelDetails = async (modelId: string): Promise<HFModel> => {
255
- const params = new URLSearchParams({ blobs: 'true' })
256
-
257
- const headers: Record<string, string> = {}
258
- if (HF_TOKEN) {
259
- headers['Authorization'] = `Bearer ${HF_TOKEN}`
260
- }
261
-
262
- const response = await fetch(`${HF_API_URL}/models/${modelId}?${params.toString()}`, { headers })
263
- if (!response.ok) {
264
- throw new Error(`Hugging Face API error: ${response.status} ${response.statusText}`)
265
- }
266
- return response.json()
267
- }
268
-
269
- // Example: Mixtral-8x22B-v0.1.IQ3_XS-00001-of-00005.gguf
270
- const ggufSplitPattern = /-(\d{5})-of-(\d{5})\.gguf$/
271
-
272
- export const buildGGUFSplitFiles = (
273
- filename: string,
274
- splitTotal: string,
275
- siblings: HFSibling[],
276
- ): HFSibling[] => {
277
- const siblingByFilename = new Map<string, HFSibling>()
278
- for (const sibling of siblings) {
279
- if (!siblingByFilename.has(sibling.rfilename)) siblingByFilename.set(sibling.rfilename, sibling)
280
- }
281
-
282
- return Array.from({ length: Number(splitTotal) }, (_, i) => {
283
- const split = String(i + 1).padStart(5, '0')
284
- const splitRFilename = filename.replace(ggufSplitPattern, `-${split}-of-${splitTotal}.gguf`)
285
- const sibling = siblingByFilename.get(splitRFilename)
286
- return {
287
- rfilename: splitRFilename,
288
- size: sibling?.size,
289
- lfs: sibling?.lfs,
290
- }
291
- })
292
- }
293
-
294
- export function register(server: McpServer) {
295
- server.tool(
296
- 'huggingface_search',
297
- {
298
- generatorType: z
299
- .enum([
300
- 'GeneratorLLM',
301
- 'GeneratorVectorStore',
302
- 'GeneratorReranker',
303
- 'GeneratorGGMLTTS',
304
- 'GeneratorGGMLTTSVocoder',
305
- 'GeneratorOnnxLLM',
306
- 'GeneratorOnnxSTT',
307
- 'GeneratorTTS',
308
- 'GeneratorMlxLLM',
309
- ])
310
- .describe('Generator type to search models for')
311
- .default('GeneratorLLM'),
312
- query: z.string().describe('Search keywords for models on Hugging Face').optional(),
313
- limit: z.number().min(1).max(100).optional().default(20),
314
- includeFiles: z
315
- .boolean()
316
- .optional()
317
- .default(false)
318
- .describe('Include list of model files (requires additional API calls)'),
319
- },
320
- async ({ generatorType, query, limit, includeFiles }) => {
321
- try {
322
- const config = generatorConfigs[generatorType]
323
- const models = await searchHFModels(config.filter, query, limit)
324
-
325
- // Filter models based on generator configuration
326
- const filteredModels = models.filter((model) => {
327
- const modelTags = model.tags || []
328
-
329
- // Check task filter if configured
330
- if (config.taskFilter && !config.taskFilter.some((t) => modelTags.includes(t))) {
331
- return false
332
- }
333
-
334
- // Check structure validation for ONNX models
335
- if (config.hasValidStructure && model.siblings) {
336
- if (!config.hasValidStructure(model.siblings)) {
337
- return false
338
- }
339
- }
340
-
341
- return true
342
- })
343
-
344
- // Build result models
345
- let results: Array<{
346
- id: string
347
- author?: string
348
- downloads?: number
349
- likes?: number
350
- pipeline_tag?: string
351
- model_type?: string
352
- model_kind: ModelKind
353
- files?: Array<{ filename: string; size?: number }>
354
- quantization_types?: string[]
355
- speaker_embed_files?: Array<{ filename: string; size?: number }>
356
- }> = filteredModels.map((model) => ({
357
- id: model.id,
358
- author: model.author,
359
- downloads: model.downloads,
360
- likes: model.likes,
361
- pipeline_tag: model.pipeline_tag,
362
- model_type: model.config?.model_type || model.cardData?.model_type,
363
- model_kind: config.modelKind,
364
- }))
365
-
366
- if (includeFiles) {
367
- results = await Promise.all(
368
- results.map(async (model) => {
369
- try {
370
- const details = await fetchHFModelDetails(model.id)
371
- const siblings = details.siblings || []
372
-
373
- if (config.modelKind === 'gguf') {
374
- const ggufFiles = siblings
375
- .filter((file) => config.filePattern?.test(file.rfilename))
376
- .map((file) => ({
377
- filename: file.rfilename,
378
- size: file.size,
379
- }))
380
- return { ...model, files: ggufFiles }
381
- } else {
382
- // ONNX models
383
- const quantTypes = detectOnnxQuantizationTypes(siblings)
384
- const speakerFiles =
385
- generatorType === 'GeneratorTTS' ? findSpeakerEmbedFiles(siblings) : []
386
-
387
- return {
388
- ...model,
389
- quantization_types: quantTypes,
390
- ...(speakerFiles.length > 0 && {
391
- speaker_embed_files: speakerFiles.map((f) => ({
392
- filename: f.rfilename,
393
- size: f.size,
394
- })),
395
- }),
396
- }
397
- }
398
- } catch {
399
- return model
400
- }
401
- }),
402
- )
403
- }
404
-
405
- return {
406
- content: [
407
- {
408
- type: 'text',
409
- text: TOON.encode({
410
- count: results.length,
411
- generatorType,
412
- modelKind: config.modelKind,
413
- models: results,
414
- hf_token_configured: !!HF_TOKEN,
415
- }),
416
- },
417
- ],
418
- }
419
- } catch (err: any) {
420
- return {
421
- content: [{ type: 'text', text: `Failed to search models: ${err.message}` }],
422
- }
423
- }
424
- },
425
- )
426
-
427
- server.tool(
428
- 'huggingface_select',
429
- {
430
- generatorType: z
431
- .enum([
432
- 'GeneratorLLM',
433
- 'GeneratorVectorStore',
434
- 'GeneratorReranker',
435
- 'GeneratorGGMLTTS',
436
- 'GeneratorGGMLTTSVocoder',
437
- 'GeneratorOnnxLLM',
438
- 'GeneratorOnnxSTT',
439
- 'GeneratorTTS',
440
- 'GeneratorMlxLLM',
441
- ])
442
- .describe('Generator type for model selection')
443
- .default('GeneratorLLM'),
444
- // eslint-disable-next-line camelcase
445
- model_id: z
446
- .string()
447
- .describe('Hugging Face model ID (e.g., "unsloth/Llama-3.2-1B-Instruct-GGUF")'),
448
- filename: z
449
- .string()
450
- .describe('Model filename to select (required for GGUF models)')
451
- .optional(),
452
- quantize_type: z
453
- .string()
454
- .describe('Quantization type for ONNX models (e.g., "q8", "fp16", "auto")')
455
- .optional()
456
- .default('auto'),
457
- speaker_embed_file: z.string().describe('Speaker embedding file for TTS models').optional(),
458
- },
459
- // eslint-disable-next-line camelcase
460
- async ({
461
- generatorType,
462
- model_id: modelId,
463
- filename,
464
- quantize_type: quantizeType,
465
- speaker_embed_file: speakerEmbedFile,
466
- }) => {
467
- try {
468
- const config = generatorConfigs[generatorType]
469
- const details = await fetchHFModelDetails(modelId)
470
- const siblings = details.siblings || []
471
-
472
- // Handle ONNX models
473
- // ONNX generators expect: model (HF model ID), modelType, quantizeType
474
- if (config.modelKind === 'onnx') {
475
- const quantTypes = detectOnnxQuantizationTypes(siblings)
476
- const speakerFiles =
477
- generatorType === 'GeneratorTTS' ? findSpeakerEmbedFiles(siblings) : []
478
- const selectedSpeakerFile = speakerEmbedFile
479
- ? siblings.find((f) => f.rfilename === speakerEmbedFile)
480
- : undefined
481
-
482
- const selectedQuantType = quantTypes.includes(quantizeType || 'auto')
483
- ? quantizeType
484
- : 'auto'
485
- const modelType = details.config?.model_type || details.cardData?.model_type
486
-
487
- // Result format matches ONNX generator property names (camelCase)
488
- const result = {
489
- // Primary model fields for generator
490
- model: modelId,
491
- modelType,
492
- quantizeType: selectedQuantType,
493
- // Speaker embedding for TTS generators
494
- ...(selectedSpeakerFile && {
495
- speakerEmbedUrl: `https://huggingface.co/${modelId}/resolve/main/${selectedSpeakerFile.rfilename}?download=true`,
496
- speakerEmbedHash: selectedSpeakerFile.lfs?.sha256,
497
- speakerEmbedHashType: 'sha256',
498
- }),
499
- // Additional info
500
- availableQuantizeTypes: quantTypes,
501
- ...(speakerFiles.length > 0 && {
502
- availableSpeakerEmbedFiles: speakerFiles.map((f) => f.rfilename),
503
- }),
504
- _hfRepoInfo: {
505
- repo: modelId,
506
- model: {
507
- id: details.id,
508
- downloads: details.downloads,
509
- likes: details.likes,
510
- author: details.author,
511
- },
512
- generatorType,
513
- modelType,
514
- quantizeType: selectedQuantType,
515
- ...(selectedSpeakerFile && {
516
- speakerEmbedFile: selectedSpeakerFile.rfilename,
517
- }),
518
- },
519
- }
520
-
521
- // Return JSON for huggingface_select to allow direct parsing by consumers
522
- return {
523
- content: [
524
- {
525
- type: 'text',
526
- text: `${JSON5.stringify(result, null, 2)} // Please use _hfRepoInfo undocumented field with @ts-ignore`,
527
- },
528
- ],
529
- }
530
- }
531
-
532
- // Handle MLX models (repo-level, like ONNX)
533
- if (config.modelKind === 'mlx') {
534
- // Fetch config.json for model architecture metadata
535
- const headers: Record<string, string> = {}
536
- if (HF_TOKEN) headers['Authorization'] = `Bearer ${HF_TOKEN}`
537
- let mlxModelConfig: Record<string, any> | null = null
538
- try {
539
- const configRes = await fetch(
540
- `https://huggingface.co/${modelId}/resolve/main/config.json`,
541
- { headers },
542
- )
543
- if (configRes.ok) mlxModelConfig = await configRes.json()
544
- } catch {
545
- // Non-critical
546
- }
547
-
548
- const modelType =
549
- mlxModelConfig?.model_type || details.config?.model_type || details.cardData?.model_type
550
-
551
- // Build MLX model metadata from config.json (for hardware guardrails)
552
- const cfg = mlxModelConfig || ({} as Record<string, any>)
553
- const textCfg = cfg.text_config || cfg
554
- const numHeads = textCfg.num_attention_heads || textCfg.n_heads || 0
555
- const hiddenSize = textCfg.hidden_size || textCfg.dim || 0
556
- const kvLoraRank = textCfg.kv_lora_rank || 0
557
- const quant = cfg.quantization || cfg.quantization_config || null
558
-
559
- // Sum safetensors/npz file sizes for model weight bytes
560
- const modelBytes = siblings
561
- .filter((f) => /\.(safetensors|npz)$/.test(f.rfilename))
562
- .reduce((sum, f) => sum + (f.size ?? 0), 0)
563
-
564
- // Build _mlxDownloadFiles list (safetensors, json, jinja, tokenizer.model)
565
- const mlxDownloadFiles = siblings
566
- .filter(
567
- (f) =>
568
- f.rfilename.endsWith('.safetensors') ||
569
- f.rfilename.endsWith('.json') ||
570
- f.rfilename.endsWith('.jinja') ||
571
- f.rfilename === 'tokenizer.model',
572
- )
573
- .map((f) => ({
574
- url: `https://huggingface.co/${modelId}/resolve/main/${f.rfilename}?download=true`,
575
- filename: `${modelId.replace('/', '-')}/${f.rfilename}`,
576
- hash_type: f.lfs ? 'sha256' : f.blobId ? 'sha1' : undefined,
577
- sha256: f.lfs?.sha256,
578
- sha1: f.lfs ? undefined : f.blobId,
579
- }))
580
-
581
- const result = {
582
- modelId,
583
- modelType,
584
- _mlxDownloadFiles: mlxDownloadFiles,
585
- _hfRepoInfo: {
586
- repo: modelId,
587
- model: {
588
- id: details.id,
589
- downloads: details.downloads,
590
- likes: details.likes,
591
- author: details.author,
592
- },
593
- generatorType,
594
- modelType,
595
- ...(modelBytes > 0 && { size: modelBytes }),
596
- ...(mlxModelConfig && {
597
- mlxModelMetadata: {
598
- hiddenSize,
599
- numLayers: textCfg.num_hidden_layers || textCfg.n_layers || 0,
600
- numHeads,
601
- numKvHeads: textCfg.num_key_value_heads ?? numHeads,
602
- headDim:
603
- textCfg.head_dim ||
604
- textCfg.v_head_dim ||
605
- (numHeads > 0 && hiddenSize > 0 ? hiddenSize / numHeads : 0),
606
- maxCtx: textCfg.max_position_embeddings || 0,
607
- kvLoraRank,
608
- qkRopeHeadDim: textCfg.qk_rope_head_dim || 0,
609
- isMLA: kvLoraRank > 0,
610
- quantBits: quant?.bits || null,
611
- quantGroupSize: quant?.group_size || null,
612
- },
613
- }),
614
- },
615
- }
616
-
617
- return {
618
- content: [
619
- {
620
- type: 'text',
621
- text: `${JSON5.stringify(result, null, 2)} // IMPORTANT: Use _mlxDownloadFiles / _hfRepoInfo undocumented fields with @ts-ignore`,
622
- },
623
- ],
624
- }
625
- }
626
-
627
- // Handle GGUF models
628
- if (!filename) {
629
- // List available GGUF files
630
- const ggufFiles = siblings
631
- .filter((file) => config.filePattern?.test(file.rfilename))
632
- .map((file) => ({
633
- filename: file.rfilename,
634
- size: file.size,
635
- }))
636
-
637
- // Return JSON for huggingface_select to allow direct parsing by consumers
638
- return {
639
- content: [
640
- {
641
- type: 'text',
642
- text: JSON.stringify(
643
- {
644
- error: 'filename is required for GGUF models',
645
- available_files: ggufFiles,
646
- },
647
- null,
648
- 2,
649
- ),
650
- },
651
- ],
652
- }
653
- }
654
-
655
- // Find the selected file
656
- const selectedFile = siblings.find((f) => f.rfilename === filename)
657
- if (!selectedFile) {
658
- return {
659
- content: [{ type: 'text', text: `File "${filename}" not found in model ${modelId}` }],
660
- }
661
- }
662
-
663
- // Check if it's a split file
664
- const matched = filename.match(ggufSplitPattern)
665
- const isSplit = !!matched
666
-
667
- // Find mmproj file if available (only for LLM generators)
668
- const mmprojFile =
669
- generatorType === 'GeneratorLLM'
670
- ? siblings.find((f) => /^mmproj-/i.test(f.rfilename))
671
- : undefined
672
-
673
- // Extract GGUF metadata (for split files, metadata is in the first split)
674
- const metadataFilename = isSplit
675
- ? filename.replace(ggufSplitPattern, '-00001-of-$2.gguf')
676
- : filename
677
- const ggufUrl = `https://huggingface.co/${modelId}/resolve/main/${metadataFilename}`
678
- const ggufSimplifiedMetadata = await extractGGUFMetadata(ggufUrl)
679
-
680
- if (isSplit) {
681
- const [, , splitTotal] = matched!
682
- const splitFiles = buildGGUFSplitFiles(filename, splitTotal, siblings)
683
-
684
- const first = splitFiles[0]
685
- const rest = splitFiles.slice(1)
686
-
687
- const result = {
688
- url: `https://huggingface.co/${modelId}/resolve/main/${first.rfilename}?download=true`,
689
- hash: first.lfs?.sha256,
690
- hash_type: 'sha256',
691
- _ggufSplitFiles: rest.map((split) => ({
692
- url: `https://huggingface.co/${modelId}/resolve/main/${split.rfilename}?download=true`,
693
- hash: split.lfs?.sha256,
694
- hash_type: 'sha256',
695
- })),
696
- ...(mmprojFile && {
697
- mmproj_url: `https://huggingface.co/${modelId}/resolve/main/${mmprojFile.rfilename}?download=true`,
698
- mmproj_hash: mmprojFile.lfs?.sha256,
699
- mmproj_hash_type: 'sha256',
700
- }),
701
- _hfRepoInfo: {
702
- repo: modelId,
703
- model: {
704
- id: details.id,
705
- downloads: details.downloads,
706
- likes: details.likes,
707
- author: details.author,
708
- },
709
- generatorType,
710
- isSplit: true,
711
- files: splitFiles.map((f) => f.rfilename),
712
- sizes: splitFiles.map((f) => f.size),
713
- size: splitFiles.reduce((acc, f) => acc + (f.size ?? 0), 0),
714
- ...(mmprojFile && {
715
- mmprojFile: mmprojFile.rfilename,
716
- mmprojSize: mmprojFile.size,
717
- }),
718
- ...(ggufSimplifiedMetadata && { ggufSimplifiedMetadata }),
719
- },
720
- }
721
-
722
- // Return JSON for huggingface_select to allow direct parsing by consumers
723
- return {
724
- content: [{ type: 'text', text: JSON5.stringify(result, null, 2) }],
725
- }
726
- } else {
727
- const result = {
728
- url: `https://huggingface.co/${modelId}/resolve/main/${filename}?download=true`,
729
- hash: selectedFile.lfs?.sha256,
730
- hash_type: 'sha256',
731
- ...(mmprojFile && {
732
- mmproj_url: `https://huggingface.co/${modelId}/resolve/main/${mmprojFile.rfilename}?download=true`,
733
- mmproj_hash: mmprojFile.lfs?.sha256,
734
- mmproj_hash_type: 'sha256',
735
- }),
736
- _hfRepoInfo: {
737
- repo: modelId,
738
- model: {
739
- id: details.id,
740
- downloads: details.downloads,
741
- likes: details.likes,
742
- author: details.author,
743
- },
744
- generatorType,
745
- file: filename,
746
- size: selectedFile.size,
747
- ...(mmprojFile && {
748
- mmprojFile: mmprojFile.rfilename,
749
- mmprojSize: mmprojFile.size,
750
- }),
751
- ...(ggufSimplifiedMetadata && { ggufSimplifiedMetadata }),
752
- },
753
- }
754
-
755
- // Return JSON for huggingface_select to allow direct parsing by consumers
756
- return {
757
- content: [
758
- {
759
- type: 'text',
760
- text: `${JSON5.stringify(result, null, 2)} // IMPORTANT: Use _hfRepoInfo undocumented field with @ts-ignore`,
761
- },
762
- ],
763
- }
764
- }
765
- } catch (err: any) {
766
- return {
767
- content: [{ type: 'text', text: `Failed to select model: ${err.message}` }],
768
- }
769
- }
770
- },
771
- )
772
- }