Skip to content

Commit

Permalink
Sweep: Augment on_ticket so that when a user adds a slack link, we au…
Browse files Browse the repository at this point in the history
…tomatically unroll the thread and extract the information (#3668)

# Description
This pull request introduces enhancements to the `on_ticket` handler
within the SweepAI application. It augments the existing functionality
by automatically unrolling Slack threads when a Slack link is included
in a ticket summary. The extracted conversation from the Slack thread is
then appended to the ticket summary, providing additional context and
information directly within the ticket.

# Summary
- Added `SLACK_API_KEY` environment variable to
`sweepai/config/server.py` for Slack integration.
- Imported `WebClient` and `SlackApiError` from `slack_sdk` in
`sweepai/handlers/on_ticket.py` to enable communication with the Slack
API.
- Implemented a new feature in `on_ticket` that:
  - Detects a Slack link in the ticket summary.
- Uses the Slack API to fetch the permalink data and retrieve the thread
replies.
  - Appends the text of the Slack thread messages to the ticket summary.
- Handles potential `SlackApiError` exceptions and logs errors
accordingly.
- The changes ensure that relevant Slack conversations are automatically
included in the ticket details, improving the ticket resolution process.

Fixes #3656.

---

<details>
<summary><b>🎉 Latest improvements to Sweep:</b></summary>
<ul>
<li>New <a href="https://progress.sweep.dev">dashboard</a> launched for
real-time tracking of Sweep issues, covering all stages from search to
coding.</li>
<li>Integration of OpenAI's latest Assistant API for more efficient and
reliable code planning and editing, improving speed by 3x.</li>
<li>Use the <a
href="https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github">GitHub
issues extension</a> for creating Sweep issues directly from your
editor.</li>
</ul>
</details>


---

### 💡 To get Sweep to edit this pull request, you can:
* Comment below, and Sweep can edit the entire PR
* Comment on a file, Sweep will only modify the commented file
* Edit the original issue to get Sweep to recreate the PR from scratch

*This is an automated message generated by [Sweep
AI](https://sweep.dev).*

---------

Co-authored-by: sweep-nightly[bot] <131841235+sweep-nightly[bot]@users.noreply.github.com>
Co-authored-by: wwzeng1 <[email protected]>
  • Loading branch information
sweep-nightly[bot] and wwzeng1 authored May 3, 2024
1 parent 2b9073a commit d9dbd22
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 11 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ dependencies = [
"boto3==1.34.70",
"scipy==1.12.0",
"jira==3.8.0",
"slack-sdk==3.27.1",
]

[tool.isort]
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# This file was autogenerated by uv via the following command:
# uv pip compile --output-file requirements.txt pyproject.toml
# uv pip compile pyproject.toml -o requirements.txt
aiohttp==3.9.3
# via voyageai
aiolimiter==1.1.0
Expand Down Expand Up @@ -536,6 +536,7 @@ six==1.16.0
# bleach
# python-dateutil
# rfc3339-validator
slack-sdk==3.27.1
smmap==5.0.1
# via gitdb
sniffio==1.3.1
Expand Down
2 changes: 2 additions & 0 deletions sweepai/config/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,5 +200,7 @@
JIRA_API_TOKEN = os.environ.get("JIRA_API_TOKEN", None)
JIRA_URL = os.environ.get("JIRA_URL", None)

SLACK_API_KEY = os.environ.get("SLACK_API_KEY", None)

LICENSE_KEY = os.environ.get("LICENSE_KEY", None)
ALTERNATE_AWS = os.environ.get("ALTERNATE_AWS", "none").lower() == "true"
25 changes: 15 additions & 10 deletions sweepai/handlers/on_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from tqdm import tqdm
from yamllint import linter


from sweepai.core.sweep_bot import GHA_PROMPT
from sweepai.agents.pr_description_bot import PRDescriptionBot
from sweepai.agents.image_description_bot import ImageDescriptionBot
Expand Down Expand Up @@ -84,6 +85,7 @@
TicketProgressStatus,
)
from sweepai.utils.prompt_constructor import HumanMessagePrompt
from sweepai.utils.slack_utils import add_slack_context
from sweepai.utils.str_utils import (
BOT_SUFFIX,
FASTER_MODEL_MESSAGE,
Expand Down Expand Up @@ -112,6 +114,7 @@
)
from sweepai.utils.user_settings import UserSettings


# from sandbox.sandbox_utils import Sandbox


Expand Down Expand Up @@ -813,7 +816,9 @@ def edit_sweep_comment(
"error_message": "We deprecated supporting GPT 3.5.",
}

error_message = validate_issue(title + summary)
internal_message_summary = summary
internal_message_summary += add_slack_context(internal_message_summary)
error_message = validate_issue(title + internal_message_summary)
if error_message:
logger.warning(f"Validation error: {error_message}")
edit_sweep_comment(
Expand All @@ -837,9 +842,8 @@ def edit_sweep_comment(
return {"success": True}

prs_extracted = PRReader.extract_prs(repo, summary)
message_summary = summary
if prs_extracted:
message_summary += "\n\n" + prs_extracted
internal_message_summary += "\n\n" + prs_extracted
edit_sweep_comment(
create_collapsible(
"I found that you mentioned the following Pull Requests that might be important:",
Expand All @@ -854,11 +858,12 @@ def edit_sweep_comment(
# search/context manager
logger.info("Searching for relevant snippets...")
if image_contents: # doing it here to avoid editing the original issue
message_summary += ImageDescriptionBot().describe_images(text=title + message_summary, images=image_contents)
internal_message_summary += ImageDescriptionBot().describe_images(text=title + internal_message_summary, images=image_contents)

snippets, tree, _, repo_context_manager = fetch_relevant_files(
cloned_repo,
title,
message_summary,
internal_message_summary,
replies_text,
username,
metadata,
Expand Down Expand Up @@ -896,7 +901,7 @@ def edit_sweep_comment(
if not repo_description:
repo_description = "No description provided."

message_summary += replies_text
internal_message_summary += replies_text

get_documentation_dict(repo)
docs_results = ""
Expand All @@ -906,7 +911,7 @@ def edit_sweep_comment(
issue_url=issue_url,
repo_description=repo_description,
title=title,
message_summary=message_summary,
message_summary=internal_message_summary,
cloned_repo=cloned_repo,
ticket_progress=ticket_progress,
chat_logger=chat_logger,
Expand Down Expand Up @@ -993,7 +998,7 @@ def edit_sweep_comment(
file_change_requests, plan = get_files_to_change(
relevant_snippets=repo_context_manager.current_top_snippets,
read_only_snippets=repo_context_manager.read_only_snippets,
problem_statement=f"{title}\n\n{message_summary}",
problem_statement=f"{title}\n\n{internal_message_summary}",
repo_name=repo_full_name,
cloned_repo=cloned_repo,
images=image_contents
Expand Down Expand Up @@ -1476,14 +1481,14 @@ def edit_sweep_comment(
branch=pr.head.ref,
)
diffs = get_branch_diff_text(repo=repo, branch=pr.head.ref, base_branch=pr.base.ref)
problem_statement = f"{title}\n{message_summary}\n{replies_text}"
problem_statement = f"{title}\n{internal_message_summary}\n{replies_text}"
all_information_prompt = GHA_PROMPT.format(
problem_statement=problem_statement,
github_actions_logs=failed_gha_logs,
changes_made=diffs,
)

repo_context_manager = prep_snippets(cloned_repo=cloned_repo, query=(title + message_summary + replies_text).strip("\n"), ticket_progress=ticket_progress) # need to do this, can use the old query for speed
repo_context_manager = prep_snippets(cloned_repo=cloned_repo, query=(title + internal_message_summary + replies_text).strip("\n"), ticket_progress=ticket_progress) # need to do this, can use the old query for speed
sweep_bot: SweepBot = construct_sweep_bot(
repo=repo,
repo_name=repo_name,
Expand Down
51 changes: 51 additions & 0 deletions sweepai/utils/slack_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import re

from loguru import logger
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError

from sweepai.config.server import SLACK_API_KEY

def get_thread_by_thread_ts(client: WebClient, channel_id: int, thread_ts: int):
response = client.conversations_replies(
channel=channel_id,
ts=thread_ts
)

if response["ok"]:
return response["messages"]
else:
print(f"Error fetching thread: {response['error']}")
return None

def add_slack_context(summary) -> str:
result = ""
if not SLACK_API_KEY:
return result
slack_link_match = re.search(r'(https://[\w-]+\.slack\.com/archives/\w+/p\d+)', summary)
if not slack_link_match:
return result
slack_link = slack_link_match.group(1)
slack_client = WebClient(token=SLACK_API_KEY)
try:
# Extract channel and message_ts from the Slack link
link_parts = slack_link.split('/')
slack_channel_id = link_parts[-2]
slack_message_ts = link_parts[-1][1:] # Remove the 'p' prefix
# you need to add a dot six places from the right
slack_message_ts = f"{slack_message_ts[:-6]}.{slack_message_ts[-6:]}"
# Fetch the message object
thread_messages = get_thread_by_thread_ts(client=slack_client, channel_id=slack_channel_id, thread_ts=slack_message_ts)
if len(thread_messages) > 0:
result += f"\n\nThe following slack thread was attached to {slack_link}:\n<slack_thread>\n"
for idx, thread_message in enumerate(thread_messages):
result += f"Message {idx}: {thread_message['text']}\n" if "text" in thread_message else ""
result += "</slack_thread>"
except SlackApiError as e:
logger.error(f"Error fetching Slack message or thread: {e}")
return result

if __name__ == "__main__":
url = ""
result = add_slack_context(url)
print(result)

0 comments on commit d9dbd22

Please sign in to comment.