This repository has been archived on 2026-08-04. You can view files and clone it, but cannot push or open issues or pull requests.
exec-aide/src/conversation/app.py

105 lines
3.6 KiB
Python
Raw Normal View History

import json
import logging
import boto3
from shared.dynamo import get_conversation, save_conversation
from shared.secrets import get_slack_token
from shared.slack import update_message
from conversation.tools import TOOLS, dispatch_tool
logger = logging.getLogger()
logger.setLevel(logging.INFO)
_bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
MODEL_ID = "us.anthropic.claude-sonnet-4-6"
MAX_TOOL_ITERATIONS = 10
SYSTEM_PROMPT = """\
You are Exec Aide, a personal executive assistant for Adam Moussa, President \
of Sea Haven Industries (a facilities maintenance company).
You have access to Adam's email inbox. Use the available tools to answer \
questions about emails, find information, and provide inbox summaries.
Be concise and direct. Adam is busy - give him the key information without \
unnecessary preamble. Use bullet points for lists of emails.
When referencing emails, always include the sender name and subject line. \
For time references, use relative terms (e.g., "2 hours ago", "yesterday").
If Adam asks about something outside your current capabilities (calendar, \
tasks, etc.), acknowledge it and let him know it's not available yet.\
"""
def lambda_handler(event, context):
user_message = event["user_message"]
thread_ts = event["thread_ts"]
channel = event["channel"]
placeholder_ts = event["placeholder_ts"]
try:
messages = get_conversation(thread_ts)
messages.append({"role": "user", "content": [{"text": user_message}]})
assistant_message = _run_conversation(messages)
final_text = _extract_text(assistant_message)
save_conversation(thread_ts, messages)
update_message(channel, placeholder_ts, final_text, get_slack_token())
logger.info("Conversation complete: thread=%s, turns=%d", thread_ts, len(messages))
except Exception:
logger.exception("Conversation failed for thread %s", thread_ts)
try:
update_message(
channel, placeholder_ts,
"Sorry, I hit an error processing that. Try again in a moment.",
get_slack_token(),
)
except Exception:
logger.exception("Failed to update error placeholder")
def _run_conversation(messages):
assistant_message = None
for _ in range(MAX_TOOL_ITERATIONS):
response = _bedrock.converse(
modelId=MODEL_ID,
system=[{"text": SYSTEM_PROMPT}],
messages=messages,
toolConfig={"tools": TOOLS},
inferenceConfig={"maxTokens": 4096},
)
assistant_message = response["output"]["message"]
messages.append(assistant_message)
if response["stopReason"] != "tool_use":
break
tool_results = []
for block in assistant_message["content"]:
tool_use = block.get("toolUse")
if tool_use:
logger.info("Tool call: %s(%s)", tool_use["name"], json.dumps(tool_use["input"]))
result = dispatch_tool(tool_use["name"], tool_use["input"])
tool_results.append({
"toolResult": {
"toolUseId": tool_use["toolUseId"],
"content": [{"text": json.dumps(result)}],
}
})
messages.append({"role": "user", "content": tool_results})
return assistant_message
def _extract_text(message):
parts = []
for block in message.get("content", []):
if "text" in block:
parts.append(block["text"])
return "\n".join(parts) if parts else "I processed your request but have nothing to report."