Agent-agnostic HPC Assistant for AMD GPU systems — site-specific skills, a RAG knowledge base, and an inference proxy for HPC login nodes.
The HPC Assistant brings safe, site-aware AI coding agents to HPC login nodes where direct LLM API access and pip install are typically unavailable. It ships site-specific skills (Slurm job management, resource and quota commands, AMD GPU profiling with rocprofv3), a RAG knowledge base built from HPC documentation and served over MCP, and an Anthropic-to-OpenAI inference proxy so users can route through alternative backends (e.g. Aitta on LUMI). Skills are stored in a generic markdown format that works with Claude Code, OpenCode, and other agent frameworks. Targets AMD Instinct GPU workloads on AMD-equipped HPC systems such as LUMI.
- RAG Knowledge Base — pluggable backend: a managed remote MCP (e.g. the LUMI AI Factory MCP, the recommended default for LUMI) or a local ChromaDB built from cloned HPC docs and served via MCP
- Agent Skills — site-specific and generic skills for resource management, Slurm job management, and GPU profiling
- Aitta Proxy — LiteLLM proxy (Anthropic-to-OpenAI translation) for using alternative inference backends (e.g. Aitta service on LUMI)
- Agent-Agnostic — skills are stored in a generic format; supports Claude Code and OpenCode, with the option to add other frameworks
- Login-Node Friendly — no
pip installrequired; all Python dependencies run inside Singularity (or Apptainer) containers
| Site | Skills | Knowledge base | Documentation |
|---|---|---|---|
| LUMI | Profiling, Resource Management, Slurm Job Management | Remote MCP (LUMI AI Factory, default) or local ChromaDB (optional) | LUMI User Guide |
Layout at a glance:
hpc-assistant/
├── skills/ # Agent skills (copied into your project by setup)
├── geak/ # Optional GEAK workflow — see geak/GEAK.md
│ ├── GEAK.md, run-geak.sbatch, setup-geak.sh, commandment-config.yaml, matmul_kernel.py
│ ├── THIRD_PARTY_DOWNLOAD_NOTICE.txt, download-notice.sh, accept-download-notice.sh
│ ├── claude/ # hooks + geak-monitor agent
│ └── opencode/ # geak-guards plugin + geak-monitor agent
├── setup/ # setup.sh, teardown.sh, launch.sh, config.sh, build_db.py, templates/, dockerfiles/, requirements/
├── bin/hpc-assistant-init # Project init (with module load)
├── modulefiles/hpc-assistant/ # Lmod module for site installs
├── LICENSE, LICENSE.TXT # MIT + AMD license summary
├── THIRD-PARTY-NOTICES.txt
├── SECURITY.md
└── .github/ # CI, issue/PR templates, CODEOWNERS
Created in your project directory by setup (not at the repository root):
- OpenCode:
.opencode/skills/,opencode.json,AGENTS.md - Claude Code:
.claude/skills/,CLAUDE.md(and.claude/hooks/,agents/when GEAK is enabled) - GEAK (opt-in): copies
run-geak.sbatch,setup-geak.sh, etc. to the project root; writes.geak-third-party-download-acceptedafter you accept the download notice
- Singularity (or Apptainer — same CLI on most sites) available on the HPC system
- One of:
- Aitta API token (recommended for LUMI)
- Anthropic API key
If using Claude Code as the agent framework:
- Claude Code CLI installed:
curl -fsSL https://claude.ai/install.sh | bash
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrcIf using OpenCode as the agent framework:
- OpenCode installed:
curl -fsSL https://opencode.ai/install | bash
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrcThere are three ways to set up the assistant:
- LUMI module + init —
module load+hpc-assistant-init(no full wizard) - Setup wizard (recommended for first-time / custom options)
- Manual setup if you prefer to configure files yourself
For LUMI (or any site that installs this repository under /appl or a shared path), use the included Lmod module and init script instead of running the full setup wizard. This installs skills and copies the LUMI OpenCode templates into your project directory; it does not prompt for credentials or start containers.
# Adjust the modulefiles path for your site install
module use /path/to/hpc-assistant/modulefiles
module load hpc-assistant
# Optional: LUMI AI Factory containerized OpenCode
module load Local-LAIF lumi-aif-agents
cd ~/projects/myapp
# Default: OpenCode + Anthropic
hpc-assistant-init
export ANTHROPIC_API_KEY=your-key-here
opencode
# Or select the Aitta backend (OpenCode talks to Aitta directly)
hpc-assistant-init --inference aitta
export AITTA_TOKEN=your-token-here
opencodehpc-assistant-init uses the same logic as the setup wizard for skills and templates (configure_opencode_project in setup/config.sh). By default it writes AGENTS.md, opencode.json, and .opencode/skills/ under the project directory. Existing AGENTS.md / opencode.json files are left unchanged unless you pass --force.
| Flag | Purpose |
|---|---|
-d DIR |
Project directory (default: current directory) |
--force |
Overwrite existing agent config files |
--framework opencode|claude-code |
Agent framework (default: opencode) |
--inference anthropic|aitta |
Inference backend (default: anthropic for both frameworks) |
--sif-path PATH |
Path to aitta-proxy.sif (claude-code + aitta only) |
--geak |
LUMI only: add GEAK skills + install hooks (Claude) or plugin (OpenCode) + run-geak.sbatch etc. |
--no-save-config |
Skip writing ~/.config/hpc-assistant/config.env |
GEAK + module path: run hpc-assistant-init --geak from your project directory. Export GEAK_ANTHROPIC_API_KEY before init if you want .anthropic_api_key written in the project (GEAK jobs call Anthropic from the compute node, independent of Aitta/OpenCode login-node inference). SLURM jobs use the LUMI multitorch image at LUMI_MULTITORCH_SIF or the default under /appl/local/laifs/containers/. run-geak.sbatch loads lumi-aif-singularity-bindings — align with your site modules if paths differ.
Claude Code + Aitta: OpenCode talks to Aitta directly, but Claude Code only speaks the Anthropic API, so it must go through the LiteLLM proxy (
aitta-proxy.sif). For that combination, runhpc-assistant-init --framework claude-code --inference aitta --sif-path <…>/aitta-proxy.sif, then usesetup/setup.sh(credentials) andsetup/launch.sh(starts the proxy) — the module init alone does not start containers.
Site operators can install the tree under e.g. /appl/local/laifs/hpc-assistant and register modulefiles/ in the site module path; the module resolves HPC_ASSISTANT_ROOT from the modulefile location automatically.
The module also sets OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true, matching setup/launch.sh when launching OpenCode (non-blocking background subagents, e.g. GEAK’s geak-monitor; OpenCode ≥ 1.14.51). Unload the module if you need that variable unset.
Skip this step entirely if you are on LUMI using the default configuration (remote MCP + Aitta + OpenCode). No containers are needed for that path.
Images are published to Docker Hub in the public
amdenterpriseai/hpc-assistant
repository. All images share that one repository and are distinguished by a tag
prefix (e.g. amdenterpriseai/hpc-assistant:aitta-proxy-latest). The repository
is public, so no Docker Hub login is required to pull.
Pull them on the HPC login node (into your SIF directory, e.g. ~/.local/share/hpc-assistant/sif/):
mkdir -p ~/.local/share/hpc-assistant/sif/
cd ~/.local/share/hpc-assistant/sif/
# Aitta proxy (LiteLLM) — only needed when using Aitta inference with Claude Code.
# OpenCode speaks OpenAI API natively and connects to Aitta directly; no proxy needed.
# This image is the official LiteLLM proxy with our Aitta config baked in; see
# setup/dockerfiles/aitta-proxy.dockerfile.
singularity build aitta-proxy.sif docker://docker.io/amdenterpriseai/hpc-assistant:aitta-proxy-latest
# The two images below are ONLY needed if you choose the "local ChromaDB"
# knowledge-base backend during setup. If you use a managed remote MCP (the
# default for LUMI), you can skip both.
singularity build build-db.sif docker://docker.io/amdenterpriseai/hpc-assistant:build-db-latest
singularity build chroma-mcp.sif docker://docker.io/amdenterpriseai/hpc-assistant:chroma-mcp-latestbash setup/setup.shThe setup wizard will:
- Ask you to select your HPC site (LUMI)
- Use the site's managed remote MCP as the knowledge base automatically (LUMI ships one, so there's nothing to build or host and no question is asked)
- Offer to use default LUMI settings (Aitta +
openai/gpt-oss-120b) — accept with Enter to skip the remaining configuration questions - If not using defaults, choose the inference backend (Aitta or Anthropic API)
- Select the agent framework (Claude Code, OpenCode, or Generic)
- (LUMI only) Optionally enable GEAK kernel optimization (Claude Code or OpenCode; prompts for an Anthropic API key)
- Prompt for SIF paths when needed (
aitta-proxy.siffor Claude Code + Aitta;build-db/chroma-mcpfor local-ChromaDB) - (Local-ChromaDB only) Clone HPC documentation and build the knowledge base
- Start the relevant services as Singularity instances (skipped when not needed — e.g. OpenCode + Aitta uses no proxy)
- Install skills and configure MCP for the selected agent framework
After initial setup, use the launch script to start (or restart) the assistant:
bash setup/launch.shThe launch script checks that services are running (restarting them if needed — handles login node changes), then starts the agent.
If you prefer to configure the assistant without the wizard, follow these steps. The repository includes pre-filled templates for LUMI under setup/templates/.
OpenCode (recommended for LUMI + Aitta):
# Using Aitta inference (default for LUMI)
cp setup/templates/lumi-opencode-aitta.json opencode.json
# Or using Anthropic API
cp setup/templates/lumi-opencode-anthropic.json opencode.jsonThe lumi-opencode-aitta.json template is pre-configured with:
- Aitta provider pointing to
https://api-staging-aitta.2.rahtiapp.fi/openai/v1 - Model:
openai/gpt-oss-120b - LUMI AI Factory MCP (
lumi-aif) registered - HPC-safe permission rules
If you need a different Aitta URL or model, edit opencode.json directly after copying.
Claude Code:
cp setup/templates/lumi-system-prompt.md CLAUDE.mdThen register the LUMI AI Factory MCP:
claude mcp add --transport http lumi-aif https://lumi-aif-agents.2.rahtiapp.fi/mcpOpenCode — the system prompt path is declared inside opencode.json as "instructions": ["AGENTS.md"]:
cp setup/templates/lumi-system-prompt.md AGENTS.mdCopy the LUMI skills into the agent's skills directory:
# OpenCode
mkdir -p .opencode/skills
cp -r skills/profiling .opencode/skills/
cp -r skills/lumi-resource-management .opencode/skills/
cp -r skills/lumi-slurm-job-management .opencode/skills/
# Claude Code
mkdir -p .claude/skills
cp -r skills/profiling .claude/skills/
cp -r skills/lumi-resource-management .claude/skills/
cp -r skills/lumi-slurm-job-management .claude/skills/OpenCode + Aitta:
export AITTA_TOKEN=your-token-here
opencodeClaude Code + Aitta:
Start the Aitta proxy (the LiteLLM proxy; requires aitta-proxy.sif). LiteLLM
routes by provider prefix, so the model is passed with a hosted_vllm/ prefix
(LITELLM_MODEL=hosted_vllm/$TARGET_MODEL) — it strips hosted_vllm/ to select
the OpenAI-compatible provider and forwards openai/gpt-oss-120b to Aitta
verbatim. (hosted_vllm is used instead of openai because the openai
provider routes /v1/messages through the OpenAI Responses API — which Aitta
doesn't implement — and also strips a leading openai/ from the model name.)
export AITTA_API_BASE=https://api-staging-aitta.2.rahtiapp.fi/openai/v1
export AITTA_TOKEN=your-token-here
export LITELLM_MODEL=hosted_vllm/openai/gpt-oss-120b
# Use `singularity` or `apptainer` depending on what is on your PATH (same subcommands).
singularity instance start \
--env AITTA_API_BASE="$AITTA_API_BASE" \
--env AITTA_TOKEN="$AITTA_TOKEN" \
--env LITELLM_MODEL="$LITELLM_MODEL" \
aitta-proxy.sif aitta-proxy
singularity exec \
--env AITTA_API_BASE="$AITTA_API_BASE" \
--env AITTA_TOKEN="$AITTA_TOKEN" \
--env LITELLM_MODEL="$LITELLM_MODEL" \
instance://aitta-proxy \
litellm --config /app/config.yaml --host 0.0.0.0 --port 8001 &
# Point Claude Code at the proxy. The proxy exposes the Aitta backend as the
# model name `aitta` (see setup/aitta-proxy/config.yaml).
export ANTHROPIC_BASE_URL=http://localhost:8001
export ANTHROPIC_API_KEY="$AITTA_TOKEN"
export ANTHROPIC_AUTH_TOKEN="$AITTA_TOKEN"
export ANTHROPIC_MODEL=aitta
export ANTHROPIC_SMALL_FAST_MODEL=aitta
claudeAnthropic API:
export ANTHROPIC_API_KEY=your-key-here
opencode # or: claudeThe assistant queries a RAG knowledge base of HPC documentation through MCP. You
pick the backend during setup; the choice is persisted to ~/.config/hpc-assistant/config.env
as KB_BACKEND and changes which tools are exposed to the agent.
For sites that expose a managed MCP server, the assistant simply registers it
with your agent framework. No data is cloned to the login node, no index is
built locally, and no chroma-mcp instance is started.
LUMI uses the LUMI AI Factory MCP server, maintained by CSC and continuously updated from the LUMI Docs and the LUMI AI Guide:
- URL:
https://lumi-aif-agents.2.rahtiapp.fi/mcp - Tool:
retrieve_docs - Source: LUMI AIF — Infrastructure for AI agents
What setup wires up:
- Claude Code —
claude mcp add --transport http lumi-aif https://lumi-aif-agents.2.rahtiapp.fi/mcp - OpenCode — adds an
mcp.lumi-aifblock of{ "type": "remote", "url": "..." }toopencode.json - Generic — point your own client at the URL; e.g.
fastmcp call https://lumi-aif-agents.2.rahtiapp.fi/mcp retrieve_docs 'query=...' 'k=2'
The agent system prompt (CLAUDE.md / AGENTS.md) is generated to instruct
the model to call retrieve_docs from the lumi-aif MCP server before
falling back to general knowledge.
Clones the site's documentation repository, chunks and embeds it with the
build-db.sif container, and serves it through the chroma-mcp.sif
Singularity instance over MCP. Useful when:
- Working on a new site that does not yet have a managed MCP, or
- You want to extend or replace the knowledge base with private/internal docs.
This path requires the extra build-db and chroma-mcp SIF images from
Pull Container Images.
For Claude Code, the Aitta proxy translates Anthropic API requests to
OpenAI-compatible format. It is the official LiteLLM
proxy (the well-known gateway for exactly this translation) with our Aitta
config baked in, pinned to a vetted stable release — see
setup/dockerfiles/aitta-proxy.dockerfile. It runs as a Singularity instance on
the login node in detached mode — no separate terminal needed.
OpenCode speaks the OpenAI API natively and connects to Aitta directly, so it does not use the proxy.
Configuration: LiteLLM reads everything from environment variables at start
(AITTA_API_BASE, AITTA_TOKEN, and LITELLM_MODEL), so the published image
contains no secrets, URLs, or model names. It exposes the Aitta backend under
the model name aitta; Claude Code is pointed at it with ANTHROPIC_MODEL=aitta.
Choosing a model: Set TARGET_MODEL (via the setup wizard or your
credentials file) to pick which Aitta model the proxy serves — the launch
scripts inject it at runtime, so no rebuild is needed. The list of known
default models lives in setup/aitta-proxy/config.yaml;
edit that file (and rebuild the SIF) if you want to change the baked-in default.
Login node handling: The proxy binds to 0.0.0.0 so it's accessible from any login node. The launch script detects which node the proxy is on and reconnects or restarts as needed.
Direct connection to Anthropic's API. Requires a valid API key and network access from the login node.
Skills are stored in skills/ in a generic markdown format compatible with multiple agent frameworks. Each skill has a SKILL.md file with YAML frontmatter and instruction content, plus optional references/ for supporting documentation.
| Skill | Scope | Description |
|---|---|---|
profiling |
Generic | AMD GPU profiling with rocprofv3 (tracing, counters, Perfetto) |
lumi-resource-management |
LUMI | Project quotas, billing units, storage allocations |
lumi-slurm-job-management |
LUMI | Slurm job submission, partitions, containers, Python/PyTorch jobs |
optimize-kernel |
LUMI (GEAK, opt-in) | Submit a Triton/HIP kernel for GEAK optimization via SLURM |
check-geak-job |
LUMI (GEAK, opt-in) | Check the status / results of a GEAK optimization job |
cancel-geak-job |
LUMI (GEAK, opt-in) | Cancel a running GEAK job |
- Create a skill directory under
skills/(e.g.skills/mysite-slurm/) - Write
SKILL.mdwith frontmatter and instructions - Add the skill name to the site config in
setup/config.sh(theget_site_configfunction) - Re-run
setup.shto install the new skills
When Claude Code is selected during setup:
- Skills from
skills/are copied to.claude/skills/ - The knowledge-base MCP is registered: remote MCP (LUMI default) or chroma (local-ChromaDB path)
- A
CLAUDE.mdis created with assistant instructions - Launch script sets environment variables and starts
claude
Use from any project directory:
claude --add-dir /path/to/hpc-assistantWhen OpenCode is selected during setup:
- Skills from
skills/are copied to.opencode/skills/ - An
AGENTS.mdis created with assistant instructions - An
opencode.jsonis generated with:- Provider configuration (Aitta via
@ai-sdk/openai-compatibleor Anthropic native) - MCP registration (remote
lumi-aifon LUMI, or local chroma when using local-ChromaDB) - Model selection
- Provider configuration (Aitta via
- Launch script exports the API token and starts
opencode
Key advantage with Aitta: OpenCode speaks OpenAI API natively via the @ai-sdk/openai-compatible provider, so it connects directly to the Aitta endpoint — no proxy needed. This means one fewer container to manage.
Use from the project directory:
cd /path/to/hpc-assistant && opencodeSkills are available as markdown files in skills/. Services (chroma-mcp, aitta-proxy) run independently. Configure your agent framework to:
- Use the chroma MCP server for RAG queries
- Read skill files for instruction context
- Point API requests at the aitta-proxy if using Aitta inference. The proxy is a
LiteLLM gateway: send Anthropic-format requests to
/v1/messages(model nameaitta) or OpenAI-format requests to/v1/chat/completions.
GEAK is an automated GPU-kernel optimization workflow that runs the
GEAK CLI inside a Singularity container on a
LUMI GPU node. From the assistant, you describe the kernel you want optimized,
the agent submits the SLURM job, and a background geak-monitor subagent polls
for results.
GEAK is opt-in and supported on Claude Code and OpenCode (hooks on
Claude Code; geak-guards.ts plugin on OpenCode).
- The three GEAK skills (
optimize-kernel,check-geak-job,cancel-geak-job) are copied into.{claude,opencode}/skills/alongside the LUMI skills. - The
geak-monitorbackground subagent is copied to.{claude,opencode}/agents/. - Claude Code: four PreToolUse / SubagentStop hooks in
.claude/hooks/, with entries written into.claude/settings.json. - OpenCode:
geak-guards.tscopied to.opencode/plugins/and registered inopencode.json. - The runtime files (
run-geak.sbatch,setup-geak.sh,commandment-config.yaml,matmul_kernel.py) are copied fromgeak/to the project root, where the wrapper scripts expect them. - An Anthropic API key is collected and stored in
<project>/.anthropic_api_key(mode 600, gitignored), and mirrored into~/.config/hpc-assistant/credentials.envasGEAK_ANTHROPIC_API_KEYsolaunch.shcan restore it on a new login node. - SLURM jobs use the LUMI lumi-multitorch Singularity image and bootstrap
GEAK in-job via
setup-geak.sh(seegeak/GEAK.md).
GEAK calls Anthropic directly from inside the SLURM container (it does not go
through the aitta-proxy). An Anthropic API key is therefore required even if
the agent itself is configured to use Aitta or another backend.
Run setup on a LUMI login node and answer "yes" at the GEAK prompt:
bash setup/setup.sh
# ... select LUMI, then Claude Code or OpenCode, then answer "y" to "Install GEAK?"
# ... paste the Anthropic API key when prompted
bash setup/launch.shThen, inside Claude Code or OpenCode:
Optimize the kernel in
matmul_kernel.py
See geak/GEAK.md for the architecture diagram, full skills /
hooks table, configuration reference, and a list of files copied into place.
Claude Code — three PreToolUse hooks and one SubagentStop hook block raw
cluster commands, protect workflow files, and prevent duplicate geak-monitor
spawns. Denials are logged to .claude/logs/geak-hook-denials.log.
OpenCode — the geak-guards.ts plugin enforces the same policies (bash/edit
guards block; monitor spawn is observational only — see geak/GEAK.md).
Denials are logged to .opencode/logs/geak-hook-denials.log.
The assistant is designed to run on shared HPC login nodes where careless commands can affect other users, waste billing units, or corrupt data. Safety is enforced at two levels:
Both Claude Code and OpenCode receive explicit HPC safety instructions:
- Never run destructive commands (
rm -rf,module purge) without confirmation - Never submit or cancel Slurm jobs without showing the full command first
- Never modify shell config files (
.bashrc, etc.) without approval - Prefer read-only investigation before making changes
- Warn about quota impact before creating large files
OpenCode's opencode.json includes granular permissions:
| Category | Policy |
|---|---|
| Read-only commands | allow - ls, cat, grep, squeue, git, module |
| Job submission | ask - sbatch, srun, salloc, scancel |
| File modification | ask - all edits require approval |
| Deletion | ask for rm, deny for rm -rf |
| System administration | deny - chown, dd, mkfs, kill -9, reboot |
| External directories | ask - any access outside the project directory |
| Doom loop detection | deny - prevents infinite tool call loops |
To customize permissions, edit the permission block in opencode.json. See the
OpenCode permissions docs for full syntax.
All configuration is stored in ~/.config/hpc-assistant/:
| File | Contents |
|---|---|
config.env |
Site, framework, KB backend, paths, proxy host/port |
credentials.env |
API keys and tokens (mode 600) |
logs/ |
Service logs (aitta-proxy, etc.) |
# Source the config helpers
source setup/config.sh
detect_container_runtime
stop_servicesOr manually (only the services that are actually running for your setup):
singularity instance stop aitta-proxy # Aitta + Claude Code only
singularity instance stop chroma-mcp # local-chroma KB onlyTo remove everything — services, knowledge base, documentation, config, credentials, and agent framework integration:
bash setup/teardown.shThis will prompt for confirmation before proceeding. SIF container images and agent binaries (Claude Code, OpenCode) are not removed.
Questions and bug reports are welcome — please open a GitHub Issue.
Report security vulnerabilities privately — see SECURITY.md.
For questions, issues, or contributions, please reach out to the maintainers:
- Sopiko Kurdadze — @sopiko99
- Albin Toft — @albitoft-amd
For questions or bug reports, please open a GitHub Issue.
Team ownership: @silogen/hpc-assistant.
See CODEOWNERS for the full ownership list.
| File | Purpose |
|---|---|
| SECURITY.md | How to report security vulnerabilities |
| .github/CODEOWNERS | Code ownership and PR review routing |
This repository follows AMD's open-source software release requirements. Compliance artifacts:
| File | Purpose |
|---|---|
| LICENSE.TXT | License summary for AMD-authored and third-party components |
| LICENSE | MIT license text for AMD-authored software |
| THIRD-PARTY-NOTICES.txt | Third-party copyright and license notices |
AMD-authored source files include SPDX license identifiers in their headers.
Container images distributed via Docker Hub include third-party components documented
in THIRD-PARTY-NOTICES.txt.
AMD-authored software in this project is licensed under the MIT License. See LICENSE.TXT for the full license summary including third-party components.