feat(captain): EmbeddingService aceita provider de embedding dedicado
Permite trocar provider de embedding sem afetar o provider de chat. Útil quando OpenAI key tradicional está fora (ban, billing, etc) mas você quer usar outro provider OpenAI-compatible só pra embeddings — exemplo clássico: Gemini OpenAI-compatible em https://generativelanguage.googleapis.com/v1beta/openai com modelo gemini-embedding-001 + dimensions=1536 (pra bater com schema pgvector). Env vars novas (com fallback pro legacy_openai_settings se não setadas): CAPTAIN_EMBEDDING_API_KEY — API key dedicada pra embeddings CAPTAIN_EMBEDDING_ENDPOINT — base URL sem /v1 (default herda OpenAI) CAPTAIN_EMBEDDING_DIMENSIONS — força redução do vector (ex: 1536) Quando CAPTAIN_EMBEDDING_API_KEY está vazia, comportamento é idêntico ao de antes (legacy_openai_settings). Backward-compatible. Também aceita as variáveis via InstallationConfig (UI) ou ENV — ENV tem precedência (padrão Chatwoot). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8fab08ba57
commit
2ade066468
@ -26,17 +26,47 @@ class Captain::Llm::EmbeddingService
|
||||
|
||||
private
|
||||
|
||||
# Embeddings sempre vão direto pra OpenAI tradicional — o endpoint Codex
|
||||
# via ChatGPT OAuth não expõe /embeddings.
|
||||
# Embeddings vão pra OpenAI tradicional por default (o endpoint Codex
|
||||
# via ChatGPT OAuth não expõe /embeddings). Override opcional via env vars
|
||||
# dedicadas — útil pra trocar provider de embedding (ex: Gemini
|
||||
# OpenAI-compatible) sem alterar o provider de chat:
|
||||
#
|
||||
# CAPTAIN_EMBEDDING_API_KEY — sobrescreve API key
|
||||
# CAPTAIN_EMBEDDING_ENDPOINT — sobrescreve base URL (sem /v1 no final)
|
||||
# CAPTAIN_EMBEDDING_DIMENSIONS — força reduction (ex: 1536 pra Gemini
|
||||
# bater com schema pgvector(1536))
|
||||
def embed_with_legacy_openai(content, model)
|
||||
legacy = Captain::Llm::ProviderConfig.legacy_openai_settings
|
||||
api_base = legacy[:api_base].present? ? "#{legacy[:api_base]}/v1" : nil
|
||||
settings = embedding_settings
|
||||
api_base = settings[:api_base].present? ? "#{settings[:api_base]}/v1" : nil
|
||||
embed_options = embed_extra_options
|
||||
|
||||
Llm::Config.with_api_key(legacy[:api_key], api_base: api_base) do |ctx|
|
||||
ctx.embed(content, model: model).vectors
|
||||
Llm::Config.with_api_key(settings[:api_key], api_base: api_base) do |ctx|
|
||||
ctx.embed(content, model: model, **embed_options).vectors
|
||||
end
|
||||
end
|
||||
|
||||
def embedding_settings
|
||||
custom_key = installation_config_value('CAPTAIN_EMBEDDING_API_KEY')
|
||||
return Captain::Llm::ProviderConfig.legacy_openai_settings if custom_key.blank?
|
||||
|
||||
{
|
||||
api_key: custom_key,
|
||||
api_base: installation_config_value('CAPTAIN_EMBEDDING_ENDPOINT')&.chomp('/')
|
||||
}
|
||||
end
|
||||
|
||||
def embed_extra_options
|
||||
dims = installation_config_value('CAPTAIN_EMBEDDING_DIMENSIONS')
|
||||
return {} if dims.blank?
|
||||
|
||||
{ dimensions: dims.to_i }
|
||||
end
|
||||
|
||||
def installation_config_value(name)
|
||||
ENV.fetch(name, nil) ||
|
||||
InstallationConfig.find_by(name: name)&.value
|
||||
end
|
||||
|
||||
def instrumentation_params(content, model)
|
||||
{
|
||||
span_name: 'llm.captain.embedding',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user