🔧 Foundation
Purpose: This guide provides step-by-step instructions for porting the minimal AI foundation (ChatModel infrastructure) from the Constellation POC.
Dependencies: This is infrastructure, not a user-facing feature. It serves as the foundation for all AI capabilities.
Related Documents:
- Text Operations Solution Architecture - System design overview
- Text Operations Epic - Capability definition
Note: This guide is specific to Text Operations (TextOps) implementation. Once Text Operations is fully implemented and validated, this entire text-intelligence/ folder will be archived.
Overview
What This Guide Covers
Phase 1 ports minimal infrastructure from the CBT Buddy POC:
- ChatModel bean + Ollama configuration
- LangChain4j integration (basic wiring)
- Constants class (AiConstants)
- Application.yml AI configuration
- Docker Compose (Ollama service)
- Text generation infrastructure (ChatModel wired, works internally)
What This Guide Does NOT Cover:
- ❌ RAG with document ingestion (Phase 2)
- ❌ Embeddings and pgvector (Phase 2)
- ❌ Document/Chunk entities (Phase 2)
- ❌ Vector similarity search (Phase 2)
- ❌ Productized Text Operations Service (Phase 3)
Prerequisites
- Springular boilerplate setup
- Docker installed and running
- Access to Constellation POC codebase for reference
- Basic understanding of Spring Boot configuration
Package Structure Setup
Before implementing, create the package structure:
server/src/main/java/com/saas/springular/common/ai/
├── config/
├── constants/
├── model/
├── service/
│ └── impl/
└── controller/
Create these directories (empty for now, files will be added in subsequent steps).
Layer Responsibilities:
- config/: Spring configuration beans. Creates
ChatModelbeans, configures timeouts, temperatures. No business logic. - constants/: Single source of truth for default values (timeouts, temperatures, limits). Used in
@Valuedefaults. - model/: Request/Response DTOs (will be added in Phase 3). Stateless records.
- service/: Business logic interfaces (will be added in Phase 3).
- service/impl/: Service implementations (will be added in Phase 3). Stateless.
- controller/: REST endpoints (will be added in Phase 3). Delegates to service layer.
POC Components to Port
1. AIConfiguration.java (ChatModel Bean)
POC Location: constellation/backend/server/src/main/java/com/saas/springular/common/ai/config/AIConfiguration.java
What to Port:
- Single
ChatModelbean (OllamaChatModel) @Valueconfiguration injection- Base URL, model name, timeout configuration
What NOT to Port Yet:
- EmbeddingModel bean (Phase 2)
- Multiple ChatModel beans with different temperatures (Phase 3)
2. AiConstants.java
POC Location: constellation/backend/server/src/main/java/com/saas/springular/common/ai/constants/AiConstants.java
What to Port:
- DEFAULT_CHAT_MODEL constant
- DEFAULT_TIMEOUT_MS constant
- DEFAULT_TEMPERATURE constant
- EMBEDDING_DIMENSIONS constant (for future use)
3. Application.yml (Ollama Config Section)
POC Location: constellation/backend/server/src/main/resources/application.yml
What to Port:
ollama.base-urlpropertyollama.chat-modelpropertyollama.timeoutproperty
4. Docker Compose (Ollama Service)
POC Location: constellation/backend/docker-compose.yml
What to Port:
- Ollama service definition
- Port mapping (11434:11434)
- Image:
ollama/ollama:latest
5. LangChain4j Dependencies (build.gradle)
POC Location: constellation/backend/server/build.gradle
What to Port:
langchain4j-ollamadependencylangchain4j-coredependency
6. JacksonConfiguration Enhancement
POC Location: Check for JacksonConfiguration in POC
What to Port:
- JavaTimeModule registration (if not already present)
- Required for JSON serialization/deserialization
Step-by-Step Porting Instructions
Step 1: Add LangChain4j Dependencies
File: server/build.gradle
Add dependencies:
dependencies {
// ... existing dependencies ...
// LangChain4j
implementation 'dev.langchain4j:langchain4j-ollama:0.29.1'
implementation 'dev.langchain4j:langchain4j-core:0.29.1'
}
Step 2: Create AiConstants Class
File: server/src/main/java/com/saas/springular/common/ai/constants/AiConstants.java
Key Points:
- ✅ Single source of truth for all default values
- ✅ Used in
@Valuedefaults - ✅ Prevents configuration drift
Create file:
package com.saas.springular.common.ai.constants;
/**
* Constants for AI configuration.
* Single source of truth for default values used throughout the AI module.
*/
public final class AiConstants {
private AiConstants() {}
// Ollama connection defaults
public static final String DEFAULT_OLLAMA_BASE_URL = "http://localhost:11434";
// Chat model defaults
public static final String DEFAULT_CHAT_MODEL = "llama2:7b";
public static final int DEFAULT_TIMEOUT_MS = 300000; // 5 minutes (appropriate for local Ollama)
public static final double DEFAULT_TEMPERATURE = 0.7; // Balanced creativity
// Future use (Phase 2)
public static final int EMBEDDING_DIMENSIONS = 768; // For future embeddings
}
Constants Pattern:
- All defaults centralized in one class
- Used in
@Valueannotations:@Value("${ollama.timeout:" + AiConstants.DEFAULT_TIMEOUT_MS + "}") - Prevents magic numbers scattered throughout code
Step 3: Add Ollama Configuration to application.yml
File: server/src/main/resources/application.yml
Add section:
ollama:
base-url: ${OLLAMA_BASE_URL:http://localhost:11434}
chat-model: ${OLLAMA_MODEL:llama2:7b}
timeout: ${OLLAMA_TIMEOUT:300000} # 5 minutes (default for local Ollama)
Configuration Notes:
- Base URL defaults to
http://localhost:11434(local Ollama) - Timeout defaults to 300000ms (5 minutes) - appropriate for local Ollama
- Environment variables can override defaults for different environments
Step 4: Create AIConfiguration Class
File: server/src/main/java/com/saas/springular/common/ai/config/AIConfiguration.java
Key Points:
- ✅ Configure timeout explicitly (required for reliability)
- ✅ Use constants from
AiConstantsfor defaults - ✅ Add logging to verify bean creation
Create file:
package com.saas.springular.common.ai.config;
import com.saas.springular.common.ai.constants.AiConstants;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.ollama.OllamaChatModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
@Slf4j
@Configuration
public class AIConfiguration {
@Value("${ollama.base-url:" + AiConstants.DEFAULT_OLLAMA_BASE_URL + "}")
private String baseUrl;
@Value("${ollama.chat-model:" + AiConstants.DEFAULT_CHAT_MODEL + "}")
private String chatModelName;
@Value("${ollama.timeout:" + AiConstants.DEFAULT_TIMEOUT_MS + "}")
private long timeoutMs;
@Bean
public ChatLanguageModel chatModel() {
log.info("Configuring OllamaChatModel: {} with base URL: {}, timeout: {}ms",
chatModelName, baseUrl, timeoutMs);
return OllamaChatModel.builder()
.baseUrl(baseUrl)
.modelName(chatModelName)
.timeout(Duration.ofMillis(timeoutMs)) // ✅ Timeout explicitly configured
.temperature(AiConstants.DEFAULT_TEMPERATURE)
.build();
}
}
Configuration Notes:
- Timeout is configured explicitly (5 minutes default for local Ollama)
- Logging added to verify bean creation and configuration values
- Constants used for defaults (single source of truth)
- Base URL includes default value for easier local development
Step 5: Add Ollama Service to Docker Compose
File: docker-compose.yml
Add service:
services:
# ... existing services ...
ollama:
image: ollama/ollama:latest
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
# Optional: set environment variables for model download
environment:
- OLLAMA_HOST=0.0.0.0
volumes:
ollama_data:
Step 6: Verify JacksonConfiguration (If Needed)
Check: If JacksonConfiguration exists, ensure JavaTimeModule is registered.
File: server/src/main/java/com/saas/springular/config/JacksonConfiguration.java (if exists)
Ensure it includes:
@Configuration
public class JacksonConfiguration {
@Bean
public ObjectMapper objectMapper() {
return JsonMapper.builder()
.addModule(new JavaTimeModule())
.build();
}
}
Note: Spring Boot may auto-configure this. Verify it works for JSON serialization.
Verification Checklist
After completing Phase 1 Foundation, verify:
-
ChatModelbean loads successfully (check application startup logs for configuration log message) - Health check endpoint works (if applicable)
- Timeout is configured (check
AIConfigurationlogs show timeout value) - Logging correlation ID present (if using MDC)
- No prompt content logged at INFO level by default (review logging configuration)
- Docker Compose starts Ollama successfully
- Application connects to Ollama (check logs for connection errors)
Quick Test:
Create a simple test to verify bean creation:
@SpringBootTest
class AIConfigurationTest {
@Autowired
private ChatLanguageModel chatModel;
@Test
void chatModelBeanExists() {
assertThat(chatModel).isNotNull();
}
}
2. Verify Ollama Service is Running
Test: Start Docker Compose and verify Ollama is accessible:
docker-compose up -d ollama
curl http://localhost:11434/api/tags
Expected: Should return list of available models.
3. Verify Basic Text Generation Works
Test: Create a simple service test:
@SpringBootTest
class BasicTextGenerationTest {
@Autowired
private ChatLanguageModel chatModel;
@Test
@Disabled("Requires Ollama running")
void generatesText() {
String response = chatModel.chat("Say hello in one word");
assertThat(response).isNotBlank();
}
}
Note: This requires Ollama to be running and a model to be downloaded. First run ollama pull llama2:7b in the Ollama container or host. Test is disabled by default to avoid requiring Ollama for all test runs.
Time Estimate
Total Time: 1-2 hours (porting infrastructure, not inventing)
Breakdown:
- Dependencies: 5 minutes
- Constants: 10 minutes
- Configuration: 15 minutes
- AIConfiguration class: 20 minutes
- Docker Compose: 10 minutes
- JacksonConfiguration check: 10 minutes
- Testing and validation: 30 minutes
Next Steps
Once Phase 1 is complete:
-
Phase 3: Build Text Operations Service (productized capabilities)
- See Rewriting — anchor primitive, first
- See Generation — extension, second
-
Phase 2 (Later): Port full RAG/embeddings foundation when products need it
Troubleshooting
Issue: ChatModel bean not found
Solution: Verify @Configuration annotation on AIConfiguration class, check package scanning.
Issue: Ollama connection timeout
Solution: Verify Ollama service is running, check base URL in application.yml matches Docker port.
Issue: Model not found
Solution: Pull model in Ollama: ollama pull llama2:7b (or model name from config).
Issue: Jackson serialization errors
Solution: Ensure JavaTimeModule is registered in ObjectMapper configuration.