trendingzones
← Back to the Introduction level

JAVA + SPRING AI — INTERMEDIATE

Wiring Up ChatClient + Ollama: Dependency, Config & a Real Endpoint

The Introduction level covered what Spring AI and ChatClientare. Here’s the actual mechanics: the one dependency to add, the properties that point Spring AI at your local Ollama instance, and a real, working @RestController that sends a message and gets a reply back.

The Quick Answer

Add the spring-ai-starter-model-ollama dependency, set spring.ai.ollama.base-url and spring.ai.ollama.chat.model in your configuration, and Spring Boot autoconfigures a ChatClient.Builder bean for you. Constructor-inject that builder — the exact same dependency injection mechanics from the first topic in this pillar — call .build() on it once, and every request handler on your @RestControllercan send a user message and get the model’s reply back as a plain String with chatClient.prompt().user(message).call().content().

Step 1: Add the Ollama Starter

Spring AI ships one starter dependency per model provider. The current, correct one for Ollama — verified directly against Spring AI’s own reference documentation for its current release — is:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>

It’s worth being explicit that this exact artifact name is worth double-checking any time you’re following an older tutorial: Spring AI has renamed its auto-configuration and starter modules more than once on its way to a stable release, so an older guide showing something like spring-ai-ollama-spring-boot-starter is showing a name from an earlier, now-superseded version, not the current one.

Step 2: Point Spring AI at Your Local Ollama

Two properties do the actual pointing — where Ollama is running, and which model to talk to:

spring:
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        model: llama3.2

base-url is the address of your local Ollama server — the same one the Ollama Introduction topic on this site shows the CLI itself talking to; 11434is Ollama’s own default port, so this property is often left unset entirely in local development, since that default already matches. chat.model selects which downloaded model actually answers — any model name you’ve pulled with ollama pull works here.

This is a property path worth being precise about, because it changed shape during Spring AI’s own march toward a stable release: earlier versions nested model selection one level deeper, under an options segment (spring.ai.ollama.chat.options.model). Spring AI’s own 2.0 upgrade notes document a general “configuration properties flattening” that dropped this extra segment across its provider properties broadly (embeddings, image, audio, and more) — Ollama’s chat property follows that same flattened shape, so spring.ai.ollama.chat.model (no .options) is the current, correct property path this pillar teaches. If you see the older, .options-included form in a tutorial, treat it as a signal that tutorial predates this change, not as a second valid option.

Step 3: The Autoconfigured ChatClient.Builder

With the starter on the classpath and those two properties set, Spring Boot autoconfigures a ChatClient.Builder bean — you never construct it yourself. That builder bean is prototype-scoped, meaning each class that asks for one gets its own fresh builder instance, rather than every class sharing a single one. The pattern is exactly the constructor injection from the very first topic in this pillar: declare that you need a ChatClient.Builder, call .build() on it once inside your constructor, and keep the resulting ChatClient as a field.

Putting It Together: A Real Endpoint

Here’s all three pieces in one place — a @RestController (exactly the annotation from topic 2) with a constructor-injected ChatClient(exactly the dependency injection mechanics from topic 1), exposing a simple endpoint that takes a user’s message and returns the model’s response:

@RestController
@RequestMapping("/chat")
public class ChatController {

    private final ChatClient chatClient;

    // Constructor injection again — this controller only declares
    // that it needs a ChatClient.Builder, and calls .build() on it
    // once. Spring supplies the builder; the class never constructs
    // one itself.
    public ChatController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    @GetMapping
    public String chat(@RequestParam String message) {
        return chatClient.prompt()
            .user(message)
            .call()
            .content();
    }
}

Reading the fluent chain left to right: .prompt() starts building a request, .user(message) sets the message the caller sent in, .call() tells ChatClientto run this synchronously (wait for the full response before returning — the Advanced level covers the alternative, streaming, response as it’s generated), and .content()unwraps just the model’s reply text as a plain String. A request to /chat?message=Tell me a joke runs this exact handler and returns whatever your local model generates in response — genuinely generated by the model running on your own machine, not a canned answer.

Try It: Build a ChatClient Call

Pick a system prompt and a user message below, and watch the exact ChatClient call that combination produces — plus a simulated reply matching the chosen persona (not a live model call; see the note under the demo):

Fun Fact

Ollama’s own OpenAI-compatible API means Spring AI’s dedicated Ollama starter isn’t technically the only route in — some setups instead point Spring AI’s OpenAI client at Ollama’s local address. This pillar teaches the dedicated Ollama starter and its own spring.ai.ollama.* configuration namespace, since it’s the integration Spring AI documents and maintains specifically for Ollama.

Test Yourself

What is the current, correct Maven artifact ID for the Spring AI Ollama starter?

In Spring AI’s current configuration property scheme, what selects which Ollama model to use for chat?

What kind of bean does Spring Boot autoconfigure once the Ollama starter is on the classpath, that you inject into your own class?

What is the exact fluent chain that sends a user message and returns the model’s reply as a String?

That endpoint handles one message at a time, with no fixed persona and no way to get back anything other than raw text. What about giving the model a standing system instruction, streaming a long response as it’s generated, or getting back an actual Java object instead of a plain string? Continue to the Advanced level →