Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,48 @@ jobs:
fi
tox -e live-api

plugin-integration-test:
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'

steps:
- uses: actions/checkout@v4

- name: Detect provider-related changes
id: provider-changes
uses: tj-actions/changed-files@v46
with:
files: |
langextract/providers/**
langextract/factory.py
langextract/inference.py
tests/provider_plugin_test.py
pyproject.toml
.github/workflows/ci.yaml

- name: Skip if no provider changes
if: steps.provider-changes.outputs.any_changed == 'false'
run: |
echo "No provider-related changes detected – skipping plugin integration test."
exit 0

- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: "3.11"

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox

- name: Run plugin smoke test
run: tox -e plugin-smoke

- name: Run plugin integration test
run: tox -e plugin-integration

ollama-integration-test:
needs: test
runs-on: ubuntu-latest
Expand Down
33 changes: 24 additions & 9 deletions langextract/providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,42 @@ def load_plugins_once() -> None:
_PLUGINS_LOADED = True
return

_PLUGINS_LOADED = True

try:
entry_points_group = metadata.entry_points(group="langextract.providers")
except Exception as exc:
logging.debug("No third-party provider entry points found: %s", exc)
return

# Set flag after successful entry point query to avoid disabling discovery
# on transient failures during enumeration.
_PLUGINS_LOADED = True

for entry_point in entry_points_group:
try:
provider = entry_point.load()

# Validate provider subclasses but don't auto-register - plugins must
# use their own @register decorators to control patterns.
if isinstance(provider, type):
registry.register(entry_point.name)(provider)
logging.info(
"Registered third-party provider from entry point: %s",
entry_point.name,
)
# pylint: disable=import-outside-toplevel
# Late import to avoid circular dependency
from langextract import inference

if issubclass(provider, inference.BaseLanguageModel):
logging.info(
"Loaded third-party provider class from entry point: %s",
entry_point.name,
)
else:
logging.warning(
"Entry point %s returned non-provider class %r; ignoring",
entry_point.name,
provider,
)
else:
# Module import triggers decorator execution
logging.debug(
"Loaded provider module from entry point: %s", entry_point.name
"Loaded provider module/object from entry point: %s",
entry_point.name,
)
except Exception as exc:
logging.warning(
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ python_functions = "test_*"
addopts = "-ra"
markers = [
"live_api: marks tests as requiring live API access",
"requires_pip: marks tests that perform pip install/uninstall operations",
"integration: marks integration tests that test multiple components together",
]

[tool.pyink]
Expand Down
Loading
Loading