Skip to content

Migrate fdm-agents from Google ADK to LangChain/LangGraph#618

Open
SvenVw wants to merge 18 commits into
developmentfrom
FDM597
Open

Migrate fdm-agents from Google ADK to LangChain/LangGraph#618
SvenVw wants to merge 18 commits into
developmentfrom
FDM597

Conversation

@SvenVw
Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw commented May 13, 2026

Summary by CodeRabbit

  • New Features

    • Structured fertilizer plan outputs with validation and run IDs for traceable AI plans
    • Per-field simulation results with isolated errors and safer fallbacks when data is missing
  • Bug Fixes

    • Better handling of agent recursion limits, timeouts, and truncated/invalid plan JSON
    • Validation for application dates and sanitized catalogue IDs to prevent lookup errors
  • Chores

    • Updated runtime dependencies and example environment variables for improved tracing and API access

Review Change Stack

Closes #597

@SvenVw SvenVw self-assigned this May 13, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

📝 Walkthrough

Walkthrough

Migrate fdm-agents from @google/adk to LangChain/LangGraph: update dependencies and env examples; replace Gemini model with ChatGoogleGenerativeAI; add Zod schemas for structured fertilizer plan output; convert tools to LangChain tool(...); refactor agent factory and one-shot runner to streaming AgentGraph; update public exports, route parsing, and tests.

Changes

LangChain Migration Core

Layer / File(s) Summary
Dependencies and environment setup
.changeset/better-pants-eat.md, fdm-agents/package.json, fdm-agents/.env.example, pnpm-workspace.yaml, fdm-app/.env.example
Changeset added; removed @google/adk, added @langchain/* and @posthog/ai; .env.example files document Gemini API key and optional LangSmith tracing; workspace catalog bumps.
Structured output schema definition
fdm-agents/src/agents/gerrit/schema.ts
New Zod schemas: FertilizerApplicationSchema, FieldPlanEntrySchema, internal N-balance and farm totals, exported FertilizerPlanSchema and FertilizerPlanOutput.
LLM model factory and tests
fdm-agents/src/models/default.ts, fdm-agents/src/models/default.test.ts
Replace Gemini with ChatGoogleGenerativeAI, add maxOutputTokens: 65536, update tests to mock LangChain Google GenAI.
Agent factory and prompt updates
fdm-agents/src/agents/gerrit/agent.ts, fdm-agents/src/agents/gerrit/agent.test.ts
Migrate to LangChain agent creation, export GERRIT_* constants, add countToolRoundtrips and dynamic system-prompt middleware enforcing a soft tool-call limit; prompt content tightened and Dutch-only constraints added; tests adjusted.
Tool suite migration and tests
fdm-agents/src/tools/fertilizer-planner/index.ts, fdm-agents/src/tools/fertilizer-planner/index.test.ts
Convert tools to LangChain tool(...) with RunnableConfig, read principalId from config.configurable, add per-field error isolation in advice/simulate tools, guard crop-index loading, and update tests to invoke(makeConfigurable()).
Streaming runner and orchestration
fdm-agents/src/runners/one-shot.ts, fdm-agents/src/runners/one-shot.test.ts
Refactor to consume AgentGraph.stream updates, implement AbortController timeout, extract token usage and tool calls from stream updates, surface structuredResponse and runId, add AgentRecursionLimitError, and rewrite tests for streaming behavior and error mapping.
Public exports and downstream integration
fdm-agents/src/index.ts, fdm-app/app/routes/farm.$b_id_farm.$calendar.gerrit.tsx, fdm-app/app/components/blocks/gerrit/schema.ts
Re-export new error and schema types, make FarmFieldSummary.b_area nullable, filter/format productive fields in prompts, prefer schema-validated structuredResponse with JSON fallback/repair in route, sanitize catalogue IDs, validate application dates, and update GEMINI model list.
Prompt builder and integration tests
fdm-agents/src/index.test.ts
Tests updated for productive-field filtering and b_lu_croprotation handling.
Calculator API small refactor
fdm-calculator/src/nutrient-advice/index.ts
Removed console.error wrapper in requestNutrientAdvice while preserving behavior.

Sequence Diagram(s)

sequenceDiagram
  participant ComponentA
  participant ComponentB
  ComponentA->>ComponentB: observable interaction
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • nmi-agro/fdm#543: Related workspace/package.json edits touching ADK usage and dependency changes.
  • nmi-agro/fdm#534: Prior Gerrit agent work that the current migration replaces.

Suggested labels

enhancement, fdm-app, fdm-core, fdm-calculator, dependencies, branch:development

Suggested reviewers

  • BoraIneviNMI
  • gerardhros

Poem

🐰 From ADK fields to LangChain's tree,
I hopped and fetched a schema key.
Tools now stream and tokens hum,
RunId saved — the plan is done.
Dutch summaries tidy, neat — hooray for me!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Pull request successfully migrates fdm-agents from Google ADK to LangChain/LangGraph, replacing all dependencies, tools, agent implementations, and test harnesses while preserving public APIs.
Out of Scope Changes check ✅ Passed Changes remain within the migration scope: LangChain dependencies, model/tool/agent refactoring, test updates, and environment configuration. Minor version bumps in pnpm-workspace.yaml and Gemini model string update are supporting maintenance aligned with migration.
Title Check ✅ Passed Title check skipped as CodeRabbit has written the PR title.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch FDM597

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

@sentry
Copy link
Copy Markdown

sentry Bot commented May 13, 2026

Codecov Report

❌ Patch coverage is 88.10811% with 22 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
fdm-agents/src/tools/fertilizer-planner/index.ts 80.39% 10 Missing ⚠️
fdm-agents/src/agents/gerrit/agent.ts 76.00% 6 Missing ⚠️
fdm-agents/src/runners/one-shot.ts 92.53% 5 Missing ⚠️
fdm-calculator/src/nutrient-advice/index.ts 96.15% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Comment thread fdm-agents/src/runners/one-shot.test.ts Dismissed
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.gerrit.tsx Fixed
@coderabbitai coderabbitai Bot changed the title @coderabbitai Migrate fdm-agents from Google ADK to LangChain/LangGraph May 13, 2026
Copy link
Copy Markdown
Contributor

@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: 7

Caution

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

⚠️ Outside diff range comments (1)
fdm-agents/src/tools/fertilizer-planner/index.ts (1)

65-78: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add guard for missing principalId.

The tool extracts principalId from config?.configurable but doesn't validate it before passing to getFields. If principalId is missing (e.g., misconfiguration), getFields will likely throw a less descriptive error. Add an early guard similar to searchFertilizersTool (lines 287-289).

🛡️ Suggested guard
 const getFarmFieldsTool = tool(
     async (input: any, config?: RunnableConfig) => {
         const principalId = config?.configurable?.principalId as PrincipalId
+        if (!principalId) {
+            return { fields: [] }
+        }
         const timeframe = {
             start: new Date(`${input.calendar}-01-01`),
             end: new Date(`${input.calendar}-12-31`),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fdm-agents/src/tools/fertilizer-planner/index.ts` around lines 65 - 78,
Missing guard for principalId in getFarmFieldsTool: add an early check that
validates config?.configurable?.principalId before calling getFields. If
principalId is falsy, throw or return a descriptive error (same pattern used in
searchFertilizersTool) so getFields is never called with an undefined
principalId. Update the getFarmFieldsTool async handler to extract principalId,
perform the guard, and only then call getFields(fdm, principalId,
input.b_id_farm, timeframe).
🧹 Nitpick comments (1)
fdm-agents/src/models/default.test.ts (1)

23-27: ⚡ Quick win

Assert maxOutputTokens in constructor expectations to prevent config drift.

Current tests validate apiKey and model but miss the explicit token cap added in the factory. Add it to both assertions.

🧪 Proposed test update
         expect(mockChatGoogleGenerativeAI).toHaveBeenCalledWith(
             expect.objectContaining({
                 apiKey: "fake-api-key",
                 model: "gemini-3.1-pro-preview",
+                maxOutputTokens: 65536,
             }),
         )
@@
         expect(mockChatGoogleGenerativeAI).toHaveBeenCalledWith(
             expect.objectContaining({
                 apiKey: "fake-api-key",
                 model: "custom-model",
+                maxOutputTokens: 65536,
             }),
         )

Also applies to: 35-39

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fdm-agents/src/models/default.test.ts` around lines 23 - 27, Tests currently
assert apiKey and model for mockChatGoogleGenerativeAI but omit the
maxOutputTokens that the factory sets; update both expect.objectContaining
assertions in default.test.ts to include maxOutputTokens with the exact numeric
value used in the factory (i.e., add maxOutputTokens: <the exact number from the
factory>) so the test verifies the token cap and prevents config drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@fdm-agents/src/agents/gerrit/agent.ts`:
- Line 16: TOOL_LIMIT_WARNING is defined using a template literal without
interpolation causing the noUnusedTemplateLiteral lint error; replace the
backticks with double quotes for the constant (export const TOOL_LIMIT_WARNING)
and ensure the string uses double quotes to match the project's quoteStyle
configuration, i.e., change the literal in agent.ts from a backticked template
to a plain double-quoted string with the same text and punctuation.
- Around line 185-197: The agent is not being given its exported identity
constants; update the createAgent call to pass the agent name and description by
adding the name: GERRIT_NAME and description: GERRIT_DESCRIPTION properties
alongside the existing model, tools, middleware, and responseFormat so that
createAgent receives these identity fields (the invocation in this file uses
createAgent, and the constants GERRIT_NAME and GERRIT_DESCRIPTION are available;
keep the rest—createDefaultModel(resolvedKey, modelName),
createFertilizerPlannerTools(fdm), dynamicSystemPromptMiddleware, and
FertilizerPlanSchema—unchanged).
- Around line 1-2: Replace usage of the deprecated isAIMessage and the
cast-heavy check by coercing each BaseMessageLike entry to a BaseMessage then
using AIMessage.isInstance: import AIMessage and coerceMessageLikeToMessage from
`@langchain/core/messages`, loop over the messages where you currently call
isAIMessage(msg as any), call coerceMessageLikeToMessage(msg) to get a
BaseMessage and then use AIMessage.isInstance(coerced) to detect assistant/AI
messages; update any type annotations that assumed instance-only checks (e.g.,
references to BaseMessageLike) to use the coerced BaseMessage for downstream
logic such as handling tool_calls.

In `@fdm-agents/src/agents/gerrit/schema.ts`:
- Around line 15-17: The field p_app_date is declared as z.string() but must be
validated as an ISO YYYY-MM-DD date; update the p_app_date schema (symbol:
p_app_date) to enforce the YYYY-MM-DD pattern instead of accepting any
string—replace the free-form string validator with a date-specific validator
(e.g., use an ISO/date-aware Zod validator or a strict regex/refine that matches
/^\d{4}-\d{2}-\d{2}$/) and include a clear .describe() or .refine() message for
invalid values so the schema rejects non-ISO dates.

In `@fdm-agents/src/index.ts`:
- Line 109: The runtime null-check on f.b_area indicates the data model allows
null/undefined for b_area, but the interface (the one declaring b_area: number)
is not nullable; update the interface that defines the type for f (the record
used in index.ts) to make b_area nullable (e.g., number | null or number |
undefined) or optional, so the type matches the check in the template string;
ensure consistency with other nullable fields such as b_soiltype_agr and
a_som_loi.

In `@fdm-app/.env.example`:
- Around line 148-150: Replace the incorrect tracing environment variable names
with the official LangSmith variable: change any occurrences of
LANGCHAIN_TRACING (and any LANGCHAIN_TRACING_V2) to LANGSMITH_TRACING in the
.env.example files referenced (update the LANGCHAIN_TRACING entry in
fdm-app/.env.example and the corresponding entry in fdm-agents/.env.example),
and ensure the surrounding comments or examples reference LangSmith tracing
consistently so the tracer activates when LANGSMITH_TRACING is set.

In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.gerrit.tsx:
- Around line 795-802: The structured output branch currently assigns parsedPlan
= structuredResponse when FertilizerPlanSchema.safeParse(structuredResponse)
fails, which can propagate malformed data; instead, when safeParse returns
!validated.success, log a warning including validation errors (from
validated.error) and fall through to the rawResult parsing path (the same path
used when structuredResponse is falsy) so the code attempts JSON extraction from
rawResult; ensure you reference FertilizerPlanSchema.safeParse, parsedPlan,
structuredResponse and rawResult when implementing the change so the
failed-validate branch does not directly assign unvalidated structuredResponse.

---

Outside diff comments:
In `@fdm-agents/src/tools/fertilizer-planner/index.ts`:
- Around line 65-78: Missing guard for principalId in getFarmFieldsTool: add an
early check that validates config?.configurable?.principalId before calling
getFields. If principalId is falsy, throw or return a descriptive error (same
pattern used in searchFertilizersTool) so getFields is never called with an
undefined principalId. Update the getFarmFieldsTool async handler to extract
principalId, perform the guard, and only then call getFields(fdm, principalId,
input.b_id_farm, timeframe).

---

Nitpick comments:
In `@fdm-agents/src/models/default.test.ts`:
- Around line 23-27: Tests currently assert apiKey and model for
mockChatGoogleGenerativeAI but omit the maxOutputTokens that the factory sets;
update both expect.objectContaining assertions in default.test.ts to include
maxOutputTokens with the exact numeric value used in the factory (i.e., add
maxOutputTokens: <the exact number from the factory>) so the test verifies the
token cap and prevents config drift.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 62ff3a10-58c8-4579-9b5b-5013b6579c81

📥 Commits

Reviewing files that changed from the base of the PR and between 9ee8f2e and 2e8d6c2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • .changeset/better-pants-eat.md
  • fdm-agents/.env.example
  • fdm-agents/package.json
  • fdm-agents/src/agents/gerrit/agent.test.ts
  • fdm-agents/src/agents/gerrit/agent.ts
  • fdm-agents/src/agents/gerrit/schema.ts
  • fdm-agents/src/index.ts
  • fdm-agents/src/models/default.test.ts
  • fdm-agents/src/models/default.ts
  • fdm-agents/src/runners/one-shot.test.ts
  • fdm-agents/src/runners/one-shot.ts
  • fdm-agents/src/tools/fertilizer-planner/index.test.ts
  • fdm-agents/src/tools/fertilizer-planner/index.ts
  • fdm-app/.env.example
  • fdm-app/app/components/blocks/gerrit/schema.ts
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.gerrit.tsx
  • pnpm-workspace.yaml

Comment thread fdm-agents/src/agents/gerrit/agent.ts Outdated
Comment on lines +1 to +2
import { isAIMessage } from "@langchain/core/messages"
import type { BaseMessageLike } from "@langchain/core/messages"
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.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

cd fdm-agents && find . -type f -name "agent.ts" | grep gerrit

Repository: nmi-agro/fdm

Length of output: 85


🏁 Script executed:

cat -n src/agents/gerrit/agent.ts | head -30

Repository: nmi-agro/fdm

Length of output: 115


🏁 Script executed:

cat -n src/agents/gerrit/agent.ts | sed -n '140,170p'

Repository: nmi-agro/fdm

Length of output: 115


🏁 Script executed:

cat -n src/agents/gerrit/agent.ts | sed -n '10,25p'

Repository: nmi-agro/fdm

Length of output: 115


🏁 Script executed:

git ls-files | grep -E "agent\.ts" | grep gerrit

Repository: nmi-agro/fdm

Length of output: 94


🏁 Script executed:

pwd && ls -la fdm-agents/ 2>/dev/null || ls -la

Repository: nmi-agro/fdm

Length of output: 821


🏁 Script executed:

cat -n fdm-agents/src/agents/gerrit/agent.ts | head -30

Repository: nmi-agro/fdm

Length of output: 3801


🏁 Script executed:

cat -n fdm-agents/src/agents/gerrit/agent.ts | sed -n '140,170p'

Repository: nmi-agro/fdm

Length of output: 2077


🏁 Script executed:

cat fdm-agents/package.json | grep -A 5 -B 5 "@langchain"

Repository: nmi-agro/fdm

Length of output: 535


🏁 Script executed:

cd fdm-agents && npm ls `@langchain/core` 2>/dev/null || cat package-lock.json | grep -A 2 '"@langchain/core"' | head -20

Repository: nmi-agro/fdm

Length of output: 183


🏁 Script executed:

web_search
LangChain `@langchain/core` BaseMessageLike AIMessage isAIMessage coerceMessageLikeToMessage

Repository: nmi-agro/fdm

Length of output: 155


🌐 Web query:

LangChain JavaScript @langchain/core BaseMessageLike isAIMessage coerceMessageLikeToMessage deprecation

💡 Result:

There’s no evidence (in the official TypeScript API reference pages we found) that BaseMessageLike or isAIMessage are currently deprecated, nor that coerceMessageLikeToMessage is deprecated. What the official docs say (as of the current reference pages): - BaseMessageLike is a type alias exported from @langchain/core and its signature is shown in the reference docs. [1] - coerceMessageLikeToMessage is documented as a function with the signature coerceMessageLikeToMessage(messageLike: BaseMessageLike): BaseMessage, and the page does not label it as deprecated. [2] - BaseMessage includes deprecatedmethod markers for getType/_getType, but that’s unrelated to BaseMessageLike / isAIMessage / coerceMessageLikeToMessage. [3] Practical takeaway: - If you’re seeing deprecation warnings in your build, they may be coming from different symbols (e.g., BaseMessage.getType/_getType are deprecated), from TypeScript usage patterns, or from a version mismatch between your installed @langchain/core and the docs you’re consulting. The coerceMessageLikeToMessage page itself does not indicate deprecation. [2] If you share the exact deprecation warning text (and your @langchain/core version), I can pinpoint which symbol is actually deprecated.

Citations:


🌐 Web query:

@langchain/core AIMessage.isInstance method

💡 Result:

In @langchain/core (JavaScript/TypeScript), AIMessage has an isInstance method that is a type guard for checking whether a value is an AIMessage. - There is a dedicated type-guard function documented as AIMessage.isInstance in the LangChain reference. It “check[s] if an object is an AIMessage” and returns a TypeScript type predicate. [1] - The documented signature is: isInstance<T extends MessageStructure>(obj: BaseMessage): obj is BaseMessage<T, MessageType> & AIMessage [1] This means when you call AIMessage.isInstance(obj), TypeScript narrows obj to an AIMessage (while preserving the typed BaseMessage generics). [1] Example usage (TypeScript): const msg: BaseMessage =... if (AIMessage.isInstance(msg)) { // msg is narrowed to AIMessage here } Notes: - The AIMessage class reference also lists an isInstance method on the AIMessage class. [2] - There is a parallel isInstance type guard for AIMessageChunk as well. [3]

Citations:


🌐 Web query:

LangChain JavaScript @langchain/core BaseMessageLike type definition dictionary form

💡 Result:

The BaseMessageLike type alias from @langchain/core is defined as: typescript BaseMessageLike: BaseMessage | MessageFieldWithRole | [MessageType, MessageContent] | string | type & BaseMessageFields & Record<string, unknown> | SerializedConstructor This union type allows flexible inputs for message handling in LangChain JS/TS, including full BaseMessage instances, dictionary-like objects with role (e.g., { role: "user", content: "..." }), tuples of type and content, plain strings, objects matching BaseMessageFields with extra properties, or serialized constructors.[1]

Citations:


🏁 Script executed:

cat -n fdm-agents/src/agents/gerrit/agent.ts | sed -n '170,190p'

Repository: nmi-agro/fdm

Length of output: 1036


🌐 Web query:

@langchain/core isAIMessage function deprecated

💡 Result:

In @langchain/core (LangChain.js), the function isAIMessage is deprecated; the docs say you should use AIMessage.isInstance instead. [1] What to change - Old: isAIMessage(x) (deprecated) [1] - New: AIMessage.isInstance(x) [2][1] Related note (from the same docs set): AIMessage also provides a getType method and an isInstance method on the class. [2] If you paste your exact import/usage (TypeScript or JavaScript), I can rewrite it to the recommended replacement.

Citations:


🏁 Script executed:

cat -n fdm-agents/src/agents/gerrit/agent.ts | sed -n '1,10p' && echo "---" && cat -n fdm-agents/src/agents/gerrit/agent.ts | sed -n '145,165p'

Repository: nmi-agro/fdm

Length of output: 1289


Replace deprecated isAIMessage and fix BaseMessageLike handling.

isAIMessage is deprecated in @langchain/core; use AIMessage.isInstance instead. More importantly, the current implementation won't correctly handle valid BaseMessageLike inputs in dictionary form (e.g., { role: "assistant", content: "", tool_calls: [...] }). Since BaseMessageLike is a union type that includes plain objects, not just BaseMessage instances, the cast-heavy check with isAIMessage(msg as any) will fail to identify dictionary-form messages as AI messages. Coerce each entry to BaseMessage first using coerceMessageLikeToMessage, then use AIMessage.isInstance to check the type.

Suggested fix
-import { isAIMessage } from "@langchain/core/messages"
+import { AIMessage, coerceMessageLikeToMessage } from "@langchain/core/messages"
 import type { BaseMessageLike } from "@langchain/core/messages"
@@
 export function countToolRoundtrips(
     messages: readonly { tool_calls?: unknown[] }[] | BaseMessageLike[],
 ): number {
     let count = 0
-    for (const msg of messages) {
-        if (
-            isAIMessage(msg as any) &&
-            (msg as any).tool_calls &&
-            (msg as any).tool_calls.length > 0
-        ) {
+    for (const raw of messages) {
+        const msg = coerceMessageLikeToMessage(raw)
+        if (AIMessage.isInstance(msg) && msg.tool_calls?.length) {
             count++
         }
     }
     return count
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fdm-agents/src/agents/gerrit/agent.ts` around lines 1 - 2, Replace usage of
the deprecated isAIMessage and the cast-heavy check by coercing each
BaseMessageLike entry to a BaseMessage then using AIMessage.isInstance: import
AIMessage and coerceMessageLikeToMessage from `@langchain/core/messages`, loop
over the messages where you currently call isAIMessage(msg as any), call
coerceMessageLikeToMessage(msg) to get a BaseMessage and then use
AIMessage.isInstance(coerced) to detect assistant/AI messages; update any type
annotations that assumed instance-only checks (e.g., references to
BaseMessageLike) to use the coerced BaseMessage for downstream logic such as
handling tool_calls.

Comment thread fdm-agents/src/agents/gerrit/agent.ts Outdated
Comment thread fdm-agents/src/agents/gerrit/agent.ts Outdated
Comment on lines +15 to +17
p_app_date: z
.string()
.describe("Application date in YYYY-MM-DD format"),
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Zod v4, is z.iso.date()the recommended schema for validatingYYYY-MM-DD strings?

💡 Result:

Yes. In Zod v4, z.iso.date is the built-in, recommended schema for validating ISO 8601 date strings in the YYYY-MM-DD format. The Zod docs state that z.iso.date validates strings in the format YYYY-MM-DD and show examples like date.parse("2020-01-01") succeeding while "2020-1-1" fails, and they also note that ISO string validators are regex/format-based rather than full date-library semantic validation (and that z.date is for validating Date instances instead). [1][2] If you need additional “date-level” semantics (e.g., min/max/range comparisons beyond the string format), Zod’s z.iso.date is string-format validation only; the docs/comments around the API indicate you should use.refine (often with a date library) for those constraints. [3]

Citations:


🏁 Script executed:

# Find and check Zod version in package.json
find . -name "package.json" -type f | head -5 | xargs grep -l "zod" | head -1 | xargs cat | grep -A2 -B2 '"zod"'

Repository: nmi-agro/fdm

Length of output: 170


🏁 Script executed:

# Read the actual file at the specified lines
cat -n fdm-agents/src/agents/gerrit/schema.ts | sed -n '10,25p'

Repository: nmi-agro/fdm

Length of output: 724


🏁 Script executed:

# Check if z.iso.date is used anywhere else in the codebase
rg "z\.iso" --type ts --type js

Repository: nmi-agro/fdm

Length of output: 38


Enforce ISO date validation for p_app_date instead of free-form string.

Line 15–17 promises YYYY-MM-DD, but z.string() accepts any text. This breaks the structured contract and can propagate invalid plan dates. Use z.iso.date() to enforce the format.

✅ Proposed fix
-    p_app_date: z
-        .string()
-        .describe("Application date in YYYY-MM-DD format"),
+    p_app_date: z
+        .iso.date()
+        .describe("Application date in YYYY-MM-DD format"),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
p_app_date: z
.string()
.describe("Application date in YYYY-MM-DD format"),
p_app_date: z
.iso.date()
.describe("Application date in YYYY-MM-DD format"),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fdm-agents/src/agents/gerrit/schema.ts` around lines 15 - 17, The field
p_app_date is declared as z.string() but must be validated as an ISO YYYY-MM-DD
date; update the p_app_date schema (symbol: p_app_date) to enforce the
YYYY-MM-DD pattern instead of accepting any string—replace the free-form string
validator with a date-specific validator (e.g., use an ISO/date-aware Zod
validator or a strict regex/refine that matches /^\d{4}-\d{2}-\d{2}$/) and
include a clear .describe() or .refine() message for invalid values so the
schema rejects non-ISO dates.

Comment thread fdm-agents/src/index.ts
Comment thread fdm-app/.env.example Outdated
Comment thread fdm-app/app/routes/farm.$b_id_farm.$calendar.gerrit.tsx Outdated
Copy link
Copy Markdown
Contributor

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@fdm-app/.env.example`:
- Around line 148-150: The .env.example uses deprecated LANGCHAIN_* names;
replace LANGCHAIN_API_KEY with LANGSMITH_API_KEY and LANGCHAIN_PROJECT with
LANGSMITH_PROJECT (keeping LANGSMITH_TRACING as-is), and update any nearby
references or documentation strings that mention LANGCHAIN_API_KEY or
LANGCHAIN_PROJECT so the example and docs consistently use the official
LANGSMITH_* environment variable names.

In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.gerrit.tsx:
- Around line 720-725: The current guard checks only fieldsSummary.length === 0
which lets farms with only non-productive (buffer/nature/small-fragment) parcels
slip through; update the check to validate the filtered/eligible set instead:
compute the productive/eligible subset from fieldsSummary using the same
criteria used later (the non-productive filter), and if that productiveFields
array is empty return the same dataWithError(...) message; reference the
existing fieldsSummary variable and the dataWithError call so you replace the
initial length check with a check against the filtered productive set before
continuing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6855d15a-f08e-48ad-9855-3e06ae35b3eb

📥 Commits

Reviewing files that changed from the base of the PR and between 2e8d6c2 and b0b5f4b.

📒 Files selected for processing (13)
  • fdm-agents/.env.example
  • fdm-agents/src/agents/gerrit/agent.test.ts
  • fdm-agents/src/agents/gerrit/agent.ts
  • fdm-agents/src/agents/gerrit/schema.ts
  • fdm-agents/src/index.test.ts
  • fdm-agents/src/index.ts
  • fdm-agents/src/models/default.test.ts
  • fdm-agents/src/runners/one-shot.test.ts
  • fdm-agents/src/runners/one-shot.ts
  • fdm-agents/src/tools/fertilizer-planner/index.ts
  • fdm-app/.env.example
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.gerrit.tsx
  • fdm-calculator/src/nutrient-advice/index.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • fdm-agents/src/agents/gerrit/agent.test.ts
  • fdm-agents/src/agents/gerrit/schema.ts
  • fdm-agents/src/models/default.test.ts
  • fdm-agents/src/runners/one-shot.test.ts
  • fdm-agents/src/runners/one-shot.ts
  • fdm-agents/src/tools/fertilizer-planner/index.ts

Comment thread fdm-app/.env.example
Comment on lines +148 to +150
LANGSMITH_TRACING=true
LANGCHAIN_API_KEY=
LANGCHAIN_PROJECT= No newline at end of file
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For LangChain JS/LangGraph tracing with LangSmith in 2026, what are the official env vars for enabling tracing and API auth? Is it LANGSMITH_TRACING + LANGSMITH_API_KEY, and is LANGCHAIN_API_KEY still supported or deprecated?

💡 Result:

As of May 2026, the official environment variables for enabling LangSmith tracing and API authentication in LangChain JS/LangGraph are LANGSMITH_TRACING and LANGSMITH_API_KEY [1][2][3]. To enable tracing, you should set: export LANGSMITH_TRACING=true export LANGSMITH_API_KEY= Regarding the legacy LANGCHAIN_API_KEY variable: while it is still supported as an alias for backward compatibility, it is considered deprecated [4]. You should prioritize using the LANGSMITH_* prefixed variables for all new configurations [4]. Summary of key environment variables: - LANGSMITH_TRACING: Set to true to enable automatic tracing [1][5]. - LANGSMITH_API_KEY: Used for authentication with the LangSmith platform [5][2]. - LANGSMITH_PROJECT: Optional; specifies the project name (defaults to default if unset) [1][6]. - LANGSMITH_ENDPOINT: Optional; only required if you are using a self-hosted LangSmith instance (defaults to the hosted LangSmith cloud endpoint) [5][6]. The older aliases (LANGCHAIN_API_KEY, LANGCHAIN_ENDPOINT, LANGCHAIN_WORKSPACE_ID) remain functional but are not recommended for new implementations [4].

Citations:


Update LangSmith environment variable names to use official LANGSMITH_ prefixes.*

Line 149 uses LANGCHAIN_API_KEY, which is a deprecated alias. The official environment variables are LANGSMITH_API_KEY (for authentication) and LANGSMITH_PROJECT (for the project name). While the legacy names remain supported for backward compatibility as of May 2026, they are not recommended for new implementations.

Suggested patch
 LANGSMITH_TRACING=true
-LANGCHAIN_API_KEY=
+LANGSMITH_API_KEY=
-LANGCHAIN_PROJECT=
+LANGSMITH_PROJECT=
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
LANGSMITH_TRACING=true
LANGCHAIN_API_KEY=
LANGCHAIN_PROJECT=
LANGSMITH_TRACING=true
LANGSMITH_API_KEY=
LANGSMITH_PROJECT=
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 149-149: [UnorderedKey] The LANGCHAIN_API_KEY key should go before the LANGSMITH_TRACING key

(UnorderedKey)


[warning] 150-150: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 150-150: [UnorderedKey] The LANGCHAIN_PROJECT key should go before the LANGSMITH_TRACING key

(UnorderedKey)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fdm-app/.env.example` around lines 148 - 150, The .env.example uses
deprecated LANGCHAIN_* names; replace LANGCHAIN_API_KEY with LANGSMITH_API_KEY
and LANGCHAIN_PROJECT with LANGSMITH_PROJECT (keeping LANGSMITH_TRACING as-is),
and update any nearby references or documentation strings that mention
LANGCHAIN_API_KEY or LANGCHAIN_PROJECT so the example and docs consistently use
the official LANGSMITH_* environment variable names.

Comment on lines +720 to +725
if (fieldsSummary.length === 0) {
return dataWithError(
null,
"Er zijn geen percelen gevonden voor dit bedrijf. Voeg eerst percelen toe.",
)
}
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validate “eligible/productive” fields, not just total field count.

Line 720 currently checks fieldsSummary.length === 0, but the prompt later filters non-productive fields out. A farm with only buffer/nature/small-fragment fields can still pass this guard and then generate with an effectively empty productive set.

Suggested patch
- if (fieldsSummary.length === 0) {
+ const productiveFields = fieldsSummary.filter(
+     (f) =>
+         !f.b_bufferstrip &&
+         f.b_lu_croprotation !== "nature" &&
+         (f.b_area == null || f.b_area >= 0.5),
+ )
+ if (productiveFields.length === 0) {
     return dataWithError(
         null,
-        "Er zijn geen percelen gevonden voor dit bedrijf. Voeg eerst percelen toe.",
+        "Er zijn geen productieve percelen beschikbaar voor dit bedrijf in dit jaar.",
     )
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fdm-app/app/routes/farm`.$b_id_farm.$calendar.gerrit.tsx around lines 720 -
725, The current guard checks only fieldsSummary.length === 0 which lets farms
with only non-productive (buffer/nature/small-fragment) parcels slip through;
update the check to validate the filtered/eligible set instead: compute the
productive/eligible subset from fieldsSummary using the same criteria used later
(the non-productive filter), and if that productiveFields array is empty return
the same dataWithError(...) message; reference the existing fieldsSummary
variable and the dataWithError call so you replace the initial length check with
a check against the filtered productive set before continuing.

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.

Migrate fdm-agents from Google ADK to LangChain/LangGraph

1 participant