-
Notifications
You must be signed in to change notification settings - Fork 140
Add support for Anthropic/Claude and other LLM providers #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
56adc8b
788083b
7723d5d
0820129
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,16 @@ | ||
| # LLM Provider: "openai" (default), "anthropic", or "ollama" | ||
| LLM_PROVIDER=openai | ||
|
|
||
| # OpenAI Configuration | ||
| OPENAI_API_KEY= | ||
| OPENAI_API_VERSION= | ||
| OPENAI_API_TYPE= | ||
| OPENAI_API_TYPE= | ||
| OPENAI_MODEL=gpt-5.2 | ||
|
|
||
| # Anthropic Configuration | ||
| ANTHROPIC_API_KEY= | ||
| ANTHROPIC_MODEL=claude-sonnet-4-5 | ||
|
|
||
| # Ollama Configuration (local models) | ||
| OLLAMA_MODEL=llama3 | ||
| OLLAMA_BASE_URL=http://localhost:11434 |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,24 +12,31 @@ | |||||||||||||||||||||||||||||||||||||||||
| # See the License for the specific language governing permissions and | ||||||||||||||||||||||||||||||||||||||||||
| # limitations under the License. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| from typing import Any, AsyncIterable, Dict, Literal, Optional, Union | ||||||||||||||||||||||||||||||||||||||||||
| from __future__ import annotations | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| from langchain.agents import AgentExecutor | ||||||||||||||||||||||||||||||||||||||||||
| from langchain.agents.format_scratchpad.openai_tools import ( | ||||||||||||||||||||||||||||||||||||||||||
| format_to_openai_tool_messages, | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser | ||||||||||||||||||||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||||||||||||||||||||
| from contextlib import contextmanager | ||||||||||||||||||||||||||||||||||||||||||
| from typing import TYPE_CHECKING, Any, AsyncIterable, Dict, Literal, Optional | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| from langchain.agents import AgentExecutor, create_tool_calling_agent | ||||||||||||||||||||||||||||||||||||||||||
| from langchain.prompts import MessagesPlaceholder | ||||||||||||||||||||||||||||||||||||||||||
| from langchain_community.callbacks import get_openai_callback | ||||||||||||||||||||||||||||||||||||||||||
| from langchain_core.language_models import BaseChatModel | ||||||||||||||||||||||||||||||||||||||||||
| from langchain_core.messages import AIMessage, HumanMessage | ||||||||||||||||||||||||||||||||||||||||||
| from langchain_core.prompts import ChatPromptTemplate | ||||||||||||||||||||||||||||||||||||||||||
| from langchain_ollama import ChatOllama | ||||||||||||||||||||||||||||||||||||||||||
| from langchain_openai import AzureChatOpenAI, ChatOpenAI | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| if TYPE_CHECKING: | ||||||||||||||||||||||||||||||||||||||||||
| from langchain_anthropic import ChatAnthropic | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
29
to
32
|
||||||||||||||||||||||||||||||||||||||||||
| if TYPE_CHECKING: | |
| from langchain_anthropic import ChatAnthropic |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The strict Union was removed in favor of a generic BaseChatModel, but get_llm()in llm.py still effectively hard-codes a strict union.
I am in favor of allowing any tool calling LLM, but that means we need the ability to construct a generic chat model without enumerating all possibilities.
Recommend either reverting back to Union with ChatAnthropic included and remove "any tool calling model" language, or find a good solution for constructing generic tool calling Chat models. Either fix is fine with me 👍
Copilot
AI
Feb 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The refactoring from OpenAI-specific agent pipeline to create_tool_calling_agent is a significant architectural change, but there are no tests to verify that it works correctly with different providers. Consider adding integration tests that validate the agent works with mock ChatOpenAI, ChatAnthropic, and ChatOllama instances to ensure the provider-agnostic implementation behaves correctly across different LLM backends.
Outdated
Copilot
AI
Feb 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The warning message is only logged once when the context manager is entered, but if a user repeatedly calls invoke() with show_token_usage=True on a non-OpenAI model, they will see this warning every time. Consider adding a flag to log this warning only once, or document this limitation in the constructor's docstring more prominently to set user expectations upfront.
Outdated
Copilot
AI
Feb 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _token_callback method uses isinstance() to check if the LLM is OpenAI-based, but this check happens at runtime on every invocation. Since the LLM type doesn't change after initialization, consider checking this once in init and storing a boolean flag (e.g., self.__supports_token_tracking) to avoid repeated isinstance calls. This would be more efficient and clearer.
| if isinstance(self.__llm, (ChatOpenAI, AzureChatOpenAI)): | |
| with get_openai_callback() as cb: | |
| yield cb | |
| else: | |
| if self.__show_token_usage: | |
| logger.warning("Token usage tracking is only supported for OpenAI and Azure models.") | |
| # Lazily determine whether the current LLM supports OpenAI-style token tracking. | |
| if not hasattr(self, "_ROSA__supports_token_tracking"): | |
| self.__supports_token_tracking = isinstance( | |
| self.__llm, (ChatOpenAI, AzureChatOpenAI) | |
| ) | |
| if self.__supports_token_tracking: | |
| with get_openai_callback() as cb: | |
| yield cb | |
| else: | |
| if self.__show_token_usage: | |
| logger.warning( | |
| "Token usage tracking is only supported for OpenAI and Azure models." | |
| ) |
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -19,14 +19,55 @@ | |||||||
|
|
||||||||
|
|
||||||||
| def get_llm(streaming: bool = False): | ||||||||
| """A helper function to get the LLM instance.""" | ||||||||
| """A helper function to get the LLM instance. | ||||||||
| Supports OpenAI (default), Anthropic and Ollama models. | ||||||||
| Set the LLM_PROVIDER env variable to switch between providers: | ||||||||
| - "openai" (default): uses OPENAI_API_KEY | ||||||||
| - "anthropic": uses ANTHROPIC_API_KEY | ||||||||
| - "ollama": uses local Ollama instance | ||||||||
| """ | ||||||||
| dotenv.load_dotenv(dotenv.find_dotenv()) | ||||||||
|
|
||||||||
| llm = ChatOpenAI( | ||||||||
| api_key=get_env_variable("OPENAI_API_KEY"), | ||||||||
| model="gpt-5.1", | ||||||||
| streaming=streaming, | ||||||||
| ) | ||||||||
| provider = os.getenv("LLM_PROVIDER", "openai").lower() | ||||||||
|
||||||||
|
|
||||||||
| if provider == "openai": | ||||||||
| llm = ChatOpenAI( | ||||||||
| api_key=get_env_variable("OPENAI_API_KEY"), | ||||||||
| model=get_env_variable("OPENAI_MODEL"), | ||||||||
RobRoyce marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||
| streaming=streaming, | ||||||||
| ) | ||||||||
| elif provider == "anthropic": | ||||||||
| try: | ||||||||
| from langchain_anthropic import ChatAnthropic | ||||||||
| except ImportError: | ||||||||
| raise ImportError( | ||||||||
| "langchain-anthropic is required for Anthropic support. " | ||||||||
| "Install it with: pip install langchain-anthropic" | ||||||||
| ) | ||||||||
| llm = ChatAnthropic( | ||||||||
| api_key=get_env_variable("ANTHROPIC_API_KEY"), | ||||||||
| model=get_env_variable("ANTHROPIC_MODEL"), | ||||||||
| streaming=streaming, | ||||||||
| ) | ||||||||
| elif provider == "ollama": | ||||||||
| try: | ||||||||
| from langchain_ollama import ChatOllama | ||||||||
| except ImportError: | ||||||||
| raise ImportError( | ||||||||
| "langchain-ollama is required for Ollama support. " | ||||||||
| "Install it with: pip install langchain-ollama" | ||||||||
| ) | ||||||||
| llm = ChatOllama( | ||||||||
| model=os.getenv("OLLAMA_MODEL", "llama3"), | ||||||||
| base_url=os.getenv("OLLAMA_BASE_URL", "http://localhost:11434"), | ||||||||
|
||||||||
| base_url=os.getenv("OLLAMA_BASE_URL", "http://localhost:11434"), | |
| base_url=os.getenv("OLLAMA_BASE_URL", "http://localhost:11434"), | |
| streaming=streaming, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding langchain-ollama to the optional-dependencies section similar to langchain-anthropic, since it's no longer imported directly in the ROSA core code and is only used in the turtle_agent example with lazy import. This would reduce the installation footprint for users who don't need Ollama support. For example:
ollama = ["langchain-ollama~=0.3.2"]