Skip to content

Conversation

@akitaSummer
Copy link
Contributor

@akitaSummer akitaSummer commented Nov 30, 2025

Checklist
  • npm test passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)
Description of change

Summary by CodeRabbit

Release Notes

  • New Features

    • Added HTTP streaming support for agents. Agents can now be exposed as HTTP endpoints with configurable paths and streaming response capabilities.
    • New configuration schema for defining agent endpoints with path, stream, and timeout settings.
  • Chores

    • Updated minimum Node.js requirement from 18.0.0 to 20.0.0.

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

@coderabbitai
Copy link

coderabbitai bot commented Nov 30, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This PR introduces HTTP endpoint exposure for LangChain agent graphs via a new lifecycle hook, enabling dynamic controller registration with support for both invoke and streaming response modes. Core graph objects are refactored to carry application context, type definitions are extended to support agent configuration, and Node.js engine requirements are updated to version 20.

Changes

Cohort / File(s) Summary
Agent HTTP Lifecycle Integration
plugin/langchain/app.ts, plugin/langchain/lib/graph/CompiledStateGraphObject.ts, plugin/langchain/lib/graph/CompiledStateGraphProto.ts
Integrates AgentHttpLoadUnitLifecycleHook into the LangChain module; passes app instance to CompiledStateGraphObject.createObject; extends constructor to accept Application; adds public app property and unitPath property; updates static factory method signature.
New Agent HTTP Lifecycle Hook
plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts
New file implementing LifecycleHook that dynamically registers HTTP GraphHttpController classes for module graphs configured with type 'http', transforming request bodies to LangChain message objects, and supporting both invoke and streaming (SSE) response modes.
Type Definitions & Configuration
plugin/langchain/typings/index.d.ts, plugin/langchain/test/fixtures/apps/langchain/app/modules/bar/module.yml
Introduces LangChainConfigSchema and LangChainConfigSchemaType for agent configuration (path, stream, type, timeout); adds test fixture configuration for FooGraph agent.
Configuration Changes
plugin/langchain/config/config.default.js, plugin/langchain/package.json
Removes bodyParser configuration; updates Node.js engine requirement from >=18.0.0 to >=20.0.0.
Tests & Utilities
plugin/langchain/test/llm.test.ts, plugin/controller/test/http/request.test.ts, core/langchain-decorator/package.json
Adds test for streaming graph endpoint with newline-delimited JSON response parsing; replaces it.only with it(); adds zod as devDependency.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant EggContainer as Egg Container
    participant LifecycleHook as AgentHttpLoadUnitLifecycleHook
    participant GraphHttpController as GraphHttpController
    participant CompiledStateGraph as CompiledStateGraph
    participant SSETransform as SSE Transform

    rect rgb(200, 220, 240)
    note over LifecycleHook: Lifecycle: preCreate Phase
    EggContainer->>LifecycleHook: preCreate(loadUnit)
    LifecycleHook->>LifecycleHook: getModuleConfig(loadUnit)
    LifecycleHook->>GraphHttpController: createGraphHttpControllerClass()
    LifecycleHook->>EggContainer: register prototype
    end

    rect rgb(240, 220, 200)
    note over Client: HTTP Request Phase
    Client->>GraphHttpController: POST /graph/stream {payload}
    GraphHttpController->>GraphHttpController: transform array elements to Messages
    end

    alt Streaming Enabled
        rect rgb(220, 240, 200)
        note over GraphHttpController: Streaming Flow
        GraphHttpController->>CompiledStateGraph: stream(input)
        CompiledStateGraph-->>GraphHttpController: async chunk iterator
        GraphHttpController->>SSETransform: pipe chunks
        SSETransform->>SSETransform: JSON.stringify + "data: " prefix
        SSETransform-->>Client: Server-Sent Events
        end
    else Non-Streaming
        rect rgb(240, 240, 200)
        note over GraphHttpController: Direct Invoke
        GraphHttpController->>CompiledStateGraph: invoke(input)
        CompiledStateGraph-->>GraphHttpController: result
        GraphHttpController-->>Client: JSON response
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • AgentHttpLoadUnitLifecycleHook.ts: New file with substantial logic density including message transformation, SSE handling, and dynamic controller registration; requires careful review of the HTTP endpoint implementation and streaming mechanics.
  • Core graph object modifications: Multiple changes to CompiledStateGraphObject and constructor signatures that affect object creation wiring; impacts initialization order and app context propagation.
  • Type definitions & configuration schema: Extends module configuration with new nested schema; verify compatibility with existing type definitions.
  • Test coverage: New streaming endpoint test and fixture configuration; verify message parsing and response format compliance.

Suggested reviewers

  • killagu
  • gxkl

Poem

🐰 Hops merrily through agent streams,
HTTP endpoints flow like dreams,
With SSE chunks in neat array,
Our graphs now serve requests the modern way!
Node twenty's here, no more delays,
Rabbit cheers for LangChain days! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'feat: add agent config' is vague and generic, lacking specificity about what agent configuration is being added or why. Consider a more descriptive title that clarifies the key feature, such as 'feat: add HTTP agent configuration for LangChain state graphs' or 'feat: enable agent graph streaming via HTTP endpoint configuration'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/agent_path

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e773e9f and 68d44d8.

📒 Files selected for processing (5)
  • core/langchain-decorator/package.json (1 hunks)
  • plugin/controller/test/http/request.test.ts (1 hunks)
  • plugin/langchain/app.ts (3 hunks)
  • plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts (1 hunks)
  • plugin/langchain/lib/graph/CompiledStateGraphObject.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/langchain-decorator/package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: Runner-macos (18)
  • GitHub Check: Runner-macos (20)
  • GitHub Check: Runner-ubuntu (16)
  • GitHub Check: Runner-ubuntu (20)
  • GitHub Check: Runner-macos (16)
  • GitHub Check: Runner-ubuntu (18)
  • GitHub Check: Analyze (typescript)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (16)
plugin/controller/test/http/request.test.ts (2)

33-33: Good practice: removed exclusive test modifier.

Changing it.only to it() ensures all tests in the suite run. Exclusive test modifiers should not be committed to the main branch.


31-32: > Likely an incorrect or invalid review comment.

plugin/langchain/app.ts (4)

10-10: LGTM: Import and field declaration follow the appropriate pattern.

The import and field declaration for AgentHttpLoadUnitLifecycleHook are correctly structured. The field is not marked readonly (unlike other lifecycle hooks), which is appropriate since it's initialized in configWillLoad rather than the constructor and conditionally cleaned up in beforeClose.

Also applies to: 18-18


30-31: LGTM: Lifecycle hook initialization and registration.

The AgentHttpLoadUnitLifecycleHook is correctly instantiated with moduleConfigs and registered during the configWillLoad phase, following the established pattern for other lifecycle hooks in this class.


34-34: LGTM: App instance passed to object factory.

The app instance is correctly passed to CompiledStateGraphObject.createObject(), aligning with the updated factory signature that enables graph objects to carry application context for HTTP endpoint registration.


43-45: LGTM: Defensive cleanup with null check.

The conditional check for #agentHttpLoadUnitHook before deletion is appropriate, as the hook is initialized in configWillLoad rather than the constructor. This guards against scenarios where beforeClose might execute before initialization completes.

plugin/langchain/lib/graph/CompiledStateGraphObject.ts (4)

14-14: LGTM: Application type import.

The Application type import from the Egg framework is correctly added to support the new app property and constructor parameter.


26-26: LGTM: App property declaration.

The readonly app: Application property is correctly added to store the application instance, enabling graph objects to carry app context for HTTP endpoint registration.


28-28: LGTM: Constructor updated to accept app instance.

The constructor signature is correctly updated to accept the app parameter, and it's properly assigned to this.app. This enables the graph object to access the application context.

Also applies to: 35-35


128-134: LGTM: Factory method refactored to capture app context.

The createObject method is correctly refactored to accept an app parameter and return a factory function. This closure-based pattern cleanly captures the app instance and enables object creation with application context.

plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts (6)

1-26: LGTM: Imports and type definitions.

The imports are comprehensive and appropriate for the lifecycle hook functionality. The ModuleConfigHolder interface and ValueOf<T> utility type are well-defined and used throughout the file.


27-32: LGTM: Class declaration and constructor.

The AgentHttpLoadUnitLifecycleHook correctly implements the LifecycleHook interface and properly initializes the moduleConfigs in its constructor.


63-83: LGTM: Message transformation logic.

The request body transformation correctly converts array-valued entries with a role field into appropriate LangChain message objects (HumanMessage, AIMessage, SystemMessage, ToolMessage). The error handling for unknown message types is appropriate.


93-123: LGTM: SSE streaming implementation.

The Server-Sent Events implementation is well-structured:

  • Correct SSE headers are set
  • Transform stream properly formats chunks as data: {json}\n\n
  • Handles different chunk types (string, object, other)
  • Readable stream is correctly piped through the transform

Note: There's a minor type handling issue in the error callback (see separate comment).


133-139: LGTM: Module config retrieval.

The #getModuleConfig method safely extracts the LangChain agent configurations using optional chaining and returns an empty array when no configuration exists. The implementation is clean and defensive.


127-130: LGTM: Decorator application.

The HTTPController and ConfigSourceQualifier decorators are correctly applied to the dynamically created GraphHttpController class, properly binding the controller to its module context.


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.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @akitaSummer, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the LangChain plugin by enabling the configuration and exposure of LangChain agents as HTTP endpoints. It provides a flexible way to integrate compiled state graphs into web applications, supporting streaming capabilities and configurable timeouts. The changes involve updating the object lifecycle to provide application context, introducing new configuration schemas, and implementing the necessary routing and request handling logic.

Highlights

  • Agent Configuration: Introduced a new configuration mechanism for LangChain agents, allowing them to be defined and exposed as HTTP endpoints with options for streaming and timeouts.
  • Application Context Injection: Modified the CompiledStateGraphObject creation process to inject the application instance, enabling it to access application-level resources like the router.
  • HTTP Agent Handler: Implemented a robust HTTP handler for compiled state graphs, supporting both standard and Server-Sent Events (SSE) streaming responses, message type mapping, and configurable timeouts.
  • Node.js Version Update: The minimum required Node.js version for the plugin has been updated from >=18.0.0 to >=20.0.0.
  • New Test Case: Added an integration test to validate the functionality of the new agent controller, specifically focusing on streaming HTTP responses.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds a new feature to configure agents and expose them as HTTP endpoints. The implementation is mostly solid, but there are a few critical issues to address. I've found a potential race condition due to incorrect thread_id generation, a bug in config handling, and a potential crash from an unchecked optional property. I've also included suggestions to improve encapsulation and remove reliance on private framework APIs.


const defaultConfig = {
configurable: {
thread_id: process.pid.toString(),
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

Using process.pid for thread_id is incorrect in a web server context. All concurrent requests handled by the same process will share the same thread_id, leading to mixed-up conversation states and data corruption. A unique ID must be generated for each request. Using crypto.randomUUID() is a robust solution. Note: you'll need to add import crypto from 'node:crypto'; at the top of the file.

Suggested change
thread_id: process.pid.toString(),
thread_id: crypto.randomUUID(),


async boundByConfig() {
const config = ModuleConfigUtil.loadModuleConfigSync(this.proto.unitPath) as ModuleConfig | undefined;
const agents: LangChainConfigSchemaType['agents'] = config?.langchain?.agents ?? [];
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The default value for agents is [], but its type LangChainConfigSchemaType['agents'] is Record<string, ...>, which is an object. The correct default value should be an empty object {} to match the type definition. This will prevent potential issues and align the code with its declared type.

Suggested change
const agents: LangChainConfigSchemaType['agents'] = config?.langchain?.agents ?? [];
const agents: LangChainConfigSchemaType['agents'] = config?.langchain?.agents ?? {};

Comment on lines 96 to 109
if ((type ?? '').toLocaleLowerCase() === 'http') {
const router = this.app.router;
const regExp = pathToRegexp(methodRealPath!, {
sensitive: true,
});
const handler = this.createHttpHandler(stream, timeout);
Reflect.apply(router.post, router,
[ `${this.graphName}.Invoke`, methodRealPath, ...[], handler ]);
this.app.rootProtoManager.registerRootProto('AgentControllerInvoke', (ctx: Context) => {
if (regExp.test(ctx.path)) {
return this.proto;
}
}, '');
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The path property in the agent configuration is optional, so methodRealPath can be undefined. Using the non-null assertion operator (!) on methodRealPath will cause a runtime error if the path is not provided in the configuration. It's safer to check for the existence of methodRealPath in the conditional statement.

Suggested change
if ((type ?? '').toLocaleLowerCase() === 'http') {
const router = this.app.router;
const regExp = pathToRegexp(methodRealPath!, {
sensitive: true,
});
const handler = this.createHttpHandler(stream, timeout);
Reflect.apply(router.post, router,
[ `${this.graphName}.Invoke`, methodRealPath, ...[], handler ]);
this.app.rootProtoManager.registerRootProto('AgentControllerInvoke', (ctx: Context) => {
if (regExp.test(ctx.path)) {
return this.proto;
}
}, '');
}
if ((type ?? '').toLocaleLowerCase() === 'http' && methodRealPath) {
const router = this.app.router;
const regExp = pathToRegexp(methodRealPath, {
sensitive: true,
});
const handler = this.createHttpHandler(stream, timeout);
Reflect.apply(router.post, router,
[ `${this.graphName}.Invoke`, methodRealPath, ...[], handler ]);
this.app.rootProtoManager.registerRootProto('AgentControllerInvoke', (ctx: Context) => {
if (regExp.test(ctx.path)) {
return this.proto;
}
}, '');
}

readonly ctx: EggContext;
readonly daoName: string;
private _obj: object;
_obj: object;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The _obj property was changed from public (default access modifier) to private. This is a good practice for encapsulation. However, in this PR, it has been changed back to public. Since it's only accessed within the class's methods (via a self closure in createHttpHandler), it can and should remain private to maintain proper encapsulation.

Suggested change
_obj: object;
private _obj: object;

Comment on lines 160 to 169
const explicitStatus = (ctx.response as any)._explicitStatus;

if (
// has body
body != null ||
// status is not set and has no body
// code should by 204
// https://github.com/koajs/koa/blob/master/lib/response.js#L140
!explicitStatus
) {
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Relying on the private property _explicitStatus from Koa's response object is fragile and may break with future updates to the framework. This logic can be simplified by just setting ctx.body = body and letting Koa handle the response status correctly (e.g., setting status 204 for an empty body). This would make the code more robust and easier to maintain.

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 (2)
plugin/langchain/test/llm.test.ts (1)

120-125: Remove redundant try-catch block.

The catch block simply rethrows the error without any transformation or logging, making it unnecessary.

           if (line.startsWith('data: ')) {
             const data = line.slice(6);
-            try {
-              const parsed = JSON.parse(data);
-              messages.push(parsed);
-            } catch (e) {
-              throw e;
-            }
+            const parsed = JSON.parse(data);
+            messages.push(parsed);
           }
plugin/langchain/lib/graph/CompiledStateGraphObject.ts (1)

162-204: Duplicate ctx.body assignment when not streaming.

Line 170 sets ctx.body = body, then when stream is false, line 202 sets it again. The first assignment is redundant.

       if (
         body != null ||
         !explicitStatus
       ) {
-        ctx.body = body;
         if (stream) {
           ctx.set({
             'content-type': 'text/event-stream',
             'cache-control': 'no-cache',
             'transfer-encoding': 'chunked',
             'X-Accel-Buffering': 'no',
           });
           // ... transform stream logic ...
           ctx.body = Readable.fromWeb(body as any, { objectMode: true }).pipe(transformStream);
         } else {
           ctx.body = body;
         }
       }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2684270 and 883b9f1.

📒 Files selected for processing (8)
  • plugin/langchain/app.ts (1 hunks)
  • plugin/langchain/lib/graph/CompiledStateGraphObject.ts (5 hunks)
  • plugin/langchain/lib/graph/CompiledStateGraphProto.ts (1 hunks)
  • plugin/langchain/package.json (1 hunks)
  • plugin/langchain/test/fixtures/apps/langchain/app/modules/bar/module.yml (1 hunks)
  • plugin/langchain/test/fixtures/apps/langchain/config/config.default.js (0 hunks)
  • plugin/langchain/test/llm.test.ts (1 hunks)
  • plugin/langchain/typings/index.d.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • plugin/langchain/test/fixtures/apps/langchain/config/config.default.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: Runner-macos (18)
  • GitHub Check: Runner-ubuntu (20)
  • GitHub Check: Runner-ubuntu (18)
  • GitHub Check: Analyze (typescript)
  • GitHub Check: Runner-macos (20)
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Runner-ubuntu (16)
  • GitHub Check: Runner-macos (16)
🔇 Additional comments (7)
plugin/langchain/test/fixtures/apps/langchain/app/modules/bar/module.yml (1)

21-26: Test fixture configuration looks correct.

The agent configuration for FooGraph aligns with the new LangChainConfigSchema and will exercise the streaming HTTP endpoint binding in tests.

plugin/langchain/lib/graph/CompiledStateGraphProto.ts (1)

27-27: LGTM!

The unitPath property is correctly initialized from the load unit and enables module config loading in CompiledStateGraphObject.boundByConfig().

Also applies to: 35-35

plugin/langchain/app.ts (1)

30-30: LGTM!

Correctly passes the application instance to the factory, enabling HTTP route registration in CompiledStateGraphObject.

plugin/langchain/typings/index.d.ts (1)

112-130: Schema definition looks good.

The agent configuration schema correctly defines the optional fields for HTTP binding with streaming support.

plugin/langchain/lib/graph/CompiledStateGraphObject.ts (1)

254-260: Factory pattern update looks correct.

The static factory now accepts the application context and returns a properly bound factory function for object creation.

plugin/langchain/test/llm.test.ts (1)

80-133: Test coverage for streaming endpoint looks good.

The test correctly validates SSE streaming behavior with proper resource cleanup via reader.releaseLock() in the finally block. The hardcoded assertion messages.length === 3 is well-founded: the test uses a controlled fixture with the deterministic FooGraph that has a predictable execution path (START → ACTION producing AIMessage with tool_calls → TOOLS → ACTION processing ToolMessage → END), with each step producing one streamed message. The test setup properly mocks the application via egg-mock with fixtures and starts an SSE server for the MCP client integration.

plugin/langchain/package.json (1)

54-56: Node.js version bump to >=20.0.0 is required by @langchain/langgraph.

@langchain/langgraph v1.0.0 requires Node.js 20 or higher, which justifies this engine constraint change. The test file confirms this requirement with a runtime check for Node versions above 19.

Comment on lines 89 to 111
async boundByConfig() {
const config = ModuleConfigUtil.loadModuleConfigSync(this.proto.unitPath) as ModuleConfig | undefined;
const agents: LangChainConfigSchemaType['agents'] = config?.langchain?.agents ?? [];
const configName = `${this.graphName.slice(0, 1).toUpperCase()}${this.graphName.slice(1)}`;
if (Object.keys(agents).includes(configName)) {
const { path: methodRealPath, type, stream, timeout } = agents[configName];

if ((type ?? '').toLocaleLowerCase() === 'http') {
const router = this.app.router;
const regExp = pathToRegexp(methodRealPath!, {
sensitive: true,
});
const handler = this.createHttpHandler(stream, timeout);
Reflect.apply(router.post, router,
[ `${this.graphName}.Invoke`, methodRealPath, ...[], handler ]);
this.app.rootProtoManager.registerRootProto('AgentControllerInvoke', (ctx: Context) => {
if (regExp.test(ctx.path)) {
return this.proto;
}
}, '');
}
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing validation for required path field.

If path is undefined in the config, pathToRegexp(methodRealPath!) will throw a runtime error. Add validation before using the path.

       if ((type ?? '').toLocaleLowerCase() === 'http') {
+        if (!methodRealPath) {
+          throw new Error(`Agent ${configName} is configured as HTTP but missing 'path' field`);
+        }
         const router = this.app.router;
         const regExp = pathToRegexp(methodRealPath!, {
📝 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
async boundByConfig() {
const config = ModuleConfigUtil.loadModuleConfigSync(this.proto.unitPath) as ModuleConfig | undefined;
const agents: LangChainConfigSchemaType['agents'] = config?.langchain?.agents ?? [];
const configName = `${this.graphName.slice(0, 1).toUpperCase()}${this.graphName.slice(1)}`;
if (Object.keys(agents).includes(configName)) {
const { path: methodRealPath, type, stream, timeout } = agents[configName];
if ((type ?? '').toLocaleLowerCase() === 'http') {
const router = this.app.router;
const regExp = pathToRegexp(methodRealPath!, {
sensitive: true,
});
const handler = this.createHttpHandler(stream, timeout);
Reflect.apply(router.post, router,
[ `${this.graphName}.Invoke`, methodRealPath, ...[], handler ]);
this.app.rootProtoManager.registerRootProto('AgentControllerInvoke', (ctx: Context) => {
if (regExp.test(ctx.path)) {
return this.proto;
}
}, '');
}
}
}
async boundByConfig() {
const config = ModuleConfigUtil.loadModuleConfigSync(this.proto.unitPath) as ModuleConfig | undefined;
const agents: LangChainConfigSchemaType['agents'] = config?.langchain?.agents ?? [];
const configName = `${this.graphName.slice(0, 1).toUpperCase()}${this.graphName.slice(1)}`;
if (Object.keys(agents).includes(configName)) {
const { path: methodRealPath, type, stream, timeout } = agents[configName];
if ((type ?? '').toLocaleLowerCase() === 'http') {
if (!methodRealPath) {
throw new Error(`Agent ${configName} is configured as HTTP but missing 'path' field`);
}
const router = this.app.router;
const regExp = pathToRegexp(methodRealPath!, {
sensitive: true,
});
const handler = this.createHttpHandler(stream, timeout);
Reflect.apply(router.post, router,
[ `${this.graphName}.Invoke`, methodRealPath, ...[], handler ]);
this.app.rootProtoManager.registerRootProto('AgentControllerInvoke', (ctx: Context) => {
if (regExp.test(ctx.path)) {
return this.proto;
}
}, '');
}
}
}
🤖 Prompt for AI Agents
In plugin/langchain/lib/graph/CompiledStateGraphObject.ts around lines 89 to
111, the code assumes agents[configName].path (methodRealPath) is defined and
passes it to pathToRegexp which will throw if undefined; add an explicit check
that methodRealPath is a non-empty string before using it, and if it's
missing/invalid either log a clear warning/error and skip registering the
route/handler (i.e., return or continue) so runtime crashes are avoided; remove
the non-null assertion usage and guard all subsequent uses (regExp creation,
router.post call, and proto registration) behind this validation.


async boundByConfig() {
const config = ModuleConfigUtil.loadModuleConfigSync(this.proto.unitPath) as ModuleConfig | undefined;
const agents: LangChainConfigSchemaType['agents'] = config?.langchain?.agents ?? [];
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incorrect default value type for agents.

agents is typed as a Record<string, ...> but defaults to [] (an array). This should default to {}.

-    const agents: LangChainConfigSchemaType['agents'] = config?.langchain?.agents ?? [];
+    const agents: LangChainConfigSchemaType['agents'] = config?.langchain?.agents ?? {};
📝 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
const agents: LangChainConfigSchemaType['agents'] = config?.langchain?.agents ?? [];
const agents: LangChainConfigSchemaType['agents'] = config?.langchain?.agents ?? {};
🤖 Prompt for AI Agents
In plugin/langchain/lib/graph/CompiledStateGraphObject.ts around line 91, the
variable `agents` is declared with type LangChainConfigSchemaType['agents'] (a
Record<string,...>) but is defaulting to an empty array ([]); change the default
to an empty object ({}) so its runtime value matches its declared Record type
(e.g., const agents = config?.langchain?.agents ?? {}), ensuring
type-compatibility and avoiding runtime type mismatches.

Comment on lines 141 to 145
const defaultConfig = {
configurable: {
thread_id: process.pid.toString(),
},
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Thread ID using process.pid is not unique per request.

Using process.pid as thread_id means all concurrent requests share the same conversation thread, which can cause data corruption or unexpected behavior when multiple requests run simultaneously. Consider using a request-specific identifier (e.g., from request headers or a generated UUID).

       const defaultConfig = {
         configurable: {
-          thread_id: process.pid.toString(),
+          thread_id: ctx.request.get('x-thread-id') || ctx.traceId || crypto.randomUUID(),
         },
       };

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In plugin/langchain/lib/graph/CompiledStateGraphObject.ts around lines 141 to
145, the default thread_id is set to process.pid which is global to the process
and not unique per request; change the code to accept a request-scoped
identifier (e.g., pass thread_id into the constructor or method from request
headers/context) and fall back to a generated UUID (use a UUID v4 generator)
only when no request id is provided, ensuring each request/conversation gets a
unique thread_id.

Comment on lines 195 to 196
} catch (error) {
callback(error);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

TypeScript type error in error callback.

The error variable is typed as unknown in the catch block, but callback expects Error | null | undefined. Add a type assertion.

             } catch (error) {
-              callback(error);
+              callback(error instanceof Error ? error : new Error(String(error)));
             }
🤖 Prompt for AI Agents
In plugin/langchain/lib/graph/CompiledStateGraphObject.ts around lines 195-196,
the catch block passes the caught variable typed as unknown into callback which
expects Error | null | undefined; change the call to assert or convert the
unknown to an Error before passing it (for example use a type assertion like
casting to Error or wrap non-Error values in new Error(String(error)) so
callback receives Error | null | undefined).

declare module '@eggjs/tegg' {
export type LangChainModuleConfig = {
ChatModel?: ChatModelConfigModuleConfigType;
langchain?: LangChainConfigSchema;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Type error: Use the type alias instead of the schema constant.

LangChainConfigSchema is a runtime schema object, not a type. The property should use LangChainConfigSchemaType to represent the inferred TypeScript type.

   export type LangChainModuleConfig = {
     ChatModel?: ChatModelConfigModuleConfigType;
-    langchain?: LangChainConfigSchema;
+    langchain?: LangChainConfigSchemaType;
   };
📝 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
langchain?: LangChainConfigSchema;
langchain?: LangChainConfigSchemaType;
🤖 Prompt for AI Agents
In plugin/langchain/typings/index.d.ts around line 138, the declaration uses the
runtime schema constant LangChainConfigSchema where a TypeScript type is
required; replace LangChainConfigSchema with the inferred type alias
LangChainConfigSchemaType so the langchain? property is typed correctly,
updating the type annotation to use LangChainConfigSchemaType instead of the
schema constant.

const config = ModuleConfigUtil.loadModuleConfigSync(this.proto.unitPath) as ModuleConfig | undefined;
const agents: LangChainConfigSchemaType['agents'] = config?.langchain?.agents ?? [];
const configName = `${this.graphName.slice(0, 1).toUpperCase()}${this.graphName.slice(1)}`;
if (Object.keys(agents).includes(configName)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

  • 能不能用 http controller 的方式去包一层? 不要手写一遍 http 的逻辑
  • http 要和 graph 解耦

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

🧹 Nitpick comments (3)
plugin/langchain/lib/graph/CompiledStateGraphObject.ts (1)

23-23: Consider keeping _obj private for encapsulation.

The _obj property was previously private and is only accessed via the public obj getter (line 124-126). Changing it to public exposes internal state unnecessarily. Based on past review comments, this should remain private.

-  _obj: object;
+  private _obj: object;
plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts (2)

63-83: Consider extracting message conversion logic to a separate helper.

The message type conversion logic is complex and could be extracted to improve readability and testability. Also, consider handling undefined roles more gracefully instead of throwing.

function convertToLangChainMessage(obj: { role: string; [key: string]: any }) {
  switch (obj.role) {
    case 'human':
      return new HumanMessage(obj);
    case 'ai':
      return new AIMessage(obj);
    case 'system':
      return new SystemMessage(obj);
    case 'tool':
      return new ToolMessage(obj);
    default:
      throw new Error(`Unknown message type: ${obj.role}`);
  }
}

59-59: Add type annotations to method parameters.

The invoke method parameters ctx and args lack type annotations, which reduces type safety.

-      async invoke(@Context() ctx, @HTTPBody() args) {
+      async invoke(@Context() ctx: EggContext, @HTTPBody() args: Record<string, unknown>) {

You'll need to import EggContext from the appropriate module.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 883b9f1 and e6a57e1.

📒 Files selected for processing (7)
  • core/controller-decorator/package.json (1 hunks)
  • plugin/controller/package.json (1 hunks)
  • plugin/controller/test/http/request.test.ts (1 hunks)
  • plugin/langchain/app.ts (3 hunks)
  • plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts (1 hunks)
  • plugin/langchain/lib/graph/CompiledStateGraphObject.ts (3 hunks)
  • plugin/mcp-proxy/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • plugin/langchain/app.ts
🧰 Additional context used
🧬 Code graph analysis (2)
plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts (7)
core/types/common/ModuleConfig.ts (1)
  • ModuleReference (1-6)
core/types/lifecycle/LifecycleHook.ts (1)
  • LifecycleHook (12-19)
core/metadata/src/model/ProtoDescriptorHelper.ts (1)
  • ProtoDescriptorHelper (21-181)
core/metadata/src/factory/EggPrototypeCreatorFactory.ts (1)
  • EggPrototypeCreatorFactory (13-115)
plugin/langchain/typings/index.d.ts (1)
  • LangChainConfigSchemaType (133-133)
core/controller-decorator/src/decorator/http/HTTPMethod.ts (1)
  • HTTPMethod (7-23)
core/core-decorator/src/decorator/ConfigSource.ts (1)
  • ConfigSourceQualifier (5-9)
plugin/langchain/lib/graph/CompiledStateGraphObject.ts (4)
plugin/langchain/typings/index.d.ts (1)
  • Application (152-154)
core/types/core-decorator/model/InjectObjectInfo.ts (1)
  • EggObjectName (1-1)
core/metadata/src/model/graph/GlobalGraph.ts (1)
  • proto (143-159)
core/runtime/src/model/ContextHandler.ts (1)
  • ContextHandler (6-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: Analyze (typescript)
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Runner-macos (20)
  • GitHub Check: Runner-ubuntu (18)
  • GitHub Check: Runner-macos (18)
  • GitHub Check: Runner-macos (16)
  • GitHub Check: Runner-ubuntu (20)
  • GitHub Check: Runner-ubuntu (16)
🔇 Additional comments (7)
plugin/mcp-proxy/package.json (1)

63-63: Consistent zod v4 upgrade.

Same major version upgrade as other packages in this PR. Ensure all zod schema definitions and validations in this package are compatible with v4 APIs.

core/controller-decorator/package.json (1)

51-51: Core package zod upgrade aligns with repo-wide update.

As a core package, this upgrade will propagate to dependent packages. Verify compatibility across the codebase.

plugin/controller/test/http/request.test.ts (1)

33-33: Good fix: removing .only from test.

Removing .only ensures the full test suite runs, not just this isolated test.

plugin/langchain/lib/graph/CompiledStateGraphObject.ts (2)

26-36: LGTM: App injection pattern.

The constructor and property additions for app: Application are well-structured, enabling the graph object to access application context for HTTP controller registration.


128-134: Clean factory function pattern.

The curried createObject(app) returning an async factory function is a good approach for injecting the application instance while maintaining the existing object creation signature.

plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts (1)

34-50: LGTM: Lifecycle hook structure.

The preCreate hook properly iterates through module configurations and dynamically registers HTTP controllers for agents with type: 'http'. The prototype descriptor creation and registration flow is correct.

plugin/controller/package.json (1)

67-67: zod v4 is already correctly integrated in this package.

The codebase is using the v4 subpath import (import * as z from 'zod/v4') with v4-compatible APIs (e.g., z.string().describe()). No v3-style breaking change patterns were detected in the codebase. The migration to zod v4 is complete and compatible.

Comment on lines +54 to +58
@HTTPMethod({
path: config.path!,
method: HTTPMethodEnum.POST,
timeout: config.timeout,
})
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add validation for config.path before using non-null assertion.

The path property in the agent configuration is optional, so config.path can be undefined. Using the non-null assertion (!) will cause a runtime error if the path is not provided. This should be validated before reaching this point.

The validation should occur in preCreate before calling #createGraphHttpControllerClass:

       for (const [ graphName, config ] of moduleConfigs) {
-        if (config?.type === 'http') {
+        if (config?.type === 'http' && config.path) {
           const GraphHttpController = this.#createGraphHttpControllerClass(loadUnit, graphName, config);

Or add an explicit error if path is missing:

   #createGraphHttpControllerClass(loadUnit: LoadUnit, graphName: string, config: ValueOf<LangChainConfigSchemaType['agents']>) {
+    if (!config.path) {
+      throw new Error(`Agent ${graphName} is configured as HTTP but missing 'path' field`);
+    }
     class GraphHttpController {

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts around lines 54
to 58, the code uses config.path! with a non-null assertion but config.path is
optional; add validation in preCreate (before calling
#createGraphHttpControllerClass) to check that config.path is defined and is a
non-empty string, and if not either supply a default path or throw a clear,
descriptive error (e.g., throw new Error("Agent HTTP config.path is required"))
so that #createGraphHttpControllerClass never receives an undefined path; ensure
the validated path is passed into the decorator call instead of using the
non-null assertion.

Comment on lines +85 to +89
const defaultConfig = {
configurable: {
thread_id: process.pid.toString(),
},
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: thread_id using process.pid is not unique per request.

Using process.pid as thread_id means all concurrent requests within the same process share the same conversation thread. This causes conversation state corruption when multiple requests run simultaneously. Use a request-specific identifier instead.

         const defaultConfig = {
           configurable: {
-            thread_id: process.pid.toString(),
+            thread_id: ctx.request.get('x-thread-id') || ctx.traceId || crypto.randomUUID(),
           },
         };

Add the import at the top of the file:

import crypto from 'node:crypto';
🤖 Prompt for AI Agents
In plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts around lines 85
to 89, the defaultConfig sets configurable.thread_id to process.pid which is
shared across all requests; replace this with a request-unique identifier by
importing crypto (import crypto from 'node:crypto') at the top and setting
configurable.thread_id = crypto.randomUUID() (or
crypto.randomBytes(16).toString('hex')) when building the config per request
rather than using process.pid, ensuring the config is created inside the request
lifecycle so each request gets its own thread_id.

Comment on lines +117 to +119
} catch (error) {
callback(error);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

TypeScript type error: error is unknown in catch block.

The error variable is typed as unknown in the catch block, but callback expects Error | null | undefined. Add proper type handling.

             } catch (error) {
-              callback(error);
+              callback(error instanceof Error ? error : new Error(String(error)));
             }
📝 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
} catch (error) {
callback(error);
}
} catch (error) {
callback(error instanceof Error ? error : new Error(String(error)));
}
🤖 Prompt for AI Agents
In plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts around lines 117
to 119, the catch block receives an unknown-typed error but calls callback with
that value; change the handling to narrow the type before calling callback: test
if error is an instance of Error and pass it directly, otherwise create a new
Error(String(error)) (or wrap with a descriptive message) and pass that to
callback so the callback receives Error | null | undefined as expected.

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

♻️ Duplicate comments (5)
plugin/langchain/lib/graph/CompiledStateGraphObject.ts (1)

23-23: _obj visibility changed from private - consider keeping it private.

The _obj property was previously private. The public getter obj (line 124-126) already provides access to this property. Making _obj public breaks encapsulation without apparent necessity.

This was flagged in a past review. Keep _obj private:

-  _obj: object;
+  private _obj: object;
plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts (4)

34-49: Add validation for config.path before creating controller.

The config.path property is optional, but the code uses config.path! (non-null assertion) in the decorator. This will cause a runtime error if path is undefined.

This was flagged in past reviews. Add validation in preCreate:

       for (const [ graphName, config ] of moduleConfigs) {
-        if (config?.type === 'http') {
+        if (config?.type === 'http' && config.path) {
           const GraphHttpController = this.#createGraphHttpControllerClass(loadUnit, graphName, config);

85-89: Critical: thread_id using process.pid causes conversation state corruption.

Using process.pid as thread_id means all concurrent requests within the same process share the same conversation thread. This causes conversation state corruption when multiple users or requests run simultaneously.

This was flagged as critical in past reviews. Use a request-unique identifier:

         const defaultConfig = {
           configurable: {
-            thread_id: process.pid.toString(),
+            thread_id: ctx.request.get('x-thread-id') || ctx.traceId || crypto.randomUUID(),
           },
         };

117-119: TypeScript type error: error is unknown in catch block.

The error variable caught in the try-catch is typed as unknown, but callback expects Error | null | undefined. This causes a TypeScript compilation error.

This was flagged in past reviews. Add type handling:

             } catch (error) {
-              callback(error);
+              callback(error instanceof Error ? error : new Error(String(error)));
             }

1-16: Missing crypto import for UUID generation.

The file will need crypto imported to fix the thread_id issue flagged below.

Add import at top of file:

+import crypto from 'node:crypto';
 import {
   ConfigSourceQualifier,
🧹 Nitpick comments (2)
plugin/langchain/app.ts (1)

18-18: Consider making the field type explicit for clarity.

The #agentHttpLoadUnitHook field lacks the readonly modifier unlike sibling hooks, which is correct since it's assigned in configWillLoad. However, adding an explicit type annotation would improve readability.

-  #agentHttpLoadUnitHook: AgentHttpLoadUnitLifecycleHook;
+  #agentHttpLoadUnitHook: AgentHttpLoadUnitLifecycleHook | undefined;

This makes the potentially-undefined state explicit before configWillLoad runs.

plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts (1)

63-83: Potential runtime error when accessing empty array.

Line 64 accesses value[0] without checking if the array is non-empty. If value is an empty array, value[0] is undefined, and typeof undefined === 'object' is false, so it would skip the block. However, this edge case handling is implicit and could be made clearer.

Make the intent explicit:

-        if (Array.isArray(value) && typeof value[0] === 'object') {
+        if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'object') {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e6a57e1 and e773e9f.

📒 Files selected for processing (8)
  • core/controller-decorator/package.json (1 hunks)
  • core/langchain-decorator/package.json (1 hunks)
  • plugin/controller/package.json (1 hunks)
  • plugin/controller/test/http/request.test.ts (1 hunks)
  • plugin/langchain/app.ts (3 hunks)
  • plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts (1 hunks)
  • plugin/langchain/lib/graph/CompiledStateGraphObject.ts (3 hunks)
  • plugin/mcp-proxy/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • plugin/controller/test/http/request.test.ts
  • plugin/mcp-proxy/package.json
🧰 Additional context used
🧬 Code graph analysis (3)
plugin/langchain/lib/graph/CompiledStateGraphObject.ts (6)
plugin/langchain/typings/index.d.ts (1)
  • Application (152-154)
core/types/core-decorator/model/InjectObjectInfo.ts (1)
  • EggObjectName (1-1)
plugin/langchain/lib/graph/CompiledStateGraphProto.ts (1)
  • CompiledStateGraphProto (17-66)
core/runtime/src/model/ContextHandler.ts (1)
  • ContextHandler (6-19)
core/lifecycle/src/IdenticalObject.ts (1)
  • IdenticalUtil (3-39)
core/types/metadata/model/EggPrototype.ts (1)
  • EggPrototype (117-154)
plugin/langchain/app.ts (2)
plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts (1)
  • AgentHttpLoadUnitLifecycleHook (27-140)
plugin/langchain/lib/graph/CompiledStateGraphProto.ts (1)
  • CompiledStateGraphProto (17-66)
plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts (4)
core/types/common/ModuleConfig.ts (1)
  • ModuleReference (1-6)
plugin/langchain/typings/index.d.ts (1)
  • LangChainConfigSchemaType (133-133)
core/controller-decorator/src/decorator/http/HTTPMethod.ts (1)
  • HTTPMethod (7-23)
core/runtime/src/factory/EggContainerFactory.ts (1)
  • EggContainerFactory (18-88)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: Runner-macos (18)
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Analyze (typescript)
🔇 Additional comments (10)
core/controller-decorator/package.json (1)

51-51: Verify zod v4 compatibility and Node engine version consistency.

Same concern as plugin/controller: zod upgraded to ^4 (line 51, production dependency), but Node engine remains at ">=14.0.0" (lines 36-37). Confirm zod v4 compatibility and align Node engine requirements across the PR if moving to >=20.0.0.

Also applies to: 36-37

core/langchain-decorator/package.json (1)

60-61: Clarify zod placement as devDependency and verify Node engine consistency.

Zod is added as a devDependency (lines 60-61), whereas in plugin/controller (line 67) and core/controller-decorator (line 51) it's a production dependency. Clarify whether zod should be in dependencies or devDependencies for this package. Additionally, Node engine remains at ">=14.0.0" (lines 35-36); align with the broader PR if moving to >=20.0.0.

Also applies to: 35-36

plugin/controller/package.json (1)

67-67: Verify zod v4 compatibility and Node engine version alignment.

The zod dependency is upgraded from ^3.24.4 to ^4 (line 67), a major version bump. Review the zod v4 migration guide to confirm compatibility with existing validation schemas. Additionally, this file declares Node engine ">=14.0.0" (lines 47-48), but the PR appears to update Node requirements to >=20.0.0 elsewhere. Align the Node engine version if the broader PR is standardizing to >=20.0.0.

plugin/langchain/app.ts (2)

29-35: LGTM! Lifecycle registration and factory wiring look correct.

The hook is registered before other object lifecycle hooks, which ensures the HTTP controllers are created during load unit initialization. The createObject(this.#app) call correctly passes the application instance to the factory.


43-45: Good defensive check, but cleanup order may not match registration order.

The conditional check for #agentHttpLoadUnitHook is good. However, lifecycles are typically cleaned up in reverse order of registration (LIFO). Currently, #agentHttpLoadUnitHook is registered second in configWillLoad (after constructor registers #graphLoadUnitHook), but it's deleted first in beforeClose.

Consider whether the cleanup order matters for this lifecycle. If the order is significant, ensure it follows LIFO:

  async beforeClose() {
-   if (this.#agentHttpLoadUnitHook) {
-     this.#app.loadUnitLifecycleUtil.deleteLifecycle(this.#agentHttpLoadUnitHook);
-   }
    this.#app.eggObjectLifecycleUtil.deleteLifecycle(this.#graphObjectHook);
    this.#app.eggObjectLifecycleUtil.deleteLifecycle(this.#boundModelObjectHook);
    this.#app.loadUnitLifecycleUtil.deleteLifecycle(this.#graphLoadUnitHook);
    this.#app.eggPrototypeLifecycleUtil.deleteLifecycle(this.#graphPrototypeHook);
+   if (this.#agentHttpLoadUnitHook) {
+     this.#app.loadUnitLifecycleUtil.deleteLifecycle(this.#agentHttpLoadUnitHook);
+   }
  }
plugin/langchain/lib/graph/CompiledStateGraphObject.ts (2)

26-35: LGTM! Constructor updated to accept Application instance.

The constructor correctly accepts and stores the app parameter. The app field is marked readonly, which is appropriate since it shouldn't change after construction.


128-134: Good curried factory pattern implementation.

The createObject static method now returns a factory function that closes over the app instance. This cleanly separates the application-level configuration from per-object creation, allowing the factory to be registered once with the app context.

plugin/langchain/lib/agent/AgentHttpLoadUnitLifecycleHook.ts (3)

122-122: Type escape with as any on stream result.

Readable.fromWeb(res as any, ...) uses a type assertion to work around potential type incompatibility between LangGraph's stream result and Web Streams API.

Verify that res from streamFunc is actually a web-compatible ReadableStream. If it's a different stream type, this could cause runtime issues. Consider adding a runtime check or documenting the expected type.


127-128: LGTM! Controller decoration approach.

Applying HTTPController and ConfigSourceQualifier decorators dynamically is a clean pattern for runtime controller generation.


133-139: LGTM! Module config extraction logic.

The #getModuleConfig method correctly navigates the nested config structure and returns agent entries only when present.

@socket-security
Copy link

socket-security bot commented Dec 1, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​langchain/​community@​1.0.59610090100100
Addedlangchain@​1.1.19910092100100
Added@​langchain/​langgraph@​1.0.29910010098100
Added@​langchain/​core@​1.1.099100100100100
Added@​langchain/​openai@​1.1.3100100100100100

View full report

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.

3 participants