An AI-powered chatbot platform built with a Spring Boot backend (Spring AI + Ollama) and a bundled React frontend. The backend does the heavy lifting — session-aware chat, GitHub OAuth2 login, rate limiting, and a scaffold for learning from good conversations — while the React UI provides a simple chat widget on top of it.
- Chat with a local LLM via Spring AI talking to an Ollama model (
smallthinker:latestby default), with both a plain JSON endpoint and a streaming (SSE) endpoint. - Session-aware conversations — each chat call carries a session ID; requests for the same session are serialized (per-session lock) and given short-term memory via Spring AI's
ChatMemory. - GitHub OAuth2 login (Spring Security) that creates/updates a
Userrecord in Postgres with GitHub profile data (avatar, followers, repos, etc.); can be toggled off for local dev viaapp.auth.enabled=false. - Per-tenant rate limiting using a Bucket4j token bucket (100 requests/minute by default).
- Ollama warm-up on startup — a background ping so the model is loaded before the first real user request.
- Structured error handling with a dedicated exception hierarchy (
ChatServiceException,ModelUnavailableException,ContextWindowExceededException,RateLimitExceededException,SessionNotFoundException, etc.) mapped to proper HTTP status codes. - Conversation quality scoring + RAG training scaffold —
ConversationQualityScorerscores exchanges (response length, user rating, whether the user immediately rephrased, error responses), and a scheduled job is wired up to ingest high-quality conversations into a PGVector store. Note: the conversation-persistence step is stubbed (TrainingDataCollector.recordcomputes a score but the DB save is still aTODO), so this pipeline doesn't yet have real data flowing through it end-to-end. - Java 21 virtual threads for Tomcat and dedicated async executors for chat vs. training work, so concurrent chat sessions don't block each other.
- React chat widget — send a message, see a typing indicator, clear the chat, basic client-side validation and error display.
- The user opens the React UI and types a message.
- The frontend sends
POST /api/chat(or hits the streaming endpoint) to the Spring Boot backend. - The backend validates the message, checks the per-tenant rate limit, acquires a per-session lock, and builds a prompt with a system message plus session-scoped chat history.
- Spring AI's
ChatClientsends the prompt to the Ollama model and gets a response. - The exchange is (asynchronously) scored for quality; the response is returned to the UI and rendered in the chat.
AIChatBot/
└── SpringAISample/ # Spring Boot backend (also serves the React UI)
├── src/main/java/com/springaisample/
│ ├── AIController.java # REST endpoints (chat, stream, feedback, auth, admin, health)
│ ├── config/ # Security/OAuth2, async executors, virtual threads, Ollama warm-up
│ ├── entity/ # JPA entities: User, Conversation
│ ├── exception/ # Custom exception hierarchy
│ ├── repository/ # Spring Data JPA repositories
│ └── service/ # ChatService, RateLimiter, RagTrainingService,
│ # TrainingDataCollector, ConversationQualityScorer, UserService
├── src/main/resources/
│ ├── application.properties / application.yml
│ └── static/ # React chat UI (Create React App)
│ └── src/{App.jsx, Chatbot.jsx, ...}
└── src/test/java/... # Spring Boot context-load test
Backend: Java 21, Spring Boot 3.3, Spring AI 1.0 (Ollama chat model), Spring Security + OAuth2 Client (GitHub login), Spring Data JPA, PostgreSQL, Spring AI PGVector vector store, Bucket4j (rate limiting), Micrometer/Actuator, Java 21 virtual threads.
Frontend: React 18, axios, plain CSS (served from src/main/resources/static, built with Create React App / react-scripts).
The POM also declares Kafka, Resilience4j, HashiCorp Vault config, JWT (
jjwt), ModelMapper, and Redis, but none of these are currently wired into the application code — they're present as dependencies for future work, not active features.
Prerequisites: Java 21, Node.js/npm, PostgreSQL (a chatbot_db database), and Ollama running locally with the configured model pulled (ollama pull smallthinker:latest, or point config at another local model).
Backend (from SpringAISample, port 8080):
./mvnw spring-boot:run # or mvnw.cmd on WindowsFrontend (from SpringAISample/src/main/resources/static, port 3000):
npm install
npm startOpen http://localhost:3000 to chat. If GitHub OAuth2 is enabled (default), you'll be redirected to log in with GitHub before hitting authenticated endpoints; /api/health and /api/stream/** stay open.
Key settings live in application.properties / application.yml:
| Setting | Purpose |
|---|---|
spring.ai.ollama.base-url, spring.ai.ollama.chat.model |
Ollama endpoint and model name |
spring.datasource.url / username / password |
PostgreSQL connection |
spring.security.oauth2.client.registration.github.* |
GitHub OAuth2 app credentials |
app.auth.enabled |
Set to false to disable OAuth2 login for local development |
app.rate-limit.requests-per-minute |
Per-tenant rate limit |
app.training.interval-minutes, app.training.quality-threshold |
RAG training job schedule/threshold |
ALLOWED_ORIGINS (env var) |
Overrides app.security.cors.allowed-origins |
Note: the checked-in application.properties/application.yml currently contain local development credentials (DB password, OAuth2 client secret) for convenience. Treat these as placeholders to replace with environment variables or a secrets manager before any real deployment — don't reuse them as-is.
The project currently has a single Spring Boot context-load test (SpringAiSampleApplicationTests); there are no dedicated unit/integration tests for the services or controllers yet, and no CI pipeline is configured.