feat: Update dependencies to add smolagents package (#6030)

* chore: Update Pillow and Pandas dependencies to latest patch versions

* chore: Update NVIDIA AI Endpoints and Pillow dependencies

* feat: Add smolagents dependency to project requirements

* feat: Add HuggingFace model bridge for LangChain integration

Implement a model bridge that allows seamless conversion between LangChain and HuggingFace model interfaces, supporting message and tool call translations

* docs: Update usage example in LangChainHFModel to improve clarity

* fix: update smolagents dependency version to 1.8.0

---------

Co-authored-by: Edwin Jose <edwin.jose@datastax.com>
This commit is contained in:
Gabriel Luiz Freitas Almeida 2025-02-13 09:37:13 -03:00 committed by GitHub
commit 70e8650813
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 264 additions and 100 deletions

View file

@ -0,0 +1,133 @@
# Import LangChain base
from langchain_core.language_models.chat_models import BaseChatModel
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolCall
from langchain_core.tools import BaseTool
# Import HuggingFace Model base
from smolagents import Model, Tool
from smolagents.models import ChatMessage, ChatMessageToolCall, ChatMessageToolCallDefinition
def _lc_tool_call_to_hf_tool_call(tool_call: ToolCall) -> ChatMessageToolCall:
"""Convert a LangChain ToolCall to a HuggingFace ChatMessageToolCall.
Args:
tool_call (ToolCall): LangChain tool call to convert
Returns:
ChatMessageToolCall: Equivalent HuggingFace tool call
"""
return ChatMessageToolCall(
function=ChatMessageToolCallDefinition(name=tool_call.name, arguments=tool_call.args),
id=tool_call.id,
)
def _hf_tool_to_lc_tool(tool) -> BaseTool:
"""Convert a HuggingFace Tool to a LangChain BaseTool.
Args:
tool (Tool): HuggingFace tool to convert
Returns:
BaseTool: Equivalent LangChain tool
"""
if not hasattr(tool, "langchain_tool"):
msg = "HuggingFace Tool does not have a langchain_tool attribute"
raise ValueError(msg)
return tool.langchain_tool
class LangChainHFModel(Model):
"""A class bridging HuggingFace's `Model` interface with a LangChain `BaseChatModel`.
This adapter allows using any LangChain chat model with the HuggingFace interface.
It handles conversion of message formats and tool calls between the two frameworks.
Usage:
>>> lc_model = LangChainChatModel(...) # any BaseChatModel
>>> hf_model = LangChainHFModel(lc_model)
>>> hf_model(messages=[{"role": "user", "content": "Hello!"}])
"""
def __init__(self, chat_model: BaseChatModel, **kwargs):
"""Initialize the bridge model.
Args:
chat_model (BaseChatModel): LangChain chat model to wrap
**kwargs: Additional arguments passed to Model.__init__
"""
super().__init__(**kwargs)
self.chat_model = chat_model
def __call__(
self,
messages: list[dict[str, str]],
stop_sequences: list[str] | None = None,
grammar: str | None = None,
tools_to_call_from: list[Tool] | None = None,
**kwargs,
) -> ChatMessage:
"""Process messages through the LangChain model and return HuggingFace format.
Args:
messages: List of message dictionaries with 'role' and 'content' keys
stop_sequences: Optional list of strings to stop generation
grammar: Optional grammar specification (not used)
tools_to_call_from: Optional list of available tools (not used)
**kwargs: Additional arguments passed to the LangChain model
Returns:
ChatMessage: Response in HuggingFace format
"""
if grammar:
msg = "Grammar is not yet supported."
raise ValueError(msg)
# Convert HF messages to LangChain messages
lc_messages = []
for m in messages:
role = m["role"]
content = m["content"]
if role == "system":
lc_messages.append(SystemMessage(content=content))
elif role == "assistant":
lc_messages.append(AIMessage(content=content))
else:
# Default any unknown role to "user"
lc_messages.append(HumanMessage(content=content))
# Convert tools to LangChain tools
if tools_to_call_from:
tools_to_call_from = [_hf_tool_to_lc_tool(tool) for tool in tools_to_call_from]
model = self.chat_model.bind_tools(tools_to_call_from) if tools_to_call_from else self.chat_model
# Call the LangChain model
result_msg: AIMessage = model.invoke(lc_messages, stop=stop_sequences, **kwargs)
# Convert the AIMessage into an HF ChatMessage
return ChatMessage(
role="assistant",
content=result_msg.content or "",
tool_calls=[_lc_tool_call_to_hf_tool_call(tool_call) for tool_call in result_msg.tool_calls],
)
# How to use
# if __name__ == "__main__":
# from langchain_community.tools import DuckDuckGoSearchRun
# from langchain_openai import ChatOpenAI
# from rich import rprint
# from smolagents import CodeAgent
# # Example usage
# model = LangChainHFModel(chat_model=ChatOpenAI(model="gpt-4o-mini"))
# search_tool = DuckDuckGoSearchRun()
# hf_tool = Tool.from_langchain(search_tool)
# code_agent = CodeAgent(
# model=model,
# tools=[hf_tool],
# )
# rprint(code_agent.run("Search for Langflow on DuckDuckGo and return the first result"))

View file

@ -41,10 +41,10 @@ dependencies = [
"alembic>=1.13.0,<2.0.0",
"passlib>=1.7.4,<2.0.0",
"bcrypt==4.0.1",
"pillow>=10.2.0,<11.0.0",
"pillow>=11.1.0,<12.0.0",
"docstring-parser>=0.16,<1.0.0",
"python-jose>=3.3.0,<4.0.0",
"pandas==2.2.2",
"pandas==2.2.3",
"multiprocess>=0.70.14,<1.0.0",
"duckdb>=1.0.0,<2.0.0",
"python-docx>=1.1.0,<2.0.0",