Files
wehub-resource-sync fbfefa28d3
CodeQL / Analyze (python) (push) Failing after 0s
Release / Build (push) Failing after 1s
Test Suite / Unit Tests (push) Failing after 0s
Release / Release (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:18:10 +08:00

317 lines
11 KiB
Python

from unittest.mock import Mock, patch
import pytest
from langchain_aws import ChatBedrock
from langchain_ollama import ChatOllama
from langchain_openai import AzureChatOpenAI, ChatOpenAI
from scrapegraphai.graphs import AbstractGraph, BaseGraph
from scrapegraphai.models import DeepSeek, OneApi
from scrapegraphai.nodes import FetchNode, ParseNode
"""
Tests for the AbstractGraph.
"""
def test_llm_missing_tokens(monkeypatch, capsys):
"""Test that missing model tokens causes default to 8192 with an appropriate warning printed."""
# Patch out models_tokens to simulate missing tokens for the given model
from scrapegraphai.graphs import abstract_graph
monkeypatch.setattr(
abstract_graph, "models_tokens", {"openai": {"gpt-3.5-turbo": 4096}}
)
llm_config = {"model": "openai/not-known-model", "openai_api_key": "test"}
# Patch _create_graph to return a dummy graph to avoid real graph creation
with patch.object(TestGraph, "_create_graph", return_value=Mock(nodes=[])):
graph = TestGraph("Test prompt", {"llm": llm_config})
# Since "not-known-model" is missing, it should default to 8192
assert graph.model_token == 8192
captured = capsys.readouterr().out
assert "Max input tokens for model" in captured
def test_burr_kwargs():
"""Test that burr_kwargs configuration correctly sets use_burr and burr_config on the graph."""
dummy_graph = Mock()
dummy_graph.nodes = []
with patch.object(TestGraph, "_create_graph", return_value=dummy_graph):
config = {
"llm": {"model": "openai/gpt-3.5-turbo", "openai_api_key": "sk-test"},
"burr_kwargs": {"some_key": "some_value"},
}
TestGraph("Test prompt", config)
# Check that the burr_kwargs have been applied and an app_instance_id added if missing
assert dummy_graph.use_burr is True
assert dummy_graph.burr_config["some_key"] == "some_value"
assert "app_instance_id" in dummy_graph.burr_config
def test_set_common_params():
"""
Test that the set_common_params method correctly updates the configuration
of all nodes in the graph.
"""
# Create a mock graph with mock nodes
mock_graph = Mock()
mock_node1 = Mock()
mock_node2 = Mock()
mock_graph.nodes = [mock_node1, mock_node2]
# Create a TestGraph instance with the mock graph
with patch.object(TestGraph, "_create_graph", return_value=mock_graph):
graph = TestGraph(
"Test prompt",
{"llm": {"model": "openai/gpt-3.5-turbo", "openai_api_key": "sk-test"}},
)
# Reset mock call counts before testing set_common_params
mock_node1.update_config.reset_mock()
mock_node2.update_config.reset_mock()
# Call set_common_params with test parameters
test_params = {"param1": "value1", "param2": "value2"}
graph.set_common_params(test_params)
# Assert that update_config was called on each node with the correct parameters
mock_node1.update_config.assert_called_once_with(test_params, False)
mock_node2.update_config.assert_called_once_with(test_params, False)
class TestGraph(AbstractGraph):
def __init__(self, prompt: str, config: dict):
super().__init__(prompt, config)
def _create_graph(self) -> BaseGraph:
fetch_node = FetchNode(
input="url| local_dir",
output=["doc"],
node_config={
"llm_model": self.llm_model,
"force": self.config.get("force", False),
"cut": self.config.get("cut", True),
"loader_kwargs": self.config.get("loader_kwargs", {}),
"browser_base": self.config.get("browser_base"),
},
)
parse_node = ParseNode(
input="doc",
output=["parsed_doc"],
node_config={"llm_model": self.llm_model, "chunk_size": self.model_token},
)
return BaseGraph(
nodes=[fetch_node, parse_node],
edges=[
(fetch_node, parse_node),
],
entry_point=fetch_node,
graph_name=self.__class__.__name__,
)
def run(self) -> str:
inputs = {"user_prompt": self.prompt, self.input_key: self.source}
self.final_state, self.execution_info = self.graph.execute(inputs)
return self.final_state.get("answer", "No answer found.")
class TestAbstractGraph:
@pytest.mark.parametrize(
"llm_config, expected_model",
[
(
{"model": "openai/gpt-3.5-turbo", "openai_api_key": "sk-randomtest001"},
ChatOpenAI,
),
(
{
"model": "azure_openai/gpt-3.5-turbo",
"api_key": "random-api-key",
"api_version": "no version",
"azure_endpoint": "https://www.example.com/",
},
AzureChatOpenAI,
),
({"model": "ollama/llama2"}, ChatOllama),
({"model": "oneapi/qwen-turbo", "api_key": "oneapi-api-key"}, OneApi),
(
{"model": "deepseek/deepseek-coder", "api_key": "deepseek-api-key"},
DeepSeek,
),
(
{
"model": "bedrock/anthropic.claude-3-sonnet-20240229-v1:0",
"region_name": "IDK",
"temperature": 0.7,
},
ChatBedrock,
),
],
)
def test_create_llm(self, llm_config, expected_model):
graph = TestGraph("Test prompt", {"llm": llm_config})
assert isinstance(graph.llm_model, expected_model)
def test_create_llm_unknown_provider(self):
with pytest.raises(ValueError):
TestGraph("Test prompt", {"llm": {"model": "unknown_provider/model"}})
@pytest.mark.parametrize(
"llm_config, expected_model",
[
(
{
"model": "openai/gpt-3.5-turbo",
"openai_api_key": "sk-randomtest001",
"rate_limit": {"requests_per_second": 1},
},
ChatOpenAI,
),
(
{
"model": "azure_openai/gpt-3.5-turbo",
"api_key": "random-api-key",
"api_version": "no version",
"azure_endpoint": "https://www.example.com/",
"rate_limit": {"requests_per_second": 1},
},
AzureChatOpenAI,
),
(
{"model": "ollama/llama2", "rate_limit": {"requests_per_second": 1}},
ChatOllama,
),
(
{
"model": "oneapi/qwen-turbo",
"api_key": "oneapi-api-key",
"rate_limit": {"requests_per_second": 1},
},
OneApi,
),
(
{
"model": "deepseek/deepseek-coder",
"api_key": "deepseek-api-key",
"rate_limit": {"requests_per_second": 1},
},
DeepSeek,
),
(
{
"model": "bedrock/anthropic.claude-3-sonnet-20240229-v1:0",
"region_name": "IDK",
"temperature": 0.7,
"rate_limit": {"requests_per_second": 1},
},
ChatBedrock,
),
],
)
def test_create_llm_with_rate_limit(self, llm_config, expected_model):
graph = TestGraph("Test prompt", {"llm": llm_config})
assert isinstance(graph.llm_model, expected_model)
@pytest.mark.asyncio
async def test_run_safe_async(self):
graph = TestGraph(
"Test prompt",
{
"llm": {
"model": "openai/gpt-3.5-turbo",
"openai_api_key": "sk-randomtest001",
}
},
)
with patch.object(graph, "run", return_value="Async result") as mock_run:
result = await graph.run_safe_async()
assert result == "Async result"
mock_run.assert_called_once()
def test_create_llm_with_custom_model_instance(self):
"""
Test that the _create_llm method correctly uses a custom model instance
when provided in the configuration.
"""
mock_model = Mock()
mock_model.model_name = "custom-model"
config = {
"llm": {
"model_instance": mock_model,
"model_tokens": 1000,
"model": "custom/model",
}
}
graph = TestGraph("Test prompt", config)
assert graph.llm_model == mock_model
assert graph.model_token == 1000
def test_set_common_params(self):
"""
Test that the set_common_params method correctly updates the configuration
of all nodes in the graph.
"""
# Create a mock graph with mock nodes
mock_graph = Mock()
mock_node1 = Mock()
mock_node2 = Mock()
mock_graph.nodes = [mock_node1, mock_node2]
# Create a TestGraph instance with the mock graph
with patch(
"scrapegraphai.graphs.abstract_graph.AbstractGraph._create_graph",
return_value=mock_graph,
):
graph = TestGraph(
"Test prompt",
{"llm": {"model": "openai/gpt-3.5-turbo", "openai_api_key": "sk-test"}},
)
# Call set_common_params with test parameters
test_params = {"param1": "value1", "param2": "value2"}
graph.set_common_params(test_params)
# Assert that update_config was called on each node with the correct parameters
def test_get_state(self):
"""Test that get_state returns the correct final state with or without a provided key, and raises KeyError for missing keys."""
graph = TestGraph(
"dummy",
{"llm": {"model": "openai/gpt-3.5-turbo", "openai_api_key": "sk-test"}},
)
# Set a dummy final state
graph.final_state = {"answer": "42", "other": "value"}
# Test without a key returns the entire final_state
state = graph.get_state()
assert state == {"answer": "42", "other": "value"}
# Test with a valid key returns the specific value
answer = graph.get_state("answer")
assert answer == "42"
# Test that a missing key raises a KeyError
with pytest.raises(KeyError):
_ = graph.get_state("nonexistent")
def test_append_node(self):
"""Test that append_node correctly delegates to the graph's append_node method."""
graph = TestGraph(
"dummy",
{"llm": {"model": "openai/gpt-3.5-turbo", "openai_api_key": "sk-test"}},
)
# Replace the graph object with a mock that has append_node
mock_graph = Mock()
graph.graph = mock_graph
dummy_node = Mock()
graph.append_node(dummy_node)
mock_graph.append_node.assert_called_once_with(dummy_node)
def test_get_execution_info(self):
"""Test that get_execution_info returns the execution info stored in the graph."""
graph = TestGraph(
"dummy",
{"llm": {"model": "openai/gpt-3.5-turbo", "openai_api_key": "sk-test"}},
)
dummy_info = {"execution": "info", "status": "ok"}
graph.execution_info = dummy_info
info = graph.get_execution_info()
assert info == dummy_info