A Spring Boot backend (with an Angular client in frontend/) that shows how to integrate LLMs with Spring AI: chat, prompt templates, structured output, retrieval augmented generation over a Chroma vector store, function calling, image generation and speech-to-text.
- Chat: plain prompts, prompt templates, structured output parsed into Java records, and a multi-turn conversation kept in memory (
/chats). Uses OpenAI (gpt-3.5-turbo) through Spring AI'sChatClient. - RAG (Retrieval Augmented Generation): documents are embedded and stored in Chroma;
/rag/askretrieves the top-K most similar documents and injects them as context in the prompt sent to the model. If nothing relevant is retrieved, the model is not called. - Function Calling: the model can invoke Java functions registered as Spring AI callbacks (
WeatherInfo, a mock;BookInfo, which looks up a book review in PostgreSQL and reports "not found" when the book does not exist). - Book recommendations: recommends books from the stored catalog (with their author and a reason) based on the user messages of a conversation kept in memory. Titles the model returns that are not in the catalog are discarded.
- Embeddings: generate embeddings for a text and run a similarity search against the vector store.
- Image Generation: DALLΒ·E 2 / DALLΒ·E 3 images from a text prompt (URL or base64).
- Speech-to-Text: Whisper transcription of bundled sample audios and of uploaded audio files.
- Authors and books CRUD: persisted in PostgreSQL with Spring Data JPA, seeded from
src/main/resources/json/on startup.
- Java 21, Spring Boot 3.2, Maven wrapper
- Spring AI 0.8.1: OpenAI chat, embeddings, image (DALLΒ·E) and audio transcription (Whisper) clients; Chroma vector store
- Vector database: Chroma (via Docker Compose)
- Database: PostgreSQL (authors and books), started by Spring Boot's Docker Compose support
- Frontend: Angular 17 (
frontend/) - Tests: JUnit 5, Mockito,
@WebMvcTestslices. Models and the vector store are always mocked, so the build needs no API keys and no running infrastructure. - Ollama (optional, not wired by default): its starter is commented out in
pom.xml.
- Java 21
- Docker & Docker Compose (PostgreSQL and Chroma)
- An OpenAI API key to call the real models (not needed to build or run the tests)
-
Clone the repository
git clone https://github.com/AZapata27/ai-integration-springai.git cd ai-integration-springai -
Configure environment variables Copy the template used by Docker Compose and adjust the values:
cp .env.example .env export OPENAI_API_KEY=sk-...OPENAI_API_KEYis read byspring.ai.openai.api-keyinapplication.yml. Never commit.env. -
Build and test (no keys or containers needed)
./mvnw verify
-
Run the application
./mvnw spring-boot:run
Spring Boot's Docker Compose support starts PostgreSQL and Chroma from
docker-compose.yaml(or start them yourself withdocker compose up -d). The API listens onhttp://localhost:8080. -
Run the frontend (optional)
cd frontend && npm ci && npm start
GET /chats/generate?message=β plain promptGET /chats/generate/prompt?param=&topic=β prompt templateGET /chats/generate/output?author=β structured output (AuthorBook)GET /chats/generate/book/output?name=β structured output (BookInfoDTO)GET /chats/generateConversation?message=β multi-turn conversation (chat id1, in memory)
POST /rag/documentsβ embed and store documents:{"documents": ["text 1", "text 2"]}GET /rag/ask?question=&topK=4β answer using the retrieved documents as context; returns the answer and its sources
curl -X POST http://localhost:8080/rag/documents \
-H "Content-Type: application/json" \
-d '{"documents":["Spring AI integra modelos de lenguaje en aplicaciones Spring Boot.","Chroma es una base de datos vectorial."]}'
curl "http://localhost:8080/rag/ask?question=Que%20es%20Spring%20AI%3F&topK=2"GET /recommendations/books?chatId=1&limit=3β catalog books recommended from the conversation history (talk first through/chats/generateConversation)
GET /functionsβ weather question answered throughWeatherInfoGET /functions/book?bookName=β book question answered throughBookInfo
GET /embeddings/generate?message=β embedding of a textGET /embeddings/vectorstore?message=β stores three demo documents and returns the most similar one
GET /images/generate?param=β DALLΒ·E 3 HD, URLGET /images/generateBytes?param=β DALLΒ·E 3, base64GET /images/generate/dalle2?param=β DALLΒ·E 2, URL
GET /transcripts/es,GET /transcripts/enβ transcribe bundled sample audiosPOST /transcripts/uploadβ transcribe an uploaded audio (multipart/form-data, fieldaudio)
GET|POST /authors,GET|PUT|DELETE /authors/{id}GET|POST /books,GET|PUT|DELETE /books/{id}
flowchart LR
FE["Angular frontend<br/>(frontend/)"] --> API
subgraph API["Spring Boot REST controllers"]
CHAT["/chats<br/>ChatController"]
RAG["/rag<br/>RagController"]
REC["/recommendations<br/>RecommendationController"]
FN["/functions<br/>FunctionCallingController"]
EMB["/embeddings<br/>EmbeddingController"]
IMG["/images<br/>ImageController"]
TR["/transcripts<br/>TranscriptController"]
CRUD["/authors, /books<br/>Author/BookController"]
end
subgraph AI["Spring AI"]
CC["ChatClient<br/>(OpenAiChatClient)"]
EC["EmbeddingClient"]
IC["ImageClient"]
TC["AudioTranscriptionClient"]
FCB["Function callbacks<br/>WeatherInfo, BookInfo"]
end
CHAT --> CC
CHAT -.-> HIST["ChatHistory<br/>(in-memory)"]
FN --> CC
CC --> FCB
CC --> OPENAI["OpenAI<br/>GPT / DALL-E / Whisper"]
IMG --> IC --> OPENAI
TR --> TC --> OPENAI
RAG --> RS["RagService"]
RS -- "1. similarity search (top-K)" --> VS["Chroma vector store"]
VS -- "2. retrieved documents" --> RS
RS -- "3. prompt = system context<br/>+ user question" --> CC
RAG -- "POST /rag/documents" --> VS
EMB --> EC --> OPENAI
EC -. "embeddings" .-> VS
EMB -- "demo documents / search" --> VS
REC --> RECS["RecommendationService"]
RECS -- "user messages" --> HIST
RECS -- "catalog" --> SVC
RECS -- "history + catalog prompt" --> CC
CRUD --> SVC["Author/Book services"] --> DB[("PostgreSQL<br/>(JPA)")]
FCB -- "BookInfo lookup" --> DB
Requests from the Angular client hit the REST controllers, which delegate to Spring AI clients. Chat and function calling go through ChatClient (OpenAI), and the model may call the registered Java functions (WeatherInfo, BookInfo).
For RAG, RagService runs a similarity search for the question in Chroma (top-K, default 4, max 10), joins the retrieved documents into the system message of the prompt and sends it with the user question to the model, which is told to answer only from that context. The response includes the sources used. Spring AI 0.8.1 has no QuestionAnswerAdvisor, so the prompt is built explicitly with a SystemPromptTemplate.
Recommendations combine the user messages stored in ChatHistory with the enabled books of the catalog; the model picks among those titles and the service keeps only titles that exist. Authors and books are persisted in PostgreSQL through Spring Data JPA; conversation history is kept in memory and is lost on restart.
ai-integration-springai/
βββ src/main/java/com/example/openiaintegration/
β βββ config/ # OpenAI chat client, Chroma vector store, function callbacks
β βββ controller/ # REST controllers
β βββ dto/ # Request/response records
β βββ model/ # JPA entities (Author, Book)
β βββ repo/ # Spring Data repositories
β βββ service/ # RAG, recommendations, CRUD services and implementations
β βββ util/ # In-memory ChatHistory
βββ src/main/resources/
β βββ application.yml # Application config
β βββ json/ # Seed data (authors, books)
β βββ audios/ # Sample audios for transcription
β βββ prompts/ # Evaluation prompt templates
βββ src/test/ # Unit and @WebMvcTest tests (all AI calls mocked)
βββ frontend/ # Angular client
βββ .github/workflows/ # CI: Maven verify + frontend build
βββ docker-compose.yaml # PostgreSQL and Chroma
βββ .env.example # Variables for docker-compose
βββ pom.xml
- OpenAI:
spring.ai.openai.api-keyreadsOPENAI_API_KEY. The chat client is defined inOpenAIConfig(gpt-3.5-turbo, max 200 tokens). - Chroma:
ChromaConfigconnects tohttp://localhost:8000and uses the collectionTestCollection; the port must matchCHROMA_LOCAL_PORTin.env. - PostgreSQL: connection details come from
docker-compose.yamland.envthrough Spring Boot's Docker Compose support; the schema is created on startup (ddl-auto: create).
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.