TramAI

What Is TramAI

TramAI is a structured-first AI library for the JVM. You define typed interfaces with @AiService, TramAI handles the LLM calls, JSON extraction, parsing, validation, and retries. The result is a typed object -- not a raw string you need to parse yourself.

In Spring Boot, add @EnableTramai to your configuration class and AI services become injectable beans -- same pattern as @Service or @Repository. Your AI contract is a first-class citizen of your architecture, not a utility you call manually.

No prompt templates, no chains, no agent frameworks. One annotation on a Kotlin interface or Java interface, one data class for the return type, and you are calling an LLM like a local function. TramAI works standalone or inside Spring Boot, and every module is opt-in.

Add Dependencies

Always import the BOM first so all TramAI modules stay on the same version (0.4.0).

Standalone (any JVM app)

dependencies {
    implementation(platform("dev.tramai:tramai-bom:0.4.0"))
    implementation("dev.tramai:tramai-standalone")
    implementation("dev.tramai:tramai-openai")
}
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>dev.tramai</groupId>
      <artifactId>tramai-bom</artifactId>
      <version>0.4.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
<dependencies>
  <dependency>
    <groupId>dev.tramai</groupId>
    <artifactId>tramai-standalone</artifactId>
  </dependency>
  <dependency>
    <groupId>dev.tramai</groupId>
    <artifactId>tramai-openai</artifactId>
  </dependency>
</dependencies>

Spring Boot

implementation(platform("dev.tramai:tramai-bom:0.4.0"))
implementation("dev.tramai:tramai-spring")
implementation("dev.tramai:tramai-openai")

Replace tramai-openai with tramai-anthropic, tramai-ollama, or tramai-deepseek. All providers follow the same pattern. Add tramai-observability for OpenTelemetry, tramai-testing for test helpers, or tramai-orchestration for workflows -- each is additive and optional.

Define Your First Service

Write a typed contract. TramAI generates a JSON schema from the return type and wires the prompt to the model.

Kotlin

import dev.tramai.core.annotations.AiService
import dev.tramai.core.annotations.Operation

data class Analysis(
    val sentiment: String,
    val confidence: Double,
    val keyPoints: List<String>,
)

@AiService
interface ReviewAnalyzer {
    @Operation(
        prompt = "Analyze this product review. Return sentiment (positive/negative/neutral), confidence 0-1, and 1-3 key points.",
        model = "gpt-4o",
    )
    suspend fun analyze(review: String): Analysis
}

Java

import dev.tramai.core.annotations.AiService;
import dev.tramai.core.annotations.Operation;

public record Analysis(
    String sentiment,
    double confidence,
    List<String> keyPoints
) {}

@AiService
public interface ReviewAnalyzer {
    @Operation(
        prompt = "Analyze this product review. Return sentiment (positive/negative/neutral), confidence 0-1, and 1-3 key points.",
        model = "gpt-4o"
    )
    Analysis analyze(String review);
}

Key points:

  • The return type (data class or record) determines the JSON schema -- TramAI generates it automatically.
  • The @Operation prompt describes the task; TramAI handles schema injection and parsing.
  • model = "gpt-4o" is required -- you must specify which registered model to use.
  • Kotlin methods use suspend (AI calls are I/O-bound); Java methods are blocking by default.

Configure and Run

import dev.tramai.standalone.Tramai
import dev.tramai.openai.OpenAiProvider

suspend fun main() {
    val tramai = Tramai {
        provider(
            OpenAiProvider(apiKey = System.getenv("OPENAI_API_KEY")),
            name = "openai",
        )
        model("gpt-4o", "openai")
    }

    val analyzer = tramai.create<ReviewAnalyzer>()

    val result = analyzer.analyze(
        "Great product, fast shipping."
    )

    println(result)
}

Set OPENAI_API_KEY in your environment and run normally:

OPENAI_API_KEY=sk-... ./gradlew run

What happens under the hood: TramAI builds the full prompt from your @Operation text and generates a JSON schema from Analysis, sends the prompt + schema to the configured model, parses the JSON response into Analysis -- if parsing or validation fails it retries automatically (default: 2 retries) -- and returns a typed Analysis object. No manual JSON parsing, no regex, no error handling in your code.

What's Next

  • Spring Boot -- auto-configure TramAI in your existing app with YAML config and dependency injection: Spring Boot Guide
  • Providers -- switch to Anthropic, Ollama, Bedrock, or any OpenAI-compatible provider: Providers Guide
  • Tutorial -- build a complete Invoice Analyzer with tests: Tutorial