Build LLM-powered applications in Java with support for chatbots, agents, RAG, tools, and much more
Request and response types for chat model interactions. These classes provide structured interfaces for configuring LLM requests and handling responses, including streaming, token usage tracking, and completion reasons.
Request object containing all inputs for a chat model call, including messages, model parameters, and tool specifications.
package dev.langchain4j.model.chat.request;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.agent.tool.ToolSpecification;
import java.util.List;
/**
* Request object containing all inputs for a chat model call
*/
public class ChatRequest {
/**
* Get messages in the conversation
* @return List of chat messages
*/
public List<ChatMessage> messages();
/**
* Get request parameters
* @return Chat request parameters
*/
public ChatRequestParameters parameters();
/**
* Get model name
* @return Model name
*/
public String modelName();
/**
* Get temperature setting
* @return Temperature value
*/
public Double temperature();
/**
* Get topP setting
* @return TopP value
*/
public Double topP();
/**
* Get topK setting
* @return TopK value
*/
public Integer topK();
/**
* Get frequency penalty
* @return Frequency penalty value
*/
public Double frequencyPenalty();
/**
* Get presence penalty
* @return Presence penalty value
*/
public Double presencePenalty();
/**
* Get max output tokens
* @return Max output tokens
*/
public Integer maxOutputTokens();
/**
* Get stop sequences
* @return List of stop sequences
*/
public List<String> stopSequences();
/**
* Get tool specifications
* @return List of tool specifications
*/
public List<ToolSpecification> toolSpecifications();
/**
* Get tool choice setting
* @return Tool choice
*/
public ToolChoice toolChoice();
/**
* Get response format
* @return Response format
*/
public ResponseFormat responseFormat();
/**
* Create builder for modification
* @return Builder with current values
*/
public Builder toBuilder();
/**
* Create new builder
* @return Builder instance
*/
public static Builder builder();
}messages(). At least one message is required for chat completion.topP and topK simultaneously unless your provider explicitly supports it. Many providers ignore one when both are present.toolSpecifications, ensure toolChoice is set appropriately (typically AUTO or REQUIRED), otherwise tools may be ignored.modelName in the request overrides the model name configured at the chat model instance level.parameters() object and individual parameter methods are set, individual parameters take precedence.build() (e.g., null or empty messages list).ChatRequestParameters - Interface for configuring default parametersChatMessage - Message types for conversation history (see messages.md)ToolSpecification - Tool definition for function calling (see tools.md)ResponseFormat - Control response structure (TEXT/JSON)ChatModel.chat() - Execute the request/**
* Builder for ChatRequest
*/
public static class Builder {
/**
* Set messages
* @param messages List of chat messages
* @return Builder instance
*/
public Builder messages(List<ChatMessage> messages);
/**
* Set messages (varargs)
* @param messages Chat messages
* @return Builder instance
*/
public Builder messages(ChatMessage... messages);
/**
* Set parameters
* @param parameters Chat request parameters
* @return Builder instance
*/
public Builder parameters(ChatRequestParameters parameters);
/**
* Set model name
* @param modelName Model name
* @return Builder instance
*/
public Builder modelName(String modelName);
/**
* Set temperature (0.0 to 1.0+)
* @param temperature Temperature value
* @return Builder instance
*/
public Builder temperature(Double temperature);
/**
* Set topP (nucleus sampling)
* @param topP TopP value
* @return Builder instance
*/
public Builder topP(Double topP);
/**
* Set topK
* @param topK TopK value
* @return Builder instance
*/
public Builder topK(Integer topK);
/**
* Set frequency penalty
* @param frequencyPenalty Frequency penalty value
* @return Builder instance
*/
public Builder frequencyPenalty(Double frequencyPenalty);
/**
* Set presence penalty
* @param presencePenalty Presence penalty value
* @return Builder instance
*/
public Builder presencePenalty(Double presencePenalty);
/**
* Set max output tokens
* @param maxOutputTokens Max output tokens
* @return Builder instance
*/
public Builder maxOutputTokens(Integer maxOutputTokens);
/**
* Set stop sequences
* @param stopSequences List of stop sequences
* @return Builder instance
*/
public Builder stopSequences(List<String> stopSequences);
/**
* Set tool specifications
* @param toolSpecifications List of tool specifications
* @return Builder instance
*/
public Builder toolSpecifications(List<ToolSpecification> toolSpecifications);
/**
* Set tool specifications (varargs)
* @param toolSpecifications Tool specifications
* @return Builder instance
*/
public Builder toolSpecifications(ToolSpecification... toolSpecifications);
/**
* Set tool choice
* @param toolChoice Tool choice
* @return Builder instance
*/
public Builder toolChoice(ToolChoice toolChoice);
/**
* Set response format
* @param responseFormat Response format
* @return Builder instance
*/
public Builder responseFormat(ResponseFormat responseFormat);
/**
* Build the request
* @return ChatRequest instance
*/
public ChatRequest build();
}build(), the builder state is retained. Subsequent modifications affect the next built instance. Use toBuilder() for clean copies.null to collection methods (messages, toolSpecifications, stopSequences) may result in NullPointerException. Use empty lists instead.build() at the end is a common mistake.messages(ChatMessage...) and toolSpecifications(ToolSpecification...) methods replace the entire collection, not append to it.temperature(0.5).temperature(0.9) results in temperature = 0.9.chatRequest.toBuilder() to create a builder pre-populated with current values for modifications.build(). For large message histories, this adds overhead.build(), not during setter calls. Invalid configurations fail late.build() when required fields are missing or invalid.ChatRequest.toBuilder() - Create builder from existing requestChatRequest.builder() - Create new builderChatRequestParameters - Alternative parameter configuration approachResponse object containing outputs from a chat model call, including the AI message, token usage, and metadata.
package dev.langchain4j.model.chat.response;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.output.TokenUsage;
import dev.langchain4j.model.output.FinishReason;
/**
* Response object containing all outputs from a chat model call
*/
public class ChatResponse {
/**
* Get AI-generated message
* @return AI message
*/
public AiMessage aiMessage();
/**
* Get response metadata
* @return Chat response metadata
*/
public ChatResponseMetadata metadata();
/**
* Get response ID
* @return Response ID
*/
public String id();
/**
* Get model name used
* @return Model name
*/
public String modelName();
/**
* Get token usage
* @return Token usage
*/
public TokenUsage tokenUsage();
/**
* Get finish reason
* @return Finish reason
*/
public FinishReason finishReason();
/**
* Create builder for modification
* @return Builder with current values
*/
public Builder toBuilder();
/**
* Create new builder
* @return Builder instance
*/
public static Builder builder();
}AiMessage and TokenUsage objects are also immutable and thread-safe.tokenUsage().modelName() may differ from requested model name if provider performs automatic model selection or fallback.metadata() object availability and contents vary by provider. Some providers return null or minimal metadata.metadata() may trigger lazy parsing of additional response fields. Cache the result if accessing multiple times.tokenUsage() for cost monitoring and budget enforcement.tokenUsage(), finishReason(), metadata()) before accessing their properties.AiMessage - The generated message content (see messages.md)TokenUsage - Token consumption trackingFinishReason - Completion status enumChatResponseMetadata - Additional provider-specific metadataChatModel.chat() - Returns ChatResponseStreamingChatResponseHandler.onCompleteResponse() - Receives ChatResponse in streaming/**
* Builder for ChatResponse
*/
public static class Builder {
/**
* Set AI message
* @param aiMessage AI message
* @return Builder instance
*/
public Builder aiMessage(AiMessage aiMessage);
/**
* Set metadata
* @param metadata Chat response metadata
* @return Builder instance
*/
public Builder metadata(ChatResponseMetadata metadata);
/**
* Set response ID
* @param id Response ID
* @return Builder instance
*/
public Builder id(String id);
/**
* Set model name
* @param modelName Model name
* @return Builder instance
*/
public Builder modelName(String modelName);
/**
* Set token usage
* @param tokenUsage Token usage
* @return Builder instance
*/
public Builder tokenUsage(TokenUsage tokenUsage);
/**
* Set finish reason
* @param finishReason Finish reason
* @return Builder instance
*/
public Builder finishReason(FinishReason finishReason);
/**
* Build the response
* @return ChatResponse instance
*/
public ChatResponse build();
}aiMessage() field is typically required. Building without it may succeed but result in unusable response objects.build(). Reusing the same builder for multiple responses requires caution.build() (rare, primarily for internal consistency checks).ChatResponse.toBuilder() - Create builder from existing responseChatResponse.builder() - Create new builderInterface for configuring chat model parameters. Can be set at model level as defaults or per-request.
package dev.langchain4j.model.chat.request;
import dev.langchain4j.agent.tool.ToolSpecification;
import java.util.List;
/**
* Interface for chat request parameters
* Configure temperature, max tokens, tools, response format, etc.
*/
public interface ChatRequestParameters {
/**
* Get model name
* @return Model name
*/
String modelName();
/**
* Get temperature
* @return Temperature value
*/
Double temperature();
/**
* Get topP
* @return TopP value
*/
Double topP();
/**
* Get topK
* @return TopK value
*/
Integer topK();
/**
* Get frequency penalty
* @return Frequency penalty value
*/
Double frequencyPenalty();
/**
* Get presence penalty
* @return Presence penalty value
*/
Double presencePenalty();
/**
* Get max output tokens
* @return Max output tokens
*/
Integer maxOutputTokens();
/**
* Get stop sequences
* @return List of stop sequences
*/
List<String> stopSequences();
/**
* Get tool specifications
* @return List of tool specifications
*/
List<ToolSpecification> toolSpecifications();
/**
* Get tool choice
* @return Tool choice
*/
ToolChoice toolChoice();
/**
* Get response format
* @return Response format
*/
ResponseFormat responseFormat();
/**
* Override these parameters with non-null values from other parameters
* @param parameters Parameters to override with
* @return New parameters with overrides applied
*/
ChatRequestParameters overrideWith(ChatRequestParameters parameters);
/**
* Use these parameters as defaults, filling nulls with values from other parameters
* @param parameters Default parameters
* @return New parameters with defaults applied
*/
ChatRequestParameters defaultedBy(ChatRequestParameters parameters);
/**
* Create builder
* @return Builder instance
*/
static DefaultChatRequestParameters.Builder<?> builder();
}overrideWith() and defaultedBy() methods create new instances rather than modifying in place, ensuring thread safety.overrideWith(other) means "other's non-null values replace mine". defaultedBy(other) means "other's values fill my nulls".params1.overrideWith(params2).defaultedBy(params3) applies operations left-to-right. Order matters.overrideWith() and defaultedBy() create new parameter objects. For hot paths, cache merged results rather than recomputing.ChatRequest.parameters() - Set parameters on a requestDefaultChatRequestParameters - Default implementationChatModel - Accepts parameters as default configurationHandler interface for receiving streaming responses from chat models token-by-token.
package dev.langchain4j.model.chat.response;
/**
* Handler for streaming chat responses
* Receives tokens and tool calls as they arrive
*/
public interface StreamingChatResponseHandler {
/**
* Called for each partial text response token
* @param partialResponse Partial response text
*/
void onPartialResponse(String partialResponse);
/**
* Called for each partial response with context
* @param partialResponse Partial response object
* @param context Response context with streaming handle
*/
void onPartialResponse(PartialResponse partialResponse, PartialResponseContext context);
/**
* Called for each partial thinking/reasoning token
* @param partialThinking Partial thinking text
*/
void onPartialThinking(PartialThinking partialThinking);
/**
* Called for each partial thinking with context
* @param partialThinking Partial thinking object
* @param context Thinking context with streaming handle
*/
void onPartialThinking(PartialThinking partialThinking, PartialThinkingContext context);
/**
* Called for each partial tool call chunk
* @param partialToolCall Partial tool call
*/
void onPartialToolCall(PartialToolCall partialToolCall);
/**
* Called for each partial tool call with context
* @param partialToolCall Partial tool call object
* @param context Tool call context with streaming handle
*/
void onPartialToolCall(PartialToolCall partialToolCall, PartialToolCallContext context);
/**
* Called when a tool call is complete
* @param completeToolCall Complete tool call
*/
void onCompleteToolCall(CompleteToolCall completeToolCall);
/**
* Called when streaming is complete
* @param completeResponse Complete response with full message
*/
void onCompleteResponse(ChatResponse completeResponse);
/**
* Called if an error occurs
* @param error Error that occurred
*/
void onError(Throwable error);
}onError(), but this terminates the stream. Handle errors internally when possible.onPartialResponse() receives incremental chunks, not complete sentences. You must accumulate them yourself to build complete text.onCompleteResponse() may never be called. Always handle this scenario gracefully.PartialResponseContext or similar context objects after the streaming completes. They become invalid.onPartialToolCall(). A complete tool call is signaled by onCompleteToolCall().onCompleteToolCall() is invoked once per tool.onPartialResponse(), use StringBuilder to accumulate text rather than string concatenation to avoid O(n²) complexity.PartialResponseContext, etc.) may involve lookups. Cache values if accessing multiple times within a callback.onError() is called, no further callbacks occur. The stream is closed.onError() receives Throwable (not Exception), so it can handle both checked and unchecked exceptions, including errors like OutOfMemoryError.onError() with IOException or provider-specific exceptions.onError() is called, ensure you clean up any accumulated partial state (partial text, incomplete tool calls).StreamingChatModel.chat() - Accepts handler for streaming requestsPartialResponse, PartialThinking, PartialToolCall - Partial data typesPartialResponseContext, PartialThinkingContext, PartialToolCallContext - Context objects for streaming controlChatResponse - Final complete response passed to onCompleteResponse()Generic wrapper for model responses with token usage and finish reason.
package dev.langchain4j.model.output;
import java.util.Map;
/**
* Generic response wrapper containing content, token usage, and finish reason
*/
public class Response<T> {
/**
* Create response with content only
* @param content Response content
*/
public Response(T content);
/**
* Create response with content and token usage
* @param content Response content
* @param tokenUsage Token usage
* @param finishReason Finish reason
*/
public Response(T content, TokenUsage tokenUsage, FinishReason finishReason);
/**
* Create response with all fields
* @param content Response content
* @param tokenUsage Token usage
* @param finishReason Finish reason
* @param metadata Additional metadata
*/
public Response(
T content,
TokenUsage tokenUsage,
FinishReason finishReason,
Map<String, Object> metadata
);
/**
* Get response content
* @return Content
*/
public T content();
/**
* Get token usage
* @return Token usage or null
*/
public TokenUsage tokenUsage();
/**
* Get finish reason
* @return Finish reason or null
*/
public FinishReason finishReason();
/**
* Get metadata
* @return Metadata map
*/
public Map<String, Object> metadata();
/**
* Create response from content (factory method)
* @param content Response content
* @return Response instance
*/
public static <T> Response<T> from(T content);
/**
* Create response from content and token usage (factory method)
* @param content Response content
* @param tokenUsage Token usage
* @return Response instance
*/
public static <T> Response<T> from(T content, TokenUsage tokenUsage);
/**
* Create response from content, usage, and finish reason (factory method)
* @param content Response content
* @param tokenUsage Token usage
* @param finishReason Finish reason
* @return Response instance
*/
public static <T> Response<T> from(T content, TokenUsage tokenUsage, FinishReason finishReason);
/**
* Create response with all fields (factory method)
* @param content Response content
* @param tokenUsage Token usage
* @param finishReason Finish reason
* @param metadata Additional metadata
* @return Response instance
*/
public static <T> Response<T> from(
T content,
TokenUsage tokenUsage,
FinishReason finishReason,
Map<String, Object> metadata
);
}T is also immutable.T is mutable, the Response wrapper itself doesn't provide thread safety guarantees for the content.T is a mutable type and you pass it to Response, external modifications will affect the Response content. Use defensive copying if needed.metadata allows external modification. Use Map.copyOf() for immutability.from()) over constructors for better readability and future extensibility.instanceof checks on the generic type.tokenUsage() to track costs across different model types (chat, embeddings, etc.). Aggregate token usage for billing.tokenUsage(), finishReason(), or metadata() will throw NPE. Always null-check optional fields.ChatResponse - Specialized response type for chat modelsTokenUsage - Token consumption detailsFinishReason - Completion statusTracks token consumption for model calls.
package dev.langchain4j.model.output;
/**
* Tracks token usage for model calls
*/
public class TokenUsage {
/**
* Create empty token usage
*/
public TokenUsage();
/**
* Create with input token count
* @param inputTokenCount Input tokens used
*/
public TokenUsage(Integer inputTokenCount);
/**
* Create with input and output counts
* @param inputTokenCount Input tokens used
* @param outputTokenCount Output tokens generated
*/
public TokenUsage(Integer inputTokenCount, Integer outputTokenCount);
/**
* Create with all counts
* @param inputTokenCount Input tokens used
* @param outputTokenCount Output tokens generated
* @param totalTokenCount Total tokens
*/
public TokenUsage(Integer inputTokenCount, Integer outputTokenCount, Integer totalTokenCount);
/**
* Get input token count
* @return Input tokens used
*/
public Integer inputTokenCount();
/**
* Get output token count
* @return Output tokens generated
*/
public Integer outputTokenCount();
/**
* Get total token count
* @return Total tokens
*/
public Integer totalTokenCount();
/**
* Add two token usages together
* @param first First token usage
* @param second Second token usage
* @return Combined token usage
*/
public static TokenUsage sum(TokenUsage first, TokenUsage second);
/**
* Add another token usage to this one
* @param that Token usage to add
* @return Combined token usage
*/
public TokenUsage add(TokenUsage that);
}add() and sum() methods return new instances rather than modifying existing ones.totalTokenCount independently rather than as inputTokenCount + outputTokenCount. Do not assume equality.sum(null, usage) or usage.add(null) handles nulls gracefully (treats null as zero), but be aware of this behavior.add() or sum() call allocates a new TokenUsage instance. For high-frequency aggregation, this creates GC pressure. Consider batching additions.add() and sum() methods perform null checks internally. Minimize calls in hot loops.totalTokenCount() for high-level budget enforcement. Track cumulative usage across conversation turns.add() and sum() treats null as zero, avoiding exceptions. However, manual null addition without these methods may throw.ChatResponse.tokenUsage() - Get token usage from chat responseResponse.tokenUsage() - Get token usage from generic responseChatResponseMetadata - May contain additional token breakdown detailsEnum indicating why model generation stopped.
package dev.langchain4j.model.output;
/**
* Indicates why model generation stopped
*/
public enum FinishReason {
/**
* Model decided the response was complete
*/
STOP,
/**
* Maximum token length was reached
*/
LENGTH,
/**
* Model is requesting tool execution
*/
TOOL_EXECUTION,
/**
* Content was filtered
*/
CONTENT_FILTER,
/**
* Other reason
*/
OTHER
}ChatResponse.finishReason() may return null for some providers. Always null-check before switching on the enum.maxOutputTokens or prompting for continuation.ChatResponseMetadata for provider details.== rather than .equals() for efficiency.maxOutputTokens budget. Adjust limits to prevent wasteful token usage.ChatResponse.finishReason() - Get finish reason from responseResponse.finishReason() - Get finish reason from generic responseEnum controlling whether and when the model should use tools.
package dev.langchain4j.model.chat.request;
/**
* Controls tool usage behavior
*/
public enum ToolChoice {
/**
* Model can choose whether to use tools
*/
AUTO,
/**
* Model must use one or more tools
*/
REQUIRED,
/**
* Model cannot use tools
*/
NONE
}ToolChoice.REQUIRED but providing empty or null toolSpecifications causes request failures. Always provide at least one tool.ToolChoice.NONE while providing toolSpecifications wastes input tokens. The model ignores the tools but they still consume tokens.ToolChoice.NONE, if you include toolSpecifications in the request, they consume input tokens. Remove them entirely when not needed.ChatRequest.toolChoice() - Set tool choice on requestChatRequestParameters.toolChoice() - Set default tool choiceToolSpecification - Tool definitions (see tools.md)Specifies the format of model responses (text or JSON schema).
package dev.langchain4j.model.chat.request;
import dev.langchain4j.model.chat.request.json.JsonSchema;
/**
* Specifies response format
*/
public class ResponseFormat {
/**
* Text response format (default)
*/
public static final ResponseFormat TEXT;
/**
* JSON response format
*/
public static final ResponseFormat JSON;
/**
* Get response format type
* @return Response format type
*/
public ResponseFormatType type();
/**
* Get JSON schema (if type is JSON)
* @return JSON schema or null
*/
public JsonSchema jsonSchema();
/**
* Create builder
* @return Builder instance
*/
public static Builder builder();
}TEXT and JSON are safe to use concurrently.ResponseFormat.JSON without a schema provides unstructured JSON. For structured output, provide a JsonSchema via the builder.type = TEXT with a jsonSchema. This is inconsistent and may cause errors.{} if it cannot generate valid content matching the schema. Handle empty responses.ChatRequest.responseFormat() - Set response format on requestChatRequestParameters.responseFormat() - Set default response formatJsonSchema - Define expected JSON structureResponseFormatType - Enum for format typepackage dev.langchain4j.model.chat.request;
/**
* Type of response format
*/
public enum ResponseFormatType {
TEXT,
JSON
}ResponseFormat.TEXT or ResponseFormat.JSON constants rather than constructing ResponseFormat with this enum directly.ResponseFormat.type() - Get the format typeResponseFormat - Response format wrapperUnderstanding how to tune parameters for optimal results is crucial for production deployments.
finishReason for LENGTH to detect truncation."###", "---" to signal sections"\nUser:", "\nAssistant:" to prevent run-on conversations"}]" or "}" to stop at structure boundariesAUTO for most scenariosREQUIRED when you need guaranteed function calling (e.g., first turn of agent loop)NONE when tools are not relevant for a specific turn (saves tokens)StringBuilder with reasonable initial capacity (recommended: 1024 characters).onError() to log failures, clean up state, and optionally retry.ConcurrentHashMap or synchronized collections if updating shared state from handler callbacks.onCompleteResponse() and onError() to prevent memory leaks.onError() handling.// Test request builder validation
@Test
void testChatRequestRequiresMessages() {
assertThrows(IllegalArgumentException.class, () -> {
ChatRequest.builder().build();
});
}
// Test parameter precedence
@Test
void testRequestParameterOverride() {
ChatRequestParameters defaults = ChatRequestParameters.builder()
.temperature(0.5)
.build();
ChatRequest request = ChatRequest.builder()
.messages(userMessage("test"))
.parameters(defaults)
.temperature(0.9) // Should override parameters
.build();
assertEquals(0.9, request.temperature());
}// Test response parsing
@Test
void testChatResponseWithTokenUsage() {
ChatResponse response = ChatResponse.builder()
.aiMessage(new AiMessage("test response"))
.tokenUsage(new TokenUsage(10, 20, 30))
.finishReason(FinishReason.STOP)
.build();
assertEquals(30, response.tokenUsage().totalTokenCount());
assertEquals(FinishReason.STOP, response.finishReason());
}// Mock handler for testing streaming logic
public class TestStreamingHandler implements StreamingChatResponseHandler {
private final StringBuilder accumulated = new StringBuilder();
private ChatResponse completeResponse;
private Throwable error;
@Override
public void onPartialResponse(String partialResponse) {
accumulated.append(partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse response) {
this.completeResponse = response;
}
@Override
public void onError(Throwable error) {
this.error = error;
}
public String getAccumulatedText() {
return accumulated.toString();
}
public ChatResponse getCompleteResponse() {
return completeResponse;
}
public Throwable getError() {
return error;
}
}
@Test
void testStreamingCompletion() {
TestStreamingHandler handler = new TestStreamingHandler();
// Simulate streaming
handler.onPartialResponse("Hello ");
handler.onPartialResponse("world");
handler.onCompleteResponse(mockResponse());
assertEquals("Hello world", handler.getAccumulatedText());
assertNotNull(handler.getCompleteResponse());
}// Mock chat model for integration tests
public class MockChatModel implements ChatModel {
private final String mockedResponse;
public MockChatModel(String mockedResponse) {
this.mockedResponse = mockedResponse;
}
@Override
public ChatResponse chat(ChatRequest request) {
return ChatResponse.builder()
.aiMessage(new AiMessage(mockedResponse))
.tokenUsage(new TokenUsage(10, mockedResponse.length(), 10 + mockedResponse.length()))
.finishReason(FinishReason.STOP)
.build();
}
}
@Test
void testChatWorkflow() {
ChatModel model = new MockChatModel("Mocked AI response");
ChatRequest request = ChatRequest.builder()
.messages(userMessage("test"))
.build();
ChatResponse response = model.chat(request);
assertEquals("Mocked AI response", response.aiMessage().text());
}@Test
void testTokenUsageSum() {
TokenUsage usage1 = new TokenUsage(100, 50);
TokenUsage usage2 = new TokenUsage(200, 75);
TokenUsage total = TokenUsage.sum(usage1, usage2);
assertEquals(300, total.inputTokenCount());
assertEquals(125, total.outputTokenCount());
}
@Test
void testTokenUsageSumWithNulls() {
TokenUsage usage1 = new TokenUsage(100, 50);
TokenUsage total = TokenUsage.sum(usage1, null);
assertEquals(100, total.inputTokenCount());
assertEquals(50, total.outputTokenCount());
}@Test
void testFinishReasonHandling() {
ChatResponse response = mockResponseWithFinishReason(FinishReason.LENGTH);
if (response.finishReason() == FinishReason.LENGTH) {
// Handle truncation
assertTrue(response.aiMessage().text().length() > 0);
}
}
@Test
void testToolExecutionFinishReason() {
ChatResponse response = mockResponseWithFinishReason(FinishReason.TOOL_EXECUTION);
assertEquals(FinishReason.TOOL_EXECUTION, response.finishReason());
assertFalse(response.aiMessage().toolCalls().isEmpty());
}messages.md for ChatMessage, UserMessage, SystemMessage, AiMessagetools.md for ToolSpecification, ToolExecutionRequestchat-models.md for ChatModel, StreamingChatModelmessages.md for AiMessage, ToolExecutionResultMessagechat-models.md for provider-specific metadata detailsPartialResponse, PartialThinking, PartialToolCallPartialResponseContext, PartialThinkingContext, PartialToolCallContextembeddings.md for embedding model token usagetools.md for ToolSpecification and tool definition best practicesagents.md for agent execution patterns with tool callsstructured-output.md for JsonSchema definition and usagestructured-output.md for advanced structured output patternsInstall with Tessl CLI
npx tessl i tessl/maven-dev-langchain4j--langchain4j@1.11.0