Files
wehub-resource-sync 76d991c447
Auto Update PR / update-prs (push) Has been cancelled
CI / format-check (push) Has been cancelled
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / live-api-tests (push) Has been cancelled
CI / plugin-integration-test (push) Has been cancelled
CI / ollama-integration-test (push) Has been cancelled
CI / test-fork-pr (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:37:14 +08:00

160 lines
4.7 KiB
Python

# Copyright 2025 Google LLC.
#
# 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.
"""Provider package for LangExtract.
This package contains provider implementations for various LLM backends.
Each provider can be imported independently for fine-grained dependency
management in build systems.
"""
from importlib import metadata
import importlib
import os
from absl import logging
from langextract.providers import builtin_registry
from langextract.providers import router
registry = router # Backward compat alias
__all__ = [
"gemini",
"openai",
"ollama",
"router",
"registry", # Backward compat
"schemas",
"load_plugins_once",
"load_builtins_once",
]
# Track provider loading for lazy initialization
_plugins_loaded = False # pylint: disable=invalid-name
_builtins_loaded = False # pylint: disable=invalid-name
def load_builtins_once() -> None:
"""Load built-in providers to register their patterns.
Idempotent function that ensures provider patterns are available
for model resolution. Uses lazy registration to ensure providers
can be re-registered after registry.clear() even if their modules
are already in sys.modules.
"""
global _builtins_loaded # pylint: disable=global-statement
# Register built-ins lazily so they can be re-registered after a registry.clear()
# even if their modules were already imported earlier in the test run.
for config in builtin_registry.BUILTIN_PROVIDERS:
router.register_lazy(
*config["patterns"],
target=config["target"],
priority=config["priority"],
)
_builtins_loaded = True
def load_plugins_once() -> None:
"""Load provider plugins from installed packages.
Discovers and loads langextract provider plugins using entry points.
This function is idempotent - multiple calls have no effect.
"""
global _plugins_loaded # pylint: disable=global-statement
if _plugins_loaded:
return
if os.environ.get("LANGEXTRACT_DISABLE_PLUGINS", "").lower() in (
"1",
"true",
"yes",
):
logging.info("Plugin loading disabled via LANGEXTRACT_DISABLE_PLUGINS")
_plugins_loaded = True
return
load_builtins_once()
try:
eps = metadata.entry_points()
# Try different APIs based on what's available
if hasattr(eps, "select"):
# Python 3.10+ API
provider_eps = eps.select(group="langextract.providers")
elif hasattr(eps, "get"):
# Python 3.9 API
provider_eps = eps.get("langextract.providers", [])
else:
# Fallback for older versions
provider_eps = [
ep
for ep in eps
if getattr(ep, "group", None) == "langextract.providers"
]
for entry_point in provider_eps:
try:
provider_class = entry_point.load()
logging.info("Loaded provider plugin: %s", entry_point.name)
if hasattr(provider_class, "get_model_patterns"):
patterns = provider_class.get_model_patterns()
for pattern in patterns:
router.register(
pattern,
priority=getattr(
provider_class,
"pattern_priority",
20, # Default plugin priority
),
)(provider_class)
logging.info(
"Registered %d patterns for %s", len(patterns), entry_point.name
)
except Exception as e:
logging.warning(
"Failed to load provider plugin %s: %s", entry_point.name, e
)
except Exception as e:
logging.warning("Error discovering provider plugins: %s", e)
_plugins_loaded = True
def _reset_for_testing() -> None:
"""Reset plugin loading state for testing. Should only be used in tests."""
global _plugins_loaded, _builtins_loaded # pylint: disable=global-statement
_plugins_loaded = False
_builtins_loaded = False
def __getattr__(name: str):
"""Lazy loading for submodules."""
if name == "router":
return importlib.import_module("langextract.providers.router")
elif name == "schemas":
return importlib.import_module("langextract.providers.schemas")
elif name == "_plugins_loaded":
return _plugins_loaded
elif name == "_builtins_loaded":
return _builtins_loaded
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")