22import os
33import httpx
44from typing import List , Dict , Any , Optional
5+ from langchain_openai import OpenAIEmbeddings
56from ragas .metrics import (
67 LLMContextPrecisionWithoutReference ,
78 LLMContextPrecisionWithReference ,
@@ -69,16 +70,19 @@ def __init__(self, llm_manager: LLMManager):
6970
7071 # Initialize metrics with LLM configuration
7172 try :
73+ # Create embeddings wrapper for metrics that need it
74+ ragas_embeddings = self ._create_ragas_embeddings_wrapper ()
75+
7276 # For LLM-dependent metrics, we need to pass the LLM instance
7377 # RAGAS expects LLM instances to have certain methods
7478 self .available_metrics = {
7579 "context_precision_without_reference" : LLMContextPrecisionWithoutReference (llm = self .llm ),
7680 "context_precision_with_reference" : LLMContextPrecisionWithReference (llm = self .llm ),
7781 "context_recall" : LLMContextRecall (llm = self .llm ),
78- "context_entity_recall" : ContextEntityRecall (),
79- "noise_sensitivity" : NoiseSensitivity (),
80- "answer_relevancy" : AnswerRelevancy (),
81- "faithfulness" : Faithfulness ()
82+ "context_entity_recall" : ContextEntityRecall (llm = self . llm ),
83+ "noise_sensitivity" : NoiseSensitivity (llm = self . llm ),
84+ "answer_relevancy" : AnswerRelevancy (llm = self . llm , embeddings = ragas_embeddings ) if ragas_embeddings else AnswerRelevancy ( llm = self . llm ),
85+ "faithfulness" : Faithfulness (llm = self . llm )
8286 }
8387 self .logger .info (f"RAGAS metrics initialized successfully with { len (self .available_metrics )} metrics" )
8488 except Exception as e :
@@ -376,6 +380,75 @@ def _create_vllm_provider(self, provider_name: str) -> Optional[ChatOpenAI]:
376380 self .logger .error (f"Error creating vLLM provider: { e } " )
377381 return None
378382
383+ def _create_ragas_embeddings_wrapper (self ):
384+ """
385+ Create a RAGAS-compatible embeddings wrapper for metrics that require embeddings
386+ This supports custom OpenAI-compatible embedding endpoints for non-OpenAI models
387+ """
388+ try :
389+ # Check for embedding-specific environment variables
390+ # These can be different from the main LLM configuration
391+ embedding_api_key = os .getenv ("OPENAI_EMBEDDING_API_KEY" )
392+ embedding_base_url = os .getenv ("OPENAI_EMBEDDING_BASE_URL" )
393+ embedding_model = os .getenv ("OPENAI_EMBEDDING_MODEL" , "text-embedding-ada-002" )
394+
395+ # If no embedding-specific config, try to use the main OpenAI config
396+ if not embedding_api_key :
397+ # Check if we have OpenAI as a configured provider
398+ openai_config = self .llm_manager .get_provider_config ("openai" )
399+ if openai_config :
400+ api_key_env = openai_config .get ("api_key_env" , "OPENAI_API_KEY" )
401+ embedding_api_key = os .getenv (api_key_env )
402+ if not embedding_base_url :
403+ # Use the same base URL as the main OpenAI config if available
404+ embedding_base_url = openai_config .get ("base_url" )
405+
406+ if not embedding_api_key :
407+ self .logger .warning ("No embedding API key found. Answer relevancy may not work optimally." )
408+ return None
409+
410+ # Configure embeddings parameters
411+ embeddings_kwargs = {
412+ "model" : embedding_model ,
413+ "openai_api_key" : embedding_api_key
414+ }
415+
416+ # Set custom base URL if provided
417+ if embedding_base_url and embedding_base_url != "https://api.openai.com/v1" :
418+ embeddings_kwargs ["openai_api_base" ] = embedding_base_url
419+ self .logger .info (f"Using custom embeddings base URL: { embedding_base_url } " )
420+
421+ # Handle SSL ignore setting
422+ openai_ignore_ssl = os .getenv ("OPENAI_IGNORE_SSL" , "false" ).lower () == "true"
423+ if openai_ignore_ssl :
424+ try :
425+ # Create HTTP clients with SSL verification disabled
426+ sync_http_client = httpx .Client (verify = False , timeout = 30 )
427+ async_http_client = httpx .AsyncClient (verify = False , timeout = 30 )
428+
429+ embeddings_kwargs ["http_client" ] = sync_http_client
430+ embeddings_kwargs ["http_async_client" ] = async_http_client
431+
432+ self .logger .info ("Created RAGAS embeddings wrapper with SSL verification disabled" )
433+ except Exception as ssl_error :
434+ self .logger .warning (f"Failed to configure SSL ignore for embeddings: { ssl_error } " )
435+ else :
436+ self .logger .info ("Created RAGAS embeddings wrapper with standard SSL" )
437+
438+ # Create LangChain embeddings wrapper with configuration
439+ embeddings = OpenAIEmbeddings (** embeddings_kwargs )
440+
441+ self .logger .info (f"Embeddings configured successfully with model: { embedding_model } " )
442+ if embedding_base_url :
443+ self .logger .info (f"Using custom endpoint: { embedding_base_url } " )
444+
445+ return embeddings
446+
447+ except Exception as e :
448+ self .logger .error (f"Failed to create RAGAS embeddings wrapper: { e } " )
449+ self .logger .warning ("Answer relevancy metric will work with LLM only (may be less optimal)" )
450+ return None
451+
379452 def _prepare_dataset (self , input : Input ) -> Dataset :
380453 """
381454 Convert input to RAGAS-compatible dataset format
@@ -390,7 +463,10 @@ def _prepare_dataset(self, input: Input) -> Dataset:
390463 "contexts" : [contexts ],
391464 "answer" : [input .response ],
392465 "ground_truths" : [[input .reference ]],
393- "reference" : [input .reference ]
466+ "reference" : [input .reference ],
467+ # AnswerRelevancy specific columns
468+ "user_input" : [input .question ],
469+ "response" : [input .response ],
394470 }
395471 self .logger .info (f"Dataset prepared with context" )
396472 return Dataset .from_dict (data )
@@ -429,7 +505,10 @@ def evaluate(self, input: Input) -> Output:
429505 "contexts" : "contexts" ,
430506 "answer" : "answer" ,
431507 "ground_truths" : "ground_truths" ,
432- "reference" : "reference"
508+ "reference" : "reference" ,
509+ # AnswerRelevancy specific mappings
510+ "user_input" : "user_input" ,
511+ "response" : "response" ,
433512 }
434513 )
435514
0 commit comments