Skip to content

feat(langchain4j): make modelName optional when ChatModel or StreamingChatModel is provided - #1126

Merged
copybara-service[bot] merged 1 commit into
google:mainfrom
marttp:issue_1124
Oct 16, 2026
Merged

copybara-service[bot] merged 1 commit into
google:mainfrom
marttp:issue_1124

Conversation

@marttp

@marttp marttp commented Apr 9, 2026 •

Copy link
Copy Markdown
Contributor

Please ensure you have read the contribution guide before creating a pull request.

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

2. Or, if no issue exists, describe the change:

N/A

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Command

mvn test -pl contrib/langchain4j -am
[INFO] Results:
[INFO] 
[WARNING] Tests run: 27, Failures: 0, Errors: 0, Skipped: 6
[INFO] 
[INFO] 
[INFO] --- jacoco:0.8.12:report (report) @ google-adk-langchain4j ---
[INFO] Loading execution data file /Users/tpcoder/Documents/Working/OSS Contribution/adk-java/contrib/langchain4j/target/jacoco.exec
[INFO] Analyzed bundle 'Agent Development Kit - LangChain4j' with 6 classes
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for Google Agent Development Kit Maven Parent POM 1.0.1-SNAPSHOT:
[INFO] 
[INFO] Google Agent Development Kit Maven Parent POM ...... SUCCESS [  0.064 s]
[INFO] Agent Development Kit .............................. SUCCESS [ 22.594 s]
[INFO] Agent Development Kit - LangChain4j ................ SUCCESS [  2.265 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  25.493 s
[INFO] Finished at: 2026-04-09T08:02:22+07:00
[INFO] ------------------------------------------------------------------------

Manual End-to-End (E2E) Tests:

I created a sample project to verify session interaction

public class MultiProviderAgent {

  /**
   * Creates a science teacher agent powered by Anthropic Claude.
   */
  public static LlmAgent createClaudeAgent() {
    AnthropicChatModel claudeModel =
        AnthropicChatModel.builder()
            .apiKey(System.getenv("ANTHROPIC_API_KEY"))
            .modelName("claude-sonnet-4-6")
            .build();

    LangChain4j adkModel = LangChain4j.builder()
        .chatModel(claudeModel)
        .build();
    
    return LlmAgent.builder()
        .name("claude-science-agent")
        .description("A science teacher powered by Claude")
        .model(adkModel)
        .instruction(
            """
                You are a science teacher.
                Explain concepts clearly and concisely in 2-3 sentences.
                """)
        .build();
  }

  /**
   * Creates a history teacher agent powered by Google Gemini.
   */
  public static LlmAgent createGeminiAgent() {
    GoogleAiGeminiChatModel geminiModel =
        GoogleAiGeminiChatModel.builder()
            .apiKey(System.getenv("GOOGLE_API_KEY"))
            .modelName("gemini-2.5-flash")
            .build();

    LangChain4j adkModel = LangChain4j.builder()
        .chatModel(geminiModel)
        .build();

    return LlmAgent.builder()
        .name("gemini-history-agent")
        .description("A history teacher powered by Gemini")
        .model(adkModel)
        .instruction(
            """
                You are a history teacher.
                Provide accurate and engaging historical facts in 2-3 sentences.
                """)
        .build();
  }

  private MultiProviderAgent() {
  }
}

And triggered by this

  public static void main(String[] args) {
    LlmAgent claudeAgent = MultiProviderAgent.createClaudeAgent();
    LlmAgent geminiAgent = MultiProviderAgent.createGeminiAgent();

    askAgent(claudeAgent, "What is quantum entanglement?");
    askAgent(geminiAgent, "What caused the fall of the Roman Empire?");
  }

E2E Result

--- claude-science-agent ---
Q: What is quantum entanglement?
A: Quantum entanglement is a phenomenon where two or more particles become linked in such a way that the quantum state of one particle instantly influences the state of the other, no matter how far apart they are. When you measure a property of one entangled particle, you immediately know the corresponding property of its partner. Einstein famously called this "spooky action at a distance," and while it seems mysterious, it has been repeatedly confirmed by experiments and is even being used in emerging technologies like quantum computing and cryptography.

--- gemini-history-agent ---
Q: What caused the fall of the Roman Empire?
A: The fall of the Western Roman Empire in 476 CE was not a single event but a complex process driven by multiple factors. Relentless barbarian invasions, like those by the Goths and Vandals, severely strained Rome's vast borders and military resources. Simultaneously, internal issues such as political instability, economic decline, and social upheaval eroded the empire's foundations from within.

Process finished with exit code 0

Checklist

  • I have read the CONTRIBUTING.md document.
  • My pull request contains a single commit.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

Additional context

The LangChain4j builder now auto-detects modelName from the underlying ChatModel or StreamingChatModel via
defaultRequestParameters().modelName(), making it optional when the model already exposes it. Explicit modelName() still takes precedence for backward compatibility. If model name cannot be resolved from any source, the builder fails fast with a clear IllegalStateException.

@marttp marttp changed the title Make modelName optional if providing ChatModel or StreamingChatModel Langchain4j - Make modelName optional if providing ChatModel or StreamingChatModel Apr 9, 2026
@marttp marttp changed the title Langchain4j - Make modelName optional if providing ChatModel or StreamingChatModel LangChain4j - Make modelName optional if providing ChatModel or StreamingChatModel Apr 9, 2026
@marttp
marttp marked this pull request as ready for review April 9, 2026 01:27
@marttp marttp closed this Jun 8, 2026
@marttp marttp reopened this Jun 8, 2026
@hemasekhar-p hemasekhar-p self-assigned this Jul 16, 2026
@hemasekhar-p

Copy link
Copy Markdown
Contributor

Hi @marttp, hank you for your contribution! We appreciate you taking the time to submit this pull request. I noticed some inconsistencies in the code formatting as per PR contribution policy. could you please address those?

@hemasekhar-p hemasekhar-p added the waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale. label Jul 16, 2026
@marttp

marttp commented Jul 18, 2026 •

Copy link
Copy Markdown
Contributor Author

Hi @hemasekhar-p, thanks for the review. I've fixed the formatting (ran google-java-format via the Maven build) and squashed the branch. Please take another look when you have a chance 🙇‍♂️

@hemasekhar-p

Copy link
Copy Markdown
Contributor

@marttp, thank you for addressing those formatting and updating the PR. Currently this PR is under review by our team, we will keep you posted if any additional information is required. thank you.

@hemasekhar-p hemasekhar-p added needs review and removed waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale. labels Jul 23, 2026
Comment on lines +142 to +148
if (!Objects.isNull(chatModel())
&& !Objects.isNull(chatModel().defaultRequestParameters())) {
modelName(chatModel().defaultRequestParameters().modelName());
} else if (!Objects.isNull(streamingChatModel())
&& !Objects.isNull(streamingChatModel().defaultRequestParameters())) {
modelName(streamingChatModel().defaultRequestParameters().modelName());
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM: the streaming model's name is never tried when the chat model has none (LangChain4j.java#L142-L148)

The else if branch only checks that the chat model's parameters object is non-null, not that it carries a model name. Because ChatModel.defaultRequestParameters() returns DefaultChatRequestParameters.EMPTY by default, this throws even when the streaming model declares a name:

LangChain4j.builder()
    .chatModel(new ChatModel() {}) // default parameters, no model name
    .streamingChatModel(streamingModelWithName)
    .build(); // IllegalStateException

Resolving the name per model fixes this:

public LangChain4j build() {
  if (modelName() == null) {
    String name = chatModel() == null ? null : nameOf(chatModel().defaultRequestParameters());
    if (name == null && streamingChatModel() != null) {
      name = nameOf(streamingChatModel().defaultRequestParameters());
    }
    if (name == null) {
      throw new IllegalStateException(
          "Set modelName(), or use a ChatModel or StreamingChatModel whose"
              + " defaultRequestParameters() declare a model name");
    }
    modelName(name);
  }
  return autoBuild();
}

private static @Nullable String nameOf(@Nullable ChatRequestParameters parameters) {
  return parameters == null ? null : parameters.modelName();
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated 944ac58

Comment on lines +151 to +153
if (modelName() == null) {
throw new IllegalStateException(
"modelName is required. Either set it explicitly via modelName() or provide a ChatModel/StreamingChatModel");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM: the failure path is untested, and its message is misleading (LangChain4j.java#L151-L153)

The three new tests only cover cases where a name is found. Please also add tests for when no source provides a name, and for a chat model without a name paired with a streaming model that has one (the case above). The current exception message asks for a ChatModel/StreamingChatModel, even though providing one without a model name hits the same failure; the message in the snippet above states what is actually missing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated 944ac58

@damianmomotgoogle damianmomotgoogle added waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale. and removed needs review labels Oct 15, 2026
@damianmomotgoogle

Copy link
Copy Markdown
Contributor

Please also update commit message to follow conventional commits - likely "feat:" here

@marttp marttp changed the title LangChain4j - Make modelName optional if providing ChatModel or StreamingChatModel feat(langchain4j): make modelName optional when ChatModel or StreamingChatModel is provided Oct 16, 2026
@marttp

marttp commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

@damianmomotgoogle thank you. I made an update 🙇‍♂️

@damianmomotgoogle damianmomotgoogle added ready to pull and removed waiting on reporter Waiting for reaction by reporter. Failing that, maintainers will eventually closed it as stale. labels Oct 16, 2026
@copybara-service
copybara-service Bot merged commit 139f09b into google:main Oct 16, 2026
14 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove mandatory model name in LangChain4j integration

3 participants