Skip to content

feat: refactor langchain integration with integration core - #25

Closed
akihikokuroda wants to merge 13 commits into
generative-computing:mainfrom
akihikokuroda:refactor_langchain
Closed

akihikokuroda wants to merge 13 commits into
generative-computing:mainfrom
akihikokuroda:refactor_langchain

Conversation

@akihikokuroda

Copy link
Copy Markdown
Contributor

Refactor of #22

Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
@akihikokuroda
akihikokuroda marked this pull request as draft February 17, 2026 18:50
@mergify

mergify Bot commented Feb 17, 2026

Copy link
Copy Markdown

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🟢 Enforce conventional commit

Wonderful, this rule succeeded.

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert|release)(?:\(.+\))?:

Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
@akihikokuroda
akihikokuroda marked this pull request as ready for review February 27, 2026 18:19
Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
return tool_calls

@staticmethod
def _parse_tool_calls_from_string(content_str: str) -> list[dict[str, Any]]:

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.

this function is very similar to the one in the base class. do we need to have a separate implementation, or can we make use of the base one?

@akihikokuroda akihikokuroda Mar 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This function is not necessary. Removing.

)
except (ValueError, SyntaxError) as e:
# If parsing fails, skip this tool call
print(f"Warning: Failed to parse tool call arguments: {e}")

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.

should this be a log rather than a print?

# Create ChatResult with llm_output if provided
return ChatResult(generations=[generation], llm_output=kwargs.get("llm_output", {}))

def _extract_tool_calls(self, response: Any) -> list[dict[str, Any]]:

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.

similar question about this one, it's also very similar to the function in the base class

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I missed during refactor. Thanks!

Comment on lines +138 to +144
bound_model = self.__class__(
mellea_session=self.mellea_session,
model_name=self.model_name,
streaming=self.streaming,
requirements=self._requirements,
strategy=self._strategy,
)

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.

do we need to pass kwargs too?

Suggested change
bound_model = self.__class__(
mellea_session=self.mellea_session,
model_name=self.model_name,
streaming=self.streaming,
requirements=self._requirements,
strategy=self._strategy,
)
bound_model = self.__class__(
mellea_session=self.mellea_session,
model_name=self.model_name,
streaming=self.streaming,
requirements=self._requirements,
strategy=self._strategy,
**kwargs,
)

Comment on lines +147 to +148
bound_model._bound_tools = tools # type: ignore
bound_model._tool_choice = kwargs.get("tool_choice") # type: ignore

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.

we may at some point want to consider making these PrivateAttr. There may be problems down the line if someone calls model_dump() on these (which I believe both LangSmith and LangServe do), but not a high priority right now.


Args:
messages: List of messages to generate from
stop: Stop sequences (not currently supported)

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.

we should log if a stop sequence is passed and that it is ignored / not supported as of now


mellea_session: Any = Field(description="The Mellea session to use for generation")
model_name: str = Field(default="mellea", description="Name to identify this model")
streaming: bool = Field(default=False, description="Whether to stream responses by default")

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.

below (~L395) we mention that we don't support streaming, so should we drop this variable so we don't mislead?

# Convert response using message converter
result = self.message_converter.from_mellea(response)

# Execute tool calls if present

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.

My understanding of langchain is that the model returns a message with tool calls, but the agent executor actually runs them and sends results back. Whereas here we're running the tools and including results inside _generate. I think that may cause problems for LangGraph / LangChain agents — worth checking against langgraph's react agent implementation

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Comment put this section of code.

Comment thread integrations/langchain/uv.lock Outdated

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.

this is just for testing right? for a library we probably don't want to commit this

Comment thread integrations/langchain/pyproject.toml Outdated
@@ -0,0 +1,75 @@
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

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.

in core we're using hatchling to build. let's standardize

Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
Comment thread integrations/langchain/README.md Outdated
This integration allows LangChain users to leverage Mellea's structured approach to generative programming while using familiar LangChain APIs. It provides full support for:

- ✅ **Chat Completion**: Standard synchronous and asynchronous generation
- ✅ **Streaming**: Real-time token-by-token generation

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.

We probably need to update this line, as we don't have real streaming implemented in mellea yet

Comment on lines +286 to +291
# NOTE: Tool execution is commented out to align with standard LangChain behavior.
# In LangChain/LangGraph, the model returns tool calls but doesn't execute them.
# The agent executor handles tool execution and sends results back to the model.
#
# If you need automatic tool execution for testing or specific use cases,
# uncomment the code below, but be aware this deviates from standard patterns.

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.

I would think if someone needed this functionality there should be some alternative implementation rather than having them uncomment code (which they will most likely have to go searching in their site-packages for).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The AgentExecutor has been deprecated in LangChain. For automatic tool call, it needs LangGraph. What can or should be done here?

metadata: dict[str, Any]


class MessageConverter(Protocol):

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.

should we also add extract_last_user_message here too?

# Display tool execution results if available
if (
hasattr(response, "response_metadata")
and "tool_execution_results" in response.response_metadata

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.

will this still show tool execution results with the code you commented out?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I remove it.

# The model only returns tool calls in the AIMessage, which the agent then executes.
# The _execute_tool_calls method below is for internal use and compatibility testing only.

class Config:

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.

pydantic has changed how they define configs in v2: https://docs.pydantic.dev/latest/migration/#changes-to-config
since we're using v2 in pyproject.toml, we may want to think about updating

Signed-off-by: Akihiko Kuroda <akihikokuroda2020@gmail.com>
@akihikokuroda

Copy link
Copy Markdown
Contributor Author

For the CI issue fix. close this and open a new one.

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.

2 participants