qwen-sci 0.2.2__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 (549) hide show
  1. qwen_sci-0.2.2.dist-info/METADATA +492 -0
  2. qwen_sci-0.2.2.dist-info/RECORD +549 -0
  3. qwen_sci-0.2.2.dist-info/WHEEL +4 -0
  4. qwen_sci-0.2.2.dist-info/entry_points.txt +6 -0
  5. qwen_sci-0.2.2.dist-info/licenses/LICENSE +21 -0
  6. src/1.txt +1 -0
  7. src/__init__.py +1 -0
  8. src/__main__.py +5 -0
  9. src/agents/__init__.py +1 -0
  10. src/agents/experiment_design_agent/__init__.py +268 -0
  11. src/agents/experiment_design_agent/artifacts.py +747 -0
  12. src/agents/experiment_design_agent/cache.py +319 -0
  13. src/agents/experiment_design_agent/completeness.py +130 -0
  14. src/agents/experiment_design_agent/contracts.py +771 -0
  15. src/agents/experiment_design_agent/counterexample_analyzer.py +187 -0
  16. src/agents/experiment_design_agent/design_evidence_paper_screener.py +470 -0
  17. src/agents/experiment_design_agent/discipline_catalog.py +178 -0
  18. src/agents/experiment_design_agent/evidence_cards.py +338 -0
  19. src/agents/experiment_design_agent/evidence_planner.py +452 -0
  20. src/agents/experiment_design_agent/formal_reasoning_planner.py +884 -0
  21. src/agents/experiment_design_agent/fulltext_acquisition.py +863 -0
  22. src/agents/experiment_design_agent/idea_adapter.py +100 -0
  23. src/agents/experiment_design_agent/idea_intake.py +87 -0
  24. src/agents/experiment_design_agent/llm_json.py +328 -0
  25. src/agents/experiment_design_agent/orchestrator.py +1220 -0
  26. src/agents/experiment_design_agent/reasoning_context.py +183 -0
  27. src/agents/experiment_design_agent/reasoning_validation.py +546 -0
  28. src/agents/experiment_design_agent/run.py +481 -0
  29. src/agents/experiment_design_agent/run_logging.py +292 -0
  30. src/agents/experiment_design_agent/scope_gate.py +198 -0
  31. src/agents/experiment_design_agent/study_type_composer.py +1360 -0
  32. src/agents/experiment_design_agent/survey_evidence.py +1381 -0
  33. src/agents/experiment_design_agent/template_router.py +195 -0
  34. src/agents/experiment_design_agent/variable_claim_extractor.py +93 -0
  35. src/agents/idea_agent/__init__.py +1 -0
  36. src/agents/idea_agent/agent/__init__.py +6 -0
  37. src/agents/idea_agent/agent/artifacts.py +308 -0
  38. src/agents/idea_agent/agent/base.py +122 -0
  39. src/agents/idea_agent/agent/ligagent.py +488 -0
  40. src/agents/idea_agent/agent/mcts.py +2060 -0
  41. src/agents/idea_agent/agent/prompts/__init__.py +48 -0
  42. src/agents/idea_agent/agent/prompts/advanced_analysis.py +235 -0
  43. src/agents/idea_agent/agent/prompts/algorithm_alignment.py +55 -0
  44. src/agents/idea_agent/agent/prompts/algorithm_structuring.py +48 -0
  45. src/agents/idea_agent/agent/prompts/component_extraction.py +62 -0
  46. src/agents/idea_agent/agent/prompts/component_novelty_evaluation.py +54 -0
  47. src/agents/idea_agent/agent/prompts/experiment_findings_extraction.py +39 -0
  48. src/agents/idea_agent/agent/prompts/idea_fusion.py +295 -0
  49. src/agents/idea_agent/agent/prompts/idea_introduction.py +30 -0
  50. src/agents/idea_agent/agent/prompts/idea_result_alignment.py +39 -0
  51. src/agents/idea_agent/agent/prompts/input_interpreter.py +52 -0
  52. src/agents/idea_agent/agent/prompts/keynote_ops.py +78 -0
  53. src/agents/idea_agent/agent/prompts/mcts_evaluation.py +235 -0
  54. src/agents/idea_agent/agent/prompts/mcts_generation.py +140 -0
  55. src/agents/idea_agent/agent/prompts/mechanism_commit_query.py +99 -0
  56. src/agents/idea_agent/agent/prompts/memory_selection.py +0 -0
  57. src/agents/idea_agent/agent/prompts/prompt_modes.py +16 -0
  58. src/agents/idea_agent/agent/prompts/rag_query.py +34 -0
  59. src/agents/idea_agent/agent/prompts/re_analysis_replan.py +48 -0
  60. src/agents/idea_agent/agent/prompts/reference_grounding.py +34 -0
  61. src/agents/idea_agent/agent/prompts/root_domain_classification.py +32 -0
  62. src/agents/idea_agent/agent/prompts/scientific_debate.py +88 -0
  63. src/agents/idea_agent/agent/prompts/scientific_materialization.py +45 -0
  64. src/agents/idea_agent/agent/prompts/skill_instantiation.py +193 -0
  65. src/agents/idea_agent/agent/prompts/theory_transfer_query.py +93 -0
  66. src/agents/idea_agent/agent/prompts/topic_background.py +15 -0
  67. src/agents/idea_agent/agent/skills/DEFAULT_SKILL_TEMPLATES.json +263 -0
  68. src/agents/idea_agent/agent/skills/edit_operator_skills/alternative-path-contrast/SKILL.md +40 -0
  69. src/agents/idea_agent/agent/skills/edit_operator_skills/alternative-path-contrast/references/contrast_patterns.md +22 -0
  70. src/agents/idea_agent/agent/skills/edit_operator_skills/feedback-closed-loop/SKILL.md +40 -0
  71. src/agents/idea_agent/agent/skills/edit_operator_skills/feedback-closed-loop/references/mechanism_cards.md +24 -0
  72. src/agents/idea_agent/agent/skills/edit_operator_skills/hierarchical-decomposition/SKILL.md +39 -0
  73. src/agents/idea_agent/agent/skills/edit_operator_skills/hierarchical-decomposition/references/hierarchy_patterns.md +22 -0
  74. src/agents/idea_agent/agent/skills/edit_operator_skills/mechanism-commit-innovation/SKILL.md +39 -0
  75. src/agents/idea_agent/agent/skills/edit_operator_skills/mechanism-commit-innovation/references/mechanism_commit_patterns.md +24 -0
  76. src/agents/idea_agent/agent/skills/edit_operator_skills/multi-scale-coordinator/SKILL.md +39 -0
  77. src/agents/idea_agent/agent/skills/edit_operator_skills/multi-scale-coordinator/references/coordination_patterns.md +22 -0
  78. src/agents/idea_agent/agent/skills/edit_operator_skills/speculative-execution-with-repair/SKILL.md +41 -0
  79. src/agents/idea_agent/agent/skills/edit_operator_skills/speculative-execution-with-repair/references/speculation_patterns.md +22 -0
  80. src/agents/idea_agent/agent/skills/edit_operator_skills/surgical-modularity/SKILL.md +39 -0
  81. src/agents/idea_agent/agent/skills/edit_operator_skills/surgical-modularity/references/modularity_patterns.md +22 -0
  82. src/agents/idea_agent/agent/skills/edit_operator_skills/theory-transfer-injection/SKILL.md +40 -0
  83. src/agents/idea_agent/agent/skills/edit_operator_skills/theory-transfer-injection/references/transfer_patterns.md +24 -0
  84. src/agents/idea_agent/example/idea_result.json +900 -0
  85. src/agents/idea_agent/run.py +276 -0
  86. src/agents/idea_agent/tests/test_advanced_analysis_contract.py +90 -0
  87. src/agents/idea_agent/tests/test_gap_hypothesis_bridge.py +166 -0
  88. src/agents/idea_agent/tests/test_idea_debate.py +480 -0
  89. src/agents/idea_agent/tests/test_idea_result_v5.py +99 -0
  90. src/agents/idea_agent/tests/test_idea_synthesis.py +92 -0
  91. src/agents/idea_agent/tests/test_mature_idea_sources.py +27 -0
  92. src/agents/idea_agent/tests/test_multimodal_idea_integration.py +386 -0
  93. src/agents/idea_agent/tests/test_profile_materialization_and_scoring.py +219 -0
  94. src/agents/idea_agent/tests/test_profile_native_skill_rendering.py +122 -0
  95. src/agents/idea_agent/tests/test_result_persistence.py +183 -0
  96. src/agents/idea_agent/tests/test_scientific_intervention_profiles.py +241 -0
  97. src/agents/idea_agent/tests/test_scientific_rubric_and_prompts.py +559 -0
  98. src/agents/idea_agent/tests/test_strict_json_object_responses.py +48 -0
  99. src/agents/idea_agent/utils/core/__init__.py +1 -0
  100. src/agents/idea_agent/utils/core/ablation_inputs.py +60 -0
  101. src/agents/idea_agent/utils/core/chat_errors.py +95 -0
  102. src/agents/idea_agent/utils/core/chat_router.py +130 -0
  103. src/agents/idea_agent/utils/core/chat_transport.py +188 -0
  104. src/agents/idea_agent/utils/core/config_loader.py +166 -0
  105. src/agents/idea_agent/utils/core/json_utils.py +28 -0
  106. src/agents/idea_agent/utils/core/logger.py +287 -0
  107. src/agents/idea_agent/utils/core/progress.py +58 -0
  108. src/agents/idea_agent/utils/core/response_parsing.py +57 -0
  109. src/agents/idea_agent/utils/core/run_inputs.py +198 -0
  110. src/agents/idea_agent/utils/mcts/__init__.py +1 -0
  111. src/agents/idea_agent/utils/mcts/component_novelty.py +342 -0
  112. src/agents/idea_agent/utils/mcts/defect_registry.py +302 -0
  113. src/agents/idea_agent/utils/mcts/idea_routes.py +61 -0
  114. src/agents/idea_agent/utils/mcts/idea_taste_presets.py +312 -0
  115. src/agents/idea_agent/utils/mcts/mcts_helpers.py +738 -0
  116. src/agents/idea_agent/utils/mcts/mcts_runtime.py +3000 -0
  117. src/agents/idea_agent/utils/mcts/scientific_intervention_ontology.py +1561 -0
  118. src/agents/idea_agent/utils/mcts/scientific_rubric.py +211 -0
  119. src/agents/idea_agent/utils/mcts/skill_parsing.py +92 -0
  120. src/agents/idea_agent/utils/papers/__init__.py +1 -0
  121. src/agents/idea_agent/utils/papers/paper_graph_vector_store.py +499 -0
  122. src/agents/idea_agent/utils/papers/paper_repository.py +372 -0
  123. src/agents/idea_agent/utils/prompting/__init__.py +1 -0
  124. src/agents/idea_agent/utils/prompting/prompt_views.py +637 -0
  125. src/agents/idea_agent/utils/workflow/__init__.py +1 -0
  126. src/agents/idea_agent/utils/workflow/advanced_analysis_contract.py +244 -0
  127. src/agents/idea_agent/utils/workflow/gap_hypothesis_bridge.py +461 -0
  128. src/agents/idea_agent/utils/workflow/idea_contract.py +426 -0
  129. src/agents/idea_agent/utils/workflow/idea_debate.py +1238 -0
  130. src/agents/idea_agent/utils/workflow/idea_diversity.py +163 -0
  131. src/agents/idea_agent/utils/workflow/idea_fusion.py +889 -0
  132. src/agents/idea_agent/utils/workflow/idea_helpers.py +478 -0
  133. src/agents/idea_agent/utils/workflow/idea_portfolio.py +422 -0
  134. src/agents/idea_agent/utils/workflow/idea_synthesis.py +482 -0
  135. src/agents/idea_agent/utils/workflow/ligagent_flow.py +581 -0
  136. src/agents/idea_agent/utils/workflow/ligagent_handlers.py +1836 -0
  137. src/agents/idea_agent/utils/workflow/ligagent_helpers.py +1331 -0
  138. src/agents/idea_agent/utils/workflow/ligagent_utils.py +322 -0
  139. src/agents/idea_agent/utils/workflow/mature_idea_sources.py +648 -0
  140. src/agents/idea_agent/utils/workflow/multimodal_data_anchoring.py +578 -0
  141. src/agents/idea_agent/utils/workflow/result_persistence.py +774 -0
  142. src/agents/idea_agent/utils/workflow/stage_contract.py +76 -0
  143. src/agents/idea_agent/utils/workflow/workflow_runtime.py +288 -0
  144. src/agents/research_plan_author/__init__.py +50 -0
  145. src/agents/research_plan_author/artifacts.py +270 -0
  146. src/agents/research_plan_author/authoring_blueprint.py +1246 -0
  147. src/agents/research_plan_author/bibtex_renderer.py +252 -0
  148. src/agents/research_plan_author/contract_repair.py +131 -0
  149. src/agents/research_plan_author/contracts.py +745 -0
  150. src/agents/research_plan_author/cross_section_editor.py +232 -0
  151. src/agents/research_plan_author/document_quality.py +987 -0
  152. src/agents/research_plan_author/idea_evolution.py +223 -0
  153. src/agents/research_plan_author/input_loader.py +76 -0
  154. src/agents/research_plan_author/latex_compiler.py +260 -0
  155. src/agents/research_plan_author/latex_safety.py +159 -0
  156. src/agents/research_plan_author/llm_json.py +74 -0
  157. src/agents/research_plan_author/markdown_renderer.py +275 -0
  158. src/agents/research_plan_author/pdf_validator.py +149 -0
  159. src/agents/research_plan_author/render.py +166 -0
  160. src/agents/research_plan_author/render_artifacts.py +187 -0
  161. src/agents/research_plan_author/run.py +765 -0
  162. src/agents/research_plan_author/run_logging.py +209 -0
  163. src/agents/research_plan_author/section_cache.py +184 -0
  164. src/agents/research_plan_author/section_composer.py +1764 -0
  165. src/agents/research_plan_author/section_router.py +209 -0
  166. src/agents/research_plan_author/semantic_validator.py +254 -0
  167. src/agents/research_plan_author/source_bundle.py +46 -0
  168. src/agents/research_plan_author/source_registry.py +208 -0
  169. src/agents/research_plan_author/survey_source_loader.py +137 -0
  170. src/agents/research_plan_author/template_adapter.py +173 -0
  171. src/agents/research_plan_author/template_profile.py +204 -0
  172. src/agents/research_plan_author/tex_renderer.py +496 -0
  173. src/agents/research_plan_author/theory_presentation.py +134 -0
  174. src/agents/research_plan_author/theory_spine.py +703 -0
  175. src/agents/survey_agent/Dockerfile +54 -0
  176. src/agents/survey_agent/README.md +350 -0
  177. src/agents/survey_agent/config/deep_survey.yaml +217 -0
  178. src/agents/survey_agent/config/deep_survey_batch.yaml +176 -0
  179. src/agents/survey_agent/config/deep_survey_fast.yaml +183 -0
  180. src/agents/survey_agent/config/evaluate_baseline.yaml +197 -0
  181. src/agents/survey_agent/config/outcomeRAG.yaml +159 -0
  182. src/agents/survey_agent/env_report.md +348 -0
  183. src/agents/survey_agent/modules/__init__.py +0 -0
  184. src/agents/survey_agent/modules/code_collector.py +2194 -0
  185. src/agents/survey_agent/modules/code_report_generator.py +1282 -0
  186. src/agents/survey_agent/modules/data_manager.py +1713 -0
  187. src/agents/survey_agent/modules/database.py +167 -0
  188. src/agents/survey_agent/modules/fulltext_download_cache.py +462 -0
  189. src/agents/survey_agent/modules/fulltext_http.py +134 -0
  190. src/agents/survey_agent/modules/fulltext_resolution.py +749 -0
  191. src/agents/survey_agent/modules/judge.py +434 -0
  192. src/agents/survey_agent/modules/outcome_RAG.py +236 -0
  193. src/agents/survey_agent/modules/paper_graph_retriever.py +1075 -0
  194. src/agents/survey_agent/modules/pe.py +1729 -0
  195. src/agents/survey_agent/modules/pseudo_creator.py +782 -0
  196. src/agents/survey_agent/modules/pseudo_reviser.py +537 -0
  197. src/agents/survey_agent/modules/refine_agent.py +1952 -0
  198. src/agents/survey_agent/modules/survey_generator.py +5691 -0
  199. src/agents/survey_agent/modules/survey_visualizer.py +1817 -0
  200. src/agents/survey_agent/modules/visual_prompts.py +327 -0
  201. src/agents/survey_agent/modules/work_analyzer.py +2750 -0
  202. src/agents/survey_agent/modules/work_collector.py +5649 -0
  203. src/agents/survey_agent/scripts/baseline_evaluation.py +411 -0
  204. src/agents/survey_agent/scripts/run_deep_survey.py +224 -0
  205. src/agents/survey_agent/scripts/run_deep_survey_adapter.py +407 -0
  206. src/agents/survey_agent/scripts/run_deep_survey_adapter_with_input.py +409 -0
  207. src/agents/survey_agent/scripts/run_deep_survey_batch.py +228 -0
  208. src/agents/survey_agent/scripts/run_deep_survey_batch_arg.py +243 -0
  209. src/agents/survey_agent/scripts/run_deep_survey_batch_settings.py +218 -0
  210. src/agents/survey_agent/scripts/run_keynotes_gen.py +128 -0
  211. src/agents/survey_agent/scripts/run_work_clusters.py +140 -0
  212. src/agents/survey_agent/utils/__init__.py +0 -0
  213. src/agents/survey_agent/utils/ablation_utils.py +317 -0
  214. src/agents/survey_agent/utils/api_call.py +2518 -0
  215. src/agents/survey_agent/utils/chat_utils.py +82 -0
  216. src/agents/survey_agent/utils/config_utils.py +70 -0
  217. src/agents/survey_agent/utils/convert_to_md.py +29 -0
  218. src/agents/survey_agent/utils/err_info.py +24 -0
  219. src/agents/survey_agent/utils/file_utils.py +176 -0
  220. src/agents/survey_agent/utils/gpu_utils.py +145 -0
  221. src/agents/survey_agent/utils/graph_visualizer.py +279 -0
  222. src/agents/survey_agent/utils/html_utils.py +181 -0
  223. src/agents/survey_agent/utils/latex_generator_intra_cluster.py +529 -0
  224. src/agents/survey_agent/utils/latex_generator_relation_table.py +193 -0
  225. src/agents/survey_agent/utils/mineru_section_packer.py +334 -0
  226. src/agents/survey_agent/utils/mineru_utils.py +396 -0
  227. src/agents/survey_agent/utils/paper.py +144 -0
  228. src/agents/survey_agent/utils/read_yaml.py +15 -0
  229. src/agents/survey_agent/utils/repo_utils.py +167 -0
  230. src/agents/survey_agent/utils/rich_logger.py +77 -0
  231. src/agents/survey_agent/utils/step2v2.py +874 -0
  232. src/agents/survey_agent/utils/step2v2_extractor.py +856 -0
  233. src/agents/survey_agent/utils/topic_survey_storage.py +309 -0
  234. src/agents/survey_agent/utils/utils.py +118 -0
  235. src/agents/survey_agent/utils/work.py +2 -0
  236. src/cli.py +1611 -0
  237. src/config/__init__.py +213 -0
  238. src/config/default.yaml +1122 -0
  239. src/harness/LICENSE +21 -0
  240. src/harness/VENDORED.md +14 -0
  241. src/harness/src/__init__.py +0 -0
  242. src/harness/src/openharness/__init__.py +0 -0
  243. src/harness/src/openharness/__main__.py +6 -0
  244. src/harness/src/openharness/api/__init__.py +21 -0
  245. src/harness/src/openharness/api/client.py +281 -0
  246. src/harness/src/openharness/api/codex_client.py +407 -0
  247. src/harness/src/openharness/api/copilot_auth.py +240 -0
  248. src/harness/src/openharness/api/copilot_client.py +134 -0
  249. src/harness/src/openharness/api/errors.py +19 -0
  250. src/harness/src/openharness/api/openai_client.py +577 -0
  251. src/harness/src/openharness/api/provider.py +186 -0
  252. src/harness/src/openharness/api/registry.py +437 -0
  253. src/harness/src/openharness/api/usage.py +17 -0
  254. src/harness/src/openharness/auth/__init__.py +29 -0
  255. src/harness/src/openharness/auth/external.py +610 -0
  256. src/harness/src/openharness/auth/flows.py +189 -0
  257. src/harness/src/openharness/auth/manager.py +482 -0
  258. src/harness/src/openharness/auth/storage.py +269 -0
  259. src/harness/src/openharness/autopilot/__init__.py +23 -0
  260. src/harness/src/openharness/autopilot/service.py +2239 -0
  261. src/harness/src/openharness/autopilot/types.py +91 -0
  262. src/harness/src/openharness/bridge/__init__.py +20 -0
  263. src/harness/src/openharness/bridge/manager.py +106 -0
  264. src/harness/src/openharness/bridge/session_runner.py +46 -0
  265. src/harness/src/openharness/bridge/types.py +37 -0
  266. src/harness/src/openharness/bridge/work_secret.py +41 -0
  267. src/harness/src/openharness/channels/UPSTREAM +6 -0
  268. src/harness/src/openharness/channels/__init__.py +22 -0
  269. src/harness/src/openharness/channels/adapter.py +131 -0
  270. src/harness/src/openharness/channels/bus/__init__.py +6 -0
  271. src/harness/src/openharness/channels/bus/events.py +38 -0
  272. src/harness/src/openharness/channels/bus/queue.py +44 -0
  273. src/harness/src/openharness/channels/impl/__init__.py +6 -0
  274. src/harness/src/openharness/channels/impl/base.py +141 -0
  275. src/harness/src/openharness/channels/impl/dingtalk.py +445 -0
  276. src/harness/src/openharness/channels/impl/discord.py +311 -0
  277. src/harness/src/openharness/channels/impl/email.py +410 -0
  278. src/harness/src/openharness/channels/impl/feishu.py +1342 -0
  279. src/harness/src/openharness/channels/impl/manager.py +257 -0
  280. src/harness/src/openharness/channels/impl/matrix.py +700 -0
  281. src/harness/src/openharness/channels/impl/mochat.py +897 -0
  282. src/harness/src/openharness/channels/impl/qq.py +141 -0
  283. src/harness/src/openharness/channels/impl/slack.py +284 -0
  284. src/harness/src/openharness/channels/impl/telegram.py +525 -0
  285. src/harness/src/openharness/channels/impl/whatsapp.py +159 -0
  286. src/harness/src/openharness/cli.py +2551 -0
  287. src/harness/src/openharness/commands/__init__.py +21 -0
  288. src/harness/src/openharness/commands/registry.py +2783 -0
  289. src/harness/src/openharness/config/__init__.py +34 -0
  290. src/harness/src/openharness/config/paths.py +160 -0
  291. src/harness/src/openharness/config/schema.py +119 -0
  292. src/harness/src/openharness/config/settings.py +1104 -0
  293. src/harness/src/openharness/coordinator/__init__.py +12 -0
  294. src/harness/src/openharness/coordinator/agent_definitions.py +975 -0
  295. src/harness/src/openharness/coordinator/coordinator_mode.py +520 -0
  296. src/harness/src/openharness/engine/__init__.py +80 -0
  297. src/harness/src/openharness/engine/cost_tracker.py +24 -0
  298. src/harness/src/openharness/engine/messages.py +222 -0
  299. src/harness/src/openharness/engine/query.py +1188 -0
  300. src/harness/src/openharness/engine/query_engine.py +313 -0
  301. src/harness/src/openharness/engine/stream_events.py +90 -0
  302. src/harness/src/openharness/hooks/__init__.py +50 -0
  303. src/harness/src/openharness/hooks/events.py +20 -0
  304. src/harness/src/openharness/hooks/executor.py +242 -0
  305. src/harness/src/openharness/hooks/hot_reload.py +31 -0
  306. src/harness/src/openharness/hooks/loader.py +68 -0
  307. src/harness/src/openharness/hooks/schemas.py +66 -0
  308. src/harness/src/openharness/hooks/types.py +38 -0
  309. src/harness/src/openharness/keybindings/__init__.py +14 -0
  310. src/harness/src/openharness/keybindings/default_bindings.py +11 -0
  311. src/harness/src/openharness/keybindings/loader.py +22 -0
  312. src/harness/src/openharness/keybindings/parser.py +18 -0
  313. src/harness/src/openharness/keybindings/resolver.py +13 -0
  314. src/harness/src/openharness/mcp/__init__.py +79 -0
  315. src/harness/src/openharness/mcp/client.py +298 -0
  316. src/harness/src/openharness/mcp/config.py +16 -0
  317. src/harness/src/openharness/mcp/types.py +76 -0
  318. src/harness/src/openharness/memory/__init__.py +25 -0
  319. src/harness/src/openharness/memory/agent.py +91 -0
  320. src/harness/src/openharness/memory/manager.py +183 -0
  321. src/harness/src/openharness/memory/memdir.py +52 -0
  322. src/harness/src/openharness/memory/migrate.py +137 -0
  323. src/harness/src/openharness/memory/paths.py +22 -0
  324. src/harness/src/openharness/memory/relevance.py +145 -0
  325. src/harness/src/openharness/memory/scan.py +116 -0
  326. src/harness/src/openharness/memory/schema.py +443 -0
  327. src/harness/src/openharness/memory/search.py +71 -0
  328. src/harness/src/openharness/memory/team.py +90 -0
  329. src/harness/src/openharness/memory/types.py +33 -0
  330. src/harness/src/openharness/memory/usage.py +153 -0
  331. src/harness/src/openharness/output_styles/__init__.py +5 -0
  332. src/harness/src/openharness/output_styles/loader.py +42 -0
  333. src/harness/src/openharness/permissions/__init__.py +26 -0
  334. src/harness/src/openharness/permissions/checker.py +200 -0
  335. src/harness/src/openharness/permissions/modes.py +13 -0
  336. src/harness/src/openharness/personalization/__init__.py +6 -0
  337. src/harness/src/openharness/personalization/extractor.py +138 -0
  338. src/harness/src/openharness/personalization/rules.py +64 -0
  339. src/harness/src/openharness/personalization/session_hook.py +65 -0
  340. src/harness/src/openharness/platforms.py +86 -0
  341. src/harness/src/openharness/plugins/__init__.py +53 -0
  342. src/harness/src/openharness/plugins/bundled/__init__.py +0 -0
  343. src/harness/src/openharness/plugins/installer.py +39 -0
  344. src/harness/src/openharness/plugins/loader.py +730 -0
  345. src/harness/src/openharness/plugins/schemas.py +24 -0
  346. src/harness/src/openharness/plugins/types.py +59 -0
  347. src/harness/src/openharness/prompts/__init__.py +14 -0
  348. src/harness/src/openharness/prompts/claudemd.py +48 -0
  349. src/harness/src/openharness/prompts/context.py +187 -0
  350. src/harness/src/openharness/prompts/environment.py +135 -0
  351. src/harness/src/openharness/prompts/system_prompt.py +109 -0
  352. src/harness/src/openharness/sandbox/Dockerfile +6 -0
  353. src/harness/src/openharness/sandbox/__init__.py +33 -0
  354. src/harness/src/openharness/sandbox/adapter.py +148 -0
  355. src/harness/src/openharness/sandbox/docker_backend.py +232 -0
  356. src/harness/src/openharness/sandbox/docker_image.py +103 -0
  357. src/harness/src/openharness/sandbox/path_validator.py +37 -0
  358. src/harness/src/openharness/sandbox/session.py +63 -0
  359. src/harness/src/openharness/services/__init__.py +30 -0
  360. src/harness/src/openharness/services/autodream/__init__.py +34 -0
  361. src/harness/src/openharness/services/autodream/backup.py +104 -0
  362. src/harness/src/openharness/services/autodream/lock.py +138 -0
  363. src/harness/src/openharness/services/autodream/prompt.py +128 -0
  364. src/harness/src/openharness/services/autodream/service.py +313 -0
  365. src/harness/src/openharness/services/compact/__init__.py +1725 -0
  366. src/harness/src/openharness/services/cron.py +137 -0
  367. src/harness/src/openharness/services/cron_scheduler.py +595 -0
  368. src/harness/src/openharness/services/lsp/__init__.py +216 -0
  369. src/harness/src/openharness/services/memory_extract/__init__.py +261 -0
  370. src/harness/src/openharness/services/oauth/__init__.py +0 -0
  371. src/harness/src/openharness/services/session_backend.py +97 -0
  372. src/harness/src/openharness/services/session_memory/__init__.py +139 -0
  373. src/harness/src/openharness/services/session_storage.py +230 -0
  374. src/harness/src/openharness/services/token_estimation.py +15 -0
  375. src/harness/src/openharness/services/tool_outputs.py +55 -0
  376. src/harness/src/openharness/skills/__init__.py +44 -0
  377. src/harness/src/openharness/skills/_frontmatter.py +97 -0
  378. src/harness/src/openharness/skills/bundled/__init__.py +75 -0
  379. src/harness/src/openharness/skills/bundled/content/commit.md +25 -0
  380. src/harness/src/openharness/skills/bundled/content/debug.md +25 -0
  381. src/harness/src/openharness/skills/bundled/content/diagnose.md +35 -0
  382. src/harness/src/openharness/skills/bundled/content/plan.md +39 -0
  383. src/harness/src/openharness/skills/bundled/content/review.md +33 -0
  384. src/harness/src/openharness/skills/bundled/content/simplify.md +26 -0
  385. src/harness/src/openharness/skills/bundled/content/skill-creator.md +175 -0
  386. src/harness/src/openharness/skills/bundled/content/test.md +38 -0
  387. src/harness/src/openharness/skills/loader.py +229 -0
  388. src/harness/src/openharness/skills/registry.py +29 -0
  389. src/harness/src/openharness/skills/types.py +24 -0
  390. src/harness/src/openharness/state/__init__.py +6 -0
  391. src/harness/src/openharness/state/app_state.py +30 -0
  392. src/harness/src/openharness/state/store.py +40 -0
  393. src/harness/src/openharness/swarm/__init__.py +70 -0
  394. src/harness/src/openharness/swarm/in_process.py +693 -0
  395. src/harness/src/openharness/swarm/lockfile.py +24 -0
  396. src/harness/src/openharness/swarm/mailbox.py +522 -0
  397. src/harness/src/openharness/swarm/permission_sync.py +1168 -0
  398. src/harness/src/openharness/swarm/registry.py +410 -0
  399. src/harness/src/openharness/swarm/spawn_utils.py +229 -0
  400. src/harness/src/openharness/swarm/subprocess_backend.py +171 -0
  401. src/harness/src/openharness/swarm/team_lifecycle.py +910 -0
  402. src/harness/src/openharness/swarm/types.py +398 -0
  403. src/harness/src/openharness/swarm/worktree.py +315 -0
  404. src/harness/src/openharness/tasks/__init__.py +18 -0
  405. src/harness/src/openharness/tasks/local_agent_task.py +28 -0
  406. src/harness/src/openharness/tasks/local_shell_task.py +17 -0
  407. src/harness/src/openharness/tasks/manager.py +475 -0
  408. src/harness/src/openharness/tasks/stop_task.py +11 -0
  409. src/harness/src/openharness/tasks/types.py +32 -0
  410. src/harness/src/openharness/themes/__init__.py +21 -0
  411. src/harness/src/openharness/themes/builtin.py +89 -0
  412. src/harness/src/openharness/themes/loader.py +55 -0
  413. src/harness/src/openharness/themes/schema.py +54 -0
  414. src/harness/src/openharness/tools/__init__.py +107 -0
  415. src/harness/src/openharness/tools/agent_tool.py +141 -0
  416. src/harness/src/openharness/tools/ask_user_question_tool.py +46 -0
  417. src/harness/src/openharness/tools/base.py +80 -0
  418. src/harness/src/openharness/tools/bash_tool.py +259 -0
  419. src/harness/src/openharness/tools/brief_tool.py +33 -0
  420. src/harness/src/openharness/tools/config_tool.py +54 -0
  421. src/harness/src/openharness/tools/cron_create_tool.py +105 -0
  422. src/harness/src/openharness/tools/cron_delete_tool.py +32 -0
  423. src/harness/src/openharness/tools/cron_list_tool.py +69 -0
  424. src/harness/src/openharness/tools/cron_toggle_tool.py +37 -0
  425. src/harness/src/openharness/tools/enter_plan_mode_tool.py +28 -0
  426. src/harness/src/openharness/tools/enter_worktree_tool.py +80 -0
  427. src/harness/src/openharness/tools/exit_plan_mode_tool.py +28 -0
  428. src/harness/src/openharness/tools/exit_worktree_tool.py +42 -0
  429. src/harness/src/openharness/tools/file_edit_tool.py +95 -0
  430. src/harness/src/openharness/tools/file_read_tool.py +72 -0
  431. src/harness/src/openharness/tools/file_write_tool.py +87 -0
  432. src/harness/src/openharness/tools/glob_tool.py +175 -0
  433. src/harness/src/openharness/tools/grep_tool.py +374 -0
  434. src/harness/src/openharness/tools/image_generation_tool.py +447 -0
  435. src/harness/src/openharness/tools/image_to_text_tool.py +237 -0
  436. src/harness/src/openharness/tools/list_mcp_resources_tool.py +36 -0
  437. src/harness/src/openharness/tools/lsp_tool.py +154 -0
  438. src/harness/src/openharness/tools/mcp_auth_tool.py +71 -0
  439. src/harness/src/openharness/tools/mcp_tool.py +72 -0
  440. src/harness/src/openharness/tools/notebook_edit_tool.py +97 -0
  441. src/harness/src/openharness/tools/read_mcp_resource_tool.py +38 -0
  442. src/harness/src/openharness/tools/remote_trigger_tool.py +74 -0
  443. src/harness/src/openharness/tools/send_message_tool.py +58 -0
  444. src/harness/src/openharness/tools/skill_tool.py +43 -0
  445. src/harness/src/openharness/tools/sleep_tool.py +32 -0
  446. src/harness/src/openharness/tools/task_create_tool.py +56 -0
  447. src/harness/src/openharness/tools/task_get_tool.py +33 -0
  448. src/harness/src/openharness/tools/task_list_tool.py +35 -0
  449. src/harness/src/openharness/tools/task_output_tool.py +35 -0
  450. src/harness/src/openharness/tools/task_stop_tool.py +30 -0
  451. src/harness/src/openharness/tools/task_update_tool.py +50 -0
  452. src/harness/src/openharness/tools/team_create_tool.py +31 -0
  453. src/harness/src/openharness/tools/team_delete_tool.py +30 -0
  454. src/harness/src/openharness/tools/todo_write_tool.py +46 -0
  455. src/harness/src/openharness/tools/tool_search_tool.py +38 -0
  456. src/harness/src/openharness/tools/web_fetch_tool.py +117 -0
  457. src/harness/src/openharness/tools/web_search_tool.py +119 -0
  458. src/harness/src/openharness/ui/__init__.py +5 -0
  459. src/harness/src/openharness/ui/app.py +320 -0
  460. src/harness/src/openharness/ui/backend_host.py +941 -0
  461. src/harness/src/openharness/ui/coordinator_drain.py +198 -0
  462. src/harness/src/openharness/ui/input.py +32 -0
  463. src/harness/src/openharness/ui/output.py +265 -0
  464. src/harness/src/openharness/ui/permission_dialog.py +14 -0
  465. src/harness/src/openharness/ui/protocol.py +247 -0
  466. src/harness/src/openharness/ui/react_launcher.py +179 -0
  467. src/harness/src/openharness/ui/runtime.py +799 -0
  468. src/harness/src/openharness/ui/textual_app.py +495 -0
  469. src/harness/src/openharness/utils/__init__.py +0 -0
  470. src/harness/src/openharness/utils/file_lock.py +86 -0
  471. src/harness/src/openharness/utils/fs.py +98 -0
  472. src/harness/src/openharness/utils/helpers.py +77 -0
  473. src/harness/src/openharness/utils/network_guard.py +340 -0
  474. src/harness/src/openharness/utils/shell.py +147 -0
  475. src/harness/src/openharness/vim/__init__.py +5 -0
  476. src/harness/src/openharness/vim/transitions.py +8 -0
  477. src/harness/src/openharness/voice/__init__.py +7 -0
  478. src/harness/src/openharness/voice/keyterms.py +10 -0
  479. src/harness/src/openharness/voice/stream_stt.py +8 -0
  480. src/harness/src/openharness/voice/voice_mode.py +44 -0
  481. src/llm/__init__.py +29 -0
  482. src/llm/image_generation.py +402 -0
  483. src/llm/provider_registry.py +419 -0
  484. src/llm/runtime_env.py +64 -0
  485. src/llm/structured_output.py +131 -0
  486. src/llm/vision.py +198 -0
  487. src/memory/__init__.py +2 -0
  488. src/memory/api/__init__.py +1 -0
  489. src/memory/api/base_memory_system_api.py +98 -0
  490. src/memory/api/base_symbolic_memory_system_api.py +190 -0
  491. src/memory/api/base_vector_memory_system_api.py +116 -0
  492. src/memory/api/faiss_memory_system_api.py +228 -0
  493. src/memory/api/slot_process_api.py +827 -0
  494. src/memory/api/symbolic_memory_system_api.py +572 -0
  495. src/memory/memory_system/__init__.py +48 -0
  496. src/memory/memory_system/component_taxonomy.py +430 -0
  497. src/memory/memory_system/llm.py +437 -0
  498. src/memory/memory_system/models.py +168 -0
  499. src/memory/memory_system/user_prompt.py +418 -0
  500. src/memory/memory_system/utils.py +282 -0
  501. src/memory/memory_system/vectorstore.py +311 -0
  502. src/memory/memory_system/working_slot.py +99 -0
  503. src/pipeline/__init__.py +129 -0
  504. src/pipeline/contracts.py +531 -0
  505. src/pipeline/discipline_taxonomy.py +916 -0
  506. src/pipeline/evidence_coverage_ledger.py +1150 -0
  507. src/pipeline/evidence_refinement.py +620 -0
  508. src/pipeline/experiment_to_symbolic.py +137 -0
  509. src/pipeline/multimodal_evidence/__init__.py +28 -0
  510. src/pipeline/multimodal_evidence/capabilities.py +107 -0
  511. src/pipeline/multimodal_evidence/claims.py +135 -0
  512. src/pipeline/multimodal_evidence/contract.py +291 -0
  513. src/pipeline/multimodal_evidence/data_sh_compiler.py +505 -0
  514. src/pipeline/multimodal_evidence/handoff_binding.py +135 -0
  515. src/pipeline/multimodal_evidence/manifest.py +254 -0
  516. src/pipeline/multimodal_evidence/native_analysis.py +479 -0
  517. src/pipeline/multimodal_evidence/perception.py +277 -0
  518. src/pipeline/multimodal_evidence/query_binding.py +65 -0
  519. src/pipeline/multimodal_evidence/rendering.py +120 -0
  520. src/pipeline/multimodal_evidence/safety.py +42 -0
  521. src/pipeline/multimodal_evidence/sampling.py +69 -0
  522. src/pipeline/multimodal_evidence/service.py +148 -0
  523. src/pipeline/multimodal_evidence/survey_integration.py +460 -0
  524. src/pipeline/paper_identity.py +52 -0
  525. src/pipeline/research_design_inventory.py +338 -0
  526. src/pipeline/research_domain_resolution.py +669 -0
  527. src/pipeline/research_identity.py +548 -0
  528. src/pipeline/research_question_contract.py +646 -0
  529. src/pipeline/retrieval_lanes.py +889 -0
  530. src/pipeline/run_loop.py +862 -0
  531. src/pipeline/science_events.py +30 -0
  532. src/pipeline/science_manifests.py +602 -0
  533. src/pipeline/science_run.py +918 -0
  534. src/pipeline/science_stages.py +709 -0
  535. src/pipeline/science_workflow.py +807 -0
  536. src/pipeline/sh_cluster_projection.py +306 -0
  537. src/pipeline/sh_graph_provenance.py +648 -0
  538. src/pipeline/subhypothesis_decomposition.py +392 -0
  539. src/pipeline/subhypothesis_evidence.py +856 -0
  540. src/pipeline/survey_evidence_plan.py +858 -0
  541. src/pipeline/survey_gap_adjudication.py +647 -0
  542. src/pipeline/survey_gap_candidates.py +840 -0
  543. src/pipeline/survey_gap_ledger.py +683 -0
  544. src/pipeline/survey_gap_triage.py +401 -0
  545. src/pipeline/survey_handoff_persistence.py +481 -0
  546. src/pipeline/survey_handoff_projection.py +457 -0
  547. src/pipeline/survey_idea_handoff.py +1112 -0
  548. src/pipeline/survey_idea_loader.py +289 -0
  549. src/research_run_ids.py +11 -0
@@ -0,0 +1,782 @@
1
+ """
2
+ PseudoWriter: Agent-based pseudocode creator that creates pseudocode from scratch
3
+ based on repository structure, paper title, and abstract.
4
+ """
5
+
6
+ import json
7
+ import os
8
+ import tiktoken
9
+ from typing import List, Optional, Dict
10
+ from dataclasses import dataclass, field
11
+ from utils.rich_logger import get_logger
12
+ from utils.api_call import ChatAgent
13
+ from utils.utils import extract_json
14
+ from utils.repo_utils import format_repo_structure
15
+
16
+
17
+ # Agent prompt for deciding operations
18
+ AGENT_OPERATE_PROMPT = """
19
+ You are an intelligent agent that creates project pseudocode by interacting with a code repository.
20
+
21
+ [Repository Information]
22
+ - Name: {repo_name}
23
+ - Title: {paper_title}
24
+ - Abstract: {paper_abstract}
25
+ - Structure:
26
+ {repo_structure}
27
+
28
+ [Current Project Pseudocode]
29
+ {current_pseudocode}
30
+
31
+ [Memory - Operation History and Results]
32
+ {memory}
33
+
34
+ [Suggestion - Review Feedback]
35
+ {suggestions}
36
+
37
+ [Context - Retrieved Content from Repository]
38
+ {context}
39
+
40
+ [OPERATION REQUIREMENTS - CRITICAL]
41
+ 1. You MUST call "get_source_code" at least 3-5 times before calling "create" to understand the actual code implementation
42
+ 2. You MUST prioritize reading actual source code files over relying on abstract - the abstract is just context, NOT the source of truth
43
+ 3. For each paper, identify the 3-5 most important source files (main entry point, core algorithms, key utilities) and read them
44
+ 4. "create" should ONLY be called AFTER you have retrieved and understood the actual source code
45
+ 5. You MUST call "revise" operation at least once every 3 rounds to improve the pseudocode based on source code findings
46
+
47
+ [Priority File Selection]
48
+ When selecting files to read, prioritize in this order:
49
+ 1. Main entry points (e.g., main.py, app.py, __main__.py, cli.py)
50
+ 2. Core algorithm files (e.g., agent.py, solver.py, model.py, engine.py)
51
+ 3. Key utility files that implement important logic
52
+ 4. Configuration files that reveal the architecture
53
+ 5. README and docs for architectural context
54
+
55
+ ---
56
+
57
+ Your task is to plan a sequence of operations to create and refine the pseudocode.
58
+
59
+ Available operations:
60
+ 1. "get_source_code": Query source code of a specific file by providing its path - USE THIS EXTENSIVELY to understand actual implementation
61
+ 2. "create": Create a new pseudocode based on ACTUAL SOURCE CODE you retrieved - NOT from abstract alone
62
+ 3. "revise": Modify the current pseudocode based on the retrieved source code context and suggestion
63
+ 4. "review": Call the review skill to provide suggestions on what to do next
64
+ 5. "finish": Complete the creation process when the pseudocode is satisfactory
65
+
66
+ Output format (JSON):
67
+ {{
68
+ "plan": [
69
+ {{"operation": "...", "file_path": "...", "reason": "..."}},
70
+ ...
71
+ ]
72
+ }}
73
+
74
+ - Each item in "plan" is one operation to execute in order
75
+ - For "get_source_code", include "file_path" with the actual path from repo_structure
76
+ - For "create", "revise", or "finish", no file_path needed
77
+ - Include at least 1 operation, up to 3 operations per plan
78
+ - You MUST call "get_source_code" multiple times to retrieve key source files before "create"
79
+ - "review" operation does not require file_path
80
+ - Prioritize reading source code files over creating pseudocode from abstract
81
+
82
+ Generate JSON directly without any other things.
83
+ """
84
+
85
+ REVIEW_PROMPT = """
86
+ You are a code review expert. Review the current project pseudocode and provide specific suggestions.
87
+
88
+ [Paper Info]
89
+ Title: {paper_title}
90
+ Abstract: {paper_abstract}
91
+
92
+ [Repository Structure]
93
+ {repo_structure}
94
+
95
+ [Current Project Pseudocode]
96
+ {final_pseudocode}
97
+
98
+ ---
99
+ Requirements:
100
+ 1. The pseudocode should be well formatted and well structured with clear logic.
101
+ 2. The pseudocode should contain all the core implementation specifically.
102
+ 3. The pseudocode should be concise. No more than 7 sections and no more than 400 lines.
103
+ 4. The pseudocode should be concise on or skip less important parts.
104
+
105
+ Please provide suggestions and scores (0-10) for the pseudocode.
106
+ - suggestions: List of concrete improvement suggestions
107
+ - conciseness_score: How concise the pseudocode is (higher = more concise, less redundant)
108
+ - logic_score: How clear and logical the pseudocode structure is (higher = better)
109
+ - specificity_score: How specific the pseudocode is about key implementation details (core model components, critical loss functions, key algorithms) - NOT about including trivial imports or boilerplate
110
+
111
+ Scoring Standards (0-10):
112
+ 1. conciseness_score:
113
+ - 9-10: Very concise, minimal redundancy, focuses on essential logic only
114
+ - 7-8: Mostly concise, minor redundancies
115
+ - 5-6: Some redundant sections or verbose descriptions
116
+ - 3-4: Noticeable redundancies, verbose in places
117
+ - 1-2: Highly repetitive, lots of unnecessary details
118
+
119
+ 2. logic_score:
120
+ - 9-10: Clear control flow, logical section organization, easy to follow
121
+ - 7-8: Good structure, minor issues with flow
122
+ - 5-6: Some logical issues, sections somewhat disconnected
123
+ - 3-4: Confusing flow, poor section organization
124
+ - 1-2: Very hard to understand the logic
125
+
126
+ 3. specificity_score: Focus on KEY content specificity, NOT adding irrelevant details like imports or file paths
127
+ - 9-10: Captures key details: core model components, critical loss functions, key algorithms, important data flow - without trivial imports
128
+ - 7-8: Good detail on main components, minor missing key details
129
+ - 5-6: Too generic, missing important specifics (e.g., just says "train model" without explaining how)
130
+ - 3-4: Lacks important details about core algorithms/components
131
+ - 1-2: Very vague, no meaningful implementation details
132
+
133
+ Output format (JSON):
134
+ {{
135
+ "suggestions": ["suggestion1", "suggestion2", ...],
136
+ "conciseness_score": 7,
137
+ "logic_score": 8,
138
+ "specificity_score": 8
139
+ }}
140
+
141
+ Generate JSON directly without any other things.
142
+ """
143
+
144
+ CREATE_PROMPT = """
145
+ TASK:
146
+
147
+ Create a well-structured pseudocode and analysis for a research project based on its paper information and repository structure.
148
+
149
+ [Paper Info]
150
+ Title: {paper_title}
151
+ Abstract: {paper_abstract}
152
+
153
+ [Repository Structure]
154
+ {repo_structure}
155
+
156
+ [Retrieved Context from Repository Files]
157
+ {context}
158
+
159
+ Please create a comprehensive pseudocode that captures:
160
+ 1. The main components and architecture of the project
161
+ 2. Core algorithms and their implementations
162
+ 3. Data flow and processing pipelines
163
+ 4. Key functions and their purposes
164
+
165
+ REQUIREMENTS:
166
+ - The pseudocode should be well formatted and well structured with clear logic
167
+ - Use clear section headers to organize the pseudocode
168
+ - Focus on the core implementation details that are essential to understanding the project
169
+ - Be concise: no more than 7 sections and no more than 400 lines
170
+ - Prioritize important parts and skip less critical details (e.g., trivial imports, boilerplate code)
171
+ - Output only the pseudocode and brief analysis directly
172
+ """
173
+
174
+ MODIFY_PROMPT = """
175
+ TASK:
176
+
177
+ Refine the current project pseudocode based on the retrieved context and review suggestions.
178
+
179
+ [Paper Info]
180
+ {paper_title}:
181
+ {paper_abstract}
182
+
183
+ [Repository Info]
184
+ {repo_structure}
185
+
186
+ [Current Pseudocode]
187
+ {final_pseudocode}
188
+
189
+ [Retrieved Context]
190
+ {context}
191
+
192
+ [Reviewer Suggestion]
193
+ {suggestions}
194
+
195
+ [Reason for Modification]
196
+ {reason}
197
+
198
+ Please provide the refined pseudocode that incorporates the relevant details from the context.
199
+
200
+ REQUIREMENTS:
201
+ - Merge necessary content from context rather than simply adding the context
202
+ - Be cautious about adding new sections in the pseudocode
203
+ - Keep the pseudocode at a reasonable length. If it gets too long, prioritize the most important additions or delete less important parts
204
+ - Output the refined pseudocode and analysis directly
205
+ """
206
+
207
+
208
+ @dataclass
209
+ class AgentContext:
210
+ """Structured input for the PseudoWriter agent."""
211
+ repo_name: str
212
+ paper_title: str
213
+ paper_abstract: str
214
+ repo_structure: Dict
215
+ current_pseudocode: str = ""
216
+ memory: List[Dict] = field(default_factory=list)
217
+ suggestions: List[str] = field(default_factory=list)
218
+ context: str = ""
219
+ error_log: List[str] = field(default_factory=list)
220
+ review_scores: Dict = field(default_factory=dict) # {"conciseness": 0-10, "logic": 0-10, "specificity": 0-10}
221
+ has_created_initial: bool = False # Track if create has been called
222
+
223
+
224
+ def compress_memory(memory: List[Dict], encodings=None) -> List[Dict]:
225
+ """
226
+ Compress memory by removing the oldest half of entries.
227
+ This is a simple implementation that can be improved later.
228
+
229
+ Args:
230
+ memory: List of memory entries
231
+ encodings: Optional tiktoken encodings for token counting
232
+
233
+ Returns:
234
+ Compressed memory list (oldest half removed)
235
+ """
236
+ if len(memory) <= 1:
237
+ return memory
238
+
239
+ # Keep the newer half (more relevant for current work)
240
+ mid_point = len(memory) // 2
241
+ compressed = memory[mid_point:]
242
+
243
+ # Log compression info
244
+ logger = get_logger("PseudoWriter")
245
+ logger.info(f"Memory compressed: {len(memory)} -> {len(compressed)} entries (removed oldest {mid_point} entries)")
246
+
247
+ return compressed
248
+
249
+
250
+ def count_tokens(text: str, encodings) -> int:
251
+ """Count tokens in text using tiktoken encoding."""
252
+ if encodings is None:
253
+ # Fallback: estimate by character count
254
+ return len(text) // 4
255
+ try:
256
+ return len(encodings.encode(text))
257
+ except Exception:
258
+ return len(text) // 4
259
+
260
+
261
+ class PseudoWriter:
262
+ """
263
+ Agent-based pseudocode creator that creates pseudocode from scratch.
264
+ Uses a planner agent with various skills to create and refine pseudocode.
265
+ """
266
+
267
+ def __init__(self, config, chat_agent: ChatAgent, repo_cache_path: str):
268
+ self.chat_agent = chat_agent
269
+ self.logger = get_logger("PseudoWriter")
270
+ self.config = config
271
+ self.repo_cache_path = repo_cache_path
272
+ self.default_max_steps = 20
273
+ self.memory_token_threshold = 50000 # Threshold for memory compression
274
+ self.use_memory_result_uplimit = True
275
+ self.memory_result_max_length = 8000 # Max characters for memory results to keep in memory display
276
+ self.agent_source_code_max_read_lines = 700
277
+
278
+ # Initialize tiktoken encoding for token counting
279
+ try:
280
+ self.encodings = tiktoken.get_encoding("cl100k_base")
281
+ except Exception:
282
+ self.logger.warning("Failed to load tiktoken encoding, using character-based estimation")
283
+ self.encodings = None
284
+
285
+ self.read_readme_first = True # Flag to read README before first agent planning round
286
+
287
+ def _find_readme_path(self, repo_structure: dict) -> Optional[str]:
288
+ """
289
+ Find README file path from repo_structure. Prioritize root directory, then search others.
290
+
291
+ Args:
292
+ repo_structure: Repository structure as a nested dict
293
+
294
+ Returns:
295
+ Relative path to README file, or None if not found
296
+ """
297
+ readme_patterns = {"readme.md", "readme.rst", "readme.txt", "readme"}
298
+
299
+ def search(node: dict, current_path: str = "") -> Optional[str]:
300
+ for key in node:
301
+ lower_key = key.lower()
302
+ if lower_key in readme_patterns:
303
+ value = node[key]
304
+ if isinstance(value, dict) and value.get("_is_file"):
305
+ return os.path.join(current_path, key) if current_path else key
306
+ value = node[key]
307
+ if isinstance(value, dict) and not value.get("_is_file"):
308
+ new_path = os.path.join(current_path, key) if current_path else key
309
+ result = search(value, new_path)
310
+ if result:
311
+ return result
312
+ return None
313
+
314
+ # First check root directory
315
+ for key in repo_structure:
316
+ lower_key = key.lower()
317
+ if lower_key in readme_patterns:
318
+ value = repo_structure[key]
319
+ if isinstance(value, dict) and value.get("_is_file"):
320
+ self.logger.info(f"Found README in root: {key}")
321
+ return key
322
+
323
+ # Search in subdirectories
324
+ return search(repo_structure)
325
+
326
+ def _get_source_code(self, repo_name: str, rel_path: str) -> Optional[str]:
327
+ """Read source code from the repository."""
328
+ try:
329
+ repo_path = os.path.join(self.repo_cache_path, repo_name)
330
+ file_abs = os.path.join(repo_path, rel_path)
331
+ if os.path.exists(file_abs):
332
+ with open(file_abs, "r", encoding="utf-8", errors="ignore") as f:
333
+ # Limit to first 500 lines to avoid too much content
334
+ lines = f.readlines()[:self.agent_source_code_max_read_lines]
335
+ return "".join(lines)
336
+ except Exception as e:
337
+ self.logger.error(f"Error reading source code for {rel_path}: {e}")
338
+ return None
339
+
340
+ def _truncate_text(self, text: str, max_length: int, suffix: str = "(too long, truncated)...") -> str:
341
+ """Truncate text to max_length, adding suffix if truncated."""
342
+ if not text:
343
+ return text
344
+ if len(text) <= max_length:
345
+ return text
346
+ return text[:max_length - len(suffix)] + suffix
347
+
348
+ def _format_memory(self, memory: List[Dict]) -> str:
349
+ """Format memory (operation history with results) as string.
350
+
351
+ Different operation types have different formatting:
352
+ - create: shows truncated pseudocode
353
+ - get_source_code: shows file path and truncated source code
354
+ - revise: shows truncated pseudocode
355
+ - review: shows suggestions and scores
356
+ - finish: shows final context/suggestions
357
+ """
358
+ if not memory:
359
+ return "No operations performed yet."
360
+
361
+ lines = []
362
+ for i, op in enumerate(memory):
363
+ op_type = op.get('operation', '')
364
+ reason = op.get('reason', '')
365
+
366
+ lines.append(f"{i+1}. {op_type}: {reason}")
367
+
368
+ # Format based on operation type
369
+ if op_type == "create":
370
+ # Show truncated pseudocode
371
+ pseudocode = op.get('pseudocode', op.get('result', ''))
372
+ if self.use_memory_result_uplimit:
373
+ pseudocode = self._truncate_text(pseudocode, self.memory_result_max_length)
374
+ lines.append(f" Pseudocode: {pseudocode}")
375
+
376
+ elif op_type == "get_source_code":
377
+ # Show file path and truncated source code
378
+ file_path = op.get('file_path', 'unknown')
379
+ source_code = op.get('source_code_result', op.get('result', ''))
380
+ if self.use_memory_result_uplimit:
381
+ source_code = self._truncate_text(source_code, self.memory_result_max_length)
382
+ lines.append(f" File: {file_path}")
383
+ lines.append(f" Source: {source_code}")
384
+
385
+ elif op_type == "revise":
386
+ # Show truncated pseudocode
387
+ pseudocode = op.get('pseudocode', op.get('result', ''))
388
+ if self.use_memory_result_uplimit:
389
+ pseudocode = self._truncate_text(pseudocode, self.memory_result_max_length)
390
+ lines.append(f" Revised to: {pseudocode}")
391
+
392
+ elif op_type == "review":
393
+ # Show suggestions and scores
394
+ suggestions = op.get('suggestions', [])
395
+ scores = op.get('review_scores', {})
396
+ if suggestions:
397
+ suggestions_text = "; ".join(suggestions) # Limit to 5 suggestions
398
+ if self.use_memory_result_uplimit:
399
+ suggestions_text = self._truncate_text(suggestions_text, self.memory_result_max_length // 2)
400
+ lines.append(f" Suggestions: {suggestions_text}")
401
+ if scores:
402
+ lines.append(f" Scores: conciseness={scores.get('conciseness', 0)}, logic={scores.get('logic', 0)}, specificity={scores.get('specificity', 0)}")
403
+
404
+ elif op_type == "finish":
405
+ # Show context and suggestions
406
+ context = op.get('context', '')
407
+ reason = op.get('reason', '')
408
+ suggestions = op.get('suggestions', [])
409
+ if context and self.use_memory_result_uplimit:
410
+ context = self._truncate_text(context, self.memory_result_max_length)
411
+ if context:
412
+ lines.append(f" Final Context: {context}")
413
+ if suggestions:
414
+ suggestions_text = "; ".join(suggestions[:5])
415
+ if self.use_memory_result_uplimit:
416
+ suggestions_text = self._truncate_text(suggestions_text, self.memory_result_max_length // 2)
417
+ lines.append(f" Final Suggestions: {suggestions_text}")
418
+ if reason:
419
+ lines.append(f" Reason: {reason}")
420
+
421
+ else:
422
+ # Generic fallback
423
+ result = op.get('result', '')
424
+ if result:
425
+ if self.use_memory_result_uplimit:
426
+ result = self._truncate_text(result, self.memory_result_max_length)
427
+ lines.append(f" Result: {result}")
428
+
429
+ return "\n".join(lines)
430
+
431
+ def _check_memory_compression(self, agent_ctx: AgentContext) -> bool:
432
+ """Check if memory needs compression based on token count."""
433
+ memory_str = self._format_memory(agent_ctx.memory)
434
+ token_count = count_tokens(memory_str, self.encodings)
435
+
436
+ if token_count > self.memory_token_threshold:
437
+ self.logger.info(f"Memory token count ({token_count}) exceeds threshold ({self.memory_token_threshold}), compressing...")
438
+ agent_ctx.memory = compress_memory(agent_ctx.memory, self.encodings)
439
+ return True
440
+ return False
441
+
442
+ def _validate_review_response(self, response: str, info_dict: dict) -> None:
443
+ """Validate review response format."""
444
+ result = extract_json(response)
445
+ if not isinstance(result, dict):
446
+ raise ValueError(f"Review response is not a dict: {type(result)}")
447
+ if "suggestions" not in result:
448
+ raise ValueError("Missing 'suggestions' field")
449
+ if "conciseness_score" not in result:
450
+ raise ValueError("Missing 'conciseness_score' field")
451
+ if "logic_score" not in result:
452
+ raise ValueError("Missing 'logic_score' field")
453
+ if "specificity_score" not in result:
454
+ raise ValueError("Missing 'specificity_score' field")
455
+
456
+ # Validate score range
457
+ conciseness = result.get("conciseness_score", 0)
458
+ logic = result.get("logic_score", 0)
459
+ specificity = result.get("specificity_score", 0)
460
+ if not (0 <= conciseness <= 10):
461
+ raise ValueError(f"conciseness_score out of range: {conciseness}")
462
+ if not (0 <= logic <= 10):
463
+ raise ValueError(f"logic_score out of range: {logic}")
464
+ if not (0 <= specificity <= 10):
465
+ raise ValueError(f"specificity_score out of range: {specificity}")
466
+ return True, response
467
+
468
+ def _call_review(self, agent_ctx: AgentContext) -> Dict:
469
+ """Call review skill to get suggestions and scores."""
470
+ prompt = REVIEW_PROMPT.format(
471
+ paper_title=agent_ctx.paper_title,
472
+ paper_abstract=agent_ctx.paper_abstract,
473
+ repo_structure=format_repo_structure(agent_ctx.repo_structure),
474
+ final_pseudocode=agent_ctx.current_pseudocode
475
+ )
476
+
477
+ response = self.chat_agent.remote_chat_with_retry(
478
+ prompt,
479
+ validate_fn=self._validate_review_response,
480
+ max_retry=3,
481
+ temperature=0
482
+ )
483
+
484
+ result = extract_json(response)
485
+ if isinstance(result, dict):
486
+ suggestions = result.get("suggestions", [])
487
+ scores = {
488
+ "conciseness": result.get("conciseness_score", 0),
489
+ "logic": result.get("logic_score", 0),
490
+ "specificity": result.get("specificity_score", 0)
491
+ }
492
+ return {"suggestions": suggestions, "scores": scores}
493
+ else:
494
+ self.logger.warning(f"Review response is not a dict: {result}")
495
+ return {"suggestions": [str(result)] if result else [], "scores": {"conciseness": 0, "logic": 0, "specificity": 0}}
496
+
497
+ def _execute_operation(self, op: dict, agent_ctx: AgentContext) -> tuple:
498
+ """
499
+ Execute a single operation and return the complete memory_entry.
500
+
501
+ Returns:
502
+ Tuple of (op_type, memory_entry) where memory_entry is ready to append to agent_ctx.memory
503
+ """
504
+ operation = op.get("operation", "")
505
+ file_path = op.get("file_path", "")
506
+ reason = op.get("reason", "")
507
+
508
+ # Initialize memory_entry with operation and reason
509
+ memory_entry = {"operation": operation, "reason": reason}
510
+
511
+ if operation == "create":
512
+ # Create initial pseudocode from scratch
513
+ create_prompt = CREATE_PROMPT.format(
514
+ paper_title=agent_ctx.paper_title,
515
+ paper_abstract=agent_ctx.paper_abstract,
516
+ repo_structure=format_repo_structure(agent_ctx.repo_structure),
517
+ context=agent_ctx.context if agent_ctx.context else "No additional context retrieved yet. Create based on paper info and repo structure."
518
+ )
519
+
520
+ result = self.chat_agent.remote_chat_with_retry(create_prompt, temperature=0)
521
+ agent_ctx.current_pseudocode = result
522
+ agent_ctx.has_created_initial = True
523
+ self.logger.info(f"Created initial pseudocode, reason: {reason}")
524
+ memory_entry["pseudocode"] = result
525
+
526
+ agent_ctx.context = ""
527
+ agent_ctx.suggestions = []
528
+
529
+ elif operation == "get_source_code":
530
+ if file_path:
531
+ source_code = self._get_source_code(agent_ctx.repo_name, file_path)
532
+ agent_ctx.context += f"=== Source code of {file_path} ===\n{source_code if source_code else 'Not available'}\n"
533
+ self.logger.info(f"Retrieved source code for: {file_path}, reason: {reason}")
534
+ memory_entry["file_path"] = file_path
535
+ memory_entry["source_code_result"] = source_code if source_code else "Not available: file not exist or read failed"
536
+ memory_entry["result"] = f"Retrieved source code for {file_path}: {memory_entry['source_code_result']}"
537
+ else:
538
+ self.logger.error("get_source_code: file_path not provided, skipped")
539
+ agent_ctx.error_log.append(f"Unknown filepath in retrieve: {file_path}")
540
+ memory_entry["file_path"] = "file_path not provided corrctly"
541
+ memory_entry["source_code_result"] = "file_path not provided"
542
+ memory_entry["result"] = f"Retrieved source code for {file_path}: {memory_entry['source_code_result']}"
543
+
544
+ elif operation == "revise":
545
+ # Check if create has been called
546
+ if not agent_ctx.has_created_initial:
547
+ self.logger.warning("revise called before create - calling create first")
548
+ create_op = {"operation": "create", "reason": "Forced create before revise"}
549
+ create_type, create_memory_entry = self._execute_operation(create_op, agent_ctx)
550
+ agent_ctx.memory.append(create_memory_entry)
551
+ # Note: first_created_pseudocode should be updated by the caller if needed
552
+
553
+ modify_prompt = MODIFY_PROMPT.format(
554
+ paper_title=agent_ctx.paper_title,
555
+ paper_abstract=agent_ctx.paper_abstract,
556
+ repo_structure=format_repo_structure(agent_ctx.repo_structure),
557
+ final_pseudocode=agent_ctx.current_pseudocode,
558
+ context=agent_ctx.context,
559
+ suggestions="\n".join(agent_ctx.suggestions) if agent_ctx.suggestions else "No suggestion yet.",
560
+ reason=reason,
561
+ )
562
+ result = self.chat_agent.remote_chat_with_retry(modify_prompt, temperature=0)
563
+ agent_ctx.current_pseudocode = result
564
+ memory_entry["pseudocode"] = result
565
+ memory_entry["result"] = f"Revised pseudocode: {memory_entry['pseudocode']}"
566
+ self.logger.info(f"Revised pseudocode, reason: {reason}")
567
+
568
+ agent_ctx.context = ""
569
+ agent_ctx.suggestions = []
570
+
571
+ elif operation == "finish":
572
+ self.logger.info(f"Operation: finish, reason: {reason}")
573
+ self.logger.info("Agent decided to finish")
574
+ # Store final context and suggestions for finish
575
+ if agent_ctx.context or agent_ctx.suggestions:
576
+ memory_entry["context"] = agent_ctx.context
577
+ memory_entry["suggestions"] = agent_ctx.suggestions.copy()
578
+
579
+ elif operation == "review":
580
+ review_result = self._call_review(agent_ctx)
581
+ suggestions = review_result.get("suggestions", [])
582
+ scores = review_result.get("scores", {"conciseness": 0, "logic": 0, "specificity": 0})
583
+ agent_ctx.suggestions.extend(suggestions)
584
+ agent_ctx.review_scores = scores
585
+ self.logger.info(f"Review scores: conciseness={agent_ctx.review_scores['conciseness']}, logic={agent_ctx.review_scores['logic']}, specificity={agent_ctx.review_scores['specificity']}")
586
+ self.logger.info(f"reason: {reason}")
587
+
588
+ memory_entry["scores"] = scores
589
+ memory_entry["suggestions"] = suggestions.copy()
590
+ memory_entry["result"] = f"Scores: {memory_entry['scores']}, Suggestions: {memory_entry['suggestions']}"
591
+
592
+ else:
593
+ self.logger.error(f"Unknown operation: {operation}, skipped")
594
+ agent_ctx.error_log.append(f"Unknown operation: {operation}, skipped")
595
+ memory_entry["result"] = f"Unknown operation: {operation}, skipped"
596
+
597
+ return operation, memory_entry
598
+
599
+ def create_pseudocode_with_agent(
600
+ self,
601
+ repo_name: str,
602
+ paper_title: str,
603
+ paper_abstract: str,
604
+ repo_structure: Dict,
605
+ initial_pseudocode: str = "",
606
+ max_steps: int = None,
607
+ hard_code_revise: bool = False,
608
+ max_rounds_without_revise: int = 3,
609
+ last_round_revise: bool = True,
610
+ ) -> tuple:
611
+ """
612
+ Create pseudocode using agent-based approach with plan-based execution.
613
+
614
+ Args:
615
+ repo_name: Repository name
616
+ paper_title: Title of the paper
617
+ paper_abstract: Abstract of the paper
618
+ repo_structure: Repository structure as a nested dict
619
+ initial_pseudocode: Optional initial pseudocode (can be empty)
620
+ max_steps: Maximum number of agent steps
621
+ hard_code_revise: If True, force call revise after max_rounds_without_revise rounds without revise
622
+ max_rounds_without_revise: Maximum rounds without revise before forcing (default: 3)
623
+ last_round_revise: If True, force revise in last round
624
+
625
+ Returns:
626
+ Tuple of (final_pseudocode, initial_pseudocode) where initial_pseudocode is the first created pseudocode before any revision
627
+ """
628
+
629
+ # Build AgentContext
630
+ agent_ctx = AgentContext(
631
+ repo_name=repo_name,
632
+ paper_title=paper_title,
633
+ paper_abstract=paper_abstract,
634
+ repo_structure=repo_structure,
635
+ current_pseudocode=initial_pseudocode,
636
+ memory=[],
637
+ suggestions=[],
638
+ context="",
639
+ error_log=[],
640
+ review_scores={"conciseness": 0, "logic": 0, "specificity": 0},
641
+ has_created_initial=bool(initial_pseudocode)
642
+ )
643
+
644
+ max_steps = max_steps or self.default_max_steps
645
+
646
+ # Track rounds since last revise
647
+ rounds_since_last_revise = 0
648
+
649
+ # Store the first created pseudocode (before any revision)
650
+ first_created_pseudocode = ""
651
+
652
+ self.logger.info(f"Starting agent-based pseudocode creation (max {max_steps} steps)")
653
+
654
+ # Initialize: Read README before first planning round if enabled
655
+ if self.read_readme_first:
656
+ readme_path = self._find_readme_path(repo_structure)
657
+ if readme_path:
658
+ self.logger.info(f"Reading README file: {readme_path}")
659
+ # Use _execute_operation to handle context and memory automatically
660
+ readme_op = {"operation": "get_source_code", "file_path": readme_path, "reason": "Initial README read"}
661
+ _, readme_memory_entry = self._execute_operation(readme_op, agent_ctx)
662
+ agent_ctx.memory.append(readme_memory_entry)
663
+ self.logger.info(f"README content added to context and memory")
664
+ else:
665
+ self.logger.info(f"No README file found in repository structure")
666
+
667
+ for step in range(max_steps):
668
+ self.logger.info(f"Agent step {step + 1}/{max_steps}")
669
+
670
+ # Check memory compression before building prompt
671
+ self._check_memory_compression(agent_ctx)
672
+
673
+ # Format the prompt
674
+ prompt = AGENT_OPERATE_PROMPT.format(
675
+ repo_name=agent_ctx.repo_name,
676
+ paper_title=agent_ctx.paper_title,
677
+ paper_abstract=agent_ctx.paper_abstract,
678
+ repo_structure=format_repo_structure(agent_ctx.repo_structure),
679
+ current_pseudocode=agent_ctx.current_pseudocode if agent_ctx.current_pseudocode else "(No pseudocode yet - use create operation)",
680
+ memory=self._format_memory(agent_ctx.memory),
681
+ suggestions="\n".join(agent_ctx.suggestions) if agent_ctx.suggestions else "No suggestion yet.",
682
+ context=agent_ctx.context if agent_ctx.context else "No context retrieved yet."
683
+ )
684
+
685
+ # Get agent's plan
686
+ try:
687
+ plan = self.chat_agent.remote_chat_with_retry(
688
+ prompt=prompt,
689
+ validate_fn=self._validate_plan,
690
+ max_retry= self.config.ModuleInfo.CodeAnalysis.PseudoCreator.planner_max_retry,
691
+ temperature=self.config.ModuleInfo.CodeAnalysis.PseudoCreator.planner_temperature
692
+ )
693
+ except Exception as e:
694
+ # Escape any markup-like content in the exception to avoid Rich markup errors
695
+ error_str = str(e).replace('[', r'\[').replace(']', r'\]')
696
+ self.logger.error(f"Failed to parse agent response: {error_str}..., skipped this round")
697
+ agent_ctx.error_log.append(f"error in parsing planning json: {e}")
698
+ continue
699
+
700
+ # Execute each operation in the plan
701
+ for op in plan:
702
+ try:
703
+ # Execute operation and get complete memory_entry directly
704
+ op_type, memory_entry = self._execute_operation(op, agent_ctx)
705
+ agent_ctx.memory.append(memory_entry)
706
+
707
+ # Check for finish - handled in _execute_operation, just log and break here
708
+ if op_type == "finish":
709
+ self.logger.info("Agent decided to finish")
710
+ break
711
+ except Exception as e:
712
+ # If an operation fails, log error and skip this operation but continue with next
713
+ self.logger.error(f"Failed to execute operation '{op.get('operation', 'unknown')}': {e}. Skipping this operation.")
714
+ agent_ctx.error_log.append(f"Operation '{op.get('operation', 'unknown')}' failed: {e}")
715
+ continue # Continue with next operation in plan
716
+
717
+ # Track revise operations
718
+ if op_type == "revise":
719
+ rounds_since_last_revise = 0
720
+ else:
721
+ rounds_since_last_revise += 1
722
+
723
+ # Track first created pseudocode for return value
724
+ if op_type == "create" and not first_created_pseudocode:
725
+ first_created_pseudocode = memory_entry.get("pseudocode", "")
726
+
727
+ # Also check if create was called internally by revise operation
728
+ # by looking at the memory entries after a revise operation
729
+ if op_type == "revise" and not first_created_pseudocode:
730
+ # Find the most recent create operation in memory after the revise call
731
+ for memory_item in reversed(agent_ctx.memory):
732
+ if memory_item.get("operation") == "create" and memory_item.get("pseudocode"):
733
+ first_created_pseudocode = memory_item.get("pseudocode", "")
734
+ break
735
+
736
+ # Check if we need to force revise
737
+ if hard_code_revise and rounds_since_last_revise >= max_rounds_without_revise:
738
+ self.logger.info(f"Force calling revise after {rounds_since_last_revise} rounds without revise")
739
+ force_revise_op = {"operation": "revise", "reason": "Forced revise after max rounds without revise"}
740
+ _, force_memory_entry = self._execute_operation(force_revise_op, agent_ctx)
741
+ force_memory_entry["reason"] = "Forced revise (hard_code)"
742
+ agent_ctx.memory.append(force_memory_entry)
743
+ rounds_since_last_revise = 0
744
+ continue
745
+
746
+ if step + 1 == max_steps:
747
+ if last_round_revise and rounds_since_last_revise > 0:
748
+ self.logger.info("Last round does not include revise, force calling revise")
749
+ force_revise_op = {"operation": "revise", "reason": "Forced revise in last round"}
750
+ _, force_memory_entry = self._execute_operation(force_revise_op, agent_ctx)
751
+ force_memory_entry["reason"] = "Forced revise (last round)"
752
+ agent_ctx.memory.append(force_memory_entry)
753
+ rounds_since_last_revise = 0
754
+
755
+ # Check if finished
756
+ if any(m.get("operation") == "finish" for m in agent_ctx.memory[-len(plan):] if plan):
757
+ self.logger.info(f"Planning agent decided to finish at step {step}")
758
+ break
759
+
760
+ # self.logger.info(f"[Memory Debug] Final memory after agent execution:\n{self._format_memory(agent_ctx.memory)}")
761
+ self.logger.info(f"Agent-based creation completed after {step + 1} steps")
762
+
763
+ if not agent_ctx.current_pseudocode or not first_created_pseudocode:
764
+ self.logger.error(f"Agents fail to generate pseudocode for {agent_ctx.repo_name}")
765
+ raise ValueError(f"Agents fail to generate pseudocode for {agent_ctx.repo_name}")
766
+
767
+ # Return both final pseudocode and the first created pseudocode (initial)
768
+ return agent_ctx.current_pseudocode, first_created_pseudocode
769
+
770
+ def _validate_plan(self, response: str, info_dict: dict = None):
771
+ result = extract_json(response)
772
+ if not isinstance(result, dict):
773
+ raise ValueError(f"Response is not a dict: {type(result)}")
774
+ if "plan" not in result:
775
+ raise ValueError("Missing 'plan' field in response")
776
+ plan = result.get("plan")
777
+ if not isinstance(plan, list):
778
+ raise ValueError("'plan' field is not a list")
779
+ for op in plan:
780
+ if "operation" not in op:
781
+ raise ValueError("Each operation must have an 'operation' field")
782
+ return True, plan