osa_tool 0.2.0__tar.gz → 0.2.1__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 (87) hide show
  1. {osa_tool-0.2.0 → osa_tool-0.2.1}/PKG-INFO +23 -9
  2. {osa_tool-0.2.0 → osa_tool-0.2.1}/README.md +21 -8
  3. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/aboutgen/about_generator.py +2 -3
  4. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/analytics/metadata.py +22 -7
  5. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/analytics/run_report_gen.py +2 -2
  6. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/analytics/sourcerank.py +1 -1
  7. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/config/settings/arguments.yaml +10 -3
  8. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/config/settings/config.toml +1 -1
  9. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/config/settings.py +1 -1
  10. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/git_agent/git_agent.py +36 -17
  11. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/models/models.py +40 -48
  12. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/osatreesitter/docgen.py +393 -235
  13. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/osatreesitter/osa_treesitter.py +3 -4
  14. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/run.py +43 -16
  15. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/scheduler/README.md +22 -21
  16. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/ui/plan_editor.py +1 -0
  17. {osa_tool-0.2.0 → osa_tool-0.2.1}/pyproject.toml +2 -1
  18. {osa_tool-0.2.0 → osa_tool-0.2.1}/LICENSE +0 -0
  19. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/__init__.py +0 -0
  20. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/aboutgen/__init__.py +0 -0
  21. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/aboutgen/prompts_about_config.py +0 -0
  22. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/analytics/__init__.py +0 -0
  23. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/analytics/prompt_builder.py +0 -0
  24. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/analytics/report_generator.py +0 -0
  25. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/analytics/report_maker.py +0 -0
  26. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/arguments_parser.py +0 -0
  27. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/config/__init__.py +0 -0
  28. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/config/settings/prompt_for_analysis.toml +0 -0
  29. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/config/settings/prompt_for_scheduler.toml +0 -0
  30. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/config/settings/prompts.toml +0 -0
  31. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/config/settings/prompts_about_section.toml +0 -0
  32. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/config/settings/prompts_article.toml +0 -0
  33. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/config/templates/template.toml +0 -0
  34. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/convertion/__init__.py +0 -0
  35. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/convertion/notebook_converter.py +0 -0
  36. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/docs/images/osa_logo.PNG +0 -0
  37. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/docs/images/osa_logo_transparent.PNG +0 -0
  38. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/docs/templates/community.toml +0 -0
  39. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/docs/templates/contributing.toml +0 -0
  40. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/docs/templates/licenses.toml +0 -0
  41. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/docs/templates/mkdocs.yml +0 -0
  42. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/docs_generator/__init__.py +0 -0
  43. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/docs_generator/community.py +0 -0
  44. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/docs_generator/contributing.py +0 -0
  45. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/docs_generator/docs_run.py +0 -0
  46. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/docs_generator/license.py +0 -0
  47. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/git_agent/__init__.py +0 -0
  48. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/github_workflow/README.md +0 -0
  49. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/github_workflow/__init__.py +0 -0
  50. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/github_workflow/generator.py +0 -0
  51. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/github_workflow/providers/black.py +0 -0
  52. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/github_workflow/providers/unit_test.py +0 -0
  53. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/models/__init__.py +0 -0
  54. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/organization/repo_organizer.py +0 -0
  55. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/osatreesitter/__init__.py +0 -0
  56. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/__init__.py +0 -0
  57. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/context/__init__.py +0 -0
  58. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/context/article_content.py +0 -0
  59. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/context/article_path.py +0 -0
  60. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/context/dependencies.py +0 -0
  61. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/context/files_contents.py +0 -0
  62. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/context/pypi_status_checker.py +0 -0
  63. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/generator/__init__.py +0 -0
  64. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/generator/base_builder.py +0 -0
  65. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/generator/builder.py +0 -0
  66. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/generator/builder_article.py +0 -0
  67. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/generator/header.py +0 -0
  68. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/generator/icons/shieldsio_icons.json +0 -0
  69. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/generator/installation.py +0 -0
  70. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/models/__init__.py +0 -0
  71. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/models/llm_service.py +0 -0
  72. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/postprocessor/__init__.py +0 -0
  73. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/postprocessor/readme_refiner.py +0 -0
  74. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/postprocessor/response_cleaner.py +0 -0
  75. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/prompts/__init__.py +0 -0
  76. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/prompts/prompts_builder.py +0 -0
  77. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/readme_core.py +0 -0
  78. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/readmegen/utils.py +0 -0
  79. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/scheduler/__init__.py +0 -0
  80. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/scheduler/prompts.py +0 -0
  81. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/scheduler/scheduler.py +0 -0
  82. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/scheduler/workflow_manager.py +0 -0
  83. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/translation/__init__.py +0 -0
  84. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/translation/dir_translator.py +0 -0
  85. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/ui/__init__.py +0 -0
  86. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/ui/web_plan_editor.py +0 -0
  87. {osa_tool-0.2.0 → osa_tool-0.2.1}/osa_tool/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: osa_tool
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Tool that just makes your open source project better!
5
5
  License: BSD-3-Clause
6
6
  Author: ITMO-NSS-team
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
15
15
  Requires-Dist: ProtoLLM (>=0.1.2)
16
16
  Requires-Dist: PyYAML (==6.0.2)
17
17
  Requires-Dist: Pygments (==2.19.1)
18
+ Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
18
19
  Requires-Dist: aiohappyeyeballs (==2.4.4)
19
20
  Requires-Dist: aiohttp (==3.11.11)
20
21
  Requires-Dist: aiosignal (==1.3.2)
@@ -137,6 +138,8 @@ Here is a short demo:
137
138
  - [Getting started](#getting-started)
138
139
  - [Examples](#examples)
139
140
  - [Documentation](#documentation)
141
+ - [Chat with developers: OSA_helpdesk](#chat-with-developers-osa_helpdesk)
142
+ - [Publications about OSA](#publications-about-osa)
140
143
  - [Contributing](#contributing)
141
144
  - [License](#license)
142
145
  - [Acknowledgments](#acknowledgments)
@@ -217,7 +220,7 @@ docker build --build-arg GIT_USER_NAME="your-user-name" --build-arg GIT_USER_EMA
217
220
 
218
221
  OSA requires Python 3.10 or higher.
219
222
 
220
- File `.env` is required to specify GitHub token (GIT_TOKEN) and LLM API key (OPENAI_API_KEY or VSE_GPT_KEY)
223
+ File `.env` is required to specify GitHub/GitLab/Gitverse token (GIT_TOKEN) and LLM API key (OPENAI_API_KEY or VSE_GPT_KEY)
221
224
 
222
225
  When running `osa-tool` from CLI, you need to set the GIT_TOKEN and API key first:
223
226
 
@@ -230,7 +233,7 @@ export GIT_TOKEN=<your_git_token>
230
233
 
231
234
  | Token name | Description | Mandatory |
232
235
  |------------------|--------------------------------------------------------------------------------------------------------------------|-----------|
233
- | `GIT_TOKEN` | Personal GitHub/GitLab token used to clone private repositories, access metadata, and interact with the GitHub/GitLab API. | Yes |
236
+ | `GIT_TOKEN` | Personal GitHub/GitLab/Gitverse token used to clone private repositories, access metadata, and interact with its API. | Yes |
234
237
  | `OPENAI_API_KEY` | API key for accessing [OpenAI](https://platform.openai.com/docs/api-reference/introduction)'s language models. | No |
235
238
  | `VSE_GPT_KEY` | API key for [vsegpt](https://vsegpt.ru/Docs/API) LLM provider compatible with OpenAI's API format. | No |
236
239
  | `X-API-Key` | API key for the [pepy.tech](https://pepy.tech/pepy-api) REST API, used to fetch Python package download statistics | No |
@@ -265,11 +268,14 @@ documentation, see the [GitHub Action Workflow Generator README](./osa_tool/gith
265
268
 
266
269
  | Flag | Description | Default |
267
270
  |----------------------|-------------------------------------------------------------------------------------|-----------------------------|
268
- | `-r`, `--repository` | URL of the GitHub repository (**Mandatory**) | |
269
- | `-b`, `--branch` | Branch name of the GitHub repository | Default branch |
270
- | `--api` | LLM API service provider | `llama` |
271
+ | `-r`, `--repository` | URL of the GitHub/GitLab/Gitverse repository (**Mandatory**) | |
272
+ | `-b`, `--branch` | Branch name of the repository | Default branch |
273
+ | `-o`, `--output` | Path to the output directory | Current working directory |
274
+ | `--api` | LLM API service provider | `itmo` |
271
275
  | `--base-url` | URL of the provider compatible with API OpenAI | `https://api.openai.com/v1` |
272
276
  | `--model` | Specific LLM model to use | `gpt-3.5-turbo` |
277
+ | `--article` | Link to the pdf file of the article | `None` |
278
+ | `--translate-dirs` | Enable automatic translation of the directory name into English | `disabled` |
273
279
  | `-m`, `--mode` | Operation mode for repository processing: `basic`, `auto` (default), or `advanced`. | `auto` |
274
280
  | `--delete-dir` | Enable deleting the downloaded repository after processing | `disabled` |
275
281
  | `--no-fork` | Avoid create fork for target repository | `False` |
@@ -287,7 +293,7 @@ the [CLI usage guide](./osa_tool/scheduler/README.md).
287
293
 
288
294
  Examples of generated README files are available in [examples](https://github.com/aimclub/OSA/tree/main/examples).
289
295
 
290
- URL of the GitHub repository, LLM API service provider (*optional*) and Specific LLM model to use (*optional*) are
296
+ URL of the GitHub/GitLab/Gitverse repository, LLM API service provider (*optional*) and Specific LLM model to use (*optional*) are
291
297
  required to use the generator.
292
298
 
293
299
  To see available models go there:
@@ -296,12 +302,14 @@ To see available models go there:
296
302
  2. [OpenAI](https://platform.openai.com/docs/models)
297
303
  3. [Ollama](https://ollama.com/library)
298
304
 
299
- Local Llama ITMO:
305
+ Local ITMO model:
300
306
 
301
307
  ```sh
302
- python -m osa_tool.run -r https://github.com/aimclub/OSA
308
+ python -m osa_tool.run -r https://github.com/aimclub/OSA --base_url [ITMO_MODEL_URL]
303
309
  ```
304
310
 
311
+ For this API provider itmo model url must be specified in dotenv (ITMO_MODEL_URL=) or in the --base-url argument.
312
+
305
313
  OpenAI:
306
314
 
307
315
  ```sh
@@ -328,6 +336,12 @@ Detailed description of OSA API is available [here](https://aimclub.github.io/OS
328
336
 
329
337
  ---
330
338
 
339
+ ## Chat with developers: OSA_helpdesk
340
+
341
+ In our Telegram chat [OSA_helpdesk](t.me/osa_helpdesk) you can ask questions about working with OSA and find the latest news about the project.
342
+
343
+ ---
344
+
331
345
  ## Publications about OSA
332
346
 
333
347
  In English:
@@ -53,6 +53,8 @@ Here is a short demo:
53
53
  - [Getting started](#getting-started)
54
54
  - [Examples](#examples)
55
55
  - [Documentation](#documentation)
56
+ - [Chat with developers: OSA_helpdesk](#chat-with-developers-osa_helpdesk)
57
+ - [Publications about OSA](#publications-about-osa)
56
58
  - [Contributing](#contributing)
57
59
  - [License](#license)
58
60
  - [Acknowledgments](#acknowledgments)
@@ -133,7 +135,7 @@ docker build --build-arg GIT_USER_NAME="your-user-name" --build-arg GIT_USER_EMA
133
135
 
134
136
  OSA requires Python 3.10 or higher.
135
137
 
136
- File `.env` is required to specify GitHub token (GIT_TOKEN) and LLM API key (OPENAI_API_KEY or VSE_GPT_KEY)
138
+ File `.env` is required to specify GitHub/GitLab/Gitverse token (GIT_TOKEN) and LLM API key (OPENAI_API_KEY or VSE_GPT_KEY)
137
139
 
138
140
  When running `osa-tool` from CLI, you need to set the GIT_TOKEN and API key first:
139
141
 
@@ -146,7 +148,7 @@ export GIT_TOKEN=<your_git_token>
146
148
 
147
149
  | Token name | Description | Mandatory |
148
150
  |------------------|--------------------------------------------------------------------------------------------------------------------|-----------|
149
- | `GIT_TOKEN` | Personal GitHub/GitLab token used to clone private repositories, access metadata, and interact with the GitHub/GitLab API. | Yes |
151
+ | `GIT_TOKEN` | Personal GitHub/GitLab/Gitverse token used to clone private repositories, access metadata, and interact with its API. | Yes |
150
152
  | `OPENAI_API_KEY` | API key for accessing [OpenAI](https://platform.openai.com/docs/api-reference/introduction)'s language models. | No |
151
153
  | `VSE_GPT_KEY` | API key for [vsegpt](https://vsegpt.ru/Docs/API) LLM provider compatible with OpenAI's API format. | No |
152
154
  | `X-API-Key` | API key for the [pepy.tech](https://pepy.tech/pepy-api) REST API, used to fetch Python package download statistics | No |
@@ -181,11 +183,14 @@ documentation, see the [GitHub Action Workflow Generator README](./osa_tool/gith
181
183
 
182
184
  | Flag | Description | Default |
183
185
  |----------------------|-------------------------------------------------------------------------------------|-----------------------------|
184
- | `-r`, `--repository` | URL of the GitHub repository (**Mandatory**) | |
185
- | `-b`, `--branch` | Branch name of the GitHub repository | Default branch |
186
- | `--api` | LLM API service provider | `llama` |
186
+ | `-r`, `--repository` | URL of the GitHub/GitLab/Gitverse repository (**Mandatory**) | |
187
+ | `-b`, `--branch` | Branch name of the repository | Default branch |
188
+ | `-o`, `--output` | Path to the output directory | Current working directory |
189
+ | `--api` | LLM API service provider | `itmo` |
187
190
  | `--base-url` | URL of the provider compatible with API OpenAI | `https://api.openai.com/v1` |
188
191
  | `--model` | Specific LLM model to use | `gpt-3.5-turbo` |
192
+ | `--article` | Link to the pdf file of the article | `None` |
193
+ | `--translate-dirs` | Enable automatic translation of the directory name into English | `disabled` |
189
194
  | `-m`, `--mode` | Operation mode for repository processing: `basic`, `auto` (default), or `advanced`. | `auto` |
190
195
  | `--delete-dir` | Enable deleting the downloaded repository after processing | `disabled` |
191
196
  | `--no-fork` | Avoid create fork for target repository | `False` |
@@ -203,7 +208,7 @@ the [CLI usage guide](./osa_tool/scheduler/README.md).
203
208
 
204
209
  Examples of generated README files are available in [examples](https://github.com/aimclub/OSA/tree/main/examples).
205
210
 
206
- URL of the GitHub repository, LLM API service provider (*optional*) and Specific LLM model to use (*optional*) are
211
+ URL of the GitHub/GitLab/Gitverse repository, LLM API service provider (*optional*) and Specific LLM model to use (*optional*) are
207
212
  required to use the generator.
208
213
 
209
214
  To see available models go there:
@@ -212,12 +217,14 @@ To see available models go there:
212
217
  2. [OpenAI](https://platform.openai.com/docs/models)
213
218
  3. [Ollama](https://ollama.com/library)
214
219
 
215
- Local Llama ITMO:
220
+ Local ITMO model:
216
221
 
217
222
  ```sh
218
- python -m osa_tool.run -r https://github.com/aimclub/OSA
223
+ python -m osa_tool.run -r https://github.com/aimclub/OSA --base_url [ITMO_MODEL_URL]
219
224
  ```
220
225
 
226
+ For this API provider itmo model url must be specified in dotenv (ITMO_MODEL_URL=) or in the --base-url argument.
227
+
221
228
  OpenAI:
222
229
 
223
230
  ```sh
@@ -244,6 +251,12 @@ Detailed description of OSA API is available [here](https://aimclub.github.io/OS
244
251
 
245
252
  ---
246
253
 
254
+ ## Chat with developers: OSA_helpdesk
255
+
256
+ In our Telegram chat [OSA_helpdesk](t.me/osa_helpdesk) you can ask questions about working with OSA and find the latest news about the project.
257
+
258
+ ---
259
+
247
260
  ## Publications about OSA
248
261
 
249
262
  In English:
@@ -87,7 +87,6 @@ class AboutGenerator:
87
87
  str: A repository description (up to 150 characters) or an empty string
88
88
  if README content is unavailable.
89
89
  """
90
- logger.info("Generating repository description...")
91
90
  if self.metadata and self.metadata.description:
92
91
  logger.warning("Description already exists in metadata. Skipping generation.")
93
92
  return self.metadata.description
@@ -203,7 +202,7 @@ class AboutGenerator:
203
202
  logger.error(f"Error validating topic '{topic}': {e}")
204
203
  continue
205
204
 
206
- logger.info(f"Validated {len(validated_topics)} topics out of {len(topics)}")
205
+ logger.info(f"Validated {len(validated_topics)} topics out of {len(topics)}.")
207
206
  return validated_topics
208
207
 
209
208
  def _validate_gitlab_topics(self, topics: List[str]) -> List[str]:
@@ -241,7 +240,7 @@ class AboutGenerator:
241
240
  logger.error(f"Error validating topic '{topic}': {e}")
242
241
  continue
243
242
 
244
- logger.info(f"Validated {len(validated_topics)} topics out of {len(topics)}")
243
+ logger.info(f"Validated {len(validated_topics)} topics out of {len(topics)}.")
245
244
  return validated_topics
246
245
 
247
246
  def _validate_gitverse_topics(self, topics: List[str]) -> List[str]:
@@ -6,6 +6,7 @@ from functools import lru_cache
6
6
 
7
7
  import requests
8
8
  from dotenv import load_dotenv
9
+ from requests import HTTPError
9
10
 
10
11
  from osa_tool.utils import get_base_repo_url, logger
11
12
 
@@ -193,7 +194,7 @@ def _parse_repository_metadata_gitverse(repo_data: dict) -> RepositoryMetadata:
193
194
  issues_url=repo_data.get("issues_url"),
194
195
  language=repo_data.get("language", ""),
195
196
  languages=repo_data.get("languages", []),
196
- topics=repo_data.get("topics", []),
197
+ topics=repo_data.get("topics", []) or [],
197
198
  has_wiki=repo_data.get("has_wiki", False),
198
199
  has_issues=repo_data.get("has_issues", False),
199
200
  has_projects=repo_data.get("has_projects", False),
@@ -206,7 +207,7 @@ def _parse_repository_metadata_gitverse(repo_data: dict) -> RepositoryMetadata:
206
207
 
207
208
 
208
209
  @lru_cache(maxsize=1)
209
- def load_data_metadata(repo_url: str) -> RepositoryMetadata | None:
210
+ def load_data_metadata(repo_url: str) -> RepositoryMetadata:
210
211
  """Retrieve repository metadata from any supported platform."""
211
212
  try:
212
213
  platform = detect_platform(repo_url)
@@ -219,9 +220,23 @@ def load_data_metadata(repo_url: str) -> RepositoryMetadata | None:
219
220
  else:
220
221
  return _load_gitverse_metadata(base_url)
221
222
 
223
+ except HTTPError as http_exc:
224
+ status_code = getattr(http_exc.response, "status_code", None)
225
+ logger.error(f"Error while fetching repository metadata: {http_exc}")
226
+
227
+ if status_code == 401:
228
+ logger.error("Authentication failed: please check your Git token (missing or expired).")
229
+ elif status_code == 404:
230
+ logger.error("Repository not found: please check the repository URL.")
231
+ elif status_code == 403:
232
+ logger.error("Access denied: your token may lack sufficient permissions or you hit a rate limit.")
233
+ else:
234
+ logger.error("Unexpected HTTP error occurred while accessing the repository metadata.")
235
+ raise
236
+
222
237
  except Exception as exc:
223
- logger.error(f"Error while fetching repository metadata: {exc}")
224
- return None
238
+ logger.error(f"Unexpected error while fetching repository metadata: {exc}")
239
+ raise
225
240
 
226
241
 
227
242
  def _load_github_metadata(base_url: str) -> RepositoryMetadata:
@@ -236,7 +251,7 @@ def _load_github_metadata(base_url: str) -> RepositoryMetadata:
236
251
  response.raise_for_status()
237
252
 
238
253
  metadata = response.json()
239
- logger.info(f"Successfully fetched GitHub metadata for repository: {base_url}")
254
+ logger.info(f"Successfully fetched GitHub metadata for repository: '{base_url}'")
240
255
  return _parse_repository_metadata_github(metadata)
241
256
 
242
257
 
@@ -256,7 +271,7 @@ def _load_gitlab_metadata(repo_url: str, base_url: str) -> RepositoryMetadata:
256
271
  response.raise_for_status()
257
272
 
258
273
  metadata = response.json()
259
- logger.info(f"Successfully fetched GitLab metadata for repository: {base_url}")
274
+ logger.info(f"Successfully fetched GitLab metadata for repository: '{base_url}'")
260
275
  return _parse_repository_metadata_gitlab(metadata)
261
276
 
262
277
 
@@ -274,5 +289,5 @@ def _load_gitverse_metadata(base_url: str) -> RepositoryMetadata:
274
289
  response.raise_for_status()
275
290
 
276
291
  metadata = response.json()
277
- logger.info(f"Successfully fetched Gitverse metadata for repository: {base_url}")
292
+ logger.info(f"Successfully fetched Gitverse metadata for repository: '{base_url}'")
278
293
  return _parse_repository_metadata_gitverse(metadata)
@@ -124,7 +124,7 @@ def parse_arguments() -> argparse.Namespace:
124
124
  Returns:
125
125
  argparse.Namespace: Parsed arguments including:
126
126
  - table_path: path to input table file (.csv or .xlsx)
127
- - api: choice of LLM API provider ("llama", "openai", "ollama")
127
+ - api: choice of LLM API provider ("itmo", "openai", "ollama")
128
128
  - base_url: base URL of the API provider
129
129
  - model: LLM model name to use
130
130
  """
@@ -134,7 +134,7 @@ def parse_arguments() -> argparse.Namespace:
134
134
  "--table-path", type=str, help="Path to an Excel (.xlsx) or CSV file containing tabular data.", required=True
135
135
  )
136
136
  parser.add_argument(
137
- "--api", type=str, choices=["llama", "openai", "ollama"], help="LLM API service provider.", default="llama"
137
+ "--api", type=str, choices=["itmo", "openai", "ollama"], help="LLM API service provider.", default="itmo"
138
138
  )
139
139
  parser.add_argument(
140
140
  "--base-url",
@@ -38,7 +38,7 @@ class SourceRank:
38
38
  return bool(pattern.search(self.tree))
39
39
 
40
40
  def contributing_presence(self) -> bool:
41
- pattern = re.compile(r"\b\w*contribut\w*\.(md|rst|txt)$", re.IGNORECASE)
41
+ pattern = re.compile(r"\b\w*contribut\w*\.(md|rst|txt)$", re.IGNORECASE | re.MULTILINE)
42
42
  return bool(pattern.search(self.tree))
43
43
 
44
44
  def requirements_presence(self) -> bool:
@@ -24,6 +24,13 @@ web_mode:
24
24
  without launching the interactive CLI editor.
25
25
  default: false
26
26
 
27
+ output:
28
+ aliases: ["-o","--output"]
29
+ type: str
30
+ description: "Path to the output directory"
31
+ example: "path/to/directory"
32
+ required: false
33
+
27
34
  branch:
28
35
  aliases: [ "-b", "--branch" ]
29
36
  type: str
@@ -35,8 +42,8 @@ api:
35
42
  aliases: [ "--api" ]
36
43
  type: str
37
44
  description: "LLM API service provider"
38
- choices: [ "llama", "openai", "ollama" ]
39
- default: "llama"
45
+ choices: [ "itmo", "openai", "ollama" ]
46
+ default: "itmo"
40
47
 
41
48
  base_url:
42
49
  aliases: [ "--base-url" ]
@@ -159,7 +166,7 @@ refine_readme:
159
166
  requirements:
160
167
  aliases: ["--requirements"]
161
168
  type: flag
162
- description: "Generate a requirements.txt file based on repository content."
169
+ description: "Generate or refine a requirements.txt file based on repository content."
163
170
  default: false
164
171
 
165
172
  organize:
@@ -22,12 +22,12 @@ repository = "https://github.com/aimclub/FEDOT"
22
22
  # Language Model API Settings
23
23
  [llm]
24
24
  api = "openai"
25
+ rate_limit = 10
25
26
  url = "https://api.openai.com/v1"
26
27
  context_window = 4096
27
28
  encoder = "cl100k_base"
28
29
  host_name = "https://api.openai.com/v1"
29
30
  localhost = "http://localhost:11434/"
30
- itmo_local_url = "http://10.32.15.21:6672"
31
31
  model = "gpt-3.5-turbo"
32
32
  path = "generate"
33
33
  temperature = 0.05
@@ -46,12 +46,12 @@ class ModelSettings(BaseModel):
46
46
  """
47
47
 
48
48
  api: str
49
+ rate_limit: PositiveInt
49
50
  url: str
50
51
  context_window: PositiveInt
51
52
  encoder: str
52
53
  host_name: AnyHttpUrl
53
54
  localhost: AnyHttpUrl
54
- itmo_local_url: AnyHttpUrl
55
55
  model: str
56
56
  path: str
57
57
  temperature: NonNegativeFloat
@@ -246,7 +246,7 @@ class GitAgent:
246
246
  response_check = requests.get(url, headers=headers)
247
247
 
248
248
  if response_check.status_code == 204:
249
- logger.info(f"GitHub repository {base_repo} is already starred.")
249
+ logger.info(f"GitHub repository '{base_repo}' is already starred.")
250
250
  return
251
251
  elif response_check.status_code != 404:
252
252
  logger.error(f"Failed to check star status: {response_check.status_code} - {response_check.text}")
@@ -255,7 +255,7 @@ class GitAgent:
255
255
  response_star = requests.put(url, headers=headers)
256
256
 
257
257
  if response_star.status_code == 204:
258
- logger.info(f"GitHub repository {base_repo} has been starred successfully.")
258
+ logger.info(f"GitHub repository '{base_repo}' has been starred successfully.")
259
259
  else:
260
260
  logger.error(f"Failed to star repository: {response_star.status_code} - {response_star.text}")
261
261
  raise ValueError("Failed to star repository.")
@@ -279,10 +279,10 @@ class GitAgent:
279
279
  response = requests.post(url, headers=headers)
280
280
 
281
281
  if response.status_code == 304:
282
- logger.info(f"GitLab repository {base_repo} is already starred.")
282
+ logger.info(f"GitLab repository '{base_repo}' is already starred.")
283
283
  return
284
284
  elif response.status_code == 201:
285
- logger.info(f"GitLab repository {base_repo} has been starred successfully.")
285
+ logger.info(f"GitLab repository '{base_repo}' has been starred successfully.")
286
286
  return
287
287
  else:
288
288
  logger.error(f"Failed to star GitLab repository: {response.status_code} - {response.text}")
@@ -303,7 +303,7 @@ class GitAgent:
303
303
  response_check = requests.get(url, headers=headers)
304
304
 
305
305
  if response_check.status_code == 204:
306
- logger.info(f"Gitverse repository {base_repo} is already starred.")
306
+ logger.info(f"Gitverse repository '{base_repo}' is already starred.")
307
307
  return
308
308
  elif response_check.status_code != 404:
309
309
  logger.error(f"Failed to check star status: {response_check.status_code} - {response_check.text}")
@@ -312,7 +312,7 @@ class GitAgent:
312
312
  response_star = requests.put(url, headers=headers)
313
313
 
314
314
  if response_star.status_code == 204:
315
- logger.info(f"Gitverse repository {base_repo} has been starred successfully.")
315
+ logger.info(f"Gitverse repository '{base_repo}' has been starred successfully.")
316
316
  return
317
317
  else:
318
318
  logger.error(f"Failed to star Gitverse repository: {response_star.status_code} - {response_star.text}")
@@ -342,7 +342,7 @@ class GitAgent:
342
342
  else:
343
343
  try:
344
344
  logger.info(
345
- f"Cloning the {self.base_branch} branch from {self.repo_url} into directory {self.clone_dir}..."
345
+ f"Cloning the '{self.base_branch}' branch from {self.repo_url} into directory {self.clone_dir}..."
346
346
  )
347
347
  self.repo = Repo.clone_from(
348
348
  url=self._get_auth_url(),
@@ -352,7 +352,15 @@ class GitAgent:
352
352
  )
353
353
  logger.info("Cloning completed")
354
354
  except GitCommandError as e:
355
- logger.error(f"Cloning failed: {repr(e)}")
355
+ stderr = e.stderr or ""
356
+ logger.error(f"Cloning failed: {e}")
357
+
358
+ if "remote branch" in stderr and "not found" in stderr:
359
+ logger.error(
360
+ f"Branch '{self.base_branch}' not found in the remote repository. Please check the branch name."
361
+ )
362
+ else:
363
+ logger.error("An unexpected Git error occurred while cloning the repository.")
356
364
  raise
357
365
 
358
366
  def create_and_checkout_branch(self, branch: str = None) -> None:
@@ -395,8 +403,18 @@ class GitAgent:
395
403
 
396
404
  logger.info("Committing changes...")
397
405
  self.repo.git.add(".")
398
- self.repo.git.commit("-m", commit_message)
399
- logger.info("Commit completed.")
406
+
407
+ try:
408
+ self.repo.git.commit("-m", commit_message)
409
+ logger.info("Commit completed.")
410
+ except GitCommandError as e:
411
+ if "nothing to commit" in str(e):
412
+ logger.warning("Nothing to commit: working tree clean")
413
+ if self.pr_report_body:
414
+ logger.info(self.pr_report_body)
415
+ return False
416
+ else:
417
+ raise
400
418
 
401
419
  logger.info(f"Pushing changes to branch {branch} in fork...")
402
420
  self.repo.git.remote("set-url", "origin", self._get_auth_url(self.fork_url))
@@ -620,16 +638,18 @@ class GitAgent:
620
638
  raise ValueError("Fork URL is not set. Please create a fork first.")
621
639
 
622
640
  base_repo = get_base_repo_url(self.repo_url)
623
- logger.info(f"Updating 'About' section for base repository - {self.repo_url}")
641
+ logger.info(f"Updating 'base' repository '{base_repo}'...")
624
642
 
625
643
  if self.platform == "github":
626
644
  self._update_github_about_section(base_repo, about_content)
627
645
  fork_repo = get_base_repo_url(self.fork_url)
646
+ logger.info(f"Updating 'fork' repository '{fork_repo}'...")
628
647
  self._update_github_about_section(fork_repo, about_content)
629
648
  elif self.platform == "gitlab":
630
649
  self._update_gitlab_about_section(base_repo, about_content)
631
650
  elif self.platform == "gitverse":
632
- self._update_gitverse_about_section(base_repo, about_content)
651
+ logger.warning("The ability to update repository data via API is not yet available on Gitverse.")
652
+ # self._update_gitverse_about_section(base_repo, about_content)
633
653
 
634
654
  def _update_github_about_section(self, repo_path: str, about_content: dict):
635
655
  """Update GitHub repository about section.
@@ -652,18 +672,18 @@ class GitAgent:
652
672
  response = requests.patch(url, headers=headers, json=about_data)
653
673
 
654
674
  if response.status_code in {200, 201}:
655
- logger.info(f"Successfully updated GitHub repository description and homepage.")
675
+ logger.info(f"Successfully updated description/homepage.")
656
676
  else:
657
- logger.error(f"{response.status_code} - Failed to update description and homepage for {repo_path}.")
677
+ logger.error(f"{response.status_code} - Failed to update description/homepage.")
658
678
 
659
679
  url = f"https://api.github.com/repos/{repo_path}/topics"
660
680
  topics_data = {"names": about_content["topics"]}
661
681
  response = requests.put(url, headers=headers, json=topics_data)
662
682
 
663
683
  if response.status_code in {200, 201}:
664
- logger.info(f"Successfully updated GitHub repository topics.")
684
+ logger.info(f"Successfully updated topics.")
665
685
  else:
666
- logger.error(f"{response.status_code} - Failed to update topics for {repo_path}.")
686
+ logger.error(f"{response.status_code} - Failed to update topics.")
667
687
 
668
688
  def _update_gitlab_about_section(self, repo_path: str, about_content: dict):
669
689
  """Update GitLab repository about section.
@@ -709,7 +729,6 @@ class GitAgent:
709
729
 
710
730
  about_data = {
711
731
  "description": about_content["description"],
712
- "homepage": about_content["homepage"],
713
732
  "topics": about_content["topics"],
714
733
  }
715
734
 
@@ -1,3 +1,5 @@
1
+ import os
2
+ import asyncio
1
3
  from abc import ABC, abstractmethod
2
4
  from uuid import uuid4
3
5
 
@@ -27,6 +29,12 @@ class ModelHandler(ABC):
27
29
  @abstractmethod
28
30
  def send_request(self, prompt: str) -> str: ...
29
31
 
32
+ @abstractmethod
33
+ async def async_request(self, prompt: str) -> str: ...
34
+
35
+ @abstractmethod
36
+ async def generate_concurrently(self, prompts: list[str]) -> list: ...
37
+
30
38
  def initialize_payload(self, config: Settings, prompt: str) -> None:
31
39
  """
32
40
  Initializes the payload for the instance.
@@ -54,17 +62,6 @@ class PayloadFactory:
54
62
  __init__:
55
63
  Initializes the instance with a unique job ID, temperature, tokens limit, prompt, and roles. The 'config' parameter should include 'llm' with 'temperature' and 'tokens' attributes. The 'prompt' parameter is the initial user prompt.
56
64
 
57
- to_payload:
58
- Converts the instance variables to a dictionary payload. This method takes the instance variables job_id, temperature, tokens_limit, and prompt and packages them into a dictionary. The returned dictionary has the following structure:
59
- {
60
- "job_id": job_id,
61
- "meta": {
62
- "temperature": temperature,
63
- "tokens_limit": tokens_limit,
64
- },
65
- "content": prompt,
66
- }
67
-
68
65
  to_payload_completions:
69
66
  Converts the instance variables to a dictionary payload for completions. This method returns a dictionary with keys 'job_id', 'meta', and 'messages'. The 'meta' key contains a nested dictionary with keys 'temperature' and 'tokens_limit'. The values for these keys are taken from the instance variables of the same names.
70
67
  """
@@ -90,36 +87,6 @@ class PayloadFactory:
90
87
  {"role": "user", "content": prompt},
91
88
  ]
92
89
 
93
- def to_payload(self) -> dict:
94
- """
95
- Converts the instance variables to a dictionary payload.
96
-
97
- This method takes the instance variables job_id, temperature, tokens_limit, and prompt and
98
- packages them into a dictionary. This can be useful for serialization or for sending the
99
- instance data over a network.
100
-
101
- No parameters are required as it uses instance variables.
102
-
103
- Returns:
104
- dict: A dictionary containing the instance variables. The dictionary has the following structure:
105
- {
106
- "job_id": job_id,
107
- "meta": {
108
- "temperature": temperature,
109
- "tokens_limit": tokens_limit,
110
- },
111
- "content": prompt,
112
- }
113
- """
114
- return {
115
- "job_id": self.job_id,
116
- "meta": {
117
- "temperature": self.temperature,
118
- "tokens_limit": self.tokens_limit,
119
- },
120
- "content": self.prompt,
121
- }
122
-
123
90
  def to_payload_completions(self) -> dict:
124
91
  """
125
92
  Converts the instance variables to a dictionary payload for completions.
@@ -197,10 +164,39 @@ class ProtollmHandler(ModelHandler):
197
164
  response = self.client.invoke(messages)
198
165
  return response.content
199
166
 
167
+ async def async_request(self, prompt: str) -> str:
168
+ """
169
+ Asynchronous alternative of send_request method.
170
+ This method do the same things in general.
171
+
172
+ Args:
173
+ prompt: The prompt to initialize the payload with.
174
+
175
+ Returns:
176
+ str: The response received from the request.
177
+ """
178
+ self.initialize_payload(self.config, prompt)
179
+ response = await self.client.ainvoke(self.payload["messages"])
180
+ return response.content
181
+
182
+ async def generate_concurrently(self, prompts: list[str]) -> list[str]:
183
+ """
184
+ Sends a batch of requests to the specified llm server endpoint.
185
+ Requests would be sent in concurrent format and processed in the order of their input.
186
+
187
+ Args:
188
+ prompts: The batch of prompts to send on llm server endpoint.
189
+
190
+ Returns:
191
+ list[str]: The list of responses from awaited coroutines.
192
+ """
193
+ coroutines = [self.async_request(p) for p in prompts]
194
+ return await asyncio.gather(*coroutines)
195
+
200
196
  def _build_model_url(self) -> str:
201
197
  """Builds the model URL based on the LLM API type."""
202
198
  url_templates = {
203
- "llama": f"self_hosted;{self.config.llm.itmo_local_url};{self.config.llm.model}",
199
+ "itmo": f"self_hosted;{os.getenv('ITMO_MODEL_URL', self.config.llm.url)};{self.config.llm.model}",
204
200
  "ollama": f"ollama;{self.config.llm.localhost};{self.config.llm.model}",
205
201
  }
206
202
  return url_templates.get(self.config.llm.api, f"{self.config.llm.url};{self.config.llm.model}")
@@ -229,12 +225,8 @@ class ProtollmHandler(ModelHandler):
229
225
  None
230
226
  """
231
227
  dotenv.load_dotenv()
232
- connector_creator = create_llm_connector
233
-
234
- model_url = self._build_model_url()
235
- llm_params = self._get_llm_params()
236
228
 
237
- self.client = connector_creator(model_url, **llm_params)
229
+ self.client = create_llm_connector(model_url=self._build_model_url(), **self._get_llm_params())
238
230
 
239
231
 
240
232
  class ModelHandlerFactory:
@@ -249,7 +241,7 @@ class ModelHandlerFactory:
249
241
  """
250
242
 
251
243
  @classmethod
252
- def build(cls, config: Settings) -> ModelHandler:
244
+ def build(cls, config: Settings) -> ProtollmHandler:
253
245
  """
254
246
  Builds and returns a handler based on the configuration of the class.
255
247