Files
sure/app/models/vector_store/registry.rb
Guillem Arias 566dd75c27 feat(ai): default Anthropic installs to pgvector RAG (4/5)
The provider-agnostic vector store stack (VectorStore::Pgvector + the
Embeddable concern) already shipped to main. This PR closes the
Anthropic loop:

- VectorStore::Registry.adapter_name now returns :pgvector when
  Setting.llm_provider == "anthropic" and no explicit
  VECTOR_STORE_PROVIDER override is set. Anthropic has no hosted vector
  store, so falling back to the local pgvector adapter is the only
  correct default. Explicit VECTOR_STORE_PROVIDER still wins.
- SearchFamilyFiles surfaces a longer message when no adapter is wired
  up — calling out pgvector + EMBEDDING_URI_BASE as the supported
  Anthropic-only path so the user is not stuck with an "OpenAI required"
  hint that is no longer accurate.

The Embeddable concern already pulls embeddings from
EMBEDDING_URI_BASE / EMBEDDING_ACCESS_TOKEN (with OpenAI as fallback),
so Anthropic installs point this at Voyage AI, a local Ollama instance,
or OpenAI embeddings — independent of the chat provider.

Tests cover the new default routing, the existing OpenAI default
staying intact, and explicit VECTOR_STORE_PROVIDER overriding the
Anthropic default.

Stacked on #1985 (PR 3/5). 5/5 settings UI + retention disclosure next.
2026-05-29 14:51:10 +02:00

78 lines
2.3 KiB
Ruby

class VectorStore::Registry
ADAPTERS = {
openai: "VectorStore::Openai",
pgvector: "VectorStore::Pgvector",
qdrant: "VectorStore::Qdrant"
}.freeze
class << self
# Returns the configured adapter instance.
# Reads from VECTOR_STORE_PROVIDER env var, falling back to :openai
# when OpenAI credentials are present.
def adapter
name = adapter_name
return nil unless name
build_adapter(name)
end
def configured?
adapter.present?
end
def adapter_name
explicit = ENV["VECTOR_STORE_PROVIDER"].presence
return explicit.to_sym if explicit && ADAPTERS.key?(explicit.to_sym)
# Default routing:
# - When the configured LLM provider is Anthropic (which has no hosted
# vector store), fall back to the local pgvector adapter. The
# Embeddable concern still pulls embeddings from EMBEDDING_URI_BASE /
# OPENAI_ACCESS_TOKEN — Anthropic users typically point this at
# Voyage AI, a local Ollama instance, or OpenAI embeddings.
# - Otherwise, use OpenAI when credentials are available.
return :pgvector if Setting.llm_provider == "anthropic"
:openai if openai_access_token.present?
end
private
def build_adapter(name)
klass = ADAPTERS[name]&.safe_constantize
raise VectorStore::ConfigurationError, "Unknown vector store adapter: #{name}" unless klass
case name
when :openai then build_openai
when :pgvector then build_pgvector
when :qdrant then build_qdrant
else raise VectorStore::ConfigurationError, "No builder defined for adapter: #{name}"
end
end
def build_openai
token = openai_access_token
return nil unless token.present?
VectorStore::Openai.new(
access_token: token,
uri_base: ENV["OPENAI_URI_BASE"].presence || Setting.openai_uri_base
)
end
def build_pgvector
VectorStore::Pgvector.new
end
def build_qdrant
url = ENV.fetch("QDRANT_URL", "http://localhost:6333")
api_key = ENV["QDRANT_API_KEY"].presence
VectorStore::Qdrant.new(url: url, api_key: api_key)
end
def openai_access_token
ENV["OPENAI_ACCESS_TOKEN"].presence || Setting.openai_access_token
end
end
end