Compare commits

...

5 Commits

Author SHA1 Message Date
Asuka Minato
ed91c760b2 Merge branch 'resolve-stack-06' into resolve-stack-07 2026-09-12 20:13:45 +09:00
Asuka Minato
23e0cc2f9e Merge remote-tracking branch 'upstream/main' into resolve-stack-06 2026-09-12 20:13:45 +09:00
Asuka Minato
efcf169be4 test: remove duplicate integration-only coverage 2026-09-10 20:26:44 +09:00
Asuka Minato
3e2f839da9 fix 2026-09-10 20:26:43 +09:00
Asuka Minato
e2c9ee04a5 test: isolate parameter extractor model fixtures 2026-09-10 20:26:43 +09:00
8 changed files with 299 additions and 485 deletions

View File

@@ -1,127 +0,0 @@
"""
Unit tests for App description validation functions.
This test module validates the 400-character limit enforcement
for App descriptions across all creation and editing endpoints.
"""
import sys
import pytest
class TestAppDescriptionValidationUnit:
"""Unit tests for description validation function"""
def test_validate_description_length_function(self):
"""Test the validate_description_length function directly"""
from libs.validators import validate_description_length
# Test valid descriptions
assert validate_description_length("") == ""
assert validate_description_length("x" * 400) == "x" * 400
assert validate_description_length(None) is None
# Test invalid descriptions
with pytest.raises(ValueError) as exc_info:
validate_description_length("x" * 401)
assert "Description cannot exceed 400 characters." in str(exc_info.value)
with pytest.raises(ValueError) as exc_info:
validate_description_length("x" * 500)
assert "Description cannot exceed 400 characters." in str(exc_info.value)
with pytest.raises(ValueError) as exc_info:
validate_description_length("x" * 1000)
assert "Description cannot exceed 400 characters." in str(exc_info.value)
def test_boundary_values(self):
"""Test boundary values for description validation"""
from libs.validators import validate_description_length
# Test exact boundary
exactly_400 = "x" * 400
assert validate_description_length(exactly_400) == exactly_400
# Test just over boundary
just_over_400 = "x" * 401
with pytest.raises(ValueError):
validate_description_length(just_over_400)
# Test just under boundary
just_under_400 = "x" * 399
assert validate_description_length(just_under_400) == just_under_400
def test_edge_cases(self):
"""Test edge cases for description validation"""
from libs.validators import validate_description_length
# Test None input
assert validate_description_length(None) is None
# Test empty string
assert validate_description_length("") == ""
# Test single character
assert validate_description_length("a") == "a"
# Test unicode characters
unicode_desc = "测试" * 200 # 400 characters in Chinese
assert validate_description_length(unicode_desc) == unicode_desc
# Test unicode over limit
unicode_over = "测试" * 201 # 402 characters
with pytest.raises(ValueError):
validate_description_length(unicode_over)
def test_whitespace_handling(self):
"""Test how validation handles whitespace"""
from libs.validators import validate_description_length
# Test description with spaces
spaces_400 = " " * 400
assert validate_description_length(spaces_400) == spaces_400
# Test description with spaces over limit
spaces_401 = " " * 401
with pytest.raises(ValueError):
validate_description_length(spaces_401)
# Test mixed content
mixed_400 = "a" * 200 + " " * 200
assert validate_description_length(mixed_400) == mixed_400
# Test mixed over limit
mixed_401 = "a" * 200 + " " * 201
with pytest.raises(ValueError):
validate_description_length(mixed_401)
if __name__ == "__main__":
# Run tests directly
import traceback
test_instance = TestAppDescriptionValidationUnit()
test_methods = [method for method in dir(test_instance) if method.startswith("test_")]
passed = 0
failed = 0
for test_method in test_methods:
try:
print(f"Running {test_method}...")
getattr(test_instance, test_method)()
print(f"✅ {test_method} PASSED")
passed += 1
except Exception as e:
print(f"❌ {test_method} FAILED: {str(e)}")
traceback.print_exc()
failed += 1
print(f"\n📊 Test Results: {passed} passed, {failed} failed")
if failed == 0:
print("🎉 All tests passed!")
else:
print("💥 Some tests failed!")
sys.exit(1)

View File

@@ -1,253 +1,5 @@
import datetime
import uuid
from collections.abc import Generator, Sequence
from decimal import Decimal
from json import dumps
"""Compatibility import for unit tests that predate the integration-test migration."""
from core.plugin.entities.plugin import PluginInstallationSource
from core.plugin.entities.plugin_daemon import PluginModelProviderEntity
from core.plugin.impl.model import PluginModelClient
from tests.unit_tests.core.workflow.nodes.parameter_extractor.fixtures.plugin_model import MockModelClass
# import monkeypatch
from graphon.model_runtime.entities.common_entities import I18nObject
from graphon.model_runtime.entities.llm_entities import (
LLMMode,
LLMResult,
LLMResultChunk,
LLMResultChunkDelta,
LLMUsage,
)
from graphon.model_runtime.entities.message_entities import AssistantPromptMessage, PromptMessage, PromptMessageTool
from graphon.model_runtime.entities.model_entities import (
AIModelEntity,
FetchFrom,
ModelFeature,
ModelPropertyKey,
ModelType,
)
from graphon.model_runtime.entities.provider_entities import ConfigurateMethod, ProviderEntity
class MockModelClass(PluginModelClient):
def fetch_model_providers(self, tenant_id: str) -> Sequence[PluginModelProviderEntity]:
"""
Fetch model providers for the given tenant.
"""
return [
PluginModelProviderEntity(
id=uuid.uuid4().hex,
created_at=datetime.datetime.now(),
updated_at=datetime.datetime.now(),
provider="openai",
tenant_id=tenant_id,
plugin_unique_identifier="langgenius/openai/openai",
plugin_id="langgenius/openai",
installation_source=PluginInstallationSource.Marketplace,
declaration=ProviderEntity(
provider="openai",
label=I18nObject(
en_US="OpenAI",
zh_Hans="OpenAI",
),
description=I18nObject(
en_US="OpenAI",
zh_Hans="OpenAI",
),
icon_small=I18nObject(
en_US="https://example.com/icon_small.png",
zh_Hans="https://example.com/icon_small.png",
),
supported_model_types=[ModelType.LLM],
configurate_methods=[ConfigurateMethod.PREDEFINED_MODEL],
models=[
AIModelEntity(
model="gpt-3.5-turbo",
label=I18nObject(
en_US="gpt-3.5-turbo",
zh_Hans="gpt-3.5-turbo",
),
model_type=ModelType.LLM,
fetch_from=FetchFrom.PREDEFINED_MODEL,
model_properties={},
features=[ModelFeature.TOOL_CALL, ModelFeature.MULTI_TOOL_CALL],
),
AIModelEntity(
model="gpt-3.5-turbo-instruct",
label=I18nObject(
en_US="gpt-3.5-turbo-instruct",
zh_Hans="gpt-3.5-turbo-instruct",
),
model_type=ModelType.LLM,
fetch_from=FetchFrom.PREDEFINED_MODEL,
model_properties={
ModelPropertyKey.MODE: LLMMode.COMPLETION,
},
features=[],
),
],
),
)
]
def get_model_schema(
self,
tenant_id: str,
user_id: str,
plugin_id: str,
provider: str,
model_type: str,
model: str,
credentials: dict,
) -> AIModelEntity | None:
"""
Get model schema
"""
return AIModelEntity(
model=model,
label=I18nObject(
en_US="OpenAI",
zh_Hans="OpenAI",
),
model_type=ModelType(model_type),
fetch_from=FetchFrom.PREDEFINED_MODEL,
model_properties={},
features=[ModelFeature.TOOL_CALL, ModelFeature.MULTI_TOOL_CALL] if model == "gpt-3.5-turbo" else [],
)
@staticmethod
def generate_function_call(
tools: list[PromptMessageTool] | None,
) -> AssistantPromptMessage.ToolCall | None:
if not tools or len(tools) == 0:
return None
function: PromptMessageTool = tools[0]
function_name = function.name
function_parameters = function.parameters
function_parameters_type = function_parameters["type"]
if function_parameters_type != "object":
return None
function_parameters_properties = function_parameters["properties"]
function_parameters_required = function_parameters["required"]
parameters = {}
for parameter_name, parameter in function_parameters_properties.items():
if parameter_name not in function_parameters_required:
continue
parameter_type = parameter["type"]
if parameter_type == "string":
if "enum" in parameter:
if len(parameter["enum"]) == 0:
continue
parameters[parameter_name] = parameter["enum"][0]
else:
parameters[parameter_name] = "kawaii"
elif parameter_type == "integer":
parameters[parameter_name] = 114514
elif parameter_type == "number":
parameters[parameter_name] = 1919810.0
elif parameter_type == "boolean":
parameters[parameter_name] = True
return AssistantPromptMessage.ToolCall(
id=str(uuid.uuid4()),
type="function",
function=AssistantPromptMessage.ToolCall.ToolCallFunction(
name=function_name,
arguments=dumps(parameters),
),
)
@staticmethod
def mocked_chat_create_sync(
model: str,
prompt_messages: list[PromptMessage],
tools: list[PromptMessageTool] | None = None,
) -> LLMResult:
tool_call = MockModelClass.generate_function_call(tools=tools)
return LLMResult(
id=str(uuid.uuid4()),
model=model,
prompt_messages=prompt_messages,
message=AssistantPromptMessage(content="elaina", tool_calls=[tool_call] if tool_call else []),
usage=LLMUsage(
prompt_tokens=2,
completion_tokens=1,
total_tokens=3,
prompt_unit_price=Decimal(0.0001),
completion_unit_price=Decimal(0.0002),
prompt_price_unit=Decimal(1),
prompt_price=Decimal(0.0001),
completion_price_unit=Decimal(1),
completion_price=Decimal(0.0002),
total_price=Decimal(0.0003),
currency="USD",
latency=0.001,
),
)
@staticmethod
def mocked_chat_create_stream(
model: str,
prompt_messages: list[PromptMessage],
tools: list[PromptMessageTool] | None = None,
) -> Generator[LLMResultChunk, None, None]:
tool_call = MockModelClass.generate_function_call(tools=tools)
full_text = "Hello, world!\n\n```python\nprint('Hello, world!')\n```"
for i in range(0, len(full_text) + 1):
if i == len(full_text):
yield LLMResultChunk(
model=model,
prompt_messages=prompt_messages,
delta=LLMResultChunkDelta(
index=0,
message=AssistantPromptMessage(
content="",
tool_calls=[tool_call] if tool_call else [],
),
),
)
else:
yield LLMResultChunk(
model=model,
prompt_messages=prompt_messages,
delta=LLMResultChunkDelta(
index=0,
message=AssistantPromptMessage(
content=full_text[i],
tool_calls=[tool_call] if tool_call else [],
),
usage=LLMUsage(
prompt_tokens=2,
completion_tokens=17,
total_tokens=19,
prompt_unit_price=Decimal(0.0001),
completion_unit_price=Decimal(0.0002),
prompt_price_unit=Decimal(1),
prompt_price=Decimal(0.0001),
completion_price_unit=Decimal(1),
completion_price=Decimal(0.0002),
total_price=Decimal(0.0003),
currency="USD",
latency=0.001,
),
),
)
def invoke_llm(
self: PluginModelClient,
*,
tenant_id: str,
user_id: str,
plugin_id: str,
provider: str,
model: str,
credentials: dict,
prompt_messages: list[PromptMessage],
model_parameters: dict | None = None,
tools: list[PromptMessageTool] | None = None,
stop: list[str] | None = None,
stream: bool = True,
app_id: str | None = None,
):
return MockModelClass.mocked_chat_create_stream(model=model, prompt_messages=prompt_messages, tools=tools)
__all__ = ["MockModelClass"]

View File

@@ -1,67 +0,0 @@
import os
from typing import Literal
import httpx
import pytest
from core.plugin.entities.plugin_daemon import PluginDaemonBasicResponse, PluginToolProviderEntity
from core.tools.entities.common_entities import I18nObject
from core.tools.entities.tool_entities import ToolProviderEntityWithPlugin, ToolProviderIdentity
class MockedHttp:
@classmethod
def list_tools(cls) -> list[PluginToolProviderEntity]:
return [
PluginToolProviderEntity(
provider="Yeuoly",
plugin_unique_identifier="langgenius/yeuoly:0.0.1@mock",
plugin_id="mock-plugin",
declaration=ToolProviderEntityWithPlugin(
identity=ToolProviderIdentity(
author="Yeuoly",
name="Yeuoly",
description=I18nObject(en_US="Yeuoly"),
icon="ssss.svg",
label=I18nObject(en_US="Yeuoly"),
)
),
)
]
@classmethod
def requests_request(
cls, method: Literal["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD"], url: str, **kwargs
) -> httpx.Response:
"""
Mocked httpx.request
"""
request = httpx.Request(method, url)
if url.endswith("/tools"):
content = PluginDaemonBasicResponse[list[PluginToolProviderEntity]](
code=0, message="success", data=cls.list_tools()
).model_dump_json()
else:
raise ValueError("")
response = httpx.Response(status_code=200)
response.request = request
response._content = content.encode("utf-8")
return response
MOCK_SWITCH = os.getenv("MOCK_SWITCH", "false").lower() == "true"
@pytest.fixture
def setup_http_mock(request, monkeypatch: pytest.MonkeyPatch):
if MOCK_SWITCH:
monkeypatch.setattr(httpx, "request", MockedHttp.requests_request)
def unpatch():
monkeypatch.undo()
yield
if MOCK_SWITCH:
unpatch()

View File

@@ -1,9 +0,0 @@
from core.plugin.impl.tool import PluginToolManager
pytest_plugins = ("tests.integration_tests.plugin.__mock.http",)
def test_fetch_all_plugin_tools(setup_http_mock):
manager = PluginToolManager()
tools = manager.fetch_tool_providers(tenant_id="test-tenant")
assert len(tools) >= 1

View File

@@ -1,3 +1,5 @@
"""Model-instance builders for parameter-extractor unit tests."""
from unittest.mock import MagicMock
from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity
@@ -13,8 +15,8 @@ def get_mocked_fetch_model_config(
provider: str,
model: str,
mode: str,
credentials: dict,
):
credentials: dict[str, object],
) -> MagicMock:
model_assembly = create_plugin_model_assembly(tenant_id="9d2074fc-6f86-45a9-b09d-6ecc63b9056b")
model_provider_factory = model_assembly.model_provider_factory
model_type_instance = model_assembly.create_model_type_instance(provider=provider, model_type=ModelType.LLM)
@@ -55,8 +57,8 @@ def get_mocked_fetch_model_instance(
provider: str,
model: str,
mode: str,
credentials: dict,
):
credentials: dict[str, object],
) -> MagicMock:
mock_fetch_model_config = get_mocked_fetch_model_config(
provider=provider,
model=model,

View File

@@ -1,10 +1,9 @@
import os
from collections.abc import Callable
from collections.abc import Callable, Generator
import pytest
from core.plugin.impl.model import PluginModelClient
from tests.integration_tests.model_runtime.__mock.plugin_model import MockModelClass
from tests.unit_tests.core.workflow.nodes.parameter_extractor.fixtures.plugin_model import MockModelClass
def mock_plugin_daemon(
@@ -17,7 +16,7 @@ def mock_plugin_daemon(
:return: unpatch function
"""
def unpatch():
def unpatch() -> None:
monkeypatch.undo()
monkeypatch.setattr(PluginModelClient, "invoke_llm", MockModelClass.invoke_llm)
@@ -27,15 +26,8 @@ def mock_plugin_daemon(
return unpatch
MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true"
@pytest.fixture
def setup_model_mock(monkeypatch: pytest.MonkeyPatch):
if MOCK:
unpatch = mock_plugin_daemon(monkeypatch)
def setup_model_mock(monkeypatch: pytest.MonkeyPatch) -> Generator[None, None, None]:
unpatch = mock_plugin_daemon(monkeypatch)
yield
if MOCK:
unpatch()
unpatch()

View File

@@ -0,0 +1,258 @@
"""Deterministic plugin-model double for parameter-extractor unit tests."""
import datetime
import uuid
from collections.abc import Generator, Sequence
from decimal import Decimal
from json import dumps
from typing import override
from core.plugin.entities.plugin import PluginInstallationSource
from core.plugin.entities.plugin_daemon import PluginModelProviderEntity
from core.plugin.impl.model import PluginModelClient
# import monkeypatch
from graphon.model_runtime.entities.common_entities import I18nObject
from graphon.model_runtime.entities.llm_entities import (
LLMMode,
LLMResult,
LLMResultChunk,
LLMResultChunkDelta,
LLMUsage,
)
from graphon.model_runtime.entities.message_entities import AssistantPromptMessage, PromptMessage, PromptMessageTool
from graphon.model_runtime.entities.model_entities import (
AIModelEntity,
FetchFrom,
ModelFeature,
ModelPropertyKey,
ModelType,
)
from graphon.model_runtime.entities.provider_entities import ConfigurateMethod, ProviderEntity
class MockModelClass(PluginModelClient):
@override
def fetch_model_providers(self, tenant_id: str) -> Sequence[PluginModelProviderEntity]:
"""
Fetch model providers for the given tenant.
"""
return [
PluginModelProviderEntity(
id=uuid.uuid4().hex,
created_at=datetime.datetime.now(),
updated_at=datetime.datetime.now(),
provider="openai",
tenant_id=tenant_id,
plugin_unique_identifier="langgenius/openai/openai",
plugin_id="langgenius/openai",
installation_source=PluginInstallationSource.Marketplace,
declaration=ProviderEntity(
provider="openai",
label=I18nObject(
en_US="OpenAI",
zh_Hans="OpenAI",
),
description=I18nObject(
en_US="OpenAI",
zh_Hans="OpenAI",
),
icon_small=I18nObject(
en_US="https://example.com/icon_small.png",
zh_Hans="https://example.com/icon_small.png",
),
supported_model_types=[ModelType.LLM],
configurate_methods=[ConfigurateMethod.PREDEFINED_MODEL],
models=[
AIModelEntity(
model="gpt-3.5-turbo",
label=I18nObject(
en_US="gpt-3.5-turbo",
zh_Hans="gpt-3.5-turbo",
),
model_type=ModelType.LLM,
fetch_from=FetchFrom.PREDEFINED_MODEL,
model_properties={},
features=[ModelFeature.TOOL_CALL, ModelFeature.MULTI_TOOL_CALL],
),
AIModelEntity(
model="gpt-3.5-turbo-instruct",
label=I18nObject(
en_US="gpt-3.5-turbo-instruct",
zh_Hans="gpt-3.5-turbo-instruct",
),
model_type=ModelType.LLM,
fetch_from=FetchFrom.PREDEFINED_MODEL,
model_properties={
ModelPropertyKey.MODE: LLMMode.COMPLETION,
},
features=[],
),
],
),
)
]
@override
def get_model_schema(
self,
tenant_id: str,
user_id: str | None,
plugin_id: str,
provider: str,
model_type: str,
model: str,
credentials: dict[str, object],
) -> AIModelEntity | None:
"""
Get model schema
"""
return AIModelEntity(
model=model,
label=I18nObject(
en_US="OpenAI",
zh_Hans="OpenAI",
),
model_type=ModelType(model_type),
fetch_from=FetchFrom.PREDEFINED_MODEL,
model_properties={},
features=[ModelFeature.TOOL_CALL, ModelFeature.MULTI_TOOL_CALL] if model == "gpt-3.5-turbo" else [],
)
@staticmethod
def generate_function_call(
tools: list[PromptMessageTool] | None,
) -> AssistantPromptMessage.ToolCall | None:
if not tools or len(tools) == 0:
return None
function: PromptMessageTool = tools[0]
function_name = function.name
function_parameters = function.parameters
function_parameters_type = function_parameters["type"]
if function_parameters_type != "object":
return None
function_parameters_properties = function_parameters["properties"]
function_parameters_required = function_parameters["required"]
parameters = {}
for parameter_name, parameter in function_parameters_properties.items():
if parameter_name not in function_parameters_required:
continue
parameter_type = parameter["type"]
if parameter_type == "string":
if "enum" in parameter:
if len(parameter["enum"]) == 0:
continue
parameters[parameter_name] = parameter["enum"][0]
else:
parameters[parameter_name] = "kawaii"
elif parameter_type == "integer":
parameters[parameter_name] = 114514
elif parameter_type == "number":
parameters[parameter_name] = 1919810.0
elif parameter_type == "boolean":
parameters[parameter_name] = True
return AssistantPromptMessage.ToolCall(
id=str(uuid.uuid4()),
type="function",
function=AssistantPromptMessage.ToolCall.ToolCallFunction(
name=function_name,
arguments=dumps(parameters),
),
)
@staticmethod
def mocked_chat_create_sync(
model: str,
prompt_messages: list[PromptMessage],
tools: list[PromptMessageTool] | None = None,
) -> LLMResult:
tool_call = MockModelClass.generate_function_call(tools=tools)
return LLMResult(
id=str(uuid.uuid4()),
model=model,
prompt_messages=prompt_messages,
message=AssistantPromptMessage(content="elaina", tool_calls=[tool_call] if tool_call else []),
usage=LLMUsage(
prompt_tokens=2,
completion_tokens=1,
total_tokens=3,
prompt_unit_price=Decimal(0.0001),
completion_unit_price=Decimal(0.0002),
prompt_price_unit=Decimal(1),
prompt_price=Decimal(0.0001),
completion_price_unit=Decimal(1),
completion_price=Decimal(0.0002),
total_price=Decimal(0.0003),
currency="USD",
latency=0.001,
),
)
@staticmethod
def mocked_chat_create_stream(
model: str,
prompt_messages: list[PromptMessage],
tools: list[PromptMessageTool] | None = None,
) -> Generator[LLMResultChunk, None, None]:
tool_call = MockModelClass.generate_function_call(tools=tools)
full_text = "Hello, world!\n\n```python\nprint('Hello, world!')\n```"
for i in range(0, len(full_text) + 1):
if i == len(full_text):
yield LLMResultChunk(
model=model,
prompt_messages=prompt_messages,
delta=LLMResultChunkDelta(
index=0,
message=AssistantPromptMessage(
content="",
tool_calls=[tool_call] if tool_call else [],
),
),
)
else:
yield LLMResultChunk(
model=model,
prompt_messages=prompt_messages,
delta=LLMResultChunkDelta(
index=0,
message=AssistantPromptMessage(
content=full_text[i],
tool_calls=[tool_call] if tool_call else [],
),
usage=LLMUsage(
prompt_tokens=2,
completion_tokens=17,
total_tokens=19,
prompt_unit_price=Decimal(0.0001),
completion_unit_price=Decimal(0.0002),
prompt_price_unit=Decimal(1),
prompt_price=Decimal(0.0001),
completion_price_unit=Decimal(1),
completion_price=Decimal(0.0002),
total_price=Decimal(0.0003),
currency="USD",
latency=0.001,
),
),
)
@override
def invoke_llm(
self: PluginModelClient,
tenant_id: str,
user_id: str | None,
plugin_id: str,
provider: str,
model: str,
credentials: dict[str, object],
prompt_messages: list[PromptMessage],
model_parameters: dict[str, object] | None = None,
tools: list[PromptMessageTool] | None = None,
stop: list[str] | None = None,
stream: bool = True,
app_id: str | None = None,
) -> Generator[LLMResultChunk, None, None]:
return MockModelClass.mocked_chat_create_stream(model=model, prompt_messages=prompt_messages, tools=tools)

View File

@@ -1,6 +1,9 @@
"""Unit coverage for parameter extraction with the plugin-model boundary replaced."""
import os
import time
import uuid
from typing import override
from unittest.mock import MagicMock
import pytest
@@ -12,29 +15,34 @@ from core.workflow.system_variables import build_system_variables
from extensions.ext_database import db
from graphon.enums import WorkflowNodeExecutionStatus
from graphon.model_runtime.entities import AssistantPromptMessage, UserPromptMessage
from graphon.model_runtime.entities.message_entities import PromptMessage
from graphon.model_runtime.memory.prompt_message_memory import PromptMessageMemory
from graphon.nodes.llm.protocols import CredentialsProvider, ModelFactory
from graphon.nodes.parameter_extractor.entities import ParameterExtractorNodeData
from graphon.nodes.parameter_extractor.parameter_extractor_node import ParameterExtractorNode
from graphon.runtime import GraphRuntimeState, VariablePool
from tests.integration_tests.workflow.nodes.__mock.model import get_mocked_fetch_model_instance
from tests.unit_tests.core.workflow.nodes.parameter_extractor.fixtures.model import get_mocked_fetch_model_instance
from tests.workflow_test_utils import build_test_graph_init_params
pytest_plugins = ("tests.integration_tests.model_runtime.__mock.plugin_daemon",)
pytest_plugins = ("tests.unit_tests.core.workflow.nodes.parameter_extractor.fixtures.plugin_daemon",)
def get_mocked_fetch_memory(memory_text: str):
class MemoryMock:
def get_mocked_fetch_memory(memory_text: str) -> MagicMock:
class MemoryMock(PromptMessageMemory):
@override
def get_history_prompt_messages(
self,
max_token_limit: int = 2000,
message_limit: int | None = None,
):
) -> list[PromptMessage]:
return [UserPromptMessage(content=memory_text), AssistantPromptMessage(content="mocked answer")]
return MagicMock(return_value=MemoryMock())
def init_parameter_extractor_node(config: dict, memory=None):
def init_parameter_extractor_node(
config: dict[str, object], memory: PromptMessageMemory | None = None
) -> ParameterExtractorNode:
graph_config = {
"edges": [
{
@@ -89,7 +97,8 @@ def _mock_db_session_close(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(db.session, "close", MagicMock())
def test_function_calling_parameter_extractor(setup_model_mock, monkeypatch: pytest.MonkeyPatch):
@pytest.mark.usefixtures("setup_model_mock")
def test_function_calling_parameter_extractor(monkeypatch: pytest.MonkeyPatch) -> None:
"""
Test function calling for parameter extractor.
"""
@@ -130,7 +139,8 @@ def test_function_calling_parameter_extractor(setup_model_mock, monkeypatch: pyt
assert result.outputs.get("__reason") == None
def test_instructions(setup_model_mock, monkeypatch: pytest.MonkeyPatch):
@pytest.mark.usefixtures("setup_model_mock")
def test_instructions(monkeypatch: pytest.MonkeyPatch) -> None:
"""
Test chat parameter extractor.
"""
@@ -180,7 +190,8 @@ def test_instructions(setup_model_mock, monkeypatch: pytest.MonkeyPatch):
assert "what's the weather in SF" in prompt.get("text")
def test_chat_parameter_extractor(setup_model_mock, monkeypatch: pytest.MonkeyPatch):
@pytest.mark.usefixtures("setup_model_mock")
def test_chat_parameter_extractor(monkeypatch: pytest.MonkeyPatch) -> None:
"""
Test chat parameter extractor.
"""
@@ -231,7 +242,8 @@ def test_chat_parameter_extractor(setup_model_mock, monkeypatch: pytest.MonkeyPa
assert '<structure>\n{"type": "object"' in prompt.get("text")
def test_completion_parameter_extractor(setup_model_mock, monkeypatch: pytest.MonkeyPatch):
@pytest.mark.usefixtures("setup_model_mock")
def test_completion_parameter_extractor(monkeypatch: pytest.MonkeyPatch) -> None:
"""
Test completion parameter extractor.
"""
@@ -278,7 +290,7 @@ def test_completion_parameter_extractor(setup_model_mock, monkeypatch: pytest.Mo
assert "SF" in result.process_data.get("prompts", [])[0].get("text")
def test_extract_json_response():
def test_extract_json_response() -> None:
"""
Test extract json response.
"""
@@ -316,7 +328,7 @@ def test_extract_json_response():
assert result["location"] == "kawaii"
def test_extract_json_from_tool_call():
def test_extract_json_from_tool_call() -> None:
"""
Test extract json response.
"""
@@ -356,7 +368,8 @@ def test_extract_json_from_tool_call():
assert result["location"] == "kawaii"
def test_chat_parameter_extractor_with_memory(setup_model_mock, monkeypatch: pytest.MonkeyPatch):
@pytest.mark.usefixtures("setup_model_mock")
def test_chat_parameter_extractor_with_memory(monkeypatch: pytest.MonkeyPatch) -> None:
"""
Test chat parameter extractor with memory.
"""