deepcode-hku 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cli/__init__.py +18 -0
- cli/cli_app.py +296 -0
- cli/cli_interface.py +744 -0
- cli/cli_launcher.py +155 -0
- cli/main_cli.py +243 -0
- cli/workflows/__init__.py +11 -0
- cli/workflows/cli_workflow_adapter.py +336 -0
- deepcode.py +219 -0
- deepcode_hku-1.0.1.dist-info/METADATA +695 -0
- deepcode_hku-1.0.1.dist-info/RECORD +44 -0
- deepcode_hku-1.0.1.dist-info/WHEEL +5 -0
- deepcode_hku-1.0.1.dist-info/entry_points.txt +2 -0
- deepcode_hku-1.0.1.dist-info/licenses/LICENSE +21 -0
- deepcode_hku-1.0.1.dist-info/top_level.txt +6 -0
- tools/__init__.py +0 -0
- tools/code_implementation_server.py +1045 -0
- tools/code_indexer.py +1657 -0
- tools/code_reference_indexer.py +486 -0
- tools/command_executor.py +324 -0
- tools/git_command.py +356 -0
- tools/pdf_converter.py +640 -0
- tools/pdf_downloader.py +1370 -0
- tools/pdf_utils.py +52 -0
- ui/__init__.py +43 -0
- ui/app.py +13 -0
- ui/components.py +1450 -0
- ui/handlers.py +773 -0
- ui/layout.py +106 -0
- ui/streamlit_app.py +38 -0
- ui/styles.py +2116 -0
- utils/__init__.py +17 -0
- utils/cli_interface.py +459 -0
- utils/dialogue_logger.py +671 -0
- utils/file_processor.py +426 -0
- utils/simple_llm_logger.py +198 -0
- workflows/__init__.py +31 -0
- workflows/agent_orchestration_engine.py +1371 -0
- workflows/agents/__init__.py +13 -0
- workflows/agents/code_implementation_agent.py +1093 -0
- workflows/agents/memory_agent_concise.py +923 -0
- workflows/agents/memory_agent_concise_index.py +935 -0
- workflows/code_implementation_workflow.py +924 -0
- workflows/code_implementation_workflow_index.py +931 -0
- workflows/codebase_index_workflow.py +726 -0
|
@@ -0,0 +1,931 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Paper Code Implementation Workflow - MCP-compliant Iterative Development
|
|
3
|
+
|
|
4
|
+
Features:
|
|
5
|
+
1. File Tree Creation
|
|
6
|
+
2. Code Implementation - Based on aisi-basic-agent iterative development
|
|
7
|
+
|
|
8
|
+
MCP Architecture:
|
|
9
|
+
- MCP Server: tools/code_implementation_server.py
|
|
10
|
+
- MCP Client: Called through mcp_agent framework
|
|
11
|
+
- Configuration: mcp_agent.config.yaml
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
18
|
+
import time
|
|
19
|
+
import yaml
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Dict, Any, Optional, List
|
|
22
|
+
|
|
23
|
+
# MCP Agent imports
|
|
24
|
+
from mcp_agent.agents.agent import Agent
|
|
25
|
+
from mcp_agent.workflows.llm.augmented_llm_anthropic import AnthropicAugmentedLLM
|
|
26
|
+
from mcp_agent.workflows.llm.augmented_llm_openai import OpenAIAugmentedLLM
|
|
27
|
+
|
|
28
|
+
# Local imports
|
|
29
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
30
|
+
from prompts.code_prompts import STRUCTURE_GENERATOR_PROMPT
|
|
31
|
+
from prompts.code_prompts import (
|
|
32
|
+
PURE_CODE_IMPLEMENTATION_SYSTEM_PROMPT_INDEX,
|
|
33
|
+
)
|
|
34
|
+
from workflows.agents import CodeImplementationAgent
|
|
35
|
+
from workflows.agents.memory_agent_concise import ConciseMemoryAgent
|
|
36
|
+
from config.mcp_tool_definitions_index import get_mcp_tools
|
|
37
|
+
# DialogueLogger removed - no longer needed
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_preferred_llm_class(config_path: str = "mcp_agent.secrets.yaml"):
|
|
41
|
+
"""
|
|
42
|
+
Automatically select the LLM class based on API key availability in configuration.
|
|
43
|
+
|
|
44
|
+
Reads from YAML config file and returns AnthropicAugmentedLLM if anthropic.api_key
|
|
45
|
+
is available, otherwise returns OpenAIAugmentedLLM.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
config_path: Path to the YAML configuration file
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
class: The preferred LLM class
|
|
52
|
+
"""
|
|
53
|
+
try:
|
|
54
|
+
# Try to read the configuration file
|
|
55
|
+
if os.path.exists(config_path):
|
|
56
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
57
|
+
config = yaml.safe_load(f)
|
|
58
|
+
|
|
59
|
+
# Check for anthropic API key in config
|
|
60
|
+
anthropic_config = config.get("anthropic", {})
|
|
61
|
+
anthropic_key = anthropic_config.get("api_key", "")
|
|
62
|
+
|
|
63
|
+
if anthropic_key and anthropic_key.strip() and not anthropic_key == "":
|
|
64
|
+
# print("🤖 Using AnthropicAugmentedLLM (Anthropic API key found in config)")
|
|
65
|
+
return AnthropicAugmentedLLM
|
|
66
|
+
else:
|
|
67
|
+
# print("🤖 Using OpenAIAugmentedLLM (Anthropic API key not configured)")
|
|
68
|
+
return OpenAIAugmentedLLM
|
|
69
|
+
else:
|
|
70
|
+
print(f"🤖 Config file {config_path} not found, using OpenAIAugmentedLLM")
|
|
71
|
+
return OpenAIAugmentedLLM
|
|
72
|
+
|
|
73
|
+
except Exception as e:
|
|
74
|
+
print(f"🤖 Error reading config file {config_path}: {e}")
|
|
75
|
+
print("🤖 Falling back to OpenAIAugmentedLLM")
|
|
76
|
+
return OpenAIAugmentedLLM
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_default_models(config_path: str = "mcp_agent.config.yaml"):
|
|
80
|
+
"""
|
|
81
|
+
Get default models from configuration file.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
config_path: Path to the configuration file
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
dict: Dictionary with 'anthropic' and 'openai' default models
|
|
88
|
+
"""
|
|
89
|
+
try:
|
|
90
|
+
if os.path.exists(config_path):
|
|
91
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
92
|
+
config = yaml.safe_load(f)
|
|
93
|
+
|
|
94
|
+
anthropic_model = config.get("anthropic", {}).get(
|
|
95
|
+
"default_model", "claude-sonnet-4-20250514"
|
|
96
|
+
)
|
|
97
|
+
openai_model = config.get("openai", {}).get("default_model", "o3-mini")
|
|
98
|
+
|
|
99
|
+
return {"anthropic": anthropic_model, "openai": openai_model}
|
|
100
|
+
else:
|
|
101
|
+
print(f"Config file {config_path} not found, using default models")
|
|
102
|
+
return {"anthropic": "claude-sonnet-4-20250514", "openai": "o3-mini"}
|
|
103
|
+
|
|
104
|
+
except Exception as e:
|
|
105
|
+
print(f"Error reading config file {config_path}: {e}")
|
|
106
|
+
return {"anthropic": "claude-sonnet-4-20250514", "openai": "o3-mini"}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class CodeImplementationWorkflowWithIndex:
|
|
110
|
+
"""
|
|
111
|
+
Paper Code Implementation Workflow Manager with Code Reference Indexer
|
|
112
|
+
|
|
113
|
+
Uses standard MCP architecture with enhanced indexing capabilities:
|
|
114
|
+
1. Connect to code-implementation server via MCP client
|
|
115
|
+
2. Use MCP protocol for tool calls
|
|
116
|
+
3. Support workspace management and operation history tracking
|
|
117
|
+
4. Integrated code reference indexer for enhanced code understanding
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
# ==================== 1. Class Initialization and Configuration (Infrastructure Layer) ====================
|
|
121
|
+
|
|
122
|
+
def __init__(self, config_path: str = "mcp_agent.secrets.yaml"):
|
|
123
|
+
"""Initialize workflow with configuration"""
|
|
124
|
+
self.config_path = config_path
|
|
125
|
+
self.api_config = self._load_api_config()
|
|
126
|
+
self.default_models = get_default_models("mcp_agent.config.yaml")
|
|
127
|
+
self.logger = self._create_logger()
|
|
128
|
+
self.mcp_agent = None
|
|
129
|
+
self.enable_read_tools = (
|
|
130
|
+
True # Default value, will be overridden by run_workflow parameter
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def _load_api_config(self) -> Dict[str, Any]:
|
|
134
|
+
"""Load API configuration from YAML file"""
|
|
135
|
+
try:
|
|
136
|
+
with open(self.config_path, "r", encoding="utf-8") as f:
|
|
137
|
+
return yaml.safe_load(f)
|
|
138
|
+
except Exception as e:
|
|
139
|
+
raise Exception(f"Failed to load API config: {e}")
|
|
140
|
+
|
|
141
|
+
def _create_logger(self) -> logging.Logger:
|
|
142
|
+
"""Create and configure logger"""
|
|
143
|
+
logger = logging.getLogger(__name__)
|
|
144
|
+
# Don't add handlers to child loggers - let them propagate to root
|
|
145
|
+
logger.setLevel(logging.INFO)
|
|
146
|
+
return logger
|
|
147
|
+
|
|
148
|
+
def _read_plan_file(self, plan_file_path: str) -> str:
|
|
149
|
+
"""Read implementation plan file"""
|
|
150
|
+
plan_path = Path(plan_file_path)
|
|
151
|
+
if not plan_path.exists():
|
|
152
|
+
raise FileNotFoundError(
|
|
153
|
+
f"Implementation plan file not found: {plan_file_path}"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
with open(plan_path, "r", encoding="utf-8") as f:
|
|
157
|
+
return f.read()
|
|
158
|
+
|
|
159
|
+
def _check_file_tree_exists(self, target_directory: str) -> bool:
|
|
160
|
+
"""Check if file tree structure already exists"""
|
|
161
|
+
code_directory = os.path.join(target_directory, "generate_code")
|
|
162
|
+
return os.path.exists(code_directory) and len(os.listdir(code_directory)) > 0
|
|
163
|
+
|
|
164
|
+
# ==================== 2. Public Interface Methods (External API Layer) ====================
|
|
165
|
+
|
|
166
|
+
async def run_workflow(
|
|
167
|
+
self,
|
|
168
|
+
plan_file_path: str,
|
|
169
|
+
target_directory: Optional[str] = None,
|
|
170
|
+
pure_code_mode: bool = False,
|
|
171
|
+
enable_read_tools: bool = True,
|
|
172
|
+
):
|
|
173
|
+
"""Run complete workflow - Main public interface"""
|
|
174
|
+
# Set the read tools configuration
|
|
175
|
+
self.enable_read_tools = enable_read_tools
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
plan_content = self._read_plan_file(plan_file_path)
|
|
179
|
+
|
|
180
|
+
if target_directory is None:
|
|
181
|
+
target_directory = str(Path(plan_file_path).parent)
|
|
182
|
+
|
|
183
|
+
# Calculate code directory for workspace alignment
|
|
184
|
+
code_directory = os.path.join(target_directory, "generate_code")
|
|
185
|
+
|
|
186
|
+
self.logger.info("=" * 80)
|
|
187
|
+
self.logger.info("🚀 STARTING CODE IMPLEMENTATION WORKFLOW")
|
|
188
|
+
self.logger.info("=" * 80)
|
|
189
|
+
self.logger.info(f"📄 Plan file: {plan_file_path}")
|
|
190
|
+
self.logger.info(f"📂 Plan file parent: {target_directory}")
|
|
191
|
+
self.logger.info(f"🎯 Code directory (MCP workspace): {code_directory}")
|
|
192
|
+
self.logger.info(
|
|
193
|
+
f"⚙️ Read tools: {'ENABLED' if self.enable_read_tools else 'DISABLED'}"
|
|
194
|
+
)
|
|
195
|
+
self.logger.info("=" * 80)
|
|
196
|
+
|
|
197
|
+
results = {}
|
|
198
|
+
|
|
199
|
+
# Check if file tree exists
|
|
200
|
+
if self._check_file_tree_exists(target_directory):
|
|
201
|
+
self.logger.info("File tree exists, skipping creation")
|
|
202
|
+
results["file_tree"] = "Already exists, skipped creation"
|
|
203
|
+
else:
|
|
204
|
+
self.logger.info("Creating file tree...")
|
|
205
|
+
results["file_tree"] = await self.create_file_structure(
|
|
206
|
+
plan_content, target_directory
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Code implementation
|
|
210
|
+
if pure_code_mode:
|
|
211
|
+
self.logger.info("Starting pure code implementation...")
|
|
212
|
+
results["code_implementation"] = await self.implement_code_pure(
|
|
213
|
+
plan_content, target_directory, code_directory
|
|
214
|
+
)
|
|
215
|
+
else:
|
|
216
|
+
pass
|
|
217
|
+
|
|
218
|
+
self.logger.info("Workflow execution successful")
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
"status": "success",
|
|
222
|
+
"plan_file": plan_file_path,
|
|
223
|
+
"target_directory": target_directory,
|
|
224
|
+
"code_directory": os.path.join(target_directory, "generate_code"),
|
|
225
|
+
"results": results,
|
|
226
|
+
"mcp_architecture": "standard",
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
except Exception as e:
|
|
230
|
+
self.logger.error(f"Workflow execution failed: {e}")
|
|
231
|
+
|
|
232
|
+
return {"status": "error", "message": str(e), "plan_file": plan_file_path}
|
|
233
|
+
finally:
|
|
234
|
+
await self._cleanup_mcp_agent()
|
|
235
|
+
|
|
236
|
+
async def create_file_structure(
|
|
237
|
+
self, plan_content: str, target_directory: str
|
|
238
|
+
) -> str:
|
|
239
|
+
"""Create file tree structure based on implementation plan"""
|
|
240
|
+
self.logger.info("Starting file tree creation...")
|
|
241
|
+
|
|
242
|
+
structure_agent = Agent(
|
|
243
|
+
name="StructureGeneratorAgent",
|
|
244
|
+
instruction=STRUCTURE_GENERATOR_PROMPT,
|
|
245
|
+
server_names=["command-executor"],
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
async with structure_agent:
|
|
249
|
+
creator = await structure_agent.attach_llm(
|
|
250
|
+
get_preferred_llm_class(self.config_path)
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
message = f"""Analyze the following implementation plan and generate shell commands to create the file tree structure.
|
|
254
|
+
|
|
255
|
+
Target Directory: {target_directory}/generate_code
|
|
256
|
+
|
|
257
|
+
Implementation Plan:
|
|
258
|
+
{plan_content}
|
|
259
|
+
|
|
260
|
+
Tasks:
|
|
261
|
+
1. Find the file tree structure in the implementation plan
|
|
262
|
+
2. Generate shell commands (mkdir -p, touch) to create that structure
|
|
263
|
+
3. Use the execute_commands tool to run the commands and create the file structure
|
|
264
|
+
|
|
265
|
+
Requirements:
|
|
266
|
+
- Use mkdir -p to create directories
|
|
267
|
+
- Use touch to create files
|
|
268
|
+
- Include __init__.py file for Python packages
|
|
269
|
+
- Use relative paths to the target directory
|
|
270
|
+
- Execute commands to actually create the file structure"""
|
|
271
|
+
|
|
272
|
+
result = await creator.generate_str(message=message)
|
|
273
|
+
self.logger.info("File tree structure creation completed")
|
|
274
|
+
return result
|
|
275
|
+
|
|
276
|
+
async def implement_code_pure(
|
|
277
|
+
self, plan_content: str, target_directory: str, code_directory: str = None
|
|
278
|
+
) -> str:
|
|
279
|
+
"""Pure code implementation - focus on code writing without testing"""
|
|
280
|
+
self.logger.info("Starting pure code implementation (no testing)...")
|
|
281
|
+
|
|
282
|
+
# Use provided code_directory or calculate it (for backwards compatibility)
|
|
283
|
+
if code_directory is None:
|
|
284
|
+
code_directory = os.path.join(target_directory, "generate_code")
|
|
285
|
+
|
|
286
|
+
self.logger.info(f"🎯 Using code directory (MCP workspace): {code_directory}")
|
|
287
|
+
|
|
288
|
+
if not os.path.exists(code_directory):
|
|
289
|
+
raise FileNotFoundError(
|
|
290
|
+
"File tree structure not found, please run file tree creation first"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
try:
|
|
294
|
+
client, client_type = await self._initialize_llm_client()
|
|
295
|
+
await self._initialize_mcp_agent(code_directory)
|
|
296
|
+
|
|
297
|
+
tools = self._prepare_mcp_tool_definitions()
|
|
298
|
+
system_message = PURE_CODE_IMPLEMENTATION_SYSTEM_PROMPT_INDEX
|
|
299
|
+
messages = []
|
|
300
|
+
|
|
301
|
+
# implementation_message = f"""**TASK: Implement Research Paper Reproduction Code**
|
|
302
|
+
|
|
303
|
+
# You are implementing a complete, working codebase that reproduces the core algorithms, experiments, and methods described in a research paper. Your goal is to create functional code that can replicate the paper's key results and contributions.
|
|
304
|
+
|
|
305
|
+
# **What you need to do:**
|
|
306
|
+
# - Analyze the paper content and reproduction plan to understand requirements
|
|
307
|
+
# - Implement all core algorithms mentioned in the main body of the paper
|
|
308
|
+
# - Create the necessary components following the planned architecture
|
|
309
|
+
# - Test each component to ensure functionality
|
|
310
|
+
# - Integrate components into a cohesive, executable system
|
|
311
|
+
# - Focus on reproducing main contributions rather than appendix-only experiments
|
|
312
|
+
|
|
313
|
+
# **RESOURCES:**
|
|
314
|
+
# - **Paper & Reproduction Plan**: `{target_directory}/` (contains .md paper files and initial_plan.txt with detailed implementation guidance)
|
|
315
|
+
# - **Reference Code Indexes**: `{target_directory}/indexes/` (JSON files with implementation patterns from related codebases)
|
|
316
|
+
# - **Implementation Directory**: `{code_directory}/` (your working directory for all code files)
|
|
317
|
+
|
|
318
|
+
# **CURRENT OBJECTIVE:**
|
|
319
|
+
# Start by reading the reproduction plan (`{target_directory}/initial_plan.txt`) to understand the implementation strategy, then examine the paper content to identify the first priority component to implement. Use the search_code tool to find relevant reference implementations from the indexes directory (`{target_directory}/indexes/*.json`) before coding.
|
|
320
|
+
|
|
321
|
+
# ---
|
|
322
|
+
# **START:** Review the plan above and begin implementation."""
|
|
323
|
+
implementation_message = f"""**Task: Implement code based on the following reproduction plan**
|
|
324
|
+
|
|
325
|
+
**Code Reproduction Plan:**
|
|
326
|
+
{plan_content}
|
|
327
|
+
|
|
328
|
+
**Working Directory:** {code_directory}
|
|
329
|
+
|
|
330
|
+
**Current Objective:** Begin implementation by analyzing the plan structure, examining the current project layout, and implementing the first foundation file according to the plan's priority order."""
|
|
331
|
+
|
|
332
|
+
messages.append({"role": "user", "content": implementation_message})
|
|
333
|
+
|
|
334
|
+
result = await self._pure_code_implementation_loop(
|
|
335
|
+
client,
|
|
336
|
+
client_type,
|
|
337
|
+
system_message,
|
|
338
|
+
messages,
|
|
339
|
+
tools,
|
|
340
|
+
plan_content,
|
|
341
|
+
target_directory,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
return result
|
|
345
|
+
|
|
346
|
+
finally:
|
|
347
|
+
await self._cleanup_mcp_agent()
|
|
348
|
+
|
|
349
|
+
# ==================== 3. Core Business Logic (Implementation Layer) ====================
|
|
350
|
+
|
|
351
|
+
async def _pure_code_implementation_loop(
|
|
352
|
+
self,
|
|
353
|
+
client,
|
|
354
|
+
client_type,
|
|
355
|
+
system_message,
|
|
356
|
+
messages,
|
|
357
|
+
tools,
|
|
358
|
+
plan_content,
|
|
359
|
+
target_directory,
|
|
360
|
+
):
|
|
361
|
+
"""Pure code implementation loop with memory optimization and phase consistency"""
|
|
362
|
+
max_iterations = 100
|
|
363
|
+
iteration = 0
|
|
364
|
+
start_time = time.time()
|
|
365
|
+
max_time = 2400 # 40 minutes
|
|
366
|
+
|
|
367
|
+
# Initialize specialized agents
|
|
368
|
+
code_agent = CodeImplementationAgent(
|
|
369
|
+
self.mcp_agent, self.logger, self.enable_read_tools
|
|
370
|
+
)
|
|
371
|
+
memory_agent = ConciseMemoryAgent(plan_content, self.logger, target_directory)
|
|
372
|
+
|
|
373
|
+
# Log read tools configuration
|
|
374
|
+
read_tools_status = "ENABLED" if self.enable_read_tools else "DISABLED"
|
|
375
|
+
self.logger.info(
|
|
376
|
+
f"🔧 Read tools (read_file, read_code_mem): {read_tools_status}"
|
|
377
|
+
)
|
|
378
|
+
if not self.enable_read_tools:
|
|
379
|
+
self.logger.info(
|
|
380
|
+
"🚫 No read mode: read_file and read_code_mem tools will be skipped"
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
# Connect code agent with memory agent for summary generation
|
|
384
|
+
# Note: Concise memory agent doesn't need LLM client for summary generation
|
|
385
|
+
code_agent.set_memory_agent(memory_agent, client, client_type)
|
|
386
|
+
|
|
387
|
+
# Initialize memory agent with iteration 0
|
|
388
|
+
memory_agent.start_new_round(iteration=0)
|
|
389
|
+
|
|
390
|
+
while iteration < max_iterations:
|
|
391
|
+
iteration += 1
|
|
392
|
+
elapsed_time = time.time() - start_time
|
|
393
|
+
|
|
394
|
+
if elapsed_time > max_time:
|
|
395
|
+
self.logger.warning(f"Time limit reached: {elapsed_time:.2f}s")
|
|
396
|
+
break
|
|
397
|
+
|
|
398
|
+
# # Test simplified memory approach if we have files implemented
|
|
399
|
+
# if iteration == 5 and code_agent.get_files_implemented_count() > 0:
|
|
400
|
+
# self.logger.info("🧪 Testing simplified memory approach...")
|
|
401
|
+
# test_results = await memory_agent.test_simplified_memory_approach()
|
|
402
|
+
# self.logger.info(f"Memory test results: {test_results}")
|
|
403
|
+
|
|
404
|
+
# self.logger.info(f"Pure code implementation iteration {iteration}: generating code")
|
|
405
|
+
|
|
406
|
+
messages = self._validate_messages(messages)
|
|
407
|
+
current_system_message = code_agent.get_system_prompt()
|
|
408
|
+
|
|
409
|
+
# Round logging removed
|
|
410
|
+
|
|
411
|
+
# Call LLM
|
|
412
|
+
response = await self._call_llm_with_tools(
|
|
413
|
+
client, client_type, current_system_message, messages, tools
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
response_content = response.get("content", "").strip()
|
|
417
|
+
if not response_content:
|
|
418
|
+
response_content = "Continue implementing code files..."
|
|
419
|
+
|
|
420
|
+
messages.append({"role": "assistant", "content": response_content})
|
|
421
|
+
|
|
422
|
+
# Handle tool calls
|
|
423
|
+
if response.get("tool_calls"):
|
|
424
|
+
tool_results = await code_agent.execute_tool_calls(
|
|
425
|
+
response["tool_calls"]
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
# Record essential tool results in concise memory agent
|
|
429
|
+
for tool_call, tool_result in zip(response["tool_calls"], tool_results):
|
|
430
|
+
memory_agent.record_tool_result(
|
|
431
|
+
tool_name=tool_call["name"],
|
|
432
|
+
tool_input=tool_call["input"],
|
|
433
|
+
tool_result=tool_result.get("result"),
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
# NEW LOGIC: Check if write_file was called and trigger memory optimization immediately
|
|
437
|
+
|
|
438
|
+
# Determine guidance based on results
|
|
439
|
+
has_error = self._check_tool_results_for_errors(tool_results)
|
|
440
|
+
files_count = code_agent.get_files_implemented_count()
|
|
441
|
+
|
|
442
|
+
if has_error:
|
|
443
|
+
guidance = self._generate_error_guidance()
|
|
444
|
+
else:
|
|
445
|
+
guidance = self._generate_success_guidance(files_count)
|
|
446
|
+
|
|
447
|
+
compiled_response = self._compile_user_response(tool_results, guidance)
|
|
448
|
+
messages.append({"role": "user", "content": compiled_response})
|
|
449
|
+
|
|
450
|
+
# NEW LOGIC: Apply memory optimization immediately after write_file detection
|
|
451
|
+
if memory_agent.should_trigger_memory_optimization(
|
|
452
|
+
messages, code_agent.get_files_implemented_count()
|
|
453
|
+
):
|
|
454
|
+
# Apply concise memory optimization
|
|
455
|
+
files_implemented_count = code_agent.get_files_implemented_count()
|
|
456
|
+
current_system_message = code_agent.get_system_prompt()
|
|
457
|
+
messages = memory_agent.apply_memory_optimization(
|
|
458
|
+
current_system_message, messages, files_implemented_count
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
# Memory optimization completed
|
|
462
|
+
|
|
463
|
+
else:
|
|
464
|
+
files_count = code_agent.get_files_implemented_count()
|
|
465
|
+
no_tools_guidance = self._generate_no_tools_guidance(files_count)
|
|
466
|
+
messages.append({"role": "user", "content": no_tools_guidance})
|
|
467
|
+
|
|
468
|
+
# Check for analysis loop and provide corrective guidance
|
|
469
|
+
if code_agent.is_in_analysis_loop():
|
|
470
|
+
analysis_loop_guidance = code_agent.get_analysis_loop_guidance()
|
|
471
|
+
messages.append({"role": "user", "content": analysis_loop_guidance})
|
|
472
|
+
self.logger.warning(
|
|
473
|
+
"Analysis loop detected and corrective guidance provided"
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
# Round completed
|
|
477
|
+
|
|
478
|
+
# Record file implementations in memory agent (for the current round)
|
|
479
|
+
for file_info in code_agent.get_implementation_summary()["completed_files"]:
|
|
480
|
+
memory_agent.record_file_implementation(file_info["file"])
|
|
481
|
+
|
|
482
|
+
# REMOVED: Old memory optimization logic - now happens immediately after write_file
|
|
483
|
+
# Memory optimization is now triggered immediately after write_file detection
|
|
484
|
+
|
|
485
|
+
# Start new round for next iteration, sync with workflow iteration
|
|
486
|
+
memory_agent.start_new_round(iteration=iteration)
|
|
487
|
+
|
|
488
|
+
# Check completion
|
|
489
|
+
if any(
|
|
490
|
+
keyword in response_content.lower()
|
|
491
|
+
for keyword in [
|
|
492
|
+
"all files implemented",
|
|
493
|
+
"implementation complete",
|
|
494
|
+
"all phases completed",
|
|
495
|
+
"reproduction plan fully implemented",
|
|
496
|
+
]
|
|
497
|
+
):
|
|
498
|
+
self.logger.info("Code implementation declared complete")
|
|
499
|
+
break
|
|
500
|
+
|
|
501
|
+
# Emergency trim if too long
|
|
502
|
+
if len(messages) > 50:
|
|
503
|
+
self.logger.warning(
|
|
504
|
+
"Emergency message trim - applying concise memory optimization"
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
# Apply emergency memory optimization
|
|
508
|
+
current_system_message = code_agent.get_system_prompt()
|
|
509
|
+
files_implemented_count = code_agent.get_files_implemented_count()
|
|
510
|
+
messages = memory_agent.apply_memory_optimization(
|
|
511
|
+
current_system_message, messages, files_implemented_count
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
return await self._generate_pure_code_final_report_with_concise_agents(
|
|
515
|
+
iteration, time.time() - start_time, code_agent, memory_agent
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
# ==================== 4. MCP Agent and LLM Communication Management (Communication Layer) ====================
|
|
519
|
+
|
|
520
|
+
async def _initialize_mcp_agent(self, code_directory: str):
|
|
521
|
+
"""Initialize MCP agent and connect to code-implementation server"""
|
|
522
|
+
try:
|
|
523
|
+
self.mcp_agent = Agent(
|
|
524
|
+
name="CodeImplementationAgent",
|
|
525
|
+
instruction="You are a code implementation assistant, using MCP tools to implement paper code replication.",
|
|
526
|
+
server_names=["code-implementation", "code-reference-indexer"],
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
await self.mcp_agent.__aenter__()
|
|
530
|
+
llm = await self.mcp_agent.attach_llm(
|
|
531
|
+
get_preferred_llm_class(self.config_path)
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
# Set workspace to the target code directory
|
|
535
|
+
workspace_result = await self.mcp_agent.call_tool(
|
|
536
|
+
"set_workspace", {"workspace_path": code_directory}
|
|
537
|
+
)
|
|
538
|
+
self.logger.info(f"Workspace setup result: {workspace_result}")
|
|
539
|
+
|
|
540
|
+
return llm
|
|
541
|
+
|
|
542
|
+
except Exception as e:
|
|
543
|
+
self.logger.error(f"Failed to initialize MCP agent: {e}")
|
|
544
|
+
if self.mcp_agent:
|
|
545
|
+
try:
|
|
546
|
+
await self.mcp_agent.__aexit__(None, None, None)
|
|
547
|
+
except Exception:
|
|
548
|
+
pass
|
|
549
|
+
self.mcp_agent = None
|
|
550
|
+
raise
|
|
551
|
+
|
|
552
|
+
async def _cleanup_mcp_agent(self):
|
|
553
|
+
"""Clean up MCP agent resources"""
|
|
554
|
+
if self.mcp_agent:
|
|
555
|
+
try:
|
|
556
|
+
await self.mcp_agent.__aexit__(None, None, None)
|
|
557
|
+
self.logger.info("MCP agent connection closed")
|
|
558
|
+
except Exception as e:
|
|
559
|
+
self.logger.warning(f"Error closing MCP agent: {e}")
|
|
560
|
+
finally:
|
|
561
|
+
self.mcp_agent = None
|
|
562
|
+
|
|
563
|
+
async def _initialize_llm_client(self):
|
|
564
|
+
"""Initialize LLM client (Anthropic or OpenAI) based on API key availability"""
|
|
565
|
+
# Check which API has available key and try that first
|
|
566
|
+
anthropic_key = self.api_config.get("anthropic", {}).get("api_key", "")
|
|
567
|
+
openai_key = self.api_config.get("openai", {}).get("api_key", "")
|
|
568
|
+
|
|
569
|
+
# Try Anthropic API first if key is available
|
|
570
|
+
if anthropic_key and anthropic_key.strip():
|
|
571
|
+
try:
|
|
572
|
+
from anthropic import AsyncAnthropic
|
|
573
|
+
|
|
574
|
+
client = AsyncAnthropic(api_key=anthropic_key)
|
|
575
|
+
# Test connection with default model from config
|
|
576
|
+
await client.messages.create(
|
|
577
|
+
model=self.default_models["anthropic"],
|
|
578
|
+
max_tokens=10,
|
|
579
|
+
messages=[{"role": "user", "content": "test"}],
|
|
580
|
+
)
|
|
581
|
+
self.logger.info(
|
|
582
|
+
f"Using Anthropic API with model: {self.default_models['anthropic']}"
|
|
583
|
+
)
|
|
584
|
+
return client, "anthropic"
|
|
585
|
+
except Exception as e:
|
|
586
|
+
self.logger.warning(f"Anthropic API unavailable: {e}")
|
|
587
|
+
|
|
588
|
+
# Try OpenAI API if Anthropic failed or key not available
|
|
589
|
+
if openai_key and openai_key.strip():
|
|
590
|
+
try:
|
|
591
|
+
from openai import AsyncOpenAI
|
|
592
|
+
|
|
593
|
+
# Handle custom base_url if specified
|
|
594
|
+
openai_config = self.api_config.get("openai", {})
|
|
595
|
+
base_url = openai_config.get("base_url")
|
|
596
|
+
|
|
597
|
+
if base_url:
|
|
598
|
+
client = AsyncOpenAI(api_key=openai_key, base_url=base_url)
|
|
599
|
+
else:
|
|
600
|
+
client = AsyncOpenAI(api_key=openai_key)
|
|
601
|
+
|
|
602
|
+
# Test connection with default model from config
|
|
603
|
+
await client.chat.completions.create(
|
|
604
|
+
model=self.default_models["openai"],
|
|
605
|
+
max_tokens=10,
|
|
606
|
+
messages=[{"role": "user", "content": "test"}],
|
|
607
|
+
)
|
|
608
|
+
self.logger.info(
|
|
609
|
+
f"Using OpenAI API with model: {self.default_models['openai']}"
|
|
610
|
+
)
|
|
611
|
+
if base_url:
|
|
612
|
+
self.logger.info(f"Using custom base URL: {base_url}")
|
|
613
|
+
return client, "openai"
|
|
614
|
+
except Exception as e:
|
|
615
|
+
self.logger.warning(f"OpenAI API unavailable: {e}")
|
|
616
|
+
|
|
617
|
+
raise ValueError(
|
|
618
|
+
"No available LLM API - please check your API keys in configuration"
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
async def _call_llm_with_tools(
|
|
622
|
+
self, client, client_type, system_message, messages, tools, max_tokens=8192
|
|
623
|
+
):
|
|
624
|
+
"""Call LLM with tools"""
|
|
625
|
+
try:
|
|
626
|
+
if client_type == "anthropic":
|
|
627
|
+
return await self._call_anthropic_with_tools(
|
|
628
|
+
client, system_message, messages, tools, max_tokens
|
|
629
|
+
)
|
|
630
|
+
elif client_type == "openai":
|
|
631
|
+
return await self._call_openai_with_tools(
|
|
632
|
+
client, system_message, messages, tools, max_tokens
|
|
633
|
+
)
|
|
634
|
+
else:
|
|
635
|
+
raise ValueError(f"Unsupported client type: {client_type}")
|
|
636
|
+
except Exception as e:
|
|
637
|
+
self.logger.error(f"LLM call failed: {e}")
|
|
638
|
+
raise
|
|
639
|
+
|
|
640
|
+
async def _call_anthropic_with_tools(
|
|
641
|
+
self, client, system_message, messages, tools, max_tokens
|
|
642
|
+
):
|
|
643
|
+
"""Call Anthropic API"""
|
|
644
|
+
validated_messages = self._validate_messages(messages)
|
|
645
|
+
if not validated_messages:
|
|
646
|
+
validated_messages = [
|
|
647
|
+
{"role": "user", "content": "Please continue implementing code"}
|
|
648
|
+
]
|
|
649
|
+
|
|
650
|
+
try:
|
|
651
|
+
response = await client.messages.create(
|
|
652
|
+
model=self.default_models["anthropic"],
|
|
653
|
+
system=system_message,
|
|
654
|
+
messages=validated_messages,
|
|
655
|
+
tools=tools,
|
|
656
|
+
max_tokens=max_tokens,
|
|
657
|
+
temperature=0.2,
|
|
658
|
+
)
|
|
659
|
+
except Exception as e:
|
|
660
|
+
self.logger.error(f"Anthropic API call failed: {e}")
|
|
661
|
+
raise
|
|
662
|
+
|
|
663
|
+
content = ""
|
|
664
|
+
tool_calls = []
|
|
665
|
+
|
|
666
|
+
for block in response.content:
|
|
667
|
+
if block.type == "text":
|
|
668
|
+
content += block.text
|
|
669
|
+
elif block.type == "tool_use":
|
|
670
|
+
tool_calls.append(
|
|
671
|
+
{"id": block.id, "name": block.name, "input": block.input}
|
|
672
|
+
)
|
|
673
|
+
|
|
674
|
+
return {"content": content, "tool_calls": tool_calls}
|
|
675
|
+
|
|
676
|
+
async def _call_openai_with_tools(
|
|
677
|
+
self, client, system_message, messages, tools, max_tokens
|
|
678
|
+
):
|
|
679
|
+
"""Call OpenAI API"""
|
|
680
|
+
openai_tools = []
|
|
681
|
+
for tool in tools:
|
|
682
|
+
openai_tools.append(
|
|
683
|
+
{
|
|
684
|
+
"type": "function",
|
|
685
|
+
"function": {
|
|
686
|
+
"name": tool["name"],
|
|
687
|
+
"description": tool["description"],
|
|
688
|
+
"parameters": tool["input_schema"],
|
|
689
|
+
},
|
|
690
|
+
}
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
openai_messages = [{"role": "system", "content": system_message}]
|
|
694
|
+
openai_messages.extend(messages)
|
|
695
|
+
|
|
696
|
+
response = await client.chat.completions.create(
|
|
697
|
+
model=self.default_models["openai"],
|
|
698
|
+
messages=openai_messages,
|
|
699
|
+
tools=openai_tools if openai_tools else None,
|
|
700
|
+
max_tokens=max_tokens,
|
|
701
|
+
temperature=0.2,
|
|
702
|
+
)
|
|
703
|
+
|
|
704
|
+
message = response.choices[0].message
|
|
705
|
+
content = message.content or ""
|
|
706
|
+
|
|
707
|
+
tool_calls = []
|
|
708
|
+
if message.tool_calls:
|
|
709
|
+
for tool_call in message.tool_calls:
|
|
710
|
+
tool_calls.append(
|
|
711
|
+
{
|
|
712
|
+
"id": tool_call.id,
|
|
713
|
+
"name": tool_call.function.name,
|
|
714
|
+
"input": json.loads(tool_call.function.arguments),
|
|
715
|
+
}
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
return {"content": content, "tool_calls": tool_calls}
|
|
719
|
+
|
|
720
|
+
# ==================== 5. Tools and Utility Methods (Utility Layer) ====================
|
|
721
|
+
|
|
722
|
+
def _validate_messages(self, messages: List[Dict]) -> List[Dict]:
|
|
723
|
+
"""Validate and clean message list"""
|
|
724
|
+
valid_messages = []
|
|
725
|
+
for msg in messages:
|
|
726
|
+
content = msg.get("content", "").strip()
|
|
727
|
+
if content:
|
|
728
|
+
valid_messages.append(
|
|
729
|
+
{"role": msg.get("role", "user"), "content": content}
|
|
730
|
+
)
|
|
731
|
+
else:
|
|
732
|
+
self.logger.warning(f"Skipping empty message: {msg}")
|
|
733
|
+
return valid_messages
|
|
734
|
+
|
|
735
|
+
def _prepare_mcp_tool_definitions(self) -> List[Dict[str, Any]]:
|
|
736
|
+
"""Prepare tool definitions in Anthropic API standard format"""
|
|
737
|
+
return get_mcp_tools("code_implementation")
|
|
738
|
+
|
|
739
|
+
def _check_tool_results_for_errors(self, tool_results: List[Dict]) -> bool:
|
|
740
|
+
"""Check tool results for errors"""
|
|
741
|
+
for result in tool_results:
|
|
742
|
+
try:
|
|
743
|
+
if hasattr(result["result"], "content") and result["result"].content:
|
|
744
|
+
content_text = result["result"].content[0].text
|
|
745
|
+
parsed_result = json.loads(content_text)
|
|
746
|
+
if parsed_result.get("status") == "error":
|
|
747
|
+
return True
|
|
748
|
+
elif isinstance(result["result"], str):
|
|
749
|
+
if "error" in result["result"].lower():
|
|
750
|
+
return True
|
|
751
|
+
except (json.JSONDecodeError, AttributeError, IndexError):
|
|
752
|
+
result_str = str(result["result"])
|
|
753
|
+
if "error" in result_str.lower():
|
|
754
|
+
return True
|
|
755
|
+
return False
|
|
756
|
+
|
|
757
|
+
# ==================== 6. User Interaction and Feedback (Interaction Layer) ====================
|
|
758
|
+
|
|
759
|
+
def _generate_success_guidance(self, files_count: int) -> str:
|
|
760
|
+
"""Generate concise success guidance for continuing implementation"""
|
|
761
|
+
return f"""✅ File implementation completed successfully!
|
|
762
|
+
|
|
763
|
+
📊 **Progress Status:** {files_count} files implemented
|
|
764
|
+
|
|
765
|
+
🎯 **Next Action:** Check if ALL files from the reproduction plan are implemented.
|
|
766
|
+
|
|
767
|
+
⚡ **Decision Process:**
|
|
768
|
+
1. **If ALL files are implemented:** Use `execute_python` or `execute_bash` to test the complete implementation, then respond "**implementation complete**" to end the conversation
|
|
769
|
+
2. **If MORE files need implementation:** Continue with dependency-aware workflow:
|
|
770
|
+
- **Start with `read_code_mem`** to understand existing implementations and dependencies
|
|
771
|
+
- **Optionally use `search_code_references`** for reference patterns (OPTIONAL - use for inspiration only, original paper specs take priority)
|
|
772
|
+
- **Then `write_file`** to implement the new component based on original paper requirements
|
|
773
|
+
- **Finally: Test** if needed
|
|
774
|
+
|
|
775
|
+
💡 **Key Point:** Always verify completion status before continuing with new file creation."""
|
|
776
|
+
|
|
777
|
+
def _generate_error_guidance(self) -> str:
|
|
778
|
+
"""Generate error guidance for handling issues"""
|
|
779
|
+
return """❌ Error detected during file implementation.
|
|
780
|
+
|
|
781
|
+
🔧 **Action Required:**
|
|
782
|
+
1. Review the error details above
|
|
783
|
+
2. Fix the identified issue
|
|
784
|
+
3. **Check if ALL files from the reproduction plan are implemented:**
|
|
785
|
+
- **If YES:** Use `execute_python` or `execute_bash` to test the complete implementation, then respond "**implementation complete**" to end the conversation
|
|
786
|
+
- **If NO:** Continue with proper development cycle for next file:
|
|
787
|
+
- **Start with `read_code_mem`** to understand existing implementations
|
|
788
|
+
- **Optionally use `search_code_references`** for reference patterns (OPTIONAL - for inspiration only)
|
|
789
|
+
- **Then `write_file`** to implement properly based on original paper requirements
|
|
790
|
+
- **Test** if needed
|
|
791
|
+
4. Ensure proper error handling in future implementations
|
|
792
|
+
|
|
793
|
+
💡 **Remember:** Always verify if all planned files are implemented before continuing with new file creation."""
|
|
794
|
+
|
|
795
|
+
def _generate_no_tools_guidance(self, files_count: int) -> str:
|
|
796
|
+
"""Generate concise guidance when no tools are called"""
|
|
797
|
+
return f"""⚠️ No tool calls detected in your response.
|
|
798
|
+
|
|
799
|
+
📊 **Current Progress:** {files_count} files implemented
|
|
800
|
+
|
|
801
|
+
🚨 **Action Required:** You must use tools. **FIRST check if ALL files from the reproduction plan are implemented:**
|
|
802
|
+
|
|
803
|
+
⚡ **Decision Process:**
|
|
804
|
+
1. **If ALL files are implemented:** Use `execute_python` or `execute_bash` to test the complete implementation, then respond "**implementation complete**" to end the conversation
|
|
805
|
+
2. **If MORE files need implementation:** Follow the development cycle:
|
|
806
|
+
- **Start with `read_code_mem`** to understand existing implementations
|
|
807
|
+
- **Optionally use `search_code_references`** for reference patterns (OPTIONAL - for inspiration only)
|
|
808
|
+
- **Then `write_file`** to implement the new component based on original paper requirements
|
|
809
|
+
- **Finally: Test** if needed
|
|
810
|
+
|
|
811
|
+
🚨 **Critical:** Always verify completion status first, then use appropriate tools - not just explanations!"""
|
|
812
|
+
|
|
813
|
+
def _compile_user_response(self, tool_results: List[Dict], guidance: str) -> str:
|
|
814
|
+
"""Compile tool results and guidance into a single user response"""
|
|
815
|
+
response_parts = []
|
|
816
|
+
|
|
817
|
+
if tool_results:
|
|
818
|
+
response_parts.append("🔧 **Tool Execution Results:**")
|
|
819
|
+
for tool_result in tool_results:
|
|
820
|
+
tool_name = tool_result["tool_name"]
|
|
821
|
+
result_content = tool_result["result"]
|
|
822
|
+
response_parts.append(
|
|
823
|
+
f"```\nTool: {tool_name}\nResult: {result_content}\n```"
|
|
824
|
+
)
|
|
825
|
+
|
|
826
|
+
if guidance:
|
|
827
|
+
response_parts.append("\n" + guidance)
|
|
828
|
+
|
|
829
|
+
return "\n\n".join(response_parts)
|
|
830
|
+
|
|
831
|
+
# ==================== 7. Reporting and Output (Output Layer) ====================
|
|
832
|
+
|
|
833
|
+
async def _generate_pure_code_final_report_with_concise_agents(
|
|
834
|
+
self,
|
|
835
|
+
iterations: int,
|
|
836
|
+
elapsed_time: float,
|
|
837
|
+
code_agent: CodeImplementationAgent,
|
|
838
|
+
memory_agent: ConciseMemoryAgent,
|
|
839
|
+
):
|
|
840
|
+
"""Generate final report using concise agent statistics"""
|
|
841
|
+
try:
|
|
842
|
+
code_stats = code_agent.get_implementation_statistics()
|
|
843
|
+
memory_stats = memory_agent.get_memory_statistics(
|
|
844
|
+
code_stats["files_implemented_count"]
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
if self.mcp_agent:
|
|
848
|
+
history_result = await self.mcp_agent.call_tool(
|
|
849
|
+
"get_operation_history", {"last_n": 30}
|
|
850
|
+
)
|
|
851
|
+
history_data = (
|
|
852
|
+
json.loads(history_result)
|
|
853
|
+
if isinstance(history_result, str)
|
|
854
|
+
else history_result
|
|
855
|
+
)
|
|
856
|
+
else:
|
|
857
|
+
history_data = {"total_operations": 0, "history": []}
|
|
858
|
+
|
|
859
|
+
write_operations = 0
|
|
860
|
+
files_created = []
|
|
861
|
+
if "history" in history_data:
|
|
862
|
+
for item in history_data["history"]:
|
|
863
|
+
if item.get("action") == "write_file":
|
|
864
|
+
write_operations += 1
|
|
865
|
+
file_path = item.get("details", {}).get("file_path", "unknown")
|
|
866
|
+
files_created.append(file_path)
|
|
867
|
+
|
|
868
|
+
report = f"""
|
|
869
|
+
# Pure Code Implementation Completion Report (Write-File-Based Memory Mode)
|
|
870
|
+
|
|
871
|
+
## Execution Summary
|
|
872
|
+
- Implementation iterations: {iterations}
|
|
873
|
+
- Total elapsed time: {elapsed_time:.2f} seconds
|
|
874
|
+
- Files implemented: {code_stats['total_files_implemented']}
|
|
875
|
+
- File write operations: {write_operations}
|
|
876
|
+
- Total MCP operations: {history_data.get('total_operations', 0)}
|
|
877
|
+
|
|
878
|
+
## Read Tools Configuration
|
|
879
|
+
- Read tools enabled: {code_stats['read_tools_status']['read_tools_enabled']}
|
|
880
|
+
- Status: {code_stats['read_tools_status']['status']}
|
|
881
|
+
- Tools affected: {', '.join(code_stats['read_tools_status']['tools_affected'])}
|
|
882
|
+
|
|
883
|
+
## Agent Performance
|
|
884
|
+
### Code Implementation Agent
|
|
885
|
+
- Files tracked: {code_stats['files_implemented_count']}
|
|
886
|
+
- Technical decisions: {code_stats['technical_decisions_count']}
|
|
887
|
+
- Constraints tracked: {code_stats['constraints_count']}
|
|
888
|
+
- Architecture notes: {code_stats['architecture_notes_count']}
|
|
889
|
+
- Dependency analysis performed: {code_stats['dependency_analysis_count']}
|
|
890
|
+
- Files read for dependencies: {code_stats['files_read_for_dependencies']}
|
|
891
|
+
- Last summary triggered at file count: {code_stats['last_summary_file_count']}
|
|
892
|
+
|
|
893
|
+
### Concise Memory Agent (Write-File-Based)
|
|
894
|
+
- Last write_file detected: {memory_stats['last_write_file_detected']}
|
|
895
|
+
- Should clear memory next: {memory_stats['should_clear_memory_next']}
|
|
896
|
+
- Files implemented count: {memory_stats['implemented_files_tracked']}
|
|
897
|
+
- Current round: {memory_stats['current_round']}
|
|
898
|
+
- Concise mode active: {memory_stats['concise_mode_active']}
|
|
899
|
+
- Current round tool results: {memory_stats['current_round_tool_results']}
|
|
900
|
+
- Essential tools recorded: {memory_stats['essential_tools_recorded']}
|
|
901
|
+
|
|
902
|
+
## Files Created
|
|
903
|
+
"""
|
|
904
|
+
for file_path in files_created[-20:]:
|
|
905
|
+
report += f"- {file_path}\n"
|
|
906
|
+
|
|
907
|
+
if len(files_created) > 20:
|
|
908
|
+
report += f"... and {len(files_created) - 20} more files\n"
|
|
909
|
+
|
|
910
|
+
report += """
|
|
911
|
+
## Architecture Features
|
|
912
|
+
✅ WRITE-FILE-BASED Memory Agent - Clear after each file generation
|
|
913
|
+
✅ After write_file: Clear history → Keep system prompt + initial plan + tool results
|
|
914
|
+
✅ Tool accumulation: read_code_mem, read_file, search_reference_code until next write_file
|
|
915
|
+
✅ Clean memory cycle: write_file → clear → accumulate → write_file → clear
|
|
916
|
+
✅ Essential tool recording with write_file detection
|
|
917
|
+
✅ Specialized agent separation for clean code organization
|
|
918
|
+
✅ MCP-compliant tool execution
|
|
919
|
+
✅ Production-grade code with comprehensive type hints
|
|
920
|
+
✅ Intelligent dependency analysis and file reading
|
|
921
|
+
✅ Automated read_file usage for implementation context
|
|
922
|
+
✅ Eliminates conversation clutter between file generations
|
|
923
|
+
✅ Focused memory for efficient next file generation
|
|
924
|
+
"""
|
|
925
|
+
return report
|
|
926
|
+
|
|
927
|
+
except Exception as e:
|
|
928
|
+
self.logger.error(f"Failed to generate final report: {e}")
|
|
929
|
+
return f"Failed to generate final report: {str(e)}"
|
|
930
|
+
|
|
931
|
+
# ==================== 8. Testing and Debugging (Testing Layer) ====================
|