Skip to content

Spring AI TypeSafe

A Java client for the TypeSafe AI System One API (jev), plus a Spring AI integration that uses it for judging, guardrails, RAG triage and tool selection.

Overview

Most AI code coerces a text-generation model into emitting structured decisions, then parses the results back into something the program can depend on. That round trip is where the failures live: the model answers in a shape the parser did not expect, a rating drifts between runs, and a single overall score hides the one defect that mattered.

Jev is not a chat model. It takes a state and a map of typed questions, and returns structured answers — no text generation, no JSON to coerce, no parsing. Every question in a call is answered against the same state in parallel, so the documented pattern is atomic questions, composed in code rather than one broad rubric prompt.

SystemOneResponse response = typeSafeClient.systemOne(
        "Help! My payouts have been failing for 3 days.",
        Map.of(
            "is_urgent",   Noul.of("Does this convey urgency?"),
            "department",  Choice.builder()
                    .instructions("Which team should handle this?")
                    .option("billing",   "Payments, invoicing, refunds")
                    .option("technical", "Bugs, outages, integrations")
                    .option("sales",     "Pricing, upgrades, new accounts")
                    .build(),
            "frustration", Score.of("How frustrated is the customer?",
                    "Calm", "Frustrated", "Very angry")));

double urgency     = response.noulValue("is_urgent");           // 0.92
String department  = response.choiceValue("department");        // "technical"
double confidence  = response.choice("department").confidence(); // 0.82
double frustration = response.scoreValue("frustration");        // 1.6

What this SDK adds on top of the API:

  • A typed client with retries, a typed exception hierarchy and a batch API
  • JevJudge — LLM-as-a-judge built from atomic criteria instead of one rubric prompt
  • Spring AI integrations that implement the framework's own SPIs: CallAdvisor, DocumentPostProcessor, ToolIndex and Evaluator

Project Structure

spring-ai-typesafe/
├── typesafe-java-sdk/            # The client: TypeSafeClient, questions, answers, errors, batches
├── typesafe-spring-ai/           # Judge, advisors, RAG post-processors, tool index
├── spring-ai-starter-typesafe/   # spring.ai.typesafe.* properties and a TypeSafeClient bean
├── typesafe-bom/                 # Bill of materials for the three above
└── examples/                     # Seven runnable demos

typesafe-spring-ai holds four packages:

Package Holds Needs
…typesafe.judge JevJudge, JevEvaluator, JevConfidenceGate, JevCompositeScore, JevConsistency
…typesafe.advisor JevSelfRefineAdvisor, JevGuardrailAdvisor
…typesafe.rag JevDocumentFilter, JevDocumentReranker spring-ai-rag
…typesafe.toolsearch JevToolIndex spring-ai-tool-search-tool

The last two dependencies are declared <optional>true</optional>, so an application that does not do RAG or tool search never sees those classes and never pays for the dependency.

Quick Start

1. Add the dependency:

<dependency>
    <groupId>org.springaicommunity</groupId>
    <artifactId>spring-ai-starter-typesafe</artifactId>
    <version>0.1.0-SNAPSHOT</version>
</dependency>

<!-- For the judge, advisors and Spring AI integrations -->
<dependency>
    <groupId>org.springaicommunity</groupId>
    <artifactId>typesafe-spring-ai</artifactId>
    <version>0.1.0-SNAPSHOT</version>
</dependency>

Plain Java without Spring Boot needs only typesafe-java-sdk.

Or import the BOM and drop the versions

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springaicommunity</groupId>
            <artifactId>typesafe-bom</artifactId>
            <version>0.1.0-SNAPSHOT</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

The three artifacts above then need no <version> of their own, and cannot drift apart from each other.

Note

You need Java 17 or later and, for typesafe-spring-ai, Spring AI 2.0.1 or later.

2. Set your API key:

export TYPESAFE_API_KEY=...

This is the same variable the official Python and JavaScript SDKs read. TypeSafeClient.builder().build() picks it up with no further configuration.

3. Ask a question:

@SpringBootApplication
public class Application {

    @Bean
    CommandLineRunner demo(TypeSafeClient typeSafeClient) {
        return args -> {
            SystemOneResponse response = typeSafeClient.systemOne(
                    "My card was charged twice.",
                    Map.of("refund_requested", Noul.of("Is the customer asking for money back?")));

            if (response.noulValue("refund_requested") > 0.8) {
                startRefund();
            }
        };
    }
}

The starter contributes the TypeSafeClient bean as soon as spring.ai.typesafe.api-key is set — see Spring Boot Starter.

If you want to… Read
Understand nouls, choices and scores The Three Primitives
Know when to act on an answer automatically Confidence
Evaluate a model's answers JevJudge
Retry a bad answer automatically JevSelfRefineAdvisor
Screen unsafe input or output JevGuardrailAdvisor
Improve a RAG pipeline JevDocumentFilter and JevDocumentReranker
Pick a tool from a large toolset JevToolIndex
Score many items at once Batches
See it all working Demos

Requirements

  • Java 17+
  • Spring Boot 4.x (for the starter)
  • Spring AI 2.0.1 or later (for typesafe-spring-ai)
  • Maven 3.6+
  • A TypeSafe API key

Building

# Build everything; the suite is offline and needs no key
mvn clean verify

# Add the tests that talk to the real API
export TYPESAFE_API_KEY=...
mvn clean verify -Pintegration-tests

# Run a demo
mvn install -DskipTests
mvn -pl examples spring-boot:run \
    -Dspring-boot.run.main-class=org.springaicommunity.typesafe.demo.TicketTriageDemo

Integration tests are gated twice — behind the integration-tests profile and behind TYPESAFE_API_KEY — so an exported key never turns an ordinary build into a billed one.

License

Apache License 2.0