LLM chat agent embedded as a Moqui Framework component. Talks to Google Gemini via direct REST API calls — no Spring Boot, no extra ports, no external SDK JARs.
A native Moqui component that adds an AI chat interface to any Moqui application:
- Chat UI is a native Moqui screen (Bootstrap 3, chat bubbles, multi-turn conversation history)
- Calls the Gemini REST API directly using
java.net.http.HttpClient(JDK 11+) - Session history lives in JVM memory (
AdkSessionHolder) — resets on restart - Agent config (model, API key, system prompt) managed from the Moqui dashboard
- Moqui authentication guards all transitions
| Requirement | Version |
|---|---|
| Java | 21+ |
| Moqui Framework | 3.x |
| Gemini API key | Get one free |
No external dependencies beyond Moqui itself. The built component is a single JAR (moqui-adk-1.0.0.jar).
cd moqui/runtime
git submodule add -b growerp https://github.com/growerp/moqui-adk.git component/moqui-adk
git commit -m "add moqui-adk submodule"From the Moqui root:
./gradlew :runtime:component:moqui-adk:jarOutput: component/moqui-adk/lib/moqui-adk-1.0.0.jar (one file, ~10 KB).
java -jar moqui.war no-run-esMoqui creates the ADK_AGENT_CONFIG table automatically on first start. No separate data-load step required.
- Log in at
http://localhost:8080/vapps(admin:SystemSupport/moqui) - Click ADK in the top navigation bar → Configuration
- Fill in:
| Field | Description | Example |
|---|---|---|
| Agent Name | Identifier for this agent | MoquiAgent |
| Model | Gemini model ID | gemini-2.0-flash |
| API Key | Your Gemini API key | AIza… |
| System Instruction | Agent persona / constraints | See below |
- Click Save Configuration
Set any of the following before starting Moqui — no UI config needed:
export GOOGLE_API_KEY=AIza...
# or GOOGLE_GENAI_API_KEY / GEMINI_API_KEY
java -jar moqui.war no-run-esThe component checks the DB first; falls back to env vars if no enabled=Y record with an API key exists.
You are a helpful assistant for the GrowERP system.
Help users understand their data, answer questions about orders,
inventory, and customers, and guide them through business processes.
Be concise and precise. When unsure, say so.
ADK → Dashboard — shows agent name, model, and configuration status.
ADK → Chat UI — chat window. Type a message and press Enter (or click Send). The agent maintains full conversation history within the session.
ADK → Configuration — update agent settings. Saving takes effect on the next chat request (no restart needed).
Browser
│
▼
Moqui screen /vapps/adk/ChatUI
│
│ POST /adk/ChatUI/createSession → returns { sessionId }
│ POST /adk/ChatUI/runAgent → returns { response }
│
▼
ChatUI.xml transitions (Groovy inline)
│
├── AdkSessionHolder (ConcurrentHashMap, JVM memory)
│ └── sessionId → [ {role,parts}, … ] ← full conversation history
│
└── java.net.http.HttpClient
└── POST https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent
body: { system_instruction, contents: [history] }
| File | Role |
|---|---|
screen/Adk/ChatUI.xml |
Chat UI + createSession / runAgent transitions |
screen/Adk/dashboard.xml |
Status overview (agent name, model, key configured) |
screen/Adk/Configuration.xml |
Form to save agent config |
service/AdkServices.xml |
update#AgentConfig, create#Session, run#Agent services |
entity/AdkEntities.xml |
AdkAgentConfig entity |
src/.../AdkSessionHolder.groovy |
Static ConcurrentHashMap for in-memory session history |
data/AdkSecuritySeedData.xml |
AdkUsers group + artifact auth rules |
| Entity | Purpose |
|---|---|
moqui.adk.AdkAgentConfig |
Agent config: name, model, API key, instruction, enabled flag |
Session history is in-memory only (AdkSessionHolder) — not persisted to the database.
ls moqui/runtime/component/moqui-adk/lib/
# Expected: moqui-adk-1.0.0.jar (one file)After starting Moqui, search startup log for:
Component moqui-adk loaded
Navigate to http://localhost:8080/vapps → ADK → Chat UI → type a message → agent replies.
# Authenticate and grab CSRF token
SESSION=$(curl -s -c /tmp/moqui-cookies.txt -b /tmp/moqui-cookies.txt \
-X POST http://localhost:8080/apps/Login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=SystemSupport&password=moqui" -D - | grep -i location)
CSRF=$(curl -s -c /tmp/moqui-cookies.txt -b /tmp/moqui-cookies.txt \
http://localhost:8080/apps/adk/ChatUI \
| grep confMoquiSessionToken | sed 's/.*value="\([^"]*\)".*/\1/')
# Create session
curl -s -X POST http://localhost:8080/apps/adk/ChatUI/createSession \
-b /tmp/moqui-cookies.txt \
-H "Content-Type: application/json" -H "Accept: application/json" \
-H "X-CSRF-Token: $CSRF" -d '{}'
# → {"sessionId":"<uuid>"}
# Send message (replace <uuid>)
curl -s -X POST http://localhost:8080/apps/adk/ChatUI/runAgent \
-b /tmp/moqui-cookies.txt \
-H "Content-Type: application/json" -H "Accept: application/json" \
-H "X-CSRF-Token: $CSRF" \
-d '{"sessionId":"<uuid>","message":"Hello, who are you?"}'
# → {"response":"..."}No API key found in DB or environment. Either:
- Go to ADK → Configuration and save an API key, or
- Set
GOOGLE_API_KEYenv var before starting Moqui
Moqui is serving a cached older version. Restart Moqui.
Gemini returned no candidate text. Check:
- API key is valid (test at aistudio.google.com)
- Model name is correct (
gemini-2.0-flashis the default) - Prompt is not blocked by Gemini safety filters
Component not built. Run:
cd moqui && ./gradlew :runtime:component:moqui-adk:jarThen restart Moqui.
cd moqui/runtime/component/moqui-adk
../../../gradlew jar
# restart MoquiScreen XML and service XML changes take effect without rebuild (Moqui hot-reloads them).
cd moqui/runtime/component/moqui-adk
git add -A && git commit -m "your change"
git push origin growerp
# Update submodule pointer in moqui-runtime
cd ../..
git add component/moqui-adk
git commit -m "bump moqui-adk submodule"
git push origin growerpPublic domain under CC0 1.0 Universal plus Grant of Patent License, consistent with Moqui Framework.