Back to Blog
Backend

Spring AI 2.1 Milestone 1: Message Parts, OpenAI Responses API, and Bring-Your-Own Embeddings

A hands-on look at Spring AI 2.1.0-M1: ordered message parts, the new OpenAI Responses API chat model, and VectorStore.upsert for pre-computed embeddings.

Sanjay ThakurSeptember 29, 20266 min read
Spring AI 2.1 Milestone 1: Message Parts, OpenAI Responses API, and Bring-Your-Own Embeddings

Spring AI 2.1.0-M1 landed on September 25, 2026, built against Spring Boot 4.2.0-M2. It's a milestone, so it isn't meant for production yet, but it previews three changes that matter if you're building AI features on the JVM: a structured message model, support for OpenAI's Responses API, and a way to store embeddings you computed elsewhere. Here's a practical walkthrough of each.

Why This Release Is Worth a Look

Most AI integrations start simple: send text, get text back. Then you add images, tool calls, and reasoning models, and a flat "message with a text field" stops describing what the model actually returned. Spring AI 2.1 addresses that directly, and it also catches up with how OpenAI now expects reasoning models to be called.

1. Messages Are Now Ordered Parts

Messages in 2.1 carry an ordered list of MessagePart entries instead of a single blob of text plus side lists. The part types are TextPart, ReasoningPart, ToolCallPart, ToolResultPart, MediaPart, and UnknownPart.

Order matters because modern models interleave content: a bit of reasoning, a tool call, more reasoning, then an answer. Keeping that sequence means a conversation can round-trip to the provider faithfully, which is important when you replay history on the next turn.

Building a multimodal prompt becomes explicit:

UserMessage question = UserMessage.builder()
    .part(TextPart.of("What changed between these two charts?"))
    .part(MediaPart.of(q2Chart))
    .part(MediaPart.of(q3Chart))
    .build();

Existing code keeps working: accessors like getText() and getToolCalls() are now views over the parts, so you can migrate gradually.

2. OpenAI Responses API Support

The new OpenAiResponsesChatModel talks to OpenAI's /v1/responses endpoint. The motivation is practical: per the release announcement, starting with GPT-5.4, Chat Completions doesn't support tool calling combined with a reasoning effort other than none. If you want a reasoning model that also calls your tools, you need the Responses API.

Switching is a single property:

spring.ai.openai.api-key=${OPENAI_API_KEY}
spring.ai.openai.chat.api=responses

Because the switch happens at the model layer, code written against ChatClient doesn't need to change:

@Service
public class ReportAssistant {

    private final ChatClient chatClient;

    public ReportAssistant(ChatClient.Builder builder) {
        this.chatClient = builder
            .defaultSystem("You are a concise financial reporting assistant.")
            .build();
    }

    public String summarize(String reportText) {
        return chatClient.prompt()
            .user(u -> u.text("Summarize the key risks in this report:\n{report}")
                        .param("report", reportText))
            .call()
            .content();
    }
}

The implementation is deliberately stateless, so you still own conversation history (for example with Spring AI's chat memory support). OpenAiResponsesChatOptions exposes Responses-specific settings such as reasoning effort, structured output, and hosted tools like web search, file search, code interpreter, and image generation.

3. Upserting Pre-Computed Embeddings

Until now, VectorStore.add() embedded documents for you using the configured EmbeddingModel. That's convenient, but many teams already produce embeddings in a separate pipeline, such as a batch job or a data platform, and re-embedding everything inside the application is wasteful.

2.1 adds VectorStore.upsert(), which takes EmbeddedDocument objects that pair a Document with a vector you supply. It has replace-by-id semantics: an existing document with the same id is replaced, otherwise a new one is inserted. Initial support covers pgvector, Redis, Elasticsearch, and Qdrant.

@Component
public class EmbeddingImporter {

    private final VectorStore vectorStore;
    private final ExternalEmbeddingClient embeddings; // your own pipeline client

    public EmbeddingImporter(VectorStore vectorStore, ExternalEmbeddingClient embeddings) {
        this.vectorStore = vectorStore;
        this.embeddings = embeddings;
    }

    public void importArticles(List<Article> articles) {
        var batch = articles.stream()
            .map(a -> new EmbeddedDocument(
                new Document(a.id(), a.summary(), Map.of("source", a.url())),
                embeddings.vectorFor(a.id())))
            .toList();

        vectorStore.upsert(batch);
    }
}

One thing to watch: the vectors must come from the same embedding model (and dimension) your query side uses, or similarity search results will be meaningless.

Trying the Milestone

Milestones aren't on Maven Central, so add the Spring milestone repository and use the BOM:

<repositories>
  <repository>
    <id>spring-milestones</id>
    <url>https://repo.spring.io/milestone</url>
  </repository>
</repositories>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-bom</artifactId>
      <version>2.1.0-M1</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Since this milestone targets Spring Boot 4.2, try it on a branch that also moves to the Boot 4.2 milestone rather than mixing it into a production service.

Other Changes Worth Knowing

  • MCP servers can be customized with McpStatelessSyncServerCustomizer and McpStatelessAsyncServerCustomizer beans.
  • The Redis vector store accepts custom metadata fields.
  • Timeout configuration for spring.ai.openai.timeout and spring.ai.openai.chat.timeout was fixed.

Separately, the Spring team announced a new release rhythm: the whole portfolio now ships patch releases on a single "Patch Thursday" each month, driven by a surge in security reports. If you maintain Spring services, it's a predictable date to plan dependency updates around.

Key Takeaways

  • Messages are now ordered lists of typed parts, preserving interleaved reasoning and tool calls; old accessors still work.
  • spring.ai.openai.chat.api=responses switches to OpenAI's Responses API, needed for reasoning models that also call tools.
  • VectorStore.upsert() with EmbeddedDocument lets you store externally computed embeddings with replace-by-id semantics.
  • 2.1.0-M1 targets Spring Boot 4.2 and is a milestone: experiment on a branch, not in production.

Resources

Spring BootSpring AIJavaAI