|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +**Semantic AI Validator** is a Kotlin library for AI-powered semantic validation of form fields using Large Language Models (LLMs). The project enables validation by describing requirements in plain English rather than writing complex regex patterns. |
| 8 | + |
| 9 | +- **Owner**: SoftwareMill |
| 10 | +- **License**: Apache 2.0 |
| 11 | +- **Target**: Spring Boot 3.5+ applications, Java 21+ |
| 12 | +- **Language**: Kotlin 2.1.0 with explicit API mode in core library |
| 13 | +- **Key Framework**: Koog AI Framework (0.5.0) for LLM integration |
| 14 | + |
| 15 | +## Module Structure |
| 16 | + |
| 17 | +This is a multi-module Gradle project with three modules: |
| 18 | + |
| 19 | +1. **semantic-ai-validator-core**: Core validation library (publishable to Maven Central) |
| 20 | + - Standalone, framework-agnostic validation logic |
| 21 | + - Annotations: `@AIVerify`, `@AIVerifyContext` |
| 22 | + - LLM client integration via Koog framework (OpenAI, Anthropic, Google, Ollama) |
| 23 | + - Reflection-based field discovery and validation orchestration |
| 24 | + - Coroutine-based async validation |
| 25 | + |
| 26 | +2. **semantic-ai-validator-spring-boot-starter**: Spring Boot integration (publishable to Maven Central) |
| 27 | + - Auto-configuration for Spring Boot 3.5+ |
| 28 | + - Type-safe configuration properties (`AIValidatorProperties`) |
| 29 | + - Integration with Spring Validation framework (`@Valid`) |
| 30 | + - `@ValidateWithAI` constraint annotation |
| 31 | + - Controller advice for error handling |
| 32 | + |
| 33 | +3. **semantic-ai-validator-demo**: Demo application (not published) |
| 34 | + - Full-featured Spring Boot application showcasing library capabilities |
| 35 | + - Web UI with Thymeleaf templates |
| 36 | + - Example forms: project validation, invoice PDF validation, product reviews |
| 37 | + - Unit tests, integration tests, and Playwright E2E tests |
| 38 | + |
| 39 | +## Common Development Commands |
| 40 | + |
| 41 | +### Building and Testing |
| 42 | + |
| 43 | +```bash |
| 44 | +# Build entire project |
| 45 | +./gradlew build |
| 46 | + |
| 47 | +# Build without tests (faster) |
| 48 | +./gradlew build -x test |
| 49 | + |
| 50 | +# Run all tests |
| 51 | +./gradlew test |
| 52 | + |
| 53 | +# Run tests for specific module |
| 54 | +./gradlew :semantic-ai-validator-core:test |
| 55 | +./gradlew :semantic-ai-validator-spring-boot-starter:test |
| 56 | +./gradlew :semantic-ai-validator-demo:test |
| 57 | + |
| 58 | +# Run specific test class |
| 59 | +./gradlew test --tests 'AIFieldValidatorTest' |
| 60 | + |
| 61 | +# Run specific test method |
| 62 | +./gradlew test --tests 'AIFieldValidatorTest.should validate field successfully' |
| 63 | + |
| 64 | +# Generate code coverage report |
| 65 | +./gradlew test koverHtmlReport |
| 66 | +# Report: build/reports/kover/html/index.html |
| 67 | +``` |
| 68 | + |
| 69 | +### Running the Demo Application |
| 70 | + |
| 71 | +```bash |
| 72 | +# Set API key first (required) |
| 73 | +export OPENAI_API_KEY=sk-... |
| 74 | + |
| 75 | +# Run demo application |
| 76 | +./gradlew :semantic-ai-validator-demo:bootRun |
| 77 | + |
| 78 | +# Alternative: run with Gradle wrapper and skip tests |
| 79 | +OPENAI_API_KEY="$OPENAI_API_KEY" ./gradlew :semantic-ai-validator-demo:bootRun -x test |
| 80 | + |
| 81 | +# Demo will be available at http://localhost:8080 |
| 82 | +``` |
| 83 | + |
| 84 | +### Publishing |
| 85 | + |
| 86 | +```bash |
| 87 | +# Publish to local Maven repository for testing |
| 88 | +./gradlew publishToMavenLocal |
| 89 | + |
| 90 | +# Check publication configuration |
| 91 | +./gradlew :semantic-ai-validator-core:publishMavenPublicationToMavenLocal |
| 92 | +./gradlew :semantic-ai-validator-spring-boot-starter:publishMavenPublicationToMavenLocal |
| 93 | +``` |
| 94 | + |
| 95 | +## Architecture Notes |
| 96 | + |
| 97 | +### LLM Client Architecture |
| 98 | + |
| 99 | +- **Koog Framework**: All LLM interactions use the Koog AI Framework (ai.koog:koog-agents:0.5.0) |
| 100 | +- **Multiple Providers**: Single unified API (`LLMClient` interface) wraps Koog executors for OpenAI, Anthropic, Google, Ollama |
| 101 | +- **Factory Pattern**: `LLMClientFactory` creates appropriate client based on provider configuration |
| 102 | +- **Async by Default**: All validation is coroutine-based using `suspend` functions |
| 103 | +- **Context7 Integration**: When working with Koog framework features, use Context7 to fetch latest docs |
| 104 | + |
| 105 | +### Validation Flow |
| 106 | + |
| 107 | +1. **Annotation Scanning** (`ReflectionUtils`): Discover `@AIVerify` fields using Kotlin reflection |
| 108 | +2. **Context Extraction**: Gather values from fields marked with `@AIVerifyContext` or listed in `contextFields` |
| 109 | +3. **Prompt Building** (`PromptBuilder`): Construct validation prompts with field values and context |
| 110 | +4. **Parallel Execution**: Validate multiple fields concurrently using coroutines |
| 111 | +5. **Response Parsing**: Parse LLM responses to extract validation results (VALID/INVALID + error messages) |
| 112 | +6. **Result Aggregation** (`AIObjectValidator`): Combine field-level results into object-level validation |
| 113 | + |
| 114 | +### Spring Integration |
| 115 | + |
| 116 | +- **Auto-Configuration** (`AIValidatorAutoConfiguration`): Conditionally creates beans based on API keys |
| 117 | +- **Constraint Validator** (`AIValidatorConstraintValidator`): Bridges to Spring's `@Valid` mechanism |
| 118 | +- **Controller Advice** (`AIValidationControllerAdvice`): Handles validation exceptions and formats error responses |
| 119 | +- **Configuration Properties** (`AIValidatorProperties`): Type-safe binding from `application.yml` |
| 120 | + |
| 121 | +### Dependency Version Management |
| 122 | + |
| 123 | +**IMPORTANT**: Koog 0.5.0 was compiled with kotlinx-serialization 1.8.1 and is **incompatible** with 1.9.0+. All modules enforce version 1.8.1 through: |
| 124 | +- `resolutionStrategy.force()` in root build.gradle.kts |
| 125 | +- Version constraints in core module |
| 126 | +- Dependency management overrides in demo module |
| 127 | + |
| 128 | +## Testing Strategy |
| 129 | + |
| 130 | +### Coverage Requirements |
| 131 | +- **Minimum**: 80% for all modules |
| 132 | +- **Target**: 90%+ for core library |
| 133 | +- **Critical paths**: 100% for validation logic |
| 134 | + |
| 135 | +### Test Types |
| 136 | + |
| 137 | +1. **Unit Tests** (Kotest + MockK): |
| 138 | + - Test individual components in isolation |
| 139 | + - Mock LLM clients and external dependencies |
| 140 | + - Fast, no Spring context |
| 141 | + |
| 142 | +2. **Integration Tests** (Spring Boot Test): |
| 143 | + - Load full Spring context |
| 144 | + - Test auto-configuration and bean wiring |
| 145 | + - Validate end-to-end Spring integration |
| 146 | + |
| 147 | +3. **E2E Tests** (Playwright): |
| 148 | + - Browser-based tests of demo application |
| 149 | + - Test complete user flows with real UI interaction |
| 150 | + - Located in `semantic-ai-validator-demo/src/test` |
| 151 | + |
| 152 | +### Running E2E Tests |
| 153 | + |
| 154 | +E2E tests use Playwright and require the demo application to be running: |
| 155 | + |
| 156 | +```bash |
| 157 | +# Terminal 1: Start demo app |
| 158 | +export OPENAI_API_KEY=sk-... |
| 159 | +./gradlew :semantic-ai-validator-demo:bootRun |
| 160 | + |
| 161 | +# Terminal 2: Run E2E tests |
| 162 | +./gradlew :semantic-ai-validator-demo:test --tests '*E2E*' |
| 163 | +``` |
| 164 | + |
| 165 | +## Code Style and Conventions |
| 166 | + |
| 167 | +### Kotlin Style |
| 168 | +- **Indentation**: 4 spaces (no tabs) |
| 169 | +- **Line length**: 120 characters max |
| 170 | +- **API visibility**: Core module uses `kotlin { explicitApi() }` - all public APIs require explicit visibility modifiers |
| 171 | +- **Immutability**: Prefer `val` over `var` |
| 172 | +- **Null safety**: Avoid nullable types unless necessary |
| 173 | + |
| 174 | +### Testing Conventions |
| 175 | +- Use **backtick test names** for readability: `` `should validate field when LLM returns VALID` `` |
| 176 | +- Follow **AAA pattern**: Arrange, Act, Assert |
| 177 | +- Use **Kotest matchers**: `shouldBe`, `shouldContain`, `shouldBeEmpty()` |
| 178 | + |
| 179 | +### Documentation |
| 180 | +- **KDoc required** for all public APIs in core and starter modules |
| 181 | +- Include **usage examples** in KDoc |
| 182 | +- Document **exceptions** and edge cases |
| 183 | +- Add **`@since` tags** for version tracking |
| 184 | + |
| 185 | +## Environment Variables |
| 186 | + |
| 187 | +```bash |
| 188 | +# LLM Provider API Keys |
| 189 | +OPENAI_API_KEY=sk-... |
| 190 | +ANTHROPIC_API_KEY=sk-ant-... |
| 191 | +GOOGLE_API_KEY=... |
| 192 | + |
| 193 | +# Ollama (for local models) - default is http://localhost:11434 |
| 194 | +OLLAMA_BASE_URL=http://localhost:11434 |
| 195 | +``` |
| 196 | + |
| 197 | +## Common Issues and Solutions |
| 198 | + |
| 199 | +### kotlinx-serialization Version Conflicts |
| 200 | + |
| 201 | +**Problem**: Build fails with `NoSuchMethodError` or serialization errors. |
| 202 | + |
| 203 | +**Solution**: Koog 0.5.0 requires kotlinx-serialization 1.8.1. The build is already configured to enforce this, but if you add new dependencies that transitively pull in 1.9.0+, add force resolution in root build.gradle.kts. |
| 204 | + |
| 205 | +### Demo App Won't Start |
| 206 | + |
| 207 | +**Problem**: Application fails to start with bean creation errors. |
| 208 | + |
| 209 | +**Solution**: Ensure at least one LLM provider API key is configured. The auto-configuration conditionally creates beans based on API key presence. |
| 210 | + |
| 211 | +### Tests Requiring Real LLM API Calls |
| 212 | + |
| 213 | +**Problem**: Some tests need actual LLM responses to validate behavior. |
| 214 | + |
| 215 | +**Solution**: Mock the `LLMClient` interface using MockK. For integration tests that need real calls, use `@TestInstance` and tag tests appropriately so they can be skipped in CI. |
| 216 | + |
| 217 | +## Contributing Workflow |
| 218 | + |
| 219 | +1. **Branch naming**: `feature/my-feature`, `fix/bug-description` |
| 220 | +2. **Commit messages**: Follow [Conventional Commits](https://www.conventionalcommits.org/) (feat:, fix:, docs:, test:, refactor:, chore:) |
| 221 | +3. **Before submitting PR**: |
| 222 | + - Run `./gradlew clean build test` |
| 223 | + - Ensure 90%+ test coverage for new code |
| 224 | + - Update relevant README files |
| 225 | + - Add KDoc to public APIs |
| 226 | +4. **Testing requirements**: All new code must have tests (unit + integration where applicable) |
| 227 | + |
| 228 | +## Useful Gradle Properties |
| 229 | + |
| 230 | +Location: `gradle.properties` |
| 231 | + |
| 232 | +```properties |
| 233 | +koogVersion=0.5.0 # Koog AI Framework version |
| 234 | +kotlinVersion=2.1.0 # Kotlin compiler version |
| 235 | +springBootVersion=3.5.3 # Spring Boot version |
| 236 | +kotestVersion=5.9.1 # Kotest testing framework |
| 237 | +playwrightVersion=1.49.0 # Playwright for E2E tests |
| 238 | +``` |
| 239 | + |
| 240 | +## Related Documentation |
| 241 | + |
| 242 | +- Main README: `/README.md` - User-facing documentation, quick start |
| 243 | +- Contributing Guide: `/CONTRIBUTING.md` - Detailed contribution guidelines |
| 244 | +- Core Module: `/semantic-ai-validator-core/README.md` - Standalone usage, API reference |
| 245 | +- Spring Starter: `/semantic-ai-validator-spring-boot-starter/README.md` - Spring Boot configuration |
| 246 | +- Demo App: `/semantic-ai-validator-demo/README.md` - Demo endpoints and examples |
| 247 | +- whenever you try to mutate a list in kotlin consider using map operation |
| 248 | +- whenever return keyword is not necessary in kotlin functions don't use it |
0 commit comments