spring-ai-integration — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited spring-ai-integration (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Choose your model provider — 1.0 GA renamed every starter to spring-ai-starter-* -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-anthropic</artifactId>
</dependency>
<!-- OR -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<!-- For RAG / vector search -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
</dependency>
</dependencies>Watch the artifact names. 1.0 GA dropped the oldspring-ai-<x>-spring-boot-startercoordinates. The pattern is nowspring-ai-starter-model-<provider>(e.g.-model-anthropic,-model-openai) andspring-ai-starter-vector-store-<store>. Agents trained on pre-GA Spring AI will emit the dead names — they resolve to nothing in Maven Central.
@Service
@RequiredArgsConstructor
public class DocumentSummaryService {
private final ChatClient chatClient;
public String summarize(String content) {
return chatClient.prompt()
.user(u -> u.text("Summarize the following document in 3 bullet points:\n\n{content}")
.param("content", content))
.call()
.content();
}
// With system prompt
public String analyzeFinancial(String document, String language) {
return chatClient.prompt()
.system("You are a financial analyst. Respond in {language}.")
.system(s -> s.param("language", language))
.user(document)
.call()
.content();
}
}@Configuration
public class AiConfig {
@Bean
public ChatMemory chatMemory() {
// 1.0 GA: InMemoryChatMemory is GONE. Use MessageWindowChatMemory —
// it caps history to a sliding window and defaults to an in-memory repository.
return MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
}
@Bean
public ChatClient chatClient(ChatClient.Builder builder, ChatMemory chatMemory) {
return builder
.defaultSystem("You are a helpful assistant for an e-commerce platform.")
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(), // GA: builder, not new(...)
new SimpleLoggerAdvisor() // logs prompts/responses
)
.build();
}
}// src/main/resources/prompts/analyze-order.st
// Analyze this order and identify any anomalies:
// Customer: {customer}
// Items: {items}
// Total: {total}
// Flag any unusual patterns.
@Service
public class OrderAnalysisService {
@Value("classpath:prompts/analyze-order.st")
private Resource promptTemplate;
public String analyzeOrder(Order order) {
return chatClient.prompt()
.user(u -> u.text(promptTemplate)
.param("customer", order.getCustomerEmail())
.param("items", order.getItems().toString())
.param("total", order.getTotal()))
.call()
.content();
}
}// Define the target record
public record OrderClassification(
String category,
String priority,
List<String> tags,
boolean requiresManualReview
) {}
@Service
public class OrderClassifier {
public OrderClassification classify(String orderDescription) {
return chatClient.prompt()
.user("Classify this order: " + orderDescription)
.call()
.entity(OrderClassification.class); // Spring AI handles JSON parsing
}
}@Configuration
public class RagConfig {
// No manual VectorStore bean — the spring-ai-starter-vector-store-pgvector
// starter auto-configures one. Just inject it. (The old `new PgVectorStore(...)`
// constructor is removed in GA; if you must build one, use PgVectorStore.builder(...).)
@Bean
public ChatClient ragChatClient(ChatClient.Builder builder, VectorStore vectorStore) {
return builder
.defaultAdvisors(
QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder().topK(5).build()) // GA: builder, not defaults().withTopK()
.build()
)
.build();
}
}
@Service
@RequiredArgsConstructor
public class KnowledgeService {
private final VectorStore vectorStore;
private final ChatClient ragChatClient;
// Ingest documents
public void ingest(List<String> documents) {
List<Document> docs = documents.stream()
.map(content -> new Document(content))
.toList();
vectorStore.add(docs);
}
// Query with RAG
public String ask(String question) {
return ragChatClient.prompt()
.user(question)
.call()
.content();
}
}@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam String prompt) {
return chatClient.prompt()
.user(prompt)
.stream()
.content();
}spring:
ai:
anthropic:
api-key: ${ANTHROPIC_API_KEY}
chat:
options:
model: claude-sonnet-4-20250514
max-tokens: 2048
temperature: 0.7
# OR for OpenAI:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o
vectorstore:
pgvector:
initialize-schema: true
dimensions: 1536spring-ai-anthropic-spring-boot-starter) — GA is spring-ai-starter-model-anthropicnew MessageChatMemoryAdvisor(new InMemoryChatMemory()) — both removed in GA; use MessageChatMemoryAdvisor.builder(chatMemory) + MessageWindowChatMemorySearchRequest.defaults().withTopK(n) — GA is SearchRequest.builder().topK(n).build()${...}.param() template variablessrc/main/resources/prompts/.entity(MyClass.class) instead of parsing manually.entity(List.class) for a list — generics erase; pass new ParameterizedTypeReference<List<X>>() {}NonTransientAiException (don't retry) vs TransientAiException (retry)conversationId on the memory advisor — all users share one chat history~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.