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,ToolIndexandEvaluator
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:
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.
What to read next¶
| 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.
Links¶
License¶
Apache License 2.0