Files
loyalty-agent-service/context.md

154 lines
6.4 KiB
Markdown

---
trigger: always_on
---
# Project Context: Loyalty Agent Service
This repository is a local development workspace for a loyalty AI assistant. It has a React chat UI at the repository root and two Spring Boot backend modules managed by a parent Maven project.
## Architecture
### Frontend: root directory
- Stack: React 19, Vite, TypeScript, Tailwind CSS, shadcn-style UI primitives, Zustand, `@stomp/stompjs`, `react-markdown`, `remark-gfm`.
- Entry point: `src/main.tsx` renders `src/App.tsx`, which renders `ChatLayout`.
- Dev port: `9333` from `vite.config.ts`.
- Main UI flow:
- `src/store/useChatStore.ts` owns chat state and STOMP connection.
- WebSocket URL is currently hard-coded as `ws://localhost:9332/ws`.
- User messages are published to `/app/chat`.
- Agent events are received from `/user/queue/chat-events`.
- `CampaignList` fetches campaign data from `http://localhost:9332/api/v1/agent/campaigns` and campaign rules from `http://localhost:9332/api/v1/agent/campaigns/{campaignId}/rules`.
### `loyalty-agent`
- Spring Boot application: `dev.sonpx.loyalty.agent.LoyaltyAgentApplication`.
- Runtime port: `9332`.
- Role: BFF and AI orchestration layer for the frontend.
- Key files:
- `controller/AgentController.java`: STOMP chat endpoint and REST campaign proxy endpoints.
- `service/LoyaltyAgentService.java`: builds a Spring AI `ChatClient`, streams LLM output, wraps tool calls, and emits tool status events.
- `config/WebSocketConfig.java`: STOMP endpoint `/ws`, application prefix `/app`, user destination prefix `/user`, broker prefixes `/topic` and `/queue`.
- `config/AgentConfig.java`: OAuth2-enabled `RestClient` for the loyalty core API.
- `controller/ConversationController.java`: simple in-memory conversation/message REST endpoints.
- AI configuration:
- Uses Ollama at `http://localhost:11434`.
- Default model in `application.yml`: `qwen3.5:2b`.
- MCP client connects to `${MCP_SERVER_URL:http://localhost:9331}`.
- Agent event types sent to the UI: `TOKEN`, `TOOL_STATUS`, `DONE`, `ERROR`.
### `loyalty-mcp-server`
- Spring Boot application: `dev.sonpx.loyalty.mcp.McpServerApplication`.
- Runtime port: `9331`.
- Role: MCP tool server for loyalty operations. The agent calls this service through Spring AI MCP SSE.
- MCP endpoints from `application.yml`:
- SSE endpoint: `/sse`
- Message endpoint: `/message`
- Key files:
- `config/McpServerConfig.java`: registers MCP tool objects.
- `tool/LoyaltyAgentTools.java`: member tiers, point pools, campaign creation, static attributes.
- `tool/reward/CampaignTools.java`: campaign listing and campaign draft lifecycle.
- `tool/reward/CampaignRuleTools.java`: campaign rule listing, creation, editing, and draft rule attachment.
- `tool/pool/PointPoolTools.java`: pool and SOP draft tools.
- `draft/DraftSessionManager.java`: in-memory draft sessions keyed by `conversationId`, with a 1-hour TTL.
- `draft/DependencyValidator.java`: validates draft dependencies before tools mutate or submit drafts.
- API clients call the loyalty core service with OAuth2 client credentials.
## External Services And Environment
Create `.env` from `.env.example` for local development.
Important variables:
- `KEYCLOAK_CLIENT_SECRET`
- `KEYCLOAK_TOKEN_URI`
- `LOYALTY_CORE_BASE_URL`
- `MCP_SERVER_URL`
- `VITE_AGENT_HTTP_URL`
- `VITE_AGENT_WS_URL`
Current defaults:
- Keycloak token URI: `http://192.168.99.235/auth/realms/ols-cn-sit/protocol/openid-connect/token`
- Loyalty core base URL: `http://192.168.99.242:8081`
- MCP server URL: `http://localhost:9331`
- Agent HTTP URL: `http://localhost:9332`
- Agent WS URL: `ws://localhost:9332`
Note: some frontend code still uses hard-coded localhost URLs instead of the `VITE_*` variables.
## Local Startup
Use the root script for the full local stack:
```bash
./start-all.sh
```
What the script does:
1. Loads `.env` if present.
2. Kills processes on ports `9331`, `9332`, and `9333`.
3. Starts `loyalty-mcp-server` and waits for port `9331`.
4. Starts a `nodemon` compile watcher for the MCP server.
5. Starts `loyalty-agent` and waits for port `9332`.
6. Starts a `nodemon` compile watcher for the agent.
7. Starts the frontend with `pnpm dev` on port `9333`.
8. Writes logs to `log/`.
Manual startup:
```bash
./mvnw spring-boot:run -pl loyalty-mcp-server
./mvnw spring-boot:run -pl loyalty-agent
pnpm dev
```
The script currently invokes `-Pgen`, but the checked-in POM files do not define a `gen` Maven profile. Do not assume OpenAPI generation is wired unless you verify or add that profile.
## Build, Lint, And Tests
Frontend:
```bash
pnpm install
pnpm lint
pnpm build
```
Backend:
```bash
./mvnw test
./mvnw test -pl loyalty-agent
./mvnw test -pl loyalty-mcp-server
```
Relevant tests:
- `loyalty-agent/src/test/java/dev/sonpx/loyalty/agent/controller/AgentControllerTest.java`
- `loyalty-agent/src/test/java/dev/sonpx/loyalty/agent/domain/AgentEventTest.java`
- `loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/McpOrchestratorTest.java`
- `loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapperTest.java`
- `loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentToolsRealApiTest.java`
`LoyaltyAgentToolsRealApiTest` may depend on real external API availability and credentials.
## Development Guidelines For Agents
- Keep the boundary clear:
- `loyalty-agent` owns frontend-facing chat orchestration and REST proxy endpoints.
- `loyalty-mcp-server` owns tool implementations and loyalty core API access.
- The frontend owns STOMP state, message rendering, chat layout, and campaign context panels.
- Preserve the STOMP contract unless intentionally changing both sides:
- Client publishes to `/app/chat`.
- Server handles `@MessageMapping("/chat")`.
- Server sends events to `/user/queue/chat-events`.
- Preserve `conversationId` propagation. MCP draft tools depend on it to keep draft campaign/pool/rule state in `DraftSessionManager`.
- Tool responses may contain `presentation.content`; the agent system prompt requires displaying that content exactly.
- Reuse existing UI primitives in `src/components/ui` and existing Tailwind conventions.
- Do not introduce persistent storage assumptions. Conversation repositories and draft sessions are currently in memory.
- Do not hard-code new service URLs when a matching environment variable already exists.
- Be careful with ports: MCP is `9331`, agent is `9332`, frontend is `9333`.