Files
Vaessl/CLAUDE.md
T

7.0 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Vaessl is an AI-powered integration bridge that accepts user text/image inputs, processes them through an LLM pipeline (via LiteLLM), and exports structured data to management systems (Homebox, WikiJS). The backend uses a provider pattern for extensibility. The frontend has a working connection management dashboard.

Commands

Backend (Spring Boot + Gradle, inside backend/)

./gradlew build           # compile and package
./gradlew test            # run all tests
./gradlew test --tests com.vaessl.app.connection.ConnectionServiceTest  # single test class

Frontend (React + Vite, inside frontend/)

npm run dev       # start dev server
npm run build     # TypeScript check + Vite build
npm run lint      # ESLint
npm run test      # Vitest watch mode
npm run test:ui   # Vitest visual dashboard

Environment

Copy .env.local (not committed) into backend/ with:

  • DB_URL, DB_TEST_URL, DB_USERNAME, DB_PASSWORD — PostgreSQL (test container on port 5434)
  • PG_DRIVER_CLASS_NAME — PostgreSQL JDBC driver class
  • OPENAI_KEY, OPENAI_BASE_URL — LiteLLM gateway (provider-agnostic, configured for gpt-4o-mini)
  • FRONTEND_LOCAL_URL, FRONTEND_PUBLIC_URL — allowed CORS origins for the backend

Frontend (optional, defaults to /api):

  • VITE_API_URL — backend base URL used by api/client.ts

Architecture

Backend (backend/src/main/java/com/vaessl/app/)

Package-by-feature layout. Server context path is /api. Main endpoints:

  • POST /api/login — authenticates a service, stores connection ID in session
  • GET /api/connections/status — lists connected services for the current session
  • DELETE /api/connections/{serviceType} — removes a service from the session; invalidates the session if no connections remain
  • POST /api/search — paged search against the requested service; returns 401 if no active session

Six packages:

shared/ — cross-cutting types used by more than one feature package

  • ServiceType (enum): identifies each integrated app (e.g. HOMEBOX); used in both connection/ and search/
  • ServiceProvider (interface): base for ConnectionProvider and SearchProvider; declares getServiceType()
  • Endpoint (enum): API path constants for all external service calls
  • SessionKeys: builds session attribute names of the form {SERVICE_TYPE}_CONNECTION_ID

config/

  • CorsConfig: env-driven allowed origins (FRONTEND_LOCAL_URL, FRONTEND_PUBLIC_URL); allowCredentials(true) is required for session cookies to work cross-origin
  • SessionConfig: JDBC-backed Spring Session with a persistent cookie (SameSite=Lax, HttpOnly)

connection/ — connecting to and persisting service credentials

  • ConnectionProvider interface: extends ServiceProvider; each integrated app implements login() and credential checking
  • ConnectionService: auto-discovers providers via Spring injection, dispatches login by ServiceType
  • ConnectionController: stores {serviceType}_CONNECTION_ID in HttpSession after login; reads session attributes to build status responses
  • Entity (ConnectionEntity) uses Single Table Inheritance — one connections table with app-specific nullable columns
  • HomeboxConnectionProvider / HomeboxEntity: Homebox-specific implementation

ai/ — shared AI infrastructure used by multiple features (search, future image analysis)

  • EmbeddingService: wraps Spring AI's EmbeddingModel; reused by any feature that needs to generate vectors
  • VectorStoreConfig: Spring bean configuration for PgVectorStore (pgvector-backed VectorStore)
  • All classes here are provider-agnostic — the OpenAI starter is pointed at LiteLLM, so the underlying model is configurable without code changes

search/ — querying connected services (keyword and AI)

  • SearchProvider interface: extends ServiceProvider; each integrated app implements getSearchResults()
  • SearchService: maintains two provider maps — keyword providers and AI providers; routes based on SearchRequest.aiSearch flag
  • SearchController: guards with session check before delegating to SearchService
  • SearchRequest: includes aiSearch: boolean — when true, routes to AI provider instead of keyword provider
  • PagedSearchResponse: includes nullable summary field — populated only for AI search results; null for keyword search
  • HomeboxSearchProvider: keyword search via Homebox API; unchanged from original implementation
  • HomeboxAiSearchProvider: AI search via pgvector similarity; returns ranked items + generated summary
  • HomeboxSyncService: fetches all Homebox items page by page, embeds them via EmbeddingService, stores in VectorStore; triggered on connection login (background sync)

AI search flow: on login → background sync indexes all Homebox items into pgvector. On AI search → embed query → pgvector similarity search → top N results passed to ChatClient for summary generation → return list + summary. Sync is idempotent (delete-then-reindex per connection).

exception/GlobalExceptionHandler via @ControllerAdvice

Frontend (frontend/src/)

React 19 + TypeScript + SCSS, Vite 6 build. Package-by-feature under components/.

  • api/client.ts — typed apiFetch wrapper; always sends credentials: 'include' for session cookies; base URL from VITE_API_URL (defaults to /api)
  • api/connections.ts — connection-specific API calls (getStatuses, login, logout)
  • api/searches.ts — search API call (search)
  • types/connection.tsServiceType, LoginRequest, AuthResponse, ConnectionStatus
  • types/search.tsSearchRequest, SearchResponse, PagedSearchResponse
  • components/connections/Dashboard, ConnectModal, ServiceCard (connection management UI)
  • components/search/SearchModal — search dialog; posts to /api/search and renders paged results
  • components/ui/ — shared UI primitives (ActionButton, Modal)

Data & AI

  • PostgreSQL + pgvector (semantic search via embeddings); also used as the Spring Session store (JDBC)
  • LiteLLM as a unified AI proxy; Spring AI OpenAI starter wired to it — OPENAI_BASE_URL points to LiteLLM, not OpenAI directly, keeping the underlying model provider configurable
  • spring-ai-starter-vector-store-pgvector provides PgVectorStore; configured in ai/VectorStoreConfig
  • Embedding dimensions must stay consistent with the configured LiteLLM embedding model — changing models requires re-syncing all indexed items
  • Processing pipeline (Phase 2): stage in DB → LLM inference → refine via UI → export to target app

Testing Strategy

Integration tests spin up a mirrored PostgreSQL container on port 5434 (same schema as production). WireMock mocks external HTTP APIs (Homebox, WikiJS). Do not mock the database in integration tests — the mirrored container strategy exists specifically to catch schema/migration divergence.