diff --git a/.env.example b/.env.example index 5e90ec6f..c969132c 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,7 @@ ENV=dev # options: dev|s # Active LLM provider. Selects which provider answers credentials, # metadata, and default-model lookups. Leave unset to default to nv_build. -# Options: openai | anthropic | anthropic_proxy | nv_build +# Options: openai | anthropic | anthropic_proxy | vertexai | nv_build SKILLSPECTOR_PROVIDER= # Provider credentials — set the one matching SKILLSPECTOR_PROVIDER (or @@ -28,6 +28,11 @@ ANTHROPIC_PROXY_API_KEY= # ANTHROPIC_PROXY_API_VERSION=vertex-2023-10-16 # optional; defaults to vertex-2023-10-16 # SKILLSPECTOR_SSL_VERIFY=false # set to false for internal/self-signed CAs +# For SKILLSPECTOR_PROVIDER=vertexai +GOOGLE_APPLICATION_CREDENTIALS= +GOOGLE_CLOUD_PROJECT= +GOOGLE_CLOUD_LOCATION= + # SkillSpector config SKILLSPECTOR_MODEL= # leave empty to use the active provider's bundled default (see README); set to override (e.g. gpt-5.2) # SKILLSPECTOR_MODEL_REGISTRY=./model_registry.yaml # optional override; defaults to each provider's bundled YAML in src/skillspector/providers/ diff --git a/README.md b/README.md index 4a09b50b..ad4e686b 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,7 @@ inference gateways. | `openai` | `OPENAI_API_KEY` (+ optional `OPENAI_BASE_URL`) | api.openai.com (or any OpenAI-compatible URL) | `gpt-5.4` | | `anthropic` | `ANTHROPIC_API_KEY` | api.anthropic.com | `claude-opus-4-6` | | `anthropic_proxy` | `ANTHROPIC_PROXY_API_KEY` + `ANTHROPIC_PROXY_ENDPOINT_URL` | Any Vertex-style raw-predict proxy | `claude-sonnet-4-6` | +| `vertexai` | `GOOGLE_CLOUD_PROJECT` + `GOOGLE_CLOUD_LOCATION` (+ optional `GOOGLE_APPLICATION_CREDENTIALS`) | VertexAI OpenAI-compat endpoint | `gemini-2.5-flash` | | `bedrock` | `AWS_PROFILE` (optional) + `AWS_REGION` — SigV4 via boto3 | AWS Bedrock Runtime | `us.anthropic.claude-sonnet-4-6-20250915-v1:0` | | `nv_build` | `NVIDIA_INFERENCE_KEY` | build.nvidia.com | `deepseek-ai/deepseek-v4-flash` | | `claude_cli` | _(none — uses local CLI auth)_ | local `claude` binary | `claude-sonnet-4-6` | @@ -226,6 +227,14 @@ export SKILLSPECTOR_PROVIDER=nv_build export NVIDIA_INFERENCE_KEY=nvapi-... skillspector scan ./my-skill/ +# VertexAI (Google Cloud) +export SKILLSPECTOR_PROVIDER=vertexai +export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json +export GOOGLE_CLOUD_PROJECT=your-project-id +export GOOGLE_CLOUD_LOCATION=us-central1 +export SKILLSPECTOR_MODEL=gemini-2.5-pro +skillspector scan ./my-skill/ + # Local Claude CLI — no API key; uses your existing `claude auth login` session # Requires: claude CLI installed and authenticated (claude auth login) export SKILLSPECTOR_PROVIDER=claude_cli @@ -526,7 +535,7 @@ Issues (2) | Variable | Description | Required | |----------|-------------|----------| -| `SKILLSPECTOR_PROVIDER` | Active LLM provider: `openai`, `anthropic`, `anthropic_proxy`, `bedrock`, `nv_build`, `claude_cli`, `codex_cli`, or `gemini_cli`. Each provider has its own bundled `model_registry.yaml` and default model (see the LLM Analysis table above). Defaults to `nv_build`. | Optional | +| `SKILLSPECTOR_PROVIDER` | Active LLM provider: `openai`, `anthropic`, `anthropic_proxy`, `vertexai`, `bedrock`, `nv_build`, `claude_cli`, `codex_cli`, or `gemini_cli`. Each provider has its own bundled `model_registry.yaml` and default model (see the LLM Analysis table above). Defaults to `nv_build`. | Optional | | `NVIDIA_INFERENCE_KEY` | Credential for the `nv_build` provider (build.nvidia.com). | Required for LLM analysis when `SKILLSPECTOR_PROVIDER=nv_build` | | `OPENAI_API_KEY` | Credential for the OpenAI provider (`SKILLSPECTOR_PROVIDER=openai`). Also serves as the tier-2 fallback in the credential waterfall when the active provider returns no credentials. | Required for LLM analysis when `SKILLSPECTOR_PROVIDER=openai` | | `OPENAI_BASE_URL` | Override the OpenAI endpoint (e.g. point at Ollama). | Optional | @@ -536,6 +545,9 @@ Issues (2) | `ANTHROPIC_PROXY_API_VERSION` | `anthropic_version` value sent in the request body (default: `vertex-2023-10-16`). | Optional | | `AWS_PROFILE` | Named AWS profile for the Bedrock provider — authenticates via SigV4 through boto3. When unset, the standard boto3 credential chain (env vars, instance metadata, SSO, etc.) resolves. | Optional (used when `SKILLSPECTOR_PROVIDER=bedrock`) | | `AWS_REGION` | AWS region for the Bedrock Runtime endpoint. Defaults to `us-west-2`. | Optional (used when `SKILLSPECTOR_PROVIDER=bedrock`) | +| `GOOGLE_APPLICATION_CREDENTIALS` | Path to a GCP service-account JSON key file. When unset, Application Default Credentials (ADC) resolve via the standard `google.auth.default()` chain. | Optional (used when `SKILLSPECTOR_PROVIDER=vertexai`) | +| `GOOGLE_CLOUD_PROJECT` | GCP project ID for the VertexAI endpoint. | Required when `SKILLSPECTOR_PROVIDER=vertexai` | +| `GOOGLE_CLOUD_LOCATION` | GCP region for the VertexAI endpoint (e.g. `us-central1`). | Required when `SKILLSPECTOR_PROVIDER=vertexai` | | `SKILLSPECTOR_MODEL` | Override the active provider's default model. See the LLM Analysis table for each provider's default. | Optional | | `SKILLSPECTOR_MODEL_REGISTRY` | Override the bundled per-provider YAML registry (`src/skillspector/providers//model_registry.yaml`) with a custom path. | Optional | | `SKILLSPECTOR_LOG_LEVEL` | Log level: `DEBUG`, `INFO`, `WARNING`, `ERROR` (default: `WARNING`). | Optional | diff --git a/model_registry.yaml b/model_registry.yaml index e1c2b8c6..f5288919 100644 --- a/model_registry.yaml +++ b/model_registry.yaml @@ -40,3 +40,24 @@ models: "openai/openai/gpt-5.3-chat": context_length: 128000 max_output_tokens: 16384 + + # Google Gemini models (via VertexAI or AI Studio OpenAI-compatible endpoints) + "gemini-2.0-flash": + context_length: 1048576 + max_output_tokens: 8192 + + "gemini-2.5-pro": + context_length: 1000000 + max_output_tokens: 65536 + + "gemini-2.5-flash": + context_length: 1048576 + max_output_tokens: 65535 + + "gemini-3.1-pro": + context_length: 1000000 + max_output_tokens: 65536 + + "gemini-3.5-flash": + context_length: 1048576 + max_output_tokens: 8192 diff --git a/pyproject.toml b/pyproject.toml index de7a4b46..b5c2209a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,7 @@ dependencies = [ "boto3>=1.34.0", "langsmith>=0.7.30", "yara-python>=4.5.0", + "google-auth>=2.53.0", ] [project.optional-dependencies] diff --git a/src/skillspector/providers/__init__.py b/src/skillspector/providers/__init__.py index 809884dc..0c32ccfc 100644 --- a/src/skillspector/providers/__init__.py +++ b/src/skillspector/providers/__init__.py @@ -25,6 +25,7 @@ openai → OpenAIProvider (api.openai.com) anthropic → AnthropicProvider (api.anthropic.com) anthropic_proxy → AnthropicProxyProvider (Vertex-style raw-predict proxy) + vertexai → VertexAIProvider (VertexAI OpenAI-compat endpoint) bedrock → BedrockProvider (AWS Bedrock Runtime, SigV4) nv_build → NvBuildProvider (build.nvidia.com) claude_cli → ClaudeCLIProvider (local ``claude`` binary, no API key) @@ -89,6 +90,10 @@ def _select_active_provider() -> LLMProvider: from .anthropic_proxy import AnthropicProxyProvider return AnthropicProxyProvider() + if name == "vertexai": + from .vertexai import VertexAIProvider + + return VertexAIProvider() if name == "bedrock": from .bedrock import BedrockProvider @@ -123,7 +128,7 @@ def _select_active_provider() -> LLMProvider: raise ValueError( f"Unknown SKILLSPECTOR_PROVIDER: {name!r}. " - "Expected one of: openai, anthropic, anthropic_proxy, bedrock, nv_build, " + "Expected one of: openai, anthropic, anthropic_proxy, vertexai, bedrock, nv_build, " "claude_cli, codex_cli, gemini_cli, antigravity_cli (or unset)." ) diff --git a/src/skillspector/providers/openai/model_registry.yaml b/src/skillspector/providers/openai/model_registry.yaml index a4d26067..7826f9aa 100644 --- a/src/skillspector/providers/openai/model_registry.yaml +++ b/src/skillspector/providers/openai/model_registry.yaml @@ -12,3 +12,23 @@ models: "gpt-5.4": context_length: 1000000 max_output_tokens: 128000 + # Google Gemini models (via VertexAI or AI Studio OpenAI-compatible endpoints) + "gemini-2.0-flash": + context_length: 1048576 + max_output_tokens: 8192 + + "gemini-2.5-pro": + context_length: 1000000 + max_output_tokens: 65536 + + "gemini-2.5-flash": + context_length: 1048576 + max_output_tokens: 65535 + + "gemini-3.1-pro": + context_length: 1000000 + max_output_tokens: 65536 + + "gemini-3.5-flash": + context_length: 1048576 + max_output_tokens: 8192 \ No newline at end of file diff --git a/src/skillspector/providers/vertexai/__init__.py b/src/skillspector/providers/vertexai/__init__.py new file mode 100644 index 00000000..00991881 --- /dev/null +++ b/src/skillspector/providers/vertexai/__init__.py @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""VertexAI provider package (VertexAI OpenAI-compatibility endpoint).""" + +from .provider import REGISTRY_PATH, VertexAIProvider + +__all__ = ["REGISTRY_PATH", "VertexAIProvider"] diff --git a/src/skillspector/providers/vertexai/model_registry.yaml b/src/skillspector/providers/vertexai/model_registry.yaml new file mode 100644 index 00000000..3fafa8cc --- /dev/null +++ b/src/skillspector/providers/vertexai/model_registry.yaml @@ -0,0 +1,30 @@ +# Token-budget metadata for the VertexAIProvider. Bundled with the +# package; consulted whenever the active provider is VertexAIProvider. +# +# Format: +# models: +# "": +# context_length: # total context window in tokens (required) +# max_output_tokens: # model's max output cap (optional) + +models: + # Google Gemini models (via VertexAI) + "gemini-2.0-flash": + context_length: 1048576 + max_output_tokens: 8192 + + "gemini-2.5-pro": + context_length: 1000000 + max_output_tokens: 65536 + + "gemini-2.5-flash": + context_length: 1048576 + max_output_tokens: 65535 + + "gemini-3.1-pro": + context_length: 1000000 + max_output_tokens: 65536 + + "gemini-3.5-flash": + context_length: 1048576 + max_output_tokens: 8192 diff --git a/src/skillspector/providers/vertexai/provider.py b/src/skillspector/providers/vertexai/provider.py new file mode 100644 index 00000000..76604675 --- /dev/null +++ b/src/skillspector/providers/vertexai/provider.py @@ -0,0 +1,120 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""VertexAI provider — Gemini models via VertexAI OpenAI-compatible endpoint. + +Reads ``GOOGLE_APPLICATION_CREDENTIALS``, ``GOOGLE_CLOUD_PROJECT``, and +``GOOGLE_CLOUD_LOCATION`` for credentials and constructs the VertexAI +OpenAI-compatible endpoint URL. Uses Google Cloud Application Default +Credentials (ADC) to generate access tokens. Defaults to Gemini 2.5 Flash. +""" + +from __future__ import annotations + +import os +from pathlib import Path + +import google.auth +import google.auth.transport.requests +from langchain_core.language_models.chat_models import BaseChatModel + +from skillspector.providers import registry +from skillspector.providers.chat_models import create_openai_compatible_chat_model + +REGISTRY_PATH = str(Path(__file__).with_name("model_registry.yaml")) + + +class VertexAIProvider: + """Stock VertexAI credentials + bundled-YAML metadata provider.""" + + DEFAULT_MODEL = "gemini-2.5-flash" + SLOT_DEFAULTS: dict[str, str] = {} + + WIRE_MODEL_PREFIX = "google/" + + def resolve_credentials(self) -> tuple[str, str | None] | None: + """Return ``(access_token, base_url)`` from Google Cloud credentials. + + Uses Application Default Credentials (ADC) via ``google.auth.default()``. + The access token is refreshed from the credentials object and returned + as the API key for the OpenAI-compatible client. + + Returns ``None`` when required environment variables are not set. + + Raises: + google.auth.exceptions.DefaultCredentialsError: When credentials + are configured but invalid or malformed. + ValueError: When token refresh fails. + """ + + project_id = os.environ.get("GOOGLE_CLOUD_PROJECT", "").strip() + location = os.environ.get("GOOGLE_CLOUD_LOCATION", "").strip() + + if not project_id or not location: + return None + + credentials, _ = google.auth.default( + scopes=["https://www.googleapis.com/auth/cloud-platform"] + ) + + credentials.refresh(google.auth.transport.requests.Request()) + + access_token = credentials.token + if not access_token: + raise ValueError( + "Failed to obtain access token from Google Cloud credentials. " + "Ensure GOOGLE_APPLICATION_CREDENTIALS points to a valid " + "service account key file." + ) + + base_url = ( + f"https://{location}-aiplatform.googleapis.com/v1beta1/" + f"projects/{project_id}/locations/{location}/endpoints/openapi" + ) + + return access_token, base_url + + def create_chat_model( + self, + model: str, + *, + max_tokens: int, + timeout: float | None = 120, + ) -> BaseChatModel | None: + """Create ``ChatOpenAI`` for the VertexAI OpenAI-compatible endpoint. + + The endpoint requires model names prefixed with ``google/`` + (e.g. ``google/gemini-2.5-flash``). The prefix is applied here + at the wire boundary so that registry lookups and token-budget + calculations continue to use bare model labels. + """ + wire_model = model if model.startswith(self.WIRE_MODEL_PREFIX) else f"{self.WIRE_MODEL_PREFIX}{model}" + return create_openai_compatible_chat_model( + model=wire_model, + credentials=self.resolve_credentials(), + max_tokens=max_tokens, + timeout=timeout, + ) + + def get_context_length(self, model: str) -> int | None: + return registry.lookup_context_length(REGISTRY_PATH, model) + + def get_max_output_tokens(self, model: str) -> int | None: + return registry.lookup_max_output_tokens(REGISTRY_PATH, model) + + def resolve_model(self, slot: str = "default") -> str: + """Resolve model: ``SKILLSPECTOR_MODEL`` env > slot default > ``DEFAULT_MODEL``.""" + user_input = os.environ.get("SKILLSPECTOR_MODEL", "").strip() + return user_input or self.SLOT_DEFAULTS.get(slot, "") or self.DEFAULT_MODEL diff --git a/tests/unit/test_providers.py b/tests/unit/test_providers.py index 61937409..f9cf02b4 100644 --- a/tests/unit/test_providers.py +++ b/tests/unit/test_providers.py @@ -23,6 +23,7 @@ from __future__ import annotations import sys +from unittest.mock import MagicMock, patch import pytest from langchain_anthropic import ChatAnthropic @@ -45,6 +46,7 @@ from skillspector.providers.gemini_cli import GeminiCLIProvider from skillspector.providers.nv_build import BUILD_BASE_URL, NvBuildProvider from skillspector.providers.openai import OpenAIProvider +from skillspector.providers.vertexai import VertexAIProvider try: from skillspector.providers.nv_inference import ( @@ -74,6 +76,9 @@ def _clean_provider_env(monkeypatch: pytest.MonkeyPatch): monkeypatch.delenv("SKILLSPECTOR_MODEL", raising=False) monkeypatch.delenv("SKILLSPECTOR_MODEL_REGISTRY", raising=False) monkeypatch.delenv("SKILLSPECTOR_PROVIDER", raising=False) + monkeypatch.delenv("GOOGLE_APPLICATION_CREDENTIALS", raising=False) + monkeypatch.delenv("GOOGLE_CLOUD_PROJECT", raising=False) + monkeypatch.delenv("GOOGLE_CLOUD_LOCATION", raising=False) registry._load.cache_clear() yield registry._load.cache_clear() @@ -275,6 +280,96 @@ def test_metadata_known_models(self) -> None: assert provider.get_context_length("claude-sonnet-4-6") == 1_000_000 +class TestVertexAIProvider: + """VertexAI provider — credentials, model prefix, and bundled YAML metadata.""" + + def test_returns_none_without_env_vars(self) -> None: + assert VertexAIProvider().resolve_credentials() is None + + def test_returns_none_with_partial_env(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("GOOGLE_CLOUD_PROJECT", "my-project") + assert VertexAIProvider().resolve_credentials() is None + + @patch("skillspector.providers.vertexai.provider.google.auth.default") + @patch("skillspector.providers.vertexai.provider.google.auth.transport.requests.Request") + def test_resolves_credentials( + self, + mock_request: MagicMock, + mock_auth_default: MagicMock, + monkeypatch: pytest.MonkeyPatch, + ) -> None: + mock_creds = MagicMock() + mock_creds.token = "fake-access-token" + mock_auth_default.return_value = (mock_creds, "default-project") + monkeypatch.setenv("GOOGLE_CLOUD_PROJECT", "my-project") + monkeypatch.setenv("GOOGLE_CLOUD_LOCATION", "us-central1") + + creds = VertexAIProvider().resolve_credentials() + assert creds is not None + token, base_url = creds + assert token == "fake-access-token" + assert "us-central1" in base_url + assert "my-project" in base_url + assert base_url.endswith("/endpoints/openapi") + + @patch("skillspector.providers.vertexai.provider.google.auth.default") + @patch("skillspector.providers.vertexai.provider.google.auth.transport.requests.Request") + def test_create_chat_model_prefixes_model_with_google( + self, + mock_request: MagicMock, + mock_auth_default: MagicMock, + monkeypatch: pytest.MonkeyPatch, + ) -> None: + mock_creds = MagicMock() + mock_creds.token = "fake-access-token" + mock_auth_default.return_value = (mock_creds, "default-project") + monkeypatch.setenv("GOOGLE_CLOUD_PROJECT", "my-project") + monkeypatch.setenv("GOOGLE_CLOUD_LOCATION", "us-central1") + + llm = VertexAIProvider().create_chat_model("gemini-2.5-flash", max_tokens=123) + assert isinstance(llm, ChatOpenAI) + assert llm.model_name == "google/gemini-2.5-flash" + assert llm.max_tokens == 123 + + @patch("skillspector.providers.vertexai.provider.google.auth.default") + @patch("skillspector.providers.vertexai.provider.google.auth.transport.requests.Request") + def test_create_chat_model_does_not_double_prefix( + self, + mock_request: MagicMock, + mock_auth_default: MagicMock, + monkeypatch: pytest.MonkeyPatch, + ) -> None: + mock_creds = MagicMock() + mock_creds.token = "fake-access-token" + mock_auth_default.return_value = (mock_creds, "default-project") + monkeypatch.setenv("GOOGLE_CLOUD_PROJECT", "my-project") + monkeypatch.setenv("GOOGLE_CLOUD_LOCATION", "us-central1") + + llm = VertexAIProvider().create_chat_model("google/gemini-2.5-flash", max_tokens=123) + assert isinstance(llm, ChatOpenAI) + assert llm.model_name == "google/gemini-2.5-flash" + + def test_create_chat_model_returns_none_without_credentials(self) -> None: + assert VertexAIProvider().create_chat_model("gemini-2.5-flash", max_tokens=123) is None + + def test_metadata_known_model_from_bundled_yaml(self) -> None: + provider = VertexAIProvider() + assert provider.get_context_length("gemini-2.5-flash") == 1_048_576 + assert provider.get_max_output_tokens("gemini-2.5-flash") == 65_535 + + def test_metadata_unknown_model_returns_none(self) -> None: + provider = VertexAIProvider() + assert provider.get_context_length("unknown-model") is None + assert provider.get_max_output_tokens("unknown-model") is None + + def test_resolve_model_default_when_no_env(self) -> None: + assert VertexAIProvider().resolve_model() == "gemini-2.5-flash" + + def test_resolve_model_env_overrides_default(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("SKILLSPECTOR_MODEL", "gemini-2.5-pro") + assert VertexAIProvider().resolve_model() == "gemini-2.5-pro" + + class TestOpenAICompatibleConstructor: """The shared OpenAI-compatible chat-model constructor.""" diff --git a/uv.lock b/uv.lock index af08a456..bbfbcb71 100644 --- a/uv.lock +++ b/uv.lock @@ -661,6 +661,19 @@ version = "0.1.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e6/79/d4f20e91327c98096d605646bdc6a5ffedae820f38d378d3515c42ec5e60/forbiddenfruit-0.1.4.tar.gz", hash = "sha256:e3f7e66561a29ae129aac139a85d610dbf3dd896128187ed5454b6421f624253", size = 43756, upload-time = "2021-01-16T21:03:35.401Z" } +[[package]] +name = "google-auth" +version = "2.55.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "pyasn1-modules" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/6f/f3f4ac177c67bbee8fe8e88f2ab4f36af88c44a096e165c5217accf6e5d3/google_auth-2.55.1.tar.gz", hash = "sha256:fb2d9b730f2c9b8d326ec8d7222f21aef2ead15bf0513793d6442485d87af0a1", size = 349527, upload-time = "2026-06-25T23:39:27.182Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/1d/f6d3ca1ad0725f2e08a1c6915640748a52de2e66596160a4d53b010cccf0/google_auth-2.55.1-py3-none-any.whl", hash = "sha256:eada68dfd52b3b81191827601e2a0c3fa12540c818534b630ddc5355769c3995", size = 252349, upload-time = "2026-06-25T23:38:52.946Z" }, +] + [[package]] name = "googleapis-common-protos" version = "1.75.0" @@ -1970,6 +1983,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" }, ] +[[package]] +name = "pyasn1" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, +] + [[package]] name = "pycparser" version = "3.0" @@ -2664,6 +2698,7 @@ version = "2.3.9" source = { editable = "." } dependencies = [ { name = "boto3" }, + { name = "google-auth" }, { name = "httpx" }, { name = "langchain-anthropic" }, { name = "langchain-aws" }, @@ -2700,6 +2735,7 @@ mcp = [ requires-dist = [ { name = "boto3", specifier = ">=1.34.0" }, { name = "build", marker = "extra == 'dev'", specifier = ">=1.4.0" }, + { name = "google-auth", specifier = ">=2.53.0" }, { name = "httpx", specifier = ">=0.28.0" }, { name = "langchain-anthropic", specifier = ">=1.4.5" }, { name = "langchain-aws", specifier = ">=0.2.0" },