From 987dde34c77a810f0383c4ab54ede8e068d34acc Mon Sep 17 00:00:00 2001
From: Ivor Berry <11559977+ivorb@users.noreply.github.com>
Date: Thu, 13 Aug 2026 16:01:42 -0400
Subject: [PATCH 01/13] Consolidate the four vision exercises into two modular
labs
Adds a pilot of the Core + Optional lab template to this repo, reskinned to a
single Wide World Importers scenario. Purely additive: the existing exercises
01-04 and their Labfiles trees are untouched.
Lab A - Analyze visual content with AI (from 01-gen-ai-vision, 04-content-understanding)
A1 Core Ask a model about an image (Responses API, image from a URL)
A2 Optional Send a local image file (base64 data URL)
A3 Optional Extract structured metadata with Content Understanding
Lab B - Generate images and video with AI (from 02-generate-image, 03-generate-video)
B1 Core Generate images from a prompt (gpt-image-2)
B2 Optional Generate video from a text prompt (sora-2, create/poll/download)
B3 Optional Animate a reference image and remix it
Instruction pages live in Instructions/Exercises/Consolidated/. Each lab ships a
starter Python tree with fill-in-the-blank scaffolds, a complete Solution tree,
a Solution README, and setup/check_env.py --task N for per-task preflight.
Currency pass (verified against Microsoft Learn / Azure SDK sources):
- Entra ID scope cognitiveservices.azure.com/.default -> ai.azure.com/.default
- Content Understanding AnalysisInput(data=) -> begin_analyze_binary(binary_input=)
- Content Understanding .value_string/.value_array -> typed .value
- requirements dotenv -> python-dotenv; pinned azure-ai-contentunderstanding==1.1.0
- Sora prose Sora-2 -> sora-2; seconds passed as int for the Python SDK
- Fixed api_key=token_provider -> api_key=token_provider() in the video app
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
---
.../A-analyze-visual-content-with-ai.md | 143 ++++++++++
.../Consolidated/A0-getting-started.md | 146 ++++++++++
.../A1-ask-a-model-about-an-image.md | 171 +++++++++++
.../A2-send-a-local-image-file.md | 165 +++++++++++
.../A3-extract-structured-metadata.md | 268 ++++++++++++++++++
.../B-generate-images-and-video-with-ai.md | 158 +++++++++++
.../Consolidated/B0-getting-started.md | 180 ++++++++++++
.../B1-generate-images-from-a-prompt.md | 164 +++++++++++
.../B2-generate-video-from-a-prompt.md | 200 +++++++++++++
.../B3-animate-a-reference-image.md | 207 ++++++++++++++
.../Python/.env.example | 12 +
.../Python/analyze_image.py | 48 ++++
.../Python/image_chat_local.py | 58 ++++
.../Python/image_chat_url.py | 56 ++++
.../Python/images/image1.jpg | Bin 0 -> 31531 bytes
.../Python/images/image2.jpg | Bin 0 -> 38725 bytes
.../Python/images/image3.jpg | Bin 0 -> 25320 bytes
.../Python/mystery-fruit.jpeg | Bin 0 -> 164509 bytes
.../Python/requirements.txt | 4 +
.../Solution/Python/.env.example | 12 +
.../Solution/Python/analyze_image.py | 76 +++++
.../Solution/Python/image_chat_local.py | 82 ++++++
.../Solution/Python/image_chat_url.py | 74 +++++
.../Solution/Python/images/image1.jpg | Bin 0 -> 31531 bytes
.../Solution/Python/images/image2.jpg | Bin 0 -> 38725 bytes
.../Solution/Python/images/image3.jpg | Bin 0 -> 25320 bytes
.../Solution/Python/mystery-fruit.jpeg | Bin 0 -> 164509 bytes
.../Solution/Python/requirements.txt | 4 +
.../Solution/README.md | 106 +++++++
.../A-analyze-visual-content-with-ai/lion.jpg | Bin 0 -> 36031 bytes
.../mango.jpeg | Bin 0 -> 139422 bytes
.../orange.jpeg | Bin 0 -> 125336 bytes
.../setup/check_env.py | 150 ++++++++++
.../Python/.env.example | 9 +
.../Python/image_client.py | 72 +++++
.../Python/reference.png | Bin 0 -> 1645092 bytes
.../Python/requirements.txt | 3 +
.../Python/video_from_image.py | 105 +++++++
.../Python/video_from_text.py | 58 ++++
.../Solution/Python/.env.example | 9 +
.../Solution/Python/image_client.py | 92 ++++++
.../Solution/Python/reference.png | Bin 0 -> 1645092 bytes
.../Solution/Python/requirements.txt | 3 +
.../Solution/Python/video_from_image.py | 138 +++++++++
.../Solution/Python/video_from_text.py | 94 ++++++
.../Solution/README.md | 110 +++++++
.../setup/check_env.py | 142 ++++++++++
47 files changed, 3319 insertions(+)
create mode 100644 Instructions/Exercises/Consolidated/A-analyze-visual-content-with-ai.md
create mode 100644 Instructions/Exercises/Consolidated/A0-getting-started.md
create mode 100644 Instructions/Exercises/Consolidated/A1-ask-a-model-about-an-image.md
create mode 100644 Instructions/Exercises/Consolidated/A2-send-a-local-image-file.md
create mode 100644 Instructions/Exercises/Consolidated/A3-extract-structured-metadata.md
create mode 100644 Instructions/Exercises/Consolidated/B-generate-images-and-video-with-ai.md
create mode 100644 Instructions/Exercises/Consolidated/B0-getting-started.md
create mode 100644 Instructions/Exercises/Consolidated/B1-generate-images-from-a-prompt.md
create mode 100644 Instructions/Exercises/Consolidated/B2-generate-video-from-a-prompt.md
create mode 100644 Instructions/Exercises/Consolidated/B3-animate-a-reference-image.md
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Python/.env.example
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Python/analyze_image.py
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Python/image_chat_local.py
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Python/image_chat_url.py
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Python/images/image1.jpg
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Python/images/image2.jpg
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Python/images/image3.jpg
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Python/mystery-fruit.jpeg
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Python/requirements.txt
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Solution/Python/.env.example
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Solution/Python/analyze_image.py
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Solution/Python/image_chat_local.py
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Solution/Python/image_chat_url.py
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Solution/Python/images/image1.jpg
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Solution/Python/images/image2.jpg
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Solution/Python/images/image3.jpg
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Solution/Python/mystery-fruit.jpeg
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Solution/Python/requirements.txt
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/Solution/README.md
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/lion.jpg
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/mango.jpeg
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/orange.jpeg
create mode 100644 Labfiles/A-analyze-visual-content-with-ai/setup/check_env.py
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/Python/.env.example
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/Python/image_client.py
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/Python/reference.png
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/Python/requirements.txt
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/Python/video_from_image.py
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/Python/video_from_text.py
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/Solution/Python/.env.example
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/Solution/Python/image_client.py
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/Solution/Python/reference.png
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/Solution/Python/requirements.txt
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/Solution/Python/video_from_image.py
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/Solution/Python/video_from_text.py
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/Solution/README.md
create mode 100644 Labfiles/B-generate-images-and-video-with-ai/setup/check_env.py
diff --git a/Instructions/Exercises/Consolidated/A-analyze-visual-content-with-ai.md b/Instructions/Exercises/Consolidated/A-analyze-visual-content-with-ai.md
new file mode 100644
index 000000000..3a4b0b9e8
--- /dev/null
+++ b/Instructions/Exercises/Consolidated/A-analyze-visual-content-with-ai.md
@@ -0,0 +1,143 @@
+---
+lab:
+ title: 'Analyze visual content with AI'
+ description: 'Build the Wide World Importers visual analysis toolkit: a vision-enabled chat app that answers questions about produce photos, and an Azure AI Content Understanding analyzer that turns the campaign asset library into searchable structured metadata. A modular lab you can complete end to end or one task at a time.'
+ level: 300
+ concepts: 'multimodal chat, image input, Responses API, Azure AI Content Understanding, structured metadata'
+ duration: 30
+ islab: true
+ status: 'draft'
+---
+
+# Analyze visual content with AI
+
+**Difficulty** ▰▰▰▱▱ **L300** (filled bars out of 5; **L100** beginner → **L500** expert)
+
+A photograph is full of information that your systems can't use — until a model reads it for
+you. In this lab you'll build the **Wide World Importers** visual analysis toolkit: first a chat
+app that can *look* at a photo and answer questions about it, then an analyzer that turns a whole
+image library into structured, searchable metadata.
+
+
+
+
+What makes a model "multimodal"?
+
+
+A **multimodal** model accepts more than one kind of input. A text-only model can only read the
+words you send it; a multimodal model can accept an **image** alongside your text and reason about
+both together. That means you can send a photo of an unfamiliar fruit and ask "what is this and
+what would I cook with it?" in a single request — no separate image classifier, no fixed list of
+labels.
+
+[Learn more →](https://learn.microsoft.com/azure/foundry/openai/how-to/responses)
+
+
+
+
+**Your scenario:** you work at **Wide World Importers**, a specialty grocery importer that ships
+unusual produce to supermarkets worldwide, and runs its own marketing studio. Store staff regularly
+receive crates of fruit nobody on the floor recognizes, and the studio's asset library has grown to
+thousands of untagged photographs. Across this lab you'll build the AI that fixes both problems.
+
+You'll start with the **Core** task that gets you to a working, vision-enabled app as quickly as
+possible. From there, a set of **Optional** tasks lets you go deeper.
+
+> **Note**: Some of the technologies used in this exercise are in preview or in active
+> development. You may experience some unexpected behavior, warnings, or errors.
+
+## What you'll learn
+
+By completing the **Core** task of this exercise, you'll be able to:
+
+- **Send an image to a multimodal model** with the Responses API — combine an `input_text` prompt
+ and an `input_image` reference in one request and read the model's answer.
+
+The **Optional** tasks let you additionally:
+
+- **Send a local image file** to the model by base64-encoding it into a data URL, instead of
+ pointing at a public link.
+- **Extract structured metadata** from images with **Azure AI Content Understanding** — define a
+ schema of fields, build an analyzer against it, and call that analyzer from Python to get
+ consistent descriptions and tags back.
+
+## How this lab is organized
+
+This lab is **modular**. Each task is written to be completed **on its own, starting fresh** —
+so you can pick a single task and do just that one. Every task also shares one starter folder,
+one virtual environment, and one `.env`, so if you'd rather work straight through, you can.
+
+1. **Start with [Getting started](A0-getting-started.md)** — create your Microsoft Foundry
+ project, deploy a model, get the starter code, and set up your `.env`. Every task begins
+ here; if you're doing the whole lab in one sitting, you only need to do this once.
+2. **Do any task.** Each task lists the setup it needs so you can start it independently. If
+ you're moving straight from the previous task, a short *"Continuing from a previous task?"*
+ note at the top lets you skip the repeated setup and keep going.
+
+## Lab at a glance
+
+Complete the **Core** task first (about **30 minutes**) — it ends with a working vision-enabled
+chat app. Then expand any **Optional** tasks that interest you. The full lab, including all
+optional tasks, takes about **1 hour 15 minutes**.
+
+| Section | Task | Difficulty | Time |
+| --- | --- | --- | --- |
+| **Core** | [Task 1 – Ask a model about an image](A1-ask-a-model-about-an-image.md) | ▰▰▰▱▱ L300 | ~30 min |
+| *Optional* | [Task 2 – Send a local image file](A2-send-a-local-image-file.md) | ▰▰▱▱▱ L200 | ~15 min |
+| *Optional* | [Task 3 – Extract structured metadata with Content Understanding](A3-extract-structured-metadata.md) | ▰▰▰▱▱ L300 | ~30 min |
+
+**Choosing your path** — pick the tasks that fit the time you have:
+
+- **Core only (~30 min):** do Task 1.
+- **Core + local files (~45 min):** add **Task 2**, which swaps the public image link for a file on disk.
+- **Everything (~1h 15m):** add **Task 3**, which moves from conversational answers to structured,
+ schema-driven metadata you can index and search.
+
+## Two ways to read an image
+
+The tasks in this lab deliberately show two different shapes of the same idea, and knowing when to
+reach for each is the real lesson:
+
+- In **Task 1** and **Task 2**, you send an image to a **chat model** and get a free-form,
+ conversational answer. This is ideal when a human is asking the question, and every question is
+ different — "what is this?", "is it ripe?", "what would I cook with it?".
+- In **Task 3**, you send an image to a **Content Understanding analyzer** and get a **structured
+ result** that always has the same fields. This is what you want when a *system* is asking, and
+ you need thousands of images described the same way so you can index and search them.
+
+Same photograph, two very different outputs — one for people, one for pipelines.
+
+## Summary
+
+Across this lab you:
+
+- Sent an **image and a prompt** to a multimodal model with the Responses API and read the answer.
+- (Optionally) **base64-encoded a local file** into a data URL so the model could read an image
+ that isn't published anywhere.
+- (Optionally) built a **Content Understanding analyzer** with a custom schema and called it from
+ Python to generate consistent descriptions and tags for a library of images.
+
+Together these cover both halves of visual analysis: answering a person's question about one
+image, and describing a whole library consistently enough to search.
+
+## Clean up
+
+If you're finished, delete the resources you created to avoid unnecessary Azure costs.
+
+1. In the [Azure portal](https://portal.azure.com), navigate to the resource group that contains your Foundry resource.
+1. On the toolbar, select **Delete resource group**, enter the resource group name, and confirm.
+
+> If you completed Task 3, the same resource group also contains the storage account you created
+> for Content Understanding — deleting the resource group removes it too.
diff --git a/Instructions/Exercises/Consolidated/A0-getting-started.md b/Instructions/Exercises/Consolidated/A0-getting-started.md
new file mode 100644
index 000000000..d6b48ffe4
--- /dev/null
+++ b/Instructions/Exercises/Consolidated/A0-getting-started.md
@@ -0,0 +1,146 @@
+---
+lab:
+ title: 'Getting started: set up your environment'
+ description: 'Shared setup for the Analyze visual content with AI lab: create a Microsoft Foundry project, deploy a multimodal model, get the starter code, and configure your environment. Complete this once before any task.'
+ level: 300
+ concepts: 'environment setup, Microsoft Foundry project, model deployment'
+ status: 'draft'
+---
+
+# Getting started
+
+This page sets up everything the **Analyze visual content with AI** lab needs. **Every task begins
+here** — complete this page first. Each task is written so you can then do it on its own; if you're
+working through the whole lab in one sitting, you only need to do this setup once.
+
+**Your scenario:** you work at **Wide World Importers**, a specialty grocery importer that ships
+unusual produce to supermarkets worldwide, and runs its own marketing studio. Across the lab you'll
+build the AI that helps store staff identify unfamiliar produce and helps the studio tag its image
+library.
+
+> **Note**: Some of the technologies used in this lab are in preview or in active
+> development. You may experience some unexpected behavior, warnings, or errors.
+
+## Prerequisites
+
+Before starting, ensure you have:
+
+- An active [Azure subscription](https://azure.microsoft.com/pricing/purchase-options/azure-account) with sufficient permissions and quota to provision Azure AI resources
+- [Visual Studio Code](https://code.visualstudio.com/) installed on your local machine
+- [Python 3.13](https://www.python.org/downloads/) or later installed\*
+- [Git](https://git-scm.com/downloads) installed and configured
+- [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) installed
+- Basic familiarity with Python
+
+> \* Python 3.14 is available, but some dependencies are not yet compiled for that release. The lab has been successfully tested with Python 3.13.12.
+
+## Create a Microsoft Foundry project
+
+Microsoft Foundry uses projects to organize models, resources, data, and other assets used to
+develop an AI solution.
+
+1. In a web browser, open the [Foundry portal](https://ai.azure.com) at `https://ai.azure.com` and sign in using your Azure credentials. Close any tips or quick start panes that are opened the first time you sign in.
+
+ > **Important**: For this lab, you're using the **New** Foundry experience. If it isn't already enabled, enable the **New Foundry** option in the toolbar at the top of the page.
+
+1. When prompted, create a **new** project with a unique name. Expand the **Advanced options** area and specify:
+ - **Foundry resource**: *Use the default name for your resource (usually {project_name}-resource)*
+ - **Subscription**: *Your Azure subscription*
+ - **Resource group**: *Create or select a resource group*
+ - **Region**: *Select any available region*\*
+
+ > \* Some Azure AI resources are constrained by regional model quotas. If you hit a quota limit later, you may need to create another resource in a different region.
+
+1. Select **Create** and wait for your project to be created.
+
+1. On the home page for your project, note that the API key, project endpoint, and **Azure OpenAI endpoint** are displayed.
+
+ > **Important**: You need the **Azure OpenAI endpoint**, not the project endpoint. Copy it now — you'll add it to your `.env` in a moment.
+
+## Deploy a model
+
+Tasks 1 and 2 need a model that can process image-based input.
+
+1. On the **Discover** page, select the **Models** tab to view the Microsoft Foundry model catalog.
+
+1. Search for and deploy the `gpt-5.2` model using the default settings. Deployment may take a minute or so.
+
+ > **Tip**: Model deployments are subject to regional quotas. If you don't have enough quota to deploy `gpt-5.2` in your project's region, use another generally available multimodal model such as `gpt-4.1` or `gpt-4o`. Alternatively, create a new project in a different region.
+
+1. When the model has been deployed, the model playground page opens, where you can chat with the model.
+
+ > **Tip**: Note the model deployment name (which by default matches the model name, for example *gpt-5.2*) — you'll need it for `MODEL_DEPLOYMENT_NAME`.
+
+## Test the model in the playground
+
+Before you write any code, confirm your deployment can actually read an image.
+
+1. In a new browser tab, download [mango.jpeg](https://microsoftlearning.github.io/mslearn-ai-vision/Labfiles/A-analyze-visual-content-with-ai/mango.jpeg) from `https://microsoftlearning.github.io/mslearn-ai-vision/Labfiles/A-analyze-visual-content-with-ai/mango.jpeg` and save it to a folder on your local file system.
+
+1. Navigate back to the chat playground page for your model deployment in the Foundry portal.
+
+1. In the main chat session panel, under the chat input box, use the attach button (**📎**) to upload the *mango.jpeg* image file, and then add the text `What desserts could I make with this fruit?` and submit the prompt.
+
+ 
+
+1. Review the response, which should provide relevant guidance for desserts you can make using a mango.
+
+## Get the starter code
+
+1. In VS Code, open the Command Palette (**Ctrl+Shift+P**), run **Git: Clone**, and enter:
+
+ ```
+ https://github.com/microsoftlearning/mslearn-ai-vision.git
+ ```
+
+ You may be prompted to confirm you trust the authors.
+
+1. Open the cloned repo, then **File > Open Folder** and select `mslearn-ai-vision/Labfiles/A-analyze-visual-content-with-ai/Python`. This single folder holds the starter code for **every** task in this lab — you use one virtual environment and one `.env` throughout.
+
+1. In VS Code, view the **Extensions** pane and, if it isn't already installed, install the **Python** extension.
+
+1. Right-click **requirements.txt** and choose **Open in Integrated Terminal**. Then create a virtual environment and install packages:
+
+ ```
+ python -m venv labenv
+ .\labenv\Scripts\Activate.ps1
+ pip install -r requirements.txt
+ ```
+
+1. Copy **.env.example** to a new file named **.env**, then open it and set the values you have so far:
+
+ - `OPENAI_ENDPOINT` — the Azure OpenAI endpoint for your Foundry resource, ending in `/openai/v1/`, so it looks like `https://{your-resource-name}.openai.azure.com/openai/v1/`
+ - `MODEL_DEPLOYMENT_NAME` — the deployment name of the model you deployed above
+
+ Save the file.
+
+ > `CONTENT_UNDERSTANDING_ENDPOINT` and `ANALYZER_ID` are only used by **Task 3**, which walks you through creating the analyzer they refer to. Leave them as they are for now.
+
+## Sign in to Azure
+
+Every task in this lab authenticates with `DefaultAzureCredential`, which uses your Azure CLI
+sign-in. In the terminal, run:
+
+```
+az login
+```
+
+> **Note**: In most scenarios, just using *az login* will be sufficient. However, if you have subscriptions in multiple tenants, you may need to specify the tenant by using the *--tenant* parameter. See [Sign into Azure interactively using the Azure CLI](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively) for details.
+
+## Check you're ready for a task
+
+Each task needs specific values in your `.env`. Before starting a task, run the preflight
+check from the `Labfiles/A-analyze-visual-content-with-ai` folder — it reads your `.env` and tells
+you what (if anything) is missing:
+
+```
+python setup/check_env.py --task 1
+```
+
+Swap `1` for the task number you're about to start. That's it — head to any task:
+
+| Task | Page |
+| --- | --- |
+| Task 1 – Ask a model about an image | [A1](A1-ask-a-model-about-an-image.md) |
+| Task 2 – Send a local image file | [A2](A2-send-a-local-image-file.md) |
+| Task 3 – Extract structured metadata with Content Understanding | [A3](A3-extract-structured-metadata.md) |
diff --git a/Instructions/Exercises/Consolidated/A1-ask-a-model-about-an-image.md b/Instructions/Exercises/Consolidated/A1-ask-a-model-about-an-image.md
new file mode 100644
index 000000000..dd0808c41
--- /dev/null
+++ b/Instructions/Exercises/Consolidated/A1-ask-a-model-about-an-image.md
@@ -0,0 +1,171 @@
+---
+lab:
+ title: 'Task 1 – Ask a model about an image'
+ description: 'Use the Responses API to send an image and a question to a multimodal model in Microsoft Foundry, and build a Wide World Importers produce assistant around it.'
+ level: 300
+ concepts: 'multimodal chat, image input, Responses API'
+ islab: true
+ status: 'draft'
+---
+
+# Task 1 — Ask a model about an image
+
+*Part of the **Analyze visual content with AI** lab. New here? Start with [Getting started](A0-getting-started.md).*
+
+> **Set up (start here):** This task needs a Foundry project with a multimodal model deployed, and
+> the starter code. If you haven't already, complete [Getting started](A0-getting-started.md) to
+> create your project, deploy the model, clone the code, and set `OPENAI_ENDPOINT` and
+> `MODEL_DEPLOYMENT_NAME` in `Python/.env`. Then, from the
+> `Labfiles/A-analyze-visual-content-with-ai` folder, verify you're ready:
+
+```
+python setup/check_env.py --task 1
+```
+
+> **Continuing from a previous task?** If you just finished an earlier task in the same
+> `Python` folder, your project, virtual environment, and `.env` are already set — go
+> straight to **Write code to get an OpenAI chat client** below.
+
+---
+
+A crate arrives at a Wide World Importers store and nobody on the floor recognizes what's in it.
+The staff member doesn't want a label — they want an answer: what is it, is it ripe, what do
+customers do with it? In this task you'll build the **produce assistant** that answers exactly
+that, by sending a photograph and a question to a multimodal model in the same request.
+
+
+
+
+What is the Responses API?
+
+
+The **Responses API** is the current API for generating model output on Microsoft Foundry. Instead
+of a flat list of chat messages, you pass an `input` array where a single message's `content` can
+mix **parts** — an `input_text` part for your question and an `input_image` part for the picture.
+The model sees both together, so it can answer questions *about* the image rather than about a
+description of it.
+
+The `image_url` part accepts a public URL (this task) or a base64 data URL for a local file
+(Task 2).
+
+[Learn more →](https://learn.microsoft.com/azure/foundry/openai/how-to/responses)
+
+
+
+
+Open the `Python` folder and activate the virtual environment from [Getting started](A0-getting-started.md) (`.\labenv\Scripts\Activate.ps1`), then continue below.
+
+### Write code to get an OpenAI chat client
+
+Open **image_chat_url.py** and add code at each commented placeholder.
+
+1. Review the code already in the file. It contains:
+ - Some **import** statements.
+ - A `main` function that loads your configuration, defines a system message, and then loops asking you for a prompt until you type `quit`.
+
+ > **Tip**: As you add code, keep the indentation aligned with the comments.
+
+1. At the top of the file, find the comment **Add references** and add the namespaces you'll need:
+
+ ```python
+ # Add references
+ from openai import OpenAI
+ from azure.identity import DefaultAzureCredential, get_bearer_token_provider
+ ```
+
+1. In the **main** function, under the comment **Get configuration settings**, note that the code loads the Azure OpenAI endpoint and model deployment name values you defined in your `.env` file.
+
+1. Find the comment **Create an OpenAI client**, and add the following code to connect to your Foundry resource:
+
+ > **Tip**: Be careful to maintain the correct indentation level for your code.
+
+ ```python
+ # Create an OpenAI client
+ credential = DefaultAzureCredential()
+ token_provider = get_bearer_token_provider(credential, "https://ai.azure.com/.default")
+ client = OpenAI(
+ base_url=openai_endpoint,
+ api_key=token_provider()
+ )
+ ```
+
+ The **DefaultAzureCredential** object authenticates using your `az login` session. The token
+ provider exchanges that sign-in for a bearer token scoped to Azure AI, which the **OpenAI**
+ client sends with every request — so there's no API key in your code.
+
+### Write code to submit a URL-based image prompt
+
+1. Note that the code includes a loop to allow a user to input a prompt until they enter "quit". In the loop section, find the comment **Get a response to image input** and add the following code to submit a prompt that includes this image:
+
+ 
+
+ ```python
+ # Get a response to image input
+ image_url = "https://microsoftlearning.github.io/mslearn-ai-vision/Labfiles/A-analyze-visual-content-with-ai/orange.jpeg"
+ response = client.responses.create(
+ model=model_deployment,
+ input=[
+ {"role": "developer", "content": system_message},
+ {"role": "user", "content": [
+ {"type": "input_text", "text": prompt},
+ {"type": "input_image", "image_url": image_url}
+ ]}
+ ]
+ )
+ print(response.output_text)
+ ```
+
+ The `developer` message carries the standing instructions for the assistant. The `user` message
+ carries **two parts** — the question as `input_text` and the picture as `input_image` — which is
+ what lets the model reason about both at once.
+
+1. Save the file (**Ctrl+S**).
+
+### Run and test
+
+1. In the terminal, make sure you're signed in and then run the app:
+
+ ```
+ az login
+ ```
+
+ ```
+ python image_chat_url.py
+ ```
+
+ `az login` lets `DefaultAzureCredential` authenticate to your Azure account.
+
+1. When prompted, enter the following prompt:
+
+ ```
+ Suggest some recipes that include this fruit
+ ```
+
+1. Review the response — it should suggest recipes built around an orange, which the model has read
+ directly from the picture. Then enter `quit` to exit the program.
+
+ > **Tip**: If the app fails because the rate limit is exceeded, wait a few seconds and try again. If there is insufficient quota available in your subscription, the model may not be able to respond.
+
+ > **Note**: In this simple app, we haven't implemented logic to retain conversation history; so the model will treat each prompt as a new request with no context of the previous prompt.
+
+> ✅ **Checkpoint**: You've sent an image and a question to a multimodal model in a single request
+> and got a grounded answer back. That's the Core of this lab. The optional tasks below extend it
+> to local files and to structured, searchable metadata.
+
+When you're finished, enter `deactivate` to exit the virtual environment.
+
+---
+
+**Next (optional):** [Task 2 — Send a local image file](A2-send-a-local-image-file.md) · [Task 3 — Extract structured metadata with Content Understanding](A3-extract-structured-metadata.md)
diff --git a/Instructions/Exercises/Consolidated/A2-send-a-local-image-file.md b/Instructions/Exercises/Consolidated/A2-send-a-local-image-file.md
new file mode 100644
index 000000000..c4536f2cc
--- /dev/null
+++ b/Instructions/Exercises/Consolidated/A2-send-a-local-image-file.md
@@ -0,0 +1,165 @@
+---
+lab:
+ title: 'Task 2 – Send a local image file'
+ description: 'Base64-encode an image from disk into a data URL so a multimodal model can read a picture that isn''t published anywhere.'
+ level: 200
+ concepts: 'image input, base64 data URLs, Responses API'
+ islab: true
+ status: 'draft'
+---
+
+# Task 2 — Send a local image file
+
+*Part of the **Analyze visual content with AI** lab. New here? Start with [Getting started](A0-getting-started.md).*
+
+> **Set up (start here):** This task needs a Foundry project with a multimodal model deployed, and
+> the starter code. If you haven't already, complete [Getting started](A0-getting-started.md) to
+> create your project, deploy the model, clone the code, and set `OPENAI_ENDPOINT` and
+> `MODEL_DEPLOYMENT_NAME` in `Python/.env`. Then, from the
+> `Labfiles/A-analyze-visual-content-with-ai` folder, verify you're ready:
+
+```
+python setup/check_env.py --task 2
+```
+
+> **Continuing from a previous task?** If you just finished Task 1 in the same `Python` folder,
+> your project, virtual environment, and `.env` are already set — go straight to
+> **Write code to upload a local image file** below. This task uses its own file,
+> `image_chat_local.py`, so your Task 1 code stays intact for comparison.
+
+---
+
+Task 1 pointed the model at a picture on a public website. That's fine for a catalog photo, but
+the shots that actually arrive at Wide World Importers come from a phone at the loading dock —
+they're on someone's disk, not on the internet. In this task you'll send an image the model can't
+reach by URL, by encoding the file itself into the request.
+
+
+
+
+What is a data URL?
+
+
+A **data URL** carries the file's bytes inside the URL itself instead of pointing at a location.
+It looks like `data:image/jpeg;base64,/9j/4AAQSkZJRg...` — a MIME type, the word `base64`, and then
+the whole file encoded as text.
+
+That matters here because the `input_image` part expects an `image_url`, and the service has to be
+able to *fetch* a normal link. A file on your laptop isn't fetchable, so you inline it instead. The
+API call is otherwise identical to Task 1 — only the value of `image_url` changes.
+
+[Learn more →](https://learn.microsoft.com/azure/foundry/openai/how-to/responses)
+
+
+
+
+Open the `Python` folder and activate the virtual environment from [Getting started](A0-getting-started.md) (`.\labenv\Scripts\Activate.ps1`), then continue below.
+
+### Write code to get an OpenAI chat client
+
+Open **image_chat_local.py** and add code at each commented placeholder.
+
+1. Review the code already in the file. It's the same produce assistant shell as Task 1, and it already imports `base64` and `Path` for you.
+
+ > **Tip**: As you add code, keep the indentation aligned with the comments.
+
+1. At the top of the file, find the comment **Add references** and add the namespaces you'll need:
+
+ ```python
+ # Add references
+ from openai import OpenAI
+ from azure.identity import DefaultAzureCredential, get_bearer_token_provider
+ ```
+
+1. Find the comment **Create an OpenAI client**, and add the following code:
+
+ ```python
+ # Create an OpenAI client
+ credential = DefaultAzureCredential()
+ token_provider = get_bearer_token_provider(credential, "https://ai.azure.com/.default")
+ client = OpenAI(
+ base_url=openai_endpoint,
+ api_key=token_provider()
+ )
+ ```
+
+### Write code to upload a local image file
+
+The starter folder includes **mystery-fruit.jpeg** — a photograph of a fruit that isn't published anywhere:
+
+
+
+1. In the loop section, find the comment **Get a response to image input** and add the following code to read the file, encode it, and send it:
+
+ ```python
+ # Get a response to image input
+ image_path = Path("mystery-fruit.jpeg")
+ image_format = "jpeg"
+ with open(image_path, "rb") as image_file:
+ image_data = base64.b64encode(image_file.read()).decode("utf-8")
+
+ data_url = f"data:image/{image_format};base64,{image_data}"
+
+ response = client.responses.create(
+ model=model_deployment,
+ input=[
+ {"role": "developer", "content": system_message},
+ {"role": "user", "content": [
+ {"type": "input_text", "text": prompt},
+ {"type": "input_image", "image_url": data_url}
+ ]}
+ ]
+ )
+ print(response.output_text)
+ ```
+
+ Compare this with Task 1: the request shape is identical. The only difference is that
+ `image_url` now holds a `data:` URL built from the file's bytes rather than a link to a
+ website.
+
+1. Save the file (**Ctrl+S**).
+
+### Run and test
+
+1. In the terminal, make sure you're signed in and then run the app:
+
+ ```
+ az login
+ ```
+
+ ```
+ python image_chat_local.py
+ ```
+
+1. When prompted, enter the following prompt:
+
+ ```
+ What is this fruit? What recipes could I use it in?
+ ```
+
+1. Review the response. The model should identify the fruit from the photo on disk and suggest
+ recipes for it. Then enter `quit` to exit the program.
+
+ > **Note**: Base64 encoding makes the request noticeably larger than a URL-based one. For very
+ > large images, expect a slightly longer round trip.
+
+> ✅ **Checkpoint**: You've sent a local image file to a multimodal model by inlining it as a data
+> URL — the same technique you'd use for a photo captured in your own app.
+
+When you're finished, enter `deactivate` to exit the virtual environment.
+
+---
+
+**Next (optional):** [Task 3 — Extract structured metadata with Content Understanding](A3-extract-structured-metadata.md)
diff --git a/Instructions/Exercises/Consolidated/A3-extract-structured-metadata.md b/Instructions/Exercises/Consolidated/A3-extract-structured-metadata.md
new file mode 100644
index 000000000..3aa2566b5
--- /dev/null
+++ b/Instructions/Exercises/Consolidated/A3-extract-structured-metadata.md
@@ -0,0 +1,268 @@
+---
+lab:
+ title: 'Task 3 – Extract structured metadata with Content Understanding'
+ description: 'Build an Azure AI Content Understanding image analyzer with a custom schema, then call it from Python to turn the Wide World Importers asset library into searchable descriptions and tags.'
+ level: 300
+ concepts: 'Azure AI Content Understanding, custom schemas, structured output, image metadata'
+ islab: true
+ status: 'draft'
+---
+
+# Task 3 — Extract structured metadata with Content Understanding
+
+*Part of the **Analyze visual content with AI** lab. New here? Start with [Getting started](A0-getting-started.md).*
+
+> **Set up (start here):** This task needs the Foundry project and starter code from
+> [Getting started](A0-getting-started.md), plus two things you'll create on this page: an Azure
+> **storage account** and a Content Understanding **analyzer**. It does **not** need the chat model
+> deployment from Tasks 1 and 2, and it doesn't reuse any code from them — you can start here cold.
+> Once you've built your analyzer below, set `CONTENT_UNDERSTANDING_ENDPOINT` and `ANALYZER_ID` in
+> `Python/.env`, then verify from the `Labfiles/A-analyze-visual-content-with-ai` folder:
+
+```
+python setup/check_env.py --task 3
+```
+
+> **Continuing from a previous task?** If you just finished Task 1 or Task 2 in the same `Python`
+> folder, your project, virtual environment, and `az login` are already set — skip the clone and
+> the `pip install`, but you still need to complete **Create a storage account** and **Create an
+> image analyzer** below, because this task uses a different service to the chat tasks.
+
+---
+
+The Wide World Importers marketing studio has thousands of photographs and almost no metadata.
+Task 1 could tell one staff member what's in one picture — but you can't build a searchable
+library on free-form paragraphs. In this task you'll define a **schema**, build an analyzer that
+fills it in the same way for every image, and call that analyzer from Python.
+
+
+
+
+What is Azure AI Content Understanding?
+
+
+**Azure AI Content Understanding** in Foundry Tools uses generative AI to interpret unstructured
+content — documents, images, audio, and video — and return output that follows a **schema you
+define**. You describe the fields you want (`Description`, `Tags`, and so on), Content
+Understanding builds an **analyzer** for that schema, and every file you send back through it comes
+out shaped the same way.
+
+That predictability is the whole point. A chat model gives you a different paragraph every time; an
+analyzer gives you the same fields every time, which is what you need to index, filter, and search
+a library at scale.
+
+[Learn more →](https://learn.microsoft.com/azure/ai-services/content-understanding/overview)
+
+
+
+
+## Create an Azure storage account
+
+Content Understanding needs somewhere to hold the content assets you analyze.
+
+1. In a new browser tab, open the [Azure portal](https://portal.azure.com) at `https://portal.azure.com` and browse to the resource group where you created your Foundry project resource.
+
+ The resource group should contain your Foundry resource and the project you created.
+
+1. Create a new **Storage account** resource in the resource group, with the following settings:
+ - **Subscription**: *Your Azure subscription*
+ - **Resource group**: *The resource group containing your Foundry resource*
+ - **Storage account name**: *A unique name for your storage account*
+ - **Region**: *The same region as your Foundry resource*
+ - **Preferred storage type**: Azure Blob Storage or Azure Data Lake Storage Gen 2
+ - **Performance**: Standard
+ - **Redundancy**: Locally-redundant storage (LRS)
+
+1. Wait for your storage account to be created.
+
+## Create an image analyzer
+
+1. In a new browser tab, navigate to [Content Understanding Studio](https://contentunderstanding.ai.azure.com/home) at `https://contentunderstanding.ai.azure.com/home` and sign in using your Azure credentials if prompted.
+
+1. At the top right, select the **Settings** icon to view your account settings for Azure AI Content Understanding.
+
+1. On the **Setup Azure resource** page, select the **Add resource** button.
+
+1. Select your subscription and the Foundry resources that match your Foundry project name.
+
+1. Check the box for **Enable auto-deployment for required models if no default deployment available**.
+
+1. Select **Next**, then select **Save** to deploy the required models.
+
+ The deployment process can take several minutes. Once the models are deployed, the resource will appear under **Connected Azure AI Foundry Resources**. Note the name of the resource.
+
+1. On the menu bar, select **Build**. Then use the **Create** button to create a new Content Understanding project with the following settings:
+ - **Project name**: *A unique name for your image analysis project*
+ - **Description**: `Wide World Importers asset library`
+ - **Type of project**: Extract content and field with custom schema
+ - **Advanced settings**: Ensure your Foundry resource and storage account are selected, a new container will be created, and a chat completion model such as `gpt-5.2` is selected.
+
+1. When the project has been created, in a new browser tab, download the [lion.jpg](https://microsoftlearning.github.io/mslearn-ai-vision/Labfiles/A-analyze-visual-content-with-ai/lion.jpg) image from `https://microsoftlearning.github.io/mslearn-ai-vision/Labfiles/A-analyze-visual-content-with-ai/lion.jpg` and save it in a local folder.
+
+ Then return to the Content Understanding project, and upload the **lion.jpg** file to the project.
+
+1. When prompted to choose a template, select **Image Analysis** and ensure the schema is set to **Start from Scratch**. Then save the project.
+
+1. After the image has been uploaded, in the **Schema** pane, use **Add new field** to add the following fields to the schema:
+
+ | Field Name | Field Description | Value type | Method |
+ |--|--|--|--|
+ | `Description` | `Image description` | String | Generate |
+ | `Tags` | `Image tags` | List of Strings | Generate |
+
+ > **Important**: The field names are case-sensitive and your application code looks for these exact names. Use `Description` and `Tags` as written.
+
+1. Save the changes to the schema.
+
+1. Select **Run analysis** to run the analyzer on the image, and review the fields that are generated; which should include an accurate description and a collection of relevant tags for the image.
+
+1. When you're satisfied that the analyzer has returned accurate values for the fields, use the **Build analyzer** button to publish an analyzer with a unique name and suitable description.
+
+ > **Tip**: You'll need the name later to identify your analyzer in application code — it's your `ANALYZER_ID`.
+
+1. When your analyzer has been built, jump to the analyzer list and verify it's listed there.
+
+1. Select your analyzer in the list to open it, and then view the **Code Example** tab to see the code necessary to use your analyzer.
+
+1. Review the Python code example, noting in the **main** function the **endpoint** for your Content Understanding resource; which should look similar to this:
+
+ ```
+ https://{your_foundry_resource}.services.ai.azure.com/
+ ```
+
+1. Under the code example, note that your **resource key** is available. You *can* use this in a client application to authenticate a connection to the endpoint; but in this exercise we're going to use Microsoft Entra ID authentication.
+
+## Prepare the application configuration
+
+1. If you haven't already, clone the repo and set up the `Python` folder as described in [Getting started](A0-getting-started.md).
+
+1. Open the **.env** file in `Labfiles/A-analyze-visual-content-with-ai/Python` and set:
+
+ - `CONTENT_UNDERSTANDING_ENDPOINT` — the resource endpoint you copied from the **Code Example** tab
+
+ - `ANALYZER_ID` — the name you gave your analyzer when you selected **Build analyzer**
+
+ > **Important**: Be sure to use the `https://{YOUR-RESOURCE-NAME}.services.ai.azure.com/` Foundry resource endpoint, not the project endpoint or the Azure OpenAI endpoint you used in Tasks 1 and 2.
+
+ Save the file.
+
+1. From the `Labfiles/A-analyze-visual-content-with-ai` folder, confirm you're ready:
+
+ ```
+ python setup/check_env.py --task 3
+ ```
+
+### Write code to analyze images and generate descriptions
+
+Open **analyze_image.py** and add code at each commented placeholder.
+
+> **Tip**: As you add code, be sure to maintain the correct indentation.
+
+1. Find the comment **Add references** and add the following code for the necessary imports:
+
+ ```python
+ # Add references
+ from azure.ai.contentunderstanding import ContentUnderstandingClient
+ from azure.ai.contentunderstanding.models import AnalysisResult
+ from azure.core.exceptions import AzureError
+ from azure.identity import DefaultAzureCredential
+ ```
+
+1. In the **main** function, note that code to get the configuration values from your environment file has been provided.
+
+1. Find the comment **Set up Content Understanding client** and add the following code:
+
+ ```python
+ # Set up Content Understanding client
+ credential = DefaultAzureCredential()
+ client = ContentUnderstandingClient(
+ endpoint=endpoint,
+ credential=credential,
+ api_version=api_version)
+ ```
+
+1. Note that code for the user to input a file number or quit the program has been provided, and that it reads the selected image into `file_bytes`.
+
+1. Find the comment **Analyze the file** and add the following code:
+
+ ```python
+ # Analyze the file
+ try:
+ poller = client.begin_analyze_binary(
+ analyzer_id=analyzer_id,
+ binary_input=file_bytes,
+ )
+ result: AnalysisResult = poller.result()
+ except AzureError as err:
+ print(f"[Azure Error]: {err.message}")
+ sys.exit(1)
+ except Exception as ex:
+ print(f"[Unexpected Error]: {ex}")
+ sys.exit(1)
+
+ for field_name, field in result.contents[0].fields.items():
+ if field_name == "Description":
+ print(f"{field_name}:\n{field.value}\n")
+ elif field_name == "Tags":
+ print(f"{field_name}:")
+ for tag in field.value:
+ print(" -", tag.value)
+ ```
+
+ This code submits the selected file data to your analyzer, polls for the results, and then
+ displays the *Description* and *Tags* values that are returned. Note the two shapes:
+ `Description` is a single string, so `field.value` is the text; `Tags` is a list, so
+ `field.value` is a collection you iterate, reading each item's own `.value`.
+
+ > **Note**: `begin_analyze_binary` is the method for analyzing raw bytes you've read from disk. There's a sibling method, `begin_analyze`, that takes `AnalysisInput(url=...)` when your content is already published at a URL.
+
+1. Save the file (**Ctrl+S**).
+
+### Test the app
+
+1. In the terminal pane, make sure you're signed in to Azure:
+
+ ```
+ az login
+ ```
+
+ > **Note**: In most scenarios, just using *az login* will be sufficient. However, if you have subscriptions in multiple tenants, you may need to specify the tenant by using the *--tenant* parameter. See [Sign into Azure interactively using the Azure CLI](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively) for details.
+
+1. Run the application:
+
+ ```
+ python analyze_image.py
+ ```
+
+1. When prompted, enter a number that corresponds to one of these images from the asset library:
+
+ | |  | 
+ |--|--|--|
+ | 1 | 2 | 3 |
+
+1. Observe the output, which should include a description of the selected image and a collection of appropriate tags.
+
+1. Try the other images and notice that every result comes back with the *same two fields*, filled in differently — that consistency is what makes the library searchable.
+
+1. When you're finished, enter any value other than 1, 2, or 3 to exit.
+
+> ✅ **Checkpoint**: You've defined a schema, built an analyzer against it, and called it from
+> Python to generate consistent metadata for a library of images.
+
+When you're finished, enter `deactivate` to exit the virtual environment.
+
+---
+
+**Next:** You've completed the optional tasks. Head back to the [lab overview](A-analyze-visual-content-with-ai.md) for a summary and clean-up steps.
diff --git a/Instructions/Exercises/Consolidated/B-generate-images-and-video-with-ai.md b/Instructions/Exercises/Consolidated/B-generate-images-and-video-with-ai.md
new file mode 100644
index 000000000..942689bc1
--- /dev/null
+++ b/Instructions/Exercises/Consolidated/B-generate-images-and-video-with-ai.md
@@ -0,0 +1,158 @@
+---
+lab:
+ title: 'Generate images and video with AI'
+ description: 'Build the Wide World Importers campaign studio: generate marketing images from text prompts with a gpt-image model, then produce and remix short promo videos with Sora 2. A modular lab you can complete end to end or one task at a time.'
+ level: 300
+ concepts: 'image generation, video generation, Sora, asynchronous jobs, responsible AI'
+ duration: 30
+ islab: true
+ status: 'draft'
+---
+
+# Generate images and video with AI
+
+**Difficulty** ▰▰▰▱▱ **L300** (filled bars out of 5; **L100** beginner → **L500** expert)
+
+Reading an image is one half of visual AI. *Making* one is the other. In this lab you'll build the
+**Wide World Importers** campaign studio — generating marketing images from a written brief, then
+producing short promo videos and remixing them without reshooting anything.
+
+
+
+
+Why is video generation shaped so differently to image generation?
+
+
+Generating an image takes seconds, so `client.images.generate(...)` simply **returns the image**.
+Generating video takes minutes, so it can't work that way — instead you create a **job**, get an ID
+back immediately, **poll** the job until its status becomes `completed`, and then **download** the
+result as a separate step.
+
+That create → poll → download rhythm is the single most important pattern in this lab. Once you
+recognize it, most long-running AI APIs look familiar.
+
+[Learn more →](https://learn.microsoft.com/azure/foundry/openai/concepts/video-generation)
+
+
+
+
+**Your scenario:** you work at **Wide World Importers**, a specialty grocery importer that ships
+unusual produce to supermarkets worldwide, and runs its own marketing studio. The studio needs
+seasonal campaign artwork faster than a photo shoot can deliver it, and short social clips for
+every product line. Across this lab you'll build the generation pipeline behind both.
+
+You'll start with the **Core** task that gets you generating images as quickly as possible. From
+there, a set of **Optional** tasks moves you into video.
+
+> **Note**: Some of the technologies used in this exercise are in preview or in active
+> development. You may experience some unexpected behavior, warnings, or errors.
+
+## What you'll learn
+
+By completing the **Core** task of this exercise, you'll be able to:
+
+- **Generate an image from a text prompt** with a `gpt-image` model, decode the base64 response,
+ and save the result to disk.
+
+The **Optional** tasks let you additionally:
+
+- **Generate a video from a text prompt** with **Sora 2**, poll the asynchronous job until it
+ finishes, and download the finished file.
+- **Animate a reference image** by using a still photograph as the first frame, and **remix** an
+ existing video with a new creative direction instead of regenerating it from scratch.
+
+## How this lab is organized
+
+This lab is **modular**. Each task is written to be completed **on its own, starting fresh** —
+so you can pick a single task and do just that one. Every task also shares one starter folder,
+one virtual environment, and one `.env`, so if you'd rather work straight through, you can.
+
+1. **Start with [Getting started](B0-getting-started.md)** — create your Microsoft Foundry
+ project, deploy the models you need, get the starter code, and set up your `.env`. Every task
+ begins here; if you're doing the whole lab in one sitting, you only need to do this once.
+2. **Do any task.** Each task lists the setup it needs so you can start it independently. If
+ you're moving straight from the previous task, a short *"Continuing from a previous task?"*
+ note at the top lets you skip the repeated setup and keep going.
+
+## Lab at a glance
+
+Complete the **Core** task first (about **30 minutes**) — it ends with a working image generation
+app. Then expand any **Optional** tasks that interest you. The full lab, including all optional
+tasks, takes about **1 hour 45 minutes**.
+
+| Section | Task | Difficulty | Time |
+| --- | --- | --- | --- |
+| **Core** | [Task 1 – Generate images from a prompt](B1-generate-images-from-a-prompt.md) | ▰▰▰▱▱ L300 | ~30 min |
+| *Optional* | [Task 2 – Generate video from a text prompt](B2-generate-video-from-a-prompt.md) | ▰▰▰▱▱ L300 | ~35 min |
+| *Optional* | [Task 3 – Animate a reference image and remix it](B3-animate-a-reference-image.md) | ▰▰▰▰▱ L400 | ~40 min |
+
+**Choosing your path** — pick the tasks that fit the time you have:
+
+- **Core only (~30 min):** do Task 1.
+- **Core + video (~1h 5m):** add **Task 2**, which introduces the create/poll/download job pattern.
+- **Everything (~1h 45m):** add **Task 3**, which grounds generation in an existing image and
+ iterates on a finished video.
+
+> **Important**: Tasks 2 and 3 need access to a video generation model. Access to these models is
+> restricted, and you may need to register your subscription before **sora-2** is available to
+> deploy. If you can't deploy it, you can still complete the Core task in full.
+
+## From instant results to long-running jobs
+
+The three tasks build one idea at a time, and the shape of the code changes as they do:
+
+- In **Task 1**, generation is **synchronous**: you call `client.images.generate(...)`, and the
+ base64 image comes straight back in the response.
+- In **Task 2**, generation becomes **asynchronous**: `client.videos.create(...)` returns a job, and
+ you write the polling loop and download step yourself.
+- In **Task 3**, you keep that same job pattern but change the *input* — a reference image becomes
+ the first frame — and then feed a finished video back in as the input to a **remix**.
+
+## Understand responsible AI considerations
+
+Azure video generation models include built-in Responsible AI (RAI) protections to help ensure safe
+and compliant use.
+
+The Sora 2 model enforces several content restrictions:
+
+- Only content suitable for audiences under 18
+- Copyrighted characters and copyrighted music are rejected
+- Real people — including public figures — cannot be generated
+- Input images with faces of humans are currently rejected
+
+Azure provides input and output moderation across all image and video generation models, along with
+Azure-specific safeguards such as content filtering and abuse monitoring. These systems help detect
+and prevent the generation or misuse of harmful, unsafe, or policy-violating content.
+
+## Summary
+
+Across this lab you:
+
+- Generated **campaign images** from text prompts with a `gpt-image` model and saved the decoded
+ results to disk.
+- (Optionally) generated **video** from a text prompt with Sora 2, polling an asynchronous job to
+ completion and downloading the file.
+- (Optionally) used a **reference image** as a video's first frame, and **remixed** a finished video
+ with a new creative direction.
+
+Together these show how generative media moves from an instant response to a managed, long-running
+pipeline — and how to iterate on a result rather than starting over.
+
+## Clean up
+
+If you're finished, delete the resources you created to avoid unnecessary Azure costs.
+
+1. In the [Azure portal](https://portal.azure.com), navigate to the resource group that contains your Foundry resource.
+1. On the toolbar, select **Delete resource group**, enter the resource group name, and confirm.
diff --git a/Instructions/Exercises/Consolidated/B0-getting-started.md b/Instructions/Exercises/Consolidated/B0-getting-started.md
new file mode 100644
index 000000000..a507be902
--- /dev/null
+++ b/Instructions/Exercises/Consolidated/B0-getting-started.md
@@ -0,0 +1,180 @@
+---
+lab:
+ title: 'Getting started: set up your environment'
+ description: 'Shared setup for the Generate images and video with AI lab: create a Microsoft Foundry project, deploy image and video generation models, get the starter code, and configure your environment. Complete this once before any task.'
+ level: 300
+ concepts: 'environment setup, Microsoft Foundry project, model deployment'
+ status: 'draft'
+---
+
+# Getting started
+
+This page sets up everything the **Generate images and video with AI** lab needs. **Every task
+begins here** — complete this page first. Each task is written so you can then do it on its own; if
+you're working through the whole lab in one sitting, you only need to do this setup once.
+
+**Your scenario:** you work at **Wide World Importers**, a specialty grocery importer that ships
+unusual produce to supermarkets worldwide, and runs its own marketing studio. Across the lab you'll
+build the studio's generation pipeline, from campaign artwork to short promo clips.
+
+> **Note**: Some of the technologies used in this lab are in preview or in active
+> development. You may experience some unexpected behavior, warnings, or errors.
+
+## Prerequisites
+
+Before starting, ensure you have:
+
+- An active [Azure subscription](https://azure.microsoft.com/pricing/purchase-options/azure-account) with sufficient permissions and quota to provision Azure AI resources
+- [Visual Studio Code](https://code.visualstudio.com/) installed on your local machine
+- [Python 3.13](https://www.python.org/downloads/) or later installed\*
+- [Git](https://git-scm.com/downloads) installed and configured
+- [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) installed
+- Basic familiarity with Python
+
+> \* Python 3.14 is available, but some dependencies are not yet compiled for that release. The lab has been successfully tested with Python 3.13.12.
+
+## Create a Microsoft Foundry project
+
+Microsoft Foundry uses projects to organize models, resources, data, and other assets used to
+develop an AI solution.
+
+1. In a web browser, open the [Foundry portal](https://ai.azure.com) at `https://ai.azure.com` and sign in using your Azure credentials. Close any tips or quick start panes that are opened the first time you sign in.
+
+ > **Important**: For this lab, you're using the **New** Foundry experience. If it isn't already enabled, enable the **New Foundry** option in the toolbar at the top of the page.
+
+1. When prompted, create a **new** project with a unique name. Expand the **Advanced options** area and specify:
+ - **Foundry resource**: *Use the default name for your resource (usually {project_name}-resource)*
+ - **Subscription**: *Your Azure subscription*
+ - **Resource group**: *Create or select a resource group*
+ - **Region**: *Select any available region*\*
+
+ > \* Some Azure AI resources are constrained by regional model quotas. If you hit a quota limit later, you may need to create another resource in a different region.
+
+1. Select **Create** and wait for your project to be created.
+
+1. On the home page for your project, note that the API key, project endpoint, and **Azure OpenAI endpoint** are displayed.
+
+ > **Important**: You need the **Azure OpenAI endpoint**, not the project endpoint. Copy it now — you'll add it to your `.env` in a moment.
+
+## Deploy the models
+
+Deploy the model for the task you plan to do. If you're working through the whole lab, deploy both.
+
+### Image generation model (Task 1)
+
+1. On the **Discover** page, select the **Models** tab to view the Microsoft Foundry model catalog.
+
+1. Search for and deploy the `gpt-image-2` model using the default settings. Deployment may take a minute or so.
+
+ After the model is deployed, the playground for the model is displayed.
+
+ > **Tip**: Note the model deployment name (which by default should be *gpt-image-2*) — you'll need it for `IMAGE_MODEL_DEPLOYMENT_NAME`.
+
+### Video generation model (Tasks 2 and 3)
+
+1. Back on the **Models** tab of the **Discover** page, search for and deploy the `sora-2` model using the default settings.
+
+ > **Note**: Access to video generation models is restricted — you may need to register your subscription before **sora-2** is available to deploy. If you can't deploy it, you can still complete Task 1 in full.
+
+1. When the model has been deployed, the model playground page opens.
+
+ > **Tip**: Note the model deployment name (which by default should be *sora-2*) — you'll need it for `VIDEO_MODEL_DEPLOYMENT_NAME`.
+
+## Test the models in the playground
+
+Before you write any code, get a feel for what each model does.
+
+### Try image generation
+
+1. In the playground for your `gpt-image-2` deployment, in the box near the bottom of the page, select the smallest available size and enter a prompt such as `A crate of fresh citrus fruit on a sunlit market stall`.
+
+1. Review the resulting image in the playground:
+
+ 
+
+1. Enter a follow-up prompt, such as `Show the crate in a busy street market` and review the resulting image.
+
+1. Continue testing with new prompts to refine the image until you're happy with it.
+
+### Try video generation
+
+1. In the playground for your `sora-2` deployment, enter the following prompt into the text box:
+
+ ```
+ A slow pan across a market stall stacked with colorful imported fruit.
+ ```
+
+1. Set the video duration to 4 seconds.
+
+1. Select **Generate** to start the video generation process.
+
+ > **Note**: Video generation typically takes 1 to 5 minutes depending on your settings. The content generation APIs include content moderation filters. If the service recognizes your prompt as harmful content, it won't return a generated video.
+
+1. When the AI-generated video is ready, it appears on the page. Review the generated video.
+
+1. In the video details pane text box, edit the video by submitting the following instructions:
+
+ ```
+ Use an inviting instrumental as the background music.
+ ```
+
+## Get the starter code
+
+1. In VS Code, open the Command Palette (**Ctrl+Shift+P**), run **Git: Clone**, and enter:
+
+ ```
+ https://github.com/microsoftlearning/mslearn-ai-vision.git
+ ```
+
+ You may be prompted to confirm you trust the authors.
+
+1. Open the cloned repo, then **File > Open Folder** and select `mslearn-ai-vision/Labfiles/B-generate-images-and-video-with-ai/Python`. This single folder holds the starter code for **every** task in this lab — you use one virtual environment and one `.env` throughout.
+
+1. In VS Code, view the **Extensions** pane and, if it isn't already installed, install the **Python** extension.
+
+1. Right-click **requirements.txt** and choose **Open in Integrated Terminal**. Then create a virtual environment and install packages:
+
+ ```
+ python -m venv labenv
+ .\labenv\Scripts\Activate.ps1
+ pip install -r requirements.txt
+ ```
+
+ > **Important**: Tasks 2 and 3 use `client.videos`, which needs a recent version of the OpenAI library. If you see `AttributeError: 'OpenAI' object has no attribute 'videos'`, run `pip install openai --upgrade`.
+
+1. Copy **.env.example** to a new file named **.env**, then open it and set:
+
+ - `OPENAI_ENDPOINT` — the Azure OpenAI endpoint for your Foundry resource, ending in `/openai/v1/`, so it looks like `https://{your-resource-name}.openai.azure.com/openai/v1/`
+ - `IMAGE_MODEL_DEPLOYMENT_NAME` — the deployment name of your image generation model (Task 1)
+ - `VIDEO_MODEL_DEPLOYMENT_NAME` — the deployment name of your video generation model (Tasks 2 and 3)
+
+ Save the file. You only need to fill in the deployment names for the tasks you plan to do.
+
+## Sign in to Azure
+
+Every task in this lab authenticates with `DefaultAzureCredential`, which uses your Azure CLI
+sign-in. In the terminal, run:
+
+```
+az login
+```
+
+> **Note**: In most scenarios, just using *az login* will be sufficient. However, if you have subscriptions in multiple tenants, you may need to specify the tenant by using the *--tenant* parameter. See [Sign into Azure interactively using the Azure CLI](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively) for details.
+
+## Check you're ready for a task
+
+Each task needs specific values in your `.env`. Before starting a task, run the preflight
+check from the `Labfiles/B-generate-images-and-video-with-ai` folder — it reads your `.env` and
+tells you what (if anything) is missing:
+
+```
+python setup/check_env.py --task 1
+```
+
+Swap `1` for the task number you're about to start. That's it — head to any task:
+
+| Task | Page |
+| --- | --- |
+| Task 1 – Generate images from a prompt | [B1](B1-generate-images-from-a-prompt.md) |
+| Task 2 – Generate video from a text prompt | [B2](B2-generate-video-from-a-prompt.md) |
+| Task 3 – Animate a reference image and remix it | [B3](B3-animate-a-reference-image.md) |
diff --git a/Instructions/Exercises/Consolidated/B1-generate-images-from-a-prompt.md b/Instructions/Exercises/Consolidated/B1-generate-images-from-a-prompt.md
new file mode 100644
index 000000000..ac9ba9c2c
--- /dev/null
+++ b/Instructions/Exercises/Consolidated/B1-generate-images-from-a-prompt.md
@@ -0,0 +1,164 @@
+---
+lab:
+ title: 'Task 1 – Generate images from a prompt'
+ description: 'Use a gpt-image model in Microsoft Foundry to generate Wide World Importers campaign artwork from text prompts, decode the base64 response, and save each image to disk.'
+ level: 300
+ concepts: 'image generation, gpt-image, base64 responses'
+ islab: true
+ status: 'draft'
+---
+
+# Task 1 — Generate images from a prompt
+
+*Part of the **Generate images and video with AI** lab. New here? Start with [Getting started](B0-getting-started.md).*
+
+> **Set up (start here):** This task needs a Foundry project with an image generation model
+> deployed, and the starter code. If you haven't already, complete
+> [Getting started](B0-getting-started.md) to create your project, deploy `gpt-image-2`, clone the
+> code, and set `OPENAI_ENDPOINT` and `IMAGE_MODEL_DEPLOYMENT_NAME` in `Python/.env`. Then, from the
+> `Labfiles/B-generate-images-and-video-with-ai` folder, verify you're ready:
+
+```
+python setup/check_env.py --task 1
+```
+
+> **Continuing from a previous task?** If you just finished an earlier task in the same
+> `Python` folder, your project, virtual environment, and `.env` are already set — go
+> straight to **Write code to connect to your model** below.
+
+---
+
+The Wide World Importers marketing studio needs seasonal artwork faster than a photo shoot can
+deliver it. In this task you'll build the tool that turns a written brief into a saved image file:
+you type a prompt, the model returns the picture, and your code decodes and stores it.
+
+
+
+
+Why does the response contain text instead of an image?
+
+
+The `gpt-image` models always return the picture as **base64-encoded data** in a `b64_json` field —
+never as a link you download. Base64 is a way of representing binary data (the PNG bytes) using
+only text characters, so it can travel inside a JSON response.
+
+That's why your code has three steps rather than one: read `b64_json` out of the response, decode
+it back into bytes with `base64.b64decode`, and write those bytes to a `.png` file.
+
+[Learn more →](https://learn.microsoft.com/azure/foundry/openai/how-to/dall-e)
+
+
+
+
+Open the `Python` folder and activate the virtual environment from [Getting started](B0-getting-started.md) (`.\labenv\Scripts\Activate.ps1`), then continue below.
+
+### Write code to connect to your model
+
+Open **image_client.py** and add code at each commented placeholder.
+
+1. Review the code already in the file. It contains:
+ - Some **import** statements.
+ - A `main` function that loads your configuration and then loops asking you for a prompt until you type `quit`.
+ - A `save_image` function, already written, that creates an `images` folder and writes the decoded bytes to a `.png` file.
+
+ > **Tip**: As you add code, be sure to maintain the correct indentation.
+
+1. Find the comment **Add references** and add the following code to reference the namespaces in the libraries you installed previously:
+
+ ```python
+ # Add references
+ from dotenv import load_dotenv
+ from azure.identity import DefaultAzureCredential, get_bearer_token_provider
+ from openai import OpenAI
+ import base64
+ ```
+
+1. In the **main** function, under the comment **Get configuration settings**, note that the code loads the endpoint and model deployment name values you defined in the configuration file.
+
+1. Under the comment **Initialize the client**, add the following code to connect to your model using the Azure credentials you're currently signed in with:
+
+ ```python
+ # Initialize the client
+ token_provider = get_bearer_token_provider(
+ DefaultAzureCredential(),
+ "https://ai.azure.com/.default"
+ )
+
+ client = OpenAI(
+ base_url=endpoint,
+ api_key=token_provider(),
+ )
+ ```
+
+### Write code to generate and save an image
+
+1. Note that the code includes a loop to allow a user to input a prompt until they enter "quit". In the loop section, under the comment **Generate an image**, add the following code to submit the prompt and retrieve the data for the generated image from your model:
+
+ ```python
+ # Generate an image
+ img = client.images.generate(
+ model=model_deployment,
+ prompt=input_text,
+ n=1
+ )
+
+ json_response = json.loads(img.model_dump_json())
+ image_data = json_response["data"][0].get("b64_json")
+ image_data_in_bytes = base64.b64decode(image_data)
+ ```
+
+ > **Note**: The gpt-image model returns the generated image as base64-encoded data in `b64_json`. The `n=1` parameter asks for a single image; you can request up to 10.
+
+1. Note that the code in the remainder of the **main** function passes the image data and a filename to the provided `save_image` function, which writes the decoded bytes out as a .png file.
+
+1. Save the file (**Ctrl+S**).
+
+### Run the client application
+
+1. In the terminal, make sure you're signed in and then run the app:
+
+ ```
+ az login
+ ```
+
+ ```
+ python image_client.py
+ ```
+
+1. When prompted, enter a request for an image, such as:
+
+ ```
+ Create a bright poster of a crate of imported citrus fruit at a street market
+ ```
+
+ After a moment or two, the app should confirm that the image has been saved. The image appears
+ in the `images` folder in your project directory with the name `image_1.png`.
+
+1. Try a few more prompts to refine the campaign look. When you're finished, enter `quit` to exit the program.
+
+ > **Note**: In this simple app, we haven't implemented logic to retain conversation history; so the model will treat each prompt as a new request with no context of the previous prompt.
+
+1. Review the generated images in the `images` folder.
+
+> ✅ **Checkpoint**: You've generated images from text prompts and saved them to disk. That's the
+> Core of this lab — and notice how fast it was, because image generation returns synchronously.
+> The optional tasks move to video, where results take minutes and the code has to be shaped
+> differently.
+
+When you're finished, enter `deactivate` to exit the virtual environment.
+
+---
+
+**Next (optional):** [Task 2 — Generate video from a text prompt](B2-generate-video-from-a-prompt.md) · [Task 3 — Animate a reference image and remix it](B3-animate-a-reference-image.md)
diff --git a/Instructions/Exercises/Consolidated/B2-generate-video-from-a-prompt.md b/Instructions/Exercises/Consolidated/B2-generate-video-from-a-prompt.md
new file mode 100644
index 000000000..ebdb3a345
--- /dev/null
+++ b/Instructions/Exercises/Consolidated/B2-generate-video-from-a-prompt.md
@@ -0,0 +1,200 @@
+---
+lab:
+ title: 'Task 2 – Generate video from a text prompt'
+ description: 'Use Sora 2 in Microsoft Foundry to create a video generation job from a text prompt, poll the asynchronous job until it completes, and download the finished file.'
+ level: 300
+ concepts: 'video generation, Sora, asynchronous jobs, polling'
+ islab: true
+ status: 'draft'
+---
+
+# Task 2 — Generate video from a text prompt
+
+*Part of the **Generate images and video with AI** lab. New here? Start with [Getting started](B0-getting-started.md).*
+
+> **Set up (start here):** This task needs a Foundry project with a **video generation** model
+> deployed, and the starter code. If you haven't already, complete
+> [Getting started](B0-getting-started.md) to create your project, deploy `sora-2`, clone the code,
+> and set `OPENAI_ENDPOINT` and `VIDEO_MODEL_DEPLOYMENT_NAME` in `Python/.env`. Then, from the
+> `Labfiles/B-generate-images-and-video-with-ai` folder, verify you're ready:
+
+```
+python setup/check_env.py --task 2
+```
+
+> **Continuing from a previous task?** If you just finished Task 1 in the same `Python` folder,
+> your project, virtual environment, and `az login` are already set — but Task 1 used a *different
+> model*. Make sure you've deployed `sora-2` and set `VIDEO_MODEL_DEPLOYMENT_NAME` in your `.env`
+> before you start, then go straight to **Write code to authenticate** below.
+
+> **Note**: Access to video generation models is restricted — you may need to register your
+> subscription before **sora-2** is available to deploy. Video generation typically takes 1 to 5
+> minutes per video, so this task takes longer to run than it does to write.
+
+---
+
+Task 1 returned an image the instant you asked for it. Video doesn't work that way — a clip takes
+minutes to render, so the API hands you a **job** instead of a result. In this task you'll write the
+create → poll → download loop that turns a written brief into a finished Wide World Importers promo
+clip on disk.
+
+
+
+
+What is a long-running operation?
+
+
+Some work takes far too long to finish inside a single HTTP request. Instead of making you wait,
+the service accepts the work, gives you an **ID** immediately, and gets on with it in the
+background. Your job is to check back — that's **polling**.
+
+A video job moves through statuses like `queued` → `in_progress` → `completed`. Your loop keeps
+retrieving the job until the status is one of the terminal values (`completed`, `failed`, or
+`cancelled`), sleeping between checks so you're not hammering the service. Only once it's
+`completed` can you download the file.
+
+[Learn more →](https://learn.microsoft.com/azure/foundry/openai/concepts/video-generation)
+
+
+
+
+Open the `Python` folder and activate the virtual environment from [Getting started](B0-getting-started.md) (`.\labenv\Scripts\Activate.ps1`), then continue below.
+
+### Write code to authenticate
+
+Open **video_from_text.py** and add code at each commented placeholder.
+
+1. Review the code already in the file. Note that, unlike the other apps in this lab, the configuration and client live at **module level** (outside `main`) so that the helper functions further down the file can use the client too.
+
+ > **Tip**: As you add code, be sure to maintain the correct indentation.
+
+1. Find the comment **Add references** and add the following code for the necessary imports:
+
+ ```python
+ # Add references
+ import time
+ from openai import OpenAI
+ from azure.identity import DefaultAzureCredential, get_bearer_token_provider
+ ```
+
+1. Find the comment **Get the token provider for Azure OpenAI authentication** and add the following code:
+
+ ```python
+ # Get the token provider for Azure OpenAI authentication
+ token_provider = get_bearer_token_provider(
+ DefaultAzureCredential(), "https://ai.azure.com/.default"
+ )
+
+ # Initialize the OpenAI client with the endpoint and token provider
+ client = OpenAI(
+ base_url=endpoint,
+ api_key=token_provider(),
+ )
+ ```
+
+### Write code to create the video job
+
+1. In the **main** function, find the comment **Generate a video from a text prompt** and add the following code:
+
+ ```python
+ # Generate a video from a text prompt
+ video = client.videos.create(
+ model=model_deployment,
+ prompt="A crate of fresh citrus fruit on a sunlit market stall, gentle camera push in",
+ size="1280x720",
+ seconds=4,
+ )
+ video = poll_video_status(video.id)
+ ```
+
+ `client.videos.create` returns as soon as the job is **accepted** — the `video` object you get
+ back holds an ID and a status, not a finished film. The call to `poll_video_status` is what
+ waits for the real thing.
+
+ > **Tip**: `size` accepts values such as `"1280x720"` (landscape) and `"720x1280"` (portrait), and `seconds` accepts 4, 8, or 12. Shorter clips finish faster, so stick with 4 while you're testing.
+
+### Write code to poll for completion
+
+1. Find the comment **Poll video status until completion** and add the following code to complete the `poll_video_status` function:
+
+ ```python
+ # Poll video status until completion
+ video = client.videos.retrieve(video_id)
+
+ while video.status not in ["completed", "failed", "cancelled"]:
+ print(f"Status: {video.status}. Waiting 20 seconds...")
+ time.sleep(20)
+ video = client.videos.retrieve(video_id)
+
+ if video.status == "completed":
+ print("Video successfully completed!")
+ else:
+ print(f"Video creation ended with status: {video.status}")
+
+ return video
+ ```
+
+ Note the three terminal statuses. Checking for *all* of them — not just `completed` — is what
+ stops the loop spinning forever when a job fails or is cancelled.
+
+### Write code to download the result
+
+1. Find the comment **Download the completed video** and add the following code to complete the `download_video` function:
+
+ ```python
+ # Download the completed video
+ print(f"Downloading video {video_id}...")
+ content = client.videos.download_content(video_id, variant="video")
+ content.write_to_file(output_filename)
+ print(f"Saved video to {output_filename}")
+ ```
+
+ The `variant="video"` argument asks for the video file itself; the service can also return other
+ variants, such as a thumbnail.
+
+1. Save the file (**Ctrl+S**).
+
+### Run and test
+
+1. In the terminal, make sure you're signed in and then run the app:
+
+ ```
+ az login
+ ```
+
+ ```
+ python video_from_text.py
+ ```
+
+1. Observe the output as the application:
+ - Creates a video job from your text prompt
+ - Prints a status line every 20 seconds while it polls
+ - Downloads the completed video
+
+ > **Note**: Video generation typically takes 1 to 5 minutes. Be patient while waiting for the status to change to "completed".
+
+1. When the application finishes, check your project folder for `original_video.mp4` and play it.
+
+ > **Tip**: If generation ends with a `failed` status, the content filters may have rejected your prompt. Try a prompt with no people, no brands, and no copyrighted characters in it.
+
+> ✅ **Checkpoint**: You've created an asynchronous video job, polled it to completion, and
+> downloaded the result — the create/poll/download pattern that almost every long-running AI API
+> follows.
+
+When you're finished, enter `deactivate` to exit the virtual environment.
+
+---
+
+**Next (optional):** [Task 3 — Animate a reference image and remix it](B3-animate-a-reference-image.md)
diff --git a/Instructions/Exercises/Consolidated/B3-animate-a-reference-image.md b/Instructions/Exercises/Consolidated/B3-animate-a-reference-image.md
new file mode 100644
index 000000000..d5c55fb32
--- /dev/null
+++ b/Instructions/Exercises/Consolidated/B3-animate-a-reference-image.md
@@ -0,0 +1,207 @@
+---
+lab:
+ title: 'Task 3 – Animate a reference image and remix it'
+ description: 'Use a still photograph as the first frame of a Sora 2 video, then remix the finished clip with a new creative direction instead of regenerating it from scratch.'
+ level: 400
+ concepts: 'video generation, reference images, remix, asynchronous jobs'
+ islab: true
+ status: 'draft'
+---
+
+# Task 3 — Animate a reference image and remix it
+
+*Part of the **Generate images and video with AI** lab. New here? Start with [Getting started](B0-getting-started.md).*
+
+> **Set up (start here):** This task needs a Foundry project with a **video generation** model
+> deployed, and the starter code. If you haven't already, complete
+> [Getting started](B0-getting-started.md) to create your project, deploy `sora-2`, clone the code,
+> and set `OPENAI_ENDPOINT` and `VIDEO_MODEL_DEPLOYMENT_NAME` in `Python/.env`. Then, from the
+> `Labfiles/B-generate-images-and-video-with-ai` folder, verify you're ready:
+
+```
+python setup/check_env.py --task 3
+```
+
+> **Continuing from a previous task?** If you just finished Task 2 in the same `Python` folder, your
+> project, virtual environment, and `.env` are already set — go straight to **Write code to
+> authenticate** below. You'll recognize the polling and download helpers: in this task they're
+> **already written for you**, so you can focus on the two calls that are new.
+
+> **Note**: This task generates **two** videos, and each typically takes 1 to 5 minutes. Expect the
+> script to run for several minutes before it finishes.
+
+---
+
+Text prompts give you a new scene every time — which is a problem when the Wide World Importers
+studio has already approved a product shot. In this task you'll pin the video to that existing
+photograph by using it as the **first frame**, and then **remix** the finished clip to try a
+different look without paying to generate it all over again.
+
+
+
+
+What's the difference between a reference image and a remix?
+
+
+They're two different ways of *not* starting from a blank page.
+
+A **reference image** grounds a **new** job: you pass `input_reference=` a still, and the model uses
+it as the video's opening frame, so the generated motion continues from a scene you already
+approved.
+
+A **remix** takes a **finished** video and re-renders it under new instructions. You pass the
+existing `video_id` plus a prompt describing the change, and get a new video back. Because the
+source clip is the starting point, you can iterate on lighting or color without re-describing the
+whole scene.
+
+> The reference image's resolution must match the video `size` you request — the supported
+> combinations are `1280x720` and `720x1280`.
+
+[Learn more →](https://learn.microsoft.com/azure/foundry/openai/concepts/video-generation)
+
+
+
+
+Open the `Python` folder and activate the virtual environment from [Getting started](B0-getting-started.md) (`.\labenv\Scripts\Activate.ps1`), then continue below.
+
+### Write code to authenticate
+
+Open **video_from_image.py** and add code at each commented placeholder.
+
+1. Review the code already in the file:
+ - `reference.png` in the same folder is the still image you'll animate.
+ - `poll_video_status` and `download_video` are **already implemented** — they're the same helpers as Task 2.
+ - `generate_video_from_image` and `remix_video` are the two functions you'll complete.
+
+ > **Tip**: As you add code, be sure to maintain the correct indentation.
+
+1. Find the comment **Add references** and add the following code for the necessary imports:
+
+ ```python
+ # Add references
+ from openai import OpenAI
+ from azure.identity import DefaultAzureCredential, get_bearer_token_provider
+ ```
+
+1. Find the comment **Get the token provider for Azure OpenAI authentication** and add the following code:
+
+ ```python
+ # Get the token provider for Azure OpenAI authentication
+ token_provider = get_bearer_token_provider(
+ DefaultAzureCredential(), "https://ai.azure.com/.default"
+ )
+
+ # Initialize the OpenAI client with the endpoint and token provider
+ client = OpenAI(
+ base_url=endpoint,
+ api_key=token_provider(),
+ )
+ ```
+
+### Write code to generate a video from a reference image
+
+1. In the **main** function, find the comment **Generate a video from a reference image** and add the following code:
+
+ ```python
+ # Generate a video from a reference image
+ video = generate_video_from_image(
+ image_path="reference.png",
+ prompt="The scene comes to life with gentle movement and ambient lighting",
+ size="1280x720",
+ seconds=4
+ )
+ ```
+
+1. Further down the file, find the comment **Create the video with an image reference** and add the following code to complete the `generate_video_from_image` function:
+
+ ```python
+ # Create the video with an image reference
+ video = client.videos.create(
+ model=model_deployment,
+ prompt=prompt,
+ size=size,
+ seconds=seconds,
+ input_reference=open(image_path, "rb"),
+ )
+ ```
+
+ This is the same `client.videos.create` call as Task 2, with one addition: `input_reference`
+ takes an open file handle for the still image. Everything after it — the polling and the
+ download — is unchanged, which is the point: grounding the job in an image doesn't change how
+ you manage it.
+
+ > **Note**: Accepted reference image types are JPEG, PNG, and WebP. Remember that Sora 2 rejects input images containing human faces.
+
+### Write code to remix the finished video
+
+1. Find the comment **Remix an existing video** and add the following code to complete the `remix_video` function:
+
+ ```python
+ # Remix an existing video
+ video = client.videos.remix(
+ video_id=video_id,
+ prompt=prompt,
+ )
+
+ print(f"Remix started. New video ID: {video.id}")
+ print(f"Initial status: {video.status}")
+
+ # Poll for completion
+ video = poll_video_status(video.id)
+ return video
+ ```
+
+ A remix is just another job: it returns a **new** video ID with its own status, so it goes
+ through exactly the same polling loop. Note that `main` passes the *first* video's ID in, and
+ downloads the remixed result under a different filename.
+
+1. Save the file (**Ctrl+S**).
+
+### Run and test
+
+1. In the terminal, make sure you're signed in and then run the app:
+
+ ```
+ az login
+ ```
+
+ ```
+ python video_from_image.py
+ ```
+
+1. Observe the output as the application:
+ - Creates a video job that starts from `reference.png`
+ - Polls until it completes, then downloads it
+ - Remixes that video with a warmer color palette
+ - Polls the remix job and downloads the result
+
+ > **Note**: This generates two videos in sequence, so allow several minutes for the script to finish.
+
+1. When the application finishes, check your project folder for `image_based_video.mp4` and `remixed_video.mp4`.
+
+1. Play both files and compare them with `reference.png`. The first video should open on the still
+ image and move from there; the remix should keep that scene but shift its color palette.
+
+ > **Tip**: If either job ends with a `failed` status, the content filters may have rejected the prompt or the reference image. Sora 2 rejects images containing human faces.
+
+> ✅ **Checkpoint**: You've grounded a video generation job in an existing image and iterated on a
+> finished video with a remix — two techniques that let a studio stay on-brand instead of rolling
+> the dice on every render.
+
+When you're finished, enter `deactivate` to exit the virtual environment.
+
+---
+
+**Next:** You've completed the optional tasks. Head back to the [lab overview](B-generate-images-and-video-with-ai.md) for a summary and clean-up steps.
diff --git a/Labfiles/A-analyze-visual-content-with-ai/Python/.env.example b/Labfiles/A-analyze-visual-content-with-ai/Python/.env.example
new file mode 100644
index 000000000..7ae3a795f
--- /dev/null
+++ b/Labfiles/A-analyze-visual-content-with-ai/Python/.env.example
@@ -0,0 +1,12 @@
+# Azure OpenAI endpoint for your Foundry resource - NOT the project endpoint.
+# Must end with /openai/v1/
+OPENAI_ENDPOINT="https://your-resource-name.openai.azure.com/openai/v1/"
+
+# Deployment name of your multimodal chat model (Tasks 1 and 2).
+MODEL_DEPLOYMENT_NAME="your_model_deployment"
+
+# Foundry resource endpoint used by Azure AI Content Understanding (Task 3).
+CONTENT_UNDERSTANDING_ENDPOINT="https://your-resource-name.services.ai.azure.com/"
+
+# Name of the analyzer you built in Content Understanding Studio (Task 3).
+ANALYZER_ID="your_analyzer_id"
\ No newline at end of file
diff --git a/Labfiles/A-analyze-visual-content-with-ai/Python/analyze_image.py b/Labfiles/A-analyze-visual-content-with-ai/Python/analyze_image.py
new file mode 100644
index 000000000..84f5da108
--- /dev/null
+++ b/Labfiles/A-analyze-visual-content-with-ai/Python/analyze_image.py
@@ -0,0 +1,48 @@
+"""
+Task 3 - Wide World Importers media library tagger.
+
+Send an image from the campaign asset library to your Azure AI Content Understanding
+analyzer and print the structured Description and Tags it returns. Fill in each blank
+below, then run:
+
+ python analyze_image.py
+"""
+
+import sys
+import os
+from dotenv import load_dotenv
+
+# Add references
+
+
+def main():
+ # Clear the console
+ os.system('cls' if os.name == 'nt' else 'clear')
+
+ # Get configuration settings
+ load_dotenv()
+ endpoint = os.getenv("CONTENT_UNDERSTANDING_ENDPOINT")
+ analyzer_id = os.getenv("ANALYZER_ID")
+ api_version = "2025-11-01"
+
+ # Set up Content Understanding client
+
+
+ while True:
+ file_no = input('\nChoose a file (1, 2, or 3), or anything else to exit: ')
+ if file_no not in ["1", "2", "3"]:
+ break
+
+ file_path = f"images/image{file_no}.jpg"
+
+ with open(file_path, "rb") as f:
+ file_bytes = f.read()
+
+ print(f"Analyzing with {analyzer_id} analyzer...")
+ print(f" File: {file_path}\n")
+
+ # Analyze the file
+
+
+if __name__ == "__main__":
+ main()
diff --git a/Labfiles/A-analyze-visual-content-with-ai/Python/image_chat_local.py b/Labfiles/A-analyze-visual-content-with-ai/Python/image_chat_local.py
new file mode 100644
index 000000000..c401a3a7d
--- /dev/null
+++ b/Labfiles/A-analyze-visual-content-with-ai/Python/image_chat_local.py
@@ -0,0 +1,58 @@
+"""
+Task 2 - Wide World Importers produce assistant (local image file).
+
+Same assistant as Task 1, but the image is read from disk and sent to the model
+as a base64 data URL instead of a public link. Fill in each blank below, then run:
+
+ python image_chat_local.py
+"""
+
+import os
+import base64
+from pathlib import Path
+from dotenv import load_dotenv
+
+# Add references
+
+
+def main():
+
+ # Clear the console
+ os.system('cls' if os.name == 'nt' else 'clear')
+
+ try:
+
+ # Get configuration settings
+ load_dotenv()
+ openai_endpoint = os.getenv("OPENAI_ENDPOINT")
+ model_deployment = os.getenv("MODEL_DEPLOYMENT_NAME")
+
+ # Create an OpenAI client
+
+
+ # Initialize prompts
+ system_message = (
+ "You are an AI assistant for Wide World Importers, a specialty grocery "
+ "importer. You provide detailed answers to questions about imported produce."
+ )
+ prompt = ""
+
+ # Loop until the user types 'quit'
+ while True:
+ prompt = input("\nAsk a question about the image\n(or type 'quit' to exit)\n")
+ if prompt.lower() == "quit":
+ break
+ elif len(prompt) == 0:
+ print("Please enter a question.\n")
+ else:
+ print("Getting a response ...\n")
+
+ # Get a response to image input
+
+
+ except Exception as ex:
+ print(ex)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/Labfiles/A-analyze-visual-content-with-ai/Python/image_chat_url.py b/Labfiles/A-analyze-visual-content-with-ai/Python/image_chat_url.py
new file mode 100644
index 000000000..10d2db056
--- /dev/null
+++ b/Labfiles/A-analyze-visual-content-with-ai/Python/image_chat_url.py
@@ -0,0 +1,56 @@
+"""
+Task 1 - Wide World Importers produce assistant (image from a URL).
+
+Ask questions about an image that the model reads from a public URL.
+Fill in each "# Add ..." / "# Get ..." blank below, then run:
+
+ python image_chat_url.py
+"""
+
+import os
+from dotenv import load_dotenv
+
+# Add references
+
+
+def main():
+
+ # Clear the console
+ os.system('cls' if os.name == 'nt' else 'clear')
+
+ try:
+
+ # Get configuration settings
+ load_dotenv()
+ openai_endpoint = os.getenv("OPENAI_ENDPOINT")
+ model_deployment = os.getenv("MODEL_DEPLOYMENT_NAME")
+
+ # Create an OpenAI client
+
+
+ # Initialize prompts
+ system_message = (
+ "You are an AI assistant for Wide World Importers, a specialty grocery "
+ "importer. You provide detailed answers to questions about imported produce."
+ )
+ prompt = ""
+
+ # Loop until the user types 'quit'
+ while True:
+ prompt = input("\nAsk a question about the image\n(or type 'quit' to exit)\n")
+ if prompt.lower() == "quit":
+ break
+ elif len(prompt) == 0:
+ print("Please enter a question.\n")
+ else:
+ print("Getting a response ...\n")
+
+ # Get a response to image input
+
+
+ except Exception as ex:
+ print(ex)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/Labfiles/A-analyze-visual-content-with-ai/Python/images/image1.jpg b/Labfiles/A-analyze-visual-content-with-ai/Python/images/image1.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..b65a4eb279c99d533e730d771e90eef4bb4b8917
GIT binary patch
literal 31531
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
literal 0
HcmV?d00001
diff --git a/Labfiles/A-analyze-visual-content-with-ai/Python/images/image2.jpg b/Labfiles/A-analyze-visual-content-with-ai/Python/images/image2.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..03bcb3f4b0a166595ad45746d42f064f4fd94361
GIT binary patch
literal 38725
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 |