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.
Files changed (44) hide show
  1. cli/__init__.py +18 -0
  2. cli/cli_app.py +296 -0
  3. cli/cli_interface.py +744 -0
  4. cli/cli_launcher.py +155 -0
  5. cli/main_cli.py +243 -0
  6. cli/workflows/__init__.py +11 -0
  7. cli/workflows/cli_workflow_adapter.py +336 -0
  8. deepcode.py +219 -0
  9. deepcode_hku-1.0.1.dist-info/METADATA +695 -0
  10. deepcode_hku-1.0.1.dist-info/RECORD +44 -0
  11. deepcode_hku-1.0.1.dist-info/WHEEL +5 -0
  12. deepcode_hku-1.0.1.dist-info/entry_points.txt +2 -0
  13. deepcode_hku-1.0.1.dist-info/licenses/LICENSE +21 -0
  14. deepcode_hku-1.0.1.dist-info/top_level.txt +6 -0
  15. tools/__init__.py +0 -0
  16. tools/code_implementation_server.py +1045 -0
  17. tools/code_indexer.py +1657 -0
  18. tools/code_reference_indexer.py +486 -0
  19. tools/command_executor.py +324 -0
  20. tools/git_command.py +356 -0
  21. tools/pdf_converter.py +640 -0
  22. tools/pdf_downloader.py +1370 -0
  23. tools/pdf_utils.py +52 -0
  24. ui/__init__.py +43 -0
  25. ui/app.py +13 -0
  26. ui/components.py +1450 -0
  27. ui/handlers.py +773 -0
  28. ui/layout.py +106 -0
  29. ui/streamlit_app.py +38 -0
  30. ui/styles.py +2116 -0
  31. utils/__init__.py +17 -0
  32. utils/cli_interface.py +459 -0
  33. utils/dialogue_logger.py +671 -0
  34. utils/file_processor.py +426 -0
  35. utils/simple_llm_logger.py +198 -0
  36. workflows/__init__.py +31 -0
  37. workflows/agent_orchestration_engine.py +1371 -0
  38. workflows/agents/__init__.py +13 -0
  39. workflows/agents/code_implementation_agent.py +1093 -0
  40. workflows/agents/memory_agent_concise.py +923 -0
  41. workflows/agents/memory_agent_concise_index.py +935 -0
  42. workflows/code_implementation_workflow.py +924 -0
  43. workflows/code_implementation_workflow_index.py +931 -0
  44. workflows/codebase_index_workflow.py +726 -0
@@ -0,0 +1,935 @@
1
+ """
2
+ Concise Memory Agent for Code Implementation Workflow
3
+ 简洁的代码实现工作流内存代理
4
+
5
+ This memory agent implements a focused approach:
6
+ 1. Before first file: Normal conversation flow
7
+ 2. After first file: Keep only system_prompt + initial_plan + current round tool results
8
+ 3. Clean slate for each new code file generation
9
+
10
+ Key Features:
11
+ - Preserves system prompt and initial plan always
12
+ - After first file generation, discards previous conversation history
13
+ - Keeps only current round tool results from essential tools:
14
+ * read_code_mem, read_file, write_file
15
+ * execute_python, execute_bash
16
+ * search_code, search_reference_code, get_file_structure
17
+ - Provides clean, focused input for next write_file operation
18
+ """
19
+
20
+ import json
21
+ import logging
22
+ import os
23
+ import time
24
+ from datetime import datetime
25
+ from typing import Dict, Any, List, Optional
26
+
27
+
28
+ class ConciseMemoryAgent:
29
+ """
30
+ Concise Memory Agent - Focused Information Retention
31
+
32
+ Core Philosophy:
33
+ - Preserve essential context (system prompt + initial plan)
34
+ - After first file generation, use clean slate approach
35
+ - Keep only current round tool results from all essential MCP tools
36
+ - Remove conversational clutter and previous tool calls
37
+
38
+ Essential Tools Tracked:
39
+ - File Operations: read_code_mem, read_file, write_file
40
+ - Code Analysis: search_code, search_reference_code, get_file_structure
41
+ - Execution: execute_python, execute_bash
42
+ """
43
+
44
+ def __init__(
45
+ self,
46
+ initial_plan_content: str,
47
+ logger: Optional[logging.Logger] = None,
48
+ target_directory: Optional[str] = None,
49
+ default_models: Optional[Dict[str, str]] = None,
50
+ ):
51
+ """
52
+ Initialize Concise Memory Agent
53
+
54
+ Args:
55
+ initial_plan_content: Content of initial_plan.txt
56
+ logger: Logger instance
57
+ target_directory: Target directory for saving summaries
58
+ default_models: Default models configuration from workflow
59
+ """
60
+ self.logger = logger or self._create_default_logger()
61
+ self.initial_plan = initial_plan_content
62
+
63
+ # Store default models configuration
64
+ self.default_models = default_models or {
65
+ "anthropic": "claude-sonnet-4-20250514",
66
+ "openai": "gpt-4o",
67
+ }
68
+
69
+ # Memory state tracking - new logic: trigger after each write_file
70
+ self.last_write_file_detected = (
71
+ False # Track if write_file was called in current iteration
72
+ )
73
+ self.should_clear_memory_next = False # Flag to clear memory in next round
74
+ self.current_round = 0
75
+
76
+ # Parse phase structure from initial plan
77
+ self.phase_structure = self._parse_phase_structure()
78
+
79
+ # Memory configuration
80
+ if target_directory:
81
+ self.save_path = target_directory
82
+ else:
83
+ self.save_path = "./deepcode_lab/papers/1/"
84
+
85
+ # Code summary file path
86
+ self.code_summary_path = os.path.join(
87
+ self.save_path, "implement_code_summary.md"
88
+ )
89
+
90
+ # Current round tool results storage
91
+ self.current_round_tool_results = []
92
+
93
+ # Track all implemented files
94
+ self.implemented_files = []
95
+
96
+ self.logger.info(
97
+ f"Concise Memory Agent initialized with target directory: {self.save_path}"
98
+ )
99
+ self.logger.info(f"Code summary will be saved to: {self.code_summary_path}")
100
+ # self.logger.info(f"🤖 Using models - Anthropic: {self.default_models['anthropic']}, OpenAI: {self.default_models['openai']}")
101
+ self.logger.info(
102
+ "📝 NEW LOGIC: Memory clearing triggered after each write_file call"
103
+ )
104
+
105
+ def _create_default_logger(self) -> logging.Logger:
106
+ """Create default logger"""
107
+ logger = logging.getLogger(f"{__name__}.ConciseMemoryAgent")
108
+ logger.setLevel(logging.INFO)
109
+ return logger
110
+
111
+ def _parse_phase_structure(self) -> Dict[str, List[str]]:
112
+ """Parse implementation phases from initial plan"""
113
+ try:
114
+ phases = {}
115
+ lines = self.initial_plan.split("\n")
116
+ current_phase = None
117
+
118
+ for line in lines:
119
+ if "Phase" in line and ":" in line:
120
+ # Extract phase name
121
+ phase_parts = line.split(":")
122
+ if len(phase_parts) >= 2:
123
+ current_phase = phase_parts[0].strip()
124
+ phases[current_phase] = []
125
+ elif current_phase and line.strip().startswith("-"):
126
+ # This is a file in the current phase
127
+ file_line = line.strip()[1:].strip()
128
+ if file_line.startswith("`") and file_line.endswith("`"):
129
+ file_name = file_line[1:-1]
130
+ phases[current_phase].append(file_name)
131
+ elif current_phase and not line.strip():
132
+ # Empty line might indicate end of phase
133
+ continue
134
+ elif current_phase and line.strip().startswith("###"):
135
+ # New section, end current phase
136
+ current_phase = None
137
+
138
+ return phases
139
+
140
+ except Exception as e:
141
+ self.logger.warning(f"Failed to parse phase structure: {e}")
142
+ return {}
143
+
144
+ def record_file_implementation(
145
+ self, file_path: str, implementation_content: str = ""
146
+ ):
147
+ """
148
+ Record a newly implemented file (simplified version)
149
+ NEW LOGIC: File implementation is tracked via write_file tool detection
150
+
151
+ Args:
152
+ file_path: Path of the implemented file
153
+ implementation_content: Content of the implemented file
154
+ """
155
+ # Add file to implemented files list if not already present
156
+ if file_path not in self.implemented_files:
157
+ self.implemented_files.append(file_path)
158
+
159
+ self.logger.info(f"📝 File implementation recorded: {file_path}")
160
+
161
+ async def create_code_implementation_summary(
162
+ self,
163
+ client,
164
+ client_type: str,
165
+ file_path: str,
166
+ implementation_content: str,
167
+ files_implemented: int,
168
+ ) -> str:
169
+ """
170
+ Create LLM-based code implementation summary after writing a file
171
+ Uses LLM to analyze and summarize the implemented code
172
+
173
+ Args:
174
+ client: LLM client instance
175
+ client_type: Type of LLM client ("anthropic" or "openai")
176
+ file_path: Path of the implemented file
177
+ implementation_content: Content of the implemented file
178
+ files_implemented: Number of files implemented so far
179
+
180
+ Returns:
181
+ LLM-generated formatted code implementation summary
182
+ """
183
+ try:
184
+ # Record the file implementation first
185
+ self.record_file_implementation(file_path, implementation_content)
186
+
187
+ # Create prompt for LLM summary
188
+ summary_prompt = self._create_code_summary_prompt(
189
+ file_path, implementation_content, files_implemented
190
+ )
191
+ summary_messages = [{"role": "user", "content": summary_prompt}]
192
+
193
+ # Get LLM-generated summary
194
+ llm_response = await self._call_llm_for_summary(
195
+ client, client_type, summary_messages
196
+ )
197
+ llm_summary = llm_response.get("content", "")
198
+
199
+ # Format the summary in the requested structure
200
+ formatted_summary = self._format_code_implementation_summary(
201
+ file_path, llm_summary, files_implemented
202
+ )
203
+
204
+ # Save to implement_code_summary.md (append mode)
205
+ await self._save_code_summary_to_file(formatted_summary, file_path)
206
+
207
+ self.logger.info(f"Created and saved code summary for: {file_path}")
208
+ return formatted_summary
209
+
210
+ except Exception as e:
211
+ self.logger.error(
212
+ f"Failed to create LLM-based code implementation summary: {e}"
213
+ )
214
+ # Fallback to simple summary
215
+ return self._create_fallback_code_summary(
216
+ file_path, implementation_content, files_implemented
217
+ )
218
+
219
+ def _create_code_summary_prompt(
220
+ self, file_path: str, implementation_content: str, files_implemented: int
221
+ ) -> str:
222
+ """
223
+ Create prompt for LLM to generate code implementation summary
224
+
225
+ Args:
226
+ file_path: Path of the implemented file
227
+ implementation_content: Content of the implemented file
228
+ files_implemented: Number of files implemented so far
229
+
230
+ Returns:
231
+ Prompt for LLM summarization
232
+ """
233
+ current_round = self.current_round
234
+
235
+ # Create formatted list of implemented files
236
+ implemented_files_list = (
237
+ "\n".join([f"- {file}" for file in self.implemented_files])
238
+ if self.implemented_files
239
+ else "- None yet"
240
+ )
241
+
242
+ prompt = f"""You are an expert code implementation summarizer. Analyze the implemented code file and create a structured summary.
243
+
244
+ **🚨 CRITICAL: The files listed below are ALREADY IMPLEMENTED - DO NOT suggest them in Next Steps! 🚨**
245
+
246
+ **All Previously Implemented Files:**
247
+ {implemented_files_list}
248
+
249
+ **Current Implementation Context:**
250
+ - **File Implemented**: {file_path}
251
+ - **Current Round**: {current_round}
252
+ - **Total Files Implemented**: {files_implemented}
253
+
254
+
255
+ **Initial Plan Reference:**
256
+ {self.initial_plan[:]}
257
+
258
+ **Implemented Code Content:**
259
+ ```
260
+ {implementation_content[:]}
261
+ ```
262
+
263
+ **Required Summary Format:**
264
+
265
+ 1. **Status Marker**: Mark the phase and round corresponding to this code file
266
+ Format: Phase {{phase_name}}, Round {{round_number}}
267
+
268
+ 2. **Implementation Progress**: List the code file completed in current round and core implementation ideas
269
+ Format: {{file_path}}: {{core implementation ideas}}
270
+
271
+ 3. **Dependencies**: According to the File Structure and initial plan, list functions that may be called by other files
272
+ Format: {{file_path}}: Function {{function_name}}: core ideas--{{ideas}}; Required parameters--{{params}}; Return parameters--{{returns}}
273
+ Required packages: {{packages}}
274
+
275
+ 4. **Next Steps**: List code files that will be implemented in the next round (EXCLUDE already implemented files)
276
+ Format: Code will be implemented: {{file_path}}; will stay on Phase {{phase}}/ will go to Phase {{next_phase}}
277
+ **WARNING: Do NOT suggest any file from the "All Previously Implemented Files" list above!**
278
+
279
+ **Instructions:**
280
+ - Be precise and concise
281
+ - Focus on function interfaces that other files will need
282
+ - Extract actual function signatures from the code
283
+ - **CRITICAL: For Next Steps, ONLY suggest files that are NOT in the "All Previously Implemented Files" list above**
284
+ - **NEVER suggest implementing a file that is already in the implemented files list**
285
+ - Predict next implementation steps based on the initial plan but exclude already completed files
286
+ - Use the exact format specified above
287
+
288
+ **Summary:**"""
289
+
290
+ return prompt
291
+
292
+ def _format_code_implementation_summary(
293
+ self, file_path: str, llm_summary: str, files_implemented: int
294
+ ) -> str:
295
+ """
296
+ Format the LLM-generated summary into the final structure
297
+
298
+ Args:
299
+ file_path: Path of the implemented file
300
+ llm_summary: LLM-generated summary content
301
+ files_implemented: Number of files implemented so far
302
+
303
+ Returns:
304
+ Formatted summary
305
+ """
306
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
307
+
308
+ # Create formatted list of implemented files
309
+ implemented_files_list = (
310
+ "\n".join([f"- {file}" for file in self.implemented_files])
311
+ if self.implemented_files
312
+ else "- None yet"
313
+ )
314
+
315
+ formatted_summary = f"""# Code Implementation Summary
316
+ **All Previously Implemented Files:**
317
+ {implemented_files_list}
318
+ **Generated**: {timestamp}
319
+ **File Implemented**: {file_path}
320
+ **Total Files Implemented**: {files_implemented}
321
+
322
+ {llm_summary}
323
+
324
+ ---
325
+ *Auto-generated by Memory Agent*
326
+ """
327
+ return formatted_summary
328
+
329
+ def _create_fallback_code_summary(
330
+ self, file_path: str, implementation_content: str, files_implemented: int
331
+ ) -> str:
332
+ """
333
+ Create fallback summary when LLM is unavailable
334
+
335
+ Args:
336
+ file_path: Path of the implemented file
337
+ implementation_content: Content of the implemented file
338
+ files_implemented: Number of files implemented so far
339
+
340
+ Returns:
341
+ Fallback summary
342
+ """
343
+ # Create formatted list of implemented files
344
+ implemented_files_list = (
345
+ "\n".join([f"- {file}" for file in self.implemented_files])
346
+ if self.implemented_files
347
+ else "- None yet"
348
+ )
349
+
350
+ summary = f"""# Code Implementation Summary
351
+ **All Previously Implemented Files:**
352
+ {implemented_files_list}
353
+ **Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
354
+ **File Implemented**: {file_path}
355
+ **Total Files Implemented**: {files_implemented}
356
+ **Summary failed to generate.**
357
+
358
+ ---
359
+ *Auto-generated by Concise Memory Agent (Fallback Mode)*
360
+ """
361
+ return summary
362
+
363
+ async def _save_code_summary_to_file(self, new_summary: str, file_path: str):
364
+ """
365
+ Append code implementation summary to implement_code_summary.md
366
+ Accumulates all implementations with clear separators
367
+
368
+ Args:
369
+ new_summary: New summary content to append
370
+ file_path: Path of the file for which the summary was generated
371
+ """
372
+ try:
373
+ # Create directory if it doesn't exist
374
+ os.makedirs(os.path.dirname(self.code_summary_path), exist_ok=True)
375
+
376
+ # Check if file exists to determine if we need header
377
+ file_exists = os.path.exists(self.code_summary_path)
378
+
379
+ # Open in append mode to accumulate all implementations
380
+ with open(self.code_summary_path, "a", encoding="utf-8") as f:
381
+ if not file_exists:
382
+ # Write header for new file
383
+ f.write("# Code Implementation Progress Summary\n")
384
+ f.write("*Accumulated implementation progress for all files*\n\n")
385
+
386
+ # Add clear separator between implementations
387
+ f.write("\n" + "=" * 80 + "\n")
388
+ f.write(
389
+ f"## IMPLEMENTATION File {file_path}; ROUND {self.current_round} \n"
390
+ )
391
+ f.write("=" * 80 + "\n\n")
392
+
393
+ # Write the new summary
394
+ f.write(new_summary)
395
+ f.write("\n\n")
396
+
397
+ self.logger.info(
398
+ f"Appended LLM-based code implementation summary to: {self.code_summary_path}"
399
+ )
400
+
401
+ except Exception as e:
402
+ self.logger.error(f"Failed to save code implementation summary: {e}")
403
+
404
+ async def _call_llm_for_summary(
405
+ self, client, client_type: str, summary_messages: List[Dict]
406
+ ) -> Dict[str, Any]:
407
+ """
408
+ Call LLM for code implementation summary generation ONLY
409
+ 调用LLM生成代码实现总结(仅用于代码总结)
410
+
411
+ This method is used only for creating code implementation summaries,
412
+ NOT for conversation summarization which has been removed.
413
+ """
414
+ if client_type == "anthropic":
415
+ response = await client.messages.create(
416
+ model=self.default_models["anthropic"],
417
+ system="You are an expert code implementation summarizer. Create structured summaries of implemented code files that preserve essential information about functions, dependencies, and implementation approaches.",
418
+ messages=summary_messages,
419
+ max_tokens=5000,
420
+ temperature=0.2,
421
+ )
422
+
423
+ content = ""
424
+ for block in response.content:
425
+ if block.type == "text":
426
+ content += block.text
427
+
428
+ return {"content": content}
429
+
430
+ elif client_type == "openai":
431
+ openai_messages = [
432
+ {
433
+ "role": "system",
434
+ "content": "You are an expert code implementation summarizer. Create structured summaries of implemented code files that preserve essential information about functions, dependencies, and implementation approaches.",
435
+ }
436
+ ]
437
+ openai_messages.extend(summary_messages)
438
+
439
+ response = await client.chat.completions.create(
440
+ model=self.default_models["openai"],
441
+ messages=openai_messages,
442
+ max_tokens=5000,
443
+ temperature=0.2,
444
+ )
445
+
446
+ return {"content": response.choices[0].message.content or ""}
447
+
448
+ else:
449
+ raise ValueError(f"Unsupported client type: {client_type}")
450
+
451
+ def start_new_round(self, iteration: Optional[int] = None):
452
+ """Start a new dialogue round and reset tool results
453
+
454
+ Args:
455
+ iteration: Optional iteration number from workflow to sync with current_round
456
+ """
457
+ if iteration is not None:
458
+ # Sync with workflow iteration
459
+ self.current_round = iteration
460
+ # self.logger.info(f"🔄 Synced round with workflow iteration {iteration}")
461
+ else:
462
+ # Default behavior: increment round counter
463
+ self.current_round += 1
464
+ self.logger.info(f"🔄 Started new round {self.current_round}")
465
+
466
+ self.current_round_tool_results = [] # Clear previous round results
467
+ # Note: Don't reset last_write_file_detected and should_clear_memory_next here
468
+ # These flags persist across rounds until memory optimization is applied
469
+ # self.logger.info(f"🔄 Round {self.current_round} - Tool results cleared, memory flags preserved")
470
+
471
+ def record_tool_result(
472
+ self, tool_name: str, tool_input: Dict[str, Any], tool_result: Any
473
+ ):
474
+ """
475
+ Record tool result for current round and detect write_file calls
476
+
477
+ Args:
478
+ tool_name: Name of the tool called
479
+ tool_input: Input parameters for the tool
480
+ tool_result: Result returned by the tool
481
+ """
482
+ # Detect write_file calls to trigger memory clearing
483
+ if tool_name == "write_file":
484
+ self.last_write_file_detected = True
485
+ self.should_clear_memory_next = True
486
+ # self.logger.info(f"🔄 WRITE_FILE DETECTED: {file_path} - Memory will be cleared in next round")
487
+
488
+ # Only record specific tools that provide essential information
489
+ essential_tools = [
490
+ "read_code_mem", # Read code summary from implement_code_summary.md
491
+ "read_file", # Read file contents
492
+ "write_file", # Write file contents (important for tracking implementations)
493
+ "execute_python", # Execute Python code (for testing/validation)
494
+ "execute_bash", # Execute bash commands (for build/execution)
495
+ "search_code", # Search code patterns
496
+ "search_reference_code", # Search reference code (if available)
497
+ "get_file_structure", # Get file structure (for understanding project layout)
498
+ ]
499
+
500
+ if tool_name in essential_tools:
501
+ tool_record = {
502
+ "tool_name": tool_name,
503
+ "tool_input": tool_input,
504
+ "tool_result": tool_result,
505
+ "timestamp": time.time(),
506
+ }
507
+ self.current_round_tool_results.append(tool_record)
508
+ # self.logger.info(f"📊 Essential tool result recorded: {tool_name} ({len(self.current_round_tool_results)} total)")
509
+
510
+ def should_use_concise_mode(self) -> bool:
511
+ """
512
+ Check if concise memory mode should be used
513
+
514
+ Returns:
515
+ True if first file has been generated and concise mode should be active
516
+ """
517
+ return self.last_write_file_detected
518
+
519
+ def create_concise_messages(
520
+ self, system_prompt: str, messages: List[Dict[str, Any]], files_implemented: int
521
+ ) -> List[Dict[str, Any]]:
522
+ """
523
+ Create concise message list for LLM input
524
+ NEW LOGIC: Always clear after write_file, keep system_prompt + initial_plan + current round tools
525
+
526
+ Args:
527
+ system_prompt: Current system prompt
528
+ messages: Original message list
529
+ files_implemented: Number of files implemented so far
530
+
531
+ Returns:
532
+ Concise message list containing only essential information
533
+ """
534
+ if not self.last_write_file_detected:
535
+ # Before any write_file, use normal flow
536
+ self.logger.info(
537
+ "🔄 Using normal conversation flow (before any write_file)"
538
+ )
539
+ return messages
540
+
541
+ # After write_file detection, use concise approach with clean slate
542
+ self.logger.info(
543
+ f"🎯 Using CONCISE memory mode - Clear slate after write_file, Round {self.current_round}"
544
+ )
545
+
546
+ concise_messages = []
547
+
548
+ # 1. Add initial plan message (always preserved)
549
+ initial_plan_message = {
550
+ "role": "user",
551
+ "content": f"""**Task: Implement code based on the following reproduction plan**
552
+
553
+ **Code Reproduction Plan:**
554
+ {self.initial_plan}
555
+
556
+ **Working Directory:** Current workspace
557
+
558
+ **Current Status:** {files_implemented} files implemented
559
+
560
+ **Objective:** Continue implementation by analyzing dependencies and implementing the next required file according to the plan's priority order.""",
561
+ }
562
+ concise_messages.append(initial_plan_message)
563
+
564
+ # 2. Add Knowledge Base
565
+ knowledge_base_message = {
566
+ "role": "user",
567
+ "content": f"""**Below is the Knowledge Base of the LATEST implemented code file:**
568
+ {self._read_code_knowledge_base()}
569
+ """,
570
+ }
571
+ concise_messages.append(knowledge_base_message)
572
+
573
+ # 3. Add current tool results (essential information for next file generation)
574
+ if self.current_round_tool_results:
575
+ tool_results_content = self._format_tool_results()
576
+ tool_results_message = {
577
+ "role": "user",
578
+ "content": f"""**Current Tool Results:**
579
+ {tool_results_content}
580
+
581
+ **🚨 NEXT STEP: First determine if ALL files from the reproduction plan have been implemented:**
582
+
583
+ **If ALL files are implemented (reproduction plan complete):**
584
+ - Use `execute_python` or `execute_bash` to test the complete implementation
585
+ - If testing successful, respond with "**implementation complete**" to end the conversation
586
+ - Only use `read_code_mem` if debugging is needed during testing
587
+
588
+ **If MORE files need to be implemented:**
589
+ - #1. `read_code_mem` → Query summaries of relevant **already-implemented** files (agent should choose which implemented file paths to reference)(important!!!)
590
+ - #2. `search_code_references` → OPTIONALLY search reference patterns for inspiration (use for reference only, original paper specs take priority)
591
+ - #3. `write_file` → Create the complete code implementation based on original paper requirements
592
+ - #4. `execute_python` or `execute_bash` → Test the partial implementation if needed
593
+
594
+ **Remember:** Always check if all planned files are implemented before continuing with new file creation.""",
595
+ }
596
+ concise_messages.append(tool_results_message)
597
+ else:
598
+ # If no tool results yet, add guidance for next steps
599
+ guidance_message = {
600
+ "role": "user",
601
+ "content": f"""**Current Round:** {self.current_round}
602
+
603
+ **Development Cycle - START HERE:**
604
+
605
+ **FIRST: Check if ALL files from the reproduction plan are implemented**
606
+ - If YES: Use `execute_python` or `execute_bash` for testing, then respond "**implementation complete**"
607
+ - If NO: Continue with file implementation cycle below
608
+
609
+ **For NEW file implementation:**
610
+ 1. **You can call read_code_mem(*already_implemented_file_path*)** to understand existing implementations and dependencies - agent should choose relevant ALREADY IMPLEMENTED file paths for reference, NOT the new file you want to create
611
+ 2. **Optionally use search_code_references** for reference patterns (OPTIONAL - for inspiration only, original paper specs take priority)
612
+ 3. Write_file can be used to implement the new component based on original paper requirements
613
+ 4. Finally: Use execute_python or execute_bash for testing (if needed)
614
+
615
+ **For TESTING/COMPLETION phase (when all files implemented):**
616
+ 1. **➡️ FIRST: Use execute_python or execute_bash** to test the complete implementation
617
+ 2. **If successful: Respond with "implementation complete"** to end the conversation
618
+ 3. Only use read_code_mem if debugging is needed during testing""",
619
+ }
620
+ concise_messages.append(guidance_message)
621
+ # **Available Essential Tools:** read_code_mem, write_file, execute_python, execute_bash
622
+ # **Remember:** Start with read_code_mem when implementing NEW files to understand existing code. When all files are implemented, focus on testing and completion. Implement according to the original paper's specifications - any reference code is for inspiration only."""
623
+ # self.logger.info(f"✅ Concise messages created: {len(concise_messages)} messages (original: {len(messages)})")
624
+ return concise_messages
625
+
626
+ def _read_code_knowledge_base(self) -> Optional[str]:
627
+ """
628
+ Read the implement_code_summary.md file as code knowledge base
629
+ Returns only the final/latest implementation entry, not all historical entries
630
+
631
+ Returns:
632
+ Content of the latest implementation entry if it exists, None otherwise
633
+ """
634
+ try:
635
+ if os.path.exists(self.code_summary_path):
636
+ with open(self.code_summary_path, "r", encoding="utf-8") as f:
637
+ content = f.read().strip()
638
+
639
+ if content:
640
+ # Extract only the final/latest implementation entry
641
+ return self._extract_latest_implementation_entry(content)
642
+ else:
643
+ return None
644
+ else:
645
+ return None
646
+
647
+ except Exception as e:
648
+ self.logger.error(f"Failed to read code knowledge base: {e}")
649
+ return None
650
+
651
+ def _extract_latest_implementation_entry(self, content: str) -> Optional[str]:
652
+ """
653
+ Extract the latest/final implementation entry from the implement_code_summary.md content
654
+ Uses a simpler approach to find the last implementation section
655
+
656
+ Args:
657
+ content: Full content of implement_code_summary.md
658
+
659
+ Returns:
660
+ Latest implementation entry content, or None if not found
661
+ """
662
+ try:
663
+ import re
664
+
665
+ # Pattern to match the start of implementation sections
666
+ section_pattern = (
667
+ r"={80}\s*\n## IMPLEMENTATION File .+?; ROUND \d+\s*\n={80}"
668
+ )
669
+
670
+ # Find all implementation section starts
671
+ matches = list(re.finditer(section_pattern, content))
672
+
673
+ if not matches:
674
+ # No implementation sections found
675
+ lines = content.split("\n")
676
+ fallback_content = (
677
+ "\n".join(lines[:10]) + "\n... (truncated for brevity)"
678
+ if len(lines) > 10
679
+ else content
680
+ )
681
+ self.logger.info(
682
+ "📖 No implementation sections found, using fallback content"
683
+ )
684
+ return fallback_content
685
+
686
+ # Get the start position of the last implementation section
687
+ last_match = matches[-1]
688
+ start_pos = last_match.start()
689
+
690
+ # Take everything from the last section start to the end of content
691
+ latest_entry = content[start_pos:].strip()
692
+
693
+ # self.logger.info(f"📖 Extracted latest implementation entry from knowledge base")
694
+ # print(f"DEBUG: Extracted content length: {len(latest_entry)}")
695
+ # print(f"DEBUG: First 200 chars: {latest_entry[:]}")
696
+
697
+ return latest_entry
698
+
699
+ except Exception as e:
700
+ self.logger.error(f"Failed to extract latest implementation entry: {e}")
701
+ # Return last 1000 characters as fallback
702
+ return content[-500:] if len(content) > 500 else content
703
+
704
+ def _format_tool_results(self) -> str:
705
+ """
706
+ Format current round tool results for LLM input
707
+
708
+ Returns:
709
+ Formatted string of tool results
710
+ """
711
+ if not self.current_round_tool_results:
712
+ return "No tool results in current round."
713
+
714
+ formatted_results = []
715
+
716
+ for result in self.current_round_tool_results:
717
+ tool_name = result["tool_name"]
718
+ tool_input = result["tool_input"]
719
+ tool_result = result["tool_result"]
720
+
721
+ # Format based on tool type
722
+ if tool_name == "read_code_mem":
723
+ file_path = tool_input.get("file_path", "unknown")
724
+ formatted_results.append(f"""
725
+ **read_code_mem Result for {file_path}:**
726
+ {self._format_tool_result_content(tool_result)}
727
+ """)
728
+ elif tool_name == "read_file":
729
+ file_path = tool_input.get("file_path", "unknown")
730
+ formatted_results.append(f"""
731
+ **read_file Result for {file_path}:**
732
+ {self._format_tool_result_content(tool_result)}
733
+ """)
734
+ elif tool_name == "write_file":
735
+ file_path = tool_input.get("file_path", "unknown")
736
+ formatted_results.append(f"""
737
+ **write_file Result for {file_path}:**
738
+ {self._format_tool_result_content(tool_result)}
739
+ """)
740
+ elif tool_name == "execute_python":
741
+ code_snippet = (
742
+ tool_input.get("code", "")[:50] + "..."
743
+ if len(tool_input.get("code", "")) > 50
744
+ else tool_input.get("code", "")
745
+ )
746
+ formatted_results.append(f"""
747
+ **execute_python Result (code: {code_snippet}):**
748
+ {self._format_tool_result_content(tool_result)}
749
+ """)
750
+ elif tool_name == "execute_bash":
751
+ command = tool_input.get("command", "unknown")
752
+ formatted_results.append(f"""
753
+ **execute_bash Result (command: {command}):**
754
+ {self._format_tool_result_content(tool_result)}
755
+ """)
756
+ elif tool_name == "search_code":
757
+ pattern = tool_input.get("pattern", "unknown")
758
+ file_pattern = tool_input.get("file_pattern", "")
759
+ formatted_results.append(f"""
760
+ **search_code Result (pattern: {pattern}, files: {file_pattern}):**
761
+ {self._format_tool_result_content(tool_result)}
762
+ """)
763
+ elif tool_name == "search_reference_code":
764
+ target_file = tool_input.get("target_file", "unknown")
765
+ keywords = tool_input.get("keywords", "")
766
+ formatted_results.append(f"""
767
+ **search_reference_code Result for {target_file} (keywords: {keywords}):**
768
+ {self._format_tool_result_content(tool_result)}
769
+ """)
770
+ elif tool_name == "get_file_structure":
771
+ directory = tool_input.get(
772
+ "directory_path", tool_input.get("path", "current")
773
+ )
774
+ formatted_results.append(f"""
775
+ **get_file_structure Result for {directory}:**
776
+ {self._format_tool_result_content(tool_result)}
777
+ """)
778
+
779
+ return "\n".join(formatted_results)
780
+
781
+ def _format_tool_result_content(self, tool_result: Any) -> str:
782
+ """
783
+ Format tool result content for display
784
+
785
+ Args:
786
+ tool_result: Tool result to format
787
+
788
+ Returns:
789
+ Formatted string representation
790
+ """
791
+ if isinstance(tool_result, str):
792
+ # Try to parse as JSON for better formatting
793
+ try:
794
+ result_data = json.loads(tool_result)
795
+ if isinstance(result_data, dict):
796
+ # Format key information
797
+ if result_data.get("status") == "summary_found":
798
+ return (
799
+ f"Summary found:\n{result_data.get('summary_content', '')}"
800
+ )
801
+ elif result_data.get("status") == "no_summary":
802
+ return "No summary available"
803
+ else:
804
+ return json.dumps(result_data, indent=2)
805
+ else:
806
+ return str(result_data)
807
+ except json.JSONDecodeError:
808
+ return tool_result
809
+ else:
810
+ return str(tool_result)
811
+
812
+ def get_memory_statistics(self, files_implemented: int = 0) -> Dict[str, Any]:
813
+ """Get memory agent statistics"""
814
+ return {
815
+ "last_write_file_detected": self.last_write_file_detected,
816
+ "should_clear_memory_next": self.should_clear_memory_next,
817
+ "current_round": self.current_round,
818
+ "concise_mode_active": self.should_use_concise_mode(),
819
+ "current_round_tool_results": len(self.current_round_tool_results),
820
+ "essential_tools_recorded": [
821
+ r["tool_name"] for r in self.current_round_tool_results
822
+ ],
823
+ "implemented_files_tracked": files_implemented,
824
+ "implemented_files_list": self.implemented_files.copy(),
825
+ "phases_parsed": len(self.phase_structure),
826
+ }
827
+
828
+ def get_implemented_files(self) -> List[str]:
829
+ """Get list of all implemented files"""
830
+ return self.implemented_files.copy()
831
+
832
+ def should_trigger_memory_optimization(
833
+ self, messages: List[Dict[str, Any]], files_implemented: int = 0
834
+ ) -> bool:
835
+ """
836
+ Check if memory optimization should be triggered
837
+ NEW LOGIC: Trigger after write_file has been detected
838
+
839
+ Args:
840
+ messages: Current message list
841
+ files_implemented: Number of files implemented so far
842
+
843
+ Returns:
844
+ True if concise mode should be applied
845
+ """
846
+ # Trigger if we detected write_file and should clear memory
847
+ if self.should_clear_memory_next:
848
+ # self.logger.info(f"🎯 Triggering CONCISE memory optimization (write_file detected, files: {files_implemented})")
849
+ return True
850
+
851
+ # No optimization before any write_file
852
+ return False
853
+
854
+ def apply_memory_optimization(
855
+ self, system_prompt: str, messages: List[Dict[str, Any]], files_implemented: int
856
+ ) -> List[Dict[str, Any]]:
857
+ """
858
+ Apply memory optimization using concise approach
859
+ NEW LOGIC: Clear all history after write_file, keep only system_prompt + initial_plan + current tools
860
+
861
+ Args:
862
+ system_prompt: Current system prompt
863
+ messages: Original message list
864
+ files_implemented: Number of files implemented so far
865
+
866
+ Returns:
867
+ Optimized message list
868
+ """
869
+ if not self.should_clear_memory_next:
870
+ # Before any write_file, return original messages
871
+ return messages
872
+
873
+ # Apply concise memory optimization after write_file detection
874
+ # self.logger.info(f"🧹 CLEARING MEMORY after write_file - creating clean slate")
875
+ optimized_messages = self.create_concise_messages(
876
+ system_prompt, messages, files_implemented
877
+ )
878
+
879
+ # Clear the flag after applying optimization
880
+ self.should_clear_memory_next = False
881
+
882
+ compression_ratio = (
883
+ ((len(messages) - len(optimized_messages)) / len(messages) * 100)
884
+ if messages
885
+ else 0
886
+ )
887
+ self.logger.info(
888
+ f"🎯 CONCISE optimization applied: {len(messages)} → {len(optimized_messages)} messages ({compression_ratio:.1f}% compression)"
889
+ )
890
+
891
+ return optimized_messages
892
+
893
+ def clear_current_round_tool_results(self):
894
+ """Clear current round tool results (called when starting new round)"""
895
+ self.current_round_tool_results = []
896
+ self.logger.info("🧹 Current round tool results cleared")
897
+
898
+ def debug_concise_state(self, files_implemented: int = 0):
899
+ """Debug method to show current concise memory state"""
900
+ stats = self.get_memory_statistics(files_implemented)
901
+
902
+ print("=" * 60)
903
+ print("🎯 CONCISE MEMORY AGENT STATE (Write-File-Based)")
904
+ print("=" * 60)
905
+ print(f"Last write_file detected: {stats['last_write_file_detected']}")
906
+ print(f"Should clear memory next: {stats['should_clear_memory_next']}")
907
+ print(f"Files implemented: {stats['implemented_files_tracked']}")
908
+ print(f"Current round: {stats['current_round']}")
909
+ print(f"Concise mode active: {stats['concise_mode_active']}")
910
+ print(f"Current round tool results: {stats['current_round_tool_results']}")
911
+ print(f"Essential tools recorded: {stats['essential_tools_recorded']}")
912
+ print(f"Implemented files tracked: {len(self.implemented_files)}")
913
+ print(f"Implemented files list: {self.implemented_files}")
914
+ print(f"Code summary file exists: {os.path.exists(self.code_summary_path)}")
915
+ print("")
916
+ print(
917
+ "📊 NEW LOGIC: write_file → clear memory → accumulate tools → next write_file"
918
+ )
919
+ print("📊 Essential Tools Tracked:")
920
+ essential_tools = [
921
+ "read_code_mem",
922
+ "read_file",
923
+ "write_file",
924
+ "execute_python",
925
+ "execute_bash",
926
+ "search_code",
927
+ "search_reference_code",
928
+ "get_file_structure",
929
+ ]
930
+ for tool in essential_tools:
931
+ tool_count = sum(
932
+ 1 for r in self.current_round_tool_results if r["tool_name"] == tool
933
+ )
934
+ print(f" - {tool}: {tool_count} calls")
935
+ print("=" * 60)