Skip to content
Open
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

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ all = [
"langchain-community (>=0.2.0,<0.4.0)",
"sentencepiece (>=0.2.0,<0.3.0)",
"langchain-openai (>=0.1.8)",
"langchain-google-genai",
"google-genai",
"scipy",
"statsmodels (>=0.14.2,<0.15.0)",
"langdetect",
Expand Down Expand Up @@ -79,6 +81,8 @@ llm = [
"datasets (>=4.4,<5.0.0) ; python_version >= '3.14'",
"sentencepiece (>=0.2.0,<0.3.0)",
"langchain-openai (>=0.1.8)",
"langchain-google-genai",
"google-genai",
"pyarrow (<16) ; python_version < '3.13'",
"pyarrow (>=20,<21) ; python_version == '3.13'",
"pyarrow (>=22,<23) ; python_version >= '3.14'",
Expand Down
48 changes: 48 additions & 0 deletions tests/unit_tests/model_validation/ragas/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from types import SimpleNamespace

from langchain_core.messages import AIMessage
from langchain_core.outputs import ChatGeneration

from validmind.tests.model_validation.ragas.utils import _ragas_is_finished_parser


def test_ragas_is_finished_parser_accepts_gemini_stop_reason():
response = SimpleNamespace(
flatten=lambda: [
SimpleNamespace(
generations=[
[
ChatGeneration(
message=AIMessage(
content="done",
response_metadata={"finish_reason": "STOP"},
)
)
]
]
)
]
)

assert _ragas_is_finished_parser(response) is True


def test_ragas_is_finished_parser_accepts_max_tokens_reason():
response = SimpleNamespace(
flatten=lambda: [
SimpleNamespace(
generations=[
[
ChatGeneration(
message=AIMessage(
content="partial",
response_metadata={"finish_reason": "MAX_TOKENS"},
)
)
]
]
)
]
)

assert _ragas_is_finished_parser(response) is True
99 changes: 99 additions & 0 deletions tests/unit_tests/test_ai_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import os
import sys
import types
from unittest import mock

from validmind.ai import utils as ai_utils


def _reset_ai_utils_state():
ai_utils.__dict__["__client"] = None
ai_utils.__dict__["__model"] = None
ai_utils.__dict__["__judge_llm"] = None
ai_utils.__dict__["__judge_embeddings"] = None
ai_utils.__dict__["__ack"] = None


def test_get_client_and_model_supports_gemini_env():
_reset_ai_utils_state()

with mock.patch.dict(os.environ, {"GEMINI_API_KEY": "test-key"}, clear=True):
client, model = ai_utils.get_client_and_model()

assert client is None
assert model == ai_utils.GEMINI_MODEL


def test_get_judge_config_builds_gemini_models():
_reset_ai_utils_state()

class FakeChatGoogleGenerativeAI:
def __init__(self, **kwargs):
self.kwargs = kwargs

class FakeGoogleGenerativeAIEmbeddings:
def __init__(self, **kwargs):
self.kwargs = kwargs

fake_module = types.SimpleNamespace(
ChatGoogleGenerativeAI=FakeChatGoogleGenerativeAI,
GoogleGenerativeAIEmbeddings=FakeGoogleGenerativeAIEmbeddings,
)

with mock.patch.dict(
os.environ,
{"GEMINI_API_KEY": "test-key", "GEMINI_MODEL": "gemini-test-model"},
clear=True,
), mock.patch.dict(sys.modules, {"langchain_google_genai": fake_module}):
judge_llm, judge_embeddings = ai_utils.get_judge_config()

assert isinstance(judge_llm, FakeChatGoogleGenerativeAI)
assert judge_llm.kwargs == {
"model": "gemini-test-model",
"api_key": "test-key",
}
assert isinstance(judge_embeddings, FakeGoogleGenerativeAIEmbeddings)
assert judge_embeddings.kwargs == {
"model": ai_utils.GEMINI_EMBEDDINGS_MODEL,
"google_api_key": "test-key",
}


def test_is_configured_uses_resolved_judge_model():
_reset_ai_utils_state()

class FakeJudgeLLM:
def invoke(self, messages):
assert messages == [("user", "ping")]
return types.SimpleNamespace(content="pong")

with mock.patch.object(
ai_utils,
"get_judge_config",
return_value=(FakeJudgeLLM(), None),
):
assert ai_utils.is_configured() is True


def test_get_deepeval_model_supports_gemini_env():
_reset_ai_utils_state()

class FakeGeminiModel:
def __init__(self, **kwargs):
self.kwargs = kwargs

fake_module = types.SimpleNamespace(GeminiModel=FakeGeminiModel)

with mock.patch.dict(
os.environ,
{"GEMINI_API_KEY": "test-key", "GEMINI_MODEL": "gemini-test-model"},
clear=True,
), mock.patch.dict(sys.modules, {"deepeval.models": fake_module}):
model = ai_utils.get_deepeval_model()

assert isinstance(model, FakeGeminiModel)
assert model.kwargs == {
"model": "gemini-test-model",
"api_key": "test-key",
"temperature": 0,
}
Loading
Loading