mirror of
https://github.com/langgenius/dify.git
synced 2026-05-12 07:37:09 +08:00
chore(api): upgrade graphon to v0.3.1 (#35987)
Co-authored-by: -LAN- <laipz8200@outlook.com>
This commit is contained in:
parent
b108ea42f6
commit
74a04afe27
@ -378,6 +378,7 @@ class DifyToolNodeRuntime(ToolNodeRuntimeProtocol):
|
||||
node_id: str,
|
||||
node_data: ToolNodeData,
|
||||
variable_pool,
|
||||
node_execution_id: str | None = None,
|
||||
) -> ToolRuntimeHandle:
|
||||
try:
|
||||
tool_runtime = ToolManager.get_workflow_tool_runtime(
|
||||
|
||||
@ -45,7 +45,7 @@ dependencies = [
|
||||
|
||||
# Emerging: newer and fast-moving, use compatible pins
|
||||
"fastopenapi[flask]~=0.7.0",
|
||||
"graphon~=0.3.0",
|
||||
"graphon~=0.3.1",
|
||||
"httpx-sse~=0.4.0",
|
||||
"json-repair~=0.59.4",
|
||||
]
|
||||
|
||||
@ -91,7 +91,11 @@ def init_llm_node(config: dict) -> LLMNode:
|
||||
return node
|
||||
|
||||
|
||||
def test_execute_llm():
|
||||
def _mock_db_session_close(monkeypatch) -> None:
|
||||
monkeypatch.setattr(db.session, "close", MagicMock())
|
||||
|
||||
|
||||
def test_execute_llm(monkeypatch):
|
||||
node = init_llm_node(
|
||||
config={
|
||||
"id": "llm",
|
||||
@ -118,7 +122,7 @@ def test_execute_llm():
|
||||
},
|
||||
)
|
||||
|
||||
db.session.close = MagicMock()
|
||||
_mock_db_session_close(monkeypatch)
|
||||
|
||||
def build_mock_model_instance() -> MagicMock:
|
||||
from decimal import Decimal
|
||||
@ -195,7 +199,7 @@ def test_execute_llm():
|
||||
assert item.node_run_result.outputs.get("usage", {})["total_tokens"] > 0
|
||||
|
||||
|
||||
def test_execute_llm_with_jinja2():
|
||||
def test_execute_llm_with_jinja2(monkeypatch):
|
||||
"""
|
||||
Test execute LLM node with jinja2
|
||||
"""
|
||||
@ -233,8 +237,7 @@ def test_execute_llm_with_jinja2():
|
||||
},
|
||||
)
|
||||
|
||||
# Mock db.session.close()
|
||||
db.session.close = MagicMock()
|
||||
_mock_db_session_close(monkeypatch)
|
||||
|
||||
def build_mock_model_instance() -> MagicMock:
|
||||
from decimal import Decimal
|
||||
|
||||
@ -83,7 +83,11 @@ def init_parameter_extractor_node(config: dict, memory=None):
|
||||
return node
|
||||
|
||||
|
||||
def test_function_calling_parameter_extractor(setup_model_mock):
|
||||
def _mock_db_session_close(monkeypatch) -> None:
|
||||
monkeypatch.setattr(db.session, "close", MagicMock())
|
||||
|
||||
|
||||
def test_function_calling_parameter_extractor(setup_model_mock, monkeypatch):
|
||||
"""
|
||||
Test function calling for parameter extractor.
|
||||
"""
|
||||
@ -114,7 +118,7 @@ def test_function_calling_parameter_extractor(setup_model_mock):
|
||||
mode="chat",
|
||||
credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
|
||||
)()
|
||||
db.session.close = MagicMock()
|
||||
_mock_db_session_close(monkeypatch)
|
||||
|
||||
result = node._run()
|
||||
|
||||
@ -124,7 +128,7 @@ def test_function_calling_parameter_extractor(setup_model_mock):
|
||||
assert result.outputs.get("__reason") == None
|
||||
|
||||
|
||||
def test_instructions(setup_model_mock):
|
||||
def test_instructions(setup_model_mock, monkeypatch):
|
||||
"""
|
||||
Test chat parameter extractor.
|
||||
"""
|
||||
@ -155,7 +159,7 @@ def test_instructions(setup_model_mock):
|
||||
mode="chat",
|
||||
credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
|
||||
)()
|
||||
db.session.close = MagicMock()
|
||||
_mock_db_session_close(monkeypatch)
|
||||
|
||||
result = node._run()
|
||||
|
||||
@ -174,7 +178,7 @@ def test_instructions(setup_model_mock):
|
||||
assert "what's the weather in SF" in prompt.get("text")
|
||||
|
||||
|
||||
def test_chat_parameter_extractor(setup_model_mock):
|
||||
def test_chat_parameter_extractor(setup_model_mock, monkeypatch):
|
||||
"""
|
||||
Test chat parameter extractor.
|
||||
"""
|
||||
@ -205,7 +209,7 @@ def test_chat_parameter_extractor(setup_model_mock):
|
||||
mode="chat",
|
||||
credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
|
||||
)()
|
||||
db.session.close = MagicMock()
|
||||
_mock_db_session_close(monkeypatch)
|
||||
|
||||
result = node._run()
|
||||
|
||||
@ -225,7 +229,7 @@ def test_chat_parameter_extractor(setup_model_mock):
|
||||
assert '<structure>\n{"type": "object"' in prompt.get("text")
|
||||
|
||||
|
||||
def test_completion_parameter_extractor(setup_model_mock):
|
||||
def test_completion_parameter_extractor(setup_model_mock, monkeypatch):
|
||||
"""
|
||||
Test completion parameter extractor.
|
||||
"""
|
||||
@ -256,7 +260,7 @@ def test_completion_parameter_extractor(setup_model_mock):
|
||||
mode="completion",
|
||||
credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
|
||||
)()
|
||||
db.session.close = MagicMock()
|
||||
_mock_db_session_close(monkeypatch)
|
||||
|
||||
result = node._run()
|
||||
|
||||
@ -350,7 +354,7 @@ def test_extract_json_from_tool_call():
|
||||
assert result["location"] == "kawaii"
|
||||
|
||||
|
||||
def test_chat_parameter_extractor_with_memory(setup_model_mock):
|
||||
def test_chat_parameter_extractor_with_memory(setup_model_mock, monkeypatch):
|
||||
"""
|
||||
Test chat parameter extractor with memory.
|
||||
"""
|
||||
@ -382,7 +386,7 @@ def test_chat_parameter_extractor_with_memory(setup_model_mock):
|
||||
mode="chat",
|
||||
credentials={"openai_api_key": os.environ.get("OPENAI_API_KEY")},
|
||||
)()
|
||||
db.session.close = MagicMock()
|
||||
_mock_db_session_close(monkeypatch)
|
||||
|
||||
result = node._run()
|
||||
|
||||
|
||||
@ -168,6 +168,7 @@ def test_node_variable_collection_get_success(
|
||||
account, tenant = create_console_account_and_tenant(db_session_with_containers)
|
||||
app = create_console_app(db_session_with_containers, tenant.id, account.id, AppMode.WORKFLOW)
|
||||
node_variable = _create_node_variable(db_session_with_containers, app.id, account.id, node_id="node_123")
|
||||
node_variable_id = node_variable.id
|
||||
_create_node_variable(db_session_with_containers, app.id, account.id, node_id="node_456", name="other")
|
||||
|
||||
response = test_client_with_containers.get(
|
||||
@ -178,7 +179,7 @@ def test_node_variable_collection_get_success(
|
||||
assert response.status_code == 200
|
||||
payload = response.get_json()
|
||||
assert payload is not None
|
||||
assert [item["id"] for item in payload["items"]] == [node_variable.id]
|
||||
assert [item["id"] for item in payload["items"]] == [node_variable_id]
|
||||
|
||||
|
||||
def test_node_variable_collection_get_invalid_node_id(
|
||||
@ -377,6 +378,7 @@ def test_system_variable_collection_get(
|
||||
account, tenant = create_console_account_and_tenant(db_session_with_containers)
|
||||
app = create_console_app(db_session_with_containers, tenant.id, account.id, AppMode.WORKFLOW)
|
||||
variable = _create_system_variable(db_session_with_containers, app.id, account.id)
|
||||
variable_id = variable.id
|
||||
|
||||
response = test_client_with_containers.get(
|
||||
f"/console/api/apps/{app.id}/workflows/draft/system-variables",
|
||||
@ -386,7 +388,7 @@ def test_system_variable_collection_get(
|
||||
assert response.status_code == 200
|
||||
payload = response.get_json()
|
||||
assert payload is not None
|
||||
assert [item["id"] for item in payload["items"]] == [variable.id]
|
||||
assert [item["id"] for item in payload["items"]] == [variable_id]
|
||||
|
||||
|
||||
def test_environment_variable_collection_get(
|
||||
|
||||
@ -17,6 +17,8 @@ def test_get_oauth_url_successful(
|
||||
test_client_with_containers: FlaskClient,
|
||||
) -> None:
|
||||
account, tenant = create_console_account_and_tenant(db_session_with_containers)
|
||||
tenant_id = tenant.id
|
||||
current_tenant_id = account.current_tenant_id
|
||||
provider = MagicMock()
|
||||
provider.get_authorization_url.return_value = "http://oauth.provider/auth"
|
||||
|
||||
@ -29,7 +31,7 @@ def test_get_oauth_url_successful(
|
||||
headers=authenticate_console_client(test_client_with_containers, account),
|
||||
)
|
||||
|
||||
assert tenant.id == account.current_tenant_id
|
||||
assert tenant_id == current_tenant_id
|
||||
assert response.status_code == 200
|
||||
assert response.get_json() == {"data": "http://oauth.provider/auth"}
|
||||
provider.get_authorization_url.assert_called_once()
|
||||
|
||||
@ -6,6 +6,7 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from controllers.console.auth.error import (
|
||||
EmailCodeError,
|
||||
@ -20,13 +21,15 @@ from controllers.console.auth.forgot_password import (
|
||||
ForgotPasswordSendEmailApi,
|
||||
)
|
||||
from controllers.console.error import AccountNotFound, EmailSendIpLimitError
|
||||
from tests.test_containers_integration_tests.controllers.console.helpers import ensure_dify_setup
|
||||
|
||||
|
||||
class TestForgotPasswordSendEmailApi:
|
||||
"""Test cases for sending password reset emails."""
|
||||
|
||||
@pytest.fixture
|
||||
def app(self, flask_app_with_containers: Flask):
|
||||
def app(self, flask_app_with_containers: Flask, db_session_with_containers: Session):
|
||||
ensure_dify_setup(db_session_with_containers)
|
||||
return flask_app_with_containers
|
||||
|
||||
@pytest.fixture
|
||||
@ -139,7 +142,8 @@ class TestForgotPasswordCheckApi:
|
||||
"""Test cases for verifying password reset codes."""
|
||||
|
||||
@pytest.fixture
|
||||
def app(self, flask_app_with_containers: Flask):
|
||||
def app(self, flask_app_with_containers: Flask, db_session_with_containers: Session):
|
||||
ensure_dify_setup(db_session_with_containers)
|
||||
return flask_app_with_containers
|
||||
|
||||
@patch("controllers.console.auth.forgot_password.AccountService.is_forgot_password_error_rate_limit")
|
||||
@ -322,7 +326,8 @@ class TestForgotPasswordResetApi:
|
||||
"""Test cases for resetting password with verified token."""
|
||||
|
||||
@pytest.fixture
|
||||
def app(self, flask_app_with_containers: Flask):
|
||||
def app(self, flask_app_with_containers: Flask, db_session_with_containers: Session):
|
||||
ensure_dify_setup(db_session_with_containers)
|
||||
return flask_app_with_containers
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@ -233,8 +233,6 @@ class TestSegmentTypeAdditionalMethods:
|
||||
assert SegmentType.GROUP.is_valid([StringSegment(value="b")]) is True
|
||||
assert SegmentType.GROUP.is_valid(["not-segment"]) is False
|
||||
|
||||
def test_unreachable_assertion_branch(self, monkeypatch: pytest.MonkeyPatch):
|
||||
monkeypatch.setattr(SegmentType, "is_array_type", lambda self: False)
|
||||
|
||||
with pytest.raises(AssertionError, match="unreachable"):
|
||||
SegmentType.ARRAY_STRING.is_valid(["a"])
|
||||
def test_unreachable_assertion_branch(self):
|
||||
with pytest.raises(AssertionError, match="Expected code to be unreachable"):
|
||||
SegmentType.is_valid("not-a-segment-type", None) # type: ignore[arg-type]
|
||||
|
||||
@ -613,7 +613,7 @@ def test_combine_message_content_with_role_handles_all_supported_roles():
|
||||
SystemPromptMessage(content=contents)
|
||||
)
|
||||
|
||||
with pytest.raises(NotImplementedError, match="Role custom is not supported"):
|
||||
with pytest.raises(AssertionError, match="Expected code to be unreachable"):
|
||||
llm_utils.combine_message_content_with_role(contents=contents, role="custom") # type: ignore[arg-type]
|
||||
|
||||
|
||||
|
||||
@ -24,7 +24,14 @@ if TYPE_CHECKING: # pragma: no cover - imported for type checking only
|
||||
|
||||
|
||||
class _StubToolRuntime:
|
||||
def get_runtime(self, *, node_id: str, node_data: Any, variable_pool: Any) -> ToolRuntimeHandle:
|
||||
def get_runtime(
|
||||
self,
|
||||
*,
|
||||
node_id: str,
|
||||
node_data: Any,
|
||||
variable_pool: Any,
|
||||
node_execution_id: str | None = None,
|
||||
) -> ToolRuntimeHandle:
|
||||
raise NotImplementedError
|
||||
|
||||
def get_runtime_parameters(self, *, tool_runtime: ToolRuntimeHandle) -> list[Any]:
|
||||
|
||||
@ -7,6 +7,17 @@ from pathlib import Path
|
||||
|
||||
def test_moved_core_nodes_resolve_after_importing_production_entrypoints():
|
||||
api_root = Path(__file__).resolve().parents[4]
|
||||
|
||||
# `PYTHONSAFEPATH=1` enables Python's safe-path mode, which suppresses the
|
||||
# usual implicit insertion of the working directory into `sys.path`.
|
||||
# Set `PYTHONPATH` explicitly so this subprocess test stays deterministic in
|
||||
# both CI and local shells that may export `PYTHONSAFEPATH`.
|
||||
env = os.environ.copy()
|
||||
existing_pythonpath = env.get("PYTHONPATH")
|
||||
env["PYTHONPATH"] = (
|
||||
str(api_root) if not existing_pythonpath else os.pathsep.join([str(api_root), existing_pythonpath])
|
||||
)
|
||||
env["PYTHONSAFEPATH"] = "1"
|
||||
script = textwrap.dedent(
|
||||
"""
|
||||
from core.app.apps import workflow_app_runner
|
||||
@ -34,7 +45,7 @@ def test_moved_core_nodes_resolve_after_importing_production_entrypoints():
|
||||
completed = subprocess.run(
|
||||
[sys.executable, "-c", script],
|
||||
cwd=api_root,
|
||||
env=os.environ.copy(),
|
||||
env=env,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
|
||||
8
api/uv.lock
generated
8
api/uv.lock
generated
@ -1597,7 +1597,7 @@ requires-dist = [
|
||||
{ name = "gmpy2", specifier = ">=2.3.0" },
|
||||
{ name = "google-api-python-client", specifier = ">=2.196.0" },
|
||||
{ name = "google-cloud-aiplatform", specifier = ">=1.151.0,<2.0.0" },
|
||||
{ name = "graphon", specifier = "~=0.3.0" },
|
||||
{ name = "graphon", specifier = "~=0.3.1" },
|
||||
{ name = "gunicorn", specifier = ">=26.0.0" },
|
||||
{ name = "httpx", extras = ["socks"], specifier = ">=0.28.1,<1.0.0" },
|
||||
{ name = "httpx-sse", specifier = "~=0.4.0" },
|
||||
@ -2940,7 +2940,7 @@ httpx = [
|
||||
|
||||
[[package]]
|
||||
name = "graphon"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "charset-normalizer" },
|
||||
@ -2961,9 +2961,9 @@ dependencies = [
|
||||
{ name = "unstructured", extra = ["docx", "epub", "md", "ppt", "pptx"] },
|
||||
{ name = "webvtt-py" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/62/83593d6e7a139ff124711ea05882cadca7065c11a38763aa9360d7e76804/graphon-0.3.0.tar.gz", hash = "sha256:cd38f842ae3dcfa956428b952efbe2a3ea9c1581446647142accbbdeb638b876", size = 241176, upload-time = "2026-04-21T15:18:48.291Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5a/ef/43217842e84160acca64a95858f1689389a50e04a53fc94f2aa836b4eaf7/graphon-0.3.1.tar.gz", hash = "sha256:49971baed1eb16c8e1983f755e659902e4f117a68dc62fad19e91472950b937d", size = 242210, upload-time = "2026-05-07T06:58:21.879Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/f7/81ee8f0368aa6a2d47f97fecc5d4a12865c987906798cbddd0e3b8387f33/graphon-0.3.0-py3-none-any.whl", hash = "sha256:9cca45ebab2a79fd4d04432f55b5b962e9e4f34fa037cc20fee7f18ec80eaa5d", size = 348486, upload-time = "2026-04-21T15:18:46.737Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/37/bef16ed3d6da7446b36769fa388f4dc79f95337ffa16d6dfc3177152507e/graphon-0.3.1-py3-none-any.whl", hash = "sha256:e6422c7e3f1ce7d2185979c17e08201816ca25d46d400ebdd035c95d501c04fe", size = 349368, upload-time = "2026-05-07T06:58:20.217Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user