Here's my response code added to the lambda,
The response is coming in json in Lambda output: Status: Succeeded Test Event Name: sopselectorjira and here's the lamda code that developed for sop selection for incident.
import boto3
import json
import requests
import logging
import re
from requests.auth import HTTPBasicAuth
from atlassian import Confluence
logger = logging.getLogger()
logger.setLevel(logging.INFO)
─────────────────────────────────────────
GET SECRETS
─────────────────────────────────────────
def get_secret(secret_name):
client = boto3.client('secretsmanager', region_name='us-east-2')
response = client.get_secret_value(SecretId=secret_name)
raw = json.loads(response['SecretString'])
return {k.strip(): v.strip() if isinstance(v, str) else v for k, v in raw.items()}
─────────────────────────────────────────
CLEAN HTML ✅ FIXED
─────────────────────────────────────────
def clean_html(html: str) -> str:
text = re.sub(r"<[^>]+>", " ", html)
text = re.sub(r"\s+", " ", text).strip()
return text
─────────────────────────────────────────
FETCH SOPs
─────────────────────────────────────────
def get_sops_from_confluence(confluence_url, email, token, space="SRE"):
confluence = Confluence(
url=confluence_url,
username=email,
password=token,
cloud=True
)
pages = confluence.get_all_pages_from_space(
space=space,
limit=50,
expand="body.storage"
)
sops = []
for p in pages:
body = clean_html(p["body"]["storage"]["value"])
sops.append({
"title": p["title"],
"url": f"{confluence_url}/spaces/{space}/pages/{p['id']}",
"body": body[:1500]
})
return sops
─────────────────────────────────────────
PROMPT BUILDER ✅ FIXED
─────────────────────────────────────────
def build_prompt(ticket_body: str, sops: list) -> str:
sop_list = ""
for i, sop in enumerate(sops):
sop_list += (
f"\nSOP #{i+1}:\n"
f"Title: {sop['title']}\n"
f"URL: {sop['url']}\n"
f"Summary: {sop['body'][:300]}\n"
)
return f"""
You are a senior SRE engineer.
Select the most relevant SOP.
INCIDENT:
{ticket_body}
AVAILABLE SOPs:
{sop_list}
Return ONLY JSON:
{{
"sop_title": "...",
"sop_url": "...",
"confidence": "high | medium | low",
"reason": "short explanation"
}}
"""
─────────────────────────────────────────
BEDROCK CALL
─────────────────────────────────────────
def select_sop_with_bedrock(ticket_body, sops):
bedrock = boto3.client('bedrock-runtime', region_name='us-east-2')
#model_id = "arn:aws:bedrock:us-east-2:008971655733:application-inference-profile/paz9kbmgbj6s"
model_id="amazon.nova-premier-v1:0"
prompt = build_prompt(ticket_body, sops)
response = bedrock.converse(
modelId=model_id,
messages=[{
"role": "user",
"content": [{"text": prompt}]
}],
inferenceConfig={
"maxTokens": 512,
"temperature": 0
}
)
raw_text = response["output"]["message"]["content"][0]["text"]
clean = re.sub(r"^```json|```$", "", raw_text, flags=re.MULTILINE).strip()
match = re.search(r"\{.*\}", clean, re.DOTALL)
if not match:
return fallback_sop(sops, "No JSON detected")
try:
return json.loads(match.group())
except Exception:
return fallback_sop(sops, "Parsing error")
─────────────────────────────────────────
FALLBACK
─────────────────────────────────────────
def fallback_sop(sops, reason):
if not sops:
return {
"sop_title": "No SOP available",
"sop_url": "N/A",
"confidence": "low",
"reason": reason
}
return {
"sop_title": sops[0]["title"],
"sop_url": sops[0]["url"],
"confidence": "low",
"reason": reason
}
─────────────────────────────────────────
JIRA UPDATE
─────────────────────────────────────────
def post_sop_to_jira(issue_key, sop, jira_url, email, token):
url = f"{jira_url}/rest/api/3/issue/{issue_key}/comment"
auth = HTTPBasicAuth(email, token)
payload = {
"body": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [
{"type": "text", "text": "SOP: "},
{
"type": "text",
"text": sop['sop_title'],
"marks": [
{
"type": "link",
"attrs": {"href": sop['sop_url']}
}
]
}
]
}
]
}
}
requests.post(url, json=payload, auth=auth)
─────────────────────────────────────────
✅ FINAL RESPONSE (TEXT FORMAT — CRITICAL FIX)
─────────────────────────────────────────
def format_bedrock_response(event, sop):
return {
"messageVersion": "1.0",
"response": {
"actionGroup": event.get("actionGroup", ""),
"function": event.get("function", ""),
"functionResponse": {
"responseBody": {
"text/plain": json.dumps(sop)
}
}
}
}
─────────────────────────────────────────
✅ LAMBDA HANDLER
─────────────────────────────────────────
def lambda_handler(event, context):
try:
logger.info(f"Incoming event: {json.dumps(event)}")
creds = get_secret('jira-credentials')
# ✅ Convert parameters list → dict
body = {}
if isinstance(event.get("parameters"), list):
for p in event["parameters"]:
body[p.get("name")] = p.get("value")
elif isinstance(event.get("parameters"), dict):
body = event["parameters"]
else:
body = event
logger.info(f"Processed body: {json.dumps(body)}")
# ✅ Convert string → issue object
if isinstance(body.get("issue"), str):
user_input = body.get("issue")
body["issue"] = {
"key": "INC-23",
"fields": {
"summary": user_input,
"description": user_input
}
}
issue = body.get("issue", {})
key = issue.get("key", "INC-23")
summary = issue.get("fields", {}).get("summary", "")
if "latency" in summary.lower() or "issue" in summary.lower():
sops = get_sops_from_confluence(
creds["confluence_url"],
creds["jira_email"],
creds["confluence_token"]
)
sop = select_sop_with_bedrock(summary, sops)
post_sop_to_jira(
key,
sop,
creds["jira_url"],
creds["jira_email"],
creds["jira_api_token"]
)
else:
sop = {
"sop_title": "N/A",
"sop_url": "N/A",
"confidence": "low",
"reason": "Not a valid incident"
}
return format_bedrock_response(event, sop)
except Exception as e:
logger.error(str(e))
return format_bedrock_response(event, {
"sop_title": "Error",
"sop_url": "N/A",
"confidence": "low",
"reason": str(e)
})
The area below shows the last 4 KB of the execution log.
We are encountering the following error in Bedrock:
"The server encountered an error processing the Lambda response. Check the Lambda response and retry the request."
Could you please advise if any changes are required in the Lambda response formatting (specifically in the format_bedrock_response function)?
Your assistance on this would be highly appreciated.