Skip to content

Commit fbf7769

Browse files
committed
Add interleaved thinking and cache prompting
1 parent 69560bb commit fbf7769

File tree

3 files changed

+68
-2
lines changed

3 files changed

+68
-2
lines changed

model-providers/anthropic/runtime/src/main/java/io/quarkiverse/langchain4j/anthropic/QuarkusAnthropicClient.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,13 @@ public class QuarkusAnthropicClient extends AnthropicClient {
5959
public static final String BETA = "tools-2024-04-04";
6060
private final String apiKey;
6161
private final String anthropicVersion;
62+
private final String configuredBeta;
6263
private final AnthropicRestApi restApi;
6364

6465
public QuarkusAnthropicClient(Builder builder) {
6566
this.apiKey = builder.apiKey;
6667
this.anthropicVersion = builder.version;
68+
this.configuredBeta = builder.beta;
6769

6870
try {
6971
var restApiBuilder = QuarkusRestClientBuilder.newBuilder().baseUri(new URI(builder.baseUrl))
@@ -104,12 +106,35 @@ private AnthropicRestApi.ApiMetadata createMetadata(AnthropicCreateMessageReques
104106
var builder = AnthropicRestApi.ApiMetadata.builder()
105107
.apiKey(apiKey)
106108
.anthropicVersion(anthropicVersion);
107-
if (hasTools(request)) {
108-
builder.beta(BETA);
109+
110+
String betaHeader = buildBetaHeaderForRequest(request);
111+
if (betaHeader != null) {
112+
builder.beta(betaHeader);
109113
}
114+
110115
return builder.build();
111116
}
112117

118+
private String buildBetaHeaderForRequest(AnthropicCreateMessageRequest request) {
119+
boolean toolsPresent = hasTools(request);
120+
121+
// If beta configured, use it (may need to combine with tools beta)
122+
if (configuredBeta != null) {
123+
// If tools present and tools beta not already in configured header
124+
if (toolsPresent && !configuredBeta.contains("tools-")) {
125+
return BETA + "," + configuredBeta;
126+
}
127+
return configuredBeta;
128+
}
129+
130+
// Backward compatibility: default tools beta when tools present
131+
if (toolsPresent) {
132+
return BETA;
133+
}
134+
135+
return null;
136+
}
137+
113138
private boolean hasTools(AnthropicCreateMessageRequest request) {
114139
if (!isNullOrEmpty(request.getTools())) {
115140
return true;

model-providers/anthropic/runtime/src/main/java/io/quarkiverse/langchain4j/anthropic/runtime/AnthropicRecorder.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ public Function<SyntheticCreationalContext<ChatModel>, ChatModel> chatModel(Stri
9898
builder.sendThinking(thinkingConfig.sendThinking().get());
9999
}
100100

101+
// Pass caching configuration to builder
102+
builder.cacheSystemMessages(chatModelConfig.cacheSystemMessages());
103+
builder.cacheTools(chatModelConfig.cacheTools());
104+
105+
// Add beta header for interleaved thinking if enabled
106+
if (thinkingConfig.interleaved().orElse(false)) {
107+
builder.beta("interleaved-thinking-2025-05-14");
108+
}
109+
101110
return new Function<>() {
102111
@Override
103112
public ChatModel apply(SyntheticCreationalContext<ChatModel> context) {
@@ -172,6 +181,15 @@ public Function<SyntheticCreationalContext<StreamingChatModel>, StreamingChatMod
172181
builder.sendThinking(thinkingConfig.sendThinking().get());
173182
}
174183

184+
// Pass caching configuration to builder
185+
builder.cacheSystemMessages(chatModelConfig.cacheSystemMessages());
186+
builder.cacheTools(chatModelConfig.cacheTools());
187+
188+
// Add beta header for interleaved thinking if enabled
189+
if (thinkingConfig.interleaved().orElse(false)) {
190+
builder.beta("interleaved-thinking-2025-05-14");
191+
}
192+
175193
return new Function<>() {
176194
@Override
177195
public StreamingChatModel apply(SyntheticCreationalContext<StreamingChatModel> context) {

model-providers/anthropic/runtime/src/main/java/io/quarkiverse/langchain4j/anthropic/runtime/config/ChatModelConfig.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,22 @@ public interface ChatModelConfig {
7777
@ConfigDocDefault("false")
7878
Optional<Boolean> logResponses();
7979

80+
/**
81+
* Cache system messages to reduce costs for repeated prompts.
82+
* Requires minimum 1024 tokens (Claude Opus/Sonnet) or 2048-4096 tokens (Haiku).
83+
* Supported models: Claude Opus 4.1, Sonnet 4.5, Haiku 4.5, and later models.
84+
*/
85+
@WithDefault("false")
86+
Boolean cacheSystemMessages();
87+
88+
/**
89+
* Cache tool definitions to reduce costs.
90+
* Requires minimum 1024 tokens (Claude Opus/Sonnet) or 2048-4096 tokens (Haiku).
91+
* Supported models: Claude Opus 4.1, Sonnet 4.5, Haiku 4.5, and later models.
92+
*/
93+
@WithDefault("false")
94+
Boolean cacheTools();
95+
8096
/**
8197
* Thinking related configuration
8298
*/
@@ -106,5 +122,12 @@ interface ThinkingConfig {
106122
*/
107123
@ConfigDocDefault("true")
108124
Optional<Boolean> sendThinking();
125+
126+
/**
127+
* Enable interleaved thinking for Claude 4 models, allowing reasoning between tool calls.
128+
* Requires Claude 4 model (e.g., claude-opus-4-20250514) and thinking.type: enabled.
129+
*/
130+
@WithDefault("false")
131+
Optional<Boolean> interleaved();
109132
}
110133
}

0 commit comments

Comments
 (0)