Skip to content

Commit d48ea74

Browse files
authored
Merge branch 'development' into kd/pixi_test_fix
2 parents 54f2322 + 876d569 commit d48ea74

64 files changed

Lines changed: 5421 additions & 839 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build_and_push_docker.yaml

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,49 +7,45 @@ on:
77
- development
88
workflow_dispatch:
99

10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
1014
jobs:
1115
build-and-push:
12-
runs-on: self-hosted
16+
runs-on: [self-hosted, cpu, heavy, gpu]
1317
steps:
1418
- name: Checkout code
15-
uses: actions/checkout@v4
19+
uses: actions/checkout@v6
20+
with:
21+
lfs: true
1622

1723
- name: Log in to Docker Hub
1824
uses: docker/login-action@v3
1925
with:
2026
username: ${{ secrets.DOCKERHUB_USERNAME }}
2127
password: ${{ secrets.DOCKERHUB_TOKEN }}
2228

23-
- name: Set up Docker Buildx
24-
uses: docker/setup-buildx-action@v3
29+
- name: Build and push all images
30+
env:
31+
AMM_IMG_SUFFIX: ${{ env.SUFFIX }}
32+
run: |
33+
docker compose -f docker/compose.yaml build
34+
docker compose -f docker/compose.yaml push
2535
26-
- name: Build and push ROS 2 image
27-
uses: docker/build-push-action@v6
28-
with:
29-
context: .
30-
file: docker/Dockerfile.ros2
31-
push: true
32-
tags: |
33-
robotecai/mobile-manipulator-demo-ros2${{ github.ref_name == 'development' && '-development' || '' }}:latest
34-
35-
- name: Build and push Agents image
36-
uses: docker/build-push-action@v6
36+
smoke-test:
37+
needs: build-and-push
38+
runs-on: [self-hosted, cpu, heavy, gpu]
39+
steps:
40+
- name: Log in to Docker Hub
41+
uses: docker/login-action@v3
3742
with:
38-
context: .
39-
file: docker/Dockerfile.agents
40-
push: true
41-
build-args: |
42-
ROS2_IMAGE=robotecai/mobile-manipulator-demo-ros2${{ github.ref_name == 'development' && '-development' || '' }}:latest
43-
tags: |
44-
robotecai/mobile-manipulator-demo-agents${{ github.ref_name == 'development' && '-development' || '' }}:latest
43+
username: ${{ secrets.DOCKERHUB_USERNAME }}
44+
password: ${{ secrets.DOCKERHUB_TOKEN }}
4545

46-
- name: Build and push O3DE image
47-
uses: docker/build-push-action@v6
48-
with:
49-
context: .
50-
file: docker/Dockerfile.o3de
51-
push: true
52-
build-args: |
53-
ROS2_IMAGE=robotecai/mobile-manipulator-demo-ros2${{ github.ref_name == 'development' && '-development' || '' }}:latest
54-
tags: |
55-
robotecai/mobile-manipulator-demo-o3de${{ github.ref_name == 'development' && '-development' || '' }}:latest
46+
- name: Headless sim smoke test (/clock)
47+
run: |
48+
IMAGE="robotecai/agentic-mobile-manipulator-o3de:latest"
49+
docker pull "$IMAGE"
50+
docker run --rm --entrypoint pixi "$IMAGE" run sim-smoke
51+

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
gems/
22
engine/
33
external/
4-
inference/
54
ros2_ws/install
65
ros2_ws/build
76
ros2_ws/src/packages
@@ -21,5 +20,7 @@ imgui.ini
2120
anomaly_images/
2221
package_condition_images/
2322
models/
23+
runs/
2424
*.bak0
2525
sim/user/
26+
backtrace.log

.gitmodules

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[submodule "inference/llama.cpp"]
2+
path = inference/llama.cpp
3+
url = https://github.com/ggml-org/llama.cpp.git
4+
shallow = true
5+
[submodule "inference/FastFlowLM"]
6+
path = inference/FastFlowLM
7+
url = https://github.com/RobotecAI/FastFlowLM.git
8+
shallow = true

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ _Physical AI for Warehouse Robotics_
55
<div align="center">
66

77
![Small warehouse featuring Robotnik Kairos](docs/images/demo_comp.jpg)
8+
9+
![CPU: ready](https://img.shields.io/badge/CPU-ready-ED1C24?style=for-the-badge&logo=amd&logoColor=white&labelColor=000000)
10+
![GPU: ready](https://img.shields.io/badge/GPU-ready-ED1C24?style=for-the-badge&logo=amd&logoColor=white&labelColor=000000)
11+
![NPU: ready](https://img.shields.io/badge/NPU-ready-ED1C24?style=for-the-badge&logo=amd&logoColor=white&labelColor=000000)
12+
813
![Static Badge](https://img.shields.io/badge/AgenticAI-darkred)
914
![Static Badge](https://img.shields.io/badge/EmbodiedAI-black)
1015
![Static Badge](https://img.shields.io/badge/LLM-darkred)
@@ -41,6 +46,7 @@ Backup video link: https://vimeo.com/1138627688/dbfa881a27
4146
- Hardware platform: **AMD Ryzen™ AI** processor hosting robotics stack and embodied agentic AI multi-agent system
4247
- Model stack: [**Liquid AI's LFM2-VL**](https://www.liquid.ai/) (Vision-Language Model) optimized for AMD hardware
4348
- Local LLM (gpt-oss-20b) for planning, instruction parsing, and dialogue
49+
- Local inference across **CPU, GPU (llama.cpp / Vulkan) and the AMD Ryzen™ AI NPU (FastFlowLM)** — the safety VLM runs on the NPU, freeing the GPU for the LLM and perception VLM
4450
- Robot platform: Mobile manipulator (Robotnik Kairos) in ROS 2 simulated warehouse environment
4551
- Simulation setup: Hardware-in-the-loop mirrors real-world deployment for safe testing
4652
- Benefits: On-device reasoning, low latency, privacy, and seamless simulation-to-hardware transition

cloud_config.toml

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,88 @@
1+
# ════════════════════════════════════════════════════════════════════════════
2+
# Cloud variant of the inference SSOT (see config.toml for the full schema).
3+
#
4+
# Here the chat/vision endpoints are remote (backend = "openai") and therefore
5+
# NOT served locally — `pixi run inference` skips backend = "openai". Only the
6+
# RAG embedding/reranker still run locally on llama.cpp, exactly as in
7+
# config.toml.
8+
#
9+
# To use it: everything (agents, `pixi run inference`, download-models) reads
10+
# config.toml, so swap this file in and provide the API key:
11+
# cp cloud_config.toml config.toml && export OPENAI_API_KEY=sk-...
12+
#
13+
# Because cloud agents use several distinct remote models, endpoints are split
14+
# per model (chat_mini / vlm_mini / vlm_nano) and agents reference the right one.
15+
# ════════════════════════════════════════════════════════════════════════════
16+
17+
# ─── Endpoints ──────────────────────────────────────────────────────────────
18+
19+
[endpoints.chat_mini]
20+
type = "llm"
21+
backend = "openai"
22+
model = "gpt-5-mini"
23+
base_url = "https://api.openai.com/v1"
24+
25+
[endpoints.vlm_mini]
26+
type = "vlm"
27+
backend = "openai"
28+
model = "gpt-5-mini"
29+
base_url = "https://api.openai.com/v1"
30+
31+
[endpoints.vlm_nano]
32+
type = "vlm"
33+
backend = "openai"
34+
model = "gpt-5-nano"
35+
base_url = "https://api.openai.com/v1"
36+
37+
# RAG models are the only endpoints still served locally (backend = "gpu"):
38+
# `pixi run inference` launches these two llama.cpp servers and skips the
39+
# remote backend = "openai" endpoints above. Same specs as config.toml.
40+
[endpoints.embeddings]
41+
type = "embedding"
42+
backend = "gpu"
43+
model = "qwen3-embedding:0.6b"
44+
host = "localhost"
45+
port = 8082
46+
model_path = "${DEMO_ROOT}/models/Qwen3-Embedding-0.6B-Q8_0.gguf"
47+
model_url = "https://huggingface.co/Qwen/Qwen3-Embedding-0.6B-GGUF/resolve/main/Qwen3-Embedding-0.6B-Q8_0.gguf?download=true"
48+
49+
# NOTE: The reranker and the embedding model are two separate models!
50+
[endpoints.reranker]
51+
type = "reranker"
52+
backend = "gpu"
53+
model = "qwen3-reranker:0.6b"
54+
host = "localhost"
55+
port = 8083
56+
model_path = "${DEMO_ROOT}/models/qwen3-reranker-0.6b-q8_0.gguf"
57+
model_url = "https://huggingface.co/ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/resolve/main/qwen3-reranker-0.6b-q8_0.gguf?download=true"
58+
59+
# ─── Agents ─────────────────────────────────────────────────────────────────
60+
161
[general]
2-
llm_model = "gpt-5-mini"
3-
llm_base_url = "https://api.openai.com/v1"
62+
llm = "chat_mini"
63+
vlm = "vlm_mini"
464
llm_reasoning = false
5-
vlm_model = "gpt-5-mini"
6-
vlm_base_url = "https://api.openai.com/v1"
765
vlm_reasoning = false
866

9-
# megamind agent utilizes llm for task delegation
10-
# to agents using both llm and vlm for task execution
1167
[megamind_agent]
12-
llm_model = "gpt-5-mini"
13-
llm_base_url = "https://api.openai.com/v1"
14-
vlm_model = "gpt-5-mini"
15-
vlm_base_url = "https://api.openai.com/v1"
68+
llm = "chat_mini"
69+
vlm = "vlm_mini"
1670
llm_reasoning = false
1771
vlm_reasoning = false
1872

19-
# inspection agent uses vlm to determine anomalies in the image
2073
[inspection_agent]
21-
vlm_model = "gpt-5-nano"
22-
vlm_base_url = "https://api.openai.com/v1"
74+
vlm = "vlm_nano"
2375
vlm_reasoning = false
2476
vlm_temperature = 0.0
2577

2678
[condition_agent]
27-
vlm_model = "gpt-5-mini"
28-
vlm_base_url = "https://api.openai.com/v1"
79+
vlm = "vlm_mini"
2980
vlm_reasoning = false
3081
vlm_temperature = 0.1
3182

32-
# safety agent uses vlm to determine anomalies in the image
33-
# and embeddings to determine OSHA safety violations
3483
[safety_agent]
35-
vlm_model = "gpt-5-nano"
36-
vlm_base_url = "https://api.openai.com/v1"
84+
vlm = "vlm_nano"
3785
vlm_reasoning = false
3886
vlm_temperature = 0.0
39-
40-
embeddings_model = "qwen3-embedding:0.6b"
41-
embeddings_base_url = "http://safety-embeddings:8082/v1"
42-
# NOTE: The reranker and the embedding model are two separate models!
43-
reranker_base_url = "http://safety-reranker:8083/v1/reranking"
44-
45-
# INFO:
46-
# Start the EMBEDDING model in llama.cpp using the following command:
47-
# ./llama-server -m /home/user/llamas/gguf/custom_ggufs/Qwen3-Embedding-0.6b_Q8_0.gguf --embedding --pooling last -c 4096 -b 2048 -ub 2048 --port 8082
48-
49-
# INFO:
50-
# Start the RERANKER model in llama.cpp using the following command:
51-
# ./llama-server -m /home/user/llamas/gguf/custom_ggufs/Qwen3-Reranker-0.6B_BF16.gguf --embedding --pooling rank -c 4096 -b 2048 -ub 2048 --port 8083
87+
embeddings = "embeddings"
88+
reranker = "reranker"

config.toml

Lines changed: 82 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,105 @@
1+
# ════════════════════════════════════════════════════════════════════════════
2+
# Inference SSOT — single source of truth for every model the demo serves.
3+
#
4+
# [endpoints.*] define each served model EXACTLY ONCE: which backend runs it,
5+
# on which host/port, and from which weights. `pixi run inference` reads these,
6+
# routes each endpoint to the right backend, and launches it:
7+
#
8+
# backend = "gpu" | "cpu" → llama.cpp (built in inference/llama.cpp)
9+
# backend = "npu" → FastFlowLM (`flm serve`, AMD Ryzen AI NPU)
10+
# backend = "openai" → remote API, NOT served locally (see cloud_config.toml)
11+
#
12+
# Agent sections below reference endpoints by name (e.g. `llm = "main_llm"`).
13+
# rai_app/initialization/llms.py resolves those references into model + base_url,
14+
# so changing a port, moving a model gpu↔npu, or swapping weights happens HERE,
15+
# in one place — not scattered across pixi tasks, smoke_test.sh, etc.
16+
# ════════════════════════════════════════════════════════════════════════════
17+
18+
# ─── Endpoints ──────────────────────────────────────────────────────────────
19+
20+
[endpoints.main_llm]
21+
type = "llm"
22+
backend = "gpu" # llama.cpp, Vulkan build
23+
model = "gpt-oss-20b" # name advertised to OpenAI-compatible clients
24+
host = "localhost"
25+
port = 8080
26+
model_path = "${DEMO_ROOT}/models/gpt-oss-20b-Q4_K_M.gguf"
27+
model_url = "https://huggingface.co/unsloth/gpt-oss-20b-GGUF/resolve/main/gpt-oss-20b-Q4_K_M.gguf?download=true"
28+
extra_args = ["--no-prefill-assistant"]
29+
30+
# safety agent VLM — FastFlowLM (npu)
31+
[endpoints.vlm_safety]
32+
type = "vlm"
33+
backend = "npu"
34+
host = "localhost"
35+
port = 8081
36+
model = "gemma3:4b"
37+
flm_model = "gemma3:4b"
38+
39+
# inspection/general/megamind/condition VLM — LFM2-VL on llama.cpp (gpu)
40+
[endpoints.vlm_inspection]
41+
type = "vlm"
42+
backend = "gpu"
43+
host = "localhost"
44+
port = 8084
45+
model = "LFM2-VL-3B"
46+
model_path = "${DEMO_ROOT}/models/LFM2-VL-3B-Q8_0.gguf"
47+
mmproj_path = "${DEMO_ROOT}/models/mmproj-LFM2-VL-3B-Q8_0.gguf"
48+
model_url = "https://huggingface.co/LiquidAI/LFM2-VL-3B-GGUF/resolve/main/LFM2-VL-3B-Q8_0.gguf?download=true"
49+
mmproj_url = "https://huggingface.co/LiquidAI/LFM2-VL-3B-GGUF/resolve/main/mmproj-LFM2-VL-3B-Q8_0.gguf?download=true"
50+
51+
[endpoints.embeddings]
52+
type = "embedding"
53+
backend = "gpu"
54+
model = "qwen3-embedding:0.6b"
55+
host = "localhost"
56+
port = 8082
57+
model_path = "${DEMO_ROOT}/models/Qwen3-Embedding-0.6B-Q8_0.gguf"
58+
model_url = "https://huggingface.co/Qwen/Qwen3-Embedding-0.6B-GGUF/resolve/main/Qwen3-Embedding-0.6B-Q8_0.gguf?download=true"
59+
60+
[endpoints.reranker]
61+
type = "reranker"
62+
backend = "gpu"
63+
model = "qwen3-reranker:0.6b"
64+
host = "localhost"
65+
port = 8083
66+
model_path = "${DEMO_ROOT}/models/qwen3-reranker-0.6b-q8_0.gguf"
67+
model_url = "https://huggingface.co/ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/resolve/main/qwen3-reranker-0.6b-q8_0.gguf?download=true"
68+
69+
# ─── Agents ─────────────────────────────────────────────────────────────────
70+
# Each agent references endpoints by name. `*_reasoning` / `*_temperature` are
71+
# client-side decoding settings and stay with the agent, not the endpoint.
72+
173
[general]
2-
llm_model = "gpt-oss-20b"
3-
llm_base_url = "http://localhost:8080/v1"
74+
llm = "main_llm"
75+
vlm = "vlm_inspection"
476
llm_reasoning = false
5-
vlm_model = "lfm2-vl"
6-
vlm_base_url = "http://localhost:8081/v1"
777
vlm_reasoning = false
878

979
# megamind agent utilizes llm for task delegation
1080
# to agents using both llm and vlm for task execution
1181
[megamind_agent]
12-
llm_model = "gpt-oss-20b"
13-
llm_base_url = "http://localhost:8080/v1"
14-
vlm_model = "lfm2-vl"
15-
vlm_base_url = "http://localhost:8081/v1"
82+
llm = "main_llm"
83+
vlm = "vlm_inspection"
1684
llm_reasoning = false
1785
vlm_reasoning = false
1886

1987
# inspection agent uses vlm to determine anomalies in the image
2088
[inspection_agent]
21-
vlm_model = "lfm2-vl"
22-
vlm_base_url = "http://localhost:8081/v1"
89+
vlm = "vlm_inspection"
2390
vlm_reasoning = false
2491
vlm_temperature = 0.0
2592

2693
[condition_agent]
27-
vlm_model = "lfm2-vl"
28-
vlm_base_url = "http://localhost:8081/v1"
94+
vlm = "vlm_inspection"
2995
vlm_reasoning = false
3096
vlm_temperature = 0.1
3197

3298
# safety agent uses vlm to determine anomalies in the image
33-
# and embeddings to determine OSHA safety violations
99+
# and embeddings + reranker to determine OSHA safety violations
34100
[safety_agent]
35-
vlm_model = "lfm2-vl"
36-
vlm_base_url = "http://localhost:8081/v1"
101+
vlm = "vlm_safety"
37102
vlm_reasoning = false
38103
vlm_temperature = 0.0
39-
40-
embeddings_model = "qwen3-embedding:0.6b"
41-
embeddings_base_url = "http://localhost:8082/v1"
42-
# NOTE: The reranker and the embedding model are two separate models!
43-
reranker_base_url = "http://localhost:8083/v1/reranking"
44-
104+
embeddings = "embeddings"
105+
reranker = "reranker"

docker/Dockerfile.agents

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)