{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load env variables and create client\n",
    "from dotenv import load_dotenv\n",
    "from anthropic import Anthropic\n",
    "\n",
    "load_dotenv()\n",
    "\n",
    "client = Anthropic()\n",
    "model = \"claude-3-7-sonnet-latest\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Helper functions\n",
    "from anthropic.types import Message\n",
    "\n",
    "\n",
    "def add_user_message(messages, message):\n",
    "    user_message = {\n",
    "        \"role\": \"user\",\n",
    "        \"content\": message.content if isinstance(message, Message) else message,\n",
    "    }\n",
    "    messages.append(user_message)\n",
    "\n",
    "\n",
    "def add_assistant_message(messages, message):\n",
    "    assistant_message = {\n",
    "        \"role\": \"assistant\",\n",
    "        \"content\": message.content if isinstance(message, Message) else message,\n",
    "    }\n",
    "    messages.append(assistant_message)\n",
    "\n",
    "\n",
    "def chat(\n",
    "    messages,\n",
    "    system=None,\n",
    "    temperature=1.0,\n",
    "    stop_sequences=[],\n",
    "    tools=None,\n",
    "    tool_choice=None,\n",
    "):\n",
    "    params = {\n",
    "        \"model\": model,\n",
    "        \"max_tokens\": 1000,\n",
    "        \"messages\": messages,\n",
    "        \"temperature\": temperature,\n",
    "        \"stop_sequences\": stop_sequences,\n",
    "    }\n",
    "\n",
    "    if tool_choice:\n",
    "        params[\"tool_choice\"] = tool_choice\n",
    "\n",
    "    if tools:\n",
    "        params[\"tools\"] = tools\n",
    "\n",
    "    if system:\n",
    "        params[\"system\"] = system\n",
    "\n",
    "    message = client.messages.create(**params)\n",
    "    return message\n",
    "\n",
    "\n",
    "def text_from_message(message):\n",
    "    return \"\\n\".join(\n",
    "        [block.text for block in message.content if block.type == \"text\"]\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Tools and Schemas\n",
    "\n",
    "from datetime import datetime, timedelta\n",
    "\n",
    "\n",
    "article_summary_schema = {\n",
    "    \"name\": \"article_summary\",\n",
    "    \"description\": \"Creates a summary of an article with its key insights. Use this tool when you need to generate a structured summary of an article, research paper, or any textual content. The tool requires the article's title, author name, and a list of the most important insights or takeaways from the content. Each insight should be a concise statement capturing a significant point from the article.\",\n",
    "    \"input_schema\": {\n",
    "        \"type\": \"object\",\n",
    "        \"properties\": {\n",
    "            \"title\": {\n",
    "                \"type\": \"string\",\n",
    "                \"description\": \"The title of the article being summarized.\",\n",
    "            },\n",
    "            \"author\": {\n",
    "                \"type\": \"string\",\n",
    "                \"description\": \"The name of the author who wrote the article.\",\n",
    "            },\n",
    "            \"key_insights\": {\n",
    "                \"type\": \"array\",\n",
    "                \"items\": {\"type\": \"string\"},\n",
    "                \"description\": \"A list of the most important takeaways or insights from the article. Each insight should be a complete, concise statement.\",\n",
    "            },\n",
    "        },\n",
    "        \"required\": [\"title\", \"author\", \"key_insights\"],\n",
    "    },\n",
    "}\n",
    "\n",
    "\n",
    "def add_duration_to_datetime(\n",
    "    datetime_str, duration=0, unit=\"days\", input_format=\"%Y-%m-%d\"\n",
    "):\n",
    "    date = datetime.strptime(datetime_str, input_format)\n",
    "\n",
    "    if unit == \"seconds\":\n",
    "        new_date = date + timedelta(seconds=duration)\n",
    "    elif unit == \"minutes\":\n",
    "        new_date = date + timedelta(minutes=duration)\n",
    "    elif unit == \"hours\":\n",
    "        new_date = date + timedelta(hours=duration)\n",
    "    elif unit == \"days\":\n",
    "        new_date = date + timedelta(days=duration)\n",
    "    elif unit == \"weeks\":\n",
    "        new_date = date + timedelta(weeks=duration)\n",
    "    elif unit == \"months\":\n",
    "        month = date.month + duration\n",
    "        year = date.year + month // 12\n",
    "        month = month % 12\n",
    "        if month == 0:\n",
    "            month = 12\n",
    "            year -= 1\n",
    "        day = min(\n",
    "            date.day,\n",
    "            [\n",
    "                31,\n",
    "                29\n",
    "                if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)\n",
    "                else 28,\n",
    "                31,\n",
    "                30,\n",
    "                31,\n",
    "                30,\n",
    "                31,\n",
    "                31,\n",
    "                30,\n",
    "                31,\n",
    "                30,\n",
    "                31,\n",
    "            ][month - 1],\n",
    "        )\n",
    "        new_date = date.replace(year=year, month=month, day=day)\n",
    "    elif unit == \"years\":\n",
    "        new_date = date.replace(year=date.year + duration)\n",
    "    else:\n",
    "        raise ValueError(f\"Unsupported time unit: {unit}\")\n",
    "\n",
    "    return new_date.strftime(\"%A, %B %d, %Y %I:%M:%S %p\")\n",
    "\n",
    "\n",
    "def set_reminder(content, timestamp):\n",
    "    print(\n",
    "        f\"----\\nSetting the following reminder for {timestamp}:\\n{content}\\n----\"\n",
    "    )\n",
    "\n",
    "\n",
    "add_duration_to_datetime_schema = {\n",
    "    \"name\": \"add_duration_to_datetime\",\n",
    "    \"description\": \"Adds a specified duration to a datetime string and returns the resulting datetime in a detailed format. This tool converts an input datetime string to a Python datetime object, adds the specified duration in the requested unit, and returns a formatted string of the resulting datetime. It handles various time units including seconds, minutes, hours, days, weeks, months, and years, with special handling for month and year calculations to account for varying month lengths and leap years. The output is always returned in a detailed format that includes the day of the week, month name, day, year, and time with AM/PM indicator (e.g., 'Thursday, April 03, 2025 10:30:00 AM').\",\n",
    "    \"input_schema\": {\n",
    "        \"type\": \"object\",\n",
    "        \"properties\": {\n",
    "            \"datetime_str\": {\n",
    "                \"type\": \"string\",\n",
    "                \"description\": \"The input datetime string to which the duration will be added. This should be formatted according to the input_format parameter.\",\n",
    "            },\n",
    "            \"duration\": {\n",
    "                \"type\": \"number\",\n",
    "                \"description\": \"The amount of time to add to the datetime. Can be positive (for future dates) or negative (for past dates). Defaults to 0.\",\n",
    "            },\n",
    "            \"unit\": {\n",
    "                \"type\": \"string\",\n",
    "                \"description\": \"The unit of time for the duration. Must be one of: 'seconds', 'minutes', 'hours', 'days', 'weeks', 'months', or 'years'. Defaults to 'days'.\",\n",
    "            },\n",
    "            \"input_format\": {\n",
    "                \"type\": \"string\",\n",
    "                \"description\": \"The format string for parsing the input datetime_str, using Python's strptime format codes. For example, '%Y-%m-%d' for ISO format dates like '2025-04-03'. Defaults to '%Y-%m-%d'.\",\n",
    "            },\n",
    "        },\n",
    "        \"required\": [\"datetime_str\"],\n",
    "    },\n",
    "}\n",
    "\n",
    "set_reminder_schema = {\n",
    "    \"name\": \"set_reminder\",\n",
    "    \"description\": \"Creates a timed reminder that will notify the user at the specified time with the provided content. This tool schedules a notification to be delivered to the user at the exact timestamp provided. It should be used when a user wants to be reminded about something specific at a future point in time. The reminder system will store the content and timestamp, then trigger a notification through the user's preferred notification channels (mobile alerts, email, etc.) when the specified time arrives. Reminders are persisted even if the application is closed or the device is restarted. Users can rely on this function for important time-sensitive notifications such as meetings, tasks, medication schedules, or any other time-bound activities.\",\n",
    "    \"input_schema\": {\n",
    "        \"type\": \"object\",\n",
    "        \"properties\": {\n",
    "            \"content\": {\n",
    "                \"type\": \"string\",\n",
    "                \"description\": \"The message text that will be displayed in the reminder notification. This should contain the specific information the user wants to be reminded about, such as 'Take medication', 'Join video call with team', or 'Pay utility bills'.\",\n",
    "            },\n",
    "            \"timestamp\": {\n",
    "                \"type\": \"string\",\n",
    "                \"description\": \"The exact date and time when the reminder should be triggered, formatted as an ISO 8601 timestamp (YYYY-MM-DDTHH:MM:SS) or a Unix timestamp. The system handles all timezone processing internally, ensuring reminders are triggered at the correct time regardless of where the user is located. Users can simply specify the desired time without worrying about timezone configurations.\",\n",
    "            },\n",
    "        },\n",
    "        \"required\": [\"content\", \"timestamp\"],\n",
    "    },\n",
    "}\n",
    "\n",
    "batch_tool_schema = {\n",
    "    \"name\": \"batch_tool\",\n",
    "    \"description\": \"Invoke multiple other tool calls simultaneously\",\n",
    "    \"input_schema\": {\n",
    "        \"type\": \"object\",\n",
    "        \"properties\": {\n",
    "            \"invocations\": {\n",
    "                \"type\": \"array\",\n",
    "                \"description\": \"The tool calls to invoke\",\n",
    "                \"items\": {\n",
    "                    \"type\": \"object\",\n",
    "                    \"properties\": {\n",
    "                        \"name\": {\n",
    "                            \"type\": \"string\",\n",
    "                            \"description\": \"The name of the tool to invoke\",\n",
    "                        },\n",
    "                        \"arguments\": {\n",
    "                            \"type\": \"string\",\n",
    "                            \"description\": \"The arguments to the tool, encoded as a JSON string\",\n",
    "                        },\n",
    "                    },\n",
    "                    \"required\": [\"name\", \"arguments\"],\n",
    "                },\n",
    "            }\n",
    "        },\n",
    "        \"required\": [\"invocations\"],\n",
    "    },\n",
    "}\n",
    "\n",
    "pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# get_current_datetime tool function\n",
    "from anthropic.types import ToolParam\n",
    "\n",
    "\n",
    "def get_current_datetime(date_format=\"%Y-%m-%d %H:%M:%S\"):\n",
    "    if not date_format:\n",
    "        raise ValueError(\"date_format cannot be empty\")\n",
    "    return datetime.now().strftime(date_format)\n",
    "\n",
    "\n",
    "get_current_datetime_schema = ToolParam(\n",
    "    {\n",
    "        \"name\": \"get_current_datetime\",\n",
    "        \"description\": \"Returns the current date and time formatted according to the specified format string. This tool provides the current system time formatted as a string. Use this tool when you need to know the current date and time, such as for timestamping records, calculating time differences, or displaying the current time to users. The default format returns the date and time in ISO-like format (YYYY-MM-DD HH:MM:SS).\",\n",
    "        \"input_schema\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "                \"date_format\": {\n",
    "                    \"type\": \"string\",\n",
    "                    \"description\": \"A string specifying the format of the returned datetime. Uses Python's strftime format codes. For example, '%Y-%m-%d' returns just the date in YYYY-MM-DD format, '%H:%M:%S' returns just the time in HH:MM:SS format, '%B %d, %Y' returns a date like 'May 07, 2025'. The default is '%Y-%m-%d %H:%M:%S' which returns a complete timestamp like '2025-05-07 14:32:15'.\",\n",
    "                    \"default\": \"%Y-%m-%d %H:%M:%S\",\n",
    "                }\n",
    "            },\n",
    "            \"required\": [],\n",
    "        },\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Tool Running\n",
    "import json\n",
    "\n",
    "\n",
    "def run_batch(invocations=[]):\n",
    "    batch_output = []\n",
    "\n",
    "    for invocation in invocations:\n",
    "        name = invocation[\"name\"]\n",
    "        args = json.loads(invocation[\"arguments\"])\n",
    "\n",
    "        tool_output = run_tool(name, args)\n",
    "\n",
    "        batch_output.append({\"tool_name\": name, \"output\": tool_output})\n",
    "\n",
    "    return batch_output\n",
    "\n",
    "\n",
    "def run_tool(tool_name, tool_input):\n",
    "    if tool_name == \"get_current_datetime\":\n",
    "        return get_current_datetime(**tool_input)\n",
    "    elif tool_name == \"add_duration_to_datetime\":\n",
    "        return add_duration_to_datetime(**tool_input)\n",
    "    elif tool_name == \"set_reminder\":\n",
    "        return set_reminder(**tool_input)\n",
    "    elif tool_name == \"batch_tool\":\n",
    "        return run_batch(**tool_input)\n",
    "\n",
    "\n",
    "def run_tools(message):\n",
    "    tool_requests = [\n",
    "        block for block in message.content if block.type == \"tool_use\"\n",
    "    ]\n",
    "    tool_result_blocks = []\n",
    "\n",
    "    for tool_request in tool_requests:\n",
    "        try:\n",
    "            tool_output = run_tool(tool_request.name, tool_request.input)\n",
    "            tool_result_block = {\n",
    "                \"type\": \"tool_result\",\n",
    "                \"tool_use_id\": tool_request.id,\n",
    "                \"content\": json.dumps(tool_output),\n",
    "                \"is_error\": False,\n",
    "            }\n",
    "        except Exception as e:\n",
    "            tool_result_block = {\n",
    "                \"type\": \"tool_result\",\n",
    "                \"tool_use_id\": tool_request.id,\n",
    "                \"content\": f\"Error: {e}\",\n",
    "                \"is_error\": True,\n",
    "            }\n",
    "\n",
    "        tool_result_blocks.append(tool_result_block)\n",
    "\n",
    "    return tool_result_blocks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Run conversation\n",
    "def run_conversation(messages):\n",
    "    while True:\n",
    "        response = chat(\n",
    "            messages,\n",
    "            tools=[\n",
    "                get_current_datetime_schema,\n",
    "                add_duration_to_datetime_schema,\n",
    "                set_reminder_schema,\n",
    "                batch_tool_schema,\n",
    "            ],\n",
    "        )\n",
    "\n",
    "        add_assistant_message(messages, response)\n",
    "        print(text_from_message(response))\n",
    "\n",
    "        if response.stop_reason != \"tool_use\":\n",
    "            break\n",
    "\n",
    "        tool_results = run_tools(response)\n",
    "        add_user_message(messages, tool_results)\n",
    "\n",
    "    return messages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'# Advancements in Machine Learning Algorithms: Implications for Autonomous Systems\\n\\nBy Dr. Jonathan Mercer\\n\\nRecent developments in machine learning algorithms have significantly expanded the capabilities of autonomous systems across diverse domains. Convolutional neural networks (CNNs) and transformer architectures have demonstrated remarkable efficacy in pattern recognition tasks, while reinforcement learning techniques continue to evolve, enabling systems to optimize decision-making processes through experience. These algorithmic innovations, coupled with increased computational resources and larger datasets, have accelerated progress in computer vision, natural language processing, and robotics. However, significant challenges remain regarding algorithmic bias, interpretability, and the ethical deployment of autonomous systems in high-stakes environments. As these technologies continue to mature, the computer science community must balance innovation with responsible development practices to ensure that autonomous systems serve beneficial purposes while mitigating potential risks to privacy, security, and social equity.'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "messages = []\n",
    "add_user_message(\n",
    "    messages,\n",
    "    \"\"\"\n",
    "    Write a one-paragraph scholarly article about computer science. \n",
    "    Include a title and author name.\n",
    "    \"\"\",\n",
    ")\n",
    "response = chat(messages)\n",
    "text_from_message(response)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'title': 'Advancements in Machine Learning Algorithms: Implications for Autonomous Systems',\n",
       " 'author': 'Dr. Jonathan Mercer',\n",
       " 'key_insights': ['Convolutional neural networks (CNNs) and transformer architectures have shown remarkable effectiveness in pattern recognition tasks',\n",
       "  'Reinforcement learning techniques are evolving to enable better decision-making processes through experience',\n",
       "  'Algorithmic innovations combined with increased computational resources and larger datasets have accelerated progress in computer vision, NLP, and robotics',\n",
       "  'Significant challenges remain in addressing algorithmic bias, interpretability, and ethical deployment in high-stakes environments',\n",
       "  'The computer science community must balance innovation with responsible development practices to ensure autonomous systems are beneficial while mitigating risks to privacy, security, and social equity']}"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "messages = []\n",
    "\n",
    "add_user_message(messages, text_from_message(response))\n",
    "response = chat(\n",
    "    messages,\n",
    "    tools=[article_summary_schema],\n",
    "    tool_choice={\"type\": \"tool\", \"name\": \"article_summary\"},\n",
    ")\n",
    "response.content[0].input"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
