mirror of
https://github.com/qodo-ai/pr-agent.git
synced 2025-07-21 04:50:39 +08:00
Compare commits
27 Commits
v0.30
...
9c87056263
Author | SHA1 | Date | |
---|---|---|---|
9c87056263 | |||
3251f19a19 | |||
299a2c89d1 | |||
f166e7f497 | |||
8dc08e4596 | |||
5062543325 | |||
35e865bfb6 | |||
abb576c84f | |||
e75b863f3b | |||
ab80677e3a | |||
bd7017d630 | |||
6e2bc01294 | |||
e14834c84e | |||
915a1c563b | |||
bc99cf83dd | |||
d00cbd4da7 | |||
721ff18a63 | |||
1a003fe4d3 | |||
68f78e1a30 | |||
7759d1d3fc | |||
738f9856a4 | |||
fbce8cd2f5 | |||
ea63c8e63a | |||
d8fea6afc4 | |||
ff16e1cd26 | |||
9b5ae1a322 | |||
8b8464163d |
@ -65,6 +65,11 @@ Zero-setup hosted solution with advanced features and priority support
|
|||||||
|
|
||||||
## News and Updates
|
## News and Updates
|
||||||
|
|
||||||
|
## Jun 21, 2025
|
||||||
|
|
||||||
|
v0.30 was [released](https://github.com/qodo-ai/pr-agent/releases)
|
||||||
|
|
||||||
|
|
||||||
## Jun 3, 2025
|
## Jun 3, 2025
|
||||||
|
|
||||||
Qodo Merge now offers a simplified free tier 💎.
|
Qodo Merge now offers a simplified free tier 💎.
|
||||||
|
@ -202,7 +202,23 @@ h1 {
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.addEventListener('load', function() {
|
window.addEventListener('load', function() {
|
||||||
function displayResults(responseText) {
|
function extractText(responseText) {
|
||||||
|
try {
|
||||||
|
console.log('responseText: ', responseText);
|
||||||
|
const results = JSON.parse(responseText);
|
||||||
|
const msg = results.message;
|
||||||
|
|
||||||
|
if (!msg || msg.trim() === '') {
|
||||||
|
return "No results found";
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error parsing results:', error);
|
||||||
|
throw new Error("Failed parsing response message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayResults(msg) {
|
||||||
const resultsContainer = document.getElementById('results');
|
const resultsContainer = document.getElementById('results');
|
||||||
const spinner = document.getElementById('spinner');
|
const spinner = document.getElementById('spinner');
|
||||||
const searchContainer = document.querySelector('.search-container');
|
const searchContainer = document.querySelector('.search-container');
|
||||||
@ -214,8 +230,6 @@ window.addEventListener('load', function() {
|
|||||||
searchContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
searchContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const results = JSON.parse(responseText);
|
|
||||||
|
|
||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
breaks: true,
|
breaks: true,
|
||||||
gfm: true,
|
gfm: true,
|
||||||
@ -223,7 +237,7 @@ window.addEventListener('load', function() {
|
|||||||
sanitize: false
|
sanitize: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const htmlContent = marked.parse(results.message);
|
const htmlContent = marked.parse(msg);
|
||||||
|
|
||||||
resultsContainer.className = 'markdown-content';
|
resultsContainer.className = 'markdown-content';
|
||||||
resultsContainer.innerHTML = htmlContent;
|
resultsContainer.innerHTML = htmlContent;
|
||||||
@ -242,7 +256,7 @@ window.addEventListener('load', function() {
|
|||||||
}, 100);
|
}, 100);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error parsing results:', error);
|
console.error('Error parsing results:', error);
|
||||||
resultsContainer.innerHTML = '<div class="error-message">Error processing results</div>';
|
resultsContainer.innerHTML = '<div class="error-message">Cannot process results</div>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,24 +289,25 @@ window.addEventListener('load', function() {
|
|||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data)
|
||||||
};
|
};
|
||||||
|
|
||||||
// const API_ENDPOINT = 'http://0.0.0.0:3000/api/v1/docs_help';
|
//const API_ENDPOINT = 'http://0.0.0.0:3000/api/v1/docs_help';
|
||||||
const API_ENDPOINT = 'https://help.merge.qodo.ai/api/v1/docs_help';
|
const API_ENDPOINT = 'https://help.merge.qodo.ai/api/v1/docs_help';
|
||||||
|
|
||||||
const response = await fetch(API_ENDPOINT, options);
|
const response = await fetch(API_ENDPOINT, options);
|
||||||
|
const responseText = await response.text();
|
||||||
|
const msg = extractText(responseText);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`An error (${response.status}) occurred during search: "${msg}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseText = await response.text();
|
displayResults(msg);
|
||||||
displayResults(responseText);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
spinner.style.display = 'none';
|
spinner.style.display = 'none';
|
||||||
resultsContainer.innerHTML = `
|
const errorDiv = document.createElement('div');
|
||||||
<div class="error-message">
|
errorDiv.className = 'error-message';
|
||||||
An error occurred while searching. Please try again later.
|
errorDiv.textContent = `${error}`;
|
||||||
</div>
|
resultsContainer.value = "";
|
||||||
`;
|
resultsContainer.appendChild(errorDiv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
[Qodo Merge](https://www.codium.ai/pricing/){:target="_blank"} is a hosted version of the open-source [PR-Agent](https://github.com/Codium-ai/pr-agent){:target="_blank"}.
|
[Qodo Merge](https://www.codium.ai/pricing/){:target="_blank"} is a hosted version of the open-source [PR-Agent](https://github.com/Codium-ai/pr-agent){:target="_blank"}.
|
||||||
It is designed for companies and teams that require additional features and capabilities.
|
It is designed for companies and teams that require additional features and capabilities.
|
||||||
|
|
||||||
Free users receive a monthly quota of 75 PR reviews per git organization, while unlimited usage requires a paid subscription. See [details](https://qodo-merge-docs.qodo.ai/installation/qodo_merge/#cloud-users).
|
Free users receive a quota of 75 monthly PR feedbacks per git organization. Unlimited usage requires a paid subscription. See [details](https://qodo-merge-docs.qodo.ai/installation/qodo_merge/#cloud-users).
|
||||||
|
|
||||||
|
|
||||||
Qodo Merge provides the following benefits:
|
Qodo Merge provides the following benefits:
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ This approach provides not just a quantitative score but also a detailed analysi
|
|||||||
|
|
||||||
[//]: # ()
|
[//]: # ()
|
||||||
|
|
||||||
## Results
|
## PR Benchmark Results
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
@ -67,6 +67,12 @@ This approach provides not just a quantitative score but also a detailed analysi
|
|||||||
<td style="text-align:left;"></td>
|
<td style="text-align:left;"></td>
|
||||||
<td style="text-align:center;"><b>39.0</b></td>
|
<td style="text-align:center;"><b>39.0</b></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="text-align:left;">Codex-mini</td>
|
||||||
|
<td style="text-align:left;">2025-06-20</td>
|
||||||
|
<td style="text-align:left;"><a href="https://platform.openai.com/docs/models/codex-mini-latest">unknown</a></td>
|
||||||
|
<td style="text-align:center;"><b>37.2</b></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="text-align:left;">Gemini-2.5-flash</td>
|
<td style="text-align:left;">Gemini-2.5-flash</td>
|
||||||
<td style="text-align:left;">2025-04-17</td>
|
<td style="text-align:left;">2025-04-17</td>
|
||||||
@ -196,7 +202,7 @@ weaknesses:
|
|||||||
- **Very low recall / shallow coverage:** In a large majority of cases it gives 0-1 suggestions and misses other evident, critical bugs highlighted by peer models, leading to inferior rankings.
|
- **Very low recall / shallow coverage:** In a large majority of cases it gives 0-1 suggestions and misses other evident, critical bugs highlighted by peer models, leading to inferior rankings.
|
||||||
- **Occasional incorrect or harmful fixes:** A noticeable subset of answers propose changes that break functionality or misunderstand the code (e.g. bad constant, wrong header logic, speculative rollbacks).
|
- **Occasional incorrect or harmful fixes:** A noticeable subset of answers propose changes that break functionality or misunderstand the code (e.g. bad constant, wrong header logic, speculative rollbacks).
|
||||||
- **Non-actionable placeholders:** Some “improved_code” sections contain comments or “…” rather than real patches, reducing practical value.
|
- **Non-actionable placeholders:** Some “improved_code” sections contain comments or “…” rather than real patches, reducing practical value.
|
||||||
-
|
|
||||||
### GPT-4.1
|
### GPT-4.1
|
||||||
|
|
||||||
Final score: **26.5**
|
Final score: **26.5**
|
||||||
@ -214,6 +220,22 @@ weaknesses:
|
|||||||
- **Occasional technical inaccuracies:** A noticeable subset of suggestions are wrong (mis-ordered assertions, harmful Bash `set` change, false dangling-reference claims) or carry metadata errors (mis-labeling files as “python”).
|
- **Occasional technical inaccuracies:** A noticeable subset of suggestions are wrong (mis-ordered assertions, harmful Bash `set` change, false dangling-reference claims) or carry metadata errors (mis-labeling files as “python”).
|
||||||
- **Repetitive / derivative fixes:** Many outputs duplicate earlier simplistic ideas (e.g., single null-check) without new insight, showing limited reasoning breadth.
|
- **Repetitive / derivative fixes:** Many outputs duplicate earlier simplistic ideas (e.g., single null-check) without new insight, showing limited reasoning breadth.
|
||||||
|
|
||||||
|
### OpenAI codex-mini
|
||||||
|
|
||||||
|
final score: **37.2**
|
||||||
|
|
||||||
|
strengths:
|
||||||
|
|
||||||
|
- **Can spot high-impact defects:** When it “locks on”, codex-mini often identifies the main runtime or security regression (e.g., race-conditions, logic inversions, blocking I/O, resource leaks) and proposes a minimal, direct patch that compiles and respects neighbouring style.
|
||||||
|
- **Produces concise, scoped fixes:** Valid answers usually stay within the allowed 3-suggestion limit, reference only the added lines, and contain clear before/after snippets that reviewers can apply verbatim.
|
||||||
|
- **Occasional broad coverage:** In a minority of cases the model catches multiple independent issues (logic + tests + docs) and outperforms every baseline answer, showing good contextual understanding of heterogeneous diffs.
|
||||||
|
|
||||||
|
weaknesses:
|
||||||
|
|
||||||
|
- **Output instability / format errors:** A very large share of responses are unusable—plain refusals, shell commands, or malformed/empty YAML—indicating brittle adherence to the required schema and tanking overall usefulness.
|
||||||
|
- **Critical-miss rate:** Even when the format is correct the model frequently overlooks the single most serious bug the diff introduces, instead focusing on stylistic nits or speculative refactors.
|
||||||
|
- **Introduces new problems:** Several suggestions add unsupported APIs, undeclared variables, wrong types, or break compilation, hurting trust in the recommendations.
|
||||||
|
- **Rule violations:** It often edits lines outside the diff, exceeds the 3-suggestion cap, or labels cosmetic tweaks as “critical”, showing inconsistent guideline compliance.
|
||||||
|
|
||||||
## Appendix - models used for generating the benchmark baseline
|
## Appendix - models used for generating the benchmark baseline
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ This page summarizes recent enhancements to Qodo Merge (last three months).
|
|||||||
It also outlines our development roadmap for the upcoming three months. Please note that the roadmap is subject to change, and features may be adjusted, added, or reprioritized.
|
It also outlines our development roadmap for the upcoming three months. Please note that the roadmap is subject to change, and features may be adjusted, added, or reprioritized.
|
||||||
|
|
||||||
=== "Recent Updates"
|
=== "Recent Updates"
|
||||||
|
- **Best Practices Hierarchy**: Introducing support for structured best practices, such as for folders in monorepos or a unified best practice file for a group of repositories.
|
||||||
- **Simplified Free Tier**: Qodo Merge now offers a simplified free tier with a monthly limit of 75 PR reviews per organization, replacing the previous two-week trial. ([Learn more](https://qodo-merge-docs.qodo.ai/installation/qodo_merge/#cloud-users))
|
- **Simplified Free Tier**: Qodo Merge now offers a simplified free tier with a monthly limit of 75 PR reviews per organization, replacing the previous two-week trial. ([Learn more](https://qodo-merge-docs.qodo.ai/installation/qodo_merge/#cloud-users))
|
||||||
- **CLI Endpoint**: A new Qodo Merge endpoint that accepts a lists of before/after code changes, executes Qodo Merge commands, and return the results. Currently available for enterprise customers. Contact [Qodo](https://www.qodo.ai/contact/) for more information.
|
- **CLI Endpoint**: A new Qodo Merge endpoint that accepts a lists of before/after code changes, executes Qodo Merge commands, and return the results. Currently available for enterprise customers. Contact [Qodo](https://www.qodo.ai/contact/) for more information.
|
||||||
- **Linear tickets support**: Qodo Merge now supports Linear tickets. ([Learn more](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/#linear-integration))
|
- **Linear tickets support**: Qodo Merge now supports Linear tickets. ([Learn more](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/#linear-integration))
|
||||||
@ -17,7 +18,6 @@ It also outlines our development roadmap for the upcoming three months. Please n
|
|||||||
|
|
||||||
|
|
||||||
=== "Future Roadmap"
|
=== "Future Roadmap"
|
||||||
- **Best Practices Hierarchy**: Introducing support for structured best practices, such as for folders in monorepos or a unified best practice file for a group of repositories.
|
|
||||||
- **Enhanced `review` tool**: Enhancing the `review` tool validate compliance across multiple categories including security, tickets, and custom best practices.
|
- **Enhanced `review` tool**: Enhancing the `review` tool validate compliance across multiple categories including security, tickets, and custom best practices.
|
||||||
- **Smarter context retrieval**: Leverage AST and LSP analysis to gather relevant context from across the entire repository.
|
- **Smarter context retrieval**: Leverage AST and LSP analysis to gather relevant context from across the entire repository.
|
||||||
- **Enhanced portal experience**: Improved user experience in the Qodo Merge portal with new options and capabilities.
|
- **Enhanced portal experience**: Improved user experience in the Qodo Merge portal with new options and capabilities.
|
||||||
|
@ -51,7 +51,7 @@ class PRAgent:
|
|||||||
def __init__(self, ai_handler: partial[BaseAiHandler,] = LiteLLMAIHandler):
|
def __init__(self, ai_handler: partial[BaseAiHandler,] = LiteLLMAIHandler):
|
||||||
self.ai_handler = ai_handler # will be initialized in run_action
|
self.ai_handler = ai_handler # will be initialized in run_action
|
||||||
|
|
||||||
async def handle_request(self, pr_url, request, notify=None) -> bool:
|
async def _handle_request(self, pr_url, request, notify=None) -> bool:
|
||||||
# First, apply repo specific settings if exists
|
# First, apply repo specific settings if exists
|
||||||
apply_repo_settings(pr_url)
|
apply_repo_settings(pr_url)
|
||||||
|
|
||||||
@ -117,3 +117,10 @@ class PRAgent:
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
async def handle_request(self, pr_url, request, notify=None) -> bool:
|
||||||
|
try:
|
||||||
|
return await self._handle_request(pr_url, request, notify)
|
||||||
|
except:
|
||||||
|
get_logger().exception("Failed to process the command.")
|
||||||
|
return False
|
||||||
|
@ -22,6 +22,7 @@ try:
|
|||||||
from azure.devops.connection import Connection
|
from azure.devops.connection import Connection
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from azure.devops.released.git import (Comment, CommentThread, GitPullRequest, GitVersionDescriptor, GitClient, CommentThreadContext, CommentPosition)
|
from azure.devops.released.git import (Comment, CommentThread, GitPullRequest, GitVersionDescriptor, GitClient, CommentThreadContext, CommentPosition)
|
||||||
|
from azure.devops.released.work_item_tracking import WorkItemTrackingClient
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
from azure.identity import DefaultAzureCredential
|
from azure.identity import DefaultAzureCredential
|
||||||
from msrest.authentication import BasicAuthentication
|
from msrest.authentication import BasicAuthentication
|
||||||
@ -39,7 +40,7 @@ class AzureDevopsProvider(GitProvider):
|
|||||||
"Azure DevOps provider is not available. Please install the required dependencies."
|
"Azure DevOps provider is not available. Please install the required dependencies."
|
||||||
)
|
)
|
||||||
|
|
||||||
self.azure_devops_client = self._get_azure_devops_client()
|
self.azure_devops_client, self.azure_devops_board_client = self._get_azure_devops_client()
|
||||||
self.diff_files = None
|
self.diff_files = None
|
||||||
self.workspace_slug = None
|
self.workspace_slug = None
|
||||||
self.repo_slug = None
|
self.repo_slug = None
|
||||||
@ -566,7 +567,7 @@ class AzureDevopsProvider(GitProvider):
|
|||||||
return workspace_slug, repo_slug, pr_number
|
return workspace_slug, repo_slug, pr_number
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_azure_devops_client() -> GitClient:
|
def _get_azure_devops_client() -> Tuple[GitClient, WorkItemTrackingClient]:
|
||||||
org = get_settings().azure_devops.get("org", None)
|
org = get_settings().azure_devops.get("org", None)
|
||||||
pat = get_settings().azure_devops.get("pat", None)
|
pat = get_settings().azure_devops.get("pat", None)
|
||||||
|
|
||||||
@ -588,13 +589,12 @@ class AzureDevopsProvider(GitProvider):
|
|||||||
get_logger().error(f"No PAT found in settings, and Azure Default Authentication failed, error: {e}")
|
get_logger().error(f"No PAT found in settings, and Azure Default Authentication failed, error: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
credentials = BasicAuthentication("", auth_token)
|
|
||||||
|
|
||||||
credentials = BasicAuthentication("", auth_token)
|
credentials = BasicAuthentication("", auth_token)
|
||||||
azure_devops_connection = Connection(base_url=org, creds=credentials)
|
azure_devops_connection = Connection(base_url=org, creds=credentials)
|
||||||
azure_devops_client = azure_devops_connection.clients.get_git_client()
|
azure_devops_client = azure_devops_connection.clients.get_git_client()
|
||||||
|
azure_devops_board_client = azure_devops_connection.clients.get_work_item_tracking_client()
|
||||||
|
|
||||||
return azure_devops_client
|
return azure_devops_client, azure_devops_board_client
|
||||||
|
|
||||||
def _get_repo(self):
|
def _get_repo(self):
|
||||||
if self.repo is None:
|
if self.repo is None:
|
||||||
@ -635,4 +635,50 @@ class AzureDevopsProvider(GitProvider):
|
|||||||
last = commits[0]
|
last = commits[0]
|
||||||
url = self.azure_devops_client.normalized_url + "/" + self.workspace_slug + "/_git/" + self.repo_slug + "/commit/" + last.commit_id
|
url = self.azure_devops_client.normalized_url + "/" + self.workspace_slug + "/_git/" + self.repo_slug + "/commit/" + last.commit_id
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
def get_linked_work_items(self) -> list:
|
||||||
|
"""
|
||||||
|
Get linked work items from the PR.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
work_items = self.azure_devops_client.get_pull_request_work_item_refs(
|
||||||
|
project=self.workspace_slug,
|
||||||
|
repository_id=self.repo_slug,
|
||||||
|
pull_request_id=self.pr_num,
|
||||||
|
)
|
||||||
|
ids = [work_item.id for work_item in work_items]
|
||||||
|
if not work_items:
|
||||||
|
return []
|
||||||
|
items = self.get_work_items(ids)
|
||||||
|
return items
|
||||||
|
except Exception as e:
|
||||||
|
get_logger().exception(f"Failed to get linked work items, error: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_work_items(self, work_item_ids: list) -> list:
|
||||||
|
"""
|
||||||
|
Get work items by their IDs.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
raw_work_items = self.azure_devops_board_client.get_work_items(
|
||||||
|
project=self.workspace_slug,
|
||||||
|
ids=work_item_ids,
|
||||||
|
)
|
||||||
|
work_items = []
|
||||||
|
for item in raw_work_items:
|
||||||
|
work_items.append(
|
||||||
|
{
|
||||||
|
"id": item.id,
|
||||||
|
"title": item.fields.get("System.Title", ""),
|
||||||
|
"url": item.url,
|
||||||
|
"body": item.fields.get("System.Description", ""),
|
||||||
|
"acceptance_criteria": item.fields.get(
|
||||||
|
"Microsoft.VSTS.Common.AcceptanceCriteria", ""
|
||||||
|
),
|
||||||
|
"tags": item.fields.get("System.Tags", "").split("; ") if item.fields.get("System.Tags") else [],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return work_items
|
||||||
|
except Exception as e:
|
||||||
|
get_logger().exception(f"Failed to get work items, error: {e}")
|
||||||
|
return []
|
||||||
|
38
pr_agent/servers/atlassian-connect-qodo-merge.json
Normal file
38
pr_agent/servers/atlassian-connect-qodo-merge.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "Qodo Merge",
|
||||||
|
"description": "Qodo Merge",
|
||||||
|
"key": "app_key",
|
||||||
|
"vendor": {
|
||||||
|
"name": "Qodo",
|
||||||
|
"url": "https://qodo.ai"
|
||||||
|
},
|
||||||
|
"authentication": {
|
||||||
|
"type": "jwt"
|
||||||
|
},
|
||||||
|
"baseUrl": "base_url",
|
||||||
|
"lifecycle": {
|
||||||
|
"installed": "/installed",
|
||||||
|
"uninstalled": "/uninstalled"
|
||||||
|
},
|
||||||
|
"scopes": [
|
||||||
|
"account",
|
||||||
|
"repository:write",
|
||||||
|
"pullrequest:write",
|
||||||
|
"wiki"
|
||||||
|
],
|
||||||
|
"contexts": [
|
||||||
|
"account"
|
||||||
|
],
|
||||||
|
"modules": {
|
||||||
|
"webhooks": [
|
||||||
|
{
|
||||||
|
"event": "*",
|
||||||
|
"url": "/webhook"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"links": {
|
||||||
|
"privacy": "https://qodo.ai/privacy-policy",
|
||||||
|
"terms": "https://qodo.ai/terms"
|
||||||
|
}
|
||||||
|
}
|
@ -196,6 +196,13 @@ Ticket Description:
|
|||||||
{{ ticket.body }}
|
{{ ticket.body }}
|
||||||
#####
|
#####
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if ticket.requirements %}
|
||||||
|
Ticket Requirements:
|
||||||
|
#####
|
||||||
|
{{ ticket.requirements }}
|
||||||
|
#####
|
||||||
|
{%- endif %}
|
||||||
=====
|
=====
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
@ -3,6 +3,7 @@ import traceback
|
|||||||
|
|
||||||
from pr_agent.config_loader import get_settings
|
from pr_agent.config_loader import get_settings
|
||||||
from pr_agent.git_providers import GithubProvider
|
from pr_agent.git_providers import GithubProvider
|
||||||
|
from pr_agent.git_providers import AzureDevopsProvider
|
||||||
from pr_agent.log import get_logger
|
from pr_agent.log import get_logger
|
||||||
|
|
||||||
# Compile the regex pattern once, outside the function
|
# Compile the regex pattern once, outside the function
|
||||||
@ -131,6 +132,32 @@ async def extract_tickets(git_provider):
|
|||||||
|
|
||||||
return tickets_content
|
return tickets_content
|
||||||
|
|
||||||
|
elif isinstance(git_provider, AzureDevopsProvider):
|
||||||
|
tickets_info = git_provider.get_linked_work_items()
|
||||||
|
tickets_content = []
|
||||||
|
for ticket in tickets_info:
|
||||||
|
try:
|
||||||
|
ticket_body_str = ticket.get("body", "")
|
||||||
|
if len(ticket_body_str) > MAX_TICKET_CHARACTERS:
|
||||||
|
ticket_body_str = ticket_body_str[:MAX_TICKET_CHARACTERS] + "..."
|
||||||
|
|
||||||
|
tickets_content.append(
|
||||||
|
{
|
||||||
|
"ticket_id": ticket.get("id"),
|
||||||
|
"ticket_url": ticket.get("url"),
|
||||||
|
"title": ticket.get("title"),
|
||||||
|
"body": ticket_body_str,
|
||||||
|
"requirements": ticket.get("acceptance_criteria", ""),
|
||||||
|
"labels": ", ".join(ticket.get("labels", [])),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
get_logger().error(
|
||||||
|
f"Error processing Azure DevOps ticket: {e}",
|
||||||
|
artifact={"traceback": traceback.format_exc()},
|
||||||
|
)
|
||||||
|
return tickets_content
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
get_logger().error(f"Error extracting tickets error= {e}",
|
get_logger().error(f"Error extracting tickets error= {e}",
|
||||||
artifact={"traceback": traceback.format_exc()})
|
artifact={"traceback": traceback.format_exc()})
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools>=61.0"]
|
requires = ["setuptools>=61.0", "wheel"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "pr-agent"
|
name = "pr-agent"
|
||||||
version = "0.2.7"
|
version = "0.3.0"
|
||||||
|
|
||||||
authors = [{ name = "QodoAI", email = "tal.r@qodo.ai" }]
|
authors = [{ name = "QodoAI", email = "tal.r@qodo.ai" }]
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ description = "QodoAI PR-Agent aims to help efficiently review and handle pull r
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
keywords = ["AI", "Agents", "Pull Request", "Automation", "Code Review"]
|
keywords = ["AI", "Agents", "Pull Request", "Automation", "Code Review"]
|
||||||
license = "Apache-2.0"
|
license = { file = "LICENSE" }
|
||||||
|
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
@ -34,7 +34,6 @@ dependencies = { file = ["requirements.txt"] }
|
|||||||
|
|
||||||
[tool.setuptools]
|
[tool.setuptools]
|
||||||
include-package-data = true
|
include-package-data = true
|
||||||
license-files = ["LICENSE"]
|
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
where = ["."]
|
where = ["."]
|
||||||
|
Reference in New Issue
Block a user