Source files for the Transcript Action Extractor article. Two files: the Azure provisioning script and the main Python extractor.
setup.sh
Provisions the full Azure OpenAI stack — resource group, Azure OpenAI resource, and gpt-4o model deployment — then reads credentials back from Azure and writes them to .env. No manual portal steps required. Safe to re-run (idempotent).
#!/usr/bin/env bashset-euopipefail
SCRIPT_DIR="$(cd"$(dirname"${BASH_SOURCE[0]}")"&&pwd)"ENV_FILE="$SCRIPT_DIR/.env"# --- Configuration -------------------------------------------------------RESOURCE_GROUP="rg-fn-openai-transcript"LOCATION="australiaeast"RESOURCE_NAME="aoai-fn-transcript-1234"DEPLOYMENT_NAME="transcript"MODEL_NAME="gpt-4o"MODEL_VERSION="2024-11-20"API_VERSION="2024-06-01"SKU_CAPACITY=10# thousands of tokens per minute# -------------------------------------------------------------------------# Verify az CLI is available and the user is logged inif!command-vaz&>/dev/null;thenecho"Error: Azure CLI not found.">&2echo"Install it from https://docs.microsoft.com/cli/azure/install-azure-cli">&2exit1fiif!azaccountshow&>/dev/null2>&1;thenecho"Error: not logged in to Azure. Run: az login">&2exit1fiecho"Subscription: $(azaccountshow--query'[name, id]'-otsv|tr'\t'' / ')"echo""# Resource group (az group create is idempotent)echo"==> Resource group: $RESOURCE_GROUP"azgroupcreate--name"$RESOURCE_GROUP"--location"$LOCATION"--outputnone
echo" OK"# Azure OpenAI resourceecho"==> Azure OpenAI resource: $RESOURCE_NAME"ifazcognitiveservicesaccountshow\--name"$RESOURCE_NAME"\--resource-group"$RESOURCE_GROUP"\&>/dev/null2>&1;thenecho" Already exists, skipping creation"elseazcognitiveservicesaccountcreate\--name"$RESOURCE_NAME"\--resource-group"$RESOURCE_GROUP"\--kind"OpenAI"\--sku"S0"\--location"$LOCATION"\--yes\--outputnone
echo" Created"fi# Model deploymentecho"==> Model deployment: $DEPLOYMENT_NAME ($MODEL_NAME$MODEL_VERSION)"ifazcognitiveservicesaccountdeploymentshow\--name"$RESOURCE_NAME"\--resource-group"$RESOURCE_GROUP"\--deployment-name"$DEPLOYMENT_NAME"\&>/dev/null2>&1;thenecho" Already exists, skipping deployment"elseazcognitiveservicesaccountdeploymentcreate\--resource-group"$RESOURCE_GROUP"\--name"$RESOURCE_NAME"\--deployment-name"$DEPLOYMENT_NAME"\--model-name"$MODEL_NAME"\--model-version"$MODEL_VERSION"\--model-format"OpenAI"\--sku-capacity"$SKU_CAPACITY"\--sku-name"Standard"\--outputnone
echo" Created"fi# Read endpoint and key back from Azure — no manual copy-paste neededecho"==> Reading credentials"ENDPOINT=$(azcognitiveservicesaccountshow\--name"$RESOURCE_NAME"\--resource-group"$RESOURCE_GROUP"\--query"properties.endpoint"-otsv)API_KEY=$(azcognitiveservicesaccountkeyslist\--name"$RESOURCE_NAME"\--resource-group"$RESOURCE_GROUP"\--query"key1"-otsv)cat>"$ENV_FILE"<<EOFAZURE_OPENAI_ENDPOINT=$ENDPOINTAZURE_OPENAI_API_KEY=$API_KEYAZURE_OPENAI_DEPLOYMENT=$DEPLOYMENT_NAMEAZURE_OPENAI_API_VERSION=$API_VERSIONEOFecho""echo".env written to $ENV_FILE"echo"Endpoint: $ENDPOINT"
transcript_action_extractor.py
Main script. Reads a plain-text transcript file, calls Azure OpenAI with a schema-enforcing system prompt, and writes the extracted action items to a JSON file.
importargparseimportjsonimportosimportsysfrompathlibimportPathfromdotenvimportload_dotenvfromopenaiimportAzureOpenAIload_dotenv(Path(__file__).resolve().parent/".env")ENDPOINT=os.environ["AZURE_OPENAI_ENDPOINT"]API_KEY=os.environ["AZURE_OPENAI_API_KEY"]DEPLOYMENT=os.environ["AZURE_OPENAI_DEPLOYMENT"]API_VERSION=os.environ["AZURE_OPENAI_API_VERSION"]_SYSTEM_PROMPT="""\You are an assistant that extracts action items from meeting transcripts.Return ONLY a valid JSON object with a single key "action_items" whose value is an array.Each element must have exactly these fields: - "owner": the person responsible (string) - "task": what needs to be done (string) - "deadline": when it is due (string, or null if not stated)Do not include any explanation or text outside the JSON object."""defextract_action_items(transcript:str)->list[dict]:ifnottranscript.strip():return[]client=AzureOpenAI(azure_endpoint=ENDPOINT,api_key=API_KEY,api_version=API_VERSION,)response=client.chat.completions.create(model=DEPLOYMENT,response_format={"type":"json_object"},messages=[{"role":"system","content":_SYSTEM_PROMPT},{"role":"user","content":transcript},],)data=json.loads(response.choices[0].message.content)returndata.get("action_items",[])defmain()->None:parser=argparse.ArgumentParser(description="Extract action items from a meeting transcript using Azure OpenAI.")parser.add_argument("transcript_file",help="Path to the plain-text transcript file")parser.add_argument("--output",metavar="FILE",help="Write JSON output to FILE (default: <transcript_stem>.json in the same folder)",)args=parser.parse_args()transcript_path=Path(args.transcript_file).resolve()ifnottranscript_path.exists():print(f"Error: file not found: {transcript_path}",file=sys.stderr)sys.exit(1)transcript=transcript_path.read_text(encoding="utf-8")items=extract_action_items(transcript)output=json.dumps(items,indent=2,ensure_ascii=False)output_path=Path(args.output)ifargs.outputelsetranscript_path.with_suffix(".json")output_path.write_text(output,encoding="utf-8")print(f"Wrote {len(items)} action item(s) to {output_path}")if__name__=="__main__":main()