From d1295b820a6c914b5b83da357773025450c44890 Mon Sep 17 00:00:00 2001 From: xu16601526267 <264125260+xu16601526267@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:45:58 +0800 Subject: [PATCH] feat(catalog): add GLM-5.3 Flash dual-Spark deployment --- catalog/engines/vllm-glm53-dual-gb10.yaml | 162 ++++++++++++++++++ catalog/models/glm-5.3-flash-nvfp4.yaml | 76 ++++++++ .../scenarios/glm-5.3-flash-nvfp4-2node.yaml | 112 ++++++++++++ internal/knowledge/loader_test.go | 96 +++++++++++ 4 files changed, 446 insertions(+) create mode 100644 catalog/engines/vllm-glm53-dual-gb10.yaml create mode 100644 catalog/models/glm-5.3-flash-nvfp4.yaml create mode 100644 catalog/scenarios/glm-5.3-flash-nvfp4-2node.yaml diff --git a/catalog/engines/vllm-glm53-dual-gb10.yaml b/catalog/engines/vllm-glm53-dual-gb10.yaml new file mode 100644 index 0000000..d928816 --- /dev/null +++ b/catalog/engines/vllm-glm53-dual-gb10.yaml @@ -0,0 +1,162 @@ +kind: engine_asset +_profile: vllm +metadata: + name: vllm-glm53-dual-gb10 + type: vllm + version: "0.1.dev20051-sm121-v8-cache-safety" + supported_formats: [safetensors] + supported_model_types: [llm, vlm] +hardware: + gpu_arch: Blackwell + vram_min_mib: 4096 +image: + # Local distribution is intentional: the image contains the GLM-5.3 SM121, + # FP8-KV, Kpool prefix-cache, and block-table safety patch stack. Install the + # same image on both Fleet devices before applying the two-node scenario. + name: aima/glm53-flash + tag: sm121-v8-local + size_approx_mb: 28200 + distribution: local + platforms: [linux/arm64] +patterns: + - "^aima/glm53-flash:sm121-v8-local$" + - "glm53-flash-arm64-cu130" +container: + devices: + - /dev/infiniband + network_mode: host + shm_size: 64gb + ulimits: + memlock: "-1:-1" + stack: "67108864:67108864" +startup: + env: + HF_HUB_OFFLINE: "1" + TRANSFORMERS_OFFLINE: "1" + PYTORCH_CUDA_ALLOC_CONF: expandable_segments:True + NCCL_NET: IB + NCCL_IB_DISABLE: "0" + NCCL_IB_ADDR_FAMILY: AF_INET + NCCL_IB_ROCE_VERSION_NUM: "2" + NCCL_IB_MERGE_NICS: "1" + NCCL_CROSS_NIC: "0" + NCCL_CUMEM_ENABLE: "0" + NCCL_IGNORE_CPU_AFFINITY: "1" + NCCL_NVLS_ENABLE: "0" + NCCL_DEBUG: WARN + accepted_config_keys: + - host + - port + - served_model_name + - trust_remote_code + - dtype + - tensor_parallel_size + - pipeline_parallel_size + - distributed_executor_backend + - nnodes + - node_rank + - master_addr + - master_port + - headless + - max_model_len + - max_num_seqs + - max_num_batched_tokens + - gpu_memory_utilization + - kv_cache_dtype + - kv_cache_memory + - block_size + - prefix_match_unit + - moe_backend + - enforce_eager + - enable_prefix_caching + - enable_prompt_tokens_details + - enable_chunked_prefill + - enable_auto_tool_choice + - tool_call_parser + - reasoning_parser + - chat_template + - default_chat_template_kwargs + - limit_mm_per_prompt + - generation_config + - speculative_config + default_args: + host: 0.0.0.0 + port: 8000 + served_model_name: GLM-5.3-Flash-NVFP4 + trust_remote_code: true + dtype: auto + tensor_parallel_size: 2 + pipeline_parallel_size: 1 + distributed_executor_backend: mp + nnodes: 2 + master_port: 29530 + max_model_len: 262144 + max_num_seqs: 2 + max_num_batched_tokens: 8192 + gpu_memory_utilization: 0.85 + kv_cache_dtype: fp8_e4m3 + kv_cache_memory: 4445787956 + block_size: 2304 + prefix_match_unit: 128 + moe_backend: marlin + enforce_eager: true + enable_prefix_caching: true + enable_prompt_tokens_details: true + enable_chunked_prefill: true + enable_auto_tool_choice: true + tool_call_parser: glm47 + reasoning_parser: glm45 + chat_template: /models/chat_template.jinja + default_chat_template_kwargs: + enable_thinking: true + limit_mm_per_prompt: + image: 4 + video: 1 + generation_config: vllm + speculative_config: + method: mtp + num_speculative_tokens: 4 + health_check: + path: /v1/models + timeout_s: 3600 + warmup: + enabled: true + prompt: "Return the word ready." + max_tokens: 8 + timeout_s: 180 + # A cold load takes roughly 15 minutes. Generic recovery must not replace a + # healthy loading process after live unified-memory fit changes. + recovery: + enabled: false +api: + protocol: openai + base_path: /v1 + upstream_model: GLM-5.3-Flash-NVFP4 +amplifier: + features: + - tensor_parallel_2 + - nvfp4 + - fp8_kv_cache + - mtp_4 + - prefix_caching + - fine_grained_prefix_matching_128 + - prompt_cache_usage_telemetry + - chunked_prefill + - multimodal + - tool_calling + - roce + performance_gain: "Two-node GB10 TP=2 with FP8 KV, MTP-4, and 128-token prefix matching for GLM-5.3 Flash NVFP4" + resource_expansion: + cpu_offload: false + ssd_offload: false + npu_offload: false + performance_multiplier: 1.52 + extends_resource_boundary: true + effective_vram_multiplier: 2.0 +time_constraints: + cold_start_s: [600, 1200] + model_switch_s: [600, 1200] +runtime: + default: container + platform_recommendations: + linux/arm64: container diff --git a/catalog/models/glm-5.3-flash-nvfp4.yaml b/catalog/models/glm-5.3-flash-nvfp4.yaml new file mode 100644 index 0000000..65aa609 --- /dev/null +++ b/catalog/models/glm-5.3-flash-nvfp4.yaml @@ -0,0 +1,76 @@ +kind: model_asset +metadata: + name: glm-5.3-flash-nvfp4 + type: vlm + family: glm + parameter_count: "320B" + released_at: "2026-08-26" + aliases: + - GLM-5.3-Flash-NVFP4 + - LibertAIDAI/GLM-5.3-Flash-NVFP4 +ui: + role: deployable + display_note: "Two DGX Spark coding profile: NVFP4 weights, FP8 KV, MTP-4, tools, multimodal input, and 256K context" + display_note_zh: "双机 DGX Spark Coding 配置:NVFP4 权重、FP8 KV、MTP-4、工具调用、多模态与 256K 上下文" +capabilities: + deployment_scenario: glm-5.3-flash-nvfp4-2node +openclaw: + chat_provider: true +storage: + formats: [safetensors] + default_path_pattern: "{{.DataDir}}/models/{{.Name}}" + sources: + - type: huggingface + repo: LibertAIDAI/GLM-5.3-Flash-NVFP4 + format: safetensors + quantization: nvfp4 + - type: local_path + path: "" +variants: + - name: glm-5.3-flash-nvfp4-2node-gb10 + hardware: + gpu_arch: Blackwell + vram_min_mib: 0 + ram_min_mib: 98304 + # One GB10 is present on each Fleet device; the scenario supplies two nodes. + gpu_count_min: 1 + unified_memory: true + engine: vllm-glm53-dual-gb10 + format: safetensors + source: + type: huggingface + repo: LibertAIDAI/GLM-5.3-Flash-NVFP4 + format: safetensors + quantization: nvfp4 + default_config: + max_model_len: 262144 + max_num_seqs: 2 + max_num_batched_tokens: 8192 + gpu_memory_utilization: 0.85 + served_model_name: GLM-5.3-Flash-NVFP4 + tensor_parallel_size: 2 + pipeline_parallel_size: 1 + kv_cache_dtype: fp8_e4m3 + kv_cache_memory: 4445787956 + block_size: 2304 + prefix_match_unit: 128 + speculative_config: + method: mtp + num_speculative_tokens: 4 + enable_prefix_caching: true + enable_prompt_tokens_details: true + enable_chunked_prefill: true + enable_auto_tool_choice: true + tool_call_parser: glm47 + reasoning_parser: glm45 + default_chat_template_kwargs: + enable_thinking: true + limit_mm_per_prompt: + image: 4 + video: 1 + expected_performance: + startup_time_s: 900 + cold_start_time_s: 1200 + vram_mib: 110000 + max_context_tokens: 262144 + notes: "Validated on two 128GB unified-memory GB10 nodes over direct RoCE. Requires the matching cache-safety image on both Fleet devices." diff --git a/catalog/scenarios/glm-5.3-flash-nvfp4-2node.yaml b/catalog/scenarios/glm-5.3-flash-nvfp4-2node.yaml new file mode 100644 index 0000000..7b2b9ca --- /dev/null +++ b/catalog/scenarios/glm-5.3-flash-nvfp4-2node.yaml @@ -0,0 +1,112 @@ +kind: deployment_scenario +metadata: + name: glm-5.3-flash-nvfp4-2node + description: "GLM-5.3 Flash NVFP4 across two DGX Spark GB10 devices using AIMA Fleet" +target: + hardware_profile: nvidia-gb10-arm64 + +inputs: + - name: worker_device + label: Worker Fleet device + description: "AIMA Fleet device ID for rank 1" + kind: device + required: true + - name: master_addr + label: Master fabric address + description: "Head-node IPv4 address reachable over the direct RoCE link" + kind: string + required: true + - name: head_host_ip + label: Head vLLM host IP + description: "Head-node address advertised by vLLM, normally the master fabric address" + kind: string + required: true + - name: worker_host_ip + label: Worker vLLM host IP + description: "Worker-node address on the direct RoCE link" + kind: string + required: true + - name: head_socket_ifname + label: Head fabric interface + description: "Head NCCL/Gloo interface name" + kind: string + required: true + - name: worker_socket_ifname + label: Worker fabric interface + description: "Worker NCCL/Gloo interface name" + kind: string + required: true + - name: head_ib_hca + label: Head IB HCA + description: "Head NCCL_IB_HCA value" + kind: string + required: true + - name: worker_ib_hca + label: Worker IB HCA + description: "Worker NCCL_IB_HCA value" + kind: string + required: true + - name: head_gid_index + label: Head RoCE GID index + description: "RoCEv2 GID index matching the head fabric address" + kind: string + required: true + - name: worker_gid_index + label: Worker RoCE GID index + description: "RoCEv2 GID index matching the worker fabric address" + kind: string + required: true + +deployments: + - id: worker + device: "{{.worker_device}}" + model: glm-5.3-flash-nvfp4 + engine: vllm-glm53-dual-gb10 + no_pull: true + role: worker + config: + node_rank: 1 + headless: true + master_addr: "{{.master_addr}}" + env: + VLLM_HOST_IP: "{{.worker_host_ip}}" + NCCL_SOCKET_IFNAME: "{{.worker_socket_ifname}}" + TP_SOCKET_IFNAME: "{{.worker_socket_ifname}}" + GLOO_SOCKET_IFNAME: "{{.worker_socket_ifname}}" + NCCL_IB_HCA: "{{.worker_ib_hca}}" + NCCL_IB_GID_INDEX: "{{.worker_gid_index}}" + notes: "Rank 1 starts first and waits for the head rendezvous." + + - id: head + device: local + model: glm-5.3-flash-nvfp4 + engine: vllm-glm53-dual-gb10 + no_pull: true + role: head + config: + node_rank: 0 + master_addr: "{{.master_addr}}" + env: + VLLM_HOST_IP: "{{.head_host_ip}}" + NCCL_SOCKET_IFNAME: "{{.head_socket_ifname}}" + TP_SOCKET_IFNAME: "{{.head_socket_ifname}}" + GLOO_SOCKET_IFNAME: "{{.head_socket_ifname}}" + NCCL_IB_HCA: "{{.head_ib_hca}}" + NCCL_IB_GID_INDEX: "{{.head_gid_index}}" + notes: "Rank 0 exposes the OpenAI-compatible API on port 8000." + +startup_order: + - step: 1 + deployment: worker + wait_for: "" + timeout_s: 0 + notes: "Launch the headless worker before rank 0." + - step: 2 + deployment: head + wait_for: health_check + timeout_s: 3600 + notes: "Wait for model load, MTP initialization, and the API health check." + +memory_budget: + total_unified_mib: 262144 + notes: "Two 128GB unified-memory nodes; the validated profile pins a 4.14 GiB FP8 KV cache on each rank." diff --git a/internal/knowledge/loader_test.go b/internal/knowledge/loader_test.go index 2943d6c..94b73cf 100644 --- a/internal/knowledge/loader_test.go +++ b/internal/knowledge/loader_test.go @@ -855,6 +855,102 @@ func TestDeepSeekV4DSparkCatalogAssets(t *testing.T) { } } +func TestGLM53FlashDualGB10CatalogAssets(t *testing.T) { + cat, err := LoadCatalog(catalogFS()) + if err != nil { + t.Fatalf("load catalog: %v", err) + } + var model *ModelAsset + for i := range cat.ModelAssets { + if cat.ModelAssets[i].Metadata.Name == "glm-5.3-flash-nvfp4" { + model = &cat.ModelAssets[i] + break + } + } + if model == nil { + t.Fatal("glm-5.3-flash-nvfp4 model asset not found") + } + if model.Metadata.ParameterCount != "320B" || model.Capabilities.DeploymentScenario != "glm-5.3-flash-nvfp4-2node" { + t.Fatalf("unexpected model metadata: %#v capabilities=%#v", model.Metadata, model.Capabilities) + } + var engine *EngineAsset + for i := range cat.EngineAssets { + if cat.EngineAssets[i].Metadata.Name == "vllm-glm53-dual-gb10" { + engine = &cat.EngineAssets[i] + break + } + } + if engine == nil || engine.Container == nil { + t.Fatal("vllm-glm53-dual-gb10 container metadata not found") + } + if engine.Image.Distribution != "local" || engine.Image.Name != "aima/glm53-flash" || engine.Image.Tag != "sm121-v8-local" { + t.Fatalf("unexpected GLM-5.3 image metadata: %#v", engine.Image) + } + if engine.Container.NetworkMode != "host" || len(engine.Container.Devices) == 0 || engine.Container.Devices[0] != "/dev/infiniband" { + t.Fatalf("unexpected GLM-5.3 container access: %#v", engine.Container) + } + if unit, ok := engine.Startup.DefaultArgs["prefix_match_unit"].(int); !ok || unit != 128 || unit%4 != 0 { + t.Fatalf("prefix_match_unit = %#v, want 128 divisible by index_kpool=4", engine.Startup.DefaultArgs["prefix_match_unit"]) + } + var scenario *DeploymentScenario + for i := range cat.DeploymentScenarios { + if cat.DeploymentScenarios[i].Metadata.Name == "glm-5.3-flash-nvfp4-2node" { + scenario = &cat.DeploymentScenarios[i] + break + } + } + if scenario == nil || len(scenario.Inputs) == 0 || len(scenario.Deployments) != 2 { + t.Fatalf("unexpected GLM-5.3 scenario: %#v", scenario) + } + if scenario.Deployments[0].ID != "worker" || !scenario.Deployments[0].NoPull || !scenario.Deployments[1].NoPull || scenario.StartupOrder[0].Deployment != "worker" { + t.Fatalf("worker-first local-image order not preserved: deployments=%#v startup=%#v", scenario.Deployments, scenario.StartupOrder) + } + resolved, err := cat.Resolve(HardwareInfo{ + GPUArch: "Blackwell", GPUVRAMMiB: 15360, GPUCount: 1, UnifiedMemory: true, + CPUArch: "arm64", RAMTotalMiB: 131072, Platform: "linux/arm64", HardwareProfile: "nvidia-gb10-arm64", + }, model.Metadata.Name, engine.Metadata.Name, map[string]any{"model_path": "/models/glm-5.3-flash-nvfp4"}) + if err != nil { + t.Fatalf("resolve GLM-5.3 catalog assets: %v", err) + } + if resolved.EngineDistribution != "local" || resolved.Container == nil || resolved.Container.NetworkMode != "host" { + t.Fatalf("GLM-5.3 engine runtime metadata was not merged: %#v", resolved) + } + podYAML, err := GeneratePod(resolved) + if err != nil { + t.Fatalf("generate GLM-5.3 pod: %v", err) + } + podText := string(podYAML) + for _, want := range []string{ + `image: aima/glm53-flash:sm121-v8-local`, + `- "--max-model-len"`, + `- "262144"`, + `- "--block-size"`, + `- "2304"`, + `- "--prefix-match-unit"`, + `- "128"`, + `- "--kv-cache-memory"`, + `- "4445787956"`, + `- "--enable-prefix-caching"`, + `- "--enable-prompt-tokens-details"`, + `- "--enable-auto-tool-choice"`, + `- "--speculative-config"`, + `num_speculative_tokens`, + `enable_thinking`, + `- "glm47"`, + `- "glm45"`, + `/models/chat_template.jinja`, + `hostNetwork: true`, + `/dev/infiniband`, + } { + if !strings.Contains(podText, want) { + t.Fatalf("GLM-5.3 pod is missing %q:\n%s", want, podText) + } + } + if strings.Contains(podText, "--quantization") { + t.Fatalf("GLM-5.3 NVFP4 is checkpoint-declared and must not receive --quantization:\n%s", podText) + } +} + func TestLoadCatalogInvalidYAML(t *testing.T) { fs := fstest.MapFS{ "hardware/bad.yaml": &fstest.MapFile{Data: []byte("not: valid: yaml: [")},