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.
- qwen_sci-0.2.2.dist-info/METADATA +492 -0
- qwen_sci-0.2.2.dist-info/RECORD +549 -0
- qwen_sci-0.2.2.dist-info/WHEEL +4 -0
- qwen_sci-0.2.2.dist-info/entry_points.txt +6 -0
- qwen_sci-0.2.2.dist-info/licenses/LICENSE +21 -0
- src/1.txt +1 -0
- src/__init__.py +1 -0
- src/__main__.py +5 -0
- src/agents/__init__.py +1 -0
- src/agents/experiment_design_agent/__init__.py +268 -0
- src/agents/experiment_design_agent/artifacts.py +747 -0
- src/agents/experiment_design_agent/cache.py +319 -0
- src/agents/experiment_design_agent/completeness.py +130 -0
- src/agents/experiment_design_agent/contracts.py +771 -0
- src/agents/experiment_design_agent/counterexample_analyzer.py +187 -0
- src/agents/experiment_design_agent/design_evidence_paper_screener.py +470 -0
- src/agents/experiment_design_agent/discipline_catalog.py +178 -0
- src/agents/experiment_design_agent/evidence_cards.py +338 -0
- src/agents/experiment_design_agent/evidence_planner.py +452 -0
- src/agents/experiment_design_agent/formal_reasoning_planner.py +884 -0
- src/agents/experiment_design_agent/fulltext_acquisition.py +863 -0
- src/agents/experiment_design_agent/idea_adapter.py +100 -0
- src/agents/experiment_design_agent/idea_intake.py +87 -0
- src/agents/experiment_design_agent/llm_json.py +328 -0
- src/agents/experiment_design_agent/orchestrator.py +1220 -0
- src/agents/experiment_design_agent/reasoning_context.py +183 -0
- src/agents/experiment_design_agent/reasoning_validation.py +546 -0
- src/agents/experiment_design_agent/run.py +481 -0
- src/agents/experiment_design_agent/run_logging.py +292 -0
- src/agents/experiment_design_agent/scope_gate.py +198 -0
- src/agents/experiment_design_agent/study_type_composer.py +1360 -0
- src/agents/experiment_design_agent/survey_evidence.py +1381 -0
- src/agents/experiment_design_agent/template_router.py +195 -0
- src/agents/experiment_design_agent/variable_claim_extractor.py +93 -0
- src/agents/idea_agent/__init__.py +1 -0
- src/agents/idea_agent/agent/__init__.py +6 -0
- src/agents/idea_agent/agent/artifacts.py +308 -0
- src/agents/idea_agent/agent/base.py +122 -0
- src/agents/idea_agent/agent/ligagent.py +488 -0
- src/agents/idea_agent/agent/mcts.py +2060 -0
- src/agents/idea_agent/agent/prompts/__init__.py +48 -0
- src/agents/idea_agent/agent/prompts/advanced_analysis.py +235 -0
- src/agents/idea_agent/agent/prompts/algorithm_alignment.py +55 -0
- src/agents/idea_agent/agent/prompts/algorithm_structuring.py +48 -0
- src/agents/idea_agent/agent/prompts/component_extraction.py +62 -0
- src/agents/idea_agent/agent/prompts/component_novelty_evaluation.py +54 -0
- src/agents/idea_agent/agent/prompts/experiment_findings_extraction.py +39 -0
- src/agents/idea_agent/agent/prompts/idea_fusion.py +295 -0
- src/agents/idea_agent/agent/prompts/idea_introduction.py +30 -0
- src/agents/idea_agent/agent/prompts/idea_result_alignment.py +39 -0
- src/agents/idea_agent/agent/prompts/input_interpreter.py +52 -0
- src/agents/idea_agent/agent/prompts/keynote_ops.py +78 -0
- src/agents/idea_agent/agent/prompts/mcts_evaluation.py +235 -0
- src/agents/idea_agent/agent/prompts/mcts_generation.py +140 -0
- src/agents/idea_agent/agent/prompts/mechanism_commit_query.py +99 -0
- src/agents/idea_agent/agent/prompts/memory_selection.py +0 -0
- src/agents/idea_agent/agent/prompts/prompt_modes.py +16 -0
- src/agents/idea_agent/agent/prompts/rag_query.py +34 -0
- src/agents/idea_agent/agent/prompts/re_analysis_replan.py +48 -0
- src/agents/idea_agent/agent/prompts/reference_grounding.py +34 -0
- src/agents/idea_agent/agent/prompts/root_domain_classification.py +32 -0
- src/agents/idea_agent/agent/prompts/scientific_debate.py +88 -0
- src/agents/idea_agent/agent/prompts/scientific_materialization.py +45 -0
- src/agents/idea_agent/agent/prompts/skill_instantiation.py +193 -0
- src/agents/idea_agent/agent/prompts/theory_transfer_query.py +93 -0
- src/agents/idea_agent/agent/prompts/topic_background.py +15 -0
- src/agents/idea_agent/agent/skills/DEFAULT_SKILL_TEMPLATES.json +263 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/alternative-path-contrast/SKILL.md +40 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/alternative-path-contrast/references/contrast_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/feedback-closed-loop/SKILL.md +40 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/feedback-closed-loop/references/mechanism_cards.md +24 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/hierarchical-decomposition/SKILL.md +39 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/hierarchical-decomposition/references/hierarchy_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/mechanism-commit-innovation/SKILL.md +39 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/mechanism-commit-innovation/references/mechanism_commit_patterns.md +24 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/multi-scale-coordinator/SKILL.md +39 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/multi-scale-coordinator/references/coordination_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/speculative-execution-with-repair/SKILL.md +41 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/speculative-execution-with-repair/references/speculation_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/surgical-modularity/SKILL.md +39 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/surgical-modularity/references/modularity_patterns.md +22 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/theory-transfer-injection/SKILL.md +40 -0
- src/agents/idea_agent/agent/skills/edit_operator_skills/theory-transfer-injection/references/transfer_patterns.md +24 -0
- src/agents/idea_agent/example/idea_result.json +900 -0
- src/agents/idea_agent/run.py +276 -0
- src/agents/idea_agent/tests/test_advanced_analysis_contract.py +90 -0
- src/agents/idea_agent/tests/test_gap_hypothesis_bridge.py +166 -0
- src/agents/idea_agent/tests/test_idea_debate.py +480 -0
- src/agents/idea_agent/tests/test_idea_result_v5.py +99 -0
- src/agents/idea_agent/tests/test_idea_synthesis.py +92 -0
- src/agents/idea_agent/tests/test_mature_idea_sources.py +27 -0
- src/agents/idea_agent/tests/test_multimodal_idea_integration.py +386 -0
- src/agents/idea_agent/tests/test_profile_materialization_and_scoring.py +219 -0
- src/agents/idea_agent/tests/test_profile_native_skill_rendering.py +122 -0
- src/agents/idea_agent/tests/test_result_persistence.py +183 -0
- src/agents/idea_agent/tests/test_scientific_intervention_profiles.py +241 -0
- src/agents/idea_agent/tests/test_scientific_rubric_and_prompts.py +559 -0
- src/agents/idea_agent/tests/test_strict_json_object_responses.py +48 -0
- src/agents/idea_agent/utils/core/__init__.py +1 -0
- src/agents/idea_agent/utils/core/ablation_inputs.py +60 -0
- src/agents/idea_agent/utils/core/chat_errors.py +95 -0
- src/agents/idea_agent/utils/core/chat_router.py +130 -0
- src/agents/idea_agent/utils/core/chat_transport.py +188 -0
- src/agents/idea_agent/utils/core/config_loader.py +166 -0
- src/agents/idea_agent/utils/core/json_utils.py +28 -0
- src/agents/idea_agent/utils/core/logger.py +287 -0
- src/agents/idea_agent/utils/core/progress.py +58 -0
- src/agents/idea_agent/utils/core/response_parsing.py +57 -0
- src/agents/idea_agent/utils/core/run_inputs.py +198 -0
- src/agents/idea_agent/utils/mcts/__init__.py +1 -0
- src/agents/idea_agent/utils/mcts/component_novelty.py +342 -0
- src/agents/idea_agent/utils/mcts/defect_registry.py +302 -0
- src/agents/idea_agent/utils/mcts/idea_routes.py +61 -0
- src/agents/idea_agent/utils/mcts/idea_taste_presets.py +312 -0
- src/agents/idea_agent/utils/mcts/mcts_helpers.py +738 -0
- src/agents/idea_agent/utils/mcts/mcts_runtime.py +3000 -0
- src/agents/idea_agent/utils/mcts/scientific_intervention_ontology.py +1561 -0
- src/agents/idea_agent/utils/mcts/scientific_rubric.py +211 -0
- src/agents/idea_agent/utils/mcts/skill_parsing.py +92 -0
- src/agents/idea_agent/utils/papers/__init__.py +1 -0
- src/agents/idea_agent/utils/papers/paper_graph_vector_store.py +499 -0
- src/agents/idea_agent/utils/papers/paper_repository.py +372 -0
- src/agents/idea_agent/utils/prompting/__init__.py +1 -0
- src/agents/idea_agent/utils/prompting/prompt_views.py +637 -0
- src/agents/idea_agent/utils/workflow/__init__.py +1 -0
- src/agents/idea_agent/utils/workflow/advanced_analysis_contract.py +244 -0
- src/agents/idea_agent/utils/workflow/gap_hypothesis_bridge.py +461 -0
- src/agents/idea_agent/utils/workflow/idea_contract.py +426 -0
- src/agents/idea_agent/utils/workflow/idea_debate.py +1238 -0
- src/agents/idea_agent/utils/workflow/idea_diversity.py +163 -0
- src/agents/idea_agent/utils/workflow/idea_fusion.py +889 -0
- src/agents/idea_agent/utils/workflow/idea_helpers.py +478 -0
- src/agents/idea_agent/utils/workflow/idea_portfolio.py +422 -0
- src/agents/idea_agent/utils/workflow/idea_synthesis.py +482 -0
- src/agents/idea_agent/utils/workflow/ligagent_flow.py +581 -0
- src/agents/idea_agent/utils/workflow/ligagent_handlers.py +1836 -0
- src/agents/idea_agent/utils/workflow/ligagent_helpers.py +1331 -0
- src/agents/idea_agent/utils/workflow/ligagent_utils.py +322 -0
- src/agents/idea_agent/utils/workflow/mature_idea_sources.py +648 -0
- src/agents/idea_agent/utils/workflow/multimodal_data_anchoring.py +578 -0
- src/agents/idea_agent/utils/workflow/result_persistence.py +774 -0
- src/agents/idea_agent/utils/workflow/stage_contract.py +76 -0
- src/agents/idea_agent/utils/workflow/workflow_runtime.py +288 -0
- src/agents/research_plan_author/__init__.py +50 -0
- src/agents/research_plan_author/artifacts.py +270 -0
- src/agents/research_plan_author/authoring_blueprint.py +1246 -0
- src/agents/research_plan_author/bibtex_renderer.py +252 -0
- src/agents/research_plan_author/contract_repair.py +131 -0
- src/agents/research_plan_author/contracts.py +745 -0
- src/agents/research_plan_author/cross_section_editor.py +232 -0
- src/agents/research_plan_author/document_quality.py +987 -0
- src/agents/research_plan_author/idea_evolution.py +223 -0
- src/agents/research_plan_author/input_loader.py +76 -0
- src/agents/research_plan_author/latex_compiler.py +260 -0
- src/agents/research_plan_author/latex_safety.py +159 -0
- src/agents/research_plan_author/llm_json.py +74 -0
- src/agents/research_plan_author/markdown_renderer.py +275 -0
- src/agents/research_plan_author/pdf_validator.py +149 -0
- src/agents/research_plan_author/render.py +166 -0
- src/agents/research_plan_author/render_artifacts.py +187 -0
- src/agents/research_plan_author/run.py +765 -0
- src/agents/research_plan_author/run_logging.py +209 -0
- src/agents/research_plan_author/section_cache.py +184 -0
- src/agents/research_plan_author/section_composer.py +1764 -0
- src/agents/research_plan_author/section_router.py +209 -0
- src/agents/research_plan_author/semantic_validator.py +254 -0
- src/agents/research_plan_author/source_bundle.py +46 -0
- src/agents/research_plan_author/source_registry.py +208 -0
- src/agents/research_plan_author/survey_source_loader.py +137 -0
- src/agents/research_plan_author/template_adapter.py +173 -0
- src/agents/research_plan_author/template_profile.py +204 -0
- src/agents/research_plan_author/tex_renderer.py +496 -0
- src/agents/research_plan_author/theory_presentation.py +134 -0
- src/agents/research_plan_author/theory_spine.py +703 -0
- src/agents/survey_agent/Dockerfile +54 -0
- src/agents/survey_agent/README.md +350 -0
- src/agents/survey_agent/config/deep_survey.yaml +217 -0
- src/agents/survey_agent/config/deep_survey_batch.yaml +176 -0
- src/agents/survey_agent/config/deep_survey_fast.yaml +183 -0
- src/agents/survey_agent/config/evaluate_baseline.yaml +197 -0
- src/agents/survey_agent/config/outcomeRAG.yaml +159 -0
- src/agents/survey_agent/env_report.md +348 -0
- src/agents/survey_agent/modules/__init__.py +0 -0
- src/agents/survey_agent/modules/code_collector.py +2194 -0
- src/agents/survey_agent/modules/code_report_generator.py +1282 -0
- src/agents/survey_agent/modules/data_manager.py +1713 -0
- src/agents/survey_agent/modules/database.py +167 -0
- src/agents/survey_agent/modules/fulltext_download_cache.py +462 -0
- src/agents/survey_agent/modules/fulltext_http.py +134 -0
- src/agents/survey_agent/modules/fulltext_resolution.py +749 -0
- src/agents/survey_agent/modules/judge.py +434 -0
- src/agents/survey_agent/modules/outcome_RAG.py +236 -0
- src/agents/survey_agent/modules/paper_graph_retriever.py +1075 -0
- src/agents/survey_agent/modules/pe.py +1729 -0
- src/agents/survey_agent/modules/pseudo_creator.py +782 -0
- src/agents/survey_agent/modules/pseudo_reviser.py +537 -0
- src/agents/survey_agent/modules/refine_agent.py +1952 -0
- src/agents/survey_agent/modules/survey_generator.py +5691 -0
- src/agents/survey_agent/modules/survey_visualizer.py +1817 -0
- src/agents/survey_agent/modules/visual_prompts.py +327 -0
- src/agents/survey_agent/modules/work_analyzer.py +2750 -0
- src/agents/survey_agent/modules/work_collector.py +5649 -0
- src/agents/survey_agent/scripts/baseline_evaluation.py +411 -0
- src/agents/survey_agent/scripts/run_deep_survey.py +224 -0
- src/agents/survey_agent/scripts/run_deep_survey_adapter.py +407 -0
- src/agents/survey_agent/scripts/run_deep_survey_adapter_with_input.py +409 -0
- src/agents/survey_agent/scripts/run_deep_survey_batch.py +228 -0
- src/agents/survey_agent/scripts/run_deep_survey_batch_arg.py +243 -0
- src/agents/survey_agent/scripts/run_deep_survey_batch_settings.py +218 -0
- src/agents/survey_agent/scripts/run_keynotes_gen.py +128 -0
- src/agents/survey_agent/scripts/run_work_clusters.py +140 -0
- src/agents/survey_agent/utils/__init__.py +0 -0
- src/agents/survey_agent/utils/ablation_utils.py +317 -0
- src/agents/survey_agent/utils/api_call.py +2518 -0
- src/agents/survey_agent/utils/chat_utils.py +82 -0
- src/agents/survey_agent/utils/config_utils.py +70 -0
- src/agents/survey_agent/utils/convert_to_md.py +29 -0
- src/agents/survey_agent/utils/err_info.py +24 -0
- src/agents/survey_agent/utils/file_utils.py +176 -0
- src/agents/survey_agent/utils/gpu_utils.py +145 -0
- src/agents/survey_agent/utils/graph_visualizer.py +279 -0
- src/agents/survey_agent/utils/html_utils.py +181 -0
- src/agents/survey_agent/utils/latex_generator_intra_cluster.py +529 -0
- src/agents/survey_agent/utils/latex_generator_relation_table.py +193 -0
- src/agents/survey_agent/utils/mineru_section_packer.py +334 -0
- src/agents/survey_agent/utils/mineru_utils.py +396 -0
- src/agents/survey_agent/utils/paper.py +144 -0
- src/agents/survey_agent/utils/read_yaml.py +15 -0
- src/agents/survey_agent/utils/repo_utils.py +167 -0
- src/agents/survey_agent/utils/rich_logger.py +77 -0
- src/agents/survey_agent/utils/step2v2.py +874 -0
- src/agents/survey_agent/utils/step2v2_extractor.py +856 -0
- src/agents/survey_agent/utils/topic_survey_storage.py +309 -0
- src/agents/survey_agent/utils/utils.py +118 -0
- src/agents/survey_agent/utils/work.py +2 -0
- src/cli.py +1611 -0
- src/config/__init__.py +213 -0
- src/config/default.yaml +1122 -0
- src/harness/LICENSE +21 -0
- src/harness/VENDORED.md +14 -0
- src/harness/src/__init__.py +0 -0
- src/harness/src/openharness/__init__.py +0 -0
- src/harness/src/openharness/__main__.py +6 -0
- src/harness/src/openharness/api/__init__.py +21 -0
- src/harness/src/openharness/api/client.py +281 -0
- src/harness/src/openharness/api/codex_client.py +407 -0
- src/harness/src/openharness/api/copilot_auth.py +240 -0
- src/harness/src/openharness/api/copilot_client.py +134 -0
- src/harness/src/openharness/api/errors.py +19 -0
- src/harness/src/openharness/api/openai_client.py +577 -0
- src/harness/src/openharness/api/provider.py +186 -0
- src/harness/src/openharness/api/registry.py +437 -0
- src/harness/src/openharness/api/usage.py +17 -0
- src/harness/src/openharness/auth/__init__.py +29 -0
- src/harness/src/openharness/auth/external.py +610 -0
- src/harness/src/openharness/auth/flows.py +189 -0
- src/harness/src/openharness/auth/manager.py +482 -0
- src/harness/src/openharness/auth/storage.py +269 -0
- src/harness/src/openharness/autopilot/__init__.py +23 -0
- src/harness/src/openharness/autopilot/service.py +2239 -0
- src/harness/src/openharness/autopilot/types.py +91 -0
- src/harness/src/openharness/bridge/__init__.py +20 -0
- src/harness/src/openharness/bridge/manager.py +106 -0
- src/harness/src/openharness/bridge/session_runner.py +46 -0
- src/harness/src/openharness/bridge/types.py +37 -0
- src/harness/src/openharness/bridge/work_secret.py +41 -0
- src/harness/src/openharness/channels/UPSTREAM +6 -0
- src/harness/src/openharness/channels/__init__.py +22 -0
- src/harness/src/openharness/channels/adapter.py +131 -0
- src/harness/src/openharness/channels/bus/__init__.py +6 -0
- src/harness/src/openharness/channels/bus/events.py +38 -0
- src/harness/src/openharness/channels/bus/queue.py +44 -0
- src/harness/src/openharness/channels/impl/__init__.py +6 -0
- src/harness/src/openharness/channels/impl/base.py +141 -0
- src/harness/src/openharness/channels/impl/dingtalk.py +445 -0
- src/harness/src/openharness/channels/impl/discord.py +311 -0
- src/harness/src/openharness/channels/impl/email.py +410 -0
- src/harness/src/openharness/channels/impl/feishu.py +1342 -0
- src/harness/src/openharness/channels/impl/manager.py +257 -0
- src/harness/src/openharness/channels/impl/matrix.py +700 -0
- src/harness/src/openharness/channels/impl/mochat.py +897 -0
- src/harness/src/openharness/channels/impl/qq.py +141 -0
- src/harness/src/openharness/channels/impl/slack.py +284 -0
- src/harness/src/openharness/channels/impl/telegram.py +525 -0
- src/harness/src/openharness/channels/impl/whatsapp.py +159 -0
- src/harness/src/openharness/cli.py +2551 -0
- src/harness/src/openharness/commands/__init__.py +21 -0
- src/harness/src/openharness/commands/registry.py +2783 -0
- src/harness/src/openharness/config/__init__.py +34 -0
- src/harness/src/openharness/config/paths.py +160 -0
- src/harness/src/openharness/config/schema.py +119 -0
- src/harness/src/openharness/config/settings.py +1104 -0
- src/harness/src/openharness/coordinator/__init__.py +12 -0
- src/harness/src/openharness/coordinator/agent_definitions.py +975 -0
- src/harness/src/openharness/coordinator/coordinator_mode.py +520 -0
- src/harness/src/openharness/engine/__init__.py +80 -0
- src/harness/src/openharness/engine/cost_tracker.py +24 -0
- src/harness/src/openharness/engine/messages.py +222 -0
- src/harness/src/openharness/engine/query.py +1188 -0
- src/harness/src/openharness/engine/query_engine.py +313 -0
- src/harness/src/openharness/engine/stream_events.py +90 -0
- src/harness/src/openharness/hooks/__init__.py +50 -0
- src/harness/src/openharness/hooks/events.py +20 -0
- src/harness/src/openharness/hooks/executor.py +242 -0
- src/harness/src/openharness/hooks/hot_reload.py +31 -0
- src/harness/src/openharness/hooks/loader.py +68 -0
- src/harness/src/openharness/hooks/schemas.py +66 -0
- src/harness/src/openharness/hooks/types.py +38 -0
- src/harness/src/openharness/keybindings/__init__.py +14 -0
- src/harness/src/openharness/keybindings/default_bindings.py +11 -0
- src/harness/src/openharness/keybindings/loader.py +22 -0
- src/harness/src/openharness/keybindings/parser.py +18 -0
- src/harness/src/openharness/keybindings/resolver.py +13 -0
- src/harness/src/openharness/mcp/__init__.py +79 -0
- src/harness/src/openharness/mcp/client.py +298 -0
- src/harness/src/openharness/mcp/config.py +16 -0
- src/harness/src/openharness/mcp/types.py +76 -0
- src/harness/src/openharness/memory/__init__.py +25 -0
- src/harness/src/openharness/memory/agent.py +91 -0
- src/harness/src/openharness/memory/manager.py +183 -0
- src/harness/src/openharness/memory/memdir.py +52 -0
- src/harness/src/openharness/memory/migrate.py +137 -0
- src/harness/src/openharness/memory/paths.py +22 -0
- src/harness/src/openharness/memory/relevance.py +145 -0
- src/harness/src/openharness/memory/scan.py +116 -0
- src/harness/src/openharness/memory/schema.py +443 -0
- src/harness/src/openharness/memory/search.py +71 -0
- src/harness/src/openharness/memory/team.py +90 -0
- src/harness/src/openharness/memory/types.py +33 -0
- src/harness/src/openharness/memory/usage.py +153 -0
- src/harness/src/openharness/output_styles/__init__.py +5 -0
- src/harness/src/openharness/output_styles/loader.py +42 -0
- src/harness/src/openharness/permissions/__init__.py +26 -0
- src/harness/src/openharness/permissions/checker.py +200 -0
- src/harness/src/openharness/permissions/modes.py +13 -0
- src/harness/src/openharness/personalization/__init__.py +6 -0
- src/harness/src/openharness/personalization/extractor.py +138 -0
- src/harness/src/openharness/personalization/rules.py +64 -0
- src/harness/src/openharness/personalization/session_hook.py +65 -0
- src/harness/src/openharness/platforms.py +86 -0
- src/harness/src/openharness/plugins/__init__.py +53 -0
- src/harness/src/openharness/plugins/bundled/__init__.py +0 -0
- src/harness/src/openharness/plugins/installer.py +39 -0
- src/harness/src/openharness/plugins/loader.py +730 -0
- src/harness/src/openharness/plugins/schemas.py +24 -0
- src/harness/src/openharness/plugins/types.py +59 -0
- src/harness/src/openharness/prompts/__init__.py +14 -0
- src/harness/src/openharness/prompts/claudemd.py +48 -0
- src/harness/src/openharness/prompts/context.py +187 -0
- src/harness/src/openharness/prompts/environment.py +135 -0
- src/harness/src/openharness/prompts/system_prompt.py +109 -0
- src/harness/src/openharness/sandbox/Dockerfile +6 -0
- src/harness/src/openharness/sandbox/__init__.py +33 -0
- src/harness/src/openharness/sandbox/adapter.py +148 -0
- src/harness/src/openharness/sandbox/docker_backend.py +232 -0
- src/harness/src/openharness/sandbox/docker_image.py +103 -0
- src/harness/src/openharness/sandbox/path_validator.py +37 -0
- src/harness/src/openharness/sandbox/session.py +63 -0
- src/harness/src/openharness/services/__init__.py +30 -0
- src/harness/src/openharness/services/autodream/__init__.py +34 -0
- src/harness/src/openharness/services/autodream/backup.py +104 -0
- src/harness/src/openharness/services/autodream/lock.py +138 -0
- src/harness/src/openharness/services/autodream/prompt.py +128 -0
- src/harness/src/openharness/services/autodream/service.py +313 -0
- src/harness/src/openharness/services/compact/__init__.py +1725 -0
- src/harness/src/openharness/services/cron.py +137 -0
- src/harness/src/openharness/services/cron_scheduler.py +595 -0
- src/harness/src/openharness/services/lsp/__init__.py +216 -0
- src/harness/src/openharness/services/memory_extract/__init__.py +261 -0
- src/harness/src/openharness/services/oauth/__init__.py +0 -0
- src/harness/src/openharness/services/session_backend.py +97 -0
- src/harness/src/openharness/services/session_memory/__init__.py +139 -0
- src/harness/src/openharness/services/session_storage.py +230 -0
- src/harness/src/openharness/services/token_estimation.py +15 -0
- src/harness/src/openharness/services/tool_outputs.py +55 -0
- src/harness/src/openharness/skills/__init__.py +44 -0
- src/harness/src/openharness/skills/_frontmatter.py +97 -0
- src/harness/src/openharness/skills/bundled/__init__.py +75 -0
- src/harness/src/openharness/skills/bundled/content/commit.md +25 -0
- src/harness/src/openharness/skills/bundled/content/debug.md +25 -0
- src/harness/src/openharness/skills/bundled/content/diagnose.md +35 -0
- src/harness/src/openharness/skills/bundled/content/plan.md +39 -0
- src/harness/src/openharness/skills/bundled/content/review.md +33 -0
- src/harness/src/openharness/skills/bundled/content/simplify.md +26 -0
- src/harness/src/openharness/skills/bundled/content/skill-creator.md +175 -0
- src/harness/src/openharness/skills/bundled/content/test.md +38 -0
- src/harness/src/openharness/skills/loader.py +229 -0
- src/harness/src/openharness/skills/registry.py +29 -0
- src/harness/src/openharness/skills/types.py +24 -0
- src/harness/src/openharness/state/__init__.py +6 -0
- src/harness/src/openharness/state/app_state.py +30 -0
- src/harness/src/openharness/state/store.py +40 -0
- src/harness/src/openharness/swarm/__init__.py +70 -0
- src/harness/src/openharness/swarm/in_process.py +693 -0
- src/harness/src/openharness/swarm/lockfile.py +24 -0
- src/harness/src/openharness/swarm/mailbox.py +522 -0
- src/harness/src/openharness/swarm/permission_sync.py +1168 -0
- src/harness/src/openharness/swarm/registry.py +410 -0
- src/harness/src/openharness/swarm/spawn_utils.py +229 -0
- src/harness/src/openharness/swarm/subprocess_backend.py +171 -0
- src/harness/src/openharness/swarm/team_lifecycle.py +910 -0
- src/harness/src/openharness/swarm/types.py +398 -0
- src/harness/src/openharness/swarm/worktree.py +315 -0
- src/harness/src/openharness/tasks/__init__.py +18 -0
- src/harness/src/openharness/tasks/local_agent_task.py +28 -0
- src/harness/src/openharness/tasks/local_shell_task.py +17 -0
- src/harness/src/openharness/tasks/manager.py +475 -0
- src/harness/src/openharness/tasks/stop_task.py +11 -0
- src/harness/src/openharness/tasks/types.py +32 -0
- src/harness/src/openharness/themes/__init__.py +21 -0
- src/harness/src/openharness/themes/builtin.py +89 -0
- src/harness/src/openharness/themes/loader.py +55 -0
- src/harness/src/openharness/themes/schema.py +54 -0
- src/harness/src/openharness/tools/__init__.py +107 -0
- src/harness/src/openharness/tools/agent_tool.py +141 -0
- src/harness/src/openharness/tools/ask_user_question_tool.py +46 -0
- src/harness/src/openharness/tools/base.py +80 -0
- src/harness/src/openharness/tools/bash_tool.py +259 -0
- src/harness/src/openharness/tools/brief_tool.py +33 -0
- src/harness/src/openharness/tools/config_tool.py +54 -0
- src/harness/src/openharness/tools/cron_create_tool.py +105 -0
- src/harness/src/openharness/tools/cron_delete_tool.py +32 -0
- src/harness/src/openharness/tools/cron_list_tool.py +69 -0
- src/harness/src/openharness/tools/cron_toggle_tool.py +37 -0
- src/harness/src/openharness/tools/enter_plan_mode_tool.py +28 -0
- src/harness/src/openharness/tools/enter_worktree_tool.py +80 -0
- src/harness/src/openharness/tools/exit_plan_mode_tool.py +28 -0
- src/harness/src/openharness/tools/exit_worktree_tool.py +42 -0
- src/harness/src/openharness/tools/file_edit_tool.py +95 -0
- src/harness/src/openharness/tools/file_read_tool.py +72 -0
- src/harness/src/openharness/tools/file_write_tool.py +87 -0
- src/harness/src/openharness/tools/glob_tool.py +175 -0
- src/harness/src/openharness/tools/grep_tool.py +374 -0
- src/harness/src/openharness/tools/image_generation_tool.py +447 -0
- src/harness/src/openharness/tools/image_to_text_tool.py +237 -0
- src/harness/src/openharness/tools/list_mcp_resources_tool.py +36 -0
- src/harness/src/openharness/tools/lsp_tool.py +154 -0
- src/harness/src/openharness/tools/mcp_auth_tool.py +71 -0
- src/harness/src/openharness/tools/mcp_tool.py +72 -0
- src/harness/src/openharness/tools/notebook_edit_tool.py +97 -0
- src/harness/src/openharness/tools/read_mcp_resource_tool.py +38 -0
- src/harness/src/openharness/tools/remote_trigger_tool.py +74 -0
- src/harness/src/openharness/tools/send_message_tool.py +58 -0
- src/harness/src/openharness/tools/skill_tool.py +43 -0
- src/harness/src/openharness/tools/sleep_tool.py +32 -0
- src/harness/src/openharness/tools/task_create_tool.py +56 -0
- src/harness/src/openharness/tools/task_get_tool.py +33 -0
- src/harness/src/openharness/tools/task_list_tool.py +35 -0
- src/harness/src/openharness/tools/task_output_tool.py +35 -0
- src/harness/src/openharness/tools/task_stop_tool.py +30 -0
- src/harness/src/openharness/tools/task_update_tool.py +50 -0
- src/harness/src/openharness/tools/team_create_tool.py +31 -0
- src/harness/src/openharness/tools/team_delete_tool.py +30 -0
- src/harness/src/openharness/tools/todo_write_tool.py +46 -0
- src/harness/src/openharness/tools/tool_search_tool.py +38 -0
- src/harness/src/openharness/tools/web_fetch_tool.py +117 -0
- src/harness/src/openharness/tools/web_search_tool.py +119 -0
- src/harness/src/openharness/ui/__init__.py +5 -0
- src/harness/src/openharness/ui/app.py +320 -0
- src/harness/src/openharness/ui/backend_host.py +941 -0
- src/harness/src/openharness/ui/coordinator_drain.py +198 -0
- src/harness/src/openharness/ui/input.py +32 -0
- src/harness/src/openharness/ui/output.py +265 -0
- src/harness/src/openharness/ui/permission_dialog.py +14 -0
- src/harness/src/openharness/ui/protocol.py +247 -0
- src/harness/src/openharness/ui/react_launcher.py +179 -0
- src/harness/src/openharness/ui/runtime.py +799 -0
- src/harness/src/openharness/ui/textual_app.py +495 -0
- src/harness/src/openharness/utils/__init__.py +0 -0
- src/harness/src/openharness/utils/file_lock.py +86 -0
- src/harness/src/openharness/utils/fs.py +98 -0
- src/harness/src/openharness/utils/helpers.py +77 -0
- src/harness/src/openharness/utils/network_guard.py +340 -0
- src/harness/src/openharness/utils/shell.py +147 -0
- src/harness/src/openharness/vim/__init__.py +5 -0
- src/harness/src/openharness/vim/transitions.py +8 -0
- src/harness/src/openharness/voice/__init__.py +7 -0
- src/harness/src/openharness/voice/keyterms.py +10 -0
- src/harness/src/openharness/voice/stream_stt.py +8 -0
- src/harness/src/openharness/voice/voice_mode.py +44 -0
- src/llm/__init__.py +29 -0
- src/llm/image_generation.py +402 -0
- src/llm/provider_registry.py +419 -0
- src/llm/runtime_env.py +64 -0
- src/llm/structured_output.py +131 -0
- src/llm/vision.py +198 -0
- src/memory/__init__.py +2 -0
- src/memory/api/__init__.py +1 -0
- src/memory/api/base_memory_system_api.py +98 -0
- src/memory/api/base_symbolic_memory_system_api.py +190 -0
- src/memory/api/base_vector_memory_system_api.py +116 -0
- src/memory/api/faiss_memory_system_api.py +228 -0
- src/memory/api/slot_process_api.py +827 -0
- src/memory/api/symbolic_memory_system_api.py +572 -0
- src/memory/memory_system/__init__.py +48 -0
- src/memory/memory_system/component_taxonomy.py +430 -0
- src/memory/memory_system/llm.py +437 -0
- src/memory/memory_system/models.py +168 -0
- src/memory/memory_system/user_prompt.py +418 -0
- src/memory/memory_system/utils.py +282 -0
- src/memory/memory_system/vectorstore.py +311 -0
- src/memory/memory_system/working_slot.py +99 -0
- src/pipeline/__init__.py +129 -0
- src/pipeline/contracts.py +531 -0
- src/pipeline/discipline_taxonomy.py +916 -0
- src/pipeline/evidence_coverage_ledger.py +1150 -0
- src/pipeline/evidence_refinement.py +620 -0
- src/pipeline/experiment_to_symbolic.py +137 -0
- src/pipeline/multimodal_evidence/__init__.py +28 -0
- src/pipeline/multimodal_evidence/capabilities.py +107 -0
- src/pipeline/multimodal_evidence/claims.py +135 -0
- src/pipeline/multimodal_evidence/contract.py +291 -0
- src/pipeline/multimodal_evidence/data_sh_compiler.py +505 -0
- src/pipeline/multimodal_evidence/handoff_binding.py +135 -0
- src/pipeline/multimodal_evidence/manifest.py +254 -0
- src/pipeline/multimodal_evidence/native_analysis.py +479 -0
- src/pipeline/multimodal_evidence/perception.py +277 -0
- src/pipeline/multimodal_evidence/query_binding.py +65 -0
- src/pipeline/multimodal_evidence/rendering.py +120 -0
- src/pipeline/multimodal_evidence/safety.py +42 -0
- src/pipeline/multimodal_evidence/sampling.py +69 -0
- src/pipeline/multimodal_evidence/service.py +148 -0
- src/pipeline/multimodal_evidence/survey_integration.py +460 -0
- src/pipeline/paper_identity.py +52 -0
- src/pipeline/research_design_inventory.py +338 -0
- src/pipeline/research_domain_resolution.py +669 -0
- src/pipeline/research_identity.py +548 -0
- src/pipeline/research_question_contract.py +646 -0
- src/pipeline/retrieval_lanes.py +889 -0
- src/pipeline/run_loop.py +862 -0
- src/pipeline/science_events.py +30 -0
- src/pipeline/science_manifests.py +602 -0
- src/pipeline/science_run.py +918 -0
- src/pipeline/science_stages.py +709 -0
- src/pipeline/science_workflow.py +807 -0
- src/pipeline/sh_cluster_projection.py +306 -0
- src/pipeline/sh_graph_provenance.py +648 -0
- src/pipeline/subhypothesis_decomposition.py +392 -0
- src/pipeline/subhypothesis_evidence.py +856 -0
- src/pipeline/survey_evidence_plan.py +858 -0
- src/pipeline/survey_gap_adjudication.py +647 -0
- src/pipeline/survey_gap_candidates.py +840 -0
- src/pipeline/survey_gap_ledger.py +683 -0
- src/pipeline/survey_gap_triage.py +401 -0
- src/pipeline/survey_handoff_persistence.py +481 -0
- src/pipeline/survey_handoff_projection.py +457 -0
- src/pipeline/survey_idea_handoff.py +1112 -0
- src/pipeline/survey_idea_loader.py +289 -0
- 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
|