osa_tool 0.2.3__tar.gz → 0.2.4.0__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.
Files changed (112) hide show
  1. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/PKG-INFO +33 -76
  2. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/README.md +9 -10
  3. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/aboutgen/about_generator.py +19 -12
  4. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/analytics/metadata.py +37 -26
  5. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/analytics/report_generator.py +19 -31
  6. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/analytics/report_maker.py +7 -3
  7. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/analytics/sourcerank.py +1 -1
  8. osa_tool-0.2.3/osa_tool/config/settings/prompt_for_analysis.toml → osa_tool-0.2.4.0/osa_tool/config/prompts/analysis.toml +1 -1
  9. osa_tool-0.2.4.0/osa_tool/config/prompts/validation.toml +177 -0
  10. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/settings/arguments.yaml +22 -6
  11. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/settings/config.toml +1 -1
  12. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/settings.py +1 -1
  13. {osa_tool-0.2.3/osa_tool/convertion → osa_tool-0.2.4.0/osa_tool/conversion}/notebook_converter.py +1 -1
  14. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/docs_generator/community.py +2 -1
  15. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/docs_generator/contributing.py +2 -1
  16. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/docs_generator/docs_run.py +1 -1
  17. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/docs_generator/license.py +2 -1
  18. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/git_agent/git_agent.py +45 -14
  19. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/organization/repo_organizer.py +3 -2
  20. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/osatreesitter/docgen.py +9 -8
  21. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/context/article_content.py +37 -0
  22. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/context/article_path.py +2 -2
  23. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/context/dependencies.py +1 -1
  24. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/context/files_contents.py +15 -1
  25. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/context/pypi_status_checker.py +1 -1
  26. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/generator/base_builder.py +1 -1
  27. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/generator/header.py +1 -1
  28. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/generator/installation.py +1 -1
  29. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/models/llm_service.py +119 -30
  30. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/postprocessor/response_cleaner.py +1 -1
  31. osa_tool-0.2.4.0/osa_tool/readmegen/readme_core.py +63 -0
  32. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/utils.py +8 -1
  33. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/run.py +59 -17
  34. osa_tool-0.2.4.0/osa_tool/run_multi_process.py +358 -0
  35. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/scheduler/README.md +23 -21
  36. osa_tool-0.2.3/osa_tool/scheduler/prompts.py → osa_tool-0.2.4.0/osa_tool/scheduler/response_validation.py +0 -28
  37. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/scheduler/scheduler.py +9 -6
  38. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/scheduler/workflow_manager.py +6 -3
  39. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/translation/dir_translator.py +2 -1
  40. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/translation/readme_translator.py +12 -5
  41. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/ui/plan_editor.py +3 -2
  42. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/ui/web_plan_editor.py +1 -1
  43. {osa_tool-0.2.3/osa_tool → osa_tool-0.2.4.0/osa_tool/utils}/arguments_parser.py +8 -2
  44. osa_tool-0.2.4.0/osa_tool/utils/logger.py +49 -0
  45. osa_tool-0.2.4.0/osa_tool/utils/prompts_builder.py +94 -0
  46. {osa_tool-0.2.3/osa_tool → osa_tool-0.2.4.0/osa_tool/utils}/utils.py +2 -11
  47. osa_tool-0.2.4.0/osa_tool/validation/code_analyzer.py +123 -0
  48. osa_tool-0.2.4.0/osa_tool/validation/doc_validator.py +183 -0
  49. osa_tool-0.2.4.0/osa_tool/validation/paper_validator.py +105 -0
  50. osa_tool-0.2.4.0/osa_tool/validation/report_generator.py +196 -0
  51. osa_tool-0.2.4.0/osa_tool/workflow/__init__.py +0 -0
  52. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/workflow/workflow_generator.py +3 -3
  53. osa_tool-0.2.4.0/pyproject.toml +59 -0
  54. osa_tool-0.2.3/osa_tool/aboutgen/prompts_about_config.py +0 -39
  55. osa_tool-0.2.3/osa_tool/analytics/run_report_gen.py +0 -167
  56. osa_tool-0.2.3/osa_tool/readmegen/prompts/prompts_builder.py +0 -280
  57. osa_tool-0.2.3/osa_tool/readmegen/readme_core.py +0 -53
  58. osa_tool-0.2.3/pyproject.toml +0 -99
  59. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/LICENSE +0 -0
  60. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/__init__.py +0 -0
  61. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/aboutgen/__init__.py +0 -0
  62. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/analytics/__init__.py +0 -0
  63. /osa_tool-0.2.3/osa_tool/analytics/prompt_builder.py → /osa_tool-0.2.4.0/osa_tool/analytics/response_validation.py +0 -0
  64. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/__init__.py +0 -0
  65. /osa_tool-0.2.3/osa_tool/config/settings/prompts_about_section.toml → /osa_tool-0.2.4.0/osa_tool/config/prompts/about_section.toml +0 -0
  66. /osa_tool-0.2.3/osa_tool/config/settings/prompts.toml → /osa_tool-0.2.4.0/osa_tool/config/prompts/readme.toml +0 -0
  67. /osa_tool-0.2.3/osa_tool/config/settings/prompts_article.toml → /osa_tool-0.2.4.0/osa_tool/config/prompts/readme_article.toml +0 -0
  68. /osa_tool-0.2.3/osa_tool/config/settings/prompt_for_scheduler.toml → /osa_tool-0.2.4.0/osa_tool/config/prompts/scheduler.toml +0 -0
  69. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/template.toml +0 -0
  70. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/github_gitverse/autopep8.yml +0 -0
  71. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/github_gitverse/black.yml +0 -0
  72. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/github_gitverse/fix_pep8.yml +0 -0
  73. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/github_gitverse/pep8.yml +0 -0
  74. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/github_gitverse/pypi_publish.yml +0 -0
  75. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/github_gitverse/slash_command_dispatch.yml +0 -0
  76. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/github_gitverse/unit_test.yml +0 -0
  77. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/gitlab/autopep8.yml +0 -0
  78. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/gitlab/black.yml +0 -0
  79. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/gitlab/fix_pep8.yml +0 -0
  80. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/gitlab/pep8.yml +0 -0
  81. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/gitlab/pypi_publish.yml +0 -0
  82. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/gitlab/slash_command_dispatch.yml +0 -0
  83. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/config/templates/workflow/gitlab/unit_test.yml +0 -0
  84. {osa_tool-0.2.3/osa_tool/convertion → osa_tool-0.2.4.0/osa_tool/conversion}/__init__.py +0 -0
  85. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/docs/images/osa_logo.PNG +0 -0
  86. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/docs/images/osa_logo_transparent.PNG +0 -0
  87. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/docs/templates/ci_config.toml +0 -0
  88. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/docs/templates/community.toml +0 -0
  89. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/docs/templates/contributing.toml +0 -0
  90. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/docs/templates/licenses.toml +0 -0
  91. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/docs/templates/mkdocs.yml +0 -0
  92. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/docs_generator/__init__.py +0 -0
  93. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/git_agent/__init__.py +0 -0
  94. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/models/__init__.py +0 -0
  95. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/models/models.py +0 -0
  96. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/osatreesitter/__init__.py +0 -0
  97. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/osatreesitter/osa_treesitter.py +0 -0
  98. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/__init__.py +0 -0
  99. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/context/__init__.py +0 -0
  100. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/context/citation_extractor.py +0 -0
  101. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/generator/__init__.py +0 -0
  102. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/generator/builder.py +0 -0
  103. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/generator/builder_article.py +0 -0
  104. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/generator/icons/shieldsio_icons.json +0 -0
  105. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/models/__init__.py +0 -0
  106. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/readmegen/postprocessor/__init__.py +0 -0
  107. {osa_tool-0.2.3/osa_tool/readmegen/prompts → osa_tool-0.2.4.0/osa_tool/scheduler}/__init__.py +0 -0
  108. {osa_tool-0.2.3/osa_tool/scheduler → osa_tool-0.2.4.0/osa_tool/translation}/__init__.py +0 -0
  109. {osa_tool-0.2.3/osa_tool/translation → osa_tool-0.2.4.0/osa_tool/ui}/__init__.py +0 -0
  110. {osa_tool-0.2.3/osa_tool/ui → osa_tool-0.2.4.0/osa_tool/utils}/__init__.py +0 -0
  111. {osa_tool-0.2.3/osa_tool/workflow → osa_tool-0.2.4.0/osa_tool/validation}/__init__.py +0 -0
  112. {osa_tool-0.2.3 → osa_tool-0.2.4.0}/osa_tool/workflow/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: osa_tool
3
- Version: 0.2.3
3
+ Version: 0.2.4.0
4
4
  Summary: Tool that just makes your open source project better!
5
5
  License-Expression: BSD-3-Clause
6
6
  License-File: LICENSE
@@ -13,73 +13,31 @@ Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Classifier: Programming Language :: Python :: 3.13
15
15
  Classifier: Programming Language :: Python :: 3.14
16
- Requires-Dist: ProtoLLM (>=0.1.5)
17
- Requires-Dist: PyYAML (==6.0.2)
18
- Requires-Dist: Pygments (==2.19.1)
19
- Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
20
- Requires-Dist: aiohappyeyeballs (==2.4.4)
21
- Requires-Dist: aiohttp (==3.11.11)
22
- Requires-Dist: aiosignal (==1.3.2)
23
- Requires-Dist: annotated-types (==0.7.0)
24
- Requires-Dist: anyio (==4.7.0)
25
- Requires-Dist: async-timeout (>=4.0.0,<5.0.0)
26
- Requires-Dist: attrs (==25.1.0)
27
- Requires-Dist: black (==25.1.0)
28
- Requires-Dist: certifi (==2024.12.14)
29
- Requires-Dist: charset-normalizer (==3.4.1)
30
- Requires-Dist: colorama (==0.4.6)
31
- Requires-Dist: distro (==1.9.0)
32
- Requires-Dist: exceptiongroup (==1.2.2)
33
- Requires-Dist: frozenlist (==1.5.0)
34
- Requires-Dist: gitdb (==4.0.11)
35
- Requires-Dist: gitpython (==3.1.43)
36
- Requires-Dist: h11 (==0.16.0)
37
- Requires-Dist: httpcore (==1.0.9)
38
- Requires-Dist: httpx (>=0.27.2,<0.28.0)
39
- Requires-Dist: idna (==3.10)
40
- Requires-Dist: iniconfig (==2.0.0)
41
- Requires-Dist: jiter (==0.8.2)
42
- Requires-Dist: markdown-it-py (==3.0.0)
43
- Requires-Dist: mdurl (==0.1.2)
44
- Requires-Dist: mkdocs (==1.6.1)
45
- Requires-Dist: mkdocs-material (==9.6.13)
46
- Requires-Dist: mkdocstrings[python] (==0.29.1)
47
- Requires-Dist: multidict (==6.1.0)
16
+ Requires-Dist: GitPython (>=3.1.40)
17
+ Requires-Dist: Pygments (>=2.18.0)
18
+ Requires-Dist: aiofiles (>=24.1.0)
19
+ Requires-Dist: black (>=25.0.0)
20
+ Requires-Dist: colorama (>=0.4.6)
21
+ Requires-Dist: docx2txt (>=0.9)
22
+ Requires-Dist: mkdocs (>=1.6.0)
23
+ Requires-Dist: mkdocs-material (>=9.6.0)
24
+ Requires-Dist: mkdocstrings-python (>=1.16.0)
25
+ Requires-Dist: mkdocstrings[python] (>=0.29.0)
48
26
  Requires-Dist: nbconvert (>=6.0)
49
27
  Requires-Dist: nbformat (>=5.0)
50
- Requires-Dist: openai (==1.58.1)
51
- Requires-Dist: openpyxl (==3.1.5)
52
- Requires-Dist: packaging (==24.2)
53
- Requires-Dist: pandas (==2.2.3)
54
- Requires-Dist: pdfminer.six (==20231228)
55
- Requires-Dist: pdfplumber (==0.11.5)
56
- Requires-Dist: pipreqs (>=0.5.0,<0.6.0) ; python_version < "3.13"
57
- Requires-Dist: pluggy (==1.5.0)
58
- Requires-Dist: prompt_toolkit (==3.0.51)
59
- Requires-Dist: propcache (==0.2.1)
60
- Requires-Dist: pydantic (>=2.9.0,<2.11.0)
61
- Requires-Dist: pydantic-extra-types (==2.9.0)
62
- Requires-Dist: pydantic_core (>=2.23.4,<2.24.0)
63
- Requires-Dist: pytest (==8.3.4)
64
- Requires-Dist: pytest-asyncio (==1.1.0)
65
- Requires-Dist: pytest-mock (==3.14.0)
66
- Requires-Dist: python-dotenv (==1.0.1)
67
- Requires-Dist: qrcode (==8.0)
68
- Requires-Dist: regex (==2024.11.6)
69
- Requires-Dist: reportlab (==4.3.1)
70
- Requires-Dist: requests (==2.32.3)
71
- Requires-Dist: rich (==13.9.4)
72
- Requires-Dist: smmap (==5.0.2)
73
- Requires-Dist: sniffio (==1.3.1)
74
- Requires-Dist: structlog (==24.4.0)
75
- Requires-Dist: tiktoken (==0.8.0)
76
- Requires-Dist: tomli (==2.2.1)
77
- Requires-Dist: tqdm (==4.67.1)
78
- Requires-Dist: tree-sitter (==0.23.2)
79
- Requires-Dist: tree-sitter-python (==0.23.4)
80
- Requires-Dist: typing_extensions (==4.12.2)
81
- Requires-Dist: urllib3 (==2.3.0)
82
- Requires-Dist: yarl (==1.18.3)
28
+ Requires-Dist: packaging (>=25.0)
29
+ Requires-Dist: pdfplumber (>=0.11.0)
30
+ Requires-Dist: pipreqs (>=0.5.0) ; python_version < "3.13"
31
+ Requires-Dist: prompt_toolkit (>=3.0.50)
32
+ Requires-Dist: protollm (>=0.1.6)
33
+ Requires-Dist: pydantic (>=2.9.0,<3.0.0)
34
+ Requires-Dist: qrcode (>=7.4.0)
35
+ Requires-Dist: reportlab (>=4.2.0,<5.0.0)
36
+ Requires-Dist: structlog (>=24.4.0)
37
+ Requires-Dist: tiktoken (>=0.8.0)
38
+ Requires-Dist: tomli (>=2.0.0)
39
+ Requires-Dist: tree-sitter (>=0.23.0)
40
+ Requires-Dist: tree-sitter-python (>=0.23.0)
83
41
  Project-URL: Documentation, https://itmo-nss-team.github.io/Open-Source-Advisor/
84
42
  Project-URL: Homepage, https://github.com/aimclub/OSA
85
43
  Project-URL: Issues, https://github.com/aimclub/OSA/issues
@@ -123,8 +81,6 @@ It also generates advices and recommendations for the repository.
123
81
 
124
82
  OSA is currently under development, so not all features are implemented.
125
83
 
126
-
127
-
128
84
  ---
129
85
 
130
86
  ## How it works?
@@ -164,7 +120,7 @@ Here is a short video:
164
120
  4. **Various LLMs**: Use OSA with an LLM accessible via API (e.g., OpenAI, VseGPT, Ollama), a local server, or try
165
121
  an [osa_bot](https://github.com/osa-bot) hosted on ITMO servers.
166
122
 
167
- 5. **GitHub Action Workflow Generator**: Automatically generates customizable CI/CD workflows for Python repositories,
123
+ 5. **Workflow Generator**: Automatically generates customizable CI/CD workflows for Python repositories,
168
124
  including unit tests, code formatting, PEP 8 compliance checks, and PyPI publication.
169
125
 
170
126
  ---
@@ -224,8 +180,9 @@ docker build --build-arg GIT_USER_NAME="your-user-name" --build-arg GIT_USER_EMA
224
180
 
225
181
  OSA requires Python 3.10 or higher.
226
182
 
227
- File `.env` is required to specify GitHub/GitLab/Gitverse token (GIT_TOKEN) and LLM API key (OPENAI_API_KEY or
228
- AUTHORIZATION_KEY)
183
+ The .env file is required to specify the LLM API key (OPENAI_API_KEY or AUTHORIZATION_KEY) and optionally a Git token. The Git token (GIT_TOKEN) may be omitted if you plan to work with a public repository without creating a fork (using the --no-fork option).
184
+
185
+ Alternatively, instead of GIT_TOKEN, you can use GITHUB_TOKEN, GITLAB_TOKEN, or GITVERSE_TOKEN for GitHub, GitLab, and Gitverse respectively.
229
186
 
230
187
  When running `osa-tool` from CLI, you need to set the GIT_TOKEN and API key first:
231
188
 
@@ -256,18 +213,18 @@ Run Open-Source-Advisor using the following command:
256
213
  [<img align="center" src="https://img.shields.io/badge/Pip-3776AB.svg?style={badge_style}&logo=pypi&logoColor=white" />](https://pypi.org/project/pip/)
257
214
 
258
215
  ```sh
259
- python -m osa_tool.run -r {repository} [--api {api}] [--base-url {base_url}] [--model {model_name}] [--article {article}] [--convert-notebooks {notebook_paths}]
216
+ python -m osa_tool.run -r {repository} [--api {api}] [--base-url {base_url}] [--model {model_name}] [--attachment {article}] [--convert-notebooks {notebook_paths}]
260
217
  ```
261
218
 
262
219
  **Using `docker`** &nbsp;
263
220
  [<img align="center" src="https://img.shields.io/badge/Docker-2CA5E0.svg?style={badge_style}&logo=docker&logoColor=white" />](https://www.docker.com/)
264
221
 
265
222
  ```sh
266
- docker run --env-file .env {image-name} -r {repository} [--api {api}] [--base-url {base_url}] [--model {model_name}] [--article {article}] [--convert-notebooks {notebook_paths}]
223
+ docker run --env-file .env {image-name} -r {repository} [--api {api}] [--base-url {base_url}] [--model {model_name}] [--attachment {article}] [--convert-notebooks {notebook_paths}]
267
224
  ```
268
225
 
269
- The --article option enables you to choose a README template for a repository based on an article. You can provide
270
- either a link to a PDF file of the article or a path to a local PDF file after the --article option. If you are using
226
+ The --attachment option enables you to choose a README template for a repository based on an article. You can provide
227
+ either a link to a PDF file of the article or a path to a local PDF file after the --attachment option. If you are using
271
228
  Docker, ensure that you upload the PDF file to the OSA folder before building the image, then, specify the path as
272
229
  /app/OSA/... or just use volume mounting to access the file.
273
230
 
@@ -287,7 +244,7 @@ documentation, see the [Workflow Generator README](./osa_tool/workflow/README.md
287
244
  | `--top_p` | Nucleus sampling probability | `None` |
288
245
  | `--temperature` | Sampling temperature to use for the LLM output (0 = deterministic, 1 = creative). | `None` |
289
246
  | `--max_tokens` | Maximum number of tokens the model can generate in a single response | `None` |
290
- | `--article` | Link to the pdf file of the article | `None` |
247
+ | `--attachment` | Path to a local PDF or .docx file, or a URL to a PDF resource | `None` |
291
248
  | `-m`, `--mode` | Operation mode for repository processing: `basic`, `auto` (default), or `advanced`. | `auto` |
292
249
  | `--delete-dir` | Enable deleting the downloaded repository after processing | `disabled` |
293
250
  | `--no-fork` | Avoid create fork for target repository | `False` |
@@ -36,8 +36,6 @@ It also generates advices and recommendations for the repository.
36
36
 
37
37
  OSA is currently under development, so not all features are implemented.
38
38
 
39
-
40
-
41
39
  ---
42
40
 
43
41
  ## How it works?
@@ -77,7 +75,7 @@ Here is a short video:
77
75
  4. **Various LLMs**: Use OSA with an LLM accessible via API (e.g., OpenAI, VseGPT, Ollama), a local server, or try
78
76
  an [osa_bot](https://github.com/osa-bot) hosted on ITMO servers.
79
77
 
80
- 5. **GitHub Action Workflow Generator**: Automatically generates customizable CI/CD workflows for Python repositories,
78
+ 5. **Workflow Generator**: Automatically generates customizable CI/CD workflows for Python repositories,
81
79
  including unit tests, code formatting, PEP 8 compliance checks, and PyPI publication.
82
80
 
83
81
  ---
@@ -137,8 +135,9 @@ docker build --build-arg GIT_USER_NAME="your-user-name" --build-arg GIT_USER_EMA
137
135
 
138
136
  OSA requires Python 3.10 or higher.
139
137
 
140
- File `.env` is required to specify GitHub/GitLab/Gitverse token (GIT_TOKEN) and LLM API key (OPENAI_API_KEY or
141
- AUTHORIZATION_KEY)
138
+ The .env file is required to specify the LLM API key (OPENAI_API_KEY or AUTHORIZATION_KEY) and optionally a Git token. The Git token (GIT_TOKEN) may be omitted if you plan to work with a public repository without creating a fork (using the --no-fork option).
139
+
140
+ Alternatively, instead of GIT_TOKEN, you can use GITHUB_TOKEN, GITLAB_TOKEN, or GITVERSE_TOKEN for GitHub, GitLab, and Gitverse respectively.
142
141
 
143
142
  When running `osa-tool` from CLI, you need to set the GIT_TOKEN and API key first:
144
143
 
@@ -169,18 +168,18 @@ Run Open-Source-Advisor using the following command:
169
168
  [<img align="center" src="https://img.shields.io/badge/Pip-3776AB.svg?style={badge_style}&logo=pypi&logoColor=white" />](https://pypi.org/project/pip/)
170
169
 
171
170
  ```sh
172
- python -m osa_tool.run -r {repository} [--api {api}] [--base-url {base_url}] [--model {model_name}] [--article {article}] [--convert-notebooks {notebook_paths}]
171
+ python -m osa_tool.run -r {repository} [--api {api}] [--base-url {base_url}] [--model {model_name}] [--attachment {article}] [--convert-notebooks {notebook_paths}]
173
172
  ```
174
173
 
175
174
  **Using `docker`** &nbsp;
176
175
  [<img align="center" src="https://img.shields.io/badge/Docker-2CA5E0.svg?style={badge_style}&logo=docker&logoColor=white" />](https://www.docker.com/)
177
176
 
178
177
  ```sh
179
- docker run --env-file .env {image-name} -r {repository} [--api {api}] [--base-url {base_url}] [--model {model_name}] [--article {article}] [--convert-notebooks {notebook_paths}]
178
+ docker run --env-file .env {image-name} -r {repository} [--api {api}] [--base-url {base_url}] [--model {model_name}] [--attachment {article}] [--convert-notebooks {notebook_paths}]
180
179
  ```
181
180
 
182
- The --article option enables you to choose a README template for a repository based on an article. You can provide
183
- either a link to a PDF file of the article or a path to a local PDF file after the --article option. If you are using
181
+ The --attachment option enables you to choose a README template for a repository based on an article. You can provide
182
+ either a link to a PDF file of the article or a path to a local PDF file after the --attachment option. If you are using
184
183
  Docker, ensure that you upload the PDF file to the OSA folder before building the image, then, specify the path as
185
184
  /app/OSA/... or just use volume mounting to access the file.
186
185
 
@@ -200,7 +199,7 @@ documentation, see the [Workflow Generator README](./osa_tool/workflow/README.md
200
199
  | `--top_p` | Nucleus sampling probability | `None` |
201
200
  | `--temperature` | Sampling temperature to use for the LLM output (0 = deterministic, 1 = creative). | `None` |
202
201
  | `--max_tokens` | Maximum number of tokens the model can generate in a single response | `None` |
203
- | `--article` | Link to the pdf file of the article | `None` |
202
+ | `--attachment` | Path to a local PDF or .docx file, or a URL to a PDF resource | `None` |
204
203
  | `-m`, `--mode` | Operation mode for repository processing: `basic`, `auto` (default), or `advanced`. | `auto` |
205
204
  | `--delete-dir` | Enable deleting the downloaded repository after processing | `disabled` |
206
205
  | `--no-fork` | Avoid create fork for target repository | `False` |
@@ -2,11 +2,12 @@ import os
2
2
  import re
3
3
  from typing import List
4
4
 
5
- from osa_tool.aboutgen.prompts_about_config import PromptAboutLoader
6
5
  from osa_tool.config.settings import ConfigLoader
7
6
  from osa_tool.git_agent.git_agent import GitAgent
8
7
  from osa_tool.models.models import ModelHandler, ModelHandlerFactory
9
- from osa_tool.utils import extract_readme_content, logger, parse_folder_name
8
+ from osa_tool.utils.logger import logger
9
+ from osa_tool.utils.prompts_builder import PromptLoader, PromptBuilder
10
+ from osa_tool.utils.utils import extract_readme_content, parse_folder_name
10
11
 
11
12
  HOMEPAGE_KEYS = [
12
13
  "documentation",
@@ -23,16 +24,15 @@ HOMEPAGE_KEYS = [
23
24
  class AboutGenerator:
24
25
  """Generates Git repository About section content."""
25
26
 
26
- def __init__(self, config_loader: ConfigLoader, git_agent: GitAgent):
27
+ def __init__(self, config_loader: ConfigLoader, prompts: PromptLoader, git_agent: GitAgent):
27
28
  self.config = config_loader.config
29
+ self.prompts = prompts
28
30
  self.model_handler: ModelHandler = ModelHandlerFactory.build(self.config)
29
31
  self.repo_url = self.config.git.repository
30
32
  self.metadata = git_agent.metadata
31
33
  self.base_path = os.path.join(os.getcwd(), parse_folder_name(self.repo_url))
32
34
  self.readme_content = extract_readme_content(self.base_path)
33
- self.prompts = PromptAboutLoader().prompts
34
35
  self.validate_topics = git_agent.validate_topics
35
-
36
36
  self._content: dict | None = None
37
37
 
38
38
  def generate_about_content(self) -> None:
@@ -92,10 +92,13 @@ class AboutGenerator:
92
92
  logger.warning("No README content found. Cannot generate description.")
93
93
  return ""
94
94
 
95
- formatted_prompt = self.prompts.description.format(readme_content=self.readme_content)
95
+ prompt = PromptBuilder.render(
96
+ self.prompts.get("about_section.description"),
97
+ readme_content=self.readme_content,
98
+ )
96
99
 
97
100
  try:
98
- description = self.model_handler.send_request(formatted_prompt)
101
+ description = self.model_handler.send_request(prompt)
99
102
  logger.debug(f"Generated description: {description}")
100
103
  return description[:350]
101
104
  except Exception as e:
@@ -123,14 +126,15 @@ class AboutGenerator:
123
126
  logger.warning(f"{amount} topics already exist in the metadata. Skipping generation.")
124
127
  return existing_topics
125
128
 
126
- formatted_prompt = self.prompts.topics.format(
129
+ prompt = PromptBuilder.render(
130
+ self.prompts.get("about_section.topics"),
131
+ readme_content=self.readme_content,
127
132
  amount=amount,
128
133
  topics=existing_topics,
129
- readme_content=self.readme_content,
130
134
  )
131
135
 
132
136
  try:
133
- response = self.model_handler.send_request(formatted_prompt)
137
+ response = self.model_handler.send_request(prompt)
134
138
  topics = [topic.strip().lower().replace(" ", "-") for topic in response.split(",") if topic.strip()]
135
139
  logger.debug(f"Generated topics from LLM: {topics}")
136
140
  validated_topics = self.validate_topics(topics)
@@ -181,8 +185,11 @@ class AboutGenerator:
181
185
  def _analyze_urls(self, urls: List[str]) -> List[str]:
182
186
  """Generates LLM prompt for URL analysis"""
183
187
  logger.info(f"Analyzing {len(urls)} project URLs...")
184
- formatted_prompt = self.prompts.analyze_urls.format(project_url=self.repo_url, urls=", ".join(urls))
185
- response = self.model_handler.send_request(formatted_prompt)
188
+
189
+ prompt = PromptBuilder.render(
190
+ self.prompts.get("about_section.analyze_urls"), project_url=self.repo_url, urls=", ".join(urls)
191
+ )
192
+ response = self.model_handler.send_request(prompt)
186
193
  if not response:
187
194
  return []
188
195
 
@@ -8,7 +8,8 @@ import requests
8
8
  from dotenv import load_dotenv
9
9
  from requests import HTTPError
10
10
 
11
- from osa_tool.utils import get_base_repo_url, logger
11
+ from osa_tool.utils.logger import logger
12
+ from osa_tool.utils.utils import get_base_repo_url
12
13
 
13
14
  load_dotenv()
14
15
 
@@ -80,34 +81,38 @@ class MetadataLoader(ABC):
80
81
  RepositoryMetadata: Parsed repository metadata.
81
82
  """
82
83
  try:
83
- return cls._load_platform_data(repo_url)
84
-
85
- except HTTPError as http_exc:
86
- status_code = getattr(http_exc.response, "status_code", None)
87
- logger.error(f"Error while fetching repository metadata: {http_exc}")
88
-
89
- if status_code == 401:
90
- logger.error("Authentication failed: please check your Git token (missing or expired).")
91
- elif status_code == 404:
92
- logger.error("Repository not found: please check the repository URL.")
93
- elif status_code == 403:
94
- logger.error("Access denied: your token may lack sufficient permissions or you hit a rate limit.")
95
- else:
96
- logger.error("Unexpected HTTP error occurred while accessing the repository metadata.")
97
- raise
98
-
99
- except Exception as exc:
100
- logger.error(f"Unexpected error while fetching repository metadata: {exc}")
101
- raise
84
+ return cls._load_platform_data(repo_url, use_token=False)
85
+ except Exception:
86
+ try:
87
+ return cls._load_platform_data(repo_url, use_token=True)
88
+
89
+ except HTTPError as http_exc:
90
+ status_code = getattr(http_exc.response, "status_code", None)
91
+ logger.error(f"Error while fetching repository metadata: {http_exc}")
92
+
93
+ if status_code == 401:
94
+ logger.error("Authentication failed: please check your Git token (missing or expired).")
95
+ elif status_code == 404:
96
+ logger.error("Repository not found: please check the repository URL.")
97
+ elif status_code == 403:
98
+ logger.error("Access denied: your token may lack sufficient permissions or you hit a rate limit.")
99
+ else:
100
+ logger.error("Unexpected HTTP error occurred while accessing the repository metadata.")
101
+ raise
102
+
103
+ except Exception as exc:
104
+ logger.error(f"Unexpected error while fetching repository metadata: {exc}")
105
+ raise
102
106
 
103
107
  @classmethod
104
108
  @abstractmethod
105
- def _load_platform_data(cls, repo_url: str) -> RepositoryMetadata:
109
+ def _load_platform_data(cls, repo_url: str, use_token: bool) -> RepositoryMetadata:
106
110
  """
107
111
  Abstract method to load metadata from a platform-specific API.
108
112
 
109
113
  Args:
110
114
  repo_url (str): The full URL of the repository.
115
+ use_token (bool): Whether to use authentication token.
111
116
 
112
117
  Returns:
113
118
  RepositoryMetadata: Parsed repository metadata.
@@ -131,7 +136,7 @@ class MetadataLoader(ABC):
131
136
 
132
137
  class GitHubMetadataLoader(MetadataLoader):
133
138
  @classmethod
134
- def _load_platform_data(cls, repo_url: str) -> RepositoryMetadata:
139
+ def _load_platform_data(cls, repo_url: str, use_token: bool) -> RepositoryMetadata:
135
140
  """
136
141
  Load GitHub repository metadata via GitHub API.
137
142
 
@@ -143,9 +148,12 @@ class GitHubMetadataLoader(MetadataLoader):
143
148
  """
144
149
  base_url = get_base_repo_url(repo_url)
145
150
  headers = {
146
- "Authorization": f"token {os.getenv('GIT_TOKEN', os.getenv('GITHUB_TOKEN', ''))}",
147
151
  "Accept": "application/vnd.github.v3+json",
148
152
  }
153
+
154
+ if use_token:
155
+ headers["Authorization"] = f"token {os.getenv('GIT_TOKEN', os.getenv('GITHUB_TOKEN', ''))}"
156
+
149
157
  url = f"https://api.github.com/repos/{base_url}"
150
158
  response = requests.get(url=url, headers=headers)
151
159
  response.raise_for_status()
@@ -203,7 +211,7 @@ class GitHubMetadataLoader(MetadataLoader):
203
211
 
204
212
  class GitLabMetadataLoader(MetadataLoader):
205
213
  @classmethod
206
- def _load_platform_data(cls, repo_url: str) -> RepositoryMetadata:
214
+ def _load_platform_data(cls, repo_url: str, use_token: bool) -> RepositoryMetadata:
207
215
  """
208
216
  Load GitLab repository metadata via GitLab API.
209
217
 
@@ -220,9 +228,12 @@ class GitLabMetadataLoader(MetadataLoader):
220
228
  gitlab_instance = gitlab_instance_match.group(1)
221
229
 
222
230
  headers = {
223
- "Authorization": f"Bearer {os.getenv('GITLAB_TOKEN', os.getenv('GIT_TOKEN'))}",
224
231
  "Content-Type": "application/json",
225
232
  }
233
+
234
+ if use_token:
235
+ headers["Authorization"] = f"Bearer {os.getenv('GITLAB_TOKEN', os.getenv('GIT_TOKEN'))}"
236
+
226
237
  project_path = base_url.replace("/", "%2F")
227
238
  url = f"{gitlab_instance}/api/v4/projects/{project_path}"
228
239
 
@@ -289,7 +300,7 @@ class GitLabMetadataLoader(MetadataLoader):
289
300
 
290
301
  class GitverseMetadataLoader(MetadataLoader):
291
302
  @classmethod
292
- def _load_platform_data(cls, repo_url: str) -> RepositoryMetadata:
303
+ def _load_platform_data(cls, repo_url: str, use_token: bool) -> RepositoryMetadata:
293
304
  """
294
305
  Load Gitverse repository metadata via Gitverse API.
295
306
 
@@ -1,10 +1,9 @@
1
1
  import os
2
2
 
3
- import tomli as tomllib
4
3
  from pydantic import ValidationError
5
4
 
6
5
  from osa_tool.analytics.metadata import RepositoryMetadata
7
- from osa_tool.analytics.prompt_builder import (
6
+ from osa_tool.analytics.response_validation import (
8
7
  RepositoryReport,
9
8
  RepositoryStructure,
10
9
  ReadmeEvaluation,
@@ -15,18 +14,22 @@ from osa_tool.analytics.sourcerank import SourceRank
15
14
  from osa_tool.config.settings import ConfigLoader
16
15
  from osa_tool.models.models import ModelHandler, ModelHandlerFactory
17
16
  from osa_tool.readmegen.postprocessor.response_cleaner import JsonProcessor
18
- from osa_tool.utils import extract_readme_content, osa_project_root, parse_folder_name, logger
17
+ from osa_tool.utils.logger import logger
18
+ from osa_tool.utils.prompts_builder import PromptLoader, PromptBuilder
19
+ from osa_tool.utils.utils import extract_readme_content, parse_folder_name
19
20
 
20
21
 
21
22
  class TextGenerator:
22
- def __init__(self, config_loader: ConfigLoader, sourcerank: SourceRank, metadata: RepositoryMetadata):
23
+ def __init__(
24
+ self, config_loader: ConfigLoader, sourcerank: SourceRank, prompts: PromptLoader, metadata: RepositoryMetadata
25
+ ):
23
26
  self.config = config_loader.config
24
27
  self.sourcerank = sourcerank
28
+ self.prompts = prompts
29
+ self.metadata = metadata
25
30
  self.model_handler: ModelHandler = ModelHandlerFactory.build(self.config)
26
31
  self.repo_url = self.config.git.repository
27
- self.metadata = metadata
28
32
  self.base_path = os.path.join(os.getcwd(), parse_folder_name(self.repo_url))
29
- self.prompt_path = os.path.join(osa_project_root(), "config", "settings", "prompt_for_analysis.toml")
30
33
 
31
34
  def make_request(self) -> RepositoryReport:
32
35
  """
@@ -35,7 +38,16 @@ class TextGenerator:
35
38
  Returns:
36
39
  str: The generated repository analysis response from the model.
37
40
  """
38
- response = self.model_handler.send_request(self._build_prompt())
41
+ response = self.model_handler.send_request(
42
+ PromptBuilder.render(
43
+ self.prompts.get("analysis.main_prompt"),
44
+ project_name=self.metadata.name,
45
+ metadata=self.metadata,
46
+ repository_tree=self.sourcerank.tree,
47
+ presence_files=self._extract_presence_files(),
48
+ readme_content=extract_readme_content(self.base_path),
49
+ )
50
+ )
39
51
  parsed_data = JsonProcessor.parse(response, expected_type=dict)
40
52
  try:
41
53
  parsed_report = RepositoryReport.model_validate(parsed_data)
@@ -55,30 +67,6 @@ class TextGenerator:
55
67
  logger.error(f"Unexpected error while parsing RepositoryReport: {e}")
56
68
  raise ValueError(f"Failed to process model response: {e}")
57
69
 
58
- def _build_prompt(self) -> str:
59
- """
60
- Builds the prompt to be sent to the model for repository analysis.
61
-
62
- This method loads the prompt structure from a file and formats it with values
63
- extracted from the repository's metadata and other relevant information like
64
- the project name, presence of key files, and repository tree.
65
-
66
- Returns:
67
- str: The formatted prompt to be used in the model request.
68
- """
69
- with open(self.prompt_path, "rb") as f:
70
- prompts = tomllib.load(f)
71
-
72
- main_prompt = prompts.get("prompt", {}).get("main_prompt", "")
73
- prompt = main_prompt.format(
74
- project_name=self.metadata.name,
75
- metadata=self.metadata,
76
- repository_tree=self.sourcerank.tree,
77
- presence_files=self._extract_presence_files(),
78
- readme_content=extract_readme_content(self.base_path),
79
- )
80
- return prompt
81
-
82
70
  def _extract_presence_files(self) -> list[str]:
83
71
  """
84
72
  Extracts information about the presence of key files in the repository.
@@ -21,16 +21,20 @@ from osa_tool.analytics.metadata import RepositoryMetadata
21
21
  from osa_tool.analytics.report_generator import TextGenerator
22
22
  from osa_tool.analytics.sourcerank import SourceRank
23
23
  from osa_tool.config.settings import ConfigLoader
24
- from osa_tool.utils import logger, osa_project_root
24
+ from osa_tool.utils.logger import logger
25
+ from osa_tool.utils.prompts_builder import PromptLoader
26
+ from osa_tool.utils.utils import osa_project_root
25
27
 
26
28
 
27
29
  class ReportGenerator:
28
30
 
29
- def __init__(self, config_loader: ConfigLoader, sourcerank: SourceRank, metadata: RepositoryMetadata):
31
+ def __init__(
32
+ self, config_loader: ConfigLoader, sourcerank: SourceRank, prompts: PromptLoader, metadata: RepositoryMetadata
33
+ ):
30
34
  self.config = config_loader.config
31
35
  self.sourcerank = sourcerank
32
36
  self.metadata = metadata
33
- self.text_generator = TextGenerator(config_loader, self.sourcerank, self.metadata)
37
+ self.text_generator = TextGenerator(config_loader, self.sourcerank, prompts, self.metadata)
34
38
  self.repo_url = self.config.git.repository
35
39
  self.osa_url = "https://github.com/aimclub/OSA"
36
40
 
@@ -2,7 +2,7 @@ import os
2
2
  import re
3
3
 
4
4
  from osa_tool.config.settings import ConfigLoader
5
- from osa_tool.utils import get_repo_tree, parse_folder_name
5
+ from osa_tool.utils.utils import get_repo_tree, parse_folder_name
6
6
 
7
7
 
8
8
  class SourceRank:
@@ -1,4 +1,4 @@
1
- [prompt]
1
+ [prompts]
2
2
  main_prompt = """
3
3
  INPUT DATA:
4
4