File size: 9,675 Bytes
1ea26af
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#!/usr/bin/env python3
"""
Test agent model inheritance - verify WebAgent and FileAgent properly inherit model configs
"""
import os
import sys
import types
import pytest

# Ensure package root is on path
sys.path.insert(0, os.path.abspath('.'))

# Stub heavy dependencies to avoid import overhead
stub_model_mod = types.ModuleType('ck_pro.agents.model')
class _StubLLM:
    def __init__(self, _default_init=False, **kwargs):
        self.call_target = kwargs.get('call_target', 'https://api.openai.com/v1/chat/completions')
        self.api_key = kwargs.get('api_key', 'default-key')
        self.model = kwargs.get('model', 'gpt-4o-mini')
        self.extract_body = kwargs.get('extract_body', {})
        self._default_init = _default_init
        
    def __call__(self, messages):
        return "test response"
        
stub_model_mod.LLM = _StubLLM
sys.modules['ck_pro.agents.model'] = stub_model_mod

# Stub other heavy modules
stub_utils_mod = types.ModuleType('ck_pro.agents.utils')
stub_utils_mod.zwarn = lambda x: None
stub_utils_mod.zlog = lambda x: None
stub_utils_mod.have_images_in_messages = lambda x: False
stub_utils_mod.rprint = lambda x, **kwargs: None
stub_utils_mod.TemplatedString = lambda x: type('T', (), {'format': lambda **k: x})()
stub_utils_mod.parse_response = lambda x: {'code': 'print("ok")'}
stub_utils_mod.CodeExecutor = lambda: type('CE', (), {'run': lambda *a, **k: None, 'get_print_results': lambda: 'ok'})()
stub_utils_mod.KwargsInitializable = object
stub_utils_mod.ActionResult = lambda x: x
sys.modules['ck_pro.agents.utils'] = stub_utils_mod

# Stub agent base
stub_agent_mod = types.ModuleType('ck_pro.agents.agent')
class _StubMultiStepAgent:
    def __init__(self, **kwargs):
        # Simulate MultiStepAgent behavior: use model from kwargs or default
        if 'model' in kwargs:
            self.model = _StubLLM(**kwargs['model'])
        else:
            self.model = _StubLLM(_default_init=True)
        self.ACTIVE_FUNCTIONS = {}
stub_agent_mod.MultiStepAgent = _StubMultiStepAgent
stub_agent_mod.register_template = lambda x: None
stub_agent_mod.ActionResult = lambda x: x
sys.modules['ck_pro.agents.agent'] = stub_agent_mod

stub_session_mod = types.ModuleType('ck_pro.agents.session')
stub_session_mod.AgentSession = object
sys.modules['ck_pro.agents.session'] = stub_session_mod

stub_tool_mod = types.ModuleType('ck_pro.agents.tool')
stub_tool_mod.Tool = object
stub_tool_mod.SimpleSearchTool = lambda **kwargs: type('SST', (), {})()
sys.modules['ck_pro.agents.tool'] = stub_tool_mod

# Stub file utils
stub_file_utils_mod = types.ModuleType('ck_pro.ck_file.utils')
stub_file_utils_mod.FileEnv = lambda **kwargs: type('FE', (), {})()
sys.modules['ck_pro.ck_file.utils'] = stub_file_utils_mod

# Stub file prompts
stub_file_prompts_mod = types.ModuleType('ck_pro.ck_file.prompts')
stub_file_prompts_mod.PROMPTS = {}
sys.modules['ck_pro.ck_file.prompts'] = stub_file_prompts_mod

# Stub web prompts
stub_web_prompts_mod = types.ModuleType('ck_pro.ck_web.prompts')
stub_web_prompts_mod.PROMPTS = {}
sys.modules['ck_pro.ck_web.prompts'] = stub_web_prompts_mod

# Import after stubbing
from ck_pro.config.settings import Settings, LLMConfig
from ck_pro.ck_file.agent import FileAgent
from ck_pro.ck_web.agent import WebAgent


class TestAgentModelInheritance:
    """Test that WebAgent and FileAgent properly inherit model configurations"""
    
    def test_file_agent_inherits_main_model_from_kwargs(self):
        """Test FileAgent inherits main model config through kwargs -> super().__init__"""
        # Create model config that should be inherited
        model_config = {
            'call_target': 'https://test.modelscope.cn/v1/chat/completions',
            'api_key': 'test-key-123',
            'model': 'test-model-456',
            'extract_body': {'temperature': 0.3}
        }
        
        # Create FileAgent with model config
        agent = FileAgent(settings=None, model=model_config)
        
        # Verify main model inherited the config
        assert agent.model.call_target == 'https://test.modelscope.cn/v1/chat/completions'
        assert agent.model.api_key == 'test-key-123'
        assert agent.model.model == 'test-model-456'
        assert agent.model.extract_body == {'temperature': 0.3}
    
    def test_file_agent_inherits_multimodal_model_from_kwargs(self):
        """Test FileAgent inherits multimodal model config from model_multimodal kwargs"""
        # Create multimodal model config
        mm_config = {
            'call_target': 'https://test-mm.modelscope.cn/v1/chat/completions',
            'api_key': 'test-mm-key',
            'model': 'test-mm-model',
            'extract_body': {'temperature': 0.0}
        }
        
        # Create FileAgent with multimodal config
        agent = FileAgent(settings=None, model_multimodal=mm_config)
        
        # Verify multimodal model inherited the config
        assert agent.model_multimodal.call_target == 'https://test-mm.modelscope.cn/v1/chat/completions'
        assert agent.model_multimodal.api_key == 'test-mm-key'
        assert agent.model_multimodal.model == 'test-mm-model'
        assert agent.model_multimodal.extract_body == {'temperature': 0.0}
    
    def test_web_agent_inherits_main_model_from_kwargs(self):
        """Test WebAgent inherits main model config through kwargs -> super().__init__"""
        # Create model config that should be inherited
        model_config = {
            'call_target': 'https://test.modelscope.cn/v1/chat/completions',
            'api_key': 'test-key-789',
            'model': 'test-model-web',
            'extract_body': {'temperature': 0.0}
        }
        
        # Create WebAgent with model config
        agent = WebAgent(settings=None, model=model_config)
        
        # Verify main model inherited the config
        assert agent.model.call_target == 'https://test.modelscope.cn/v1/chat/completions'
        assert agent.model.api_key == 'test-key-789'
        assert agent.model.model == 'test-model-web'
        assert agent.model.extract_body == {'temperature': 0.0}
    
    def test_web_agent_inherits_multimodal_model_from_kwargs(self):
        """Test WebAgent inherits multimodal model config from model kwargs (reused)"""
        # WebAgent reuses main model config for multimodal
        model_config = {
            'call_target': 'https://test-web-mm.modelscope.cn/v1/chat/completions',
            'api_key': 'test-web-mm-key',
            'model': 'test-web-mm-model',
            'extract_body': {'temperature': 0.1}
        }
        
        # Create WebAgent with model config
        agent = WebAgent(settings=None, model=model_config)
        
        # Verify multimodal model inherited the same config
        assert agent.model_multimodal.call_target == 'https://test-web-mm.modelscope.cn/v1/chat/completions'
        assert agent.model_multimodal.api_key == 'test-web-mm-key'
        assert agent.model_multimodal.model == 'test-web-mm-model'
        assert agent.model_multimodal.extract_body == {'temperature': 0.1}
    
    def test_file_agent_defaults_when_no_model_config(self):
        """Test FileAgent falls back to defaults when no model config provided"""
        # Create FileAgent without model config
        agent = FileAgent(settings=None)
        
        # Should use default LLM(_default_init=True) behavior
        assert agent.model._default_init == True
        assert agent.model_multimodal._default_init == True
    
    def test_web_agent_defaults_when_no_model_config(self):
        """Test WebAgent falls back to defaults when no model config provided"""
        # Create WebAgent without model config
        agent = WebAgent(settings=None)
        
        # Should use default LLM(_default_init=True) behavior
        assert agent.model._default_init == True
        assert agent.model_multimodal._default_init == True

    def test_full_config_chain_settings_to_agents(self):
        """Test complete config chain: Settings -> CKAgent kwargs -> sub-agents"""
        # Create settings with ModelScope endpoints
        settings = Settings()
        settings.ck.model = LLMConfig(
            call_target='https://api-inference.modelscope.cn/v1/chat/completions',
            api_key='parent-key',
            model='Qwen3-235B-A22B-Instruct-2507'
        )
        settings.file.model = LLMConfig(
            call_target='https://file.modelscope.cn/v1/chat/completions',
            api_key='file-key',
            model='file-model'
        )
        settings.web.model = LLMConfig(
            call_target='https://web.modelscope.cn/v1/chat/completions',
            api_key='web-key',
            model='web-model'
        )

        # Convert to CKAgent kwargs
        kwargs = settings.to_ckagent_kwargs()

        # Extract sub-agent configs
        web_kwargs = kwargs.get('web_agent', {})
        file_kwargs = kwargs.get('file_agent', {})

        # Create agents with extracted configs
        web_agent = WebAgent(settings=settings, **web_kwargs)
        file_agent = FileAgent(settings=settings, **file_kwargs)

        # Verify web agent got correct config
        assert web_agent.model.call_target == 'https://web.modelscope.cn/v1/chat/completions'
        assert web_agent.model.api_key == 'web-key'
        assert web_agent.model.model == 'web-model'

        # Verify file agent got correct config
        assert file_agent.model.call_target == 'https://file.modelscope.cn/v1/chat/completions'
        assert file_agent.model.api_key == 'file-key'
        assert file_agent.model.model == 'file-model'


if __name__ == "__main__":
    pytest.main([__file__, "-v"])