osa_tool 0.3.2__tar.gz → 0.3.4__tar.gz
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.
- {osa_tool-0.3.2 → osa_tool-0.3.4}/PKG-INFO +1 -1
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/settings.py +4 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/git/git_agent.py +5 -2
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/llm/llm.py +15 -5
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/models/agent_status.py +1 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/docgen.py +27 -3
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/sections/header.py +5 -1
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/finalizer/agent.py +2 -1
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/planner/agent.py +3 -1
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/reviewer/agent.py +2 -2
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/graph.py +1 -1
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/state.py +2 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/run_chat.py +9 -1
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/ui/input_for_chat.py +9 -1
- osa_tool-0.3.4/osa_tool/web_chat.py +255 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/pyproject.toml +1 -1
- {osa_tool-0.3.2 → osa_tool-0.3.4}/LICENSE +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/README.md +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/assets/notosans-sc-black.ttf +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/assets/notosans-sc-bold.ttf +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/assets/notosans-sc.ttf +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/locales/english.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/locales/russian.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/osa_config.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/about_section.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/analysis.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/docstring_generation.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/osa_agent.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/paper_analysis.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/paper_claims.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/readme/prompts.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/readme/system_messages.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/readme.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/repo_organization.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/repository_quality.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/requirements.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/scheduler.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/system_messages.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/settings/arguments.yaml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/settings/config.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/template.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/autopep8.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/black.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/fix_pep8.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/pep8.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/pypi_publish.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/ruff.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/slash_command_dispatch.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/unit_test.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/autopep8.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/black.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/fix_pep8.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/pep8.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/pypi_publish.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/slash_command_dispatch.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/unit_test.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/git/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/git/metadata.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/git/request_utils.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/llm/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/llm/host.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/models/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/models/event.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/models/llm_output_models.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/models/task.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/images/osa_logo.PNG +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/images/osa_logo_transparent.PNG +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/templates/ci_config.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/templates/community.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/templates/contributing.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/templates/licenses.toml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/templates/mkdocs.yml +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/artifacts.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/notebook_report/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/notebook_report/analyzer.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/notebook_report/models.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/notebook_report/report_maker.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_analysis/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_analysis/data_context.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_analysis/models.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_analysis/pipeline.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_analysis/verifier.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/claim_deduplicator.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/claim_extractor.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/claim_input_planner.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/claim_schemas.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/claim_validation.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/exceptions.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/io.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/marker_converter.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/models.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/openrouter_converter.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/pdf_splitter.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/pipeline.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/section_extractor.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/section_parser.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_quality/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_quality/checks.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_quality/repository_quality_scorer.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_quality/scoring_engine.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_report/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_report/report_generator.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_report/report_localization.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_report/report_maker.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_report/response_validation.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/directory_translation/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/directory_translation/dirs_and_files_translator.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/adapters/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/adapters/base.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/adapters/javascript_adapter.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/adapters/python_adapter.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/adapters/typescript_adapter.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/core/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/core/osa_parser.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/core/source_view.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/docstring_generation.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/docstring_transformer.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/insert/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/insert/base_augmentor.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/insert/factory.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/insert/python_augmentor.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/insert/ts_js_augmentor.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/topology.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/notebook_conversion/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/notebook_conversion/notebook_converter.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/base.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/cpp.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/csharp.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/factory.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/generic.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/go.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/java.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/javascript.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/kotlin.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/latex.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/python.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/ruby.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/rust.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/swift.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/executor/action_executor.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/executor/batch_updater.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/health_checker.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/planning_manager.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/snapshot_manager.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/utils.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/organize.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/requirements_generation/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/requirements_generation/requirements_generation.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/workflow_generation/README.md +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/workflow_generation/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/workflow_generation/workflow_executor.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/workflow_generation/workflow_generator.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/about_generation/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/about_generation/about_generator.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/community_docs_generation/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/community_docs_generation/codemeta_generator.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/community_docs_generation/community.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/community_docs_generation/contributing.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/community_docs_generation/docs_run.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/community_docs_generation/license_generation.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/inputs/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/inputs/article_content.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/inputs/article_path.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/inputs/pypi_status_checker.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/graph.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/llm_schemas.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/models.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/assembler.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/context_collector.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/deterministic_builder.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/intent_analyzer.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/readme_patch.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/section_generator.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/section_planner.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/self_eval.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/writer.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/runtime_context.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/section_catalog.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/section_catalog_entries.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/state.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/readme_agent.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/readme_utils.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/sections/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/sections/icons/shieldsio_icons.json +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/sections/installation.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_translation/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_translation/readme_translator.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/operations_catalog.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/registry.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/executor/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/executor/agent.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/finalizer/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/finalizer/models.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/intent_router/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/intent_router/agent.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/intent_router/models.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/planner/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/planner/models.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/repo_analysis/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/repo_analysis/agent.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/reviewer/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/reviewer/models.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/base.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/context.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/run.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/run_multi_process.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/scheduler/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/scheduler/plan.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/scheduler/response_validation.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/scheduler/scheduler.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/scheduler/workflow_manager.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/focused_cli.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_analysis/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_analysis/__main__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_analysis/cli.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_claims/README.md +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_claims/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_claims/aggregate.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_claims/batch.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_claims/evaluate.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/progress.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/dependencies.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/documentation.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/models.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/repo_analyzer.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/repo_graph.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/scorecard.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/sourcerank.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/testing.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_quality/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_quality/__main__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/ui/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/ui/plan_editor.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/__init__.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/arguments_parser.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/logger.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/prompts_builder.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/response_cleaner.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/token_counter.py +0 -0
- {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/utils.py +0 -0
|
@@ -64,6 +64,10 @@ class GitSettings(BaseModel):
|
|
|
64
64
|
else:
|
|
65
65
|
raise ValueError(f"{self.repository} does not exist.")
|
|
66
66
|
else:
|
|
67
|
+
# A URL copied from a browser often carries a trailing slash, and the
|
|
68
|
+
# remote helpers append ".git" to this value verbatim, which would make
|
|
69
|
+
# "owner/repo/" into "owner/repo/.git" and fail the clone.
|
|
70
|
+
self.repository = str(self.repository).rstrip("/")
|
|
67
71
|
self.host_domain, self.host, self.name, self.full_name = parse_git_url(str(self.repository))
|
|
68
72
|
return self
|
|
69
73
|
|
|
@@ -904,7 +904,7 @@ class GitHubAgent(GitAgent):
|
|
|
904
904
|
else:
|
|
905
905
|
logger.error(f"Failed to post a comment to PR #{pr_number}: {response.status_code} - {response.text}")
|
|
906
906
|
|
|
907
|
-
def create_pull_request(self, title: str = None, body: str = None, changes: bool = False) -> None:
|
|
907
|
+
def create_pull_request(self, title: str = None, body: str = None, changes: bool = False) -> str | None:
|
|
908
908
|
"""Creates or updates a pull request on GitHub.
|
|
909
909
|
|
|
910
910
|
This method implements intelligent PR management:
|
|
@@ -975,6 +975,7 @@ class GitHubAgent(GitAgent):
|
|
|
975
975
|
f"updating PR #{pr_number}",
|
|
976
976
|
raise_exception=False,
|
|
977
977
|
)
|
|
978
|
+
return existing_pr.get("html_url")
|
|
978
979
|
elif changes:
|
|
979
980
|
report_files = self.get_attachment_branch_files()
|
|
980
981
|
report_branch = "osa_tool_attachments"
|
|
@@ -997,7 +998,9 @@ class GitHubAgent(GitAgent):
|
|
|
997
998
|
|
|
998
999
|
response = request_with_retry("post", url, json=pr_data, headers=headers)
|
|
999
1000
|
if response.status_code == 201:
|
|
1000
|
-
|
|
1001
|
+
pr_url = response.json()["html_url"]
|
|
1002
|
+
logger.info(f"GitHub pull request created successfully: {pr_url}")
|
|
1003
|
+
return pr_url
|
|
1001
1004
|
else:
|
|
1002
1005
|
self._handle_api_error(response, "creating pull request", raise_exception=True)
|
|
1003
1006
|
|
|
@@ -641,7 +641,7 @@ class ProtollmHandler(ModelHandler):
|
|
|
641
641
|
time.sleep(retry_delay)
|
|
642
642
|
|
|
643
643
|
logger.debug(repr(last_error))
|
|
644
|
-
raise
|
|
644
|
+
raise last_error
|
|
645
645
|
|
|
646
646
|
async def async_run_chain(
|
|
647
647
|
self, prompt: str, parser: PydanticOutputParser, system_message: str = None, retry_delay: float = 0.5
|
|
@@ -702,15 +702,23 @@ class ProtollmHandler(ModelHandler):
|
|
|
702
702
|
}
|
|
703
703
|
return url_templates.get(self.model_settings.api, f"{self.model_settings.base_url};{model_name}")
|
|
704
704
|
|
|
705
|
-
def _get_llm_params(self):
|
|
705
|
+
def _get_llm_params(self, model_name: str | None = None):
|
|
706
706
|
"""Extract LLM parameters from config"""
|
|
707
707
|
llm_params = ["temperature", "max_tokens", "top_p"]
|
|
708
708
|
|
|
709
|
-
|
|
709
|
+
params = {
|
|
710
710
|
name: getattr(self.model_settings, name)
|
|
711
711
|
for name in llm_params
|
|
712
712
|
if getattr(self.model_settings, name, None) is not None
|
|
713
713
|
}
|
|
714
|
+
if (
|
|
715
|
+
self.model_settings.api == "openai"
|
|
716
|
+
and urlparse(self.model_settings.base_url).hostname == "api.openai.com"
|
|
717
|
+
and (model_name or self.model_settings.model).startswith(("gpt-6-", "gpt-6."))
|
|
718
|
+
):
|
|
719
|
+
params.pop("temperature", None)
|
|
720
|
+
params.pop("top_p", None)
|
|
721
|
+
return params
|
|
714
722
|
|
|
715
723
|
def _configure_api(self, model_name: str) -> None:
|
|
716
724
|
"""
|
|
@@ -727,10 +735,12 @@ class ProtollmHandler(ModelHandler):
|
|
|
727
735
|
# 401 "User not found" errors)
|
|
728
736
|
dotenv.load_dotenv(override=True)
|
|
729
737
|
|
|
738
|
+
params = self._get_llm_params(model_name)
|
|
739
|
+
if urlparse(self.model_settings.base_url).hostname != "api.openai.com":
|
|
740
|
+
params["extra_body"] = {"providers": {"only": self.model_settings.allowed_providers}}
|
|
730
741
|
self.client = create_llm_connector(
|
|
731
742
|
model_url=self._build_model_url(model_name),
|
|
732
|
-
|
|
733
|
-
**self._get_llm_params(),
|
|
743
|
+
**params,
|
|
734
744
|
)
|
|
735
745
|
|
|
736
746
|
def _limit_tokens(
|
{osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/docgen.py
RENAMED
|
@@ -1522,6 +1522,30 @@ class DocGen(object):
|
|
|
1522
1522
|
dot_path = ".".join(processed_parts)
|
|
1523
1523
|
return f"::: {dot_path}"
|
|
1524
1524
|
|
|
1525
|
+
@staticmethod
|
|
1526
|
+
def _path_inside_repo(target: str | Path, repo_path: Path) -> Path:
|
|
1527
|
+
"""
|
|
1528
|
+
Returns a path relative to the repository root.
|
|
1529
|
+
|
|
1530
|
+
Analysis results carry absolute paths whenever OSA runs with --output.
|
|
1531
|
+
Dropping only the leading component then leaves the rest of the filesystem
|
|
1532
|
+
prefix in place, which nests osa_docs under the machine's own directory
|
|
1533
|
+
tree and, on Windows, pushes the result past the path length limit.
|
|
1534
|
+
|
|
1535
|
+
Args:
|
|
1536
|
+
target: The analysed file or directory path.
|
|
1537
|
+
repo_path: The resolved root of the analysed repository.
|
|
1538
|
+
|
|
1539
|
+
Returns:
|
|
1540
|
+
Path: The target expressed relative to the repository root.
|
|
1541
|
+
"""
|
|
1542
|
+
candidate = Path(target)
|
|
1543
|
+
try:
|
|
1544
|
+
return candidate.resolve().relative_to(repo_path)
|
|
1545
|
+
except ValueError:
|
|
1546
|
+
# callers may pass a path that merely starts with the repository directory
|
|
1547
|
+
return Path(*candidate.parts[1:])
|
|
1548
|
+
|
|
1525
1549
|
def generate_documentation_mkdocs(self, path: str, files_info, modules_info) -> None:
|
|
1526
1550
|
"""
|
|
1527
1551
|
Generates MkDocs documentation for a Python project based on provided path.
|
|
@@ -1547,8 +1571,8 @@ class DocGen(object):
|
|
|
1547
1571
|
continue
|
|
1548
1572
|
parent_dir = Path(file).parent
|
|
1549
1573
|
file_name = Path(file).name
|
|
1550
|
-
relative_path =
|
|
1551
|
-
new_path = Path(init_doc_path,
|
|
1574
|
+
relative_path = self._path_inside_repo(file, repo_path)
|
|
1575
|
+
new_path = Path(init_doc_path, self._path_inside_repo(parent_dir, repo_path))
|
|
1552
1576
|
new_path.mkdir(parents=True, exist_ok=True)
|
|
1553
1577
|
text = (
|
|
1554
1578
|
f"# {file_name.strip('.py').replace('_', ' ').title()}\n\n"
|
|
@@ -1559,7 +1583,7 @@ class DocGen(object):
|
|
|
1559
1583
|
new_file.write_text(text)
|
|
1560
1584
|
|
|
1561
1585
|
for module in modules_info:
|
|
1562
|
-
new_file = Path(init_doc_path,
|
|
1586
|
+
new_file = Path(init_doc_path, self._path_inside_repo(module, repo_path))
|
|
1563
1587
|
new_file.mkdir(parents=True, exist_ok=True)
|
|
1564
1588
|
text = modules_info[module]
|
|
1565
1589
|
Path(new_file, "index.md").write_text(text)
|
{osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/sections/header.py
RENAMED
|
@@ -15,6 +15,10 @@ from osa_tool.tools.repository_analysis.sourcerank import SourceRank
|
|
|
15
15
|
from osa_tool.utils.logger import logger
|
|
16
16
|
from osa_tool.utils.utils import osa_project_root, resolve_repo_path, resolve_repo_web_identity
|
|
17
17
|
|
|
18
|
+
# Shields.io badge style applied to the tech icons. Every URL in shieldsio_icons.json
|
|
19
|
+
# carries a single "{0}" placeholder for it; "flat" matches the license badge above them.
|
|
20
|
+
BADGE_STYLE = "flat"
|
|
21
|
+
|
|
18
22
|
|
|
19
23
|
class HeaderBuilder:
|
|
20
24
|
"""Produce the top-of-README header with project name, info badges, and tech badges."""
|
|
@@ -129,7 +133,7 @@ class HeaderBuilder:
|
|
|
129
133
|
badges: list[str] = ["Built with:\n"]
|
|
130
134
|
for tech in sorted(self.techs):
|
|
131
135
|
if tech in icons:
|
|
132
|
-
badges.append(f"")
|
|
136
|
+
badges.append(f"})")
|
|
133
137
|
if len(badges) >= self.max_tech_badges + 1:
|
|
134
138
|
break
|
|
135
139
|
if len(badges) <= 3:
|
|
@@ -45,6 +45,7 @@ class FinalizerAgent(BaseAgent):
|
|
|
45
45
|
|
|
46
46
|
events = self._collect_events(state)
|
|
47
47
|
summary = self._summarize_events(state, events) if events else ""
|
|
48
|
+
state.final_summary = summary
|
|
48
49
|
|
|
49
50
|
# PR or logs
|
|
50
51
|
if create_fork and create_pr:
|
|
@@ -56,7 +57,7 @@ class FinalizerAgent(BaseAgent):
|
|
|
56
57
|
if about_message:
|
|
57
58
|
pr_body += "\n\n---\n\n" + about_message
|
|
58
59
|
|
|
59
|
-
self.context.git_agent.create_pull_request(
|
|
60
|
+
state.pull_request_url = self.context.git_agent.create_pull_request(
|
|
60
61
|
body=pr_body,
|
|
61
62
|
changes=changes,
|
|
62
63
|
)
|
|
@@ -54,7 +54,9 @@ class PlannerAgent(BaseAgent):
|
|
|
54
54
|
|
|
55
55
|
state.status = AgentStatus.ANALYZING
|
|
56
56
|
logger.info("Planner started")
|
|
57
|
-
|
|
57
|
+
# Installed wheels do not contain the source checkout's docs tree.
|
|
58
|
+
# Runtime registration must not attempt to regenerate developer docs.
|
|
59
|
+
register_all_operations(generate_docs=False)
|
|
58
60
|
|
|
59
61
|
# Maintain plan history when re-planning after review: append last plan or clear for new request
|
|
60
62
|
if state.active_request_source == "reviewer" and state.plan:
|
|
@@ -90,9 +90,9 @@ class ReviewerAgent(BaseAgent):
|
|
|
90
90
|
if state.review_cycle_count >= state.max_review_cycles:
|
|
91
91
|
self._reset_clarification(state)
|
|
92
92
|
state.review_cycles_exhausted = True
|
|
93
|
-
state.
|
|
93
|
+
state.status = AgentStatus.STOPPED
|
|
94
94
|
logger.warning(
|
|
95
|
-
"Max review cycles reached (%s/%s);
|
|
95
|
+
"Max review cycles reached (%s/%s); ending without finalization",
|
|
96
96
|
state.review_cycle_count,
|
|
97
97
|
state.max_review_cycles,
|
|
98
98
|
)
|
|
@@ -70,7 +70,7 @@ def build_graph(context: AgentContext) -> Any:
|
|
|
70
70
|
# reviewer → finalizer | planner
|
|
71
71
|
graph.add_conditional_edges(
|
|
72
72
|
"reviewer",
|
|
73
|
-
lambda state: ("finalizer" if state.approval else "planner"),
|
|
73
|
+
lambda state: (END if state.review_cycles_exhausted else "finalizer" if state.approval else "planner"),
|
|
74
74
|
)
|
|
75
75
|
|
|
76
76
|
graph.add_edge("finalizer", END)
|
|
@@ -67,6 +67,8 @@ class OSAState(BaseModel):
|
|
|
67
67
|
max_review_cycles: int = 3
|
|
68
68
|
review_cycle_count: int = 0
|
|
69
69
|
review_cycles_exhausted: bool = False
|
|
70
|
+
final_summary: Optional[str] = None
|
|
71
|
+
pull_request_url: Optional[str] = None
|
|
70
72
|
|
|
71
73
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
72
74
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import sys
|
|
2
3
|
import time
|
|
3
4
|
|
|
4
5
|
from osa_tool.config.osa_config import OSAConfig
|
|
@@ -22,8 +23,15 @@ from osa_tool.utils.utils import (
|
|
|
22
23
|
def main():
|
|
23
24
|
# Create a command line argument parser
|
|
24
25
|
parser = build_parser_from_yaml(extra_sections=["settings"])
|
|
26
|
+
parser.add_argument("--web-chat", action="store_true", help="Use the JSON-lines web chat protocol.")
|
|
27
|
+
parser.add_argument("--log-dir", help="Directory for OSA log files.")
|
|
25
28
|
args = parser.parse_args()
|
|
26
29
|
|
|
30
|
+
if args.web_chat:
|
|
31
|
+
from osa_tool.web_chat import run_web_chat
|
|
32
|
+
|
|
33
|
+
return run_web_chat(args)
|
|
34
|
+
|
|
27
35
|
# Collecting user parameters
|
|
28
36
|
user_input: InitialChatInput = collect_user_input()
|
|
29
37
|
|
|
@@ -76,4 +84,4 @@ def main():
|
|
|
76
84
|
|
|
77
85
|
|
|
78
86
|
if __name__ == "__main__":
|
|
79
|
-
main()
|
|
87
|
+
sys.exit(main())
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from
|
|
1
|
+
from contextvars import ContextVar
|
|
2
|
+
from typing import Callable, Optional
|
|
2
3
|
|
|
3
4
|
from pydantic import BaseModel
|
|
4
5
|
from rich.console import Console
|
|
@@ -6,6 +7,9 @@ from rich.console import Console
|
|
|
6
7
|
from osa_tool.osa_agent.state import OSAState
|
|
7
8
|
|
|
8
9
|
console = Console()
|
|
10
|
+
_web_question_handler: ContextVar[Callable[["OSAState"], dict] | None] = ContextVar(
|
|
11
|
+
"web_question_handler", default=None
|
|
12
|
+
)
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
class InitialChatInput(BaseModel):
|
|
@@ -57,6 +61,10 @@ def wait_for_user_clarification(state: OSAState) -> dict:
|
|
|
57
61
|
if not state.clarification_required:
|
|
58
62
|
raise RuntimeError("wait_for_user_clarification called but no clarification was requested")
|
|
59
63
|
|
|
64
|
+
web_handler = _web_question_handler.get()
|
|
65
|
+
if web_handler is not None:
|
|
66
|
+
return web_handler(state)
|
|
67
|
+
|
|
60
68
|
payload = state.clarification_payload or {}
|
|
61
69
|
question = payload.get("question", "Additional information required:")
|
|
62
70
|
fields = payload.get("fields", [])
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""JSON-lines transport for the agent graph used by browser clients.
|
|
2
|
+
|
|
3
|
+
The terminal chat remains in :mod:`osa_tool.run_chat`. Only this opt-in mode
|
|
4
|
+
uses stdin and stdout as a machine-readable protocol.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
import uuid
|
|
13
|
+
from contextlib import contextmanager
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, TextIO
|
|
16
|
+
|
|
17
|
+
from osa_tool.config.osa_config import OSAConfig
|
|
18
|
+
from osa_tool.config.settings import ConfigManager
|
|
19
|
+
from osa_tool.core.models.agent_status import AgentStatus
|
|
20
|
+
from osa_tool.osa_agent.context import AgentContext
|
|
21
|
+
from osa_tool.osa_agent.graph import build_graph
|
|
22
|
+
from osa_tool.osa_agent.state import OSAState
|
|
23
|
+
from osa_tool.run import initialize_git_platform, resolve_publishing_options
|
|
24
|
+
from osa_tool.ui.input_for_chat import _web_question_handler
|
|
25
|
+
from osa_tool.utils.logger import logger, setup_logging
|
|
26
|
+
from osa_tool.utils.utils import (
|
|
27
|
+
osa_project_root,
|
|
28
|
+
parse_folder_name,
|
|
29
|
+
prepare_local_output_repository,
|
|
30
|
+
switch_to_output_directory,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ChatProtocolError(ValueError):
|
|
35
|
+
"""The browser sent an invalid protocol message."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ChatCancelled(Exception):
|
|
39
|
+
"""The browser closed or cancelled an interactive run."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class JsonLineChatIO:
|
|
43
|
+
def __init__(self, source: TextIO, sink: TextIO):
|
|
44
|
+
self.source = source
|
|
45
|
+
self.sink = sink
|
|
46
|
+
self.output_dir: Path | None = None
|
|
47
|
+
|
|
48
|
+
def emit(self, event_type: str, **fields: Any) -> None:
|
|
49
|
+
self.sink.write(json.dumps({"type": event_type, **fields}, default=str) + "\n")
|
|
50
|
+
self.sink.flush()
|
|
51
|
+
|
|
52
|
+
def _read(self) -> dict[str, Any]:
|
|
53
|
+
line = self.source.readline(1_048_577)
|
|
54
|
+
if not line:
|
|
55
|
+
raise ChatCancelled("Browser connection closed")
|
|
56
|
+
if len(line) > 1_048_576:
|
|
57
|
+
raise ChatProtocolError("Message is too large")
|
|
58
|
+
try:
|
|
59
|
+
value = json.loads(line)
|
|
60
|
+
except json.JSONDecodeError as exc:
|
|
61
|
+
raise ChatProtocolError("Expected a JSON object") from exc
|
|
62
|
+
if not isinstance(value, dict):
|
|
63
|
+
raise ChatProtocolError("Expected a JSON object")
|
|
64
|
+
if value.get("type") == "cancel":
|
|
65
|
+
raise ChatCancelled("Run cancelled")
|
|
66
|
+
return value
|
|
67
|
+
|
|
68
|
+
def read_start(self) -> dict[str, str | None]:
|
|
69
|
+
message = self._read()
|
|
70
|
+
repository = message.get("repository")
|
|
71
|
+
request = message.get("request")
|
|
72
|
+
attachment = message.get("attachment")
|
|
73
|
+
if message.get("type") != "start":
|
|
74
|
+
raise ChatProtocolError("First message must have type 'start'")
|
|
75
|
+
if not isinstance(repository, str) or not repository.strip():
|
|
76
|
+
raise ChatProtocolError("Repository is required")
|
|
77
|
+
if not isinstance(request, str) or not request.strip():
|
|
78
|
+
raise ChatProtocolError("Request is required")
|
|
79
|
+
if attachment is not None and not isinstance(attachment, str):
|
|
80
|
+
raise ChatProtocolError("Attachment must be a string or null")
|
|
81
|
+
return {
|
|
82
|
+
"repository": repository.strip(),
|
|
83
|
+
"request": request.strip(),
|
|
84
|
+
"attachment": attachment.strip() or None if isinstance(attachment, str) else None,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
def ask(self, state: OSAState) -> dict[str, str | None]:
|
|
88
|
+
payload = state.clarification_payload or {}
|
|
89
|
+
fields = payload.get("fields", [])
|
|
90
|
+
question_id = uuid.uuid4().hex
|
|
91
|
+
if state.clarification_type == "review":
|
|
92
|
+
self.emit(
|
|
93
|
+
"review",
|
|
94
|
+
tasks=_task_results(state),
|
|
95
|
+
artifacts=_artifact_paths(state, self.output_dir),
|
|
96
|
+
events=_artifact_events(state),
|
|
97
|
+
)
|
|
98
|
+
self.emit(
|
|
99
|
+
"question",
|
|
100
|
+
id=question_id,
|
|
101
|
+
agent=state.clarification_agent,
|
|
102
|
+
kind=state.clarification_type,
|
|
103
|
+
question=payload.get("question", "Additional information required"),
|
|
104
|
+
fields=fields,
|
|
105
|
+
)
|
|
106
|
+
while True:
|
|
107
|
+
try:
|
|
108
|
+
message = self._read()
|
|
109
|
+
if message.get("type") != "answer" or message.get("id") != question_id:
|
|
110
|
+
raise ChatProtocolError("Answer does not match the pending question")
|
|
111
|
+
values = message.get("values")
|
|
112
|
+
if not isinstance(values, dict):
|
|
113
|
+
raise ChatProtocolError("Answer values must be an object")
|
|
114
|
+
names = {field["name"] for field in fields}
|
|
115
|
+
if set(values) - names:
|
|
116
|
+
raise ChatProtocolError("Answer contains unknown fields")
|
|
117
|
+
answers: dict[str, str | None] = {}
|
|
118
|
+
for field in fields:
|
|
119
|
+
name = field["name"]
|
|
120
|
+
value = values.get(name)
|
|
121
|
+
if value is not None and not isinstance(value, str):
|
|
122
|
+
raise ChatProtocolError(f"{name} must be text")
|
|
123
|
+
value = value.strip() if value else None
|
|
124
|
+
if field.get("required") and not value:
|
|
125
|
+
raise ChatProtocolError(f"{name} is required")
|
|
126
|
+
if name == "accept" and value and value.lower() not in {"yes", "y", "no", "n"}:
|
|
127
|
+
raise ChatProtocolError("Approval must be yes or no")
|
|
128
|
+
answers[name] = value
|
|
129
|
+
return answers
|
|
130
|
+
except ChatProtocolError as exc:
|
|
131
|
+
self.emit("error", message=str(exc), recoverable=True)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@contextmanager
|
|
135
|
+
def use_web_questions(handler):
|
|
136
|
+
token = _web_question_handler.set(handler)
|
|
137
|
+
try:
|
|
138
|
+
yield
|
|
139
|
+
finally:
|
|
140
|
+
_web_question_handler.reset(token)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _artifact_paths(state: OSAState, output_dir: Path | None) -> list[str]:
|
|
144
|
+
if output_dir is None:
|
|
145
|
+
return []
|
|
146
|
+
paths: set[str] = set()
|
|
147
|
+
for artifact in state.artifacts.values():
|
|
148
|
+
if not isinstance(artifact, dict):
|
|
149
|
+
continue
|
|
150
|
+
for event in artifact.get("events", []):
|
|
151
|
+
target = event.get("target") if isinstance(event, dict) else getattr(event, "target", None)
|
|
152
|
+
if not isinstance(target, (str, Path)):
|
|
153
|
+
continue
|
|
154
|
+
path = Path(target)
|
|
155
|
+
if not path.is_absolute():
|
|
156
|
+
path = Path(state.repo_path or output_dir) / path
|
|
157
|
+
path = path.resolve()
|
|
158
|
+
if path.is_file() and path.is_relative_to(output_dir):
|
|
159
|
+
paths.add(str(path))
|
|
160
|
+
return sorted(paths)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _task_results(state: OSAState) -> list[dict[str, Any]]:
|
|
164
|
+
return [{"id": task.id, "status": task.status.value, "result": task.result} for task in state.plan]
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _artifact_events(state: OSAState) -> list[dict[str, str]]:
|
|
168
|
+
events: list[dict[str, str]] = []
|
|
169
|
+
for artifact in state.artifacts.values():
|
|
170
|
+
if not isinstance(artifact, dict):
|
|
171
|
+
continue
|
|
172
|
+
for event in artifact.get("events", []):
|
|
173
|
+
kind = event.get("kind") if isinstance(event, dict) else getattr(event, "kind", None)
|
|
174
|
+
target = event.get("target") if isinstance(event, dict) else getattr(event, "target", None)
|
|
175
|
+
if kind is not None and target is not None:
|
|
176
|
+
events.append({"kind": str(getattr(kind, "value", kind)), "target": str(target)})
|
|
177
|
+
return events[:50]
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _run(args, io: JsonLineChatIO) -> int:
|
|
181
|
+
start = io.read_start()
|
|
182
|
+
args.repository = start["repository"]
|
|
183
|
+
output_dir = Path(args.output).resolve() if args.output else None
|
|
184
|
+
io.output_dir = output_dir
|
|
185
|
+
logs_dir = args.log_dir or str(Path(osa_project_root()).parent / "logs")
|
|
186
|
+
setup_logging(parse_folder_name(args.repository), logs_dir)
|
|
187
|
+
|
|
188
|
+
if output_dir is not None:
|
|
189
|
+
switch_to_output_directory(output_dir)
|
|
190
|
+
if os.path.isdir(args.repository):
|
|
191
|
+
args.repository = str(prepare_local_output_repository(args.repository, output_dir))
|
|
192
|
+
|
|
193
|
+
config_manager = ConfigManager(args)
|
|
194
|
+
git_agent, workflow_manager = initialize_git_platform(args, config_manager)
|
|
195
|
+
create_fork, create_pull_request = resolve_publishing_options(args, git_agent)
|
|
196
|
+
context = AgentContext(
|
|
197
|
+
OSAConfig(
|
|
198
|
+
config_manager=config_manager,
|
|
199
|
+
git_agent=git_agent,
|
|
200
|
+
workflow_manager=workflow_manager,
|
|
201
|
+
create_fork=create_fork,
|
|
202
|
+
create_pull_request=create_pull_request,
|
|
203
|
+
delete_dir=args.delete_dir,
|
|
204
|
+
enable_replanning=True,
|
|
205
|
+
enable_memory=True,
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
state = OSAState(
|
|
209
|
+
repo_url=start["repository"],
|
|
210
|
+
attachment=start["attachment"],
|
|
211
|
+
active_request=start["request"],
|
|
212
|
+
active_request_source="user",
|
|
213
|
+
session_id=uuid.uuid4().hex,
|
|
214
|
+
status=AgentStatus.INIT,
|
|
215
|
+
)
|
|
216
|
+
io.emit("started", session_id=state.session_id)
|
|
217
|
+
with use_web_questions(io.ask):
|
|
218
|
+
for value in build_graph(context).stream(state, stream_mode="values"):
|
|
219
|
+
state = OSAState.model_validate(value)
|
|
220
|
+
if state.active_agent:
|
|
221
|
+
io.emit("progress", agent=state.active_agent, status=state.status.value)
|
|
222
|
+
|
|
223
|
+
if state.review_cycles_exhausted:
|
|
224
|
+
io.emit("stopped", reason="review_cycles_exhausted", message="Revision limit reached without approval")
|
|
225
|
+
return 0
|
|
226
|
+
if state.status != AgentStatus.COMPLETED:
|
|
227
|
+
raise RuntimeError(f"Agent graph ended with status {state.status.value}")
|
|
228
|
+
io.emit(
|
|
229
|
+
"completed",
|
|
230
|
+
summary=state.final_summary or "Run completed",
|
|
231
|
+
tasks=_task_results(state),
|
|
232
|
+
artifacts=_artifact_paths(state, output_dir),
|
|
233
|
+
pull_request_url=state.pull_request_url,
|
|
234
|
+
)
|
|
235
|
+
return 0
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def run_web_chat(args) -> int:
|
|
239
|
+
"""Run the web protocol with stdout reserved exclusively for JSON lines."""
|
|
240
|
+
sys.stdout.flush()
|
|
241
|
+
wire = os.fdopen(os.dup(sys.stdout.fileno()), "w", encoding="utf-8", buffering=1)
|
|
242
|
+
os.dup2(sys.stderr.fileno(), sys.stdout.fileno())
|
|
243
|
+
sys.stdout = sys.stderr
|
|
244
|
+
io = JsonLineChatIO(sys.stdin, wire)
|
|
245
|
+
try:
|
|
246
|
+
return _run(args, io)
|
|
247
|
+
except ChatCancelled as exc:
|
|
248
|
+
io.emit("stopped", reason="cancelled", message=str(exc))
|
|
249
|
+
return 0
|
|
250
|
+
except Exception as exc:
|
|
251
|
+
logger.exception("Web chat failed")
|
|
252
|
+
io.emit("error", message=str(exc), recoverable=False)
|
|
253
|
+
return 1
|
|
254
|
+
finally:
|
|
255
|
+
wire.close()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/autopep8.yml
RENAMED
|
File without changes
|
{osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/black.yml
RENAMED
|
File without changes
|
{osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/fix_pep8.yml
RENAMED
|
File without changes
|
{osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/pep8.yml
RENAMED
|
File without changes
|
|
File without changes
|
{osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/ruff.yml
RENAMED
|
File without changes
|
|
File without changes
|
{osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/unit_test.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|