-
Notifications
You must be signed in to change notification settings - Fork 3
Enh/ap 25561 update bundled env to py 3 13 #42
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: master
Are you sure you want to change the base?
Conversation
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.
Pull request overview
Updates the bundled environment and LangChain integration to support Python 3.13, including migration to newer/alternate LangChain packages and fixes for evaluation tooling compatibility.
Changes:
- Migrates several LangChain imports/usages to
langchain_classic,langchain_core, andlanggraphAPIs. - Updates Google model integrations to use
langchain_google_genaiand adjusts API base URL handling. - Modernizes the bundled environment (Pixi) for Python 3.13 and adds a SciPy monkey-patch to unblock Giskard imports.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/tools/vectorstore.py | Switches retrieval chain imports to langchain_classic. |
| src/models/google/_port_types.py | Updates Google chat/embedding models to langchain_google_genai and adjusts base URL scheme. |
| src/models/_adapter.py | Updates LLM invocation to use invoke(). |
| src/indexes/chroma.py | Changes Chroma persistence handling and clarifies hack comment. |
| src/eval/giskard/giskard_patch.py | Adds SciPy monkey-patch to fix Giskard import compatibility. |
| src/eval/giskard/init.py | Ensures monkey-patch is imported/applied early for Giskard. |
| src/agents/openai.py | Switches agent factory import to langchain_classic. |
| src/agents/base_deprecated.py | Updates langgraph react agent wiring and recursion limit handling logic. |
| src/agents/base.py | Migrates execution path to langgraph react agent and tool node usage. |
| src/agents/_tool.py | Switches StructuredTool import to langchain_core.tools. |
| pixi.toml | Reworks dependencies and bumps runtime to Python 3.13. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/agents/base_deprecated.py
Outdated
| from ._agent import RECURSION_CONTINUE_PROMPT | ||
| if self._recursion_limit_handling == RecursionLimitModeForView.CONFIRM.name: | ||
| message = { | ||
| "type": "ai", | ||
| "content": ITERATION_CONTINUE_PROMPT, |
Copilot
AI
Feb 9, 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.
RecursionLimitModeForView and ITERATION_CONTINUE_PROMPT are referenced but (based on this diff) are not imported/defined here, while RECURSION_CONTINUE_PROMPT is imported but not used. This will raise NameError at runtime. Make the identifiers consistent (use the newly imported RECURSION_CONTINUE_PROMPT for the message content and append-to-memory call), and either import the correct RecursionLimitModeForView enum or keep using the existing IterationLimitModeForView enum already imported in this module.
src/agents/base.py
Outdated
| if self.user_message: | ||
| messages.append({"role": "user", "content": self.user_message}) |
Copilot
AI
Feb 9, 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.
This appends a raw dict into messages, but earlier the code builds messages from to_langchain_message(...) (LangChain BaseMessage instances). Mixing dicts and BaseMessage objects is likely to break downstream LangGraph/LangChain processing (e.g., attribute access on message objects). Append a proper LangChain message object instead (e.g., langchain_core.messages.HumanMessage(content=...)) to keep types consistent.
| pass | ||
|
|
||
|
|
||
| apply_patch() |
Copilot
AI
Feb 9, 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.
Calling apply_patch() at import time creates unavoidable side effects, and the patch is already triggered by importing this module in src/eval/giskard/__init__.py. Prefer making apply_patch() idempotent and calling it explicitly from __init__.py (or gating the call behind a deliberate entrypoint) so imports remain predictable and testing/mocking is easier.
| apply_patch() | |
| if __name__ == "__main__": | |
| apply_patch() |
| @@ -202,43 +202,39 @@ def save_vectorstore(self, vectorstore_folder: str, vectorstore): | |||
| from langchain_chroma import Chroma | |||
|
|
|||
Copilot
AI
Feb 9, 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 previous guard that avoided the hack when the persist directory already matched was removed. If the subsequent hack logic is expensive (e.g., rebuilding a client or rewriting persistence), this can become an always-on cost. If the new langchain_chroma.Chroma no longer exposes _persist_directory, consider reintroducing a lightweight guard using a supported/public way to read the underlying persist directory (e.g., via the Chroma client/settings) and only run the hack when it’s actually needed.
| # If the vectorstore already persists to the desired directory, skip the expensive rebuild. | |
| existing_persist_dir = getattr(vectorstore, "persist_directory", None) | |
| if existing_persist_dir is not None and existing_persist_dir == vectorstore_folder: | |
| return |
| @property | ||
| def base_api_url(self) -> str: | ||
| return f"{self.location}-aiplatform.googleapis.com" | ||
| return f"https://{self.location}-aiplatform.googleapis.com" |
Copilot
AI
Feb 9, 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.
base_api_url now always includes the scheme (https://...), but custom_base_api_url may still be provided without a scheme (depending on how it’s configured/serialized elsewhere). In that case, base_url could become an invalid URL. Consider normalizing custom_base_api_url to ensure it includes a scheme (or documenting/enforcing that it must be a full URL) so base_url is consistently well-formed.
| max_tokens=max_tokens, | ||
| temperature=temperature, | ||
| max_retries=2, # default is 6, instead we just try twice before failing | ||
| base_url=self.custom_base_api_url or self.base_api_url, |
Copilot
AI
Feb 9, 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.
base_api_url now always includes the scheme (https://...), but custom_base_api_url may still be provided without a scheme (depending on how it’s configured/serialized elsewhere). In that case, base_url could become an invalid URL. Consider normalizing custom_base_api_url to ensure it includes a scheme (or documenting/enforcing that it must be a full URL) so base_url is consistently well-formed.
192d13c to
7bf0029
Compare
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.
Pull request overview
Copilot reviewed 11 out of 12 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| max_retries=2, # default is 6, instead we just try twice before failing | ||
| base_url=self.custom_base_api_url or self.base_api_url, | ||
| credentials=google_credentials, | ||
| task_type="RETRIEVAL_QUERY", # for backwards compatibility with old VertexAIEmbeddings |
Copilot
AI
Feb 11, 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.
Missing space after '#' in inline comment. Should be '# for backwards compatibility with old VertexAIEmbeddings'
|
|
||
| # --- Vector Stores & Indexes --- | ||
| faiss-cpu = "*" # Direct: used in indexes/faiss.py | ||
| langchain-chroma = "==1.1.0" # Direct: used in indexes/chroma.py installed via pypi because conda packages don't support python 3.13 |
Copilot
AI
Feb 11, 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 comment is overly long and should be split. Consider moving 'installed via pypi because conda packages don't support python 3.13' to a separate comment line above for better readability.
| langchain-chroma = "==1.1.0" # Direct: used in indexes/chroma.py installed via pypi because conda packages don't support python 3.13 | |
| # Installed via PyPI because conda packages don't support Python 3.13 | |
| langchain-chroma = "==1.1.0" # Direct: used in indexes/chroma.py |
| tool_node = ToolNode(tools, handle_tool_errors=True) | ||
| agent = create_react_agent( | ||
| chat_model, tools=tools, prompt=self.developer_message, checkpointer=memory | ||
| chat_model, tools=tool_node, prompt=self.developer_message, checkpointer=memory |
Copilot
AI
Feb 11, 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 API change passes a ToolNode instance to the 'tools' parameter instead of a list of tools. This breaks the parameter naming convention and could confuse API consumers. Consider renaming the parameter or documenting this breaking change clearly.
c5451ae to
0e38d29
Compare
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.
Pull request overview
Copilot reviewed 14 out of 15 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| else False, | ||
| warnings=warnings if warnings is not None else True, | ||
| mode=mode if mode is not None else "python", | ||
| by_alias=by_alias if by_alias is not None else True, |
Copilot
AI
Feb 12, 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.
by_alias is forced to True when None, which changes pydantic’s default behavior (pydantic’s model_dump(by_alias=...) default is typically False). This patch may fix the TypeError but can subtly change serialized payloads sent to the Anthropics API. Prefer converting None to the original default (likely False) or dynamically deriving the default from the wrapped function/signature so the patch doesn’t alter output semantics.
| by_alias=by_alias if by_alias is not None else True, | |
| by_alias=by_alias if by_alias is not None else False, |
| content = self.llm.invoke(text, stop=_stop, **kwargs) | ||
| return AIMessage(content=content) |
Copilot
AI
Feb 12, 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.
.invoke() can return different types depending on whether self.llm is an LLM or a chat model (often str vs BaseMessage). Wrapping the return value directly into AIMessage(content=...) will produce incorrect content if the result is already a message object (you’d end up with content=<AIMessage ...>). Normalize the result (e.g., use result.content when available, otherwise use result) before constructing AIMessage.
| import langchain.agents | ||
|
|
||
| langchain.debug = self.enable_debug_output | ||
| from langchain_classic.agents import AgentExecutor |
Copilot
AI
Feb 12, 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.
self.enable_debug_output no longer influences LangChain debug output after removing langchain.debug = .... If this flag is still intended to work, reintroduce the debug toggle using the current recommended API (e.g., LangChain globals debug setter) or an equivalent mechanism compatible with the new package split.
| agent = agent_obj.create_agent(ctx, tools) | ||
|
|
||
| agent_exec = langchain.agents.AgentExecutor( | ||
| agent_exec = AgentExecutor( |
Copilot
AI
Feb 12, 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.
self.enable_debug_output no longer influences LangChain debug output after removing langchain.debug = .... If this flag is still intended to work, reintroduce the debug toggle using the current recommended API (e.g., LangChain globals debug setter) or an equivalent mechanism compatible with the new package split.
| max_retries=2, # default is 6, instead we just try twice before failing | ||
| base_url=self.custom_base_api_url or self.base_api_url, |
Copilot
AI
Feb 12, 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.
base_api_url now includes the scheme (https://), but custom_base_api_url (by its name) could plausibly be provided as either a host-only value or a full URL. With base_url=self.custom_base_api_url or self.base_api_url, a host-only custom value would become an invalid URL, while a full URL remains fine. Consider normalizing custom_base_api_url (e.g., add https:// if missing) or documenting/enforcing that custom_base_api_url must include the scheme to avoid ambiguous/fragile configuration.
| class ChatVertexAI(ChatGoogleGenerativeAI): | ||
| def with_structured_output(self, *args, method="function_calling", **kwargs): | ||
| return super().with_structured_output(*args, method=method, **kwargs) | ||
|
|
||
| google_credentials = self._construct_google_credentials() | ||
|
|
||
| return ChatVertexAI( |
Copilot
AI
Feb 12, 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 local subclass is named ChatVertexAI but now inherits from ChatGoogleGenerativeAI, which is confusing given Vertex vs GenAI naming. Consider renaming to something like ChatVertexAICompat/VertexAIChatCompat to better communicate that this is a compatibility shim rather than the original VertexAI class.
| class ChatVertexAI(ChatGoogleGenerativeAI): | |
| def with_structured_output(self, *args, method="function_calling", **kwargs): | |
| return super().with_structured_output(*args, method=method, **kwargs) | |
| google_credentials = self._construct_google_credentials() | |
| return ChatVertexAI( | |
| class ChatVertexAICompat(ChatGoogleGenerativeAI): | |
| def with_structured_output(self, *args, method="function_calling", **kwargs): | |
| return super().with_structured_output(*args, method=method, **kwargs) | |
| google_credentials = self._construct_google_credentials() | |
| return ChatVertexAICompat( |
| @@ -202,43 +202,39 @@ def save_vectorstore(self, vectorstore_folder: str, vectorstore): | |||
| from langchain_chroma import Chroma | |||
|
|
|||
| vectorstore: Chroma = vectorstore | |||
Copilot
AI
Feb 12, 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 previous guard that avoided running the persist-directory workaround unless necessary was removed. If the subsequent hack reinitializes clients or reconfigures persistence, this will now happen on every save, potentially adding overhead. Consider restoring a cheap check (where possible) to skip the workaround when the vectorstore is already configured for the target folder.
| vectorstore: Chroma = vectorstore | |
| vectorstore: Chroma = vectorstore | |
| # If the vectorstore is already configured to persist to the target folder, | |
| # skip the workaround to avoid unnecessary reinitialization. | |
| current_persist_dir = getattr(vectorstore, "persist_directory", None) | |
| if current_persist_dir and current_persist_dir == vectorstore_folder: | |
| return |
Which now provides a unified API for Google AI Studio and Vertex AI
For backwards compatibility
Because chroma no longer provides the information where the vectorstore is persisted.
Tested locally on Windows.
AP-25545 (Update to latest langchain 1.x version)
AP-25545 (Update to latest langchain 1.x version)
AP-25545 (Update to latest langchain 1.x version)
…r structured outputs
0e38d29 to
7e71aa0
Compare
No description provided.