Files

323 lines
10 KiB
Python
Raw Permalink Normal View History

2026-08-01 10:28:22 +05:30
# -*- coding: utf-8 -*-
from types import SimpleNamespace
def test_strip_missing_local_files_removes_file_blocks():
from adclaw.agents.model_factory import _strip_missing_local_files
msg = SimpleNamespace(
content=[
{"type": "text", "text": "keep"},
{
"type": "file",
"file_url": "file:///home/adclaw/.adclaw/media/telegram/missing.pptx",
},
],
get_text_content=lambda: "fallback",
)
_strip_missing_local_files([msg])
assert msg.content == [{"type": "text", "text": "keep"}]
def test_strip_missing_local_files_removes_nested_source_blocks():
from adclaw.agents.model_factory import _strip_missing_local_files
msg = SimpleNamespace(
content=[
{
"type": "image",
"source": {
"type": "url",
"url": "file:///home/adclaw/.adclaw/media/telegram/missing.jpg",
},
},
],
get_text_content=lambda: "",
)
_strip_missing_local_files([msg])
assert msg.content == "[local file removed]"
def test_strip_missing_local_files_keeps_remote_urls():
from adclaw.agents.model_factory import _strip_missing_local_files
block = {
"type": "image",
"source": {"type": "url", "url": "https://example.com/image.jpg"},
}
msg = SimpleNamespace(
content=[block],
get_text_content=lambda: "",
)
_strip_missing_local_files([msg])
assert msg.content == [block]
def test_strip_reasoning_blocks_drops_thinking_only_messages():
from adclaw.agents.model_factory import _strip_reasoning_blocks
thinking_only = SimpleNamespace(
content=[{"type": "thinking", "thinking": "private chain"}],
)
mixed = SimpleNamespace(
content=[
{"type": "thinking", "thinking": "private chain"},
{"type": "text", "text": "visible answer"},
],
)
messages = _strip_reasoning_blocks([thinking_only, mixed])
assert messages == [mixed]
assert mixed.content == [{"type": "text", "text": "visible answer"}]
def test_strip_bare_tool_call_text_messages_drops_json_only_assistant():
from adclaw.agents.model_factory import _strip_bare_tool_call_text_messages
raw_tool = SimpleNamespace(
role="assistant",
content='{"name":"agent.status","arguments":{}}',
)
normal_json = SimpleNamespace(
role="assistant",
content='{"campaign":"CEDAR-17"}',
)
user_msg = SimpleNamespace(
role="user",
content='{"name":"agent.status","arguments":{}}',
)
messages = _strip_bare_tool_call_text_messages(
[raw_tool, normal_json, user_msg],
)
assert messages == [normal_json, user_msg]
def test_bare_tool_call_detector_accepts_json_string_arguments():
from adclaw.agents.model_factory import is_bare_tool_call_json_text
assert is_bare_tool_call_json_text(
'{"name":"agent.status","arguments":"{\\"verbose\\":false}"}',
)
def test_normalize_assistant_tool_call_content_replaces_null():
from adclaw.agents.model_factory import (
_normalize_assistant_tool_call_content,
)
messages = [
{"role": "assistant", "content": None, "tool_calls": [{"id": "t1"}]},
{"role": "assistant", "content": None},
]
_normalize_assistant_tool_call_content(messages)
assert messages[0]["content"] == ""
assert messages[1]["content"] is None
def test_host_ai_remote_model_gets_safe_max_tokens(monkeypatch):
from adclaw.agents.model_factory import _create_remote_model_instance
class FakeChatModel:
def __init__(self, model_name, **kwargs):
self.model_name = model_name
self.kwargs = kwargs
monkeypatch.delenv("MASKANX_HOST_AI_MAX_OUTPUT_TOKENS", raising=False)
monkeypatch.delenv("MASKANX_HOST_AI_MAX_TOKENS", raising=False)
llm_cfg = SimpleNamespace(
provider_id="maskanx-host-ai",
model="@cf/google/gemma-4-26b-a4b-it",
api_key="ach_test",
base_url="https://real.maskanx.app/api/host-ai/v1",
)
model = _create_remote_model_instance(llm_cfg, FakeChatModel)
assert model.kwargs["generate_kwargs"] == {"max_tokens": 4096}
assert "reasoning_effort" not in model.kwargs
def test_host_ai_remote_model_uses_canonical_max_output_tokens(monkeypatch):
from adclaw.agents.model_factory import _create_remote_model_instance
class FakeChatModel:
def __init__(self, model_name, **kwargs):
self.model_name = model_name
self.kwargs = kwargs
monkeypatch.setenv("MASKANX_HOST_AI_MAX_OUTPUT_TOKENS", "321")
monkeypatch.setenv("MASKANX_HOST_AI_MAX_TOKENS", "654")
llm_cfg = SimpleNamespace(
provider_id="maskanx-host-ai",
model="@cf/google/gemma-4-26b-a4b-it",
api_key="ach_test",
base_url="https://real.maskanx.app/api/host-ai/v1",
)
model = _create_remote_model_instance(llm_cfg, FakeChatModel)
assert model.kwargs["generate_kwargs"] == {"max_tokens": 321}
assert "reasoning_effort" not in model.kwargs
def test_host_ai_gpt_oss_uses_low_reasoning_effort(monkeypatch):
from agentscope.model import OpenAIChatModel
from adclaw.agents.model_factory import _create_remote_model_instance
class FakeOpenAIChatModel(OpenAIChatModel):
def __init__(self, model_name, **kwargs):
self.model_name = model_name
self.kwargs = kwargs
monkeypatch.delenv("MASKANX_HOST_AI_REASONING_EFFORT", raising=False)
llm_cfg = SimpleNamespace(
provider_id="maskanx-host-ai",
model="@cf/openai/gpt-oss-20b",
api_key="ach_test",
base_url="https://real.maskanx.app/api/host-ai/v1",
)
model = _create_remote_model_instance(llm_cfg, FakeOpenAIChatModel)
assert model.kwargs["reasoning_effort"] == "low"
def test_host_ai_gpt_oss_reasoning_effort_can_be_disabled(monkeypatch):
from agentscope.model import OpenAIChatModel
from adclaw.agents.model_factory import _create_remote_model_instance
class FakeOpenAIChatModel(OpenAIChatModel):
def __init__(self, model_name, **kwargs):
self.model_name = model_name
self.kwargs = kwargs
monkeypatch.setenv("MASKANX_HOST_AI_REASONING_EFFORT", "off")
llm_cfg = SimpleNamespace(
provider_id="maskanx-host-ai",
model="@cf/openai/gpt-oss-20b",
api_key="ach_test",
base_url="https://real.maskanx.app/api/host-ai/v1",
)
model = _create_remote_model_instance(llm_cfg, FakeOpenAIChatModel)
assert "reasoning_effort" not in model.kwargs
def test_non_host_ai_remote_model_does_not_get_host_generation_cap():
from adclaw.agents.model_factory import _create_remote_model_instance
class FakeChatModel:
def __init__(self, model_name, **kwargs):
self.model_name = model_name
self.kwargs = kwargs
llm_cfg = SimpleNamespace(
provider_id="openai",
model="gpt-4.1",
api_key="sk-test",
base_url="https://api.openai.com/v1",
)
model = _create_remote_model_instance(llm_cfg, FakeChatModel)
assert "generate_kwargs" not in model.kwargs
assert "reasoning_effort" not in model.kwargs
def test_tool_result_string_is_truncated_for_host_ai_context(monkeypatch):
from adclaw.agents.model_factory import _create_file_block_support_formatter
class FakeFormatter:
@staticmethod
def convert_tool_result_to_string(output):
return str(output), []
monkeypatch.setenv("MASKANX_HOST_AI_ENABLED", "true")
monkeypatch.setenv("MASKANX_LLM_TOOL_RESULT_MAX_CHARS", "160")
formatter = _create_file_block_support_formatter(FakeFormatter)
raw = "A" * 200 + "TAIL"
text, data = formatter.convert_tool_result_to_string(raw)
assert data == []
assert len(text) <= 160
assert text.startswith("A")
assert text.endswith("TAIL")
assert "tool result truncated from 204 to 160 chars" in text
def test_parent_tool_result_conversion_is_truncated(monkeypatch):
from adclaw.agents.model_factory import _create_file_block_support_formatter
class FakeFormatter:
@staticmethod
def convert_tool_result_to_string(output):
return "B" * 220 + "END", [("file.txt", {"type": "file"})]
monkeypatch.setenv("MASKANX_HOST_AI_ENABLED", "true")
monkeypatch.setenv("MASKANX_LLM_TOOL_RESULT_MAX_CHARS", "180")
formatter = _create_file_block_support_formatter(FakeFormatter)
text, data = formatter.convert_tool_result_to_string([{"type": "text"}])
assert data == [("file.txt", {"type": "file"})]
assert len(text) <= 180
assert text.endswith("END")
assert "tool result truncated from 223 to 180 chars" in text
def test_tool_result_string_is_not_truncated_for_byo_provider(monkeypatch):
from adclaw.agents.model_factory import _create_file_block_support_formatter
class FakeFormatter:
@staticmethod
def convert_tool_result_to_string(output):
return str(output), []
monkeypatch.delenv("MASKANX_HOST_AI_ENABLED", raising=False)
monkeypatch.delenv("MASKANX_HOST_AI_BASE_URL", raising=False)
monkeypatch.setenv("MASKANX_LLM_TOOL_RESULT_MAX_CHARS", "160")
formatter = _create_file_block_support_formatter(FakeFormatter)
raw = "A" * 200 + "TAIL"
text, data = formatter.convert_tool_result_to_string(raw)
assert data == []
assert text == raw
def test_file_block_fallback_tool_result_is_truncated(monkeypatch):
from adclaw.agents.model_factory import _create_file_block_support_formatter
class FakeFormatter:
@staticmethod
def convert_tool_result_to_string(output):
raise ValueError("Unsupported block type: file")
monkeypatch.setenv("MASKANX_HOST_AI_ENABLED", "true")
monkeypatch.setenv("MASKANX_LLM_TOOL_RESULT_MAX_CHARS", "220")
formatter = _create_file_block_support_formatter(FakeFormatter)
long_name = "deck-" + ("X" * 260) + ".pptx"
block = {"type": "file", "name": long_name, "path": "/tmp/deck.pptx"}
text, data = formatter.convert_tool_result_to_string([block])
assert data == [("/tmp/deck.pptx", block)]
assert len(text) <= 220
assert "tool result truncated" in text
assert "/tmp/deck.pptx" in text