feat(web): add custom provider support for embedding & vision models, and fix memory_get Windows path bug!

1. Embedding model: support custom provider
   - Add "custom" entry to EMBEDDING_VENDORS with supports_dim_param=False
   - Parse custom:<id> credentials and model fallback in agent_initializer
   - Expand custom_providers as custom:<id> entries in Web UI dropdown

2. Vision model: support custom provider
   - Add custom:<id> routing in _route_by_provider_id
   - Add _build_custom_provider reading credentials from custom_providers
   - Expand custom_providers in Web UI dropdown, add validation in _set_vision

3. Fix memory_get Windows path validation bug!
   - str.startswith(path+'/') always False on Windows due to backslashes, So All Users can not use "memory_get" tool in Windows.
   - Use os.path.realpath + os.sep, consistent with MemoryService

4. Fix historical needsModel:false bug preventing embedding provider switch
   - Change embedding needsModel to true in console.js
   - Support custom:<id> resolution in cow_cli /memory status, also for adding custom provider support

Closes #2908, Closes #2880
This commit is contained in:
HnBigVolibear
2026-06-19 18:35:44 +08:00
parent a5aaecc48d
commit 8ddfcbb125
7 changed files with 237 additions and 22 deletions

View File

@@ -395,7 +395,13 @@ class AgentInitializer:
from agent.memory.embedding import EMBEDDING_VENDORS
from config import conf
meta = EMBEDDING_VENDORS.get(provider_key)
# Custom providers ("custom:<id>") resolve credentials
# from the custom_providers list.
resolved_provider_key = provider_key
if provider_key.startswith("custom:"):
resolved_provider_key = "custom"
meta = EMBEDDING_VENDORS.get(resolved_provider_key)
if meta is None:
logger.error(
f"[AgentInitializer] Unknown embedding_provider '{provider_key}'. "
@@ -414,7 +420,17 @@ class AgentInitializer:
)
return None
model = (conf().get("embedding_model") or "").strip() or meta["default_model"]
model = (conf().get("embedding_model") or "").strip()
# Custom providers without a model fall back to the provider's default.
if not model and resolved_provider_key == "custom":
from models.custom_provider import parse_custom_bot_type, get_custom_providers, _find_provider_by_id
_, custom_id = parse_custom_bot_type(provider_key)
if custom_id:
entry = _find_provider_by_id(get_custom_providers(), custom_id)
if entry and entry.get("model"):
model = entry["model"]
if not model and resolved_provider_key != "custom":
model = meta["default_model"]
try:
cfg_dim = int(conf().get("embedding_dimensions") or 0)
except (TypeError, ValueError):
@@ -423,7 +439,7 @@ class AgentInitializer:
try:
provider = create_embedding_provider(
provider=provider_key,
provider=resolved_provider_key,
model=model,
api_key=api_key,
api_base=api_base,
@@ -450,6 +466,17 @@ class AgentInitializer:
"""Pick the API key for an explicit embedding provider from config."""
from config import conf
# Custom providers ("custom:<id>") resolve from the custom_providers list.
if provider_key.startswith("custom:"):
from models.custom_provider import parse_custom_bot_type, get_custom_providers, _find_provider_by_id
_, custom_id = parse_custom_bot_type(provider_key)
if custom_id:
providers = get_custom_providers()
entry = _find_provider_by_id(providers, custom_id)
if entry:
return entry.get("api_key", "")
return ""
key_map = {
"openai": "open_ai_api_key",
"linkai": "linkai_api_key",
@@ -470,6 +497,17 @@ class AgentInitializer:
"""Pick the API base for an explicit embedding provider from config."""
from config import conf
# Custom providers ("custom:<id>") resolve from the custom_providers list.
if provider_key.startswith("custom:"):
from models.custom_provider import parse_custom_bot_type, get_custom_providers, _find_provider_by_id
_, custom_id = parse_custom_bot_type(provider_key)
if custom_id:
providers = get_custom_providers()
entry = _find_provider_by_id(providers, custom_id)
if entry and entry.get("api_base"):
return entry["api_base"]
return default_base
base_map = {
"openai": "open_ai_api_base",
"linkai": "linkai_api_base",