Mateo/lchain (#549)
The latest change to langchain-arcade was blocked becuase it still relied on poetry, I moved that to uv
This commit is contained in:
parent
c481f1d677
commit
639f726b30
8 changed files with 202 additions and 142 deletions
46
.github/workflows/publish-langchain.yml
vendored
46
.github/workflows/publish-langchain.yml
vendored
|
|
@ -13,6 +13,7 @@ jobs:
|
|||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
|
@ -20,15 +21,11 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
with:
|
||||
version: 1.8.5
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
- name: Set up the environment
|
||||
uses: ./.github/actions/setup-uv-env
|
||||
with:
|
||||
python-version: "3.11"
|
||||
cache: "pip"
|
||||
working-directory: contrib/langchain
|
||||
|
||||
- name: Test LangChain Arcade
|
||||
working-directory: contrib/langchain
|
||||
|
|
@ -41,33 +38,38 @@ jobs:
|
|||
if: inputs.version != ''
|
||||
working-directory: contrib/langchain
|
||||
run: |
|
||||
poetry version ${{ inputs.version }}
|
||||
uv version ${{ inputs.version }}
|
||||
|
||||
- name: Publish to PyPI
|
||||
- name: Build release distributions
|
||||
working-directory: contrib/langchain
|
||||
run: |
|
||||
poetry build
|
||||
# Extract version from pyproject.toml using poetry and save it
|
||||
VERSION=$(poetry version -s)
|
||||
VERSION=$(uv version --short)
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
poetry config pypi-token.pypi ${{ secrets.PYPI_TOKEN }}
|
||||
# Attempt to publish the toolkit to PyPI. Skip if the version already exists
|
||||
if poetry publish --skip-existing 2>&1 | grep -q "File exists. Skipping"; then
|
||||
echo "Version already exists on PyPI. Skipping publish."
|
||||
echo "skip_publish=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "skip_publish=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
uv build --out-dir dist | tee build.log
|
||||
|
||||
# Verify build artifacts
|
||||
ls -la dist/
|
||||
echo "Built artifacts for langchain_arcade v${{ env.VERSION }}"
|
||||
|
||||
- name: Upload release distributions
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-dists-langchain_arcade-${{ env.VERSION }}
|
||||
path: dist/
|
||||
|
||||
- name: Publish release distributions to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
|
||||
- name: Send status to Slack
|
||||
if: steps.Publish_LangChain.outputs.skip_publish != 'true'
|
||||
if: always()
|
||||
uses: slackapi/slack-github-action@v2.0.0
|
||||
with:
|
||||
webhook: ${{ secrets.PACKAGE_RELEASE_SLACK_WEBHOOK_URL }}
|
||||
webhook-type: webhook-trigger
|
||||
payload: |
|
||||
{
|
||||
"status": "${{ job.status }}",
|
||||
"status": "${{ job.status == 'failure' || job.status == 'cancelled' && 'Failed' || 'Success' }}",
|
||||
"package": "langchain_arcade",
|
||||
"version": "${{ env.VERSION }}",
|
||||
"url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
|
|
|
|||
25
.github/workflows/test-langchain.yml
vendored
25
.github/workflows/test-langchain.yml
vendored
|
|
@ -18,15 +18,17 @@ jobs:
|
|||
- name: Check out
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
|
||||
- name: Set up the environment
|
||||
uses: ./.github/actions/setup-uv-env
|
||||
with:
|
||||
version: 1.8.5
|
||||
python-version: "3.11"
|
||||
working-directory: contrib/langchain
|
||||
|
||||
- name: Install
|
||||
run: cd contrib/langchain && make install && make check
|
||||
|
||||
tox:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
|
|
@ -41,21 +43,18 @@ jobs:
|
|||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
- name: Set up the environment
|
||||
uses: ./.github/actions/setup-uv-env
|
||||
with:
|
||||
version: 1.8.5
|
||||
python-version: ${{ matrix.python-version }}
|
||||
working-directory: contrib/langchain
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd contrib/langchain && make install
|
||||
|
||||
- name: Install tox
|
||||
- name: Test & Coverage
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install tox tox-gh-actions
|
||||
|
||||
- name: Test with tox
|
||||
run: cd contrib/langchain && tox
|
||||
cd contrib/langchain && make test && make coverage
|
||||
|
||||
- name: Upload coverage reports to Codecov with GitHub Action on Python 3.11
|
||||
uses: codecov/codecov-action@v4.0.1
|
||||
|
|
|
|||
|
|
@ -1,66 +1,47 @@
|
|||
VERSION ?= "0.1.0"
|
||||
.PHONY: help
|
||||
|
||||
help:
|
||||
@echo "🛠️ github Commands:\n"
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
|
||||
.PHONY: install
|
||||
install: ## Install the poetry environment and install the pre-commit hooks
|
||||
@echo "📦 Checking if Poetry is installed"
|
||||
@if ! command -v poetry >/dev/null 2>&1; then \
|
||||
echo "📦 Installing Poetry with pip"; \
|
||||
pip install poetry==1.8.5; \
|
||||
else \
|
||||
echo "📦 Poetry is already installed"; \
|
||||
fi
|
||||
@echo "🚀 Installing package in development mode with all extras"
|
||||
poetry install --all-extras
|
||||
@echo "🚀 Installing pre-commit hooks"
|
||||
poetry run pre-commit install
|
||||
install: ## Install the uv environment and install all packages with dependencies
|
||||
@echo "🚀 Creating virtual environment and installing all packages using uv"
|
||||
@uv sync --active --all-extras --no-sources
|
||||
@uv run pre-commit install
|
||||
@echo "✅ All packages and dependencies installed via uv"
|
||||
|
||||
.PHONY: check
|
||||
check: ## Run code quality tools.
|
||||
@echo "🚀 Checking Poetry lock file consistency with 'pyproject.toml': Running poetry check --lock"
|
||||
@poetry check --lock
|
||||
@echo "🚀 Linting code: Running pre-commit"
|
||||
@poetry run pre-commit run -a
|
||||
@echo "🚀 Static type checking: Running mypy"
|
||||
@poetry run mypy $(git ls-files '*.py')
|
||||
.PHONY: build
|
||||
build: clean-build ## Build wheel file using uv
|
||||
@echo "🚀 Creating wheel file"
|
||||
uv build
|
||||
|
||||
.PHONY: clean-build
|
||||
clean-build: ## clean build artifacts
|
||||
@echo "🗑️ Cleaning dist directory"
|
||||
rm -rf dist
|
||||
|
||||
.PHONY: test
|
||||
test: ## Test the code with pytest
|
||||
@echo "🚀 Testing code: Running pytest"
|
||||
@poetry run pytest -W ignore -v --cov --cov-config=pyproject.toml --cov-report=xml
|
||||
@uv run pytest -W ignore -v --cov --cov-config=pyproject.toml --cov-report=xml
|
||||
|
||||
.PHONY: set-version
|
||||
set-version: ## Set the version in the pyproject.toml file
|
||||
@echo "🚀 Setting version in pyproject.toml"
|
||||
@poetry version $(VERSION)
|
||||
.PHONY: coverage
|
||||
coverage: ## Generate coverage report
|
||||
@echo "coverage report"
|
||||
coverage report
|
||||
@echo "Generating coverage report"
|
||||
coverage html
|
||||
|
||||
.PHONY: unset-version
|
||||
unset-version: ## Set the version in the pyproject.toml file
|
||||
@echo "🚀 Setting version in pyproject.toml"
|
||||
@poetry version 0.1.0
|
||||
.PHONY: bump-version
|
||||
bump-version: ## Bump the version in the pyproject.toml file by a patch version
|
||||
@echo "🚀 Bumping version in pyproject.toml"
|
||||
uv version --bump patch
|
||||
|
||||
.PHONY: build
|
||||
build: clean-build ## Build wheel file using poetry
|
||||
@echo "🚀 Creating wheel file"
|
||||
@poetry build
|
||||
|
||||
.PHONY: clean-build
|
||||
clean-build: ## clean build artifacts
|
||||
@rm -rf dist
|
||||
|
||||
.PHONY: publish
|
||||
publish: ## publish a release to pypi.
|
||||
@echo "🚀 Publishing: Dry run."
|
||||
@poetry config pypi-token.pypi $(PYPI_TOKEN)
|
||||
@poetry publish --dry-run
|
||||
@echo "🚀 Publishing."
|
||||
@poetry publish
|
||||
|
||||
.PHONY: build-and-publish
|
||||
build-and-publish: build publish ## Build and publish.
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "🛠️ Arcade Dev Commands:\n"
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
.PHONY: check
|
||||
check: ## Run code quality tools.
|
||||
@echo "🚀 Linting code: Running pre-commit"
|
||||
@uv run pre-commit run -a
|
||||
@echo "🚀 Static type checking: Running mypy"
|
||||
@uv run mypy --config-file=pyproject.toml
|
||||
|
|
|
|||
|
|
@ -53,7 +53,9 @@ def tool_definition_to_pydantic_model(tool_def: ToolDefinition) -> type[BaseMode
|
|||
for param in tool_def.input.parameters or []:
|
||||
param_type = get_python_type(param.value_schema.val_type)
|
||||
if param_type == list and param.value_schema.inner_val_type: # noqa: E721
|
||||
inner_type: type[Any] = get_python_type(param.value_schema.inner_val_type)
|
||||
inner_type: type[Any] = get_python_type(
|
||||
param.value_schema.inner_val_type
|
||||
)
|
||||
param_type = list[inner_type] # type: ignore[valid-type]
|
||||
param_description = param.description or "No description provided."
|
||||
default = ... if param.required else None
|
||||
|
|
@ -90,9 +92,14 @@ def process_tool_execution_response(
|
|||
"tool": tool_name,
|
||||
}
|
||||
|
||||
if execute_response.output is not None and execute_response.output.error is not None:
|
||||
if (
|
||||
execute_response.output is not None
|
||||
and execute_response.output.error is not None
|
||||
):
|
||||
error = execute_response.output.error
|
||||
error_message = str(error.message) if hasattr(error, "message") else "Unknown error"
|
||||
error_message = (
|
||||
str(error.message) if hasattr(error, "message") else "Unknown error"
|
||||
)
|
||||
error_details["error"] = error_message
|
||||
|
||||
# Add all non-None optional error fields to the details
|
||||
|
|
@ -133,10 +140,13 @@ def create_tool_function(
|
|||
A callable function that executes the tool.
|
||||
"""
|
||||
if langgraph and not LANGGRAPH_ENABLED:
|
||||
raise ImportError("LangGraph is not installed. Please install it to use this feature.")
|
||||
raise ImportError(
|
||||
"LangGraph is not installed. Please install it to use this feature."
|
||||
)
|
||||
|
||||
requires_authorization = (
|
||||
tool_def.requirements is not None and tool_def.requirements.authorization is not None
|
||||
tool_def.requirements is not None
|
||||
and tool_def.requirements.authorization is not None
|
||||
)
|
||||
|
||||
def tool_function(config: RunnableConfig, **kwargs: Any) -> Any:
|
||||
|
|
@ -161,7 +171,9 @@ def create_tool_function(
|
|||
# Authorize the user for the tool
|
||||
auth_response = client.tools.authorize(tool_name=tool_name, user_id=user_id)
|
||||
if auth_response.status != "completed":
|
||||
auth_message = f"Please use the following link to authorize: {auth_response.url}"
|
||||
auth_message = (
|
||||
f"Please use the following link to authorize: {auth_response.url}"
|
||||
)
|
||||
if langgraph:
|
||||
raise NodeInterrupt(auth_message)
|
||||
return {"error": auth_message}
|
||||
|
|
@ -249,10 +261,13 @@ def create_async_tool_function(
|
|||
An async callable function that executes the tool.
|
||||
"""
|
||||
if langgraph and not LANGGRAPH_ENABLED:
|
||||
raise ImportError("LangGraph is not installed. Please install it to use this feature.")
|
||||
raise ImportError(
|
||||
"LangGraph is not installed. Please install it to use this feature."
|
||||
)
|
||||
|
||||
requires_authorization = (
|
||||
tool_def.requirements is not None and tool_def.requirements.authorization is not None
|
||||
tool_def.requirements is not None
|
||||
and tool_def.requirements.authorization is not None
|
||||
)
|
||||
|
||||
async def tool_function(config: RunnableConfig, **kwargs: Any) -> Any:
|
||||
|
|
@ -275,9 +290,13 @@ def create_async_tool_function(
|
|||
return {"error": error_message}
|
||||
|
||||
# Authorize the user for the tool
|
||||
auth_response = await client.tools.authorize(tool_name=tool_name, user_id=user_id)
|
||||
auth_response = await client.tools.authorize(
|
||||
tool_name=tool_name, user_id=user_id
|
||||
)
|
||||
if auth_response.status != "completed":
|
||||
auth_message = f"Please use the following link to authorize: {auth_response.url}"
|
||||
auth_message = (
|
||||
f"Please use the following link to authorize: {auth_response.url}"
|
||||
)
|
||||
if langgraph:
|
||||
raise NodeInterrupt(auth_message)
|
||||
return {"error": auth_message}
|
||||
|
|
|
|||
|
|
@ -188,7 +188,9 @@ class ToolManager(LangChainToolManager):
|
|||
"""
|
||||
tool_map = _create_tool_map(self.definitions, use_underscores=use_underscores)
|
||||
return [
|
||||
wrap_arcade_tool(self._client, tool_name, definition, langgraph=use_interrupts)
|
||||
wrap_arcade_tool(
|
||||
self._client, tool_name, definition, langgraph=use_interrupts
|
||||
)
|
||||
for tool_name, definition in tool_map.items()
|
||||
]
|
||||
|
||||
|
|
@ -228,7 +230,9 @@ class ToolManager(LangChainToolManager):
|
|||
Raises:
|
||||
ValueError: If no tools or toolkits are provided and raise_on_empty is True.
|
||||
"""
|
||||
tools_list = self._retrieve_tool_definitions(tools, toolkits, raise_on_empty, limit, offset)
|
||||
tools_list = self._retrieve_tool_definitions(
|
||||
tools, toolkits, raise_on_empty, limit, offset
|
||||
)
|
||||
self._tools = _create_tool_map(tools_list)
|
||||
return self.to_langchain()
|
||||
|
||||
|
|
@ -332,7 +336,9 @@ class ToolManager(LangChainToolManager):
|
|||
# If no specific tools or toolkits are requested, raise an error.
|
||||
if not tools and not toolkits:
|
||||
if raise_on_empty:
|
||||
raise ValueError("No tools or toolkits provided to retrieve tool definitions.")
|
||||
raise ValueError(
|
||||
"No tools or toolkits provided to retrieve tool definitions."
|
||||
)
|
||||
return []
|
||||
|
||||
# Retrieve individual tools if specified
|
||||
|
|
@ -378,7 +384,10 @@ class ToolManager(LangChainToolManager):
|
|||
self._tools.update(_create_tool_map([tool]))
|
||||
|
||||
def add_toolkit(
|
||||
self, toolkit_name: str, limit: Optional[int] = None, offset: Optional[int] = None
|
||||
self,
|
||||
toolkit_name: str,
|
||||
limit: Optional[int] = None,
|
||||
offset: Optional[int] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Add all tools from a specific toolkit to the manager.
|
||||
|
|
@ -584,7 +593,9 @@ class AsyncToolManager(LangChainToolManager):
|
|||
"""
|
||||
tool_map = _create_tool_map(self.definitions, use_underscores=use_underscores)
|
||||
return [
|
||||
wrap_arcade_tool(self._client, tool_name, definition, langgraph=use_interrupts)
|
||||
wrap_arcade_tool(
|
||||
self._client, tool_name, definition, langgraph=use_interrupts
|
||||
)
|
||||
for tool_name, definition in tool_map.items()
|
||||
]
|
||||
|
||||
|
|
@ -686,7 +697,9 @@ class AsyncToolManager(LangChainToolManager):
|
|||
# If no specific tools or toolkits are requested, raise an error.
|
||||
if not tools and not toolkits:
|
||||
if raise_on_empty:
|
||||
raise ValueError("No tools or toolkits provided to retrieve tool definitions.")
|
||||
raise ValueError(
|
||||
"No tools or toolkits provided to retrieve tool definitions."
|
||||
)
|
||||
return []
|
||||
|
||||
# First, gather single tools if the user specifically requested them.
|
||||
|
|
@ -734,7 +747,10 @@ class AsyncToolManager(LangChainToolManager):
|
|||
self._tools.update(_create_tool_map([tool]))
|
||||
|
||||
async def add_toolkit(
|
||||
self, toolkit_name: str, limit: Optional[int] = None, offset: Optional[int] = None
|
||||
self,
|
||||
toolkit_name: str,
|
||||
limit: Optional[int] = None,
|
||||
offset: Optional[int] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Add all tools from a specific toolkit to the manager.
|
||||
|
|
|
|||
|
|
@ -1,26 +1,32 @@
|
|||
[tool.poetry]
|
||||
[build-system]
|
||||
requires = [ "hatchling",]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langchain-arcade"
|
||||
version = "1.4.0"
|
||||
description = "An integration package connecting Arcade and Langchain/LangGraph"
|
||||
authors = ["Arcade <dev@arcade.dev>"]
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/arcadeai/arcade-ai/tree/main/contrib/langchain"
|
||||
license = "MIT"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.10,<4"
|
||||
arcadepy = "1.7.*"
|
||||
langchain-core = ">=0.3.49,<0.4"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"arcadepy>=1.7.0",
|
||||
"langchain-core>=0.3.49,<0.4",
|
||||
]
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^8.1.2"
|
||||
pytest-cov = "^4.0.0"
|
||||
mypy = "^1.5.1"
|
||||
pre-commit = "^3.4.0"
|
||||
tox = "^4.11.1"
|
||||
pytest-asyncio = "^0.23.7"
|
||||
langgraph = ">=0.3.23,<0.4"
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pytest>=8.3.0,<8.4.0",
|
||||
"pytest-cov>=4.0.0,<4.1.0",
|
||||
"pytest-mock>=3.11.1,<3.12.0",
|
||||
"pytest-asyncio>=0.24.0,<0.25.0",
|
||||
"mypy>=1.5.1,<1.6.0",
|
||||
"pre-commit>=3.4.0,<3.5.0",
|
||||
"ruff>=0.7.4,<0.8.0",
|
||||
"langgraph>=0.3.23,<0.4"
|
||||
]
|
||||
|
||||
|
||||
[tool.mypy]
|
||||
|
|
@ -45,3 +51,9 @@ source = ["langchain_arcade"]
|
|||
|
||||
[tool.coverage.report]
|
||||
skip_empty = true
|
||||
|
||||
[tool.ruff.lint]
|
||||
ignore = ["C901"]
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = [ "langchain_arcade",]
|
||||
|
|
|
|||
|
|
@ -165,7 +165,9 @@ async def test_init_tools_parameterized(
|
|||
client.tools.list.return_value = page_cls(items=[mock_tool])
|
||||
|
||||
# Act
|
||||
result = await maybe_await(manager.init_tools(tools=["GoogleSearch_Search"]), is_async)
|
||||
result = await maybe_await(
|
||||
manager.init_tools(tools=["GoogleSearch_Search"]), is_async
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert "GoogleSearch_Search" in manager.tools
|
||||
|
|
@ -220,7 +222,9 @@ async def test_deprecated_get_tools_parameterized(
|
|||
|
||||
# Act - Check for deprecation warning
|
||||
with pytest.warns(DeprecationWarning):
|
||||
result = await maybe_await(manager.get_tools(tools=["GoogleSearch_Search"]), is_async)
|
||||
result = await maybe_await(
|
||||
manager.get_tools(tools=["GoogleSearch_Search"]), is_async
|
||||
)
|
||||
|
||||
# Assert - Method should still work
|
||||
assert len(result) == 1
|
||||
|
|
@ -280,7 +284,9 @@ async def test_add_toolkit_parameterized(
|
|||
|
||||
# Mock the response for toolkit listing
|
||||
page_cls = AsyncOffsetPage if is_async else SyncOffsetPage
|
||||
client.tools.list.return_value = page_cls(items=[mock_tool_list_emails, mock_tool_trash_email])
|
||||
client.tools.list.return_value = page_cls(
|
||||
items=[mock_tool_list_emails, mock_tool_trash_email]
|
||||
)
|
||||
|
||||
# Act
|
||||
await maybe_await(manager.add_toolkit("Search"), is_async)
|
||||
|
|
@ -290,7 +296,9 @@ async def test_add_toolkit_parameterized(
|
|||
assert "Gmail_SendEmail" in manager.tools
|
||||
assert "Gmail_ListEmails" in manager.tools
|
||||
assert "Gmail_TrashEmail" in manager.tools
|
||||
client.tools.list.assert_called_once_with(toolkit="Search", limit=NOT_GIVEN, offset=NOT_GIVEN)
|
||||
client.tools.list.assert_called_once_with(
|
||||
toolkit="Search", limit=NOT_GIVEN, offset=NOT_GIVEN
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -339,7 +347,9 @@ async def test_wait_for_auth_with_response_object_parameterized(
|
|||
client = async_mock_arcade_client if is_async else mock_arcade_client
|
||||
|
||||
completed_response = AuthorizationResponse(
|
||||
id="auth_abc", status="completed", tool_fully_qualified_name="GoogleSearch_Search"
|
||||
id="auth_abc",
|
||||
status="completed",
|
||||
tool_fully_qualified_name="GoogleSearch_Search",
|
||||
)
|
||||
client.auth.wait_for_completion.return_value = completed_response
|
||||
|
||||
|
|
@ -409,7 +419,8 @@ async def test_get_tools_with_explicit_parameterized(
|
|||
# Act - Check for deprecation warning
|
||||
with pytest.warns(DeprecationWarning):
|
||||
retrieved_tools = await maybe_await(
|
||||
manager.get_tools(tools=["GoogleSearch_Search", "BingSearch_Search"]), is_async
|
||||
manager.get_tools(tools=["GoogleSearch_Search", "BingSearch_Search"]),
|
||||
is_async,
|
||||
)
|
||||
|
||||
# Assert
|
||||
|
|
@ -427,7 +438,9 @@ def test_arcade_tool_manager_deprecation_warning():
|
|||
with pytest.warns(DeprecationWarning) as warnings_record:
|
||||
ArcadeToolManager(client=MagicMock())
|
||||
# Assert
|
||||
assert any("ArcadeToolManager is deprecated" in str(w.message) for w in warnings_record)
|
||||
assert any(
|
||||
"ArcadeToolManager is deprecated" in str(w.message) for w in warnings_record
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -468,7 +481,9 @@ def test_requires_auth_true(manager, make_tool):
|
|||
# Arrange
|
||||
tool_name = "GoogleSearch_Search"
|
||||
# Pass a MagicMock with 'authorization' to ensure it gets converted
|
||||
mock_tool_def = make_tool(tool_name, requirements=MagicMock(authorization="some_required_auth"))
|
||||
mock_tool_def = make_tool(
|
||||
tool_name, requirements=MagicMock(authorization="some_required_auth")
|
||||
)
|
||||
manager._tools[tool_name] = mock_tool_def
|
||||
|
||||
# Act
|
||||
|
|
@ -533,7 +548,9 @@ def test_retrieve_tool_definitions_tools_only(manager, mock_arcade_client, make_
|
|||
mock_arcade_client.tools.get.return_value = mock_tool
|
||||
|
||||
# Act
|
||||
results = manager._retrieve_tool_definitions(tools=["GoogleSearch_Search"], toolkits=None)
|
||||
results = manager._retrieve_tool_definitions(
|
||||
tools=["GoogleSearch_Search"], toolkits=None
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert len(results) == 1
|
||||
|
|
@ -541,7 +558,9 @@ def test_retrieve_tool_definitions_tools_only(manager, mock_arcade_client, make_
|
|||
mock_arcade_client.tools.get.assert_called_once_with(name="GoogleSearch_Search")
|
||||
|
||||
|
||||
def test_retrieve_tool_definitions_toolkits_only(manager, mock_arcade_client, make_tool):
|
||||
def test_retrieve_tool_definitions_toolkits_only(
|
||||
manager, mock_arcade_client, make_tool
|
||||
):
|
||||
"""
|
||||
Test the internal _retrieve_tool_definitions method by specifying toolkits.
|
||||
"""
|
||||
|
|
@ -567,7 +586,9 @@ def test_retrieve_tool_definitions_raise_on_empty(manager):
|
|||
"""
|
||||
# Act & Assert
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
manager._retrieve_tool_definitions(tools=None, toolkits=None, raise_on_empty=True)
|
||||
manager._retrieve_tool_definitions(
|
||||
tools=None, toolkits=None, raise_on_empty=True
|
||||
)
|
||||
|
||||
assert "No tools or toolkits provided" in str(excinfo.value)
|
||||
|
||||
|
|
@ -578,7 +599,9 @@ def test_retrieve_tool_definitions_empty_no_raise(manager):
|
|||
are provided and raise_on_empty is False.
|
||||
"""
|
||||
# Act
|
||||
results = manager._retrieve_tool_definitions(tools=None, toolkits=None, raise_on_empty=False)
|
||||
results = manager._retrieve_tool_definitions(
|
||||
tools=None, toolkits=None, raise_on_empty=False
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert results == []
|
||||
|
|
@ -601,9 +624,13 @@ async def test_retrieve_tool_definitions_with_limit_offset_parameterized(
|
|||
|
||||
# Act
|
||||
if is_async:
|
||||
results = await manager._retrieve_tool_definitions(toolkits=["Search"], limit=10, offset=5)
|
||||
results = await manager._retrieve_tool_definitions(
|
||||
toolkits=["Search"], limit=10, offset=5
|
||||
)
|
||||
else:
|
||||
results = manager._retrieve_tool_definitions(toolkits=["Search"], limit=10, offset=5)
|
||||
results = manager._retrieve_tool_definitions(
|
||||
toolkits=["Search"], limit=10, offset=5
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert len(results) > 0
|
||||
|
|
@ -618,7 +645,9 @@ def test_get_client_config_with_kwargs():
|
|||
manager = ToolManager(client=MagicMock()) # Client won't be used here
|
||||
|
||||
# Act
|
||||
with patch.dict("os.environ", {"ARCADE_API_KEY": "env_key", "ARCADE_BASE_URL": "env_url"}):
|
||||
with patch.dict(
|
||||
"os.environ", {"ARCADE_API_KEY": "env_key", "ARCADE_BASE_URL": "env_url"}
|
||||
):
|
||||
result = manager._get_client_config(api_key="kwarg_key", base_url="kwarg_url")
|
||||
|
||||
# Assert
|
||||
|
|
@ -634,7 +663,9 @@ def test_get_client_config_with_env_vars():
|
|||
manager = ToolManager(client=MagicMock()) # Client won't be used here
|
||||
|
||||
# Act
|
||||
with patch.dict("os.environ", {"ARCADE_API_KEY": "env_key", "ARCADE_BASE_URL": "env_url"}):
|
||||
with patch.dict(
|
||||
"os.environ", {"ARCADE_API_KEY": "env_key", "ARCADE_BASE_URL": "env_url"}
|
||||
):
|
||||
result = manager._get_client_config()
|
||||
|
||||
# Assert
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ python =
|
|||
|
||||
[testenv]
|
||||
passenv = PYTHON_VERSION
|
||||
allowlist_externals = poetry
|
||||
allowlist_externals = uv
|
||||
commands =
|
||||
poetry install -v --all-extras
|
||||
pytest --doctest-modules tests --cov --cov-config=pyproject.toml --cov-report=xml
|
||||
uv sync --active --all-extras
|
||||
uv pytest --doctest-modules tests --cov --cov-config=pyproject.toml --cov-report=xml
|
||||
|
|
|
|||
Loading…
Reference in a new issue