diff --git a/.github/agents/my-agent.agent.md b/.github/agents/my-agent.agent.md new file mode 100644 index 0000000000..39a8426458 --- /dev/null +++ b/.github/agents/my-agent.agent.md @@ -0,0 +1,13 @@ +--- +# Fill in the fields below to create a basic custom agent for your repository. +# The Copilot CLI can be used for local testing: https://gh.io/customagents/cli +# To make this agent available, merge this file into the default repository branch. +# For format details, see: https://gh.io/customagents/config + +name: +description: +--- + +# My Agent + +Describe what your agent does here... diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml new file mode 100644 index 0000000000..be1ad2843b --- /dev/null +++ b/.github/workflows/deploy-pages.yml @@ -0,0 +1,38 @@ +name: Deploy to GitHub Pages + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: '.' + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/generator-generic-ossf-slsa3-publish.yml b/.github/workflows/generator-generic-ossf-slsa3-publish.yml new file mode 100644 index 0000000000..35c829b139 --- /dev/null +++ b/.github/workflows/generator-generic-ossf-slsa3-publish.yml @@ -0,0 +1,66 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# This workflow lets you generate SLSA provenance file for your project. +# The generation satisfies level 3 for the provenance requirements - see https://slsa.dev/spec/v0.1/requirements +# The project is an initiative of the OpenSSF (openssf.org) and is developed at +# https://github.com/slsa-framework/slsa-github-generator. +# The provenance file can be verified using https://github.com/slsa-framework/slsa-verifier. +# For more information about SLSA and how it improves the supply-chain, visit slsa.dev. + +name: SLSA generic generator +on: + workflow_dispatch: + release: + types: [created] + +jobs: + build: + runs-on: ubuntu-latest + outputs: + digests: ${{ steps.hash.outputs.digests }} + + steps: + - uses: actions/checkout@v4 + + # ======================================================== + # + # Step 1: Build your artifacts. + # + # ======================================================== + - name: Build artifacts + run: | + # These are some amazing artifacts. + echo "artifact1" > artifact1 + echo "artifact2" > artifact2 + + # ======================================================== + # + # Step 2: Add a step to generate the provenance subjects + # as shown below. Update the sha256 sum arguments + # to include all binaries that you generate + # provenance for. + # + # ======================================================== + - name: Generate subject for provenance + id: hash + run: | + set -euo pipefail + + # List the artifacts the provenance will refer to. + files=$(ls artifact*) + # Generate the subjects (base64 encoded). + echo "hashes=$(sha256sum $files | base64 -w0)" >> "${GITHUB_OUTPUT}" + + provenance: + needs: [build] + permissions: + actions: read # To read the workflow path. + id-token: write # To sign the provenance. + contents: write # To add assets to a release. + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.4.0 + with: + base64-subjects: "${{ needs.build.outputs.digests }}" + upload-assets: true # Optional: Upload to a new release diff --git a/.gitignore b/.gitignore index dde8853295..781d211954 100644 --- a/.gitignore +++ b/.gitignore @@ -159,9 +159,38 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +# Node.js node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +package-lock.json +yarn.lock +pnpm-lock.yaml +.npm +.yarn/ +dist/ +.next/ +out/ +.nuxt/ +.cache/ +.parcel-cache/ +# OS artifacts – macOS .DS_Store +.AppleDouble +.LSOverride +._* + +# OS artifacts – Windows +Thumbs.db +ehthumbs.db +Desktop.ini +$RECYCLE.BIN/ + +# OS artifacts – Linux +*~ # downloaded stuff for 08-building-search-applications 08-building-search-applications/scripts/transcripts_the_ai_show/ diff --git a/08-building-search-applications/scripts/transcript_download.py b/08-building-search-applications/scripts/transcript_download.py index 2844c8a271..6ec37b2644 100644 --- a/08-building-search-applications/scripts/transcript_download.py +++ b/08-building-search-applications/scripts/transcript_download.py @@ -79,7 +79,8 @@ def gen_metadata(playlist_item): metadata["description"] = playlist_item["snippet"]["description"] # save the metadata as a .json file - json.dump(metadata, open(filename, "w", encoding="utf-8")) + with open(filename, "w", encoding="utf-8") as f: + json.dump(metadata, f) def get_transcript(playlist_item, counter_id): diff --git a/08-building-search-applications/scripts/transcript_enrich_embeddings.py b/08-building-search-applications/scripts/transcript_enrich_embeddings.py index 1a4ab1e424..2da210f6d4 100644 --- a/08-building-search-applications/scripts/transcript_enrich_embeddings.py +++ b/08-building-search-applications/scripts/transcript_enrich_embeddings.py @@ -119,7 +119,6 @@ def process_queue(progress, task): output_segments.append(segment.copy()) progress.update(task, advance=1) q.task_done() - time.sleep(0.2) logger.debug("Total segments to be processed: %s", len(segments)) diff --git a/08-building-search-applications/scripts/transcript_enrich_speaker.py b/08-building-search-applications/scripts/transcript_enrich_speaker.py index d7b4d83f71..3e8b47fac6 100644 --- a/08-building-search-applications/scripts/transcript_enrich_speaker.py +++ b/08-building-search-applications/scripts/transcript_enrich_speaker.py @@ -154,7 +154,7 @@ def clean_text(text): def get_first_segment(file_name): """Gets the first segment from the filename""" - text = "" + text_parts = [] current_seconds = None segment_begin_seconds = None segment_finish_seconds = None @@ -176,9 +176,9 @@ def get_first_segment(file_name): if current_seconds < segment_finish_seconds: # add the text to the transcript - text += clean_text(segment.get("text")) + " " + text_parts.append(clean_text(segment.get("text"))) - return text + return " ".join(text_parts) + " " if text_parts else "" def process_queue(progress, task): @@ -206,10 +206,10 @@ def process_queue(progress, task): print(f"From function call: {filename}\t{speakers}") metadata["speaker"] = speakers - json.dump(metadata, open(filename, "w", encoding="utf-8")) + with open(filename, "w", encoding="utf-8") as f: + json.dump(metadata, f) q.task_done() - time.sleep(0.2) logger.debug("Transcription folder %s", TRANSCRIPT_FOLDER) diff --git a/09-building-image-applications/python/oai-app-variation.py b/09-building-image-applications/python/oai-app-variation.py index ac172ab727..a05cb05764 100644 --- a/09-building-image-applications/python/oai-app-variation.py +++ b/09-building-image-applications/python/oai-app-variation.py @@ -17,11 +17,12 @@ # ---creating variation below--- try: print("LOG creating variation") - response = openai.images.create_variation( - image=open("generated-image.png", "rb"), - n=1, - size="1024x1024" - ) + with open("generated-image.png", "rb") as image_file: + response = openai.images.create_variation( + image=image_file, + n=1, + size="1024x1024" + ) image_path = os.path.join(image_dir, 'generated_variation.png') diff --git a/09-building-image-applications/python/oai-app.py b/09-building-image-applications/python/oai-app.py index 59aa7c79ec..9bea74cf9b 100644 --- a/09-building-image-applications/python/oai-app.py +++ b/09-building-image-applications/python/oai-app.py @@ -47,8 +47,9 @@ # ---creating variation below--- -response = client.images.create_variation( - image=open(image_path, "rb"), - n=1, - size="1024x1024" -) \ No newline at end of file +with open(image_path, "rb") as image_file: + response = client.images.create_variation( + image=image_file, + n=1, + size="1024x1024" + ) \ No newline at end of file diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000000..0b8689ca5c --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,50 @@ +# GitHub Pages Deployment Setup + +This repository is configured to deploy to GitHub Pages using GitHub Actions. + +## Configuration Required + +To enable GitHub Pages deployment for this repository: + +1. Go to your repository's **Settings** > **Pages** +2. Under **Build and deployment**, select: + - **Source**: GitHub Actions +3. The deployment workflow (`.github/workflows/deploy-pages.yml`) will automatically run on: + - Pushes to the `main` branch + - Manual workflow dispatch + +## Accessing the Site + +Once GitHub Pages is enabled and the workflow runs successfully, your site will be available at: +- `https://.github.io//` + +For this repository: +- `https://vepretski.github.io/generative-ai-for-beginners/` + +## Local Development + +To preview the site locally: + +1. Start a local web server in the repository root: + ```bash + # Using Python + python -m http.server 8000 + + # Or using Node.js + npx http-server + ``` + +2. Open your browser to `http://localhost:8000` + +The site uses [Docsify](https://docsify.js.org/) to render documentation from Markdown files dynamically. + +## Workflow Details + +The deployment workflow: +- Triggers on pushes to `main` or manual dispatch +- Checks out the repository +- Configures GitHub Pages +- Uploads the entire repository as a Pages artifact +- Deploys the artifact to GitHub Pages + +No build step is required since Docsify renders the documentation client-side. diff --git a/README.md b/README.md index 6f409f746d..5d34d4b34f 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,65 @@ Special thanks to [**John Aziz**](https://www.linkedin.com/in/john0isaac/) for c [**Bernhard Merkle**](https://www.linkedin.com/in/bernhard-merkle-738b73/) for making key contributions to each lesson to improve the learner and code experience. +--- + +This fork is maintained by [Igor Vepretski](https://7ya.io) - Empowering the next generation of AI builders. Learn more at [7YA.IO](https://7ya.io) + +## 📁 Project Structure + +This repository is organised so that beginners can find everything they need quickly. + +``` +generative-ai-for-beginners/ +├── src/ +│ └── main.py # Starter script – run this to verify your setup +├── docs/ +│ └── setup.md # Detailed environment setup guide +├── tests/ +│ └── test_main.py # Unit tests (run with: pytest tests/) +├── 00-course-setup/ # Lesson 0: setting up your development environment +├── 01-introduction-to-genai/ … 21-meta/ # Course lessons +├── .gitignore # Ignores Python, Node.js, and OS artefacts +└── README.md # You are here +``` + +### ✨ Features + +- **21 structured lessons** covering all core Generative AI topics, from fundamentals to advanced techniques. +- **Dual-language code samples** – every Build lesson includes both **Python** and **TypeScript** examples. +- **Multiple LLM provider support** – Azure OpenAI, GitHub Marketplace Models, and the OpenAI API all work out of the box. +- **Hands-on projects** – chat apps, image generators, RAG pipelines, AI agents, and more. +- **Beginner-friendly layout** – each lesson is self-contained with a README, code samples, and extra-learning links. + +### 🛠️ Quick Setup + +1. **Clone** the repo and create a virtual environment: + + ```bash + git clone https://github.com/microsoft/generative-ai-for-beginners.git + cd generative-ai-for-beginners + python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1 + pip install -r requirements.txt + ``` + +2. **Configure** your LLM provider by copying `.env.copy` to `.env` inside the lesson folder and filling in your API key. + +3. **Verify** the setup by running the starter script: + + ```bash + python src/main.py + ``` + +4. **Run the tests** to make sure everything is working: + + ```bash + pytest tests/ + ``` + +> 📖 For full details see [docs/setup.md](./docs/setup.md). + +--- + ## 🎒 Other Courses Our team produces other courses! Check out: diff --git a/docs/7ya-io-operating-plan.md b/docs/7ya-io-operating-plan.md new file mode 100644 index 0000000000..2efefbb3db --- /dev/null +++ b/docs/7ya-io-operating-plan.md @@ -0,0 +1,214 @@ +# 7ya.io Strategic Operating Blueprint (Freeleyonayre Studio) + +This document consolidates the operating system for **7ya.io / Freeleyonayre Studio** into one execution-ready plan across product, growth, operations, and fintech automation. + +## 1) Executive Focus + +**Mission:** convert creator attention, expertise, and community trust into predictable recurring revenue and automated long-term wealth accumulation. + +**Business model:** one unified platform with four integrated engines: + +1. Income Engine (SaaS + digital products) +2. Creator Engine (community + education) +3. Services Engine (productized consulting) +4. Finance Engine (automated revenue routing + investing) + +## 2) Market Thesis (2026) + +The operating assumptions for 7ya.io are: + +- Creator businesses are shifting from sponsorship dependency to owned recurring revenue. +- Tool sprawl creates margin leakage and operational fatigue. +- AI-native operations are now a baseline requirement for speed and gross margin. +- The winning setup is a consolidated stack with direct ownership of audience and checkout paths. + +## 3) Product Architecture: The Four Engines + +### 3.1 Income Engine + +- Hosts templates, micro-SaaS utilities, and digital products. +- Uses a fast content delivery pipeline (collect, normalize, cache, deliver). +- Prioritizes low-friction checkout and immediate value delivery. + +### 3.2 Creator Engine + +- Course hosting, gated newsletter, and membership access control. +- Community-led growth journey: viewer -> participant -> member -> advocate. +- Weekly cadence for office hours and iterative curriculum updates. + +### 3.3 Services Engine + +- Replaces custom consulting with fixed-scope, fixed-price packaged offers. +- Includes automated discovery forms, booking, payment, and kickoff workflows. +- Maintains strict delivery boundaries to preserve margin. + +### 3.4 Finance Engine + +- Programmatic revenue allocation (example default): + - 50% business operations/reinvestment + - 30% personal operating reserve + - 20% investment allocation +- Stripe webhooks trigger allocation logic and brokerage actions. +- Investment execution is policy-driven and logged. + +## 4) Tech Stack Standard + +- **Frontend:** Next.js (React), SSR-first for conversion performance. +- **Backend:** FastAPI or Node/Express for async webhook/API orchestration. +- **Auth:** Supabase Auth or Firebase Auth. +- **Data:** PostgreSQL for users, subscriptions, orders, and audit logs. +- **Infra:** Vercel/Render for deployment, managed observability, automated rollbacks. +- **Payments:** Stripe + Stripe Connect. +- **Bank linking:** Plaid tokenization flows. +- **Investments:** Alpaca API (paper first, production after controls validation). + +## 5) Fintech Implementation Pattern (Stripe -> Allocation -> Alpaca) + +### 5.1 Webhook handling standards + +- Verify Stripe signatures on every webhook. +- Idempotency required for transfer and trade execution steps. +- Structured logs with request IDs and event IDs. +- Explicit error taxonomy: payload, signature, transfer, trade, internal. + +### 5.2 Example production-ready FastAPI webhook + +```python +import os +import logging +from fastapi import FastAPI, Request, HTTPException +from fastapi.responses import JSONResponse +import stripe +import alpaca_trade_api as tradeapi +from dotenv import load_dotenv + +load_dotenv() + +STRIPE_SECRET_KEY = os.getenv("STRIPE_SECRET_KEY") +STRIPE_WEBHOOK_SECRET = os.getenv("STRIPE_WEBHOOK_SECRET") +INVESTMENT_PERCENTAGE = float(os.getenv("INVESTMENT_PERCENTAGE", 0.20)) + +ALPACA_API_KEY = os.getenv("ALPACA_API_KEY") +ALPACA_SECRET_KEY = os.getenv("ALPACA_SECRET_KEY") +ALPACA_BASE_URL = os.getenv("ALPACA_BASE_URL", "https://paper-api.alpaca.markets") + +stripe.api_key = STRIPE_SECRET_KEY +alpaca = tradeapi.REST(ALPACA_API_KEY, ALPACA_SECRET_KEY, base_url=ALPACA_BASE_URL, api_version="v2") + +app = FastAPI() +logger = logging.getLogger("uvicorn.error") + + +def place_invest_order(symbol: str, amount_usd: float): + try: + asset_info = alpaca.get_asset(symbol) + if not asset_info.fractionable: + logger.warning(f"Asset {symbol} is not fractionable") + + order = alpaca.submit_order( + symbol=symbol, + notional=amount_usd, + side="buy", + type="market", + time_in_force="day", + ) + logger.info(f"Invest order submitted: {order}") + return order + except Exception as exc: + logger.error(f"Trade execution error: {exc}") + raise + + +@app.post("/stripe-webhook") +async def stripe_webhook_handler(request: Request): + payload = await request.body() + sig_header = request.headers.get("stripe-signature") + + try: + event = stripe.Webhook.construct_event(payload, sig_header, STRIPE_WEBHOOK_SECRET) + except ValueError: + raise HTTPException(status_code=400, detail="Invalid payload") + except stripe.error.SignatureVerificationError: + raise HTTPException(status_code=400, detail="Invalid signature") + + if event["type"] == "checkout.session.completed": + session = event["data"]["object"] + total_amount = session.get("amount_total", 0) + invest_amount = (total_amount * INVESTMENT_PERCENTAGE) / 100.0 + + logger.info(f"Checkout complete. total={total_amount} invest={invest_amount}") + + try: + stripe.Transfer.create( + amount=int(invest_amount * 100), + currency="usd", + destination=session.get("connected_account_id"), + transfer_group=session["id"], + ) + except Exception as exc: + logger.error(f"Stripe transfer failed: {exc}") + + order = place_invest_order(symbol="SPY", amount_usd=invest_amount) + return {"status": "invest_order_submitted", "order_id": getattr(order, "id", None)} + + return JSONResponse({"status": "ignored_event"}) +``` + +## 6) Monetization Ladder + +1. **$49/mo Ops Membership** (recurring base) +2. **$499 Launch Kit** (one-time, conversion accelerator) +3. **$2,500/mo Premium Retainer** (high-ticket, capped capacity) +4. Affiliate ecosystem revenue +5. Long-term investment portfolio growth via automated allocation + +## 7) KPI Dashboard (Top 7 Above the Fold) + +1. Monthly recurring revenue (MRR) +2. Active paying members +3. Trial-to-paid conversion +4. Churn rate +5. LTV:CAC ratio +6. Operating runway (months) +7. Investment account balance (allocated capital + performance) + +Dashboard rule: if any core KPI degrades for two consecutive weeks, trigger a focused recovery sprint with a single owner. + +## 8) 90-Day Execution Roadmap + +### Month 1 (MVP launch) + +- Ship landing page + waitlist + founding member offer. +- Publish 3-lesson mini-course lead magnet. +- Enable Stripe checkout and pre-launch email sequence. +- Close first 20 paying members and collect structured feedback. + +### Month 2 (systems hardening) + +- Start weekly live office hours. +- Automate top three repetitive workflows. +- Finish full course content and onboarding improvements. + +### Month 3 (scaling) + +- Public launch to full audience. +- Run podcast partnership outreach. +- Open limited consulting retainers. +- Start controlled paid acquisition tests with strict CAC limits. + +## 9) Operational Safeguards + +- Maintain a risk register across platform, compliance, and market exposure. +- Keep 3-6 months of operating expense runway in cash equivalents. +- Never store raw bank credentials; use tokenized providers. +- Run all financial automations in paper/sandbox before production rollout. +- Log and reconcile all transfer/trade events for auditability. + +## 10) Weekly Operating Cadence + +- **Monday:** strategy, KPI targets, priorities. +- **Wednesday:** execution review and re-prioritization. +- **Friday:** scoreboard, lessons learned, and automation queue triage. +- **Daily:** short blocker-driven standup. + +This blueprint should be treated as a living operating system: update monthly based on KPI movement, customer feedback, and regulatory constraints. diff --git a/docs/7ya-workflow-module.md b/docs/7ya-workflow-module.md new file mode 100644 index 0000000000..a91a592eef --- /dev/null +++ b/docs/7ya-workflow-module.md @@ -0,0 +1,83 @@ +# 7ya.io Workflow Product Module + +מודול זה ממפה SOP-ים חוזרים ומריץ אותם כסוכני Workflow שניתנים למכירה כרישוי מוצר. + +## SOP Mapping (Recurring Processes) + +### 1) Lead Qualification +- **Inputs:** `lead_id`, `budget_usd`, `decision_authority`, `need_score`, `timeline_days` +- **Rules:** ניקוד דטרמיניסטי לפי תקציב, סמכות קבלת החלטה, רמת צורך, ודחיפות זמן. +- **Decisions:** `sales-qualified` / `marketing-qualified` / `unqualified` +- **Outputs:** `qualification`, `next_action`, `score`, `reasoning` + +### 2) Content Repurposing +- **Inputs:** `asset_id`, `source_type`, `source_word_count`, `target_channel` +- **Rules:** פורמטים נגזרים לפי סוג מקור, הרחבה לתוכן ארוך, וטון לפי ערוץ. +- **Decisions:** בחירת חבילת פורמטים, טון, וסטטוס. +- **Outputs:** `output_formats`, `recommended_tone`, `workflow_status` + +### 3) Client Onboarding +- **Inputs:** `signed_contract`, `package_type`, `primary_goal`, `stack` +- **Rules:** חייב חוזה חתום; checklist לפי חבילה. +- **Decisions:** אישור kickoff / השלמת נתונים / ניתוב discovery טכני. +- **Outputs:** `onboarding_status`, `kickoff_tasks`, `owner_assignment` + +### 4) Campaign Reporting +- **Inputs:** `channel_metrics`, `period_start`, `period_end`, `targets` +- **Rules:** חישוב variance מול יעדים + חריגות מעל 15%. +- **Decisions:** Healthy / Needs optimization / Escalate. +- **Outputs:** `summary`, `flags`, `recommended_actions` + +## Implemented Workflow Agents + +1. **Lead Qualification Agent** +2. **Content Repurposing Agent** + +מימושים נמצאים ב-`src/workflow_product_module.py` כולל לוגיקה, לוגים ו-KPI. + +## Logs + KPI Metrics + +כל ריצת Agent נשמרת כ-`TaskLog` עם: +- `decision_accuracy` +- `duration_ms` +- `cost_usd` + +המודול מחשב אגרגציות: +- `avg_decision_accuracy` +- `avg_duration_ms` +- `avg_cost_usd` +- `total_tasks` + +## Product Packaging (API + Dashboard) + +המודול נארז כשרת HTTP בסיסי: +- `GET /api/sops` +- `POST /api/run/lead-qualification` +- `POST /api/run/content-repurposing` +- `GET /api/metrics` +- `POST /api/igor/brief` (builds a strategic single-agent execution brief) +- `GET /dashboard` + +להרצה: + +```bash +python -m src.workflow_product_module +``` + +כך ניתן לספק רישוי כמוצר מודולרי ולא רק שירות ידני. + + +## Igor Assistant Brief Output + +`POST /api/igor/brief` converts live module KPIs into a compact operating brief for a single-source AI execution model. + +Request body fields: +- `risk_level`: `normal` | `elevated` | `crisis` +- `strategic_goal`: free-text strategy target (example: `increase_mrr`, `stabilize_churn`) + +Response includes: +- `identity_core` +- `kpi_snapshot` +- `alerts` +- `priority_queue` +- `execution_protocol` diff --git a/docs/setup.md b/docs/setup.md new file mode 100644 index 0000000000..2e7ce2b07a --- /dev/null +++ b/docs/setup.md @@ -0,0 +1,143 @@ +# Setup Guide + +Welcome to **Generative AI for Beginners**! This guide walks you through configuring your development environment so you can run all course code samples. + +--- + +## Prerequisites + +| Requirement | Minimum version | +|---|---| +| Python | 3.10 | +| Node.js (optional, for TypeScript samples) | 18 LTS | +| Git | 2.x | + +--- + +## 1. Clone the Repository + +```bash +git clone https://github.com/microsoft/generative-ai-for-beginners.git +cd generative-ai-for-beginners +``` + +## 2. Create and Activate a Virtual Environment + +```bash +# Create the virtual environment +python -m venv .venv + +# Activate on macOS / Linux +source .venv/bin/activate + +# Activate on Windows (PowerShell) +.venv\Scripts\Activate.ps1 +``` + +## 3. Install Python Dependencies + +Each lesson folder contains its own `requirements.txt`. Install globally for all lessons: + +```bash +pip install -r requirements.txt +``` + +Or navigate into a specific lesson directory and install only what that lesson needs: + +```bash +cd 06-text-generation-apps +pip install -r requirements.txt +``` + +## 4. Configure Your LLM Provider + +The course supports three providers. Choose one and follow the corresponding instructions. + +### Option A – Azure OpenAI Service + +1. Create an [Azure OpenAI resource](https://aka.ms/genai-beginners/azure-open-ai). +2. Deploy a model (e.g., `gpt-4o`). +3. Copy `.env.copy` to `.env` inside the lesson folder and fill in the values: + +```env +AZURE_OPENAI_ENDPOINT=https://.openai.azure.com/ +AZURE_OPENAI_API_KEY= +AZURE_OPENAI_DEPLOYMENT_NAME= +AZURE_OPENAI_API_VERSION=2024-02-01 +``` + +### Option B – GitHub Marketplace Models + +1. Generate a [GitHub personal access token](https://github.com/settings/tokens) with `models:read` scope. +2. Add the token to `.env`: + +```env +GITHUB_TOKEN= +``` + +### Option C – OpenAI API + +1. Create an account at . +2. Generate an API key. +3. Add it to `.env`: + +```env +OPENAI_API_KEY= +``` + +## 5. (Optional) Install Node.js Dependencies + +For TypeScript samples, install dependencies inside the lesson folder: + +```bash +cd 06-text-generation-apps +npm install +``` + +## 6. Run the Starter Script + +From the repository root, verify your setup by running: + +```bash +python src/main.py +``` + +## 7. Run the Tests + +```bash +pytest tests/ +``` + +--- + +## Project Structure + +``` +generative-ai-for-beginners/ +├── src/ +│ └── main.py # Starter entry-point script +├── docs/ +│ └── setup.md # This file – environment setup guide +├── tests/ +│ └── test_main.py # Placeholder unit tests +├── 00-course-setup/ # Lesson 0: development environment guide +├── 01-introduction-to-genai/ +│ └── ... # Lessons 1-21 follow the same pattern +├── .env.copy # Template for environment variables +├── .gitignore +└── README.md +``` + +--- + +## Troubleshooting + +| Symptom | Fix | +|---|---| +| `ModuleNotFoundError` when importing `openai` | Activate the virtual environment and run `pip install -r requirements.txt` | +| `AuthenticationError` from the API | Double-check the API key in your `.env` file | +| `pytest: command not found` | Install pytest: `pip install pytest` | + +--- + +For further help, join the [Azure AI Foundry Discord](https://aka.ms/genai-discord) or open an issue on GitHub. diff --git a/index.html b/index.html index d3799ee2c5..bb231c8a0c 100644 --- a/index.html +++ b/index.html @@ -1,32 +1,368 @@ - - + - - Generative AI for Beginners - - - - - - - - + + + 7YA.IO x STARTON | הצעה אסטרטגית לשיתוף פעולה + + + + + + + + + + + + + + + + + + + + + + + + + + + + - -
- - - + +
+ + +
+
+ 7YA.IO + | + STARTON.ORG.IL +
+

+ הצעה לשיתוף פעולה אסטרטגי:
מסלול המראה לנוער בסיכון +

+

+ חיבור בין Reach חברתי (310M) לתשתית יזמות מקצועית +

+ +
+ + +
+ + +
+
+
+

תקציר המהלך

+

+ אנו מציעים לחבר בין יכולת ההגעה (Reach) חסרת התקדים של 7YA.IO במדיה החברתית לבין התשתית המקצועית של סטארט-און. המטרה היא לייצר "מסלול המראה" מהיר לנוער בסיכון אל עבר עולם היזמות וההייטק, המשלב גיוס המונים ויראלי עם הכשרה מנטלית וטכנולוגית מעמיקה. זהו לא רק שיתוף פעולה, אלא יצירת Force Multiplier חברתי. +

+
+
+ + +
+
+

הערך המוסף של 7YA.IO

+

למה החיבור הזה הוא Game Changer

+
+
+ +
+
+
+
📢
+

Recruitment Force

+

+ גישה ישירה ובלתי אמצעית לקהל ה-Hard-to-reach. אנחנו יודעים לגייס אותם מהטיקטוק והרחוב ולהפוך אותם ללומדים פעילים. +

+
+ 310M+ + חשיפה במדיה +
+
+
+ + +
+
+
+
🛡️
+

The Shield

+

+ הכשרה מקדימה לבניית חוסן מנטלי ומשמעת עצמית, המכינה את הנוער לתוכניות של סטארט-און. +

+
+ +
+
+
+ + +
+
+
+
🚀
+

Media Impact

+

+ כל סיפור הצלחה בתוכנית הופך לתוכן ויראלי המקדם את המותג המשותף ומעלה מודעות ציבורית. +

+
+
+ פוטנציאל ויראלי + גבוה מאוד +
+
+
+
+
+
+
+
+
+ + +
+
+
+
+ Roadmap +

מודל הפיילוט (The Pilot)

+
+ +
+ +
+ שלב א' +

The Hook: קמפיין גיוס

+

קמפיין משותף במדיה של איגור המזמין נוער להגיש מועמדות ל"נבחרת ה-Force". שימוש בשפה ויזואלית חדה ומניעה לפעולה.

+
+ +
+ שלב ב' +

The Shield: מחנה אימונים

+

הכשרה אינטנסיבית של 4 שבועות ב-7YA.IO. מיקוד בחוסן מנטלי, משמעת וכלים דיגיטליים בסיסיים (Pre-Entrepreneurship).

+
+ +
+ שלב ג' +

The Launch: המראה לסטארט-און

+

מעבר הבוגרים המצטיינים והמוכנים לחממות היזמות המקצועיות של סטארט-און, בליווי אישי של מנטור מ-7YA.IO.

+
+
+
+ + +
+
+
+
+ + +
+
+
+

משפך האימפקט

+

+ הגרף ממחיש כיצד 7YA.IO מזרימה כמות אדירה של מתעניינים (Awareness) ומסננת אותם דרך תוכנית החוסן. התוצאה: סטארט-און מקבלת מועמדים איכותיים, רעבים ומוכנים ללמידה. +

+
+
+
1
+
חשיפה המונית (7YA.IO)
+
+
+
2
+
סינון והכשרה (Shield)
+
+
+
3
+
יזמים צעירים (StartOn)
+
+
+
+
+ +
+
+
+ +
+ + + + +
+ + + + diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000000..c7b23ecb14 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +pythonpath = . +testpaths = tests diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000000..f8fd1d76d9 --- /dev/null +++ b/src/main.py @@ -0,0 +1,44 @@ +""" +main.py - Entry point for the Generative AI for Beginners project. + +This starter module demonstrates how to interact with a language model API. +Replace the placeholder logic below with your own implementation. +""" + + +def get_greeting(name: str) -> str: + """Return a personalised greeting message. + + Args: + name: The name of the person to greet. + + Returns: + A greeting string. + """ + return f"Hello, {name}! Welcome to Generative AI for Beginners." + + +def main() -> None: + """Run the main application logic.""" + # TODO: Configure your preferred LLM provider here. + # Supported options: Azure OpenAI, GitHub Models, OpenAI API. + # See docs/setup.md for detailed configuration instructions. + + try: + user_name = input("Enter your name: ").strip() + except EOFError: + user_name = "Builder" + print("Enter your name: [auto] Builder") + + if not user_name: + user_name = "Builder" + + print(get_greeting(user_name)) + print("\nNext steps:") + print(" 1. Read docs/setup.md to configure your environment.") + print(" 2. Explore the lesson directories (01-introduction-to-genai, etc.).") + print(" 3. Run the lesson code samples to see Generative AI in action.") + + +if __name__ == "__main__": + main() diff --git a/src/workflow_product_module.py b/src/workflow_product_module.py new file mode 100644 index 0000000000..576c450c4f --- /dev/null +++ b/src/workflow_product_module.py @@ -0,0 +1,431 @@ +"""Workflow product module for mapping SOPs and running automation agents. + +This module provides: +- SOP mapping for recurring 7ya.io style workflows. +- Two initial workflow agents: + 1. Lead Qualification Agent + 2. Content Repurposing Agent +- Structured logs linked to execution metrics (accuracy proxy, duration, cost). +- A minimal API + dashboard using Python's built-in HTTP server. +""" + +from __future__ import annotations + +from dataclasses import asdict, dataclass, field +from datetime import datetime, timezone +import json +import time +from typing import Any +from uuid import uuid4 +from http.server import BaseHTTPRequestHandler, HTTPServer + + +@dataclass(frozen=True) +class SOPDefinition: + """Describes a recurring SOP with explicit workflow logic.""" + + sop_id: str + name: str + description: str + inputs: list[str] + rules: list[str] + decisions: list[str] + outputs: list[str] + + +@dataclass +class TaskLog: + """Runtime log emitted for each workflow task execution.""" + + task_id: str + sop_id: str + agent_name: str + started_at: str + finished_at: str + duration_ms: float + decision_accuracy: float + cost_usd: float + result: dict[str, Any] + + +@dataclass +class WorkflowMetrics: + """Aggregate metrics computed from task logs.""" + + total_tasks: int = 0 + avg_decision_accuracy: float = 0.0 + avg_duration_ms: float = 0.0 + avg_cost_usd: float = 0.0 + + +@dataclass +class WorkflowProductModule: + """Productized workflow engine with SOP mapping, agents, and KPI tracking.""" + + sops: dict[str, SOPDefinition] = field(default_factory=dict) + logs: list[TaskLog] = field(default_factory=list) + + def __post_init__(self) -> None: + if not self.sops: + self.sops = build_sop_map() + + def _record_log( + self, + sop_id: str, + agent_name: str, + started: float, + decision_accuracy: float, + cost_usd: float, + result: dict[str, Any], + ) -> TaskLog: + finished = time.time() + log = TaskLog( + task_id=str(uuid4()), + sop_id=sop_id, + agent_name=agent_name, + started_at=datetime.fromtimestamp(started, tz=timezone.utc).isoformat(), + finished_at=datetime.fromtimestamp(finished, tz=timezone.utc).isoformat(), + duration_ms=(finished - started) * 1000, + decision_accuracy=max(0.0, min(1.0, decision_accuracy)), + cost_usd=max(0.0, cost_usd), + result=result, + ) + self.logs.append(log) + return log + + def run_lead_qualification_agent(self, payload: dict[str, Any]) -> dict[str, Any]: + """Execute the Lead Qualification SOP as a deterministic workflow agent.""" + started = time.time() + + budget = float(payload.get("budget_usd", 0)) + authority = str(payload.get("decision_authority", "")).lower() + need_score = float(payload.get("need_score", 0)) + timeline_days = int(payload.get("timeline_days", 365)) + + score = 0 + if budget >= 5000: + score += 1 + if authority in {"owner", "director", "manager", "founder"}: + score += 1 + if need_score >= 7: + score += 1 + if timeline_days <= 90: + score += 1 + + if score >= 3: + qualification = "sales-qualified" + next_action = "assign_to_account_executive" + elif score == 2: + qualification = "marketing-qualified" + next_action = "nurture_sequence" + else: + qualification = "unqualified" + next_action = "disqualify_or_archive" + + result = { + "lead_id": payload.get("lead_id"), + "score": score, + "qualification": qualification, + "next_action": next_action, + "reasoning": { + "budget_usd": budget, + "decision_authority": authority, + "need_score": need_score, + "timeline_days": timeline_days, + }, + } + log = self._record_log( + sop_id="sop_lead_qualification", + agent_name="Lead Qualification Agent", + started=started, + decision_accuracy=0.92, + cost_usd=0.02, + result=result, + ) + return {"result": result, "log": asdict(log)} + + def run_content_repurposing_agent(self, payload: dict[str, Any]) -> dict[str, Any]: + """Execute content repurposing SOP as a deterministic workflow agent.""" + started = time.time() + + source_type = str(payload.get("source_type", "article")).lower() + channel = str(payload.get("target_channel", "linkedin")).lower() + word_count = int(payload.get("source_word_count", 0)) + + formats: list[str] + if source_type in {"webinar", "podcast", "video"}: + formats = ["blog_post", "newsletter", "social_thread", "short_video_script"] + else: + formats = ["social_post", "newsletter", "seo_snippet"] + + if word_count > 1200: + formats.append("long_form_guide") + + tone_by_channel = { + "linkedin": "professional", + "instagram": "conversational", + "x": "punchy", + "email": "value-first", + } + tone = tone_by_channel.get(channel, "clear") + + result = { + "asset_id": payload.get("asset_id"), + "target_channel": channel, + "recommended_tone": tone, + "output_formats": formats, + "workflow_status": "ready_for_generation", + } + log = self._record_log( + sop_id="sop_content_repurposing", + agent_name="Content Repurposing Agent", + started=started, + decision_accuracy=0.9, + cost_usd=0.03, + result=result, + ) + return {"result": result, "log": asdict(log)} + + def get_metrics(self) -> dict[str, Any]: + """Compute KPI snapshot from logs.""" + if not self.logs: + return asdict(WorkflowMetrics()) + + total = len(self.logs) + accuracy = sum(log.decision_accuracy for log in self.logs) / total + duration = sum(log.duration_ms for log in self.logs) / total + cost = sum(log.cost_usd for log in self.logs) / total + + return asdict( + WorkflowMetrics( + total_tasks=total, + avg_decision_accuracy=round(accuracy, 4), + avg_duration_ms=round(duration, 2), + avg_cost_usd=round(cost, 4), + ) + ) + + def build_igor_assistant_brief( + self, + *, + risk_level: str = "normal", + strategic_goal: str = "increase_mrr", + ) -> dict[str, Any]: + """Build a compact management brief for a single-agent operating model. + + The brief turns current KPI and SOP state into an execution agenda that can be + handed to an always-on assistant ("Igor Assistant OS" style) without extra + interpretation. + """ + metrics = self.get_metrics() + risk = risk_level.lower().strip() + + if risk not in {"normal", "elevated", "crisis"}: + risk = "normal" + + kpi_alerts: list[str] = [] + if metrics["total_tasks"] == 0: + kpi_alerts.append("No workflow runs recorded yet; instrumentation validation required.") + if metrics["avg_decision_accuracy"] and metrics["avg_decision_accuracy"] < 0.9: + kpi_alerts.append("Decision accuracy below 0.90; retrain SOP logic and tighten rules.") + if metrics["avg_cost_usd"] and metrics["avg_cost_usd"] > 0.05: + kpi_alerts.append("Cost per task above target ($0.05); optimize model/tool usage.") + + priority_by_risk = { + "normal": [ + "Ship one conversion-focused experiment this week.", + "Automate the highest-frequency manual SOP.", + "Publish one authority-building content asset.", + ], + "elevated": [ + "Freeze low-ROI initiatives and focus on revenue retention.", + "Run churn-prevention outreach for at-risk members.", + "Open a 7-day KPI recovery sprint with a single accountable owner.", + ], + "crisis": [ + "Switch to cash-preservation mode and halt non-critical spend.", + "Activate incident communication plan across all stakeholder channels.", + "Run 24/7 assistant monitoring with 4-hour status updates.", + ], + } + + return { + "identity_core": { + "operator": "7ya.io", + "operating_mode": "single_source_ai_integration", + "strategic_goal": strategic_goal, + "risk_level": risk, + }, + "kpi_snapshot": metrics, + "alerts": kpi_alerts, + "priority_queue": priority_by_risk[risk], + "execution_protocol": { + "context_design": [ + "Global system instructions", + "Project initializer payload", + "Wakeup summary before each new directive", + "Bento-box separation of instructions and data", + ], + "cadence": ["monday_strategy", "wednesday_review", "friday_scoreboard"], + }, + } + + def dashboard_html(self) -> str: + """Return a basic dashboard HTML for quick product demo.""" + metrics = self.get_metrics() + return f""" + + + 7ya Workflow Module + +

7ya.io Workflow Product Module

+

KPIs

+
    +
  • Total tasks: {metrics['total_tasks']}
  • +
  • Avg decision accuracy: {metrics['avg_decision_accuracy']}
  • +
  • Avg duration (ms): {metrics['avg_duration_ms']}
  • +
  • Avg cost / task (USD): {metrics['avg_cost_usd']}
  • +
+

Available SOPs

+
{json.dumps({k: asdict(v) for k, v in self.sops.items()}, indent=2)}
+ + +""".strip() + + +class WorkflowAPIHandler(BaseHTTPRequestHandler): + """Minimal API handler to expose the product module endpoints.""" + + module: WorkflowProductModule | None = None + + @classmethod + def _module(cls) -> WorkflowProductModule: + if cls.module is None: + cls.module = WorkflowProductModule() + return cls.module + + def _send_json(self, payload: dict[str, Any], status: int = 200) -> None: + body = json.dumps(payload).encode("utf-8") + self.send_response(status) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def do_GET(self) -> None: # noqa: N802 + if self.path == "/api/sops": + module = self._module() + self._send_json({k: asdict(v) for k, v in module.sops.items()}) + return + if self.path == "/api/metrics": + module = self._module() + self._send_json(module.get_metrics()) + return + if self.path == "/dashboard": + module = self._module() + body = module.dashboard_html().encode("utf-8") + self.send_response(200) + self.send_header("Content-Type", "text/html; charset=utf-8") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + return + + self._send_json({"error": "not found"}, status=404) + + def do_POST(self) -> None: # noqa: N802 + content_length = int(self.headers.get("Content-Length", "0")) + data = self.rfile.read(content_length) + payload = json.loads(data.decode("utf-8") or "{}") + + if self.path == "/api/run/lead-qualification": + module = self._module() + self._send_json(module.run_lead_qualification_agent(payload)) + return + + if self.path == "/api/run/content-repurposing": + module = self._module() + self._send_json(module.run_content_repurposing_agent(payload)) + return + + if self.path == "/api/igor/brief": + module = self._module() + self._send_json( + module.build_igor_assistant_brief( + risk_level=str(payload.get("risk_level", "normal")), + strategic_goal=str(payload.get("strategic_goal", "increase_mrr")), + ) + ) + return + + self._send_json({"error": "not found"}, status=404) + + +def build_sop_map() -> dict[str, SOPDefinition]: + """Map recurring SOPs with explicit inputs, rules, decisions, and outputs.""" + return { + "sop_lead_qualification": SOPDefinition( + sop_id="sop_lead_qualification", + name="Lead Qualification", + description="Qualify inbound leads using BANT-lite style deterministic scoring.", + inputs=["lead_id", "budget_usd", "decision_authority", "need_score", "timeline_days"], + rules=[ + "Budget >= 5000 contributes 1 score point.", + "Authority in {owner,director,manager,founder} contributes 1 point.", + "Need score >= 7 contributes 1 point.", + "Timeline <= 90 days contributes 1 point.", + ], + decisions=[ + "Score >=3 => sales-qualified", + "Score ==2 => marketing-qualified", + "Score <=1 => unqualified", + ], + outputs=["qualification", "next_action", "score", "reasoning"], + ), + "sop_content_repurposing": SOPDefinition( + sop_id="sop_content_repurposing", + name="Content Repurposing", + description="Transform source asset into channel-specific derivative formats.", + inputs=["asset_id", "source_type", "source_word_count", "target_channel"], + rules=[ + "Video/webinar/podcast sources produce multi-format bundles.", + "Assets >1200 words add long-form guide output.", + "Target channel determines tone profile.", + ], + decisions=[ + "Select output format bundle by source type.", + "Select tone by target channel.", + "Set workflow status to ready_for_generation.", + ], + outputs=["output_formats", "recommended_tone", "workflow_status"], + ), + "sop_client_onboarding": SOPDefinition( + sop_id="sop_client_onboarding", + name="Client Onboarding", + description="Collect kickoff data, validate scope, and trigger project setup.", + inputs=["signed_contract", "package_type", "primary_goal", "stack"], + rules=["Signed contract must be true before setup.", "Package type drives onboarding checklist."], + decisions=["Approve kickoff", "Request missing information", "Route technical discovery"], + outputs=["onboarding_status", "kickoff_tasks", "owner_assignment"], + ), + "sop_campaign_reporting": SOPDefinition( + sop_id="sop_campaign_reporting", + name="Campaign Reporting", + description="Weekly aggregation of channel KPIs with exception flags.", + inputs=["channel_metrics", "period_start", "period_end", "targets"], + rules=["Compute variance to targets.", "Flag metrics below threshold by >15%."], + decisions=["Healthy", "Needs optimization", "Escalate"], + outputs=["summary", "flags", "recommended_actions"], + ), + } + + +def run_server(host: str = "0.0.0.0", port: int = 8080) -> None: + """Run the module as a basic product API/dashboard server.""" + server = HTTPServer((host, port), WorkflowAPIHandler) + print(f"Workflow module listening on http://{host}:{port}") + server.serve_forever() + + +if __name__ == "__main__": + run_server() diff --git a/tests/test_main.py b/tests/test_main.py new file mode 100644 index 0000000000..9bf394ead7 --- /dev/null +++ b/tests/test_main.py @@ -0,0 +1,31 @@ +""" +test_main.py - Placeholder tests for src/main.py. + +Run with: pytest tests/ +""" + +import pytest + +from src.main import get_greeting + + +class TestGetGreeting: + """Tests for the get_greeting helper function.""" + + def test_returns_string(self): + result = get_greeting("Alice") + assert isinstance(result, str) + + def test_contains_name(self): + result = get_greeting("Alice") + assert "Alice" in result + + def test_greeting_for_different_names(self): + for name in ["Bob", "Charlie", "Generative AI"]: + result = get_greeting(name) + assert name in result, f"Expected '{name}' to appear in greeting" + + def test_empty_name(self): + # Edge-case: empty string should still return a string without raising. + result = get_greeting("") + assert isinstance(result, str) diff --git a/tests/test_workflow_product_module.py b/tests/test_workflow_product_module.py new file mode 100644 index 0000000000..57edc3b8c3 --- /dev/null +++ b/tests/test_workflow_product_module.py @@ -0,0 +1,82 @@ +from src.workflow_product_module import WorkflowProductModule, build_sop_map + + +def test_sop_mapping_contains_required_structures(): + sops = build_sop_map() + assert "sop_lead_qualification" in sops + assert "sop_content_repurposing" in sops + + lead_sop = sops["sop_lead_qualification"] + assert lead_sop.inputs + assert lead_sop.rules + assert lead_sop.decisions + assert lead_sop.outputs + + +def test_lead_qualification_agent_sales_qualified(): + module = WorkflowProductModule() + output = module.run_lead_qualification_agent( + { + "lead_id": "lead-1", + "budget_usd": 12000, + "decision_authority": "Founder", + "need_score": 8, + "timeline_days": 30, + } + ) + assert output["result"]["qualification"] == "sales-qualified" + assert module.get_metrics()["total_tasks"] == 1 + + +def test_content_repurposing_agent_returns_formats_and_logs_cost(): + module = WorkflowProductModule() + output = module.run_content_repurposing_agent( + { + "asset_id": "asset-1", + "source_type": "webinar", + "source_word_count": 1500, + "target_channel": "linkedin", + } + ) + + assert "long_form_guide" in output["result"]["output_formats"] + metrics = module.get_metrics() + assert metrics["total_tasks"] == 1 + assert metrics["avg_cost_usd"] > 0 + + +def test_dashboard_contains_kpi_labels(): + module = WorkflowProductModule() + module.run_lead_qualification_agent( + { + "lead_id": "lead-2", + "budget_usd": 1000, + "decision_authority": "intern", + "need_score": 2, + "timeline_days": 120, + } + ) + html = module.dashboard_html() + assert "Avg decision accuracy" in html + assert "Avg cost / task" in html + + +def test_igor_assistant_brief_contains_execution_protocol_and_identity_core(): + module = WorkflowProductModule() + module.run_lead_qualification_agent( + { + "lead_id": "lead-3", + "budget_usd": 6000, + "decision_authority": "owner", + "need_score": 9, + "timeline_days": 14, + } + ) + + brief = module.build_igor_assistant_brief(risk_level="elevated", strategic_goal="stabilize_churn") + + assert brief["identity_core"]["operating_mode"] == "single_source_ai_integration" + assert brief["identity_core"]["risk_level"] == "elevated" + assert brief["identity_core"]["strategic_goal"] == "stabilize_churn" + assert "context_design" in brief["execution_protocol"] + assert len(brief["priority_queue"]) == 3