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
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,11 @@ git clone https://github.com/ostefani/wepaircode
cd wepaircode

# 2. Create virtual environment
python3 -m venv .venv
python3.14 -m venv .venv
source .venv/bin/activate

# 3. Install dependencies
pip install -r requirements.txt
python3.14 -m pip install -r requirements.txt
python -m pip install -r requirements.txt
python -m pytest tests/ -q


Expand Down
25 changes: 24 additions & 1 deletion agent/embeddings/factory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import importlib
from collections.abc import Callable
from ipaddress import ip_address
from urllib.parse import urlparse

from ..config import Config
from .adapters import (
Expand All @@ -16,16 +18,37 @@
"openai": "agent.embeddings.providers.openai.OpenAIEmbeddingAdapter",
}

LOCAL_DUMMY_API_KEY = "dummy"


def _is_local_api_base(api_base: str) -> bool:
try:
host = urlparse(api_base).hostname or ""
except ValueError:
return False
host = host.lower().rstrip(".")
if host == "localhost":
return True
try:
return ip_address(host).is_loopback
except ValueError:
return False


def build_embedding_provider_config(config: Config) -> EmbeddingProviderConfig:
options = {
key: value
for key, value in dict(config.embedding_provider_options).items()
if key not in RESERVED_EMBEDDING_PROVIDER_OPTION_KEYS
}

api_key = config.embedding_api_key or config.api_key
if not api_key and _is_local_api_base(config.embedding_api_base):
api_key = LOCAL_DUMMY_API_KEY
return EmbeddingProviderConfig(
provider=config.embedding_provider,
api_base=config.embedding_api_base,
api_key=config.embedding_api_key or config.api_key,
api_key=api_key,
model=config.embedding_model,
options=options,
)
Expand Down
36 changes: 36 additions & 0 deletions tests/test_embedding_adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,42 @@ def test_embedding_factory_falls_back_to_primary_api_key(monkeypatch) -> None:
assert instance.calls[0]["input"] == ["hello"]


def test_embedding_factory_defaults_to_dummy_api_key_for_local_servers(
monkeypatch,
) -> None:
AsyncOpenAI, _, _ = _install_fake_openai(monkeypatch)

config = Config(
embedding_provider="openai",
api_key="",
embedding_api_key="",
embedding_api_base="http://localhost:8082/v1",
embedding_model="embed-model",
)

adapter = asyncio.run(create_embedding_adapter(config))
asyncio.run(adapter.embed(["hello"]))

instance = AsyncOpenAI.instances[0]
assert instance.kwargs["api_key"] == embeddings_factory.LOCAL_DUMMY_API_KEY
assert instance.calls[0]["input"] == ["hello"]
asyncio.run(adapter.aclose())


def test_embedding_factory_preserves_empty_api_key_for_remote_servers() -> None:
config = Config(
embedding_provider="openai",
api_key="",
embedding_api_key="",
embedding_api_base="https://api.openai.com/v1",
embedding_model="embed-model",
)

provider_config = embeddings_factory.build_embedding_provider_config(config)

assert provider_config.api_key == ""


def test_embedding_factory_probes_when_configured(monkeypatch) -> None:
AsyncOpenAI, _, _ = _install_fake_openai(monkeypatch)

Expand Down
Loading