Skip to content
This repository was archived by the owner on Apr 9, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 22 additions & 19 deletions alita_sdk/tools/ado/wiki/ado_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,11 @@ def _process_images(self, page_content: str, wiki_identified: str, image_descrip
logger.error(f"Failed to initialize repos wrapper for wiki '{wiki_identified}': {str(e)}")

for image_name, image_url in matches:
# BUG 1 fix: skip images with empty URLs — cannot fetch or resolve them
if not image_url:
logger.warning(f"Skipping image '{image_name}': empty URL, leaving original markdown unchanged.")
continue

if image_url.startswith("/.attachments/"):
try:
if repos_wrapper is None:
Expand All @@ -517,26 +522,24 @@ def _process_images(self, page_content: str, wiki_identified: str, image_descrip
image_description_prompt=image_description_prompt,
repos_wrapper=repos_wrapper)
except Exception as e:
logger.error(f"Error parsing attachment: {str(e)}")
description = f"Error parsing attachment: {image_url}"
# Skip rather than replace with a corrupted description
logger.warning(f"Skipping image '{image_name}': error parsing attachment '{image_url}': {str(e)}")
continue
else:
if not image_url:
logger.warning(f"Skipping image '{image_name}' with empty URL")
description = "[Image could not be processed: empty URL]"
else:
try:
response = requests.get(image_url)
response.raise_for_status()
file_content = response.content
description = parse_file_content(
file_content=file_content,
file_name="image.png",
llm=self.llm,
prompt=image_description_prompt
)
except Exception as e:
logger.error(f"Error fetching external image: {str(e)}")
description = f"Error fetching external image: {image_url}"
try:
response = requests.get(image_url)
response.raise_for_status()
file_content = response.content
description = parse_file_content(
file_content=file_content,
file_name="image.png",
llm=self.llm,
prompt=image_description_prompt
)
except Exception as e:
# BUG 2 fix: skip rather than replace with a corrupted literal description
logger.warning(f"Skipping image '{image_name}': error fetching external image '{image_url}': {str(e)}")
continue

new_image_markdown = f"![{image_name}]({description})"
page_content = page_content.replace(f"![{image_name}]({image_url})", new_image_markdown)
Expand Down
63 changes: 46 additions & 17 deletions alita_sdk/tools/jira/api_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,16 @@
jira_issue_key=(str, Field(description="Jira issue key from which field with images will be extracted, e.g. TEST-1234")),
field_name=(str, Field(description="Field name containing images to be processed. Common values are 'description', 'comment', or custom fields like 'customfield_10300'")),
prompt=(Optional[str], Field(description="Custom prompt to use for image description generation. If not provided, a default prompt will be used", default=None)),
context_radius=(Optional[int], Field(description="Number of characters to include before and after each image for context. Default is 500", default=500))
context_radius=(Optional[int], Field(description="Number of characters to include before and after each image for context. Default is 500", default=500)),
process_images=(Optional[bool], Field(description="Whether to process images with LLM and replace references with descriptions. Set to False to return raw field content without image processing. Default is True", default=True))
)

GetCommentsWithImageDescriptions = create_model(
"GetCommentsWithImageDescriptionsModel",
jira_issue_key=(str, Field(description="Jira issue key from which comments with images will be extracted, e.g. TEST-1234")),
prompt=(Optional[str], Field(description="Custom prompt to use for image description generation. If not provided, a default prompt will be used", default=None)),
context_radius=(Optional[int], Field(description="Number of characters to include before and after each image for context. Default is 500", default=500))
context_radius=(Optional[int], Field(description="Number of characters to include before and after each image for context. Default is 500", default=500)),
process_images=(Optional[bool], Field(description="Whether to process images with LLM and replace references with descriptions. Set to False to return raw comments without image processing. Default is True", default=True))
)

GetRemoteLinks = create_model(
Expand Down Expand Up @@ -1270,7 +1272,9 @@ def _process_image_with_llm(self, image_data, image_name: str = "", context_text
# Get the LLM instance
llm = self.llm
if not llm:
return "[LLM not available for image processing]"
raise ToolException(
"LLM is required for image processing but is not configured in this toolkit instance."
)

# Try to load and validate the image with PIL instead of using imghdr
try:
Expand Down Expand Up @@ -1451,25 +1455,28 @@ def _extract_image_data(self, field_data):
return f"Unsupported field content type: {type(field_data)}. Expected a string, list, or dict."

def get_field_with_image_descriptions(self, jira_issue_key: str, field_name: str, prompt: Optional[str] = None,
context_radius: int = 500):
context_radius: int = 500, process_images: bool = True):
"""
Get a field from Jira issue and augment any images in it with textual descriptions that include
image names and contextual information from surrounding text.

This method will:
1. Extract the specified field content from Jira
2. Detect images in the content
3. Retrieve and process each image with an LLM, providing surrounding context
4. Replace image references with the generated text descriptions
3. Retrieve and process each image with an LLM, providing surrounding context (if process_images=True)
4. Replace image references with the generated text descriptions (if process_images=True)

Args:
jira_issue_key: The Jira issue key to retrieve field from (e.g., 'TEST-1234')
field_name: The field containing images (e.g., 'description')
prompt: Custom prompt for the LLM when analyzing images. If None, a default prompt will be used.
context_radius: Number of characters to include before and after each image for context. Default is 500.
process_images: Whether to process images with LLM. Set to False to return raw field content
without image processing. Default is True.

Returns:
The field content with image references replaced with contextual descriptions
The field content with image references replaced with contextual descriptions,
or raw field content when process_images=False
"""
try:
# Get the specified field from the Jira issue
Expand All @@ -1485,6 +1492,16 @@ def get_field_with_image_descriptions(self, jira_issue_key: str, field_name: str
# Handle multiple images or non-string content
field_content = self._extract_image_data(field_content)

if not process_images:
logger.info(f"Image processing skipped for field '{field_name}' of issue '{jira_issue_key}' (process_images=False)")
return f"Field '{field_name}' from issue '{jira_issue_key}':\n\n{field_content}"

if not self.llm:
raise ToolException(
"LLM is required for image processing but is not configured in this toolkit instance. "
"Please configure an LLM in the toolkit settings or set process_images=False to skip image processing."
)

# Regular expression to find image references in Jira markup
image_pattern = r'!([^!|]+)(?:\|[^!]*)?!'

Expand Down Expand Up @@ -1594,7 +1611,7 @@ def process_image_match(self, match, body, attachment_resolver, context_radius=5
logger.error(f"Error retrieving attachment {image_ref}: {str(e)}")
return f"[Image: {image_ref} - Error: {str(e)}]"

def get_processed_comments_list_with_image_description(self, jira_issue_key: str, prompt: Optional[str] = None, context_radius: int = 500):
def get_processed_comments_list_with_image_description(self, jira_issue_key: str, prompt: Optional[str] = None, context_radius: int = 500, process_images: bool = True):
# Retrieve all comments for the issue
comments = self._client.issue_get_comments(jira_issue_key)

Expand All @@ -1619,10 +1636,13 @@ def get_processed_comments_list_with_image_description(self, jira_issue_key: str
comment_created = comment.get('created', 'Unknown date')
comment_body = self._extract_image_data(comment_body)

# Process the comment body by replacing image references with descriptions
processed_body = re.sub(image_pattern,
lambda match: self.process_image_match(match, comment_body, attachment_resolver, context_radius, prompt),
comment_body)
if process_images:
# Process the comment body by replacing image references with descriptions
processed_body = re.sub(image_pattern,
lambda match: self.process_image_match(match, comment_body, attachment_resolver, context_radius, prompt),
comment_body)
else:
processed_body = comment_body

# Add the processed comment to our results
processed_comments.append({
Expand All @@ -1635,28 +1655,37 @@ def get_processed_comments_list_with_image_description(self, jira_issue_key: str

return processed_comments

def get_comments_with_image_descriptions(self, jira_issue_key: str, prompt: Optional[str] = None, context_radius: int = 500):
def get_comments_with_image_descriptions(self, jira_issue_key: str, prompt: Optional[str] = None, context_radius: int = 500, process_images: bool = True):
"""
Get all comments from Jira issue and augment any images in them with textual descriptions.

This method will:
1. Extract all comments from the specified Jira issue
2. Detect images in each comment
3. Retrieve and process each image with an LLM, providing surrounding context
4. Replace image references with the generated text descriptions
3. Retrieve and process each image with an LLM, providing surrounding context (if process_images=True)
4. Replace image references with the generated text descriptions (if process_images=True)

Args:
jira_issue_key: The Jira issue key to retrieve comments from (e.g., 'TEST-1234')
prompt: Custom prompt for the LLM when analyzing images. If None, a default prompt will be used.
context_radius: Number of characters to include before and after each image for context. Default is 500.
process_images: Whether to process images with LLM. Set to False to return raw comments without
image processing. Default is True.

Returns:
The comments with image references replaced with contextual descriptions
The comments with image references replaced with contextual descriptions,
or raw comments when process_images=False
"""
try:
if process_images and not self.llm:
raise ToolException(
"LLM is required for image processing but is not configured in this toolkit instance. "
"Please configure an LLM in the toolkit settings or set process_images=False to skip image processing."
)
processed_comments = self.get_processed_comments_list_with_image_description(jira_issue_key=jira_issue_key,
prompt=prompt,
context_radius=context_radius)
context_radius=context_radius,
process_images=process_images)
if not processed_comments:
return f"No comments found for issue '{jira_issue_key}'"
# Format the output
Expand Down
Loading