Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions catalog/engines/vllm-dspark-gb10.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
kind: engine_asset
_profile: vllm
metadata:
name: vllm-dspark-gb10
type: vllm
version: "anemll-0.1.1"
supported_formats: [safetensors]
supported_model_types: [llm]
hardware:
gpu_arch: Blackwell
vram_min_mib: 4096
image:
name: ghcr.io/anemll/dspark-vllm-gx10
tag: "0.1.1"
platforms: [linux/arm64]
registries: [ghcr.io]
patterns:
- "dspark-vllm-gx10"
container:
devices:
- /dev/infiniband
network_mode: host
shm_size: 64gb
ulimits:
memlock: "-1:-1"
stack: "67108864:67108864"
startup:
init_commands:
- "ulimit -l unlimited; ulimit -s 65536"
- "if [ -f /models/encoding/encoding_dsv4.py ]; then cp -f /models/encoding/encoding_dsv4.py /usr/local/lib/python3.12/dist-packages/vllm/tokenizers/deepseek_v4_encoding.py; fi"
env:
HF_HOME: /cache/huggingface
HF_HUB_DISABLE_XET: "1"
VLLM_CACHE_ROOT: /cache/huggingface/vllm-cache
VLLM_ALLOW_LONG_MAX_MODEL_LEN: "1"
VLLM_SPARSE_INDEXER_MAX_LOGITS_MB: "256"
VLLM_MEMORY_PROFILER_ESTIMATE_CUDAGRAPHS: "0"
VLLM_USE_FLASHINFER_SAMPLER: "1"
VLLM_USE_B12X_MOE: "1"
VLLM_USE_B12X_WO_PROJECTION: "1"
VLLM_DSPARK_GPU_REJECTED_CONTEXT_MASK: "1"
VLLM_USE_BREAKABLE_CUDAGRAPH: "0"
TORCH_CUDA_ARCH_LIST: "12.1a"
FLASHINFER_CUDA_ARCH_LIST: "12.1a"
CUTE_DSL_ARCH: sm_121a
FLASHINFER_DISABLE_VERSION_CHECK: "1"
FLASHINFER_WORKSPACE_BASE: /cache/huggingface/flashinfer
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_CROSS_NIC: "1"
NCCL_CUMEM_ENABLE: "0"
NCCL_IGNORE_CPU_AFFINITY: "1"
NCCL_NVLS_ENABLE: "0"
NCCL_DEBUG: WARN
default_args:
port: 8888
host: 0.0.0.0
served_model_name: deepseek-v4-flash-dspark
trust_remote_code: true
tensor_parallel_size: 2
pipeline_parallel_size: 1
kv_cache_dtype: nvfp4_ds_mla
block_size: 256
max_model_len: 1048576
max_num_seqs: 6
max_num_batched_tokens: 8192
max_cudagraph_capture_size: 36
gpu_memory_utilization: 0.80
enable_prefix_caching: true
enable_prompt_tokens_details: true
async_scheduling: true
enable_chunked_prefill: true
speculative_config:
method: dspark
num_speculative_tokens: 5
draft_sample_method: probabilistic
tokenizer_mode: deepseek_v4
distributed_executor_backend: mp
moe_backend: flashinfer_b12x
tool_call_parser: deepseek_v4
enable_auto_tool_choice: true
reasoning_parser: deepseek_v4
reasoning_config:
reasoning_parser: deepseek_v4
reasoning_start_str: <think>
reasoning_end_str: </think>
default_chat_template_kwargs:
thinking: false
generation_config: vllm
enable_flashinfer_autotune: true
nnodes: 2
master_port: 25000
health_check:
path: /health
timeout_s: 1200
warmup:
enabled: true
prompt: "Return the word ready."
max_tokens: 8
timeout_s: 180
amplifier:
features:
- dspark_mtp
- nvfp4_ds_mla
- flashinfer_b12x
time_constraints:
cold_start_s: [600, 1200]
model_switch_s: [600, 1200]
power_constraints:
typical_draw_watts: [60, 100]
runtime:
default: container
platform_recommendations:
linux/arm64: container
55 changes: 55 additions & 0 deletions catalog/models/deepseek-v4-flash-0731.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
kind: model_asset
metadata:
name: deepseek-v4-flash-0731
type: llm
family: deepseek
# 284B target model plus the attached DSpark speculative module. The
# ~45.61 GB shown by some quantized mirrors is artifact size, not parameter count.
parameter_count: "304B"
released_at: "2026-07-31"
aliases:
- DeepSeek-V4-Flash-0731
- DeepSeek-V4-Flash-DSpark
capabilities:
deployment_scenario: deepseek-v4-flash-dspark-2node
storage:
formats: [safetensors]
default_path_pattern: "{{.DataDir}}/models/{{.Name}}"
sources:
- type: huggingface
repo: deepseek-ai/DeepSeek-V4-Flash-0731
format: safetensors
quantization: nvfp4
- type: modelscope
repo: 0xSero/deepseek-v4-flash-0731-spark
format: safetensors
quantization: nvfp4
- type: local_path
path: ""
variants:
- name: deepseek-v4-flash-0731-dspark-2node-nvfp4
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-dspark-gb10
format: safetensors
source:
type: huggingface
repo: deepseek-ai/DeepSeek-V4-Flash-0731
format: safetensors
quantization: nvfp4
default_config:
max_model_len: 1048576
max_num_seqs: 6
max_num_batched_tokens: 8192
gpu_memory_utilization: 0.80
served_model_name: deepseek-v4-flash-dspark
expected_performance:
startup_time_s: 900
cold_start_time_s: 1200
vram_mib: 110000
notes: "Two-node DSpark NVFP4 deployment. Exact throughput depends on RoCE wiring, GID selection, context length, and MTP warmup."
112 changes: 112 additions & 0 deletions catalog/scenarios/deepseek-v4-flash-dspark-2node.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
kind: deployment_scenario
metadata:
name: deepseek-v4-flash-dspark-2node
description: "DeepSeek V4 Flash DSpark 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: deepseek-v4-flash-0731
engine: vllm-dspark-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: deepseek-v4-flash-0731
engine: vllm-dspark-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 8888."

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: 1200
notes: "Wait for model load, graph capture, and the API health check."

memory_budget:
total_unified_mib: 262144
notes: "Two 128GB unified-memory nodes; per-node limits and KV-cache allocation are controlled by the model variant."
85 changes: 83 additions & 2 deletions cmd/aima/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1369,7 +1369,7 @@ func TestApplyScenarioSkipsRemainingDeploymentsAndPostDeployAfterWaitFailure(t *
},
}

data, err := applyScenario(context.Background(), cat, "docker", deps, "demo", false)
data, err := applyScenario(context.Background(), cat, "docker", deps, "demo", false, nil)
if err != nil {
t.Fatalf("applyScenario: %v", err)
}
Expand Down Expand Up @@ -1430,7 +1430,7 @@ func TestApplyScenarioWaitsOnLastStepBeforePostDeploy(t *testing.T) {
},
}

data, err := applyScenario(context.Background(), cat, "docker", deps, "demo-last", false)
data, err := applyScenario(context.Background(), cat, "docker", deps, "demo-last", false, nil)
if err != nil {
t.Fatalf("applyScenario: %v", err)
}
Expand All @@ -1455,6 +1455,87 @@ func TestApplyScenarioWaitsOnLastStepBeforePostDeploy(t *testing.T) {
}
}

func TestApplyScenarioDryRunRoutesRemoteDeploymentAndExpandsBindings(t *testing.T) {
cat := &knowledge.Catalog{DeploymentScenarios: []knowledge.DeploymentScenario{{
Metadata: knowledge.ScenarioMetadata{Name: "two-node"},
Inputs: []knowledge.ScenarioInput{
{Name: "worker_device", Required: true},
{Name: "master_addr", Required: true},
},
Deployments: []knowledge.ScenarioDeployment{
{ID: "worker", Device: "{{.worker_device}}", Model: "same-model", Engine: "engine", Config: map[string]any{"node_rank": 1}, Env: map[string]string{"MASTER_ADDR": "{{.master_addr}}"}},
{ID: "head", Device: "local", Model: "same-model", Engine: "engine", Config: map[string]any{"node_rank": 0}},
},
StartupOrder: []knowledge.ScenarioStartupStep{
{Step: 1, Deployment: "worker"},
{Step: 2, Deployment: "head"},
},
}}}

var remoteParams map[string]any
localCalls := 0
deps := &mcp.ToolDeps{
FleetExecTool: func(ctx context.Context, deviceID, toolName string, params json.RawMessage) (json.RawMessage, error) {
if deviceID != "spark-worker" || toolName != "deploy.dry_run" {
t.Fatalf("unexpected remote call device=%q tool=%q", deviceID, toolName)
}
if err := json.Unmarshal(params, &remoteParams); err != nil {
t.Fatal(err)
}
return json.Marshal(mcp.TextResult(`{"name":"remote-plan"}`))
},
DeployDryRun: func(ctx context.Context, engine, model, slot string, config map[string]any) (json.RawMessage, error) {
localCalls++
if rank := config["node_rank"]; rank != 0 {
t.Fatalf("local node_rank = %#v, want 0", rank)
}
return json.RawMessage(`{"name":"local-plan"}`), nil
},
}

data, err := applyScenario(context.Background(), cat, "docker", deps, "two-node", true, map[string]string{
"worker_device": "spark-worker",
"master_addr": "10.0.0.1",
})
if err != nil {
t.Fatalf("applyScenario: %v", err)
}
if localCalls != 1 {
t.Fatalf("local dry-run calls = %d, want 1", localCalls)
}
config, ok := remoteParams["config"].(map[string]any)
if !ok {
t.Fatalf("remote params = %#v, config missing", remoteParams)
}
env, ok := config["_env"].(map[string]any)
if !ok {
t.Fatalf("remote config = %#v, _env missing", config)
}
if env["MASTER_ADDR"] != "10.0.0.1" {
t.Fatalf("remote env = %#v", env)
}
var response struct {
Deployments []scenarioDeployResult `json:"deployments"`
}
if err := json.Unmarshal(data, &response); err != nil {
t.Fatal(err)
}
if len(response.Deployments) != 2 || response.Deployments[0].Device != "spark-worker" || response.Deployments[1].Device != "local" {
t.Fatalf("deployments = %#v", response.Deployments)
}
}

func TestApplyScenarioRejectsMissingRequiredBinding(t *testing.T) {
cat := &knowledge.Catalog{DeploymentScenarios: []knowledge.DeploymentScenario{{
Metadata: knowledge.ScenarioMetadata{Name: "needs-input"},
Inputs: []knowledge.ScenarioInput{{Name: "worker_device", Required: true}},
}}}
_, err := applyScenario(context.Background(), cat, "docker", &mcp.ToolDeps{}, "needs-input", true, nil)
if err == nil || !strings.Contains(err.Error(), "worker_device") {
t.Fatalf("error = %v, want missing worker_device", err)
}
}

func TestClassifyDeploymentFailure(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading
Loading