Skip to content

CHORE: Refactoring AI triaging#536

Open
sumitmsft wants to merge 3 commits intomainfrom
sumitmsft/ai-triage-response-changes
Open

CHORE: Refactoring AI triaging#536
sumitmsft wants to merge 3 commits intomainfrom
sumitmsft/ai-triage-response-changes

Conversation

@sumitmsft
Copy link
Copy Markdown
Contributor

@sumitmsft sumitmsft commented Apr 23, 2026

Work Item / Issue Reference

AB#https://sqlclientdrivers.visualstudio.com/mssql-python/_workitems/edit/44588
GitHub Issue: #<ISSUE_NUMBER>

Summary

This pull request significantly enhances the GitHub issue triage and notification workflows by introducing automated detection and reporting of similar issues, suggesting practical workarounds, and generating draft customer responses. The changes streamline information flow between the triage and notification steps, improving both the quality and clarity of information provided to maintainers and engineers.

Key improvements include:

Automated Issue Analysis and Enrichment

  • The triage workflow now automatically searches for similar or duplicate issues (both open and recently closed) and summarizes findings, helping engineers quickly identify related problems and avoid duplicate work.
  • For bug and break-fix issues, the workflow generates practical workaround suggestions, including code snippets and downgrade guidance where applicable, to assist users while a permanent fix is developed.
  • A draft, empathetic customer response is generated for every issue, incorporating context from code analysis, workarounds, and similar issues, to help engineers communicate effectively and consistently with users.

Workflow Output and Notification Enhancements

  • The outputs for suggested response, similar issues analysis, and workaround suggestions are now passed through all relevant workflow steps and jobs, ensuring this information is available for notifications and further automation. (.github/workflows/issue-triage.yml: [1] [2] [3]; .github/workflows/issue-notify.yml: [4] [5] [6]
  • The Teams notification (and local test harness) is updated to include new sections for similar issues, workarounds, and the suggested customer response, with improved formatting for clarity and ease of use. (.github/workflows/issue-notify.yml: [1] [2]; test-triage-local.js: [3] [4]

Refactoring and Prompt Improvements

  • Prompts for the triage AI have been updated for clarity and to ensure the new outputs (similar issues, workarounds, suggested response) are generated in structured JSON, making downstream processing more reliable.

These enhancements provide maintainers and engineers with richer, more actionable information for each issue, improving triage efficiency and the quality of user communication.


References:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11]

…und suggestions to AI triage

- Added dedicated LLM call to generate copy-paste-ready customer responses for all issue categories
- Added similar/duplicate issue detection: searches recent open + closed issues via GitHub API, uses LLM to identify duplicates and recently fixed related issues
- Added workaround generation for BUG/BREAK_FIX: suggests practical workarounds with code snippets and downgrade guidance
- Added 'Similar Issues & Recent Fixes', 'Workarounds', and 'Suggested Response to Customer' sections in Teams notification card
- Updated test-triage-local.js to match new workflow logic
- Similar issues and workaround context fed into customer response for richer replies
Copilot AI review requested due to automatic review settings April 23, 2026 11:46
@sumitmsft sumitmsft changed the title Add suggested customer response, similar issue detection, and workaround suggestions to AI triage CHORE: Add suggested customer response, similar issue detection, and workaround suggestions to AI triage Apr 23, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enhances the repository’s GitHub issue triage automation (and its local test harness) by adding three new AI-assisted outputs—similar-issue detection, workaround suggestions, and a suggested customer response—so engineers can respond faster and with more context in the Teams notification.

Changes:

  • Add a “Similar Issues & Recent Fixes” analysis step and surface it through workflow outputs.
  • Add a “Workarounds” generation step for BUG/BREAK_FIX issues and surface it through workflow outputs.
  • Add a dedicated “Suggested Response to Customer” generation step and include it in Teams notifications + local triage test script.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
test-triage-local.js Extends local triage flow to fetch/format similar issues, generate workarounds, and generate a suggested response in the Teams payload.
.github/workflows/issue-triage.yml Adds additional GitHub Models calls for similar issues, workarounds, and suggested response; publishes them as job outputs and passes them to notification workflow.
.github/workflows/issue-notify.yml Adds new reusable-workflow inputs and renders new sections in the Teams HTML card.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/issue-notify.yml Outdated
Comment on lines +181 to +187
[
(if .summary then "<b>Summary:</b> " + (.summary | @html) else empty end),
(if .duplicate_issues and (.duplicate_issues | length) > 0
then "<b>Similar/Duplicate Issues:</b><br>" + ([.duplicate_issues[] | "&nbsp;&nbsp;• <a href=\"https://github.com/microsoft/mssql-python/issues/" + (.issue_number | tostring) + "\">#" + (.issue_number | tostring) + "</a> " + (.title | @html) + " [" + .state + "] — <i>" + (.similarity | @html) + ":</i> " + (.explanation | @html)] | join("<br>"))
else empty end),
(if .recently_fixed and (.recently_fixed | length) > 0
then "<b>Recently Fixed:</b><br>" + ([.recently_fixed[] | "&nbsp;&nbsp;• <a href=\"https://github.com/microsoft/mssql-python/issues/" + (.issue_number | tostring) + "\">#" + (.issue_number | tostring) + "</a> " + (.title | @html) + " (closed " + (.closed_at | @html) + ") — " + (.relevance | @html)] | join("<br>"))
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Teams HTML for similar issues interpolates LLM-provided fields into HTML/attributes without consistently escaping them (e.g., .state is inserted raw into the string, and .issue_number is used inside an href). Since this JSON comes from the model output, treat it as untrusted: HTML-escape all displayed fields (including state) and URI/number-validate issue_number before using it in a link.

Suggested change
[
(if .summary then "<b>Summary:</b> " + (.summary | @html) else empty end),
(if .duplicate_issues and (.duplicate_issues | length) > 0
then "<b>Similar/Duplicate Issues:</b><br>" + ([.duplicate_issues[] | "&nbsp;&nbsp;• <a href=\"https://github.com/microsoft/mssql-python/issues/" + (.issue_number | tostring) + "\">#" + (.issue_number | tostring) + "</a> " + (.title | @html) + " [" + .state + "] — <i>" + (.similarity | @html) + ":</i> " + (.explanation | @html)] | join("<br>"))
else empty end),
(if .recently_fixed and (.recently_fixed | length) > 0
then "<b>Recently Fixed:</b><br>" + ([.recently_fixed[] | "&nbsp;&nbsp;• <a href=\"https://github.com/microsoft/mssql-python/issues/" + (.issue_number | tostring) + "\">#" + (.issue_number | tostring) + "</a> " + (.title | @html) + " (closed " + (.closed_at | @html) + ") — " + (.relevance | @html)] | join("<br>"))
def valid_issue_number:
(.issue_number | tostring) as $n
| if ($n | test("^[0-9]+$")) then $n else null end;
def issue_link:
valid_issue_number as $n
| if $n != null
then "<a href=\"https://github.com/microsoft/mssql-python/issues/" + $n + "\">#" + ($n | @html) + "</a>"
else "#" + ((.issue_number | tostring) | @html)
end;
[
(if .summary then "<b>Summary:</b> " + (.summary | @html) else empty end),
(if .duplicate_issues and (.duplicate_issues | length) > 0
then "<b>Similar/Duplicate Issues:</b><br>" + ([.duplicate_issues[] | "&nbsp;&nbsp;• " + issue_link + " " + (.title | @html) + " [" + (.state | @html) + "] — <i>" + (.similarity | @html) + ":</i> " + (.explanation | @html)] | join("<br>"))
else empty end),
(if .recently_fixed and (.recently_fixed | length) > 0
then "<b>Recently Fixed:</b><br>" + ([.recently_fixed[] | "&nbsp;&nbsp;• " + issue_link + " " + (.title | @html) + " (closed " + (.closed_at | @html) + ") — " + (.relevance | @html)] | join("<br>"))

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread .github/workflows/issue-notify.yml
Comment thread test-triage-local.js
Comment on lines +478 to +486
const openRes = await fetch(`https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/issues?state=open&per_page=30&sort=created&direction=desc`, { headers });
const openIssues = await openRes.json();

const closedRes = await fetch(`https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/issues?state=closed&per_page=30&sort=updated&direction=desc`, { headers });
const closedIssues = await closedRes.json();

const allIssues = [...openIssues, ...closedIssues]
.filter(i => i.number !== issueNumber && !i.pull_request)
.map(i => ({
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The similar-issues fetch path assumes the GitHub API responses are arrays. If openRes/closedRes is non-2xx (rate limit / auth / abuse detection), await res.json() will be an error object, and [...openIssues, ...closedIssues] will throw. Add response.ok checks (and/or validate Array.isArray(...)) before spreading so the script fails gracefully with a clear message.

Copilot uses AI. Check for mistakes.
Comment thread test-triage-local.js
Comment on lines +192 to +202
const parts = [];
if (parsed.summary) parts.push(`<b>Summary:</b> ${escVal(parsed.summary)}`);
if (parsed.duplicate_issues && parsed.duplicate_issues.length > 0) {
parts.push(`<b>Similar/Duplicate Issues:</b><br>${parsed.duplicate_issues.map(d =>
`&nbsp;&nbsp;• <a href="https://github.com/microsoft/mssql-python/issues/${d.issue_number}">#${d.issue_number}</a> ${escVal(d.title)} [${d.state}] — <i>${escVal(d.similarity)}:</i> ${escVal(d.explanation)}`
).join("<br>")}`);
}
if (parsed.recently_fixed && parsed.recently_fixed.length > 0) {
parts.push(`<b>Recently Fixed:</b><br>${parsed.recently_fixed.map(f =>
`&nbsp;&nbsp;• <a href="https://github.com/microsoft/mssql-python/issues/${f.issue_number}">#${f.issue_number}</a> ${escVal(f.title)} (closed ${escVal(f.closed_at)}) — ${escVal(f.relevance)}`
).join("<br>")}`);
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue_number from the LLM output is interpolated directly into an HTML attribute (href=.../${d.issue_number}) without validation/escaping. Because the JSON originates from model output (which can be influenced by issue content), this allows HTML injection in the Teams payload. Coerce issue_number to a number (or strictly whitelist ^[0-9]+$) and escape/encode it before building the URL.

Suggested change
const parts = [];
if (parsed.summary) parts.push(`<b>Summary:</b> ${escVal(parsed.summary)}`);
if (parsed.duplicate_issues && parsed.duplicate_issues.length > 0) {
parts.push(`<b>Similar/Duplicate Issues:</b><br>${parsed.duplicate_issues.map(d =>
`&nbsp;&nbsp;• <a href="https://github.com/microsoft/mssql-python/issues/${d.issue_number}">#${d.issue_number}</a> ${escVal(d.title)} [${d.state}] — <i>${escVal(d.similarity)}:</i> ${escVal(d.explanation)}`
).join("<br>")}`);
}
if (parsed.recently_fixed && parsed.recently_fixed.length > 0) {
parts.push(`<b>Recently Fixed:</b><br>${parsed.recently_fixed.map(f =>
`&nbsp;&nbsp;• <a href="https://github.com/microsoft/mssql-python/issues/${f.issue_number}">#${f.issue_number}</a> ${escVal(f.title)} (closed ${escVal(f.closed_at)}) — ${escVal(f.relevance)}`
).join("<br>")}`);
const safeIssueNumber = (value) => {
const normalized = String(value).trim();
return /^[0-9]+$/.test(normalized) ? normalized : null;
};
const parts = [];
if (parsed.summary) parts.push(`<b>Summary:</b> ${escVal(parsed.summary)}`);
if (parsed.duplicate_issues && parsed.duplicate_issues.length > 0) {
parts.push(`<b>Similar/Duplicate Issues:</b><br>${parsed.duplicate_issues.map(d => {
const issueNumber = safeIssueNumber(d.issue_number);
const issueLink = issueNumber
? `<a href="https://github.com/microsoft/mssql-python/issues/${encodeURIComponent(issueNumber)}">#${issueNumber}</a>`
: `#${escVal(d.issue_number)}`;
return `&nbsp;&nbsp;• ${issueLink} ${escVal(d.title)} [${escVal(d.state)}] — <i>${escVal(d.similarity)}:</i> ${escVal(d.explanation)}`;
}).join("<br>")}`);
}
if (parsed.recently_fixed && parsed.recently_fixed.length > 0) {
parts.push(`<b>Recently Fixed:</b><br>${parsed.recently_fixed.map(f => {
const issueNumber = safeIssueNumber(f.issue_number);
const issueLink = issueNumber
? `<a href="https://github.com/microsoft/mssql-python/issues/${encodeURIComponent(issueNumber)}">#${issueNumber}</a>`
: `#${escVal(f.issue_number)}`;
return `&nbsp;&nbsp;• ${issueLink} ${escVal(f.title)} (closed ${escVal(f.closed_at)}) — ${escVal(f.relevance)}`;
}).join("<br>")}`);

Copilot uses AI. Check for mistakes.
Comment thread test-triage-local.js
suggestedResponse ? `<hr>` : '',
suggestedResponse ? `<h3>✉️ Suggested Response to Customer</h3>` : '',
suggestedResponse ? `<p><i>Copy-paste or edit the response below and post it on the issue:</i></p>` : '',
suggestedResponse ? `<blockquote>${esc(suggestedResponse)}</blockquote>` : '',
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow version converts the suggested response into HTML with <br> line breaks, but the local test script renders it inside <blockquote> without converting newlines. This makes the local preview diverge from the workflow output and can make the response hard to read. Consider replacing \n with <br> (after escaping) to match the workflow behavior.

Suggested change
suggestedResponse ? `<blockquote>${esc(suggestedResponse)}</blockquote>` : '',
suggestedResponse ? `<blockquote>${esc(suggestedResponse).replace(/\n/g, "<br>")}</blockquote>` : '',

Copilot uses AI. Check for mistakes.
… hrefs

- Escape .state and .confidence via @html (jq) / escVal() (JS) instead of raw interpolation
- Validate .issue_number as numeric-only before using in href attributes (jq: test, JS: regex filter + Number())
- Entries with non-numeric issue_number are silently dropped from the output
- Applied consistently in issue-notify.yml and test-triage-local.js
- Replace @html/@escval escaping of confidence with strict allowlist validation
- Only 'high', 'medium', 'low' are accepted; anything else renders as 'unknown'
- Prevents HTML injection via unexpected confidence values from LLM output
- Applied in both issue-notify.yml (jq) and test-triage-local.js
@sumitmsft sumitmsft changed the title CHORE: Add suggested customer response, similar issue detection, and workaround suggestions to AI triage CHORE: Refactoring AI triaging Apr 23, 2026
@github-actions github-actions Bot added the pr-size: large Substantial code update label Apr 23, 2026
@github-actions
Copy link
Copy Markdown

📊 Code Coverage Report

🔥 Diff Coverage

100%


🎯 Overall Coverage

79%


📈 Total Lines Covered: 6718 out of 8477
📁 Project: mssql-python


Diff Coverage

Diff: main...HEAD, staged and unstaged changes

No lines with coverage information in this diff.


📋 Files Needing Attention

📉 Files with overall lowest coverage (click to expand)
mssql_python.pybind.logger_bridge.cpp: 59.2%
mssql_python.pybind.ddbc_bindings.h: 67.8%
mssql_python.row.py: 70.5%
mssql_python.pybind.logger_bridge.hpp: 70.8%
mssql_python.pybind.ddbc_bindings.cpp: 74.4%
mssql_python.pybind.connection.connection.cpp: 75.8%
mssql_python.__init__.py: 77.3%
mssql_python.ddbc_bindings.py: 79.6%
mssql_python.pybind.connection.connection_pool.cpp: 79.6%
mssql_python.connection.py: 85.2%

🔗 Quick Links

⚙️ Build Summary 📋 Coverage Details

View Azure DevOps Build

Browse Full Coverage Report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-size: large Substantial code update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants