This is an automated email from the ASF dual-hosted git repository.

gopidesupavan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 7991cd81c77 Add test_connection support to LangChainHook and 
LlamaIndexHook (#71841)
7991cd81c77 is described below

commit 7991cd81c7713c405d2d69248fa4eb5d078c649b
Author: Jyun-An Chen <[email protected]>
AuthorDate: Sun Aug 23 18:52:58 2026 +0800

    Add test_connection support to LangChainHook and LlamaIndexHook (#71841)
    
    Clicking Test on a LangChain or LlamaIndex connection in the UI always
    reported "doesn't implement or inherit test_connection method" since
    neither hook implemented it, unlike PydanticAIHook and MCPHook which
    already validate their connections this way.
---
 .../airflow/providers/common/ai/hooks/langchain.py | 15 ++++++++++
 .../providers/common/ai/hooks/llamaindex.py        | 15 ++++++++++
 .../tests/unit/common/ai/hooks/test_langchain.py   | 35 ++++++++++++++++++++++
 .../tests/unit/common/ai/hooks/test_llamaindex.py  | 35 ++++++++++++++++++++++
 4 files changed, 100 insertions(+)

diff --git 
a/providers/common/ai/src/airflow/providers/common/ai/hooks/langchain.py 
b/providers/common/ai/src/airflow/providers/common/ai/hooks/langchain.py
index 81834a83764..b2f4e1a8c22 100644
--- a/providers/common/ai/src/airflow/providers/common/ai/hooks/langchain.py
+++ b/providers/common/ai/src/airflow/providers/common/ai/hooks/langchain.py
@@ -171,3 +171,18 @@ class LangChainHook(BaseHook):
             kind="embedding",
         )
         return init_embeddings(model_id, **self._connection_kwargs(conn))
+
+    def test_connection(self) -> tuple[bool, str]:
+        """
+        Test connection by resolving the chat model.
+
+        Validates that the model identifier is valid and the provider can be
+        instantiated with the supplied credentials. Does NOT make an LLM API
+        call -- that would be expensive and fail for reasons unrelated to
+        connectivity (quotas, billing, rate limits).
+        """
+        try:
+            self.get_chat_model()
+            return True, "Model resolved successfully."
+        except Exception as e:
+            return False, str(e)
diff --git 
a/providers/common/ai/src/airflow/providers/common/ai/hooks/llamaindex.py 
b/providers/common/ai/src/airflow/providers/common/ai/hooks/llamaindex.py
index 05e002d8642..354e12afe45 100644
--- a/providers/common/ai/src/airflow/providers/common/ai/hooks/llamaindex.py
+++ b/providers/common/ai/src/airflow/providers/common/ai/hooks/llamaindex.py
@@ -187,3 +187,18 @@ class LlamaIndexHook(BaseHook):
             kind="llm",
         )
         return OpenAI(model=model_id, **self._connection_kwargs(conn))
+
+    def test_connection(self) -> tuple[bool, str]:
+        """
+        Test connection by resolving the LLM.
+
+        Validates that the model identifier is valid and the provider can be
+        instantiated with the supplied credentials. Does NOT make an LLM API
+        call -- that would be expensive and fail for reasons unrelated to
+        connectivity (quotas, billing, rate limits).
+        """
+        try:
+            self.get_llm()
+            return True, "Model resolved successfully."
+        except Exception as e:
+            return False, str(e)
diff --git a/providers/common/ai/tests/unit/common/ai/hooks/test_langchain.py 
b/providers/common/ai/tests/unit/common/ai/hooks/test_langchain.py
index 646f72aa107..e1af49eef96 100644
--- a/providers/common/ai/tests/unit/common/ai/hooks/test_langchain.py
+++ b/providers/common/ai/tests/unit/common/ai/hooks/test_langchain.py
@@ -271,6 +271,41 @@ class TestGetEmbeddingModel:
         
mock_init_embeddings.assert_called_once_with("openai:text-embedding-3-small")
 
 
+class TestConnectionTest:
+    @patch("langchain.chat_models.init_chat_model")
+    @patch.object(LangChainHook, "get_connection")
+    def test_successful_connection(self, mock_get_conn, mock_init_chat_model):
+        mock_get_conn.return_value = _conn(password="sk-test", extra={"model": 
"openai:gpt-4o"})
+
+        hook = LangChainHook()
+        success, message = hook.test_connection()
+
+        assert success is True
+        assert message == "Model resolved successfully."
+
+    @patch("langchain.chat_models.init_chat_model")
+    @patch.object(LangChainHook, "get_connection")
+    def test_failed_connection(self, mock_get_conn, mock_init_chat_model):
+        mock_get_conn.return_value = _conn(password="sk-test", extra={"model": 
"openai:gpt-4o"})
+        mock_init_chat_model.side_effect = ValueError("Unknown provider 
'badprovider'")
+
+        hook = LangChainHook()
+        success, message = hook.test_connection()
+
+        assert success is False
+        assert "Unknown provider" in message
+
+    @patch.object(LangChainHook, "get_connection")
+    def test_failed_connection_no_model(self, mock_get_conn):
+        mock_get_conn.return_value = _conn()
+
+        hook = LangChainHook()
+        success, message = hook.test_connection()
+
+        assert success is False
+        assert "No chat model identifier set" in message
+
+
 class TestSameHookForBoth:
     """A single hook instance must serve both chat and embedding calls."""
 
diff --git a/providers/common/ai/tests/unit/common/ai/hooks/test_llamaindex.py 
b/providers/common/ai/tests/unit/common/ai/hooks/test_llamaindex.py
index 9d6e71790b3..c91866822ce 100644
--- a/providers/common/ai/tests/unit/common/ai/hooks/test_llamaindex.py
+++ b/providers/common/ai/tests/unit/common/ai/hooks/test_llamaindex.py
@@ -168,3 +168,38 @@ class TestGetLlm:
 
         with pytest.raises(ValueError, match="No llm model identifier set"):
             hook.get_llm()
+
+
+class TestConnectionTest:
+    @patch("llama_index.llms.openai.OpenAI")
+    @patch.object(LlamaIndexHook, "get_connection")
+    def test_successful_connection(self, mock_get_conn, mock_cls):
+        mock_get_conn.return_value = _conn(password="sk-test", 
extra={"llm_model": "gpt-4o"})
+
+        hook = LlamaIndexHook()
+        success, message = hook.test_connection()
+
+        assert success is True
+        assert message == "Model resolved successfully."
+
+    @patch("llama_index.llms.openai.OpenAI")
+    @patch.object(LlamaIndexHook, "get_connection")
+    def test_failed_connection(self, mock_get_conn, mock_cls):
+        mock_get_conn.return_value = _conn(password="sk-test", 
extra={"llm_model": "gpt-4o"})
+        mock_cls.side_effect = ValueError("Invalid API key")
+
+        hook = LlamaIndexHook()
+        success, message = hook.test_connection()
+
+        assert success is False
+        assert "Invalid API key" in message
+
+    @patch.object(LlamaIndexHook, "get_connection")
+    def test_failed_connection_no_model(self, mock_get_conn):
+        mock_get_conn.return_value = _conn()
+
+        hook = LlamaIndexHook()
+        success, message = hook.test_connection()
+
+        assert success is False
+        assert "No llm model identifier set" in message

Reply via email to