{
  "nbformat": 4,
  "nbformat_minor": 5,
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.13.0"
    },
    "blog_metadata": {
      "topic": "Building Copilot-style experiences in Python with the Microsoft Teams SDK",
      "slug": "building-copilot-style-experiences-in-python-with-the-micros",
      "generated_by": "LinkedIn Post Generator + Azure OpenAI",
      "generated_at": "2026-05-04T00:37:40.745Z"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Building Copilot-style experiences in Python with the Microsoft Teams SDK\n",
        "\n",
        "Python developers can now build Teams-native, Copilot-style experiences using the Microsoft 365 Agents SDK while keeping their existing Python AI and backend services. This notebook turns the blog concepts into hands-on, runnable examples that simulate Teams message handling, grounded responses, adaptive cards, and permission-aware workflow actions.\n",
        "\n",
        "The focus here is validation of the architecture and patterns, not a production-ready Teams deployment. In production, you would add proper authentication, adapters, Teams app registration, and Microsoft 365 integration plumbing."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "%pip install fastapi uvicorn pydantic python-dotenv"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "from __future__ import annotations\n",
        "\n",
        "import json\n",
        "import os\n",
        "from typing import Any\n",
        "\n",
        "from fastapi import FastAPI, Request"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Why Teams-native AI matters\n",
        "\n",
        "A Teams-native assistant lives inside collaboration flows instead of sitting beside them as a generic chatbot. That means your Python app can combine conversation context, enterprise identity, workflow actions, and AI orchestration in one experience.\n",
        "\n",
        "A simple mental model is:\n",
        "\n",
        "- Teams sends an activity\n",
        "- Your Python app extracts context and runs business logic\n",
        "- Your app responds with Teams-friendly interaction patterns such as messages, cards, and actions"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 1: Minimal Teams activity endpoint\n",
        "\n",
        "This example shows the conceptual shape of a Teams message endpoint using FastAPI. It extracts the incoming text, user ID, and conversation ID, then routes the prompt into a simple orchestration function and returns a message-shaped response.\n",
        "\n",
        "This is intentionally a sketch for local validation. A real Teams app would use proper adapters, authentication, and activity handling."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "from fastapi import FastAPI, Request\n",
        "\n",
        "app = FastAPI()\n",
        "\n",
        "def orchestrate(prompt: str, user_id: str, conversation_id: str) -> str:\n",
        "    return f\"Hi {user_id}, I can help with: {prompt[:80]}\"\n",
        "\n",
        "@app.post(\"/api/messages\")\n",
        "async def messages(req: Request):\n",
        "    activity = await req.json()\n",
        "    text = activity.get(\"text\", \"\")\n",
        "    user_id = activity.get(\"from\", {}).get(\"id\", \"unknown\")\n",
        "    conversation_id = activity.get(\"conversation\", {}).get(\"id\", \"unknown\")\n",
        "    reply = orchestrate(text, user_id, conversation_id)\n",
        "    return {\n",
        "        \"type\": \"message\",\n",
        "        \"conversation\": {\"id\": conversation_id},\n",
        "        \"text\": reply,\n",
        "    }\n",
        "\n",
        "sample_activity = {\n",
        "    \"text\": \"Summarize the latest incident and next actions.\",\n",
        "    \"from\": {\"id\": \"user-123\"},\n",
        "    \"conversation\": {\"id\": \"conv-456\"}\n",
        "}\n",
        "\n",
        "sample_reply = {\n",
        "    \"type\": \"message\",\n",
        "    \"conversation\": {\"id\": sample_activity[\"conversation\"][\"id\"]},\n",
        "    \"text\": orchestrate(\n",
        "        sample_activity[\"text\"],\n",
        "        sample_activity[\"from\"][\"id\"],\n",
        "        sample_activity[\"conversation\"][\"id\"],\n",
        "    ),\n",
        "}\n",
        "\n",
        "print(json.dumps(sample_reply, indent=2))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 2: Copilot-style action flow with grounded data and an adaptive card\n",
        "\n",
        "This example simulates a common enterprise pattern:\n",
        "\n",
        "1. Retrieve grounded enterprise data\n",
        "2. Generate a concise answer from that data\n",
        "3. Return a Teams-compatible adaptive card payload\n",
        "\n",
        "This is useful for knowledge lookup, meeting follow-up, incident triage, and other contextual assistant scenarios."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "def search_enterprise_data(query: str, user_id: str) -> list[dict]:\n",
        "    return [{\"title\": \"QBR Notes\", \"snippet\": \"Revenue up 12% in EMEA.\"}]\n",
        "\n",
        "def generate_answer(query: str, docs: list[dict]) -> str:\n",
        "    context = \" \".join(d[\"snippet\"] for d in docs)\n",
        "    return f\"{query}\\n\\nGrounded summary: {context}\"\n",
        "\n",
        "def build_adaptive_card(answer: str) -> dict:\n",
        "    return {\n",
        "        \"contentType\": \"application/vnd.microsoft.card.adaptive\",\n",
        "        \"content\": {\n",
        "            \"type\": \"AdaptiveCard\",\n",
        "            \"version\": \"1.4\",\n",
        "            \"body\": [\n",
        "                {\"type\": \"TextBlock\", \"wrap\": True, \"text\": answer}\n",
        "            ]\n",
        "        }\n",
        "    }\n",
        "\n",
        "def handle_copilot_action(query: str, user_id: str) -> dict:\n",
        "    docs = search_enterprise_data(query, user_id)\n",
        "    answer = generate_answer(query, docs)\n",
        "    return {\n",
        "        \"composeExtension\": {\n",
        "            \"type\": \"result\",\n",
        "            \"attachmentLayout\": \"list\",\n",
        "            \"attachments\": [build_adaptive_card(answer)]\n",
        "        }\n",
        "    }\n",
        "\n",
        "result = handle_copilot_action(\"What changed in the latest regional business review?\", \"user-123\")\n",
        "print(json.dumps(result, indent=2))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 3: Permission-aware tool invocation\n",
        "\n",
        "Teams-native assistants often do more than answer questions. They also trigger workflow actions such as approvals, updates, and task execution.\n",
        "\n",
        "This example adds a simple role check before approving a discount. It demonstrates how enterprise identity and authorization can shape what the assistant is allowed to do."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "USER_ROLES = {\n",
        "    \"alice@contoso.com\": {\"Approver\", \"Sales\"},\n",
        "    \"bob@contoso.com\": {\"Sales\"}\n",
        "}\n",
        "\n",
        "def has_role(user_upn: str, required_role: str) -> bool:\n",
        "    return required_role in USER_ROLES.get(user_upn, set())\n",
        "\n",
        "def approve_discount(user_upn: str, deal_id: str, percent: int) -> dict:\n",
        "    if not has_role(user_upn, \"Approver\"):\n",
        "        return {\n",
        "            \"status\": \"denied\",\n",
        "            \"message\": \"You do not have permission to approve discounts.\"\n",
        "        }\n",
        "    return {\"status\": \"approved\", \"dealId\": deal_id, \"discount\": percent}\n",
        "\n",
        "approved = approve_discount(\"alice@contoso.com\", \"DEAL-1042\", 15)\n",
        "denied = approve_discount(\"bob@contoso.com\", \"DEAL-1042\", 15)\n",
        "\n",
        "print(\"Approved path:\")\n",
        "print(json.dumps(approved, indent=2))\n",
        "print(\"\\nDenied path:\")\n",
        "print(json.dumps(denied, indent=2))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Required environment variables for a Teams-connected backend\n",
        "\n",
        "If you connect this prototype to a real Teams app or bot registration, you will typically need environment variables such as:\n",
        "\n",
        "- `MICROSOFT_APP_ID`\n",
        "- `MICROSOFT_APP_PASSWORD`\n",
        "- `TENANT_ID`\n",
        "- `BOT_ENDPOINT`\n",
        "\n",
        "Do not hardcode secrets in notebooks or source files. Use environment variables, a `.env` file for local development, or a secret manager in production."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Example 4: Python environment variable setup and validation\n",
        "\n",
        "The original post included PowerShell for setting Teams backend variables. Since this notebook is Python-first, the example below shows how to set placeholder values in Python and validate that the expected configuration exists."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {},
      "source": [
        "import os\n",
        "\n",
        "os.environ.setdefault(\"MICROSOFT_APP_ID\", \"00000000-0000-0000-0000-000000000000\")\n",
        "os.environ.setdefault(\"MICROSOFT_APP_PASSWORD\", \"your-client-secret\")\n",
        "os.environ.setdefault(\"TENANT_ID\", \"11111111-1111-1111-1111-111111111111\")\n",
        "os.environ.setdefault(\"BOT_ENDPOINT\", \"https://your-api.contoso.com/api/messages\")\n",
        "\n",
        "config = {\n",
        "    \"MICROSOFT_APP_ID\": os.getenv(\"MICROSOFT_APP_ID\"),\n",
        "    \"TENANT_ID\": os.getenv(\"TENANT_ID\"),\n",
        "    \"BOT_ENDPOINT\": os.getenv(\"BOT_ENDPOINT\"),\n",
        "}\n",
        "\n",
        "print(\"Configured Teams backend settings:\")\n",
        "print(json.dumps(config, indent=2))"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Architecture flow\n",
        "\n",
        "The blog's flow can be represented as this sequence:\n",
        "\n",
        "- Teams user message\n",
        "- Python `/api/messages` endpoint\n",
        "- Extract user and conversation context\n",
        "- Permission check\n",
        "- Ground enterprise data\n",
        "- AI orchestration\n",
        "- Adaptive card or structured result\n",
        "- Return response to Teams\n",
        "\n",
        "The examples above validate each of these stages in isolation so you can evolve them into a full Teams-native assistant."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Next Steps\n",
        "\n",
        "This notebook validated the core building blocks behind a Copilot-style Teams experience in Python: message intake, context extraction, grounded response generation, adaptive card output, and permission-aware actions.\n",
        "\n",
        "Next, you can:\n",
        "\n",
        "- Replace mock retrieval with real enterprise search or knowledge sources\n",
        "- Connect orchestration to your preferred LLM or agent framework\n",
        "- Add authentication and proper Teams activity handling\n",
        "- Register a Teams app and point it to your deployed backend\n",
        "- Expand from message replies into approvals, task execution, and meeting follow-up workflows"
      ]
    }
  ]
}