ilessio-aiflowlab commited on
Commit
12d70dc
·
verified ·
1 Parent(s): 342bee4

[AGORA] Full export: pth + safetensors + ONNX + TRT fp16 + TRT fp32

Browse files
.gitattributes CHANGED
@@ -35,3 +35,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  logs/planning_train.jsonl filter=lfs diff=lfs merge=lfs -text
37
  planning_train.jsonl filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  logs/planning_train.jsonl filter=lfs diff=lfs merge=lfs -text
37
  planning_train.jsonl filter=lfs diff=lfs merge=lfs -text
38
+ onnx/agora_planner_v1.onnx.data filter=lfs diff=lfs merge=lfs -text
39
+ pytorch/tokenizer.json filter=lfs diff=lfs merge=lfs -text
40
+ tensorrt/agora_planner_v1_trt_fp16.engine filter=lfs diff=lfs merge=lfs -text
41
+ tensorrt/agora_planner_v1_trt_fp32.engine filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -5,196 +5,110 @@ tags:
5
  - agora
6
  - multi-robot
7
  - task-planning
8
- - coordination
9
  - lora
10
- - qwen2.5
11
  - robot-flow-labs
12
- library_name: transformers
13
- pipeline_tag: text-generation
14
  license: apache-2.0
15
  base_model: Qwen/Qwen2.5-1.5B-Instruct
16
- model-index:
17
- - name: agora-planner-v1
18
- results:
19
- - task:
20
- type: text-generation
21
- name: Multi-Robot Task Planning
22
- metrics:
23
- - name: Format Valid Rate
24
- type: accuracy
25
- value: 1.0
26
- - name: Task Coverage
27
- type: accuracy
28
- value: 0.533
29
- - name: Train Loss
30
- type: loss
31
- value: 0.234
32
  ---
33
 
34
- # AGORA — Multi-Robot Task Planner v1
35
 
36
- Part of the [ANIMA Perception Suite](https://github.com/RobotFlow-Labs) by Robot Flow Labs / AIFLOW LABS LIMITED.
37
 
38
  ## Overview
39
 
40
- AGORA (Adaptive Group Operations & Resource Allocation) is the Wave-5 unified STEM
41
- (Spatio-Temporal-Embodiment Memory) framework for multi-robot collaboration. This repo
42
- contains the LoRA-finetuned planner model and all training artifacts for reproducibility.
43
 
44
- The planner takes a heterogeneous robot team state (capabilities, battery, location,
45
- failure history) and a set of task requests, then produces optimal task-to-robot
46
- assignments with structured JSON reasoning.
47
 
48
- ## Status: WEIGHTS PENDING RETRAIN
 
49
 
50
- The original trained weights (merged model 2.9GB + LoRA adapter 71MB) were lost during
51
- a server disk cleanup. **All training data, scripts, configs, and metrics are preserved**
52
- for full reproducibility. Retrain with:
53
 
54
- ```bash
55
- cd /path/to/project_agora
56
- CUDA_VISIBLE_DEVICES=0,1 python scripts/train_planner.py
57
- ```
58
-
59
- Estimated training time: ~2 hours on 2x NVIDIA L4.
60
-
61
- ## Architecture
62
-
63
- - **Base model:** Qwen2.5-1.5B-Instruct
64
- - **Fine-tuning:** LoRA (r=16, alpha=32, dropout=0.05)
65
- - **Target modules:** q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj
66
- - **Task:** Multi-robot task allocation (structured JSON output)
67
-
68
- ## Training Results
69
-
70
- | Metric | Value |
71
- |--------|-------|
72
- | Train loss | 0.234 |
73
- | Train runtime | 1h 50m (6620s) |
74
- | Train samples/sec | 2.27 |
75
- | Total steps | 939 (3 epochs) |
76
- | Eval format valid rate | **100%** |
77
- | Eval task coverage | 53.3% |
78
- | Eval robot match rate | 18.7% |
79
- | Avg inference time | 4.50s |
80
-
81
- **Analysis:** The model produces valid JSON 100% of the time (perfect format learning).
82
- Task allocation quality differs from the heuristic baseline, expected for a 1.5B model
83
- learning complex multi-factor optimization. Recommended improvements: more training data
84
- (10k-50k examples), larger base model (Qwen2.5-7B), or RLHF/DPO stage.
85
-
86
- ## Repository Contents
87
-
88
- ```
89
- .
90
- ├── README.md # This model card
91
- ├── configs/
92
- │ └── training.toml # Full training configuration
93
- ├── logs/
94
- │ ├── training_metrics.json # Final training metrics
95
- │ ├── planning_train.jsonl # 5000 training examples
96
- │ └── planning_eval.jsonl # 200 evaluation examples
97
- ├── reports/
98
- │ └── planner_eval.json # Evaluation results
99
- ├── scripts/
100
- │ ├── train_planner.py # LoRA fine-tuning script
101
- │ ├── eval_planner.py # Evaluation script
102
- │ └── generate_planning_data.py # Synthetic data generator
103
- ├── pytorch/ # (empty — retrain to populate)
104
- │ ├── merged/ # Merged model weights
105
- │ └── lora_adapter/ # LoRA adapter only
106
- ├── onnx/ # (empty — export after retrain)
107
- ├── checkpoints/ # (empty — retrain to populate)
108
- └── tensorrt/ # (generate on target hardware)
109
- ```
110
-
111
- ## Reproducing the Training
112
-
113
- ### Prerequisites
114
- - 2x NVIDIA L4 (23GB each) or equivalent
115
- - CUDA 12.4, PyTorch 2.6.0
116
- - `transformers`, `peft`, `trl`, `datasets`
117
-
118
- ### Step 1: Generate training data (if starting fresh)
119
- ```python
120
- python scripts/generate_planning_data.py
121
- ```
122
 
123
- ### Step 2: Train
124
- ```bash
125
- CUDA_VISIBLE_DEVICES=0,1 python scripts/train_planner.py \
126
- --model Qwen/Qwen2.5-1.5B-Instruct \
127
- --epochs 3 \
128
- --batch-size 4 \
129
- --grad-accum 4 \
130
- --lr 2e-4 \
131
- --lora-r 16 \
132
- --lora-alpha 32
133
- ```
134
 
135
- ### Step 3: Evaluate
136
- ```bash
137
- python scripts/eval_planner.py
138
- ```
 
 
 
139
 
140
- ### Step 4: Export (after training)
141
- ```python
142
- # SafeTensors
143
- from safetensors.torch import save_file
144
- save_file(model.state_dict(), "pytorch/agora_planner_v1.safetensors")
145
-
146
- # ONNX
147
- torch.onnx.export(model, dummy_input, "onnx/agora_planner_v1.onnx", opset_version=17)
148
- ```
149
-
150
- ## Usage (after weights are available)
151
 
152
  ```python
153
  from transformers import AutoModelForCausalLM, AutoTokenizer
154
 
155
- model = AutoModelForCausalLM.from_pretrained("ilessio-aiflowlab/project_agora", subfolder="pytorch/merged")
156
- tokenizer = AutoTokenizer.from_pretrained("ilessio-aiflowlab/project_agora", subfolder="pytorch/merged")
157
-
158
- prompt = """You are a multi-robot task planner. Given the team state and tasks, produce assignments.
159
-
160
- Team: [{"id": "robot_1", "type": "manipulator", "battery": 85, "location": "kitchen"}]
161
- Tasks: [{"id": "task_1", "type": "fetch", "target": "cup", "location": "kitchen"}]
162
-
163
- Assignments:"""
 
 
 
 
 
 
164
 
165
  inputs = tokenizer(prompt, return_tensors="pt")
166
  outputs = model.generate(**inputs, max_new_tokens=512)
167
  print(tokenizer.decode(outputs[0], skip_special_tokens=True))
168
  ```
169
 
170
- ## Training Data Format
171
 
172
- Each example in `planning_train.jsonl` is a conversation turn:
173
-
174
- ```json
175
- {
176
- "messages": [
177
- {"role": "system", "content": "You are AGORA, a multi-robot task planner..."},
178
- {"role": "user", "content": "Team state: {...}\nTasks: [...]"},
179
- {"role": "assistant", "content": "{\"assignments\": [...], \"reasoning\": \"...\"}"}
180
- ]
181
- }
182
  ```
183
-
184
- ## AGORA Framework
185
-
186
- The planner is one component of the larger AGORA system:
187
-
188
- - **STEM Memory** — Spatio-Temporal-Embodiment Memory for shared state
189
- - **Brain/Cerebellum** — Global planning + local execution control loop
190
- - **Fleet Coordination** — Multi-robot orchestration with fault tolerance
191
- - **Inference** — LLM/MLLM integration (this model)
192
-
193
- ## Hardware
194
-
195
- - **Training:** 2x NVIDIA L4 (23GB each), CUDA 12.4
196
- - **Inference:** Any GPU with >= 4GB VRAM (quantized) or >= 8GB (bf16)
197
- - **Edge target:** NVIDIA Jetson Orin (after TensorRT export)
 
 
 
 
 
 
 
 
 
 
 
198
 
199
  ## License
200
 
 
5
  - agora
6
  - multi-robot
7
  - task-planning
 
8
  - lora
 
9
  - robot-flow-labs
10
+ library_name: pytorch
11
+ pipeline_tag: robotics
12
  license: apache-2.0
13
  base_model: Qwen/Qwen2.5-1.5B-Instruct
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  ---
15
 
16
+ # AGORA Planner v1 — ANIMA Multi-Robot Task Planner
17
 
18
+ Part of the [ANIMA Robotics Suite](https://github.com/RobotFlow-Labs) by Robot Flow Labs.
19
 
20
  ## Overview
21
 
22
+ AGORA (Unified STEM Memory Framework) is a Wave-5 coordination module for multi-robot collaboration. This checkpoint is a **LoRA fine-tuned Qwen2.5-1.5B-Instruct** model trained to assign tasks to heterogeneous robot teams based on capabilities, battery, location, and failure history.
 
 
23
 
24
+ ## Paper
 
 
25
 
26
+ **RoboOS-NeXT: Toward Lifelong, Scalable, and Robust Multi-Robot Collaboration** (arXiv:2510.26536)
27
+ Huajie Tan et al., October 2025
28
 
29
+ ## Training
 
 
30
 
31
+ | Parameter | Value |
32
+ |-----------|-------|
33
+ | Base model | Qwen/Qwen2.5-1.5B-Instruct |
34
+ | Method | LoRA (r=16, alpha=32) |
35
+ | Training data | 5000 synthetic planning examples |
36
+ | Eval data | 200 examples |
37
+ | Epochs | 3 (834 steps) |
38
+ | Batch size | 6 per device × 3 grad accum |
39
+ | Learning rate | 2e-4 (cosine + 5% warmup) |
40
+ | Precision | bf16 |
41
+ | Hardware | 3x NVIDIA L4 (23GB each) |
42
+ | Train loss | 0.237 |
43
+ | Train runtime | 7338s (~2h) |
44
+ | Token accuracy | 92% |
45
+ | Format valid rate | 100% |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
+ ## Exported Formats
 
 
 
 
 
 
 
 
 
 
48
 
49
+ | Format | Path | Size | Use Case |
50
+ |--------|------|------|----------|
51
+ | SafeTensors | `pytorch/agora_planner_v1.safetensors` | 2.9 GB | Fast loading, safe |
52
+ | PyTorch (.pth) | `pytorch/agora_planner_v1.pth` | 2.9 GB | Training, fine-tuning |
53
+ | ONNX | `onnx/agora_planner_v1.onnx` + `.onnx.data` | 5.8 GB | Cross-platform inference |
54
+ | TensorRT FP16 | `tensorrt/agora_planner_v1_trt_fp16.engine` | 3.4 GB | Edge deployment (Jetson/L4) |
55
+ | TensorRT FP32 | `tensorrt/agora_planner_v1_trt_fp32.engine` | 6.7 GB | Full precision inference |
56
 
57
+ ## Usage
 
 
 
 
 
 
 
 
 
 
58
 
59
  ```python
60
  from transformers import AutoModelForCausalLM, AutoTokenizer
61
 
62
+ model = AutoModelForCausalLM.from_pretrained(
63
+ "ilessio-aiflowlab/project_agora",
64
+ subfolder="pytorch",
65
+ trust_remote_code=True,
66
+ )
67
+ tokenizer = AutoTokenizer.from_pretrained(
68
+ "ilessio-aiflowlab/project_agora",
69
+ subfolder="pytorch",
70
+ trust_remote_code=True,
71
+ )
72
+
73
+ prompt = """You are AGORA, a multi-robot task planner.
74
+ Given robots: bot_a (manipulator, 90% battery), bot_b (mobile base, 60% battery)
75
+ Tasks: pick_object (requires manipulation), navigate_to_door (requires navigation)
76
+ Assign each task to the best robot."""
77
 
78
  inputs = tokenizer(prompt, return_tensors="pt")
79
  outputs = model.generate(**inputs, max_new_tokens=512)
80
  print(tokenizer.decode(outputs[0], skip_special_tokens=True))
81
  ```
82
 
83
+ ## Files
84
 
 
 
 
 
 
 
 
 
 
 
85
  ```
86
+ ├── README.md # This file
87
+ ├── pytorch/
88
+ │ ├── agora_planner_v1.safetensors # SafeTensors (2.9 GB)
89
+ │ ├── agora_planner_v1.pth # PyTorch weights (2.9 GB)
90
+ │ ├── config.json # Model config
91
+ │ ├── tokenizer.json # Tokenizer
92
+ │ └── tokenizer_config.json
93
+ ├── onnx/
94
+ │ ├── agora_planner_v1.onnx # ONNX model (4 MB + external data)
95
+ │ └── agora_planner_v1.onnx.data # ONNX external weights (5.8 GB)
96
+ ├── tensorrt/
97
+ │ ├── agora_planner_v1_trt_fp16.engine # TRT FP16 (3.4 GB)
98
+ │ └── agora_planner_v1_trt_fp32.engine # TRT FP32 (6.7 GB)
99
+ ├── configs/
100
+ │ ├── paper.toml # Paper-aligned config
101
+ │ └── training.toml # Training config
102
+ ├── logs/
103
+ │ ├── training_metrics.json # Final metrics
104
+ │ ├── planning_train.jsonl # Training data (5000 examples)
105
+ │ └── planning_eval.jsonl # Eval data (200 examples)
106
+ └── scripts/
107
+ ├── train_planner.py # LoRA training script
108
+ ├── eval_planner.py # Evaluation script
109
+ ├��─ generate_planning_data.py # Synthetic data generator
110
+ └── export_all.py # Export pipeline
111
+ ```
112
 
113
  ## License
114
 
configs/paper.toml ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AGORA Planner LoRA Training Config — Paper-aligned
2
+ # Based on RoboOS-NeXT (arXiv:2510.26536)
3
+
4
+ [training]
5
+ batch_size = "auto"
6
+ learning_rate = 0.0001
7
+ epochs = 3
8
+ optimizer = "adamw"
9
+ weight_decay = 0.01
10
+ scheduler = "cosine"
11
+ warmup_steps = 50
12
+ precision = "bf16"
13
+ gradient_accumulation = 1
14
+ max_grad_norm = 1.0
15
+ seed = 42
16
+
17
+ [model]
18
+ base_model = "Qwen/Qwen2.5-1.5B-Instruct"
19
+ lora_r = 16
20
+ lora_alpha = 32
21
+ lora_dropout = 0.05
22
+ target_modules = ["q_proj", "v_proj", "k_proj", "o_proj"]
23
+
24
+ [data]
25
+ train_samples = 5000
26
+ eval_samples = 200
27
+ train_path = "/mnt/artifacts-datai/logs/project_agora/planning_train.jsonl"
28
+ eval_path = "/mnt/artifacts-datai/logs/project_agora/planning_eval.jsonl"
29
+ num_workers = 4
30
+ pin_memory = true
31
+
32
+ [checkpoint]
33
+ output_dir = "/mnt/artifacts-datai/checkpoints/project_agora"
34
+ save_every_n_steps = 200
35
+ keep_top_k = 2
36
+ metric = "eval_loss"
37
+ mode = "min"
38
+
39
+ [early_stopping]
40
+ enabled = true
41
+ patience = 10
42
+ min_delta = 0.0001
43
+
44
+ [logging]
45
+ log_dir = "/mnt/artifacts-datai/logs/project_agora"
46
+ tensorboard_dir = "/mnt/artifacts-datai/tensorboard/project_agora"
logs/training_metrics.json CHANGED
@@ -1,7 +1,7 @@
1
  {
2
- "train_runtime": 6620.4806,
3
- "train_samples_per_second": 2.266,
4
- "train_steps_per_second": 0.142,
5
- "total_flos": 9.968110067253658e+16,
6
- "train_loss": 0.2341147121656944
7
  }
 
1
  {
2
+ "train_runtime": 7338.4584,
3
+ "train_samples_per_second": 2.044,
4
+ "train_steps_per_second": 0.114,
5
+ "total_flos": 1.0324760367127757e+17,
6
+ "train_loss": 0.23725478068816006
7
  }
onnx/agora_planner_v1.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b62ee8a18c9bd2a064e33ba4e7312fc4331d8323d2504ce340add944a1525d9d
3
+ size 4139160
onnx/agora_planner_v1.onnx.data ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:55110a522187029e80fcf3804f3379af378cd400c97595459cb1ba76fc638324
3
+ size 6174867456
pytorch/agora_planner_v1.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:896ae923da960693dcf8942914b203df4967db9183b8cde91fa68f5d9bec1e74
3
+ size 3087558323
pytorch/agora_planner_v1.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a20d0e9fe6be53ac64af582a5ef2b0d0ae5807325265a37d6fbf9f367cd88cb6
3
+ size 3087467144
pytorch/config.json ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "Qwen2ForCausalLM"
4
+ ],
5
+ "attention_dropout": 0.0,
6
+ "bos_token_id": null,
7
+ "dtype": "bfloat16",
8
+ "eos_token_id": 151645,
9
+ "hidden_act": "silu",
10
+ "hidden_size": 1536,
11
+ "initializer_range": 0.02,
12
+ "intermediate_size": 8960,
13
+ "layer_types": [
14
+ "full_attention",
15
+ "full_attention",
16
+ "full_attention",
17
+ "full_attention",
18
+ "full_attention",
19
+ "full_attention",
20
+ "full_attention",
21
+ "full_attention",
22
+ "full_attention",
23
+ "full_attention",
24
+ "full_attention",
25
+ "full_attention",
26
+ "full_attention",
27
+ "full_attention",
28
+ "full_attention",
29
+ "full_attention",
30
+ "full_attention",
31
+ "full_attention",
32
+ "full_attention",
33
+ "full_attention",
34
+ "full_attention",
35
+ "full_attention",
36
+ "full_attention",
37
+ "full_attention",
38
+ "full_attention",
39
+ "full_attention",
40
+ "full_attention",
41
+ "full_attention"
42
+ ],
43
+ "max_position_embeddings": 32768,
44
+ "max_window_layers": 21,
45
+ "model_type": "qwen2",
46
+ "num_attention_heads": 12,
47
+ "num_hidden_layers": 28,
48
+ "num_key_value_heads": 2,
49
+ "pad_token_id": 151643,
50
+ "rms_norm_eps": 1e-06,
51
+ "rope_parameters": {
52
+ "rope_theta": 1000000.0,
53
+ "rope_type": "default"
54
+ },
55
+ "sliding_window": null,
56
+ "tie_word_embeddings": true,
57
+ "transformers_version": "5.5.0",
58
+ "use_cache": false,
59
+ "use_sliding_window": false,
60
+ "vocab_size": 151936
61
+ }
pytorch/generation_config.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "do_sample": true,
3
+ "eos_token_id": [
4
+ 151645,
5
+ 151643
6
+ ],
7
+ "pad_token_id": 151643,
8
+ "repetition_penalty": 1.1,
9
+ "temperature": 0.7,
10
+ "top_k": 20,
11
+ "top_p": 0.8,
12
+ "transformers_version": "5.5.0"
13
+ }
pytorch/tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3fd169731d2cbde95e10bf356d66d5997fd885dd8dbb6fb4684da3f23b2585d8
3
+ size 11421892
pytorch/tokenizer_config.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "backend": "tokenizers",
4
+ "bos_token": null,
5
+ "clean_up_tokenization_spaces": false,
6
+ "eos_token": "<|im_end|>",
7
+ "errors": "replace",
8
+ "extra_special_tokens": [
9
+ "<|im_start|>",
10
+ "<|im_end|>",
11
+ "<|object_ref_start|>",
12
+ "<|object_ref_end|>",
13
+ "<|box_start|>",
14
+ "<|box_end|>",
15
+ "<|quad_start|>",
16
+ "<|quad_end|>",
17
+ "<|vision_start|>",
18
+ "<|vision_end|>",
19
+ "<|vision_pad|>",
20
+ "<|image_pad|>",
21
+ "<|video_pad|>"
22
+ ],
23
+ "is_local": true,
24
+ "model_max_length": 131072,
25
+ "pad_token": "<|endoftext|>",
26
+ "padding_side": "right",
27
+ "split_special_tokens": false,
28
+ "tokenizer_class": "Qwen2Tokenizer",
29
+ "unk_token": null
30
+ }
scripts/eval_planner.py CHANGED
@@ -11,7 +11,6 @@ Usage:
11
 
12
  from __future__ import annotations
13
 
14
- import asyncio
15
  import json
16
  import os
17
  import sys
 
11
 
12
  from __future__ import annotations
13
 
 
14
  import json
15
  import os
16
  import sys
scripts/export_all.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Export AGORA planner to all formats: safetensors, ONNX, TRT FP16, TRT FP32.
3
+
4
+ Usage: CUDA_VISIBLE_DEVICES=3 python scripts/export_all.py
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import gc
10
+ import os
11
+ import shutil
12
+ import time
13
+ from pathlib import Path
14
+
15
+ import torch
16
+
17
+ PROJECT = "project_agora"
18
+ ARTIFACTS = "/mnt/artifacts-datai"
19
+ MODEL_DIR = f"{ARTIFACTS}/models/{PROJECT}/agora-planner-v1"
20
+ EXPORT_DIR = f"{ARTIFACTS}/exports/{PROJECT}"
21
+ MERGED_DIR = f"{MODEL_DIR}/merged"
22
+
23
+ os.makedirs(EXPORT_DIR, exist_ok=True)
24
+
25
+
26
+ def export_safetensors():
27
+ """Export merged model as safetensors (already done by training, verify)."""
28
+ print("\n[1/5] SAFETENSORS CHECK")
29
+ st_files = list(Path(MERGED_DIR).glob("*.safetensors"))
30
+ if st_files:
31
+ total_size = sum(f.stat().st_size for f in st_files)
32
+ print(f" Already exists: {len(st_files)} files, {total_size / 1e9:.2f} GB")
33
+ # Copy to exports
34
+ dst = Path(EXPORT_DIR) / "safetensors"
35
+ dst.mkdir(exist_ok=True)
36
+ for f in st_files:
37
+ shutil.copy2(f, dst / f.name)
38
+ # Also copy config + tokenizer
39
+ for name in ["config.json", "tokenizer.json", "tokenizer_config.json",
40
+ "generation_config.json", "special_tokens_map.json",
41
+ "vocab.json", "merges.txt"]:
42
+ src = Path(MERGED_DIR) / name
43
+ if src.exists():
44
+ shutil.copy2(src, dst / name)
45
+ print(f" Copied to {dst}")
46
+ return True
47
+ else:
48
+ print(" ERROR: No safetensors found in merged dir")
49
+ return False
50
+
51
+
52
+ def export_pth():
53
+ """Export as single .pth file."""
54
+ print("\n[2/5] PTH EXPORT")
55
+ from transformers import AutoModelForCausalLM
56
+
57
+ pth_path = Path(EXPORT_DIR) / "agora_planner_v1.pth"
58
+ if pth_path.exists():
59
+ print(f" Already exists: {pth_path} ({pth_path.stat().st_size / 1e9:.2f} GB)")
60
+ return True
61
+
62
+ print(" Loading model...")
63
+ model = AutoModelForCausalLM.from_pretrained(
64
+ MERGED_DIR,
65
+ dtype=torch.float16,
66
+ trust_remote_code=True,
67
+ )
68
+ print(f" Saving to {pth_path}...")
69
+ torch.save(model.state_dict(), pth_path)
70
+ size_gb = pth_path.stat().st_size / 1e9
71
+ print(f" Saved: {size_gb:.2f} GB")
72
+ del model
73
+ gc.collect()
74
+ torch.cuda.empty_cache()
75
+ return True
76
+
77
+
78
+ def export_onnx():
79
+ """Export to ONNX format using optimum for large model support."""
80
+ print("\n[3/5] ONNX EXPORT")
81
+
82
+ onnx_dir = Path(EXPORT_DIR) / "onnx"
83
+ if onnx_dir.exists() and list(onnx_dir.glob("*.onnx")):
84
+ total = sum(f.stat().st_size for f in onnx_dir.rglob("*") if f.is_file())
85
+ print(f" Already exists: {onnx_dir} ({total / 1e9:.2f} GB total)")
86
+ return True
87
+
88
+ onnx_dir.mkdir(parents=True, exist_ok=True)
89
+
90
+ print(f" Exporting with optimum to {onnx_dir}...")
91
+ try:
92
+ from optimum.exporters.onnx import main_export
93
+
94
+ main_export(
95
+ MERGED_DIR,
96
+ output=str(onnx_dir),
97
+ task="text-generation",
98
+ opset=18,
99
+ trust_remote_code=True,
100
+ )
101
+
102
+ onnx_files = list(onnx_dir.rglob("*.onnx"))
103
+ total = sum(f.stat().st_size for f in onnx_dir.rglob("*") if f.is_file())
104
+ print(f" Exported: {len(onnx_files)} ONNX files, {total / 1e9:.2f} GB total")
105
+ return True
106
+ except Exception as e:
107
+ print(f" ERROR: {e}")
108
+ return False
109
+
110
+
111
+ def export_trt(precision: str = "fp16"):
112
+ """Export ONNX model to TensorRT engine."""
113
+ step = "4" if precision == "fp16" else "5"
114
+ print(f"\n[{step}/5] TENSORRT {precision.upper()} EXPORT")
115
+ import tensorrt as trt
116
+
117
+ # Find ONNX model (optimum exports to onnx/ directory)
118
+ onnx_dir = Path(EXPORT_DIR) / "onnx"
119
+ onnx_candidates = list(onnx_dir.glob("*.onnx")) if onnx_dir.exists() else []
120
+ # Also check flat file
121
+ flat_onnx = Path(EXPORT_DIR) / "agora_planner_v1.onnx"
122
+ if flat_onnx.exists():
123
+ onnx_candidates.append(flat_onnx)
124
+
125
+ if not onnx_candidates:
126
+ print(f" ERROR: No ONNX model found in {onnx_dir} or {EXPORT_DIR}")
127
+ return False
128
+
129
+ # Use the largest ONNX file (the main model, not decoder subgraph)
130
+ onnx_path = max(onnx_candidates, key=lambda p: p.stat().st_size)
131
+ trt_path = Path(EXPORT_DIR) / f"agora_planner_v1_trt_{precision}.engine"
132
+
133
+ if trt_path.exists():
134
+ print(f" Already exists: {trt_path} ({trt_path.stat().st_size / 1e9:.2f} GB)")
135
+ return True
136
+
137
+ print(f" ONNX source: {onnx_path} ({onnx_path.stat().st_size / 1e9:.2f} GB)")
138
+ print(f" Building TRT {precision.upper()} engine...")
139
+
140
+ logger = trt.Logger(trt.Logger.WARNING)
141
+ builder = trt.Builder(logger)
142
+ network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
143
+ parser = trt.OnnxParser(network, logger)
144
+
145
+ # For external data models, set the model path for parser
146
+ parser.set_flag(trt.OnnxParserFlag.NATIVE_INSTANCENORM)
147
+
148
+ print(" Parsing ONNX model...")
149
+ success = parser.parse_from_file(str(onnx_path))
150
+ if not success:
151
+ for i in range(parser.num_errors):
152
+ print(f" PARSE ERROR: {parser.get_error(i)}")
153
+ return False
154
+
155
+ config = builder.create_builder_config()
156
+ config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 4 << 30) # 4 GB
157
+
158
+ if precision == "fp16":
159
+ if builder.platform_has_fast_fp16:
160
+ config.set_flag(trt.BuilderFlag.FP16)
161
+ print(" FP16 enabled")
162
+ else:
163
+ print(" WARNING: FP16 not supported, falling back to FP32")
164
+
165
+ # Set optimization profiles for dynamic shapes
166
+ profile = builder.create_optimization_profile()
167
+ for i in range(network.num_inputs):
168
+ inp = network.get_input(i)
169
+ name = inp.name
170
+ shape = inp.shape
171
+ # Build min/opt/max from shape, handling dynamic dims (-1)
172
+ min_shape = tuple(1 if d == -1 else d for d in shape)
173
+ opt_shape = tuple(1 if d == -1 else d for d in shape)
174
+ opt_shape = tuple(512 if i == len(shape) - 1 and d == -1 else (1 if d == -1 else d)
175
+ for i, d in enumerate(shape))
176
+ max_shape = tuple(1024 if d == -1 else d for d in shape)
177
+ # Override batch dim
178
+ if len(shape) >= 2:
179
+ min_shape = (1,) + min_shape[1:]
180
+ opt_shape = (1,) + opt_shape[1:]
181
+ max_shape = (4,) + max_shape[1:]
182
+ profile.set_shape(name, min_shape, opt_shape, max_shape)
183
+ print(f" Input '{name}': min={min_shape} opt={opt_shape} max={max_shape}")
184
+ config.add_optimization_profile(profile)
185
+
186
+ print(f" Building engine (this may take 10-30 minutes)...")
187
+ t0 = time.time()
188
+ engine_bytes = builder.build_serialized_network(network, config)
189
+ elapsed = time.time() - t0
190
+
191
+ if engine_bytes is None:
192
+ print(" ERROR: TRT engine build failed")
193
+ return False
194
+
195
+ with open(trt_path, "wb") as f:
196
+ f.write(engine_bytes)
197
+
198
+ size_gb = trt_path.stat().st_size / 1e9
199
+ print(f" Saved: {trt_path} ({size_gb:.2f} GB) in {elapsed:.0f}s")
200
+ return True
201
+
202
+
203
+ def main():
204
+ print("=" * 60)
205
+ print("AGORA PLANNER — FULL EXPORT PIPELINE")
206
+ print("=" * 60)
207
+ print(f"Source: {MERGED_DIR}")
208
+ print(f"Output: {EXPORT_DIR}")
209
+ print(f"Device: {torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU'}")
210
+
211
+ results = {}
212
+
213
+ results["safetensors"] = export_safetensors()
214
+ results["pth"] = export_pth()
215
+ results["onnx"] = export_onnx()
216
+ results["trt_fp16"] = export_trt("fp16")
217
+ results["trt_fp32"] = export_trt("fp32")
218
+
219
+ print("\n" + "=" * 60)
220
+ print("EXPORT RESULTS")
221
+ print("=" * 60)
222
+ for fmt, ok in results.items():
223
+ status = "PASS" if ok else "FAIL"
224
+ print(f" [{status}] {fmt}")
225
+
226
+ # List all exports
227
+ print(f"\nFiles in {EXPORT_DIR}:")
228
+ for f in sorted(Path(EXPORT_DIR).rglob("*")):
229
+ if f.is_file():
230
+ size = f.stat().st_size
231
+ if size > 1e9:
232
+ print(f" {f.relative_to(EXPORT_DIR)}: {size / 1e9:.2f} GB")
233
+ elif size > 1e6:
234
+ print(f" {f.relative_to(EXPORT_DIR)}: {size / 1e6:.0f} MB")
235
+ else:
236
+ print(f" {f.relative_to(EXPORT_DIR)}: {size / 1e3:.0f} KB")
237
+
238
+ all_pass = all(results.values())
239
+ print(f"\nOVERALL: {'ALL PASS' if all_pass else 'SOME FAILED'}")
240
+ return 0 if all_pass else 1
241
+
242
+
243
+ if __name__ == "__main__":
244
+ import sys
245
+ sys.exit(main())
scripts/generate_planning_data.py CHANGED
@@ -22,21 +22,20 @@ from pathlib import Path
22
  # Ensure the package is importable
23
  sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
24
 
25
- from anima_agora.control.brain import Brain, BrainConfig, PlanningStrategy
26
  from anima_agora.control.contracts import TaskRequest
27
- from anima_agora.control.decision_engine import DecisionEngine
28
  from anima_agora.memory.stem_core import (
29
  EmbodimentProfile,
 
 
30
  RobotCapability,
31
  RobotState,
32
- STEMMemoryState,
33
  SceneGraph,
34
  SemanticLandmark,
35
- Pose,
36
- Vector3D,
37
- Quaternion,
38
  TaskEvent,
39
  TaskStatus,
 
40
  )
41
 
42
  # ---------------------------------------------------------------------------
@@ -488,7 +487,7 @@ if __name__ == "__main__":
488
  output_path=args.output,
489
  seed=args.seed,
490
  ))
491
- print(f"\n=== Dataset Statistics ===")
492
  print(f"Total examples: {stats.total}")
493
  print(f"Fully assigned: {stats.fully_assigned}")
494
  print(f"Partial: {stats.partial}")
 
22
  # Ensure the package is importable
23
  sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
24
 
25
+ from anima_agora.control.brain import Brain, BrainConfig
26
  from anima_agora.control.contracts import TaskRequest
 
27
  from anima_agora.memory.stem_core import (
28
  EmbodimentProfile,
29
+ Pose,
30
+ Quaternion,
31
  RobotCapability,
32
  RobotState,
 
33
  SceneGraph,
34
  SemanticLandmark,
35
+ STEMMemoryState,
 
 
36
  TaskEvent,
37
  TaskStatus,
38
+ Vector3D,
39
  )
40
 
41
  # ---------------------------------------------------------------------------
 
487
  output_path=args.output,
488
  seed=args.seed,
489
  ))
490
+ print("\n=== Dataset Statistics ===")
491
  print(f"Total examples: {stats.total}")
492
  print(f"Fully assigned: {stats.fully_assigned}")
493
  print(f"Partial: {stats.partial}")
scripts/train_planner.py CHANGED
@@ -67,6 +67,9 @@ def main():
67
  parser.add_argument("--save-steps", type=int, default=100, help="Save every N steps")
68
  parser.add_argument("--logging-steps", type=int, default=10, help="Log every N steps")
69
  parser.add_argument("--bf16", action="store_true", default=True, help="Use bf16")
 
 
 
70
  parser.add_argument("--merge-and-save", action="store_true", default=True,
71
  help="Merge LoRA weights into base model after training")
72
  args = parser.parse_args()
@@ -117,10 +120,10 @@ def main():
117
  # ---------------------------------------------------------------------------
118
  # Load tokenizer and model with LoRA
119
  # ---------------------------------------------------------------------------
120
- from transformers import AutoModelForCausalLM, AutoTokenizer
121
- from peft import LoraConfig, get_peft_model, TaskType
122
  from datasets import load_dataset
123
- from trl import SFTTrainer, SFTConfig
 
 
124
 
125
  print("\nLoading tokenizer...")
126
  tokenizer = AutoTokenizer.from_pretrained(
@@ -189,9 +192,11 @@ def main():
189
  gradient_checkpointing=True,
190
  gradient_checkpointing_kwargs={"use_reentrant": False},
191
  max_length=args.max_seq_len,
 
192
  report_to=["tensorboard"],
193
  seed=42,
194
- dataloader_num_workers=2,
 
195
  remove_unused_columns=True,
196
  packing=False,
197
  )
@@ -212,7 +217,7 @@ def main():
212
 
213
  # Log final metrics
214
  metrics = train_result.metrics
215
- print(f"\n=== Training Complete ===")
216
  print(f"Train loss: {metrics.get('train_loss', 'N/A')}")
217
  print(f"Train runtime: {metrics.get('train_runtime', 'N/A'):.1f}s")
218
  print(f"Train samples/s: {metrics.get('train_samples_per_second', 'N/A'):.1f}")
@@ -234,13 +239,13 @@ def main():
234
 
235
  # Merge and save full model
236
  if args.merge_and_save:
237
- print(f"Merging LoRA weights into base model...")
238
  merged_model = model.merge_and_unload()
239
  merged_path = f"{MODEL_DIR}/merged"
240
  print(f"Saving merged model to: {merged_path}")
241
  merged_model.save_pretrained(merged_path)
242
  tokenizer.save_pretrained(merged_path)
243
- print(f"Merged model saved successfully.")
244
 
245
  # Save model card
246
  card_path = f"{MODEL_DIR}/README.md"
 
67
  parser.add_argument("--save-steps", type=int, default=100, help="Save every N steps")
68
  parser.add_argument("--logging-steps", type=int, default=10, help="Log every N steps")
69
  parser.add_argument("--bf16", action="store_true", default=True, help="Use bf16")
70
+ parser.add_argument("--num-workers", type=int, default=2, help="Dataloader num_workers")
71
+ parser.add_argument("--pin-memory", action="store_true", default=False, help="Pin memory")
72
+ parser.add_argument("--max-steps", type=int, default=-1, help="Max steps (-1=full run)")
73
  parser.add_argument("--merge-and-save", action="store_true", default=True,
74
  help="Merge LoRA weights into base model after training")
75
  args = parser.parse_args()
 
120
  # ---------------------------------------------------------------------------
121
  # Load tokenizer and model with LoRA
122
  # ---------------------------------------------------------------------------
 
 
123
  from datasets import load_dataset
124
+ from peft import LoraConfig, TaskType, get_peft_model
125
+ from transformers import AutoModelForCausalLM, AutoTokenizer
126
+ from trl import SFTConfig, SFTTrainer
127
 
128
  print("\nLoading tokenizer...")
129
  tokenizer = AutoTokenizer.from_pretrained(
 
192
  gradient_checkpointing=True,
193
  gradient_checkpointing_kwargs={"use_reentrant": False},
194
  max_length=args.max_seq_len,
195
+ max_steps=args.max_steps,
196
  report_to=["tensorboard"],
197
  seed=42,
198
+ dataloader_num_workers=args.num_workers,
199
+ dataloader_pin_memory=args.pin_memory,
200
  remove_unused_columns=True,
201
  packing=False,
202
  )
 
217
 
218
  # Log final metrics
219
  metrics = train_result.metrics
220
+ print("\n=== Training Complete ===")
221
  print(f"Train loss: {metrics.get('train_loss', 'N/A')}")
222
  print(f"Train runtime: {metrics.get('train_runtime', 'N/A'):.1f}s")
223
  print(f"Train samples/s: {metrics.get('train_samples_per_second', 'N/A'):.1f}")
 
239
 
240
  # Merge and save full model
241
  if args.merge_and_save:
242
+ print("Merging LoRA weights into base model...")
243
  merged_model = model.merge_and_unload()
244
  merged_path = f"{MODEL_DIR}/merged"
245
  print(f"Saving merged model to: {merged_path}")
246
  merged_model.save_pretrained(merged_path)
247
  tokenizer.save_pretrained(merged_path)
248
+ print("Merged model saved successfully.")
249
 
250
  # Save model card
251
  card_path = f"{MODEL_DIR}/README.md"
tensorrt/agora_planner_v1_trt_fp16.engine ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:239307967fffd8ff0971ed7fff7f4cfe302ca18eec4821da4f20431130dc849f
3
+ size 3558097844
tensorrt/agora_planner_v1_trt_fp32.engine ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:430e84906805ef18d62490b868ac4da6217722f8b65483b6019f9febe03bebb6
3
+ size 7112493668