{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## MiniAutoGen: Implementing a Multi-Agent Chat with Advanced Pipelines\n",
    "\n",
    "The following example showcases the power and flexibility of MiniAutoGen, illustrating how to set up a multi-agent chat environment complete with sophisticated pipelines integrating LLMs, user interactions, and custom logic.\n",
    "\n",
    "We are using some pre build components, you can see this [here.](../miniautogen/pipeline/components/components.py)\n",
    "\n",
    "**Prerequisites**\n",
    "\n",
    "Ensure you have the MiniAutoGen library installed and an OpenAI API key available. This example also uses the dotenv library for environment management."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Initializing LLM Clients\n",
    "\n",
    "from miniautogen.llms.llm_client import LiteLLMClient\n",
    "litellm_client = LiteLLMClient(model=\"ollama/mistral-openorca\") #We he are using a local model on OLLAMA.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Building the Chat Environment\n",
    "\n",
    "from miniautogen.chat.chat import Chat\n",
    "from miniautogen.agent.agent import Agent\n",
    "from miniautogen.chat.chatadmin import ChatAdmin\n",
    "from miniautogen.pipeline.pipeline import Pipeline\n",
    "from miniautogen.pipeline.components.components import (\n",
    "    UserResponseComponent, AgentReplyComponent, TerminateChatComponent,\n",
    "    Jinja2SingleTemplateComponent, LLMResponseComponent, NextAgentSelectorComponent\n",
    ")\n",
    "\n",
    "# Define a Jinja2 template for formatting messages\n",
    "template_str = \"\"\"\n",
    "[{\"role\": \"system\", \"content\": \"{{ agent.role }}\"}{% for message in messages %},\n",
    "  {% if message.sender_id == agent.agent_id %}\n",
    "    {\"role\": \"assistant\", \"content\": {{ message.message | tojson | safe }}}\n",
    "  {% else %}\n",
    "    {\"role\": \"user\", \"content\": {{ message.message | tojson | safe }}}\n",
    "  {% endif %}\n",
    "{% endfor %}]\n",
    "\"\"\"\n",
    "\n",
    "# Initialize Jinja2 component with the template\n",
    "jinja_component = Jinja2SingleTemplateComponent()\n",
    "jinja_component.set_template_str(template_str)\n",
    "\n",
    "# Set up pipelines for different components\n",
    "pipeline_user = Pipeline([UserResponseComponent()])\n",
    "pipeline_jinja = Pipeline([jinja_component, LLMResponseComponent(litellm_client)])\n",
    "pipeline_admin = Pipeline([NextAgentSelectorComponent(), AgentReplyComponent(), TerminateChatComponent()])\n",
    "\n",
    "# Create the chat environment\n",
    "chat = Chat()\n",
    "\n",
    "# Define agents with JSON data\n",
    "json_data = {'agent_id': 'Bruno', 'name': 'Bruno', 'role': 'user'}\n",
    "agent1 = Agent.from_json(json_data)\n",
    "agent1.pipeline = pipeline_user  # Assign the user pipeline to agent1\n",
    "\n",
    "agent2 = Agent(\"dev\", \"Carlos\", \"Python Senior Developer\")\n",
    "agent2.pipeline = pipeline_jinja  # Assign the LLM pipeline to agent2\n",
    "\n",
    "# Add agents to the chat\n",
    "chat.add_agent(agent1)\n",
    "chat.add_agent(agent2)\n",
    "\n",
    "# Add test messages to the chat\n",
    "json_messages = [{'sender_id': 'Bruno', 'message': 'It’s a test, don’t worry'}]\n",
    "chat.add_messages(json_messages)\n",
    "\n",
    "# Initialize and configure ChatAdmin\n",
    "chat_admin = ChatAdmin(\"admin\", \"Admin\", \"admin_role\", pipeline_admin, chat, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:miniautogen.chat.chatadmin:Chat Admin started.\n",
      "INFO:miniautogen.chat.chatadmin:Executing round 1\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[{'role': 'system', 'content': 'Python Senior Developer'}, {'role': 'user', 'content': 'It’s a test, don’t worry'}]\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:miniautogen.chat.chatadmin:Executing round 2\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " As a Python Senior Developer, I understand the importance of ensuring that your application performs at its best. To do this, I will take the following steps:\n",
      "\n",
      "1. Review and analyze the current codebase to identify any potential bottlenecks or inefficiencies.\n",
      "2. Identify areas where optimizations can be made, such as refactoring complex functions, reducing unnecessary calls or loops, or improving algorithmic efficiency.\n",
      "3. Implement performance improvements using profiling tools and benchmarking techniques to ensure that the changes have a positive impact on overall performance.\n",
      "4. Conduct thorough testing and integration with existing systems to ensure compatibility and maintainability of the optimized codebase.\n",
      "5. Monitor and track any changes in performance over time to validate the effectiveness of the implemented improvements, as well as to identify any new potential bottlenecks or opportunities for further optimization.\n",
      "6. Document and share best practices for optimizing Python code, including patterns and techniques that have been proven effective in similar projects.\n",
      "7. Collaborate with other developers and stakeholders to prioritize optimizations based on project goals and resource constraints.\n",
      "8. Continuously research and stay updated on the latest performance optimization techniques and tools in the Python ecosystem.\n",
      "\n",
      "By following these steps, I will ensure that your application is optimized for maximum performance while maintaining a high level of code quality and maintainability.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:miniautogen.chat.chatadmin:Executing round 3\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Create a calc function on python.\n",
      "[{'role': 'system', 'content': 'Python Senior Developer'}, {'role': 'user', 'content': 'It’s a test, don’t worry'}, {'role': 'assistant', 'content': ' As a Python Senior Developer, I understand the importance of ensuring that your application performs at its best. To do this, I will take the following steps:\\n\\n1. Review and analyze the current codebase to identify any potential bottlenecks or inefficiencies.\\n2. Identify areas where optimizations can be made, such as refactoring complex functions, reducing unnecessary calls or loops, or improving algorithmic efficiency.\\n3. Implement performance improvements using profiling tools and benchmarking techniques to ensure that the changes have a positive impact on overall performance.\\n4. Conduct thorough testing and integration with existing systems to ensure compatibility and maintainability of the optimized codebase.\\n5. Monitor and track any changes in performance over time to validate the effectiveness of the implemented improvements, as well as to identify any new potential bottlenecks or opportunities for further optimization.\\n6. Document and share best practices for optimizing Python code, including patterns and techniques that have been proven effective in similar projects.\\n7. Collaborate with other developers and stakeholders to prioritize optimizations based on project goals and resource constraints.\\n8. Continuously research and stay updated on the latest performance optimization techniques and tools in the Python ecosystem.\\n\\nBy following these steps, I will ensure that your application is optimized for maximum performance while maintaining a high level of code quality and maintainability.'}, {'role': 'user', 'content': 'Create a calc function on python.'}]\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:miniautogen.chat.chatadmin:Executing round 4\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " def calc(num1, num2, operation):\n",
      "    if operation == '+':\n",
      "        return num1 + num2\n",
      "    elif operation == '-':\n",
      "        return num1 - num2\n",
      "    elif operation == '/':\n",
      "        return num1 / num2\n",
      "    elif operation == '*':\n",
      "        return num1 * num2\n",
      "    else:\n",
      "        raise ValueError(f\"Invalid operation {operation}\")\n",
      "\n",
      "Example usage:\n",
      "num1 = 5\n",
      "num2 = 3\n",
      "operation = '+'\n",
      "result = calc(num1, num2, operation)\n",
      "print(result)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:miniautogen.chat.chatadmin:Chat Admin stopped.\n",
      "INFO:miniautogen.chat.chatadmin:Chat Admin stopped.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Terminate\n",
      "Terminating chat...\n"
     ]
    }
   ],
   "source": [
    "#running the chat\n",
    "chat_admin.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'id': 1,\n",
       "  'sender_id': 'Bruno',\n",
       "  'message': 'It’s a test, don’t worry',\n",
       "  'timestamp': Timestamp('2024-01-03 15:06:02.518774'),\n",
       "  'additional_info': None},\n",
       " {'id': 2,\n",
       "  'sender_id': 'dev',\n",
       "  'message': ' As a Python Senior Developer, I understand the importance of ensuring that your application performs at its best. To do this, I will take the following steps:\\n\\n1. Review and analyze the current codebase to identify any potential bottlenecks or inefficiencies.\\n2. Identify areas where optimizations can be made, such as refactoring complex functions, reducing unnecessary calls or loops, or improving algorithmic efficiency.\\n3. Implement performance improvements using profiling tools and benchmarking techniques to ensure that the changes have a positive impact on overall performance.\\n4. Conduct thorough testing and integration with existing systems to ensure compatibility and maintainability of the optimized codebase.\\n5. Monitor and track any changes in performance over time to validate the effectiveness of the implemented improvements, as well as to identify any new potential bottlenecks or opportunities for further optimization.\\n6. Document and share best practices for optimizing Python code, including patterns and techniques that have been proven effective in similar projects.\\n7. Collaborate with other developers and stakeholders to prioritize optimizations based on project goals and resource constraints.\\n8. Continuously research and stay updated on the latest performance optimization techniques and tools in the Python ecosystem.\\n\\nBy following these steps, I will ensure that your application is optimized for maximum performance while maintaining a high level of code quality and maintainability.',\n",
       "  'timestamp': Timestamp('2024-01-03 15:06:59.077218'),\n",
       "  'additional_info': None},\n",
       " {'id': 3,\n",
       "  'sender_id': 'Bruno',\n",
       "  'message': 'Create a calc function on python.',\n",
       "  'timestamp': Timestamp('2024-01-03 15:07:28.248400'),\n",
       "  'additional_info': None},\n",
       " {'id': 4,\n",
       "  'sender_id': 'dev',\n",
       "  'message': ' def calc(num1, num2, operation):\\n    if operation == \\'+\\':\\n        return num1 + num2\\n    elif operation == \\'-\\':\\n        return num1 - num2\\n    elif operation == \\'/\\':\\n        return num1 / num2\\n    elif operation == \\'*\\':\\n        return num1 * num2\\n    else:\\n        raise ValueError(f\"Invalid operation {operation}\")\\n\\nExample usage:\\nnum1 = 5\\nnum2 = 3\\noperation = \\'+\\'\\nresult = calc(num1, num2, operation)\\nprint(result)',\n",
       "  'timestamp': Timestamp('2024-01-03 15:07:58.886564'),\n",
       "  'additional_info': None},\n",
       " {'id': 5,\n",
       "  'sender_id': 'Bruno',\n",
       "  'message': 'Terminate',\n",
       "  'timestamp': Timestamp('2024-01-03 15:08:08.420534'),\n",
       "  'additional_info': None}]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chat.get_messages().to_dict(orient='records')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.11.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
