Skip to content

Conversation

@nandoolle
Copy link

@nandoolle nandoolle commented Dec 27, 2025

Summary

This PR replaces direct LangChain4j Java imports with the LangChain4Clj Clojure wrapper library, significantly simplifying the codebase while maintaining full backward compatibility.

Motivation

  • Reduce Java interop boilerplate: Eliminates ~2100 lines of Java interop code
  • Cleaner Clojure-first API: Uses idiomatic Clojure patterns instead of direct Java method calls
  • Easier maintenance: Single dependency instead of 4 separate LangChain4j artifacts
  • Better abstraction: langchain4clj handles schema conversion, message serialization, and listener creation

Dependencies:

  • dev.langchain4j/langchain4j {:mvn/version "1.8.0"}
  • dev.langchain4j/langchain4j-open-ai {:mvn/version "1.8.0"}
  • dev.langchain4j/langchain4j-anthropic {:mvn/version "1.8.0"}
  • dev.langchain4j/langchain4j-google-ai-gemini {:mvn/version "1.8.0"}
  • io.github.nandoolle/langchain4clj {:mvn/version "1.6.1"}

Breaking Changes

token-tracking-listener atom structure changed:

  • Old: {:total-input-tokens N :total-output-tokens N :total-tokens N :request-count N}
  • New: {:input-tokens N :output-tokens N :total-tokens N :request-count N :last-request {...} :by-model {...}}

This is documented in the function's docstring. Consumers should update to use :input-tokens/:output-tokens keys.

Backward Compatibility

  • All existing public APIs maintained
  • :ctx key preserved in listeners for prompt_cli.clj compatibility
  • LangChain4j classes still available via transitive dependency (tests verified)

Summary by CodeRabbit

  • Refactor

    • Modernized agent system architecture, replacing service-based approach with function-based assistant model.
    • Simplified model creation with preset-based configuration system.
    • Enhanced memory management with improved tracking and clearing mechanisms.
  • New Features

    • Added streamlined memory utilities for state management.
    • Introduced simplified tool conversion and adaptation layer.
  • Tests

    • Updated test suite to validate new agent and model creation patterns.

✏️ Tip: You can customize this high-level summary in your review settings.

Migrate from LangChain4j to langchain4clj

  - Replace ChatMessageSerializer/Deserializer with
langchain4clj.messages
  - Replace ChatModelListener imports with langchain4clj.listeners
  - Remove unused chat-request->edn and edn->chat-request functions
Migrate from LangChain4j to LangChain4Clj
Migrate from LangChain4j to langchain4clj

  - Replace ChatMessageSerializer/Deserializer with
langchain4clj.messages
  - Replace ChatModelListener imports with langchain4clj.listeners
  - Remove unused chat-request->edn and edn->chat-request functions
@coderabbitai
Copy link

coderabbitai bot commented Dec 27, 2025

📝 Walkthrough

Walkthrough

This pull request migrates the LangChain integration from direct LangChain4j dependencies to a new langchain4clj wrapper library. The refactoring replaces AiService-based agent creation with assistant functions, simplifies memory management through new helper functions, consolidates model creation logic via presets, removes the schema conversion module, and updates all dependent code and tests accordingly.

Changes

Cohort / File(s) Summary
Dependency Migration
deps.edn
Replaced four separate LangChain4j dependencies (langchain4j, langchain4j-open-ai, langchain4j-anthropic, langchain4j-google-ai-gemini) with single io.github.nandoolle/langchain4clj dependency and added clarifying comments.
Core Agent Refactoring
src/clojure_mcp/agent/general_agent.clj
Added MEMORY-RESET-BUFFER constant; refactored memory initialization to use chain/memory-add! instead of manual UserMessage construction; replaced AiService-based agent creation with chain/create-assistant-fn; updated public return map from :service to :assistant-fn with retained :memory, :model, :tools, :system-message keys; switched invocation from service .chat to direct :assistant-fn invocation.
LangChain Integration Layer
src/clojure_mcp/agent/langchain.clj
Major rewrite introducing memory management primitives (chat-memory, memory-add!, memory-messages, memory-clear!, memory-count); replaced multi-provider model builders with agent-model and reasoning-agent-model leveraging langchain4clj presets; added create-assistant-fn for unified assistant creation; introduced tool conversion utilities (adapt-tool-fn, registration-map->tool-map, convert-tools); removed legacy chat-request and AiService interfaces.
Chat Listener Delegation
src/clojure_mcp/agent/langchain/chat_listener.clj
Simplified public functions (create-listener, logging-listener, token-tracking-listener, compose-listeners) to delegate to corresponding langchain4clj.listeners implementations; removed inline EDN-conversion and context interop logic.
Message Conversion Updates
src/clojure_mcp/agent/langchain/message_conv.clj
Added message->edn and edn->message for single-message conversions; replaced direct Java serialization with lc-msg/messages->json and lc-msg/json->messages round-trips; removed legacy chat-request transformation functions.
Model Creation Simplification
src/clojure_mcp/agent/langchain/model.clj
Introduced create-model-from-config with preset-based lookup and provider normalization; added available-models exposing presets; deprecated build-model as backward-compatibility wrapper; replaced explicit per-provider configuration scaffolding and default-configs map with streamlined presets resolution; added get-tool-model for tool-specific model creation.
Schema Module Removal
src/clojure_mcp/agent/langchain/schema.clj
Entire file deleted; removed edn->sch multimethods and schema builders for all JSON schema types previously handled here (now delegated to langchain4clj).
CLI Integration
src/clojure_mcp/prompt_cli.clj
Updated imports to use lc-messages and chain aliases; replaced direct serialization calls with lc-messages/messages->json and lc-messages/json->messages; replaced memory.add with chain/memory-add!.
Agent Tests
test/clojure_mcp/agent/general_agent_test.clj
Replaced .messages field access with memory-count and memory-messages wrappers; switched from legacy model builders to lc/create-model with presets; updated agent map expectations from :service to :assistant-fn.
Model Tests
test/clojure_mcp/agent/langchain/model_test.clj
Rewrote tests to validate presets-based model creation via create-model-from-config; added test-available-models checking preset presence; introduced test-build-model-backward-compat for deprecated wrapper; added test-get-tool-model coverage; removed exhaustive builder-based test cases.
Schema Tests Removal
test/clojure_mcp/agent/langchain/schema_test.clj
Entire test file deleted; removed extensive schema conversion validation tests (now responsibilities of langchain4clj).
Config Tests
test/clojure_mcp/config/tools_config_test.clj
Replaced model/*env-overrides* references with lc/*env-overrides* to align with langchain4clj namespace.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User
    participant CLI as prompt_cli
    participant Agent as general_agent
    participant Memory as Memory<br/>(chain)
    participant Tools as Tools<br/>(chain)
    participant Model as Model<br/>(lc/create-model)
    participant Assistant as Assistant Fn

    User->>CLI: Input prompt + context
    CLI->>Agent: create-general-agent(config)
    
    rect rgb(240, 248, 255)
    Note over Agent,Assistant: Agent Initialization
    Agent->>Model: lc/create-model(config)
    Model-->>Agent: model instance
    Agent->>Memory: chat-memory()
    Memory-->>Agent: memory instance
    Agent->>Memory: memory-add!(system-message)
    Agent->>Tools: convert-tools(tool-specs)
    Tools-->>Agent: tool-map
    Agent->>Assistant: create-assistant-fn(model, memory, tools, system-msg)
    Assistant-->>Agent: assistant-fn
    Agent-->>CLI: {assistant-fn, memory, model, tools, ...}
    end
    
    rect rgb(255, 240, 245)
    Note over CLI,Assistant: Chat Invocation
    CLI->>Agent: chat-with-agent(agent, prompt)
    Agent->>Memory: memory-count()
    Memory-->>Agent: count
    alt count > MEMORY-RESET-BUFFER
        Agent->>Memory: memory-clear!()
        Agent->>Memory: memory-add!(context)
    end
    Agent->>Assistant: assistant-fn(prompt)
    Assistant->>Memory: memory-add!(prompt)
    Assistant->>Model: invoke with memory
    Model-->>Assistant: response
    Assistant->>Memory: memory-add!(response)
    Assistant-->>Agent: result
    Agent-->>CLI: response
    end
    
    CLI->>User: Output response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • Config validation #104: Both PRs modify configuration validation and schema handling, with this PR removing the schema module entirely and shifting schema responsibilities to langchain4clj.

Poem

🐰 Hops with glee through refactored code,
From services replaced by functions bright,
Memory helpers now make the load,
Less verbose, more composable might!
With langchain4clj as our guide,
This rabbit's code review fills with pride! 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: migrating from LangChain4j (Java interop) to LangChain4Clj (Clojure wrapper). It accurately reflects the primary objective documented in the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
deps.edn (1)

98-98: Redundant nrepl dependency declaration.

nrepl/nrepl is now declared in main :deps (line 4), making this :extra-deps entry redundant for the :nrepl alias. Consider removing it to avoid version drift.

Proposed fix
  :nrepl {:extra-paths ["test" "dev"]
-         :extra-deps {nrepl/nrepl {:mvn/version "1.3.1"}}
          :main-opts ["-m" "nrepl.cmdline" "--port" "7888"]}
src/clojure_mcp/agent/general_agent.clj (1)

140-145: Consider extracting the threshold buffer as a named constant.

The magic number 15 in (- memory-size 15) represents a buffer before triggering reset. For clarity, consider extracting this to a constant like MEMORY-RESET-BUFFER.

🔎 Proposed refactor
 (def DEFAULT-MEMORY-SIZE 100)
 (def DEFAULT-STATELESS-BUFFER 100)
 (def MIN-PERSISTENT-WINDOW 10)
+(def MEMORY-RESET-BUFFER 15)

Then use:

-  (if (> (chain/memory-count memory) (- memory-size 15))
+  (if (> (chain/memory-count memory) (- memory-size MEMORY-RESET-BUFFER))
src/clojure_mcp/agent/langchain.clj (1)

128-136: Consider making max-iterations configurable.

The max-iterations is hardcoded to 10. While this is a reasonable default, consider making it configurable for agents that may need more iterations for complex tool chains.

🔎 Proposed refactor
-(defn create-assistant-fn [{:keys [model memory tools system-message]}]
+(defn create-assistant-fn [{:keys [model memory tools system-message max-iterations]
+                            :or {max-iterations 10}}]
   (assistant/create-assistant
    {:model model
     :memory memory
     :tools (when (seq tools) (convert-tools tools))
     :system-message system-message
-    :max-iterations 10}))
+    :max-iterations max-iterations}))
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 355b722 and 15665d9.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • deps.edn
  • src/clojure_mcp/agent/general_agent.clj
  • src/clojure_mcp/agent/langchain.clj
  • src/clojure_mcp/agent/langchain/chat_listener.clj
  • src/clojure_mcp/agent/langchain/message_conv.clj
  • src/clojure_mcp/agent/langchain/model.clj
  • src/clojure_mcp/agent/langchain/schema.clj
  • src/clojure_mcp/prompt_cli.clj
  • test/clojure_mcp/agent/general_agent_test.clj
  • test/clojure_mcp/agent/langchain/model_test.clj
  • test/clojure_mcp/agent/langchain/schema_test.clj
  • test/clojure_mcp/config/tools_config_test.clj
💤 Files with no reviewable changes (2)
  • test/clojure_mcp/agent/langchain/schema_test.clj
  • src/clojure_mcp/agent/langchain/schema.clj
🧰 Additional context used
📓 Path-based instructions (2)
**/*.clj

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.clj: Use :require with ns aliases in import statements (e.g., [clojure.string :as string])
Use kebab-case for variable and function names
End predicate functions with ? (e.g., is-top-level-form?)
Use try/catch with specific exception handling; use atom for tracking errors
Use 2-space indentation and maintain whitespace in edited forms
Align namespace names with directory structure (e.g., clojure-mcp.repl-tools for clojure_mcp/repl_tools.clj)
Include clear tool :description for LLM guidance in MCP tool definitions
Validate inputs and provide helpful error messages in MCP tools
Return structured data with both result and error status from MCP tools
Maintain atom-based state for consistent service access in MCP tools

Files:

  • src/clojure_mcp/prompt_cli.clj
  • src/clojure_mcp/agent/langchain.clj
  • src/clojure_mcp/agent/langchain/chat_listener.clj
  • src/clojure_mcp/agent/langchain/message_conv.clj
  • test/clojure_mcp/agent/langchain/model_test.clj
  • test/clojure_mcp/config/tools_config_test.clj
  • test/clojure_mcp/agent/general_agent_test.clj
  • src/clojure_mcp/agent/general_agent.clj
  • src/clojure_mcp/agent/langchain/model.clj
**/*_test.clj

📄 CodeRabbit inference engine (CLAUDE.md)

Use deftest with descriptive test names; use testing for subsections; use is for assertions

Files:

  • test/clojure_mcp/agent/langchain/model_test.clj
  • test/clojure_mcp/config/tools_config_test.clj
  • test/clojure_mcp/agent/general_agent_test.clj
🧠 Learnings (5)
📚 Learning: 2025-12-07T23:16:26.445Z
Learnt from: CR
Repo: bhauman/clojure-mcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-07T23:16:26.445Z
Learning: Applies to **/*.clj : Include clear tool `:description` for LLM guidance in MCP tool definitions

Applied to files:

  • src/clojure_mcp/prompt_cli.clj
  • deps.edn
  • test/clojure_mcp/config/tools_config_test.clj
📚 Learning: 2025-12-07T23:16:26.445Z
Learnt from: CR
Repo: bhauman/clojure-mcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-07T23:16:26.445Z
Learning: Applies to **/*.clj : Maintain atom-based state for consistent service access in MCP tools

Applied to files:

  • src/clojure_mcp/prompt_cli.clj
  • deps.edn
  • CHANGELOG.md
  • test/clojure_mcp/agent/general_agent_test.clj
  • src/clojure_mcp/agent/general_agent.clj
📚 Learning: 2025-12-07T23:16:26.445Z
Learnt from: CR
Repo: bhauman/clojure-mcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-07T23:16:26.445Z
Learning: Applies to **/*.clj : Return structured data with both result and error status from MCP tools

Applied to files:

  • src/clojure_mcp/prompt_cli.clj
  • test/clojure_mcp/config/tools_config_test.clj
📚 Learning: 2025-12-07T23:16:26.445Z
Learnt from: CR
Repo: bhauman/clojure-mcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-07T23:16:26.445Z
Learning: Applies to **/*.clj : Align namespace names with directory structure (e.g., `clojure-mcp.repl-tools` for `clojure_mcp/repl_tools.clj`)

Applied to files:

  • deps.edn
  • test/clojure_mcp/config/tools_config_test.clj
📚 Learning: 2025-12-07T23:16:26.445Z
Learnt from: CR
Repo: bhauman/clojure-mcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-07T23:16:26.445Z
Learning: Applies to **/*.clj : Use `:require` with ns aliases in import statements (e.g., `[clojure.string :as string]`)

Applied to files:

  • test/clojure_mcp/config/tools_config_test.clj
🔇 Additional comments (34)
deps.edn (1)

24-26: Dependency consolidation looks good.

The migration from multiple LangChain4j Maven artifacts to a single langchain4clj dependency aligns with PR objectives and simplifies dependency management. The comment clearly documents that LangChain4j classes remain available transitively.

src/clojure_mcp/agent/langchain/chat_listener.clj (2)

37-47: Breaking change clearly documented.

The docstring properly documents the atom structure change, providing both old and new formats and guidance for consumers. This helps with migration.


49-51: Simple re-export of compose-listeners.

Using def to alias the function is idiomatic Clojure for re-exporting without wrapping.

test/clojure_mcp/config/tools_config_test.clj (2)

4-5: Imports updated correctly for langchain4clj migration.

The model namespace is retained for get-tool-model calls while lc/*env-overrides* is properly used for environment binding.


43-51: Environment override binding migrated correctly.

The test now uses lc/*env-overrides* for test API key injection, consistent with the langchain4clj wrapper approach.

src/clojure_mcp/prompt_cli.clj (4)

14-22: Imports updated for langchain4clj message handling.

The namespace now properly uses langchain4clj.messages for serialization and the chain alias for memory operations. Import organization follows coding guidelines with :require aliases.


89-89: Message serialization delegated to langchain4clj.

Using lc-messages/messages->json aligns with the migration objective to remove direct Java interop.


118-118: Message deserialization delegated to langchain4clj.

Using lc-messages/json->messages for loading sessions is consistent with the serialization path.


367-368: Memory addition uses new chain helper.

chain/memory-add! replaces direct Java memory manipulation, consistent with the memory-centric API migration.

src/clojure_mcp/agent/langchain/message_conv.clj (3)

1-5: Clean namespace simplification.

The docstring accurately describes the purpose, and requires are minimal—only what's needed for JSON handling and langchain4clj message conversion.


7-18: JSON round-trip approach is straightforward.

The conversion via JSON intermediary is clear and delegates complexity to langchain4clj. This eliminates the need for custom serialization logic.


20-34: Single-message converters properly handle nil.

Using some-> ensures nil messages don't cause exceptions during conversion.

CHANGELOG.md (1)

5-31: Comprehensive changelog entry for the migration.

The entry clearly documents the migration scope, breaking changes (with before/after examples for the token-tracking listener), and removed components. This will help consumers understand the impact and migrate their code.

test/clojure_mcp/agent/langchain/model_test.clj (3)

1-11: Test imports updated for langchain4clj migration.

The test properly imports both the Clojure wrapper (langchain4clj.presets, langchain4clj.core) and the underlying Java model classes for type assertions.


30-67: Thorough test coverage for model creation from config.

Tests cover user config precedence, preset fallback with API key override, Anthropic models, and error handling for unknown model keys. Good coverage of the key scenarios.


81-93: Backward compatibility test for legacy :model-name key.

This test ensures existing configs using :model-name continue to work after the migration to langchain4clj which expects :model. Good migration safety check.

test/clojure_mcp/agent/general_agent_test.clj (4)

6-8: Test imports updated for langchain4clj integration.

Properly imports langchain4clj.core and langchain4clj.presets for model creation in tests.


38-50: Memory tests migrated to use new helpers.

Tests now use chain/memory-count and chain/memory-messages instead of direct memory field access, aligning with the memory-centric API.


71-87: Agent creation test updated for new API.

Model creation uses lc/create-model with presets, and the test correctly expects :assistant-fn in the returned agent map (replacing the previous :service key per the PR summary).


116-126: Context update test uses memory helpers.

The test properly uses chain/memory-add! and chain/memory-count for memory operations, consistent with the migration.

src/clojure_mcp/agent/general_agent.clj (7)

1-13: LGTM - Namespace and constants are well-defined.

The namespace declaration follows guidelines with proper :require and ns aliases. The constants DEFAULT-MEMORY-SIZE, DEFAULT-STATELESS-BUFFER, and MIN-PERSISTENT-WINDOW are appropriately named using SCREAMING-KEBAB-CASE for constants.


88-113: LGTM - Clean conditional logic for memory configuration.

The function properly handles the three memory modes (stateless default, stateless for small values, persistent window) with clear conditions and appropriate use of the MIN-PERSISTENT-WINDOW constant.


115-129: LGTM - Clean migration to langchain4clj memory API.

The function now uses chain/memory-add! with an idiomatic EDN map representation instead of direct Java interop, improving readability.


147-199: LGTM - Well-structured agent creation with proper validation.

The function correctly validates required parameters, creates the assistant function using the new langchain4clj wrapper, and returns a comprehensive agent map. The docstring accurately documents the breaking change from :service to :assistant-fn.


201-234: LGTM - Clean chat invocation with proper error handling.

The function correctly handles blank prompts, manages memory for both stateless and persistent modes, and invokes the assistant function properly. The structured return with :result and :error follows the coding guidelines.


236-247: LGTM - Simple and effective context update.

The function correctly clears memory, reinitializes with new context, and returns the updated agent map.


249-262: LGTM - Correct agent recreation for tool addition.

The docstring clearly notes that adding tools requires recreating the agent. The select-keys properly preserves only the configuration keys needed for recreation.

src/clojure_mcp/agent/langchain.clj (3)

1-10: LGTM - Clean namespace declaration following guidelines.

Proper use of :require with ns aliases as per coding guidelines. The docstring clearly describes the adapter's purpose.


14-36: LGTM - Idiomatic memory wrapper functions.

The memory functions properly wrap the langchain4clj assistant API. Function names with ! suffix correctly indicate side effects. The memory-add! function appropriately handles both map and message object inputs.


88-104: LGTM - Well-structured tool conversion with proper validation.

The preconditions validate inputs, and the function correctly handles schema as either a JSON string or a map. The conversion to langchain4clj format is clean.

src/clojure_mcp/agent/langchain/model.clj (4)

1-7: LGTM - Clean namespace declaration.

The namespace declaration follows guidelines with proper requires and aliases.


22-33: LGTM - Proper Long to Integer coercion for Java interop.

The coerce-integers function correctly handles the type coercion needed for LangChain4j's Java API, which expects Integer rather than Long for certain parameters.


51-73: LGTM - Robust model creation with proper error handling and security.

The function correctly:

  • Falls back from user-models to presets
  • Provides informative error with available options
  • Excludes :api-key from debug logging (good security practice)
  • Applies proper config normalization

75-88: LGTM - Graceful handling of missing or failed tool model configuration.

The function uses when-let chain for safe nil handling and catches exceptions to log warnings rather than propagating, allowing callers to handle missing models gracefully.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/clojure_mcp/agent/general_agent.clj (1)

253-266: Fix key mismatch in add-tools.

Line 265 selects :system-prompt from the agent map, but create-general-agent returns :system-message (line 197). This mismatch will cause add-tools to pass nil for :system-prompt, triggering the validation error at line 175.

🔎 Proposed fix
 (defn add-tools
   "Add additional tools to an existing agent.
    Note: This requires recreating the agent.
    
    Args:
    - agent: The agent map
    - new-tools: Vector of new tools to add
    
    Returns: A new agent map with additional tools"
   [agent new-tools]
   (create-general-agent
    (-> agent
-       (select-keys [:system-prompt :context :model :memory-size])
+       (select-keys [:system-message :context :model :memory-size])
+       (clojure.set/rename-keys {:system-message :system-prompt})
        (assoc :tools (vec (concat (:tools agent) new-tools))))))
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 15665d9 and 5745111.

📒 Files selected for processing (4)
  • deps.edn
  • src/clojure_mcp/agent/general_agent.clj
  • src/clojure_mcp/agent/langchain.clj
  • src/clojure_mcp/agent/langchain/chat_listener.clj
🚧 Files skipped from review as they are similar to previous changes (1)
  • deps.edn
🧰 Additional context used
📓 Path-based instructions (1)
**/*.clj

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.clj: Use :require with ns aliases in import statements (e.g., [clojure.string :as string])
Use kebab-case for variable and function names
End predicate functions with ? (e.g., is-top-level-form?)
Use try/catch with specific exception handling; use atom for tracking errors
Use 2-space indentation and maintain whitespace in edited forms
Align namespace names with directory structure (e.g., clojure-mcp.repl-tools for clojure_mcp/repl_tools.clj)
Include clear tool :description for LLM guidance in MCP tool definitions
Validate inputs and provide helpful error messages in MCP tools
Return structured data with both result and error status from MCP tools
Maintain atom-based state for consistent service access in MCP tools

Files:

  • src/clojure_mcp/agent/langchain/chat_listener.clj
  • src/clojure_mcp/agent/general_agent.clj
  • src/clojure_mcp/agent/langchain.clj
🧠 Learnings (6)
📚 Learning: 2025-12-07T23:16:26.445Z
Learnt from: CR
Repo: bhauman/clojure-mcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-07T23:16:26.445Z
Learning: Applies to **/*.clj : Use `try/catch` with specific exception handling; use atom for tracking errors

Applied to files:

  • src/clojure_mcp/agent/langchain/chat_listener.clj
📚 Learning: 2025-12-27T06:54:07.157Z
Learnt from: nandoolle
Repo: bhauman/clojure-mcp PR: 138
File: src/clojure_mcp/agent/langchain/model.clj:91-97
Timestamp: 2025-12-27T06:54:07.157Z
Learning: When reviewing Clojure code (e.g., in src/clojure_mcp/... ), verify calls to presets/get-preset follow the two arities: [preset-key] and [preset-key overrides]. The 2-arg version should merge the provided overrides map with the preset defaults and return the resulting configuration. Check for correct map merging, preservation of immutability, and validation of overrides keys to ensure predictable configuration outcomes.

Applied to files:

  • src/clojure_mcp/agent/langchain/chat_listener.clj
  • src/clojure_mcp/agent/general_agent.clj
  • src/clojure_mcp/agent/langchain.clj
📚 Learning: 2025-12-07T23:16:26.445Z
Learnt from: CR
Repo: bhauman/clojure-mcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-07T23:16:26.445Z
Learning: Applies to **/*.clj : Maintain atom-based state for consistent service access in MCP tools

Applied to files:

  • src/clojure_mcp/agent/general_agent.clj
📚 Learning: 2025-12-07T23:16:26.445Z
Learnt from: CR
Repo: bhauman/clojure-mcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-07T23:16:26.445Z
Learning: Applies to **/*.clj : Include clear tool `:description` for LLM guidance in MCP tool definitions

Applied to files:

  • src/clojure_mcp/agent/general_agent.clj
📚 Learning: 2025-12-27T06:53:25.104Z
Learnt from: nandoolle
Repo: bhauman/clojure-mcp PR: 138
File: src/clojure_mcp/agent/langchain.clj:46-57
Timestamp: 2025-12-27T06:53:25.104Z
Learning: For src/clojure_mcp/agent/langchain.clj, when upgrading to langchain4clj 1.6.1, use the reasoning model presets :anthropic/claude-sonnet-4-reasoning, :google/gemini-2-5-flash-reasoning, or :openai/o4-mini-reasoning. These presets produce valid configurations containing a :thinking map; verify that the resulting configuration includes a :thinking key and aligns with the expected schema before use.

Applied to files:

  • src/clojure_mcp/agent/langchain.clj
📚 Learning: 2025-12-07T23:16:26.445Z
Learnt from: CR
Repo: bhauman/clojure-mcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-07T23:16:26.445Z
Learning: Applies to **/*.clj : Return structured data with both result and error status from MCP tools

Applied to files:

  • src/clojure_mcp/agent/langchain.clj
🔇 Additional comments (15)
src/clojure_mcp/agent/langchain/chat_listener.clj (3)

7-28: LGTM! Consistent :ctx handling across all handlers.

The on-request, on-response, and on-error handlers now consistently attach :ctx from :raw-context for backward compatibility. This resolves the inconsistency previously flagged.


30-35: LGTM!

Clean delegation to lc-listeners/logging-listener with appropriate two-arity wrapper.


37-51: LGTM! Breaking change is well-documented.

The docstring clearly documents the atom structure change from legacy keys (:total-input-tokens, :total-output-tokens) to current keys (:input-tokens, :output-tokens), guiding consumers to update their code accordingly.

src/clojure_mcp/agent/langchain.clj (6)

16-35: LGTM! Clean memory API delegation.

Memory functions properly delegate to langchain4clj.assistant with appropriate handling of both map and object message types in memory-add!.


39-67: LGTM! Model creation with appropriate fallback handling.

The model creation flow properly detects available providers via environment variables and returns nil when no provider is configured, allowing callers to handle the absence gracefully. The reasoning model presets are confirmed to exist in langchain4clj 1.6.1 per previous verification.


71-90: LGTM! Timeout prevents indefinite blocking.

The 5-minute timeout on deref (line 87-89) appropriately prevents indefinite blocking if the callback-style tool function never invokes its callback, addressing the concern from previous review feedback.


92-130: LGTM! Robust tool conversion with validation.

The tool conversion functions include proper precondition validation (lines 95-96) and handle both string (JSON) and map schema formats. The separation between standard tool-map format and AiServices format is clean and appropriate.


134-150: LGTM! Clean assistant creation with sensible defaults.

The function properly defaults max-iterations to 10 and only converts tools when non-empty (line 148), avoiding unnecessary conversion overhead and potential nil issues.


154-158: LGTM! Clean message helper functions.

The message helpers appropriately delegate to messages/edn->message with proper type handling, coercing content to string in user-message when needed.

src/clojure_mcp/agent/general_agent.clj (6)

15-17: LGTM! Clear constant definition.

The MEMORY-RESET-BUFFER constant is well-documented and appropriately scoped for use in memory management logic.


119-133: LGTM! Context initialization with clean delegation.

The function properly combines context strings and delegates to chain/memory-add! with the appropriate message structure.


135-149: LGTM! Proactive memory reset with buffer.

The function uses MEMORY-RESET-BUFFER to trigger reset before reaching the limit, preventing potential overflow issues while preserving conversation coherence by re-initializing with context.


151-203: LGTM! Agent creation with proper validation and delegation.

The function validates required inputs (model and system-prompt), creates memory based on configuration, and properly initializes the assistant via chain/create-assistant-fn. Note that the returned map uses :system-message while the input uses :system-prompt.


205-238: LGTM! Proper memory management and assistant invocation.

The function correctly handles stateless vs. persistent memory modes, uses chain/memory-clear! for stateless agents, and invokes the :assistant-fn appropriately. Memory reset logic for persistent agents prevents overflow while maintaining context.


240-251: LGTM! Clean context update with memory refresh.

The function properly clears and re-initializes memory with the new context, returning an updated agent map.

@nandoolle nandoolle force-pushed the refactor/langchain4clj-migration branch from 5745111 to 7428fdf Compare December 27, 2025 07:12
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/clojure_mcp/agent/langchain.clj (1)

59-67: Document that these functions can return nil when no API key is configured.

Both agent-model and reasoning-agent-model will return nil if no supported API key environment variable is set. While create-general-agent in general_agent.clj checks for nil models, the docstrings should explicitly state this return behavior to help callers handle the case properly.

🔎 Suggested docstring additions
 (defn agent-model []
+  "Creates a chat model using the first available provider.
+   
+   Detects provider based on environment variables:
+   - ANTHROPIC_API_KEY, GEMINI_API_KEY, or OPENAI_API_KEY
+   
+   Returns the model instance, or nil if no API key is configured."
   (when-let [provider (detect-available-provider)]
     (when-let [model-key (default-model-for-provider provider false)]
       (lc/create-model (presets/get-preset model-key)))))

 (defn reasoning-agent-model []
+  "Creates a reasoning-capable chat model using the first available provider.
+   
+   Detects provider based on environment variables:
+   - ANTHROPIC_API_KEY, GEMINI_API_KEY, or OPENAI_API_KEY
+   
+   Returns the model instance, or nil if no API key is configured."
   (when-let [provider (detect-available-provider)]
     (when-let [model-key (default-model-for-provider provider true)]
       (lc/create-model (presets/get-preset model-key)))))
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5745111 and 7428fdf.

📒 Files selected for processing (4)
  • deps.edn
  • src/clojure_mcp/agent/general_agent.clj
  • src/clojure_mcp/agent/langchain.clj
  • src/clojure_mcp/agent/langchain/chat_listener.clj
🚧 Files skipped from review as they are similar to previous changes (1)
  • deps.edn
🧰 Additional context used
📓 Path-based instructions (1)
**/*.clj

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.clj: Use :require with ns aliases in import statements (e.g., [clojure.string :as string])
Use kebab-case for variable and function names
End predicate functions with ? (e.g., is-top-level-form?)
Use try/catch with specific exception handling; use atom for tracking errors
Use 2-space indentation and maintain whitespace in edited forms
Align namespace names with directory structure (e.g., clojure-mcp.repl-tools for clojure_mcp/repl_tools.clj)
Include clear tool :description for LLM guidance in MCP tool definitions
Validate inputs and provide helpful error messages in MCP tools
Return structured data with both result and error status from MCP tools
Maintain atom-based state for consistent service access in MCP tools

Files:

  • src/clojure_mcp/agent/langchain.clj
  • src/clojure_mcp/agent/general_agent.clj
  • src/clojure_mcp/agent/langchain/chat_listener.clj
🧠 Learnings (5)
📚 Learning: 2025-12-27T06:53:25.104Z
Learnt from: nandoolle
Repo: bhauman/clojure-mcp PR: 138
File: src/clojure_mcp/agent/langchain.clj:46-57
Timestamp: 2025-12-27T06:53:25.104Z
Learning: For src/clojure_mcp/agent/langchain.clj, when upgrading to langchain4clj 1.6.1, use the reasoning model presets :anthropic/claude-sonnet-4-reasoning, :google/gemini-2-5-flash-reasoning, or :openai/o4-mini-reasoning. These presets produce valid configurations containing a :thinking map; verify that the resulting configuration includes a :thinking key and aligns with the expected schema before use.

Applied to files:

  • src/clojure_mcp/agent/langchain.clj
📚 Learning: 2025-12-07T23:16:26.445Z
Learnt from: CR
Repo: bhauman/clojure-mcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-07T23:16:26.445Z
Learning: Applies to **/*.clj : Return structured data with both result and error status from MCP tools

Applied to files:

  • src/clojure_mcp/agent/langchain.clj
📚 Learning: 2025-12-27T06:54:07.157Z
Learnt from: nandoolle
Repo: bhauman/clojure-mcp PR: 138
File: src/clojure_mcp/agent/langchain/model.clj:91-97
Timestamp: 2025-12-27T06:54:07.157Z
Learning: When reviewing Clojure code (e.g., in src/clojure_mcp/... ), verify calls to presets/get-preset follow the two arities: [preset-key] and [preset-key overrides]. The 2-arg version should merge the provided overrides map with the preset defaults and return the resulting configuration. Check for correct map merging, preservation of immutability, and validation of overrides keys to ensure predictable configuration outcomes.

Applied to files:

  • src/clojure_mcp/agent/langchain.clj
  • src/clojure_mcp/agent/general_agent.clj
  • src/clojure_mcp/agent/langchain/chat_listener.clj
📚 Learning: 2025-12-07T23:16:26.445Z
Learnt from: CR
Repo: bhauman/clojure-mcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-07T23:16:26.445Z
Learning: Applies to **/*.clj : Maintain atom-based state for consistent service access in MCP tools

Applied to files:

  • src/clojure_mcp/agent/general_agent.clj
📚 Learning: 2025-12-07T23:16:26.445Z
Learnt from: CR
Repo: bhauman/clojure-mcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-07T23:16:26.445Z
Learning: Applies to **/*.clj : Use `try/catch` with specific exception handling; use atom for tracking errors

Applied to files:

  • src/clojure_mcp/agent/langchain/chat_listener.clj

@nandoolle
Copy link
Author

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)

src/clojure_mcp/agent/general_agent.clj (1)> 253-266: Fix key mismatch in add-tools.

Line 265 selects :system-prompt from the agent map, but create-general-agent returns :system-message (line 197). This mismatch will cause add-tools to pass nil for :system-prompt, triggering the validation error at line 175.

🔎 Proposed fix

 (defn add-tools
   "Add additional tools to an existing agent.
    Note: This requires recreating the agent.
    
    Args:
    - agent: The agent map
    - new-tools: Vector of new tools to add
    
    Returns: A new agent map with additional tools"
   [agent new-tools]
   (create-general-agent
    (-> agent
-       (select-keys [:system-prompt :context :model :memory-size])
+       (select-keys [:system-message :context :model :memory-size])
+       (clojure.set/rename-keys {:system-message :system-prompt})
        (assoc :tools (vec (concat (:tools agent) new-tools))))))

📜 Review details

issue addressed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant