Skip to content

Commit bd1ad66

Browse files
authored
Refactor/unify prompt loader (#319)
* unify prompt loader * unify prompt loader part 2 * fix bugs and tests * resolve conflicts + osa version increment
1 parent 96798af commit bd1ad66

File tree

77 files changed

+583
-1077
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+583
-1077
lines changed

osa_tool/aboutgen/about_generator.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
import re
33
from typing import List
44

5-
from osa_tool.aboutgen.prompts_about_config import PromptAboutLoader
65
from osa_tool.config.settings import ConfigLoader
76
from osa_tool.git_agent.git_agent import GitAgent
8-
from osa_tool.logger import logger
97
from osa_tool.models.models import ModelHandler, ModelHandlerFactory
10-
from osa_tool.utils import extract_readme_content, 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
1111

1212
HOMEPAGE_KEYS = [
1313
"documentation",
@@ -24,16 +24,15 @@
2424
class AboutGenerator:
2525
"""Generates Git repository About section content."""
2626

27-
def __init__(self, config_loader: ConfigLoader, git_agent: GitAgent):
27+
def __init__(self, config_loader: ConfigLoader, prompts: PromptLoader, git_agent: GitAgent):
2828
self.config = config_loader.config
29+
self.prompts = prompts
2930
self.model_handler: ModelHandler = ModelHandlerFactory.build(self.config)
3031
self.repo_url = self.config.git.repository
3132
self.metadata = git_agent.metadata
3233
self.base_path = os.path.join(os.getcwd(), parse_folder_name(self.repo_url))
3334
self.readme_content = extract_readme_content(self.base_path)
34-
self.prompts = PromptAboutLoader().prompts
3535
self.validate_topics = git_agent.validate_topics
36-
3736
self._content: dict | None = None
3837

3938
def generate_about_content(self) -> None:
@@ -93,10 +92,13 @@ def generate_description(self) -> str:
9392
logger.warning("No README content found. Cannot generate description.")
9493
return ""
9594

96-
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+
)
9799

98100
try:
99-
description = self.model_handler.send_request(formatted_prompt)
101+
description = self.model_handler.send_request(prompt)
100102
logger.debug(f"Generated description: {description}")
101103
return description[:350]
102104
except Exception as e:
@@ -124,14 +126,15 @@ def generate_topics(self, amount: int = 7) -> List[str]:
124126
logger.warning(f"{amount} topics already exist in the metadata. Skipping generation.")
125127
return existing_topics
126128

127-
formatted_prompt = self.prompts.topics.format(
129+
prompt = PromptBuilder.render(
130+
self.prompts.get("about_section.topics"),
131+
readme_content=self.readme_content,
128132
amount=amount,
129133
topics=existing_topics,
130-
readme_content=self.readme_content,
131134
)
132135

133136
try:
134-
response = self.model_handler.send_request(formatted_prompt)
137+
response = self.model_handler.send_request(prompt)
135138
topics = [topic.strip().lower().replace(" ", "-") for topic in response.split(",") if topic.strip()]
136139
logger.debug(f"Generated topics from LLM: {topics}")
137140
validated_topics = self.validate_topics(topics)
@@ -182,8 +185,11 @@ def _extract_readme_urls(readme_content: str) -> List[str]:
182185
def _analyze_urls(self, urls: List[str]) -> List[str]:
183186
"""Generates LLM prompt for URL analysis"""
184187
logger.info(f"Analyzing {len(urls)} project URLs...")
185-
formatted_prompt = self.prompts.analyze_urls.format(project_url=self.repo_url, urls=", ".join(urls))
186-
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)
187193
if not response:
188194
return []
189195

osa_tool/aboutgen/prompts_about_config.py

Lines changed: 0 additions & 39 deletions
This file was deleted.

osa_tool/analytics/metadata.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
from dotenv import load_dotenv
99
from requests import HTTPError
1010

11-
from osa_tool.logger import logger
12-
from osa_tool.utils import get_base_repo_url
11+
from osa_tool.utils.logger import logger
12+
from osa_tool.utils.utils import get_base_repo_url
1313

1414
load_dotenv()
1515

osa_tool/analytics/report_generator.py

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import os
22

3-
import tomli as tomllib
43
from pydantic import ValidationError
54

65
from osa_tool.analytics.metadata import RepositoryMetadata
7-
from osa_tool.analytics.prompt_builder import (
6+
from osa_tool.analytics.response_validation import (
87
RepositoryReport,
98
RepositoryStructure,
109
ReadmeEvaluation,
@@ -13,21 +12,24 @@
1312
)
1413
from osa_tool.analytics.sourcerank import SourceRank
1514
from osa_tool.config.settings import ConfigLoader
16-
from osa_tool.logger import logger
1715
from osa_tool.models.models import ModelHandler, ModelHandlerFactory
1816
from osa_tool.readmegen.postprocessor.response_cleaner import JsonProcessor
19-
from osa_tool.utils import extract_readme_content, osa_project_root, parse_folder_name
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
2020

2121

2222
class TextGenerator:
23-
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+
):
2426
self.config = config_loader.config
2527
self.sourcerank = sourcerank
28+
self.prompts = prompts
29+
self.metadata = metadata
2630
self.model_handler: ModelHandler = ModelHandlerFactory.build(self.config)
2731
self.repo_url = self.config.git.repository
28-
self.metadata = metadata
2932
self.base_path = os.path.join(os.getcwd(), parse_folder_name(self.repo_url))
30-
self.prompt_path = os.path.join(osa_project_root(), "config", "settings", "prompt_for_analysis.toml")
3133

3234
def make_request(self) -> RepositoryReport:
3335
"""
@@ -36,7 +38,16 @@ def make_request(self) -> RepositoryReport:
3638
Returns:
3739
str: The generated repository analysis response from the model.
3840
"""
39-
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+
)
4051
parsed_data = JsonProcessor.parse(response, expected_type=dict)
4152
try:
4253
parsed_report = RepositoryReport.model_validate(parsed_data)
@@ -56,30 +67,6 @@ def make_request(self) -> RepositoryReport:
5667
logger.error(f"Unexpected error while parsing RepositoryReport: {e}")
5768
raise ValueError(f"Failed to process model response: {e}")
5869

59-
def _build_prompt(self) -> str:
60-
"""
61-
Builds the prompt to be sent to the model for repository analysis.
62-
63-
This method loads the prompt structure from a file and formats it with values
64-
extracted from the repository's metadata and other relevant information like
65-
the project name, presence of key files, and repository tree.
66-
67-
Returns:
68-
str: The formatted prompt to be used in the model request.
69-
"""
70-
with open(self.prompt_path, "rb") as f:
71-
prompts = tomllib.load(f)
72-
73-
main_prompt = prompts.get("prompt", {}).get("main_prompt", "")
74-
prompt = main_prompt.format(
75-
project_name=self.metadata.name,
76-
metadata=self.metadata,
77-
repository_tree=self.sourcerank.tree,
78-
presence_files=self._extract_presence_files(),
79-
readme_content=extract_readme_content(self.base_path),
80-
)
81-
return prompt
82-
8370
def _extract_presence_files(self) -> list[str]:
8471
"""
8572
Extracts information about the presence of key files in the repository.

osa_tool/analytics/report_maker.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,20 @@
2121
from osa_tool.analytics.report_generator import TextGenerator
2222
from osa_tool.analytics.sourcerank import SourceRank
2323
from osa_tool.config.settings import ConfigLoader
24-
from osa_tool.logger import logger
25-
from osa_tool.utils import 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
2627

2728

2829
class ReportGenerator:
2930

30-
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+
):
3134
self.config = config_loader.config
3235
self.sourcerank = sourcerank
3336
self.metadata = metadata
34-
self.text_generator = TextGenerator(config_loader, self.sourcerank, self.metadata)
37+
self.text_generator = TextGenerator(config_loader, self.sourcerank, prompts, self.metadata)
3538
self.repo_url = self.config.git.repository
3639
self.osa_url = "https://github.com/aimclub/OSA"
3740

osa_tool/analytics/sourcerank.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import re
33

44
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
66

77

88
class SourceRank:
File renamed without changes.

osa_tool/config/settings/prompt_for_analysis.toml renamed to osa_tool/config/prompts/analysis.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[prompt]
1+
[prompts]
22
main_prompt = """
33
INPUT DATA:
44

0 commit comments

Comments
 (0)