Atmosphere's unified AI abstraction layer sits between @AiEndpoint handlers and the four
supported AI frameworks (Spring AI, LangChain4j, Google ADK, Embabel). It provides:
- Tool Calling SPI —
@AiToolannotation → framework-agnosticToolDefinition→ native bridges - Capability Discovery —
AiCapabilityenum +ModelRouterfor smart routing/failover - Conversation Memory —
ConversationPersistenceSPI backed by Redis or SQLite - Guardrails — sealed
GuardrailResult(Pass/Modify/Block) pipeline - RAG —
ContextProviderSPI for retrieval-augmented generation - Retry/Circuit Breaker —
RetryPolicyrecord +DefaultModelRouterhealth tracking - Multi-Modal Content —
Contentsealed interface (Text/Image/File) - Observability —
AiMetricsSPI aligned with OpenTelemetry GenAI conventions
@AiEndpoint handler
│
▼
AiStreamingSession.stream(message)
│
├── ToolRegistry.allTools() → AiRequest.withTools(...)
├── AiGuardrail.inspectRequest() → Pass / Modify / Block
├── ContextProvider.retrieve() → RAG augmentation
├── AiInterceptor.preIntercept()
│
▼
AiSupport.stream(request, session) ← adapter selected by ModelRouter
│
├── SpringAiSupport → SpringAiToolBridge → ToolCallback[]
├── LangChain4jSupport → LangChain4jToolBridge → ToolSpecification[]
├── AdkAiSupport → AdkToolBridge → BaseTool[]
└── EmbabelAiSupport → (agent orchestration)
│
▼
StreamingSession → WebSocket/SSE/gRPC client
Tools → Guardrails (pre) → Context Providers (RAG) → Interceptors (pre)
→ [LLM call] →
Interceptors (post) → Client
| File | Purpose |
|---|---|
AiCapability.java |
9-value enum for feature discovery |
Content.java |
Sealed interface: Text, Image, File records |
AiGuardrail.java |
Pre/post guardrails with sealed GuardrailResult |
ContextProvider.java |
RAG SPI with Document record |
ModelRouter.java |
Routing SPI with FallbackStrategy enum |
DefaultModelRouter.java |
Circuit breaker + FAILOVER/ROUND_ROBIN/CONTENT_BASED |
RetryPolicy.java |
Exponential backoff with jitter |
AiMetrics.java |
OpenTelemetry-aligned observability SPI |
ConversationPersistence.java |
Thin persistence SPI (load/save/remove) |
PersistentConversationMemory.java |
Write-through cache + sliding window |
annotation/AiTool.java |
Method annotation for AI-callable tools |
annotation/Param.java |
Parameter annotation for tool methods |
tool/ToolDefinition.java |
Framework-agnostic tool record + builder |
tool/ToolParameter.java |
Parameter metadata with JSON Schema types |
tool/ToolExecutor.java |
Functional interface for tool execution |
tool/ToolResult.java |
Execution result record (success/failure) |
tool/ToolRegistry.java |
Tool registry interface |
tool/DefaultToolRegistry.java |
ConcurrentHashMap-backed + @AiTool scanning |
| File | Framework | How Tools Are Registered |
|---|---|---|
SpringAiToolBridge.java |
Spring AI 2.0 | ToolDefinition → ToolCallback → promptSpec.toolCallbacks(...) |
LangChain4jToolBridge.java |
LangChain4j 1.0 | ToolDefinition → ToolSpecification → ChatRequest.builder().toolSpecifications(...) |
ToolAwareStreamingResponseHandler.java |
LangChain4j 1.0 | Handles tool call loop (execute → re-submit, max 5 rounds) |
AdkToolBridge.java |
Google ADK 0.2 | ToolDefinition → BaseTool subclass → LlmAgent.builder().tools(...) |
| File | Backend | Key Design |
|---|---|---|
RedisConversationPersistence.java |
Redis (Lettuce) | Key prefix atmosphere:conversation:, SETEX with TTL |
SqliteConversationPersistence.java |
SQLite (embedded) | Table ai_conversations, WAL mode, in-memory option |
AiStreamingAdapter.java— addeddefault capabilities()AiSupport.java— addeddefault capabilities()StreamingSession.java— addeddefault sendContent(Content)AiRequest.java— addedwithTools()/tools()for tool transportAiEndpoint.java— addedtools[],excludeTools[],guardrails[],contextProviders[],fallbackStrategyAiStreamingSession.java— full pipeline: tools → guardrails → RAG → interceptors → LLMAiEndpointProcessor.java— tool scanning, guardrail/context instantiationAiEndpointHandler.java— passes tools/guardrails/context to session- All adapter
*Support.javafiles — addedcapabilities()and tool bridge wiring
// 1. Developer declares a tool
public class WeatherTools {
@AiTool(name = "get_weather", description = "Get weather for a city")
public String getWeather(@Param("city") String city) {
return weatherService.get(city);
}
}
// 2. @AiEndpoint selects tools
@AiEndpoint(path = "/chat", tools = WeatherTools.class)
// 3. AiEndpointProcessor scans and registers
registry.register(new WeatherTools()); // reflection-based
// 4. AiStreamingSession.stream() attaches tools to request
request = request.withTools(toolRegistry.allTools());
// 5. Adapter bridges to native format
// Spring AI: SpringAiToolBridge.toToolCallbacks(tools) → promptSpec.toolCallbacks(...)
// LangChain4j: LangChain4jToolBridge.toToolSpecifications(tools) → chatRequest.toolSpecifications(...)
// ADK: AdkToolBridge.toAdkTools(tools) → LlmAgent.builder().tools(...)
// 6. Framework handles tool call loop automatically (Spring AI, ADK)
// or ToolAwareStreamingResponseHandler manages it (LangChain4j)75 tests across 4 modules, all passing:
| Module | Test Class | Tests |
|---|---|---|
atmosphere-ai |
DefaultToolRegistryTest |
11 — register, scan, execute, annotated tools |
atmosphere-ai |
ToolDefinitionTest |
10 — builder, validation, JSON schema types, ToolResult |
atmosphere-ai |
ContentTest |
9 — sealed variants, base64, validation, pattern matching |
atmosphere-ai |
RetryPolicyTest |
8 — backoff, jitter, cap, shouldRetry |
atmosphere-ai |
PersistentConversationMemoryTest |
10 — CRUD, persistence across instances, eviction |
atmosphere-ai |
DefaultModelRouterTest |
10 — failover, round-robin, capability filter, health |
atmosphere-durable-sessions-sqlite |
SqliteConversationPersistenceTest |
8 — CRUD, large payloads, special chars |
atmosphere-spring-ai |
SpringAiToolBridgeTest |
9 — schema gen, arg parsing, callback execution |
atmosphere-langchain4j |
LangChain4jToolBridgeTest |
8 — spec conversion, tool execution, arg parsing |
-
Hybrid @AiTool scope — tools registered globally in
ToolRegistry, selected per-endpoint via@AiEndpoint(tools=...)andexcludeTools=... -
ConversationPersistence as thin SPI — only 3 methods (load/save/remove). Redis and SQLite implementations in their respective durable-sessions modules with optional dependency on atmosphere-ai.
-
Manual JSON serialization in
PersistentConversationMemoryto avoid adding Jackson dependency to the core AI module. -
LangChain4j tool loop handled by
ToolAwareStreamingResponseHandler(max 5 rounds) since LangChain4j doesn't auto-execute tool callbacks in streaming mode. -
ADK tools at build time — ADK requires tools at agent construction.
AdkAiSupport.configureWithTools()method provided for this. Runtime request tools logged with guidance. -
Circuit breaker in
DefaultModelRouter— consecutive failure tracking, cooldown-based recovery, last-resort fallback to all backends when all healthy ones exhausted.